informations
i' ve slightly modified the txt to asm converter to include the getprocaddress api. with the getmodulehandlea and the getprocaddress api, you can call any api you want, even if they are not imported. in fact, only with getmodulehandlea, we could do it, by searching the address of getprocaddress in kernel32.dll. sometime, when the library that you want to use is not mapped in the process, that is, no imports comes from this library, we' ll have to use the api loadlibrarya. another thing is that this time, we' ll still use a cave, but we' ll extend a section to get more place for our code and datas. since we won' t add our code in the text section, we' ll have to jump from one section to another, but hiew don' t do that correctly, so we' ll also see how we can use softice and procdump to patch an executable. the aim of this reversing will be to add a 'open' button that will permit to load a file in the edit control.
required files
lesson 2 files - all the needed files for the lesson 2
tools used
hiew
procdump
softice
symantec resourcestudio
tutorial
I. using softice to patch an executable
we' ll see briefly with a small example, the one of the first reversing, how we can patch an executable with softice and procdump. so launch the target, bpx messageboxa and click on the 'about' button. the wrong push is the one at 0040107b, it should be push 00403028, so type :
a 0040107b push 00403028then type another carriage return. now if you click on the 'about' button, the correct text is displayed. but the patch is temporary. so launch procdump, select the right task in the task list, right-click it and click on 'dump (full)', then save the dump. now, if you launch the saved executable, the messagebox displays the right text, it' s patched.
beware, when you dump the file, make sure that there is no breakpoints set, otherwise it will dump wrong datas.
II. adding the 'open' button
open the program in a resource editor, and add a button, call it '&open' and use the resource handle 105 (69h) as it' s unused.
III. extending the last section
we' ll use the last section as a cave, so we' ll extend it to have enough room for our datas and our code. we' ll add 350h bytes (848). so open procdump, click on the 'pe editor' button, open the program, and click on the 'sections' button. that' s what you see :
name vSize vOffset rSize rOffset characteristics .text 00000210h 00001000h 00000400h 00000400h 60000020h .rdata 000001d0h 00002000h 00000200h 00000800h 40000040h .data 00000068h 00003000h 00000200h 00000a00h c0000040h .rsrc 00000600h 00004000h 00000600h 00000c00h 40000040hso extend the vSize and rSize of .rsrc to 950, and change the characteristics to e0000060h, so we can store data and execute code. so, our code will begin at offset c00h + 600h = 1200h, at virtual address 4000h + 600h = 4600h, that is rva 00404600h.
IV. the task
we want to add a 'open' button that will load a file in the edit control. so we' ll need several apis.
open the program in hiew and go to the .text section at offset 400h. you see at the rva 00401002 a call to 004011bc. go to this address, you see several jumps to api functions. that' s how the program calls the apis. it will make our reversing easier. so scroll up a bit, we' ll list all the apis and the addresses that correspond to each of them :
api rva library exitprocess 004011b6 kernel32.dll getmodulehandlea 004011bc kernel32.dll getprocaddress 004011c2 kernel32.dll globalalloc 004011c8 kernel32.dll globalfree 004011ce kernel32.dll globallock 004011d4 kernel32.dll lstrcpya 004011da kernel32.dll dialogboxparama 004011e0 user32.dll enddialog 004011e6 user32.dll getdlgitemtexta 004011ec user32.dll loadicona 004011f2 user32.dll messageboxa 004011f8 user32.dll postquitmessage 004011fe user32.dll sendmessagea 00401204 user32.dll setdlgitemtexta 0040120a user32.dllnow, we have to know the code we want to add, and the datas we need. so here' s our open code in pseudo-code :
get a filename (getopenfilenamea, openfilename structure, bfFileBuffer buffer) open it (createfilea) read it (readfile, dword numberofbytes) close it (closehandle) set the text (setdlgitemtexta) return to the codewe' ll also need to allocate some space. some apis are not imported, they are :
api library getopenfilenamea comdlg32.dll createfilea kernel32.dll readfile kernel32.dll closehandle kernel32.dll loadlibrarya kernel32.dll freelibrary kernel32.dllwe need loadlibrarya and freelibrary since getopenfilenamea is in the comdlg32.dll, and this library is not mapped in the process. we' ll also need these datas :
type data size rva string 'kernel32.dll',0 0dh 00404600h string 'CreateFileA',0 0ch 0040460dh string 'ReadFile',0 09h 00404619h string 'CloseHandle',0 0ch 00404622h string 'LoadLibraryA',0 0dh 0040462eh string 'FreeLibrary',0 0ch 0040463bh string 'comdlg32.dll',0 0dh 00404647h string 'GetOpenFileNameA',0 011h 00404654h structure openfilename 04ch 00404665h buffer bfFileBuffer 050h 004046b1h dword createfilea address 04h 00404701h dword readfile address 04h 00404705h dword closehandle address 04h 00404709h dword freelibrary address 04h 0040470dh dword comdlg32.dll handle 04h 00404711h dword getopenfilenamea address 04h 00404715h dword allocated space handle 04h 00404719h dword allocated space address 04h 0040471dhwe' ll load all the needed api address at the start of the program, then we' ll treat our open command, and we' ll free comdlg32.dll when the program will close.
we can add code at rva 0040471dh + 04h = 00404721h.
first, we' ll add all the variables we need in the program. let' s look at the importants elements of the openfilename structure :
element value offset in structure lStructSize 04ch, size of the structure 0h hwndOwner handle of owner 4h hInstance hinstance of owner 8h lpstrFile 004046b1h, rva of bfFileBuffer 1ch nMaxFile 50h, size of bfFileBuffer 20h Flags 00281000h 34hwe need to write the values in little endian format, that is :
rva value 00404665h 4c,00,00,00 00404681h b1,46,40,00 00404685h 50,00,00,00 00404699h 00,10,28,00but in fact, in the softice data window, if you are in dword mode, just write the dwords, that is 0000004ch, 004046b1h,....
open the program in an hexeditor, go at offset 1200h and add 350h 0 bytes.
launch the program, bpx messageboxa, click on the 'about' button, type d 00404600 and write all the datas, then launch procdump and dump the task.
V. reversing the program
we need to have 3 snippets of codes :
1. at the start of the program, we need to get all the apis offset, and to initialize the hInstance element of the openfilename structure and allocate some space.
2. when the user click the 'open' button, we have to fill the hWnd element of the openfilename structure, open the file and to load it in the edit control.
3. when the program closes, we need to free comdlg32.dll and free the allocated space.
let' s start with the first snippet. we need the hInstance element, that hInstance is given by the api getmodulehandlea. most of the time, in lots of programs, getmodulehandlea is called at the beginning of the program. so bpx getmodulehandlea and launch the program. you see this :
00401000 6A00 push 00000000 00401002 E8B5010000 call kernel32!getmodulehandlea ; call to getmodulehandlea 00401007 A358304000 mov dword ptr [00403058], eax ; eax is the hInstance, the mov is 5 bytes 0040100C 6A00 push 00000000 0040100E 6828104000 push 00401028 ; 00401028 is the rva of the dialogproc 00401013 6A00 push 00000000 00401015 6A64 push 00000064 00401017 FF3558304000 push dword ptr [00403058] 0040101D E8BE010000 call user32!dialogboxparama ; call to dialogboxparamasince we need to jump to our code, we' ll have to overwrite 5 bytes of code, so we' ll overwrite the mov at 00401007, and we' ll execute it in our code. so type :
a 00401007 jmp 00404721then type another carriage return. type :
a 00404721 mov dword ptr [00403058],eax ; execute the mov mov dword ptr [0040466d],eax ; fill the hInstance element of the openfilename structure push 00404600 ; 'kernel32.dll' call 004011bc ; getmodulehandlea mov ebx,eax ; ebx : handle of kernel32.dll push 0040460d ; 'createfilea' push ebx ; handle of kernel32.dll call 004011c2 ; getprocaddress mov dword ptr [00404701],eax ; store the createfilea address push 00404619 ; 'readfile' push ebx call 004011c2 mov dword ptr [00404705],eax ; store the readfile address push 00404622 ; 'closehandle' push ebx call 004011c2 mov dword ptr [00404709],eax ; store the closehandle address push 0040463b ; 'freelibrary' push ebx call 004011c2 mov dword ptr [0040470d],eax ; store the freelibrary address push 0040462e ; 'loadlibrarya' push ebx call 004011c2 push 00404647 ; 'comdlg32.dll' call eax ; call loadlibrarya mov dword ptr [00404711],eax ; store comedlg32.dll handle push 00404654 ; 'getopenfilenamea' push eax ; comdlg32.dll handle call 004011c2 mov dword ptr [00404715],eax ; store the getopenfilenamea address push 10000 ; number of bytes to allocate push 42 ; ghnd call 004011c8 ; globalalloc mov dword ptr [00404719],eax ; store the allocated space handle push eax call 004011d4 ; globallock mov dword ptr [0040471d],eax ; store the allocated space address jmp 0040100c ; jump back to the programrun procdump and dump the task.
now we' ll see where we can free the library and the allocated space when we exit. we saw that the dialogproc was at offset 00401028, so launch the program. type :
addrin the 'owner' column, you can see txt2asm. so type :
addr txt2asmnow we are in the txt2asm context. so type :
u 00401028you see this :
00401028 55 push ebp 00401029 8BEC mov ebp, esp 0040102B 57 push edi 0040102C 817D0C10010000 cmp dword ptr [ebp+0C], 00000110 ; WM_INITDIALOG 00401033 0F84F5000000 je 0040112E 00401039 837D0C10 cmp dword ptr [ebp+0C], 00000010 ; WM_CLOSE 0040103D 0F8448010000 je 0040118B 00401043 817D0C11010000 cmp dword ptr [ebp+0C], 00000111 ; WM_COMMAND 0040104A 7411 je 0040105D 0040104C 5F pop edi 0040104D 33C0 xor eax, eax 0040104F C9 leave 00401050 C21000 ret 0010what interests us is the wm_close message. so type :
u 0040118byou see this :
0040118B 6A00 push 00000000 0040118D FF7508 push [ebp+08] 00401190 E851000000 call user32!enddialog 00401195 FF355C304000 push dword ptr [0040305C] 0040119B E82E000000 call kernel32!globalfree 004011A0 FF3554304000 push dword ptr [00403054] 004011A6 E823000000 call kernel32!globalfree 004011AB E9A3FEFFFF jmp 00401053we need 5 bytes, so we' ll replace the pushs at 0040118b and 0040118d and we' ll return at rva 00401190. we need to know where we can add code. so type :
u 00404721you see that our code finishes at rva 004047bfh. so type :
a 0040118b jmp 004047bfand then :
a 004047af push 00404711 ; comdlg32.dll handle call dword ptr [0040470d] ; freelibrary push 00404719 ; handle of allocated space call 004011ce ; globalfree push 0 ; replace the push push dword ptr [ebp+08] ; replace the push jmp 00401190 ; jump back to the programdump the task.
no we need to check if the 'open' button has been clicked. the next rva for the code is 004047deh. we saw that the wm_command message is treated at rva 0040105d. so type u 0040105d. you see this :
0040105D 8B4510 mov eax, dword ptr [ebp+10] 00401060 8B5510 mov edx, dword ptr [ebp+10] 00401063 C1EA10 shr edx, 10 00401066 83FA00 cmp edx, 00000000 ; a button has been clicked ? 00401069 75E8 jne 00401053 ; no, jump 0040106B 6683F865 cmp ax, 0065 ; the button is 'about' 0040106F 7516 jne 00401087 ; no, check the other buttons. 00401071 6A40 push 00000040 00401073 6800304000 push 00403000 00401078 680E304000 push 0040300E 0040107D FF7508 push [ebp+08] 00401080 E873010000 call user32!messageboxa ; display the about messagebox 00401085 EBCC jmp 00401053 ; return to the dialogprocso we' ll replace the cmp at 0040106b by a jmp to our code, then we check if ax is 65h, if yes we jump to 00401071, if no, we check if ax is 69h (the open button), if no we jump to 00401087 to check the other buttons, if yes, we open the file and we return to the dialogproc at rva 00401053. so type :
a 0040106b jmp 004047dethen type :
a 004047ce cmp ax,65 ; the button is 'about' ? jz 00401071 ; yes, display the about messagebox cmp ax,69 ; the button is 'open' ? jnz 00401087 ; no, check the other buttons push dword ptr [ebp+08] ; put the handle of the dialog pop dword ptr [00404669] ; in the hwndOwner element of the openfilename structure push 00404665 ; openfilename structure address call dword ptr [00404715] ; getopenfilenamea test eax,eax ; a file has been chosen ? jz 00401053 ; no, return to the dialogproc pushad push 0 push 00000080 ; FILE_ATTRIBUTE_NORMAL (push 80000000, then change the bytes) push 3 ; OPEN_EXISTING push 0 push 0 push 80000000 ; GENERIC_READ push 004046b1 ; bfFileBuffer rva call dword ptr [00404701] ; createfilea push eax ; push the handle of the file for closehandle push 0 push 004046b1 ; numberofbytes rva push 0000ffff ; push f000ffff, then change the bytes push dword ptr [0040471d] ; allocated space address push eax call dword ptr [00404705] ; readfile mov eax,dword ptr [0040471d] ; allocated space address add eax,dword ptr [004046b1] ; end of the read file and byte ptr [eax],0 ; add a zero after the read file call dword ptr [00404709] ; closehandle push dword ptr [0040471d] ; allocated space address push 68 ; handle of the edit control push dword ptr [ebp+08] ; handle of dialog call 0040120a ; setdlgitemtexta popad jmp 00401053 ; return to the dialogprocthere' s two push that are a bit tricky, because we need them to push dwords, so we have to push a big number, then show the code in the data view and replace the bytes by the right ones, so the push don' t push 80000000 but 00000008.
it works. but there' s still a bug. since we replace the beginning of the filename by the numberofbytes dword when we read the file, sometimes, the getopenfilenamea api won' t work anymore. the code ends at rva 00404870, so we can use the dword at 00404870 for the numberofbytes dword. so change the push 004046b1 in the readfile arguments and the add eax, dword ptr [004046b1] with the rva 00404870. now clean the datas, that is, replace the bfFileBuffer by 0 bytes if you loaded a file, and dump the process.