2.Initial
approach (Introduction)
|
Hello
there! It's been quite a while since I've last written a tutorial,
sorry 4 that, here is on on extasy's OpenMe #2. It can be found
at reversemes.immortaldescendants.org.
Now, let's take a look at the task of the reverseme:
"The only goal you have is to make this program save
and open the state of the checkboxes, but, IT HAS TO SAVE THEM IN
THE EXE ITSELF ! It means that when you will run this program, it
will restore the state of them when it initialises, and it won't
need a savefile to restore the state. " (as
taken from the readme)
The idea I had was: when the reme starts, it also starts a child/sister/utility
program that would store the state of the checkboxes and write them
to the reme after its execution stops.
Now, as I've conceived the solution, it has four main parts: adapting
the exe, adding the code used on exe initialisation (that restores
the state of the checkboxes), adding the code used on exe exit (that
saves the new state of the checkboxes) and coding the little util
that will write the bytes to the exe (util.exe).
Following, the definitions of four apis:
BOOL CheckDlgButton(
HWND hDlg, // handle to dialog
box
int nIDButton, // button-control identifier
UINT uCheck // check state
);
HWND FindWindow(
LPCTSTR lpClassName, // pointer to class name
LPCTSTR lpWindowName // pointer to window name
);
UINT IsDlgButtonChecked(
hWND hDlg, // handle of dialog box
int nIDButton // button identifier
);
LRESULT SendMessage(
HWND hWnd, // handle of destination window
UINT Msg, // message to send
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);
HINSTANCE ShellExecute(
HWND hwnd, // handle to parent window
LPCTSTR lpOperation, // pointer to string that specifies operation
to perform
LPCTSTR lpFile, // pointer to filename or folder name string
LPCTSTR lpParameters, // pointer to string that specifies executable-file
parameters
LPCTSTR lpDirectory, // pointer to string that specifies default
directory
INT nShowCmd // whether file is shown when opened
);
|
3.Essay
(steps)
|
PART
I
The first and foremost thing to do: start IDA and disassemble
the exe. At this point I had to decide what apis I'd need that arent
included in the openme2.exe file. Those that came to my mind were:
CheckDlgButton (check/uncheck a checkbox), IsDlgButtonChecked (determine
the state of a checkbox), SendMessageA (we need a way to tell our
utility exe "stuff"), FindWindowA (to get the window handle
of our utilily proggie) [all from user32.dll] and ShellExecuteA
(run a file, in this case our util.exe) [from shell32.dll]. So,
run IID King and add those functions. In openme2.exe.txt you'll
get the code needed to access these apis during runtime. In my case
these were:
CheckDlgButton: call dword ptr [405064]
FindWindowA: call dword ptr [405068]
IsDlgButtonChecked: call dword ptr [40506C]
SendMessageA: call dword ptr [405070]
ShellExecuteA: call dword ptr [40606C]
After adding them, we need to establish
where to place our code/data. I've taken the first .IIDKing section
and renamed it to .ourdata, then I've placed the following data (using
PEBuild):
"open" (4050E0) (parameter for ShellExecuteA)
"util.exe" (4050F0) (parameter
for ShellExecuteA)
"OurClass" (405100) (parameter
for FindWindowA)
"OurWindow"(405110) (parameter
for FindWindowA)
Also, I've established the places where I'll keep some uninitialised
data (variables):
405120 for the handle of util.exe window
405130-405138 for the 1/0 of the 9 checkboxes (according to the
checked/unchecked state)
405140 for is/isnt a certain checkbox checked (the result of IsDlgButtonChecked).
Third step: take the second .IIDKing section, rename it to .ourtext
and determine where our code can start - I found 4060A0.
Note:
All the offsets are virtual offsets. Try to figure out the physical
ones by yourself if you'd like :P
PART
II
What
do we need to do at this point? Add code that reads the bytes
405130-405138 and checks a checkbox if the corresponding
byte is not null, then start our util.exe. At this point, take a
look at the disassembly and try to figure out where to jump to the
first part of our code that, as I've said, starts at 4060A0. I've
found 401037 to be a suitable location. Replace the 6 bytes there
with jmp 4060A0; inc eax; dec eax. Now, go at 4060A0 and assemble:
.ourtext:004060A0 6A 00 push 0 ;SW_HIDE
.ourtext:004060A2 6A 00 push 0 ;NULL
.ourtext:004060A4 6A 00 push 0 ;NULL
.ourtext:004060A6 68 F0 50 40 00 push offset aUtil_exe ; "util.exe"
.ourtext:004060AB 68 E0 50 40 00 push offset aOpen ; "open"
.ourtext:004060B0 FF 35 40 30 40 00 push dword_403040 ;hInstance
of openme2.exe, determined using the disassembly
.ourtext:004060B6 FF 15 6C 60 40 00 call ds:ShellExecuteA
.ourtext:004060BC B8 30 51 40 00 mov eax, offset dword_405130 ;the
bytes of the checkboxes
.ourtext:004060C1 B9 09 00 00 00 mov ecx, 9 ;number of checkboxes
.ourtext:004060C6 BA 65 00 00 00 mov edx, 65h ;ID of first checkbox,
again determined using the disassembly
.ourtext:004060CB 8A 18 mov bl, [eax] ;value of checkbox byte
.ourtext:004060CD 80 FB 01 cmp bl, 1 ; is checkbox with id edx to
be checked
.ourtext:004060D0 75 0E jnz short loc_4060E0
.ourtext:004060D2 60 pusha ;push registers to stack
.ourtext:004060D3 6A 01 push 1 ;BST_CHECKED
.ourtext:004060D5 52 push edx ; id of checkbox
.ourtext:004060D6 FF 75 08 push dword ptr [ebp+8] ;handle of our
exe window
.ourtext:004060D9 FF 15 64 50 40 00 call ds:CheckDlgButton
.ourtext:004060DF 61 popa ;pop registers from stack
.ourtext:004060E0 40 inc eax ;next byte
.ourtext:004060E1 42 inc edx ;next id
.ourtext:004060E2 49 dec ecx ;next checkbox
.ourtext:004060E3 0F 85 E2 FF FF FF jnz loc_4060CB ;if it's not
the last, do code allover again
.ourtext:004060E9 E9 AD AF FF FF jmp loc_40109B ;go back to normal
program flow
PART III
We can add our code for this part starting with 406100.
Okay, now we need to determine the part of the code that's responsable
with the code for the 'Save&Quit' button. Take a look here:
.text:00401050 81 7D 0C 11 01 00+ cmp [ebp+arg_4],
111h ;is it WM_COMMAND? ie. click etc.
.text:00401057 75 39 jnz short loc_401092
.text:00401059 8B 45 10 mov eax, [ebp+arg_8] ;wParam of message
(id of control issuing the message)
.text:0040105C 8B 55 10 mov edx, [ebp+arg_8];this
.text:0040105F C1 EA 10 shr edx, 10h;code
.text:00401062 66 0B D2 or dx, dx;is rather
.text:00401065 75 34 jnz short loc_40109B;useless
.text:00401067 66 83 F8 76 cmp ax, 76h
.text:0040106B 75 0C jnz short loc_401079
.text:0040106D 6A 00 push 0
.text:0040106F FF 75 08 push [ebp+arg_0]
.text:00401072 E8 33 00 00 00 call j_EndDialog
.text:00401077 EB 17 jmp short loc_401090
.text:00401079 66 83 F8 6E cmp ax, 6Eh
.text:0040107D 75 11 jnz short loc_401090
.text:0040107F 6A 00 push 0
.text:00401081 6A 00 push 0
.text:00401083 68 05 30 40 00 push offset aSaveAndOpen2Re ; "Save
and Open 2\r\nRead the readme.txt fo"...
.text:00401088 FF 75 08 push [ebp+arg_0]
.text:0040108B E8 20 00 00 00 call j_MessageBoxA
.text:00401090 jmp short loc_40109B
As I see it, the code starting from 40105C to 40106B can be replaced
with cmp eax,76 ; jne 401079 ; jmp 406100. Now, go at 406100 and
assenble:
.ourtext:00406100 68 10 51 40 00 push offset
aOurwindow ; "OurWindow"
.ourtext:00406105 68 00 51 40 00 push offset aOurclass ; "OurClass"
.ourtext:0040610A FF 15 68 50 40 00 call ds:FindWindowA
.ourtext:00406110 A3 20 51 40 00 mov ds:dword_405120, eax ;store
value of window handle
.ourtext:00406115 BA 65 00 00 00 mov edx, 65h ;again, id of first
checkbox
.ourtext:0040611A B9 09 00 00 00 mov ecx, 9 ;number of dialog boxes
.ourtext:0040611F 60 pusha ;store registers
.ourtext:00406120 52 push edx ;id of checkbox
.ourtext:00406121 FF 75 08 push dword ptr [ebp+8] ;window handle
.ourtext:00406124 FF 15 6C 50 40 00 call ds:IsDlgButtonChecked
.ourtext:0040612A A3 40 51 40 00 mov ds:dword_405140, eax ;store
result
.ourtext:0040612F 61 popa ;restore registers
.ourtext:00406130 60 pusha ;store registers
.ourtext:00406131 FF 35 40 51 40 00 push ds:dword_405140 ;result
of IsDlgButtonChecked
.ourtext:00406137 52 push edx ;id of checkbox
.ourtext:00406138 68 00 05 00 00 push 500h ;fictive message
.ourtext:0040613D FF 35 20 51 40 00 push ds:dword_405120 ;handle
of util.exe window
.ourtext:00406143 FF 15 70 50 40 00 call ds:SendMessageA ;issue
the message
.ourtext:00406149 61 popa ;restore registers
.ourtext:0040614A 42 inc edx ;next id
.ourtext:0040614B 49 dec ecx ;next checkbox
.ourtext:0040614C 0F 85 CD FF FF FF jnz loc_40611F ;if ecx>0
verify next checkbox
.ourtext:00406152 E9 16 AF FF FF jmp loc_40106D ;go back to normal
program flow
Ah, but what happens if the user doesn't close the window
via the 'Save&Quit' button. Oops! Forgot about that. But it's
easy to fix. We'll simply redirect the code for WM_CLOSE (10h) to
our code at 406100. So, at 401044 do a jmp 406100 in hiew. That's
it!
PART IV
Now, all we have left to do is code our util.exe and we're
all set. Here are the parts of interest:
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM,
lParam:LPARAM
.IF uMsg==WM_DESTROY ; if the user closes our window
invoke PostQuitMessage,NULL ; quit our application
.ELSEIF uMsg==1280 ;if message is 1280=500h
mov ecx, OFFSET boxes ;move the starting offset of the boxes buffer
in ecx
add ecx,wParam; add the wParam (id of checkbox)
sub ecx,101 ;delete 101 (now in ecx we'll have the offset of ecx
+ i, where i=0..8, i=number of checkbox-1)
.IF lParam==1 ;is it checked?
mov byte ptr [ecx],1
.ENDIF
.IF wParam==6Dh ;was this message issued for the last checkbox?
If so, that means that the openme2.exe will soon terminate.
invoke SetTimer,hWnd,NULL,1000,NULL ;set a timer for one second
.ENDIF
.ELSEIF uMsg==WM_TIMER ;if one second elapsed, it must mean that
OpenMe #2 is finished, so we can write to it
invoke CreateFile,OFFSET openme_filename,GENERIC_WRITE,NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
mov esi,eax
invoke SetFilePointer,eax,3888,NULL,FILE_BEGIN ;move to 3888=F30h=physical
offset for 405130
invoke WriteFile,esi,OFFSET boxes,9,OFFSET dummybuffer,NULL ;write
to the file the buffer with the bytes
invoke CloseHandle,esi
invoke PostMessage,hWnd,WM_CLOSE,NULL,NULL ;close our app
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam ; Default message processing
ret
.ENDIF
xor eax,eax
ret
WndProc endp
Note: I set a delay of one second from the time util.exe
receives the message from the last checkbox to the time it tries
to write to the openme2.exe file. This implies two things: the write
process could fail on slow/busy systems since the reverseme process
could still be open, resulting in a sharing error(not much of an issue
though) and that you must wait for about a second from the time
you close openme to the time you run it again. Also, multiple instances
can cause (and probably will :P ) problems.
|