Solution to douby's ReverseMe 1 by Dracon (DREAD) Tools used: ----------- * IDA 3.85 (Disassembler) * HIEW (hexeditor which can generate Opcodes) * SoftIce (to debug my new code) * Win32 Api reference * alphabetic list of important Api constants (very handy) Also usefull are: * SAdd (add new sections of any size to an exe) * ProcDump (has an integrated PE-Editor) * OpGen (Opcode Generator to create the opcodes for jumps from one virtual address to another AND it can retrieve the addresses of all imported windows-functions) First of all I must say that douby's idea of a "ReverseMe" is great. Adding new functions this way makes your best tools and programs even better. It's not easy though. You must know something about the PE file-format and of course win32asm. I also recommend that you read the essay about adding functions to Notepad by NeuRaL_NoiSE. The challenge was (sorted, easiest first): 1) Add a scrollbar to the editbox 2) Add the exit option to the program 3) Add the loading option to the program 4) Add the saving option to the program (easy if you solved #3) Let's start! 1. Scrollbar ============ This one is easy. CreateWindowEx or CreateWindow are used to create an edit control, the one used in the ReverseMe. This is the snippet from IDA (in WDasm it looks the same, but without the ".text:", only the address is shown): .text:0040116D mov edx, [esp+8+HWND] ; move HWND to edx .text:00401171 push 0 ; pointer to some data .text:00401173 mov ecx, [eax+4] .text:00401176 push ecx ; hInstance .text:00401177 push 1 ; child ID .text:00401179 push edx ; parent window .text:0040117A push 0 ; initial height .text:0040117C push 0 ; initial width .text:0040117E push 0 ; initial y position .text:00401180 push 0 ; initial x position .text:00401182 push 508000C4h ; window style !!! .text:00401187 push 0 ; window name .text:00401189 push offset aEdit ; "EDIT" - class name .text:0040118E push 0 ; extended styles .text:00401190 call ds:CreateWindowExA ; Create the window Remember, all parameters are pushed in reverse order, the last one first. To get the scrollbars we must modify the style parameter - all styles are combined by a logical OR. I have already looked up the values for WS_HSCROLL (0x100000) and WS_VSCROLL (0x200000) for you, so we calculate: 0x508000C4 OR 0x100000 OR 0x200000 = 50B000C4 file-offset 0x1182: 68 C4 00 80 50 change to: 68 C4 00 B0 50 Short test, okay, works. ;-) All constants can be found in the include files (win32.inc, user32.inc, ...) and someone has created html-pages with all important constants in alphabetic order which is VERY handy. 2. Exit menu entry ================== Now it's getting more difficult. If you click on a menu-entry the program will get a WM_COMMAND message with the id in the low-order word of WPARAM. Fortunately, douby decided to show us a messagebox "Nope, doesn't work yet!". Fine. Look for this error string and examine the code above it. I have already renamed some of the location and variables to make the code clearer. Other comments are inserted by IDA, e.g. for the switch jump: .text:004011CE WM_COMMAND_msg: ; CODE XREF: WindowProc+12 .text:004011CE cmp esi, 7 .text:004011D1 jz short WM_SETFOCUS_msg .text:004011D3 cmp esi, 111h .text:004011D9 jnz short DEFAULT_msg ; default .text:004011DB push edi .text:004011DC call ds:GetMenu ; Get the handle of the ; menu assigned to the ; given window .text:004011E2 mov eax, ebx .text:004011E4 and eax, 0FFFFh .text:004011E9 add eax, 0FFFF63BFh ; switch 5 cases .text:004011EE cmp eax, 4 .text:004011F1 ja short DEFAULT_msg ; default ; ----> Important <---- .text:004011F3 jmp ds:off_0_401260[eax*4] ; switch jump for ; Command IDs .text:004011FA loc_0_4011FA: ; case 0x9c41 .text:004011FA push 0 .text:004011FC push offset a3rr0r ; "3rr0r!" .text:00401201 push offset aNopeDoesnTWork ; "Nope doesn't ; work yet" .text:00401206 push edi .text:00401207 call ds:MessageBoxA .text:0040120D pop edi .text:0040120E pop esi .text:0040120F xor eax, eax .text:00401211 pop ebx .text:00401212 retn 10h Look at the line that I have marked. The next jump depends on the ID, here is the table for it (at location 401260): .text:00401260 dd offset loc_0_4011FA ; IDM_LOAD .text:00401260 dd offset DEFAULT_msg ; default .text:00401260 dd offset loc_0_4011FA ; IDM_EXIT .text:00401260 dd offset IDM_ABOUT ; IDM_ABOUT .text:00401260 dd offset loc_0_4011FA ; IDM_SAVE As you can see, 3 jumps go back (4011FA) and display our messagebox, one shows the Aboutbox and one jumps to DefWinProc. Okay, how can we know WHICH number belongs to the different IDs? I have already done the whole work for you: you can modify the jump table in that way that only ONE value will show the error message (0x4011FA), all others are set do DEFAULT_msg (0x403712). Run the program and click on the menu items. When you see the messagebox you know which entry in the jump table belongs to it. Got the idea? Good. Another approach would be to open the program with BRW (Borland Resource Workshop) and to examine the menu. The IDs should be right in front of you. Now you know when the user chooses "Exit" from the menu. How you exit a program? Very easy, you call PostQuitMessage(0). You could now add some code to do this, but wait a moment. There should already be a call to this function somewhere. Found it? .text:00401159 push 0 .text:0040115B call ds:PostQuitMessage .text:00401161 pop edi .text:00401162 pop esi .text:00401163 xor eax, eax .text:00401165 pop ebx .text:00401166 retn 10h Fine, we store now this offset in the jumptable (3rd entry, file-offset 0x1268): FA 11 40 00 -----> 59 11 40 00 I hope you know why to store the address this way. On all Intel computers, words and dwords will be stored with the lowest byte first: 00 40 11 59 -> 59 11 40 00. That's how these values are read and written from and to memory. Test it, and it works! 3. File loading =============== Now we get to the tricky parts. A filedialog should prompt the user for a file which will then be loaded. The problem is, the necessary API functions aren't included yet. We will use "GetProcAddress" to get the address of these functions at runtime. If the dll is already loaded (e.g. kernel32.dll for File I/O) you can use "GetFileModuleHandleA" to retrieve the handle. If the dll is NOT loaded (comdlg32.dll for the common dialogs) you will need "LoadLibraryA". An important question: where do we add our code? The (executable) code is in the "text" section of the file. Use a tool like IDA, PE-Browser or ProcDump to check it: Virtual Size = 0x2B3E, Section Size in File: 0x3000. What does this mean? Only 0x2B3E bytes are needed for the code, but 0x3000 bytes are reserved because of the alignment setting. Set the Virtual Size to 0x3000 and you have 0x4C2 (1218) bytes for your code. This should be more than enough! How do you change this value? Open the file with a hexeditor and look for "text" (offset 0x1C9). Read a good doc about the PE file header to find out more (Microsoft, Iczelion). The first 8 chars are reserved for the name of a section, after that follows the virtual size (WORD) - change it from 3E2B to 0030. Some more explanation: ProcDump is a very good tool which can modify an exe-file in different ways. It can change the pe-header for you. Run ProcDump, click on "PE Editor" and choose the ReverseMe. We are interested in the "Sections". You will get a list of all sections and you can right-click to edit them. The code is in the text-section. Two values are important: the virtual and the physical size. The physical size depends on the file alignment setting (Visual C++ uses 1000h, but it can be any multiply of 200h), the virtual size tells how much space is needed for the code and is important for the Windows PE-loader which loads the exe into memory and executes the code. If we set the virtual size to the physical one we can use the unused bytes for our purpose: in our case the virtual size is 0x2B3E and we can use the bytes from 0x2B40 (to have a rememberable value) to 0x2FFF (at 0x3000 starts the next section) for our code. 1218 bytes are really a lot, but often you will have less, maybe too less. In this case you will need the program "SAdd" by NeuRaL_NoiSE. It creates, appends and zero pads a new section to a dll or exe, you only have to specify a name and the size. The only problem is that (according to NeuRal_NoiSE in his essay) HIEW can't assemble a jump to this section, you must use sice to get the opcodes. There is also a nice program, called "OpGen" by NeuRal_NoiSE himself, who can calculate the opcodes of every jump: you feed it with the starting and the ending virtual address and it will give you the opcodes. For jumps within a section you can use HIEW: enter "jmp xxxxxx" where xxxxxx is the PHYSICAL offset of the destination, don't use the virtual one here! Let's return to the ReverseMe. We need to allocate memory for a buffer to hold the filename and later for the whole file. If you look at the import-table of the exe you will find 2 functions for this task: "VirtualAlloc" reserves or commits a region of pages in the virtual address space of the calling process. Only whole 64k pages are allocated, so if you need 10 bytes you will actually get 64k. Our edit-control is (according to the helpfile) limited to 0xFFFF bytes (64k-1), but this is NOT true, the limit is smaller. You can use "EM_GETLIMITTEXT" to get the exact value, I used 60000 which seems to be okay. Nevertheless, we will allocate 64k which are first used to retrieve the filename from the FileDialog and later to read the file into memory. The exact parameters for "VirtualAlloc" are: lpAddress: can be NULL dwSize: we allocate 64 kBytes flAllocationType: we choose MEM_COMMIT (0x1000) flProtect: PAGE_READWRITE (0x4) is okay When we don't need the memory anymore, it will be freed with "VirtualFree": lpAddress: the Address of our memory dwSize: must be 0 dwFreeType: MEM_RELEASE (0x8000) Now we need some space for the OPENFILENAME structure. Again, we use VirtualAlloc to allocate the necessary memory (sizeof(OPENFILENAME) = 76 bytes) and later VirtualFree to free it. It's necessary to initialize the structure. The allocated memory contains only 0s, this saves us some typing: - size of the structure (important) - parent window (can be NULL, or you use [esp+10] which is the current handle, look below for more about it) - hInstance (only necessary if we use a custom template, can be 0) - pointer to a filter (maybe later...) - pointer for custom filter (not important - NULL) - size of custom filter (not important - NULL) - index of current filter (not important - NULL) - pointer to filename: very important, we will put our pointer here - size of file-buffer: use 512 bytes (200h), should be enough although we have allocated 64k - FileTitle, size of FileTitle, Initial Dir and Title can all be NULL - Flags: OFN_FILEMUSTEXIST (0x1000) is nice - the other members are also unimportant Where do we store the variables? The reference says, that only the registers EBX, EDI and ESI don't change if you call Win32-Api functions, all others could be modified. 3 registers are enough for now, but if you need more you should consider using the stack. We have also another option. The memory of the OPENFILENAME structure will be used only once, later we can use it to store our variables in it, e.g. if we save the offset of OPENFILENAME in esi you can use [esi+xx] to access the memory. Anything more? Yes, we will need the names of the functions which should be loaded with "GetProcAddress". I will put these zero-terminated strings at offset 0x3F00, you can also use the data-section for it. Do what we have done with the text-section: compare the virtual with the physical size, adjust the virtual one and you are ready to store more data in it. We have enough space, so there is no need for this. comdlg32.dll (virtual offset 0x403F00) kernel32.dll (0x403F0D) CloseHandle (0x403F1A) CreateFileA (0x403F26) GetOpenFileNameA (0x403F32) ReadFile (0x403F43) user32.dll (0x403F4C) GetDlgItem (0x403F57) SetWindowTextA (0x403F62) The code starts at offset 0x3B40. You can use HIEW to enter it, but be carefull with the calls like "VirtualAlloc". The address for these procedures must be looked up in the disassembled file. Search for the functions and you will see its addresses: VirtualAlloc: 0x00404034 VirtualFree: 0x0040408C LoadLibraryA: 0x00404028 GetProcAddress: 0x0040402C GetModuleHandleA: 0x00404044 Another way to get these addresses is to use "OpGen" once more. Click on "Lookup Imports for patching" and the program will give you a list of all imported functions and its addresses. Now you can use them in HIEW like this: Function: VirtualAlloc address: 0x404034 enter in HIEW: call d,[00404034] HIEW will show you (as a comment) that you actually call VirtualAlloc from Kernel32.dll. Let's have a look at the asm-code: ; Handle IDM_LOAD : starting at offset 0x3B40 ; ; allocate memory for filename buffer push 4 ; PAGE_READWRITE push 1000h ; MEM_COMMIT push 10000h ; 64k push 0 ; let Windows decide call VirtualAlloc mov edi, eax ; store pointer in edi ; allocate memory for OPENFILENAME push 4 push 1000h push 4Ch ; 76 bytes push 0 call VirtualAlloc mov esi, eax ; store pointer in esi ; initialize the important members mov [esi], 4Ch ; size of structure mov eax, [esp+10h] ; this is the parent HWND mov [esi+4], eax mov [esi+1Ch], edi ; pointer to filename buffer mov [esi+20h], 200h ; size of buffer, 200h is enough mov [esi+34h], 1000h ; flags: OFN_FILEMUSTEXIST ; load comdlg32.dll ; I don't free it, this should be done at program exit push 403F00h ; offset "comdlg32.dll" call LoadLibraryA mov ebx, eax ; store handle in ebx ; get address of GetOpenFileNameA push 403F32h ; offset "GetOpenFileNameA" push ebx ; dll-handle call GetProcAddress ; call the function, only 1 parameter push esi ; offset OPENFILENAME structure call eax ; GetOpenFileNameA ; has the user choosen a file? or eax, eax ; eax == 0 ? jz FINISH ; yes, jump to end - use 0x3DA0 Wouldn't it be nice to see the filename in the titlebar? Let's do it. We will need the handle for "user32.dll" and the address of "SetWindowTextA" also later, so we store them once they are retrieved. The OPENFILENAME structure won't be used anymore, so we use this memory, you must only keep track where you put all your variables. ; get handle to "user32.dll" push 403F4Ch ; offset "user32.dll" call GetModuleHandleA mov [esi+20], eax ; store handle for later use ; get address of SetWindowTextA push 403F62h ; offet "SetWindowTextA" push eax call GetProcAddress mov [esi+24], eax ; store address for later use ; call function push edi ; should contain the filename push [esp+14] ; push HWND call eax ; SetWindowTextA The user has choosen a file and we will now open and load it. I use "CreateFileA" and "ReadFile", please check the doc for the exact parameters. As mentioned above, only about 60000 bytes will be loaded, because the edit-control can't show more, and even if you load this amount you won't be able to edit the file until you delete some text. You can use "EM_SETLIMITTEXT" to increase the limit to a max. of FFFFh. ; open the file ; get library handle for kernel32.dll push 403F0D ; offset "kernel32.dll" call GetModuleHandleA mov ebx, eax ; store it in ebx ; get address of CreateFileA push 403F26h ; offset "CreateFileA" push eax call GetProcAddress ; call CreateFileA ; check its parameters in the doc push 0 ; hTemplateFile push 8000080h ; FILE_ATTRIBUT_NORMAL, FILE_FLAG_SEQUENTIAL_SCAN push 3h ; OPEN_EXISTING push 0 ; Security Attributes push 1 ; FILE_SHARE_READ is allowed push 80000000h ; GENERIC_READ push edi ; our file call eax ; CreateFileA cmp eax, -1 ; INVALID_HANDLE_VALUE (-1) ? je FINISH ; yes, do nothing, jump to end ; store handle in memory of OPENFILENAME mov [esi], eax ; get address of ReadFile push 403F43h ; offset ReadFile push ebx ; kernel32.dll handle call GetProcAddress ; set up parameters push 0 ; overlapped structure mov ecx, esi ; next parameter is a pointer for read bytes add ecx, 4 ; I use esi+4 for it push ecx push EA00h ; number of bytes to read (ca. 60000) push edi ; offset of buffer to receive data push [esi] ; filehandle call eax ; ReadFile ; close file, we don't need the handle anymore push 403F1Ah ; offset CloseHandle push ebx ; kernel32.dll handle call GetProcAddress push [esi] ; filehandle call eax ; CloseHandle ; now check how many bytes we have read mov eax, [esi+4] or eax, eax ; test if 0 jz FINISH ; do nothing if no bytes read The file is now loaded and we want to display it. It's necessary to know the windows-handle of the edit-control. Do we know it? Look at the (disassembled) code for the messagebox "Nope doesn't work yet." The first parameter is HWND (last one pushed), and we see: it's stored in edi. If you scroll up a little bit you will find that [esp+10] is the location where the HWND is right now. This is the handle of the main window though. I have used a spy to get the handle of the edit-control and it was always HWND+4, but there is a cleaner approach. You can use "GetDlgItem" to retrieve the windows-handle. You need the HWND of the parent window and the ID of your child-window or dialog-control. The ID of the edit-control we can find out if we look at the "CreateWindowEx" code once more (I have explained every parameter in the Scrollbar-section) - it's 1. If we have the handle we can use "SetWindowTextA" which actually sends a WM_SETTEXT message to our control. ; get HWND of edit-control via GetDlgItem push 403F57h ; offet "GetDlgItem" push [esi+20] ; handle of "user32.dll" call GetProcAddress push 1 ; ID of edit control push [esp+14] ; parent window is stored in [esp+10] ; but we have already pushed a DWORD, so ; HWND is now in [esp+10+4] call eax ; GetDlgItem mov ebx, [esi+24] ; address of "SetWindowTextA" push edi ; address of filebuffer push eax ; HWND of edit-control call ebx ; SetWindowTextA jmp FINISH After writing this code I found out that you can also use "SetDlgItemText" to set the text of the control. This function does exactly the same and is shorter to code. :) The parameters are almost the same, you need the HWND of the parent window, the ID of your control and the pointer to the new text. I leave this as an exercise for you. Now we are ready. Only some cleanup has to be done: free all memory allocated with VirtualAlloc, pop the registers edi, esi and ebx, and finally return. This finishing code starts at offset 0x3DA0, so jmp (je, jz) FINISH means that you must enter "jmp 03DA0" in HIEW. It will generate the right opcode for this jump. FINISH: ; free memory push 8000h ; MEM_RELEASE push 0 push esi ; OPENFILENAME call VirtualFree push 8000h push 0 push edi ; filename buffer call VirtualFree pop edi pop esi pop ebx ret 10h Puh, that was much code. Does it work? YES!!! Great. I must say that it's much work to do something like this, you must be very carefull (and concentrated) while adding the code and the right offsets in HIEW. 4. File saving ============== Now that we have figured out how to load a file it shouldn't be a problem to save it. You must (again) allocate memory, initialize the OPENFILENAME structure, call GetSaveFileNameA, create a file, get the contents of the edit-control (this time with "GetDlgItemTextA") and save everything with "WriteFile". Here is the code, starting at offset 0x3C70. Don't forget to set the entry of the jumptable (0x1270) to 0x403C70! After reading the previous part you shouldn't have any problems to understand this one as well. ; allocate memory for filename buffer push 4 ; PAGE_READWRITE push 1000h ; MEM_COMMIT push 10000h ; 64k push 0 ; let Windows decide call VirtualAlloc mov edi, eax ; store pointer in edi ; allocate memory for OPENFILENAME push 4 push 1000h push 4Ch ; 76 bytes push 0 call VirtualAlloc mov esi, eax ; store pointer in esi ; initialize the important members mov [esi], 4Ch ; size of structure mov eax, [esp+10h] ; this is the parent HWND mov [esi+4], eax mov [esi+1Ch], edi ; pointer to filename buffer mov [esi+20h], 200h ; size of buffer, 200h is enough ; load comdlg32.dll push 403F00h ; offset "comdlg32.dll" call LoadLibraryA mov ebx, eax ; store handle in ebx ; get address of GetSaveFileNameA push 403F71h ; offset "GetSaveFileNameA" push ebx ; dll-handle call GetProcAddress ; call the function push esi ; offset OPENFILENAME structure call eax ; GetSaveFileNameA ; has the user choosen a file? or eax, eax ; eax == 0 ? jz FINISH ; get handle to "user32.dll" push 403F4Ch ; offset "user32.dll" call GetModuleHandleA mov ebx, eax ; store handle in ebx ; get address of SetWindowTextA push 403F62h ; offet "SetWindowTextA" push eax call GetProcAddress ; call function push edi ; should contain the filename push [esi+4] ; push HWND call eax ; SetWindowTextA ; get address of GetDlgItemTextA push 403F82h ; offet "GetDlgItemTextA" push ebx call GetProcAddress ; call GetDlgItemTextA ; The text won't be stored in edi because it still contains the filename ; which I will need later. I use edi+100h, no filename is THAT long and ; the buffer is still big enough for the max. 60000 bytes. push FEFFh ; max. size of string mov ecx, edi add ecx, 100h push ecx ; where to store the string push 1 ; ID of edit-control push [esp+1C] ; windows-handle call eax or eax, eax ; success? jz FINISH mov [esi+10], eax ; store number of bytes retrieved ; open the file ; get library handle for kernel32.dll push 403F0D ; offset "kernel32.dll" call GetModuleHandleA mov ebx, eax ; store it in ebx ; get address of CreateFileA push 403F26h ; offset "CreateFileA" push eax call GetProcAddress ; call CreateFileA ; check its parameters in the doc push 0 ; hTemplateFile push 8000080h ; FILE_ATTRIBUT_NORMAL, FILE_FLAG_SEQUENTIAL_SCAN push 2 ; CREATE_ALWAYS push 0 ; Security Attributes push 0 ; no sharing allowed push 40000000h ; GENERIC_WRITE push edi ; our file call eax ; CreateFileA cmp eax, -1 ; INVALID_HANDLE_VALUE ? je FINISH ; do nothing, jump to end ; store handle in memory of OPENFILENAME mov [esi], eax ; get address of WriteFile ; I thought this function was already imported. Deadly wrong! ; My computer crashed several times... push 403F92h ; offset "WriteFile" push ebx call GetProcAddress ; call WriteFile push 0 ; overlapped structure mov ecx, esi ; pointer to DWORD for written bytes add ecx, 8 ; I use [esi+8] push ecx ; push the address push [esi+10] ; number of bytes to write mov ecx, edi ; the buffer starts at edi+100h add ecx, 100h push ecx ; push the buffer push [esi] ; filehandle call eax ; WriteFile ; close file, we don't need the handle anymore push 403F1Ah ; offset CloseHandle push ebx ; kernel32.dll handle call GetProcAddress push [esi] ; filehandle call eax ; CloseHandle jmp FINISH The FINISH stuff is the same, starting at physical offset 0x3DA0. Don't use the virtual offset in HIEW, it will be calculated at runtime. Virtual offsets are only needed for strings and for the jumps of the jumptable. 5. Conclusion ============= That's it and I am burnt out. I hope you got the idea HOW to add code to an unknown target without having the source. The "ReverseMe" is quite small so you have to use GetProcAddress very often. In a normal application MANY functions are already imported and ready to use. Read NNs essay how to add new menu-items and how to handle these messages. 6. Greetings: ============= Knotty Dread NeuRaL NoiSE (his essay ROCKS) douby s^witz all DREAD members (you know show you are) Comments, critics, improvements go to: andreas.theDragon@gmx.net finished: 2000-06-01