W32Dasm Disassembled
Part3:
Adding a Recent Files List by Harlequin December 2000
|
|
|
Introduction |
Although W32Dasm is an outstanding program it has a few deficiencies, one of these is a lack of a recent files list. In this essay I shall be adding just such a thing.
This is part 3 in a series of essays on adding functionality to
W32Dasm v.8.93. What more is there to say let's go......
Preliminaries |
I am using W32Dasm v8.93 currently and have been since I started
reversing so all references in this essay will be to this version.
Before starting I made a copy of W32Dasm called Target which is the
file on which I will be working and making changes, as I will be
using W32dasm in the making of many of the changes I will refer to
the new W32Dasm as Target and W32Dasm as itself.
All the changes in this essay are made after the installation of my
previous two W32Dasm Disassembled essays . All references in this
essay will assume that you have installed them, if you have not then
not only will you not be able to follow this essay but much of the
code will not work!!!!!
So first of all you should disassemble Target using W32Dasm and save
the deadlisting.
The Essay |
Objectives:
To add a recent files list menu item to W32Dasm
Ok before we can attempt any of the above there is a small problem to
address, the matter of space. Last time we used nearly all our space
and we probably won't have enough in the location we were using. So
we need some more!
There are many ways to achieve this including adding a new section of
our own, this would however add bytes to the file size and as I want
to maintain my updates in the form of patches this is not the best
solution. We could simply go looking for new spaces in other sections
but if you do this you will find very little. I have another solution
however which serves me twofold, and it is staring you in the face
every time you start up W32Dasm. Yep that big ugly bitmap in the
middle of the screen. We could start changing all the code which
loads the bitmap so that it doesn't and use it's space but better still.
I created my own little bitmap with a whopping 5kb file size. (the W32Dasm one is 38kb) Then using ExeScope I import it in place of BITMAP #333. Next save the file but make sure you have the 'Permit to change the filesize' checkbox UNCHECKED. ExeScope will write the new bitmap into target without changing the file size. This also has the advantage that the extra bytes from the old W32Dasm bitmap remain, so in a byte comparison to make a patch we only have the 5kb of our bitmap to alter. Now we have about 33kb of free space from offset E7110h to EF3A0h, enough to drive a herd of camels through never mind, make the changes I shall be making here. This space is in the .rsrc section though so we won't be able to execute any of our code unless we change the section characteristics from 50000040h to +CODE+EXECUTABLE = 70000060h.
If you installed Part2 from my patch you will already have this bitmap. I made a mistake on Part2 and had to quickly update it after I had started Part3 so I just left the bitmap in place.
Although I have all this space I am going to continue to do most of the work inside my W32patch.dll simply because I can and I prefer to, besides less bytes changed means a smaller patch. I shall also continue to use the 'Combi' function which we created in part2. Another reason for this is that you may not always have a glorious 33kb of space in which to play.
Ok then:
#1
This is very very simple. Simply open Target.exe in Resource Hacker (an outstanding program with a few lacking features, such as the ability to update without changing the file size as in ExeScope, and the distinct lack of information about the resource, such as offset and size etc, a recent file list and the ability to just close a file without closing Resource Hacker. Perhaps it could be the next project :-) open the menu resource and type in the following:
MENUITEM "&Open File to Disassemble..", 24332
MENUITEM SEPARATOR
POPUP "Recent Files"
{
MENUITEM " ", 701
MENUITEM " ", 702
MENUITEM " ", 703
MENUITEM " ", 704
MENUITEM " ", 705
}
MENUITEM SEPARATOR
MENUITEM "&Save Disassembly Text File and Create Project File", 24333, GRAYED
Save it. That's it done.
#2
Next we need to load in the current list of recently opened files. Obviously we need somewhere to load them from and as Target.exe uses its own .ini file W32dasm8.ini to store the rest of its setting why not use it. Firstly though we need to decide when we are going to load this file list. A good time to do this so that we don't add extra bytes to the file might be in our Combi function2 that we created in Part2 to send the windows message.
I added the following code to Combi function2:
No_commandline:
mov eax, 004941FBh ;The address where we jumped out of target.exe code
mov dword ptr [eax], 0001D1E9h ;Patched back to normal
INVOKE GetMenu,hinst ;Get the handle of the applications window
mov Hmenu,eax ;Save the handle in Hmenu
mov ebx,offset [Key1] ;address of 1st key name in ini file
mov ecx,5 ;total # of keys
loop1:
push ecx ;save ecx as it gets changed in the API's
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ebx,ADDR DefaultNm,\
ADDR RetVal, SIZEOF RetVal, ADDR IniNm ;Get the ini entry
INVOKE lstrcmpiA,ADDR DefaultNm , ADDR RetVal ;Check if there was anything
.if eax!=0 ;If so
INVOKE ModifyMenuA, Hmenu, MenuID, 0,
MenuID, ADDR RetVal ;Make menu text ini entry text
.ENDIF
inc ebx ;Point ebx to next key address
inc ebx
inc MenuID ;Point to next menu item
pop ecx ;Get ecx back
loopnz loop1 ;Go again if not zero
.ENDIF
popad ; Restore all
ret ; Let's go GPF
The above function reads the five Recent file strings from the
W32dasm8.ini file and if a string is found it then sets the menu item
text to the string value. I am keeping this very simple and very
basic, my menus are normally just spaces showing as blank when they
are empty. I do not add or delete menu items should they be full or
empty, nor do I cater for hotkeys. These are all simple additions
that could be added without difficulty.
I haven't shown my variable definitions above but you can see them in
the source code (sure you can figure that out though :-). The only
other addition I made which I have not shown above was to mov the
window handle into hinst at the start of function2.
Ok we now have our menu but if you run target.exe I am sure you will
very quickly discover a small problem. All our new menu items are
disabled!!.
This little beastie kept me amused for a good couple of hours I can
tell you. At first I thought I had made a mistake somewhere and
checked and double checked everything. Then I added an EnableMenuItem
call to my above function and still they were disabled. After some
consideration I decided that Target.exe must be sabotaging my new
menu's. So I placed a
BPX EnableMenuItem
Ran target and selected a menu item. Softice breaks F11 and we are here:
:0049C5AC 83C408 add esp, 00000008
:0049C5AF 33C0 xor eax, eax
:0049C5B1 84DB test bl, bl
:0049C5B3 7501 jne 0049C5B6
:0049C5B5 40 inc eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0049C5B3(C)
:0049C5B6 0D00040000 or eax, 00000400
:0049C5BB 50 push eax
:0049C5BC FF7614 push [esi+14]
:0049C5BF FF7610 push [esi+10]
* Reference To: USER32.EnableMenuItem, Ord:0000h
|
:0049C5C2 E8232A0100 Call 004AEFEA
:0049C5C7 5E pop esi
:0049C5C8 5B pop ebx
:0049C5C9 5D pop ebp
:0049C5CA C3 ret
The above little function is called every time a menu item is shown, I have no idea how! I tried a breakpoint :
bpx 004A566A IF *(edi+4)==116 || *(edi+4)==117 || *(edi+4)==11F
but nothing showed. (I am not going to explain the above breakpoint
get your API reference and your window.inc out and do some research
:-). I tried tracing the code through but soon tired of that messy
little job. So I still don't know how we get here. How is irrelevant
however, so long as we know that we do. So what does the above
snippet do??
Well dependant on the value in 'bl' it sets all the menu items in the
current POPUP to either enabled or disabled. Again where the hell the
value in 'bl' comes from I don't know and to be honest I don't care.
A quick look around the registers showed the menu item id stored in
edi+4, our way out of this thing:
:0049C5AC 83C408 add esp, 00000008
:0049C5AF 33C0 xor eax, eax
:0049C5B1 84DB test bl, bl
:0049C5B3 7501 jne 0049C5B6
becomes:
:0049C5AC E95FE30400 jmp 004EA910
:0049C5B1 84DB test bl, bl
To take us to our new space :-) then:
:004EA910 817F04D0020000 cmp dword ptr [edi+04], 000002D0 ;cmp to 720d
:004EA917 7902 jns 004EA91B ;jump if bigger
:004EA919 B301 mov bl, 01 ;move 1 into bl if item is one of ours
:004EA91B 83C408 add esp, 00000008 ;replaced instruction
:004EA91E 31C0 xor eax, eax ;and another
:004EA920 E98C1CFBFF jmp 0049C5B1 ;that'll teach it
Ok fixed, all this does is check if the menu item we are processing has an ID greater than 720d which of course mine don't where as poor old Peters all do. Then we just set bl accordingly and let target.exe have control again.
#3
Right now we have some pretty nice looking little menu entries. (At least you do if you edited some file names into W32dasm8.ini like I did. You should put them under a new section [RECENT] with keys 1,2,3,4,5) So next I guess we had better do something with them.
I am sure there must be another way of doing this but after a little
tracing it doesn't appear to me as though there is an easier
way, however I learn more every day so if you see another option
please humor me (and let me know of course).
It is pretty easy to find our way to the normal Wmsg comparison
checks with a
bpx 004A566A IF *(edi+4)==111
Then simply F8 into the calls until we arrive here:
:00452617 C745E8261F4C00 mov [ebp-18], 004C1F26
:0045261E 8B4604 mov eax, dword ptr [esi+04]
:00452621 3D21010000 cmp eax, 00000121
:00452626 7F41 jg 00452669
:00452628 0F84A30D0000 je 004533D1
:0045262E 3D11010000 cmp eax, 00000111
:00452633 7F1B jg 00452650
:00452635 7463 je 0045269A
:00452637 2DA0000000 sub eax, 000000A0
:0045263C 0F84530D0000 je 00453395
:00452642 83E860 sub eax, 00000060
:00452645 0F8493000000 je 004526DE
:0045264B E92F120000 jmp 0045387F
and you recognize the normal checks with :0045262E checking if a
WM_COMMAND was received.
From here on in tracing the route of your messages gets a little more
complex, so why bother? If we stop and think about what we want to do
for a second....
Our menu click will produce a WM_COMMAND with the menu ID in *(edi+8)
what we want to do next is fake an 'Open File' menu command. If we
are smart and place the Recent file string into the command line and
then call the 'Open File' menu our code entered in Part2 of W32Dasm
Disassembled will do the rest for us. So lets give it a go:
:00452635 E9ED820900 jmp 004EA927 --- jmp to our space
:0045263A 90 nop --- keep it tidy
:0045263B 90 nop
Then in our space:
:004EA927 9C pushfd --- save flags
:004EA928 60 pushad --- save registers
:004EA929 817F08BC020000 cmp dword ptr [edi+08], 000002BC --- cmp wparam to 700d
:004EA930 7C26 jl 004EA958 --- if its less its not ours
:004EA932 817F08C6020000 cmp dword ptr [edi+08], 000002C6 cmp wparam to 710d
:004EA939 791D jns 004EA958 --- if its greater its not ours
:004EA93B B8E0F34A00 mov eax, 004AF3E0 --- var5 for w32patch.dll Combi function
:004EA940 C70003000000 mov dword ptr [eax], 00000003 --- function3
:004EA946 83C004 add eax, 00000004 --- point eax to var4
:004EA949 8B5708 mov edx, dword ptr [edi+08] --- wparam
:004EA94C 8910 mov dword ptr [eax], edx --- into var4
:004EA94E 83C004 add eax, 00000004 --- point eax to var3
:004EA951 8938 mov dword ptr [eax], edi --- MSG address into var3
:004EA953 E8A149FCFF call 004AF2F9 --- our Combi calling function
:004EA958 61 popad --- restore registers
:004EA959 9D popfd --- restore flags
:004EA95A 0F843A7DF6FF je 0045269A --- replace the overwritten
:004EA960 81E8A0000000 sub eax, 000000A0 --- instructions and
:004EA966 0F84298AF6FF je 00453395 --- carry on as if nothing had happened
:004EA96C E9D47CF6FF jmp 00452645 --- except the odd GPF of course
As we have all this new found space in the shrunken bitmap memory I have gone crazy with the bytes a little :-). All we are doing in this function is saving all flags and registers then checking to see if the wparam part of the MSG is within range to be one of our menu items. If it is not we carry on as normal, if it is we call Combi function3 then carry on as normal.
So now for Combi function3
.ELSEIF dword ptr[eax] == 00000003h --- Function3
mov ebx,var4 --- Our menu item ID
mov eax,dword ptr[ebx] --- Value into eax
sub eax,2BDh --- Subtract 701d from it
shl eax,1 --- Multiply by 2
mov ebx,offset [Key1] --- 1st ini file key address
add ebx,eax --- Add eax to the address to give correct key
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ebx,ADDR DefaultNm,\
ADDR RetVal, SIZEOF RetVal, ADDR IniNm --- Get the ini entry
INVOKE lstrcmpiA,ADDR DefaultNm , ADDR RetVal --- Check if there was anything
.if eax!=0 --- If value found at the key
mov esi,004D1F8Ch --- Commandline address
mov edi,dword ptr[esi] --- into edi
mov ecx, -1 --- counter -Dolphinz Code
mov al, 0 --- search byte
push edi --- save pointer
repnz scasb --- search for end of pointer
not ecx --- number of bytes read
mov dword ptr[edi-2],00000020h --- clear end if string
pop edi --- start of commandline
INVOKE lstrcatA,edi,ADDR Retval --- Add on our Recent file
mov eax, 004941FBh --- The door from Part2 we closed
mov dword ptr [eax], 01B159E9h --- Lets open it again
.ENDIF
.ENDIF
An explanation:
We first get our menuID from the MSG wparam contained in edi. Then by
subtracting 701 decimal we are left with a value which when
multiplied by 2 and added to the offset of our Key1 address will give
us the offset to the correct Key for the menu item which was
selected. So we then get the string from the ini file using this key.
Next we check if there was a string and that a blank menu item was
not selected. If there was we start processing.
What we do here is load the address for the commandline, seek the
end, set the last character to a space (if it was already a space it
won't make any difference) then we place our ini file string on the
end, creating an artificial commandline. All we do then is re-open
the door which we closed behind us in Part2. If you remember this
will jump out of the main windows message loop into Combi function2.
If there is a commandline it will generate a message to call the
'Open file' dialog code and then shut the door again. We then finally
and simply return control to Target.exe so it can process our current
message, which of course as there is no procedure, it cannot do, so
it will simply ignore it.
Now we have our first big problem. If you run target.exe it will work fine normally, if you load a recent file it will work fine also. However if we try to load another recent file after this we have a problem. The file doesn't load and when you exit target.exe you get a major GPF a complete system reboot. I can see nothing in our code that would cause this so lets debug.
Load target.exe, BPX enablemenuitem, click 'Disassemble', softice breaks. F11 we are in target code. As everything of note that we have done is in Combi place a BPX 004AF2F9 to break just before we enter Combi. F10 F10 and a few F8's and we are in Combi. Now if we trace the code we can watch our command line being built. There is a small problem here:
mov dword ptr[edi-2],00000020h
that each time we do this we erase one character from the end of the commandline. What happens when we run out of characters?? I am not to concerned about this as the minimum characters we could start with are ("c:\W32dsm89.exe") 17 including the "s. As W32dasm is not the sort of program in which you are constantly loading new files (like a text editor) I am prepared to live with this little bug, you could fix it if you like :-). So if we continue on everything seems to work fine, Ctrl+D and we break this time into Combi function1. Once again everything looks fine, but we know that the first recent file load works anyway so we wouldn't expect to see anything. Now if we repeat the process.
Combi function3 works without issue. However when we get to Combi function1 here:
mov esi,004D1F8Ch ; Commandline address
mov edi,dword ptr[esi] ; into edi
If you check our commandline it has become corrupted somewhere along the way. Somebody has thoughtlessly placed a '00' byte in our string part way through. If you edit back in the missing character and CtrL+D everything runs fine. We have our culprit. But how to find him? Well if you repeat the process a couple of times you will see it is always the same address that gets written into, this address is edi+29. So if we run through to the above lines then place a:
BPMD edi+24
Softice will break whenever anybody reads\writes to the dword of which our mischievous '00' address is part. Disable the above breakpoint Ctrl+D and let the recent file load. Ctrl+D and re-enable the breakpoint. Now try loading another recent file. We see kernel reads it then our Combi function3 then we break here:
:0043E23A mov edx, dword ptr [ebx+0063E133] --- Clear filename address
:0043E240 mov byte ptr [edx], 00 --- of any left over strings
:0043E243 mov dword ptr [ebx+0001EB94], 0000004C --- lStructSize;
Recognize this?? you should I just cut and paste it from W32Dasm
Disassembled Part2. This is the setup for the OPENFILENAME structure
prior to the GetOpenFileNameA call. So this is the little sod who is
destroying our commandline.
The question is how to stop him? At first I was thinking of writing
in a live patch and patching it back in when we had done and a few
other over complicated ideas, then it occurred. Sometimes when you
start doing this sort of detailed work it is easy to forget the
simple things, why not just open up our hex editor and 909090 it? it
doesn't do much anyway.
As you may guess this fixes all the problems and we are on our way again.
#4
Our final objective at last :-). We have a working menu but it is not a recent file list, it is just a list of your edited W32Dsm8.ini file. Our final task is to update this list every time a new file is opened. Let's be about it.....
As always firstly we need to think about exactly what we want to do.
If target.exe opens a new file we want to first check that it is not
already on the recent file list, if not we want to place it in
position 1 in the file list and move all the other entries down one.
However we only want to do this if Target.exe opens the file
successfully. Finally we want to update the current menu items with
the recent file list.
So as we want to do all this only if the file is successfully opened a good place to start would be:
BPX CreateFileA
Then open a file, a little bit of the usual F'in (and a little blindin') and we end up here:
* Reference To: KERNEL32.CreateFileA, Ord:0000h
|
:00451C04 E8D1CE0500 Call 004AEADA
:00451C09 83F8FF cmp eax, FFFFFFFF
:00451C0C 7506 jne 00451C14
:00451C0E 33C0 xor eax, eax
:00451C10 EB02 jmp 00451C14
Where target.exe opens the file and then checks if the open was
successful, looks like a good spot to me :-)
So as usual:
:00451C09 83F8FF cmp eax, FFFFFFFF
:00451C0C 7506 jne 00451C14
:00451C0E 33C0 xor eax, eax
Becomes:
:00451C09 E9658D0900 jmp 004EA973
:00451C0E 33C0 xor eax, eax
To jump to our space again, then:
:004EA973 83F8FF cmp eax, FFFFFFFF --- replaced instruction
:004EA976 0F849272F6FF je 00451C0E --- if file not open go away
:004EA97C 60 pushad --- save all as always
:004EA97D B8E0F34A00 mov eax, 004AF3E0 --- Var5
:004EA982 C70004000000 mov dword ptr [eax], 00000004 --- Function4
:004EA988 E86C49FCFF call 004AF2F9 --- Call Combi
:004EA98D 61 popad --- Restore all
:004EA98E E98172F6FF jmp 00451C14 --- Run away
Now for Function4:
.ELSEIF dword ptr[eax] == 00000004h --- Function4
--- Create Path string for new file
INVOKE GetCurrentDirectoryA,SIZEOF New file,ADDR New file
INVOKE lstrcatA,ADDR Newfile,ADDR Backslsh
INVOKE lstrcatA,ADDR Newfile,esi
--- Get the ini entries and compare each to the New file name. If we get a match leave.
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ADDR Key1,ADDR DefaultNm,\
ADDR RetVal, SIZEOF RetVal, ADDR IniNm
INVOKE lstrcmpiA,ADDR Retval,ADDR Newfile
test eax,eax
je Already_Exists
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ADDR Key2,ADDR DefaultNm,\
ADDR RetVal1, SIZEOF RetVal1, ADDR IniNm
INVOKE lstrcmpiA,ADDR Retval1,ADDR Newfile
test eax,eax
je Already_Exists
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ADDR Key3,ADDR DefaultNm,\
ADDR RetVal2, SIZEOF RetVal2, ADDR IniNm
INVOKE lstrcmpiA,ADDR Retval2,ADDR Newfile
test eax,eax
je Already_Exists
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ADDR Key4,ADDR DefaultNm,\
ADDR RetVal3, SIZEOF RetVal3, ADDR IniNm
INVOKE lstrcmpiA,ADDR Retval3,ADDR Newfile
test eax,eax
je Already_Exists
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ADDR Key5,ADDR DefaultNm,\
ADDR RetVal4, SIZEOF RetVal4, ADDR IniNm
INVOKE lstrcmpiA,ADDR Retval4,ADDR Newfile
test eax,eax
je Already_Exists
--- If there was no match then write the new ini entries
INVOKE WritePrivateProfileStringA,ADDR SectionNm,ADDR Key1,ADDR Newfile,ADDR IniNm
INVOKE WritePrivateProfileStringA,ADDR SectionNm,ADDR Key2,ADDR RetVal,ADDR IniNm
INVOKE WritePrivateProfileStringA,ADDR SectionNm,ADDR Key3,ADDR RetVal1,ADDR IniNm
INVOKE WritePrivateProfileStringA,ADDR SectionNm,ADDR Key4,ADDR RetVal2,ADDR IniNm
INVOKE WritePrivateProfileStringA,ADDR SectionNm,ADDR Key5,ADDR RetVal3,ADDR IniNm
--- Code copied from function2 to update the menu entries
mov ecx,004AF3FCh --- Get our Hwnd
mov eax,dword ptr[ecx] --- into eax
INVOKE GetMenu,eax --- Get the handle of the applications window
mov Hmenu,eax --- Save the handle in Hmenu
mov ebx,offset [Key1] --- address of 1st key name in ini file
mov ecx,5 --- total # of keys
loop2:
push ecx --- save ecx as it gets changed in the API's
INVOKE GetPrivateProfileStringA,ADDR SectionNm,ebx,ADDR DefaultNm,\
ADDR RetVal, SIZEOF RetVal, ADDR IniNm --- Get the ini entry
INVOKE lstrcmpiA,ADDR DefaultNm , ADDR RetVal --- Check if there was anything
.if eax!=0 --- If so
INVOKE ModifyMenuA, Hmenu, MenuID, 0,MenuID, ADDR RetVal --- menu text=ini text
.ENDIF
inc ebx --- Point ebx to next key address
inc ebx
inc MenuID --- Point to next menu item
pop ecx --- Get ecx back
loopnz loop2 --- Go again if not zero
Already_Exists:
.ENDIF
Th th th th that's all folks!
We have completed Part3!!
In Conclusion |
I refer quite heavily to my previous two essays throughout this one. Also until now each essay has been a standalone capable of being installed independent of the others and working successfully (fingers crossed). In this essay though much of the code is intermingled with the code of the previous two essays. For this reason you must have the previous patches installed to follow and complete this essay, indeed if you do not your system will crash!
For this reason the patch I will provide with this essay will patch in all three added functions. It will not do a byte comparison however, so if you already have the first two patches installed it will just repatch over them without problem.
Final Notes |
Just as a point of interest, and this is probably something you already know, I discovered quite by accident that if you drag a file in explorer and drop it on Target.exe as modified in part2 then the file is automatically opened. I guess this works for any program which is passed files on the command line. Amazing! here I am taking programs to pieces and rebuilding them and I don't even know some of the features of my own operating system. Like I say, everybody has something to learn, some of us have more than others :-)
That's it for just now, I have many plans for Part4 ..... but I am taking a little break from this (got some real life going on) so I won't drop any hints as to what they are. Hope you have enjoyed so far, hope mayby I have helped in some way. Any comments, questions mail me: Harlequin00 AT cjb . net
Please go out and buy W32Dasm, I am sure that if more people start paying for using it Peter Urbanik will add all these and the many more features himself. Perhaps Peter should indeed contact Fravia+'s board and see if someone can't put together a decent protection for W32Dasm, a program this good deserves it.
Thanks go to: RalDnor for saying nice things to me and for posting my essays.
Everybody out there writing essays! I wouldn't be
here without them. Peter Urbanik for such a great tool
Bill Gates for GPF's. Never of any use until I
started this reversing stuff, now I couldn't do it without them.
|
Essay
by: Harlequin
Page
Created: 4th December 2000