A Quick little Byte Editor
About Quick Edit
Ever get one of those sillly little programs which require only a
simple mov al,01 or changing a je to jne etc. You have to fire up
your hex editor find the location, change the bytes, save it, search
the file on disk and run it. Well Quick edit makes this process
It is very simple to use.
Enter the RVA of the bytes you wish to edit as shown in the W32Dasm Deadlisting. You must enter leading zeros so that the value is 8 digits long.
Clicking 'GoTo' will then open the file and display the bytes at this address.
You should note that if a file Pfilename exists Quick edit will open this instead of the filename. This does mean that the bytes may not be the same as those in your W32Dasm listing but does mean that you can add to already existing patches. If the file Pfilename does not exist then Quick Edit opens the actual program (filename).
The two edit boxes are then updated with the bytes found at the RVA
in the file. The top edit box is read only and will not change. This
is here simply in case you make a mistake in your editing, you can
then see what the original bytes were.
The lower editbox is where you make your changes. Note that you can only change bytes, you should not add them, if you do they will be written and the file will be corrupt.
When the bytes are loaded the 'Run' button becomes enabled. Clicking
this will run the program as it is, unedited!
Clicking the 'Save' button saves your changes into the file Pfilename.
Once you have clicked the 'Save' button the 'Run' button will now run the modified file Pfilename not the original.
You can make several changes at a time by simply changing the RVA and
pressing 'GoTo' again. Note that the file is loaded each time you do
this so if you make a change then click the GoTo button again with
the same address the displayed original bytes will be updated with
your changed bytes.
As I said it's pretty simple really.
This series is not intended to increase in difficulty rather it is
just a reflection of my ideas and their implementation as and when I
execute them. This essay is pretty simple, the 'Quick Edit' code I
wrote separately and most of it was just a case of copy and paste
from the W32Patch Part1 code. I created the dialog box in BRW, I
tested it using another little program I wrote for this purpose. It
was very simple and was written as a tool for my own use, however if
I find it useful there is always the chance someone else might so
here it is :-)
I wanted this Part to be completely independent of the previous Parts. So to do this I thought I would keep well away from any of the code we created in Parts1-3. In order to keep away from previous code I considered it was better to replace an existing function rather than create a brand new one. I decided to replace the useless and ugly About box function. Implementing the whole thing was also very simple and quick so in order to (have something to write ;-) demonstrate something different I made it all more complicated for myself.
I decided to use an approach I learned from an excellent essay by Extasy, a method of implementing your dll and functions without having the LoadLibrary or GetProcAddress API's to lean on.
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.
So first of all you should disassemble Target.exe using W32Dasm and save the deadlisting.
Add our own DLL and Procedure to the import table
To my knowledge there are only four ways to achieve our objective #1 it is always useful to have all these at our disposal though as often one way may not be possible within a specific target. The other three ways are:
Load the DLL and procedure using the LoadLibrary and GetProcAddress which already are made available by the target program. This is the method I used in parts 1-3 of W32Dasm Disassembled.
Use the snippet of code provided by NeuRaL_NoiSE in his Hnotepad essay pack. This snippet was created by Jack Qwerty of the virus group (29A). It is basically a replacement for the LoadLibrary and GetProcAddress functions. Compiled it produces a very small procedure which can be pasted into your target. This procedure accepts the DLL name and Procedure name as parameters the opens and searches the DLL retrieving a handle to the Procedure. Ingenious! There are some very clever people about.Where there is a problem there is always somebody capable of a solution, such is the nature of the human race.
Extasy's method. This adds our dll name and function name to the import names list then prepends the relevant addresses to the import directory and subtracts the number of bytes added from the import table address.
Unfortunately Extasy's method does not work with every target and as
you may have guessed it doesn't work with target.exe. This is because
the import directory in target.exe begins at the .idata section
boundary. If we prepend data to the import directory we are then
placing this data in the .INIT section. This is obviously not
favorable, I tried this method and then shrinking the .INIT section
and expanding the .idata section etc etc but I could not get it to work.
My next thought was to append the info to the import directory but as you can see there is no space for this in target.exe. So next I tried inserting the number of bytes needed (14h) into .idata and deleting the same number of bytes from the end of .idata. As the array tables are in the same section I had to adjust all the RVA's by 14h. This didn't work either, although I am not sure why, I may spend more time on this one day.
So next I thought, if I cannot get data before the directory and I cannot get data after the directory why not just move the whole directory to a place where I can?
The first thing we need is the address of the import table, this you
can find manually by reading through the PE header should you so
desire but with so many tools which will do it for us why bother, I
used procdump. You should know how to find it manually though, and
indeed you should read a few of the many PE header descriptions
available until you are familiar with PE files. This sort of stuff
becomes a lot easier when you know what is going on.
In target.exe we find the Import table at RVA 4D4000. Now as we are going to be using a lot of RVA values and converting them to file offsets I would suggest using one of the excellent tools for doing this, check out protools.
So if we convert the RVA to an offset we get D1C00h the beginning of the import table.
The import directory layout has the following format:
typedef struct tagImportDirectory
DWORD dwRVAFunctionNameList; --- 004D408Ch
DWORD dwRVAModuleName; --- 004D2000h
DWORD dwRVAFunctionAddressList; --- 004D45E4h
The values shown in yellow are the RVA's for the first DLL (Kernel32)
in target.exe. This format is then repeated for each dll included in
The table is terminated with a complete entry (5 DWORDS) all of '00000000' bytes.
So our table begins at offset D1C00h and ends at D1C8Bh a total of
140 bytes, lets move it. I use Uedit32 for this, selecting all 140
bytes, copy, then at offset D1B00h paste. This of course adds 140
bytes to your file so place the cursor at D1B00h then menu -
Select delete, set to 140 and click ok, and there they are gone.
Now if we want to make this our new import directory we need to change the pointer to it in the PE header you can use procdump or something if you like, I prefer to do it manually. We find the import directory address at offset 180h.
As we are crossing section boundaries we cannot just subtract 100bytes from this value, it is easier to use your offset calculator to convert offset D1B00h to RVA 4D3700h, insert this value at 180h and we are ready.
So we want to add our DLL name to the list. It is probably not
a good idea to add the name to the beginning of the names list as
this list starts at offset D0400h. As this is a nice round number it
could well be a section boundary, I don't know I didn't check.
Instead I decide to place my dll name in the nice little bit of space
preceding the import table and just after the existing procedure list.
So at offset D1A90h edit in our dll name W32Patch.dll.
Now the directory structure uses five DWORDS for each dll so ours will start at offset D1B78h and the pointer to our dll name will be the fourth Dword at offset D1B84h. So edit in the RVA to our new dll name 90360D00h and we have a pointer to our dll. Note we do not add the image address (400000h) this is added at runtime. Next we need to point to our procedure.
The remaining two addresses in the directory don't actually point to our procedure name they point to two arrays of addresses, each of which points to the procedure name. So we not only need to insert out name we also have to create these arrays. The two arrays are identical on disk and you might think to cut corners and make only one array for your new functions. Don't do this! the first array will always remain as you see it on disk, this serves as a reference during runtime should a problem occur with the loaded data. The second array becomes the loaded data. When the program loads, the RVA's in this array are replaced with VA's to the actual procedure list. This information is then placed in the data section of your program so the procedures can be accessed.
Firstly lets enter our procedure name 'ByteEd'. Remember that your dll name must terminate with a '00' byte as per a normal string, you should also know that each procedure name is proceeded by two '00' bytes. This I believe is where the procedure ordinal value is stored at runtime, although I am not sure. It is important to include them though and you should also note that these two preceding bytes are the start of the procedure name as far as addressing is concerned, they are not just surplus. So we leave three bytes at the end of our dll name and insert our procedure name at offset D1A9Fh.
Now to create the arrays to point to the name. Leaving two '00' bytes at the end of the name insert our procedure RVA 000D369Dh (including the two '00' bytes remember) reversed (9D360D00h) at offset D1AA8h (I start them here because it is neater and often better to start on a Dword boundary where possible). This is our first array. The application knows it has reached the end of an array when it encounters a zero value, so to terminate this array we must leave a '00000000' Dword before starting array 2 at offset D1AB0h. This of course is the same value as array 1.
Good we are done now we have to update the import directory to point to these arrays.
So the first DWORD of our directory entries at offset D1B78h becomes A8360D00h to point to the RVA of our array1. The fifth DWORD at offset D1B88h becomes B0360D00h to point to the RVA of our array2.
That is all there is to it we have added our dll and function to the import table.
Our final table should look like this:
000d1a90h: 57 33 32 50 61 74 63 68 2E 64 6C 6C 00 00 00 42 ; W32Patch.dll...B 000d1aa0h: 79 74 65 45 64 00 00 00 9D 36 0D 00 00 00 00 00 ; yteEd...6...... 000d1ab0h: 9D 36 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 6.............. ......... 000d1b70h: 3C 20 0D 00 2C 4B 0D 00 A8 36 0D 00 00 00 00 00 ; 000d1b80h: 00 00 00 00 90 36 0D 00 B0 36 0D 00 00 00 00 00 ;
To call this new procedure from within the target.exe you simply call the address of the variable in array2 which points to its name. So for our procedure we would use:
Call dword ptr [004D36B0]
So lets make use of our new procedure and insert Quick Edit into target.exe.
Open target in Resource Hacker and change the menu item 'About' to
'Quick Edit' then save it.
Now we need to locate the about box function. In your deadlisting use the dialog references to bring you here:
:0045258B 55 push ebp
:0045258C 8BEC mov ebp, esp
:0045258E 81C430FFFFFF add esp, FFFFFF30
:00452594 53 push ebx
:00452595 8B5D08 mov ebx, dword ptr [ebp+08]
:00452598 B81C134B00 mov eax, 004B131C
:0045259D E866580500 call 004A7E08
:004525A2 66C78540FFFFFF0800 mov word ptr [ebp+FFFFFF40], 0008
:004525AB 6A00 push 00000000
:004525AD 83C4FC add esp, FFFFFFFC
* Possible Reference to Dialog: DialogID_55F0
:004525B0 C70424F0550000 mov dword ptr [esp], 000055F0
:004525B7 8B5333 mov edx, dword ptr [ebx+33]
W32Dasm gives us no idea how we came here but who cares this is our function lets patch :-)
The first thing we need to do is to find a memory address with the current filename and file directory as these will be passed as parameters to Quick Edit. To do this I ran W32Dasm, opened target.exe, loaded it, put a breakpoint on 0045458Bh and loaded a file into target.exe. I then clicked the About menu item and we break. Now I had an advantage here as I didn't have to search for the filename in memory, I recognized the base address from part1 stored in EDI. So I checked my part1 essay and added the required values to the base address and bingo, sure enough there were the filename and directory name I was looking for. So armed with all I need lets go, in W32Dasm's Patch window I enter the following:
:0045258B 60 pushad --- Save all registers as normal
:0045258C 81C14F066400 add ecx, 0064064F --- Base address already in ecx, Lucky eh?
:00452592 51 push ecx --- Push pointer to filename
:00452593 89F9 mov ecx, edi --- Base address into ecx again
:00452595 81C131016F00 add ecx, 006F0131 --- Directory string
:0045259B 51 push ecx --- Push it
:0045259C FF15B0364D00 call dword ptr [004D36B0] --- Call our function
:004525A2 61 popad --- Reset registers
:004525A3 C3 ret --- Break out the beer
and that is all there is to it we now have our Quick Edit function.
As you can see this procedure was very very simple, all you really need is an understanding of the PE file structure. It shows that there are usually several options to achieve our objective and if your first line of attack does not work on a target don't give up, get imaginative, there is invariably an alternative.
This little adaption functions completely independent of any of the previous parts of the W32Dasm disassembled essays. It will also not effect any other patches, such as dreads VB string patch. You do get my little bitmap in the patch though, you can allow me that surely ;-)
Don't even know if anybody is reading any of these essays? Some
feedback would be nice! I shall stop writing them if they are of no
use to anyone :-(
Mail me at: Mail @ Harlequin00 . cjb . net
Thanks go to:
+Tsehp for posting my essays.
Everybody out there writing essays! I wouldn't be
here without them.
Peter Urbanik for such a great tool
Page Created: 20th December 2000