tutorials - reversing - third reversing
tutorials - reversing - third reversing

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 00403028
then 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 40000040h
so 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.dll
now, 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 code
we' 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.dll
we 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		0040471dh
we' 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			34h
we 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,00
but 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 dialogboxparama
since 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 00404721
then 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 program
run 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 :
addr
in the 'owner' column, you can see txt2asm. so type :
addr txt2asm
now we are in the txt2asm context. so type :
u 00401028
you 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 0010
what interests us is the wm_close message. so type :
u 0040118b
you 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 00401053
we 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 00404721
you see that our code finishes at rva 004047bfh. so type :
a 0040118b
jmp 004047bf
and 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 program
dump 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 dialogproc
so 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 004047de
then 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 dialogproc
there' 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.


roy, crisiscrackers
[01/03/01]




for any comment, mail roy