-
You want to convert the VirtualAddress = 4030A2h in the relative offset.
1. Conversion in RVA:
RVA = Virtual Address - Image Base = 4030A2h - 400000h = 30A2h
2. Check: is the value in the section .text:
30A2h >= 1000h <----------- YES
30A2h < (1000h + 342h) <--- NO!!!
3. Check: is the value in the section .rdata:
30A2h >= 2000h <----------- YES
30A2h < (2000h + 174h) <----- NO!!!
4. Check: is the value in the section .data:
30A2h >= 3000h <----------- YES
30A2h < (3000h+164h) <----- YES
5. You have found the right section, the Virtual Address verifies the
expression and you can use the final formula to obtain the offset
value:
- Offset = 30A2h - 3000h + A00h = AA2h
-
- The hexadecimal value AA2h is the
offset you are looking for; these are only simple steps to be
mechanically applied!
-
-
Strategy.
- This new version of Hex WorkShop
will have to be able to jump to the byte pointed by the specified
Virtual Address. Obviously, this does not mean that we have to write the
entire jump routine because Hex WorkShop is already able to jump
from byte to another; infact, it lets you jump specifying an offset.
We will work with Virtual Address which -as we saw before- we can
easily convert into Offset so, why we should write a new
jump routine?
-
- Here is what we have to do: to read the
VA, to convert into Offset and to jump to the new byte.
- First of all, to read the VA we
should find a dialog in which create a new control; this control
will be used to insert the VA. We will add a new edit box directly
into 'GoTo' dialog and we will read the inserted VA when the 'Go'
button is been pressed.
- The conversion from Va to Offset is
the most delicate step, we need to pay attention. To convert a VA to a Offset it's necessary know some file's informations and, since
Hex Workshop lets you open (simultaneously) more than a single file we need to answer these questions: how can we identify the file on which we are working and, how can we recover all the values necessary to the conversione? The simplest and logical way is certainly to retrieve the information (of the file) when the button, labelled 'Go', is pressed; then, with all the datas we should perform the conversion.
Unfortunately, I already have tried to follow this way with the previous version of the program without luck. Being unable to solve the problem in this way, I decided to take another way for this new version of the program; the solution is not really elegant but seems to work pretty well.
Also
this is "reversing" at the end!
- Every time a new file is opened,
HexWS displays the content of the file inside a MDI_child window. All
the work we will do will be based on the handles of these MDI
windows containing the opened files! The idea is to save the handle of the MDI_child created, and all the data needed for the conversion, during the phase of opening of the file; one structure for each file. We will have as many structures as many open files (with valid PE header!) and the handle of the MDI_child window will be the key to recognise a specific structure. When we will have to perform the conversion we will only have to retrieve the handle of the focused MDI_child and look for that handle through the various structures saved until that moment.
The simplest part of the tutorial will be the last. How we will see sooner, to pass the Offset to the jump routine will be the last of our problems!
- The reversing of the
program is conceptually all here. Make a copy of the exe file; we are
ready to start with the tutorial.
-
- We modify the 'Goto' dialog.
- We start from the easiest part, the modification of the 'Goto' dialog, the one used to personalize the jump. What we will add? The only needed thing is the edit box but to make the new dialog more cool we will add a static control (a label...). To modify the dialog I have used Resource Hacker but you can use your preferred resource editor. You load Hex WorkShop with your editor and look for, inside 'DIALOG', the 'Goto' dialog. Well, it's the number 112. You modify as you prefer, compile and save the new file; here is my new dialog:
-
-
-
- You have to give an ID to the new 2 controls; the ID is necessary for identifying the controls and for reading the data they will contain. Indeed, you can give the ID only to the edit control, we won't use anymore the static control. I put ID=40000 to the edit control; you can put the number you prefer but pay attention because it's better to use a number never used before by other controls.
-
- How we save the data.
- To perform the conversation we need various values from the PE-header of a opened file. Considering what I told you before, we need to save these values from some part when the file is opened. Before seeing where we will save these data, we see what we need to save:
- - MDI child handle of the generic
opened file
- - Image Base
- - Number of sections inside the
file
- - Virtual Offset, Raw Offset and
PointerToRawData (for each section!!!)
-
- To save the number of section is not really necessary but it's useful for the implementation I will use. We will save all the data in the easiest way: sequentially. For each file we will use two structures:
-
- INFOFILE STRUCT
-
handleMDI DWORD ? ; MDI child handle
-
imageBase DWORD ? ; Image Base
-
nSections WORD ? ; Number of sections
- INFOFILE ENDS
-
- SECTIONVALUES STRUCT
-
virtualOffset DWORD ? ; Virtual Offset
-
rawOffset DWORD ? ; Raw Offset
-
pointerToRawData DWORD ? ; Pointer to Raw
Data
- SECTIONVALUES ENDS
-
- The meaning of these two structures is intuitive. SectionValues structure will contain the data of a single section; for each file, we will use
"nSections" structures of this type.
- From now on, with the 'FileStructure' term I will refer to the set of the saved (or to save) data concerning a single file.
-
- We start saving the data.
- Unfortunately we can't save all the FileStructure's data at the same time, so we will split this process into two distinct parts: first of all, we will see how to save MDI_child handle and then we will see how to save all the other data.
-
- Each child window is created
sending the WM_MDICREATE message by the function SendMessage; the
function has this syntax:
-
- LRESULT SendMessage(
-
HWND hWnd, // handle of destination
window
-
UINT Msg, // message to send:
WM_MDICREATE
-
WPARAM wParam, // first message
parameter: not used, it's setted to 0
-
LPARAM lParam //second message
parameter: pointer to MDICREATESTRUCT structure, the definitionof
thhe MDI child
- );
-
- The function returns the handle of the window that is created, the value we want! We will find where the message is sent putting a conditional breakpoint on SendMessage or using IDA. In both cases you will be able to reach this point:
-
- 004ED198 mov ebx, ds:SendMessageA
004ED19E lea eax, [ebp+lParam]
004ED1A1 push eax <-------------------- lParam: pointer to MDICREATESTRUCT
004ED1A2 push 0 <---------------------- wParam: 0
004ED1A4 push WM_MDICREATE <----------- the message
004ED1A9 push dword ptr [esi+0BCh] <--- hWnd: handle
'father' of all MDI_child
004ED1AF call ebx <-------------------- SendMessageA
004ED1B1 mov [ebp+hWnd], eax <--------- eax = handle of
the child window that is been created
- 004ED1B4 call AfxUnhookWindowCreate
004ED1B9 test eax, eax
004ED1BB jnz short loc_4ED1C7
- We have found the handle we were
looking for; now, we need to find where to put the jump to our
personal new snippet, snippet that will let us to save the handle.
Address 4ED1B1 represents a good point. To add new code to the
program I will use Code Snippet Creator.
- Run CSC and create a new project
(SaveHandle); these are the properties of the project:
-
- - Patch as New Section
- - Redirect Control From Code
Section: Virtual Address = 004ED1B1
- - Return To Program: Virtual
Address = 004Ed1B9
-
- How I often do, the new code will be added in a new section. To jump to our new code I will modify the instruction at 4ED1B1. The way to return to the original program will be managed by CSC but we need to remember that instructions at 4ED1B1 and 4ED1B4 will be changed by CSC (to perform the jump) so we should put them into the
snippet.
- The snippet:
- include HWS4.inc ; This file
contains constants definition and various structures
jmp @start
fileStructure BYTE 300h dup(0) ; Address from where we will start to save all the FileStructure
AfxUnhookWindowCreate DWORD 004D44FDh ; Address of
the function overwritten by CSC's patch
nextFreeFileStructure DWORD fileStructure ; Address where to
insert the next FileStructure
mdiHandle HANDLE ? ;
Handle
of MDI_client (the father of all the future child window...)
@start:
pushad ; Save all!
; Save the handle of the MDI client (you will understand
later the reason why...)
push [esi+0BCh] ; I know it's [esi+BCh] because it's
one of the parameters of the previous SendMessageA
pop [mdiHandle] ; I save, into the current
FileStructure, the handle of the window that is been created
mov esi, [nextFreeFileStructure]
mov [esi], eax
popad ; Restore all!
; Two original instruction overwritten by CSC'patch
mov dword ptr [ebp+18h], eax
call AfxUnhookWindowCreate
- Compile and patch HexWS, CSC will ask you to set the characteristics of the new section; the most common characteristics are already setted. Ah, remember to give a name to the section!
- This snippet contains two simple rescues but before passing to the next rescues I would like to comment the snippet. In particular, first of all, the reserved 0x300h bytes for saving FileStructures. These bytes are statically allocated at the beginning of the snippet.
It's surely a non elegant way for solving the problem; since we do not know the number of files that will be opened and the number of the bytes used by each structure, the logical thing to do is to reserve the space into memory in dynamic way using function like HeapAlloc for example.
Reading some messages all over the net, I saw that type of functions are in some way OS dependent; so I decided to use a static allocation. Maybe 0x300h bytes are too much, you personalize it as you want!
- I would like to spend some words
for the meaning of nexFreeFileStructure. Every time a new file is opened, we should move through all the FileStructure structures saved before and, to save the new FileStructure after all the structures.
We use a DWORD (nextFreeFileStructure) to avoid this boring movement and to save the starting address of a new hypothetical FileStructure.
-
- Go on with the rest of the saving.
The others data to save are all inside the file so we will go to
read them after the file is mapped into memory. HexWS uses the
function MapViewOfFile to map the file; put a breakpoint onto this
function and when the debugger will break we will see:
-
- 00416A25 push eax
00416A26 push 4 <-------------------------- dwDesiredAccess:
FILE_MAP_READ, read only access to the maped object
00416A28 push [ebp+hFileMappingObject] <--- hFileMappingObject: handle
of the mapping object (returned by CreateFileMapping)
00416A2B call ds:MapViewOfFile <----------- MapViewOfFile:
return the starting address of the mapped view
00416A31 test eax, eax <------------------- is the file
mapped correctly?
00416A33 mov [esi+14h], eax <-------------- save the
address returned by the function
00416A36 jz short loc_416A40 <------------- jump if an
error occurred in the mapping phase
00416A38 mov [esi+28h], edi
00416A3B mov [esi+2Ch], ebx
00416A3E jmp short loc_416A5B
-
- As you can see we have the call of
the function followed by the check above the returned value. If
all it's ok, at 416A38 eax points to the address where the file
has been mapped in memory. Very good, I say that 416A38 is a good
point to jump to the new code.
- Using CSC we create the second project
(SaveValuesForConvertion); these are the properties:
-
- - Snippet VA = 0058E33D
- - Patch into Existing Section
- - Redirect Control From Code
Section: Virtual Address = 00416A38
- - Return To Program: Virtual
Address = 00416A5B
-
- The next snippet will be inserted
in the section created before and the address of the first
instruction will be at 58E33D; why exactly this address? This is
the address of the next byte after the previous snippet.
Obviously, you can put the code wherever you like :-)
- CSC will manage the return to the
program.
- include HWS4.inc
jmp @start
currentStrutturaFile DWORD 0058E309h ; Initial address of the
current FileStructure (contains only the handle)
@start:
pushad
; In order to save the next information I must have first of all saved the handle
mov edi, [currentStrutturaFile]
mov edi, [edi]
.IF dword ptr [edi] == 0
jmp @done
.ENDIF
mov esi, eax ; Memory addres from where the file is
mapped
assume esi:ptr IMAGE_DOS_HEADER
; Has the file a valid PE header
.IF [esi].e_magic == IMAGE_DOS_SIGNATURE ; Is "MZ"
in the file?
add esi, [esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
.IF [esi].Signature == IMAGE_NT_SIGNATURE ; Is "PE"
in the file?
jmp @PE ;
A valid PE header is in the file!
.ENDIF
.ENDIF
; The file does not have a valid PE header, I remove
- ; the
handle saved before, I don't
save anything else.
mov dword ptr [edi], 0
jmp @done
@PE: ; Saves all the necessary data
push currentStrutturaFile
assume edi: ptr INFOFILE
movzx ecx, [esi].FileHeader.NumberOfSections
mov [edi].nSections, cx ; Saves the number of sections
of the file
mov eax, [esi].OptionalHeader.ImageBase
mov [edi].imageBase, eax ; Saves ImageBase
add edi, sizeof INFOFILE
assume edi: ptr SECTIONVALUES
add esi, sizeof IMAGE_NT_HEADERS ; PE + F8 =
beginning of SectionHeaders
assume esi:ptr IMAGE_SECTION_HEADER
.WHILE ecx>0 ;
I have to read all the sections
; Saves the data of the current section
mov eax,[esi].VirtualAddress
mov [edi].virtualOffset, eax ; Saves VirtualOffset
mov eax,[esi].SizeOfRawData
mov [edi].rawOffset, eax ; Saves RawOffset
mov eax,[esi].PointerToRawData
mov [edi].pointerToRawData, eax ; Saves PointerToRawData
; Updates the pointers used to save the data of the
next section
add edi, sizeof SECTIONVALUES
add esi, sizeof IMAGE_SECTION_HEADER
dec ecx
.ENDW
assume esi:nothing
assume edi:nothing
; Saves the starting address used to save the next
FileStructure
pop esi
mov [esi], edi
@done:
popad
; The original instructions
mov [esi+28h], edi
mov [esi+2Ch], ebx
- In this case we have added some
more lines but at the end it's only a simple saving! We see in
detail the made steps:
- 1. I check if the MDI_child handle
of the opened file has been saved. This check may seem -initially- useless but it's necessary. When a file is saved, HexWS re-map the file in memory causing the execution of this snippet. If we omit this control, a copy of this FileStructure would be created with the only difference of the handle field which would stay empty; infact, when the file is saved, no new MDI child will be created and the result is that a new handle won't be saved.
- 2. The new option has sense only for a file with a valid PE header so I use a simple check onto the type of the file. The classical method is looking for two magic words: "MZ" and "PE". For an not PE file, I do not save nothing, I only remove the handle saved before.
- 3. Finally, I save the data. When
all the need data are saved I update the pointer to the address
where the next FileStructure will be saved.
-
- We have done this part of the
tutorial where the necessary data useful to perform the conversion
are saved, now we have to used this data to perform the conversion
from VirtualAddress to Offset.
-
- Implementation of the
conversion from Virtual Address to Offset.
- In this part of the tutorial we
will add the code necessary to:
- - read the VA
- - convert the readed VA in Offset
- - pass the obtained Offset to the
HWS's jump routine
-
- As usual, we need to find a good
attack point. Load a file (with a valid PE header) and open the
'Goto' dialog. We need to intercept the routine that manage the
pressure of the 'Go' button, how can we do? How Neural_Noise teachs, when a 'Help' button is present it's good to use it! Put a
breakpoint on the function WinHelpA and hit that button. Your
debugger will pop exactly inside the routine that manage the
visualization of the guide so, you exit from this routine and jump back
to the point where the function is called:
-
- 004D77D0 mov ecx, [ebp+8]
004D77D3 call [ebp+14] <---------- call to the
routine that manage the visualization of the guide
004D77D6 jmp short loc_4D7857 <--- we are here!
-
- Is this code related with the
routine we are looking for? Surely! The call at 4D77D3 it's parametric, it depends on the value pointed by ebp+14; in this case, this call is like a "selector", all the routines called by the 'Goto' dialog come from that call. To check if it's really like this is very simple; put a breakpoint on this call, open the 'Goto' dialog and jump to a new offset. The debugger will pop, we were right!
- Theoretically, the program will read the offset you have inserted, will do all the necessary checks and then will move the cursor on the new offset.
- I remember to you our idea: to read the Virtual address, to convert into offset and to write it in the box relative to the offset; we will let the program do all the rest. This method will work if and only if 'Hex' and 'Beginning of File' are signed!
- Obviously, some lines before HexWS reads the offset, we will deviate the normal flow of the program; offset that (I suppose) will be read with one of the functions like GetDlgItemText, GetWindowText . I am too
lazy to step all the instruction inside the routine, I will try to
put a breakpoint on these functions and I will see what will
happen. Wow, we are lucky:
-
- 004D49F5 push esi
004D49F6 mov esi, ecx
004D49F8 mov ecx, [esi+38h]
004D49FB test ecx, ecx
004D49FD jnz short loc_4D4A2D
004D49FF push dword ptr [esi+1Ch] <------- hWnd: handle of
the edit box containing the offset
004D4A02 call ds:GetWindowTextLengthA <---
reads the number
of characters in the offset's edit box
004D4A08 lea ecx, [eax+1]
004D4A0B push ecx
004D4A0C mov ecx, [esp+4+arg_0]
004D4A10 push eax
004D4A11 call GetBufferSetLength
004D4A16 push eax <----------------------- address of the
buffer which will contain the offset
004D4A17 push dword ptr [esi+1Ch] <------- hWnd: handle of
the edit box containing the offset
004D4A1A call ds:GetWindowTextA <---------
reads the offset
inserted
-
- Well, we have found what we were
looking for. We open again CSC and we create a new project
(FromVAtoOffset) with these properties:
-
- - Snippet VA = 58E3C4
- - Patch into Existing Section
- - Redirect Control From Code
Section: Virtual Address = 4D49FF
- - Return To Program: Virtual
Address = 4D4A08
-
- We see the code and then we will
comment it:
- include HWS4.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
jmp @start
fileStructure DWORD 0058E005h
offsetValue dd ? ; Offset
obtained by the conversion
VA db 9 dup(0) ; Virtual Address
readed from the box (if it's been inserted...)
format db "%lX", 0 ; One of the
parameters of the function wsprintf
gotoHandle HANDLE ? ; Handle of
the 'GoTo' dialog
mdiHandle DWORD 58E30Dh ; Handle of the
MDI_client (the father of all the MDI child opened)
errorMessage db "VA is not valid...";,0
notVAMessage db "Only offset for this file!",0
@start:
pushad
; Recover and save the handle of the 'Goto' dialog
invoke GetParent, [esi+1Ch]
mov [gotoHandle], eax
; Read (and check) the Virtual Address inserted
invoke GetDlgItemText, [gotoHandle], 40000, offset VA, 9
.IF eax == 0
jmp @endConversion ; No Virtual Address is been
inserted. Quit...
.ENDIF
; Recover the MDI child's handle that has the focus
mov edi, mdiHandle
invoke SendMessage, [edi], WM_MDIGETACTIVE, NULL, NULL
mov edi, eax
; I look for the FileStructure relative of the current file;
the key
; used for the search is the MDI child's handle with the
focus
mov esi, FileStructure
assume esi: ptr INFOFILE
.WHILE edi != [esi].handleMDI ; Check if the
file is in the list. The file is not in the list if it has not a PE header
.IF [esi].handleMDI == NULL ; The file has not
a PE header but a Virtual Address was inserted in the box
; I print a simple error message
invoke SetDlgItemText, [gotoHandle], 1008, offset notVAMessage
invoke SetDlgItemText, [gotoHandle], 40000, NULL
jmp @endConversion
.ENDIF
; Move on the next FileStructure
movzx eax, [esi].nSections
imul eax, sizeof SECTIONVALUES
add eax, sizeof INFOFILE
add esi, eax
.ENDW
; Conversion of the Virtual Address from ascii to dword
xor eax, eax
lea edi, dword ptr [VA] ; esi points to the Virtual
Address inserted
.WHILE byte ptr [edi] != 0 ; Go on until I have not read
all the characters of the Virtual Address
; I convert the single character
.IF (byte ptr [edi] > 60h && byte ptr [edi] < 67h)
sub byte ptr [edi], 57h
.ELSEIF (byte ptr [edi] > 40h && byte ptr [edi] < 47h)
sub byte ptr [edi], 37h
.ELSEIF (byte ptr [edi] > 2Fh && byte ptr [edi] < 3Ah)
sub byte ptr [edi], 30h
.ELSE
jmp @error
.ENDIF
; I add it at the final dword
shl eax, 4
add al, byte ptr [edi]
inc edi
.ENDW
; The conversion begin!
mov edi, eax
sub edi, [esi].imageBase ; Relative Virtual Address = Virtual Address - ImageBase
movzx ecx, [esi].nSections
add esi, sizeof INFOFILE
assume esi: ptr SECTIONVALUES
; I look for the section where the RVA is and I perform the
conversion
.WHILE ecx > 0
mov eax, [esi].virtualOffset
.IF edi >= eax ; Is (RVA >= Virtual Offset)
?
add eax, [esi].rawOffset ; Virtual Offset + Size of Raw Data
.IF edi < eax ; Is
(RVA < (Virtual Offset + Size of Raw Data)) ?
; I have found the right section!
mov eax, [esi].virtualOffset
sub edi, eax ; RVA - Virtual Offset
mov eax, [esi].pointerToRawData
add eax, edi ; eax = (RVA - Virtual Offset) + Raw Offset = offset
jmp @after_convert
.ENDIF
.ENDIF
; The current section is not the right one. I move on
the next
add esi, sizeof SECTIONVALUES
dec ecx
.ENDW
@error:
; I arrive here if the inserted Virtual Address is not
correct.
; I print a simple error message in the Offset box
invoke SetDlgItemText, [gotoHandle], 1008, offset errorMessage
jmp @endConversion
@after_convert:
; Format the offset
invoke wsprintfA, offset offsetValue, offset format, eax
; Print the converted offset in the right box
invoke SetDlgItemText, [gotoHandle], 1008, offset offsetValue
@endConversion:
popad
; The original instructions
push dword ptr [esi+1Ch]
call GetWindowTextLength
- The conversion will execute if a Virtual Addres has been inserted, so, first of all, we try to read the value. To read the Virtual Address I use the GetDlgItemText function:
-
- UINT GetDlgItemText(
HWND hDlg, // handle of dialog box
int nIDDlgItem, // identifier of control
LPTSTR lpString, // address of buffer for text
int nMaxCount // maximum size of string
);
-
- As you can see, we can't call the
function if we don't have the handle of the dialog box. Now you
can understand the reason why, in the beginning of the routine, I
called the function GetParent; here is the syntax of that
function:
-
- HWND GetParent(
HWND hWnd // handle of the child control
);
-
- Very simple, we pass the handle of
the Offset's edit box and the function returns the handle of the
dialog box.
- If no Virtual Address is specified we exit from the routine and the flow of the program will run normally, reading offset inserted. Otherwise we have to check whether the Virtual Address inserted is correct and, in this case, to convert it.
- If you take attention to the code
you will notice that I added a further check to avoid that a
Virtual Address is read when the opened file has not a valid PE
header.
- It's time to describe the passes
used to perform the conversion:
- 1. I convert the Virtual Address
in string form into the Virtual Address in *DWORD* form. This task may be simplify using one of the most common conversion functions; as an example, for those who uses masm, they can use the function htodw. The problem regarding the use of this function is related to the fact that it does not control the correctness of the characters and to use it may bring us to have
error (i.e.: 40697G is not valid!). I decide to write it ad-hoc. If a non valid character has been inserted, the routine finishes showing an error message.
- 2. Using the formula we have seen
in the preamble, I make the conversion. Not always however! If you
remember, in the initial formula we have imposed some conditions
about the conversion; if one of these conditions is not verified,
an error message will be showed.
- 3. The last task: print the converted offset on his edit box. It's the conclusive part of this snippet; it consists in the formatting of the obtained offset and in the writing of it in the right edit box. To do the job I used two functions: wsprintf and SetDlgItemText.
-
- int wsprintf(
LPTSTR lpOut, // pointer to buffer for output
LPCTSTR lpFmt, // pointer to format control
string
... // optional arguments
);
-
- The second parameter defines the way in which you want that the formatting is made. In this specific case, the output value should be a hexadecimal number so, this is the reason why I have used the value %lX ('X' stands for uppercase, you can omit it). Optional arguments: we have only one parameter to specify: the value we want to convert. That is all...
-
- BOOL SetDlgItemText(
HWND hDlg, // handle of dialog box
int nIDDlgItem, // identifier of control
LPCTSTR lpString // text to set
);
-
- This function is the dual function of GetDlgItemText, inserts the value pointed by the third parameter in the chosen control (in this specific case an edit box).
-
- Elimination of a structure.
- We have to do the last thing: when we close a file, we have to eliminate a FileStrutture.
We start with the search of a good attack point. Fundamentally we have to manage two different cases: the file has been modified and the file has not been modified.
- A quick way for finding a good attack point is: every time you close a modified file that has not been saved, HexWS ask you whether you want to save it, showing a messagebox with multiple choice: save and do not save; we use this box! You open a file, change at least one byte, put a breakpoint on MessageBox and try to close the file. Your debugger will break; you exit from the routine that display the box and step some lines, you should be able to reach:
-
- 004DC64A dec eax <-------------------- eax
represents your choice: 7 you does not save the file,
6 otherwise
004DC64B dec eax
004DC64C jz short loc_4DC666
004DC64E sub eax, 4
004DC651 jnz short loc_4DC661 <------- jump if the file
won't be saved
004DC653 mov eax, [edi]
004DC655 mov ecx, edi
004DC657 call dword ptr [eax+9Ch] <--- save the file
004DC65D test eax, eax <-------------- eax = 1 if
everything it's ok
004DC65F jz short loc_4DC666
004DC661 push 1 <--------------------- in both cases I
will arrive here
004DC663 pop esi
004DC664 jmp short loc_4DC668
004DC666 xor esi, esi
-
- 4DC661 seems to me a good point for changing the flow of the program because is executed in both cases. Unfortunately we need to take care about another attack point; the program can be closed even if it's not modified. What can we do to find this new attack point? The better thing to do is to put a breakpoint at the beginning of the routine containing the code above and to see what happens. Put a breakpoint on 4DC591 and close a non modified file:
-
- 004DC5A2 call dword ptr [eax+58h]
004DC5A5 test eax, eax
004DC5A7 jnz short loc_4DC5B1 <--- jump if the file has
been modified
004DC5A9 push 1 <----------------- here if the file has
not been modified
004DC5AB pop eax
004DC5AC jmp loc_4DC683
004DC5B1 mov eax, dword_550BCC
-
- Well, we have found the second point: 4DC5A9. We have to modify the program in two distinct points and, we have two choices: to write two identically snippets with different properties or put all the code into only one snippet. I choose the second option so, I will add only one routine. These are the properties for the last project
(RemoveStrutturaFile):
- - Snippet VA = 58E69A
- - Patch into Existing Section
- - Redirect Control From Code
Section: Virtual Address = 4DC5A9
- - Fon't Return Control To Program
-
- This time we manage the return because we must differentiate two cases.
- include HWS4.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
jmp @start
fileStructure DWORD 0058E005h
nextFreeFileStructure DWORD 0058E309h ; Address where the
next FileStructure will be saved
mdiHandle DWORD 0058E30Dh ; MDI
client handle (the father of all opened MDI child)
@start:
@firstPlace: ; Here from 4DC5A9
; The next two instructions are from the original
program
push 1
pop eax
push 004DC683h ; The address for the managment of
the return to Hex WorkShop
jmp @sameSnippet
@secondPlace: ; Here from 4DC661 (the
modified file is closed, saved or non saved)
; The next two instruction are from the original
program
push 1
pop esi
push 004DC668h ; The address for the managment of
the return to Hex WorkShop
@sameSnippet:
pushad
mov edi, fileStructure
assume edi: ptr INFOFILE
; Retrieve the handle of the MDI child that will be
closed
mov esi, mdiHandle
invoke SendMessage, dword ptr [esi], WM_MDIGETACTIVE, NULL, NULL
mov esi, eax ; esi = handle of the MDI child
relative to the file that will be closed
; Look for the FileStructure to eliminate
.WHILE esi != [edi].handleMDI
; The FileStructure relative of the file that has to be
closed might be not saved, it's better to check
.IF [edi].handleMDI == NULL
jmp @done
.ENDIF
; Move on the next FileStructure
movzx eax, [edi].nSections
imul eax, sizeof SECTIONVALUES
add eax, sizeof INFOFILE
add edi, eax
.ENDW
; Calculus the dimension (n° of bytes) of the FileStructure
to eliminate
movzx esi, [edi].nSections
imul esi, sizeof SECTIONVALUES
add esi, sizeof INFOFILE
push esi ; Push the dimension of the
FileStructure to eliminate
add esi, edi ; esi -> next FileStructure (if exist) of
the one to eliminate
push esi ; Push the pointer to the FileStructure
after the one to eliminate
; Calculus the number of bytes of the successive
FileStructure. The values is saved in ecx
xor ecx, ecx
assume esi: ptr INFOFILE
.WHILE [esi].handleMDI != NULL
movzx eax, [esi].nSections
imul eax, sizeof SECTIONVALUES
add eax, sizeof INFOFILE
add esi, eax
add ecx, eax
.ENDW
pop esi
; Check whether FileStructure to eliminate is the last
saved
.IF ecx != 0 ; It is not the last one, I have to
move back all the successive FileStructure(s)
@shiftBack:
lodsb
stosb
loop @shiftBack
.ENDIF
; Update the pointer to nextFreeFileStructure
mov esi, dword ptr [nextFreeFileStructure]
mov dword ptr [esi], edi
; Clear the final bytes; the number of bytes to clear is the
number of FileStructure's bytes
pop ecx
@zeroLastStructure:
mov byte ptr [edi+ecx-1], 0
loop @zeroLastStructure
@done:
popad
retn
- Initially you can see an unusual thing, I retrieve immediately the original instruction; I have decided to do it soon only for a convenience matter. Besides this, I also push the return address.
- After that, I delete the FileStructure. Even in this case, basing all the job above the various handles, first of all I have to retrieve the handle of the MDI child focused; this task is necessary to identify the right FileStructure to cut.
The elimination of the FileStructure happens writing over the data of the structures that follow it; if it is the last one of the list I cover it of byte 00. Remember you to update NextFreeFileStructure putting the new address!
- Another thing and we have finish;
we have to modify the instruction at 4DC661 into the new "jmp
0058E5B2", do you know the reason why? Simple, 58E5B2 is the address
of "@secondPlace". To change these bytes you can use
your preferred hexadecimal editore; from DC661h offset put the
following bytes: E9 4C 1F 0B 00.
-
- Postamble:
conclusion.
- I tested this new version of Hex
WorkShop on w98se and winxp machines and it seems does not have
bug. If you find one (or more) bug, please, contact me, I will try
to solve the problem!
-
- Greetings and thanks.
- I would like to thank all the
mates at RCE messageboard and all the members of UIC. Feel free to
mail me for comments, suggestions, bug reports and/or whatever you
want...