How to reverse engineer a Windows 95 target
REVERSE ENGINEERING EXERCISES FOR THE MASSES - (2d)
by fravia+ (MSRE), August 1997
(Part D: Back to Main - 11 August 1997)
Courtesy of Fravia's page of reverse engineering
Well, a very interesting essay... I wrote it myself! :-)
This essay will be divided in
four (or more) parts:A = Introduction to filemon
B = reverse engineering without source code
C = Filemon reversed
Back to Main
D = VXD vagaries and mysteries
Although already disponible, this essay is still under construction
and will be modified and ameliorated until the wording below will disappear (I reckon
until mid-september)
UNDER CONSTRUCTION
REVERSE ENGINEERING EXERCISES FOR THE MASSES - (5)
How to reverse engineer a Windows 95 program
Filemon.exe Version 2
(Part D: Back to Main)
by Fravia (MSRE), August 1997
Well, in the first part of this essay we had a general "introduction" to the structure
of filemon.exe, in the second one we reversed quite a lot of code without using
the C source... in the third one we finished all "home-made" functions of our target... time to tackle the WinMain and the MainWndProc functions...
WinMain was already known
Let's resume the "logistic" of our target, all the functions data we have already gathered:
01) FUNCTION Abort ;This dwells between 1000-1020
02) FUNCTION WinMain ;This dwells between 1020-10B0
03) FUNCTION InitApplication ;This dwells between 10B0-1130
04) FUNCTION InitInstance ;This dwells between 1130-1190
05) FUNCTION MainWndProc ;This dwells between 1190-1750
06) FUNCTION Split;This dwells between 1750-1790
07) FUNCTION ListAppend ;This dwells between 1790-1A40
08) FUNCTION UpdateStatistics ;This dwells between 1A40-1B50
09) FUNCTION CreateListView;This dwells between 1B50-1C20
10) FUNCTION SaveFile ;This dwells between 1C20-1ED0
11) FUNCTION FilterProc;This dwells between 1ED0-2190
12) FUNCTION About;This dwells between 2190-21C9
Now that we know where everything dwells, let's have a look at the [1020-10B0]:WinMain function.
In Windows we enter WinMain coming from Program entry point, here the call is at :00402626 E8F5E9FFFF: call 1020WinMain. We will not work a lot on it, because we don't need to reverse anything at all: WinMain is a PERFECTLY WELL KNOWN procedure:
int PASCAL WinMain(hinstCurrent, hinstPrevious, lpCmdLine, nCmdShow)
HINSTANCE hinstCurrent; /* handle of current instance */
HINSTANCE hinstPrevious; /* handle of previous instance */
LPSTR lpszCmdLine; /* address of command line */
int nCmdShow; /* show-window type (open/icon) */
The WinMain function is called by the system as the initial entry
point for a Windows application.
Parameter Description
hinstCurrent Identifies the current instance of the application.
hinstPrevious Identifies the previous instance of the application.
lpszCmdLine Points to a null-terminated string specifying the command
line for the application.
nCmdShow Specifies how the window is to be shown. This parameter
can be one of various SW_ values.
0x00 SW_HIDE; 0x01 SW_SHOWNORMAL; 0x02 SW_SHOWMINIMIZED; 0x03 SW_SHOWMAXIMIZED;
0x04 SW_SHOWNOACTIVATE; 0x05 SW_SHOW; 0x06 SW_MINIMIZE; 0x07 SW_SHOWMINNOACTIVATE;
0x08 SW_SHOWNA; 0x09 SW_RESTORE... etc
Ah ah! Therefore let's go back to the target's entry point code, where it calls WinMain and let's better check what's going on:
:jumped from 260F
:00402619 50 push eax ;WinMain rightmost nCmdShow
:0040261A 56 push esi ;WinMain next: lpszCmdLine
:0040261B 6A00 push 0 ;WinMain next: hinstPrevious
:0040261D 6A00 push 0 ;GMH_ lpszModuleName
:0040261F FF156CB24400 Call dword ptr [0044B26C] ;GetModuleHandle(lpszModuleName)
:00402625 50 push eax ;WiNMain leftmost: hinstCurrent
:00402626 E8F5E9FFFF call 1020_WinMain(modulehandle, 0, esi, old_eax)
Well, I hope that the approach of my essay will work... You saw smack at the beginning the functions InitInstance and InitApplication, as if they were "extraordinary" functions... and we come only now to WinMain... you see, there is something important I didn't tell you until now: WinMain, in ANY WINDOWS PROGRAM, calls a function InitInstance and a function InitApplication... yes, always!
Here an example: the following text is taken -without modifying a comma- from Borland's C++ 4.52 API helpfile.
In the following example the WinMain function initializes the application, initializes the instance, and establishes a message loop:int PASCAL WinMain(HINSTANCE hinstCurrent, HINSTANCE hinstPrevious,
LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
if (hinstPrevious == NULL) /* other instances? */
if (!InitApplication(hinstCurrent)) /* shared items */
return FALSE; /* initialization failed */
/* Perform initializations for this instance. */
if (!InitInstance(hinstCurrent, nCmdShow))
return FALSE;
/* Get and dispatch messages until WM_QUIT message. */
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); /* translates virtual key codes */
DispatchMessage(&msg); /* dispatches message to window */
}
return (int) msg.wParam; /* return value of PostQuitMessage */
}
Well, so what? Well, have a look at filemon's c source code NOW!
You are astonished, aren't you... yes, it's THE SAME code, I could have told you when we have examined InitInstance but, see, I just wanted to carry you through the entire target before telling you that BIG CHUNKS OF WINDOWS CODE are already perfectly known (with all their variables and parameters and relative calls) for (almost) any application! (Peter Urbanik, are you reading this? D'you understand what this could mean for developing the perfect reverse engineering tool? :-)
I know, dear reader, you are quite impatient now, you have read a lot of text and you already believe that you now understand everything, that you can disassemble every windows application on the face
of this planet... it is true for "easy" targets, but there is unfortunately still a lot to understand in order to reverse engineer more "complex" windows code, as you will see... bear with me a little more... let's investigate filemon's WinMain itself now
Wow! A WinMain function in all its dazzly splendour!
:WinMain
:00401020 83EC1C sub esp, 1C ;Adjust Stack
:00401023 53 push ebx ;will pop
:00401024 56 push esi ;will pop
:00401025 8B742428 mov esi, [esp + 28] ;get param hInstance
:00401029 57 push edi ;will pop
:0040102A 56 push esi ;push hInstance
:0040102B E880000000 call 10B0=InitApplication (hInstance)
:00401030 83C404 add esp, 4 ;(4=was only one param)
:00401033 85C0 test eax, eax ;return FALSE?
:00401035 750B jne 00401042 ;OK, continue
:00401037 33C0 xor eax, eax ;prepare FALSE return
:00401039 5F pop edi ;pop them all
:0040103A 5E pop esi
:0040103B 5B pop ebx
:0040103C 83C41C add esp, 1C ;Adjust stack
:0040103F C21000 ret 0010 ;bye WinMain
:ContinueWinMain_after_InitApplication
:00401042 8B442438 mov eax, [esp + 38] ;get nCmdShow
:00401046 50 push eax ;push nCmdShow
:00401047 56 push esi ;push hInstance
:00401048 E8E3000000 call 1130=InitInstance(hInstance, nCmdShow)
:0040104D 83C408 add esp, 8 ;(8 because had two param)
:00401050 85C0 test eax, eax ;worked?
:00401052 750B jne 0040105F ;yes, so continue WinMain
:00401054 33C0 xor eax, eax ;no, so prepare FALSE
:00401056 5F pop edi ;popall
:00401057 5E pop esi
:00401058 5B pop ebx
:00401059 83C41C add esp, 1C ;adjust and return
:0040105C C21000 ret 0010 ;bye WinMain
Passed parameter counting
Ok, after these two code snippets it's time to explain and understand a very important point: PARAMETER PASSING MATH.
As you can see, the call to InitApplication is followed by a stack "4" Adjust, the call to InitInstance is followed by a stack "8" Adjust, this happens because the first function (InitApplication) is called with only ONE parameter (esi), and the second one (InitInstance) is called with two (esi and eax).Don't confond this with the LENGTH of the parameters... this is the space that the POINTERS have taken, yet it's important to understand HOW MANY parameters had the function originally... it's a very simple relation:
add, esp 4 = ONE parameter
add, esp 8 = TWO parameters
add, esp C = THREE parameters, add esp, 10 = 4 parameters, add esp, 14 = 5 parameters... and so on... you dig it?
Once more, that was parameters "discarding": if the programmer used the C calling convention (or rather if the compilerdid) he must remember to discard the parameters, adjusting SP after the call.
Let's clarify also the more "general" stack adjusting a little: There are two ways to adjust the stack: you can use the RETn instruction, where "n" is the number of bytes of parameters pushed, or you can save the return address in some register (or in memory) and pop the parameter off one by one, or you can use a mix of the two methods... the popping technique is useful if you are optimizing for speed AND space.
When a routine receives control, the top of the stack contains A RETURN ADDRESS (two or four words, depending on whether the routine is near or far) and, above it, any parameters being passed.
There are THREE basic techniques for accessing the parameters passed to the routines:
1) use the BP register to address the stack
2) use another base or index register to get the parameters
3) pop the return address and then pop the parameters.
Since we are (supposed to be) reverse engineering our targets, we DO NOT know, most of the time the exact "prototypes" of the "home-made" functions we are examining... prototypes establish the return type for functions that return any type other than int; contain a full list of parameter types, identifiers for each expression that will be passed as an actual argument and can also reflect the fact that the number of arguments passed will be variable, or that there will be NO arguments passed. The parameter list in a prototype is a list of type names separated by commas, for instance you may well have in a protection schema a FARPROC GetProtectionAddress(hinst, lpszProtSnippet)
Since here we have added 1C at the beginning of WinMain, we'll have some combination of LPSTR 32/16 bytes pointers 16 bytes handles and ints. Well, if you look at the two snippets above, you'll already have some hints:
:00401025 8B742428 mov esi, [esp + 28] ;so we have one of them at 28
:00401042 8B442438 mov eax, [esp + 38] ;and one at 38
Therefore we know that one of the parameters passed to WinMain will now be collected at +28 and one at +38, and that the first one (+28) is collected in esi and pushed as LEFTMOST parameter in our two functions InitApplication and InitInstance... c'mon, even if you did not know already that MOST of the main functions in Window need hWnd as leftmost parameters, that we are Initialising now from WinMain and that esi is the "classical" hWnd register, you would have guessed by now that the one at 28 is hWnd!
Just keep in mind that we had an adjust 1C entering WinMain... more about parameter passing later... let's go on with WinMain, we have seen the "standard" calls to the two procedures InitInstance and Initapplication, what's happening next? Well, the usual Message chain, what else... Windoze is so boring!
Well, here are the three functions, just read their prototypes... you'll understand all the following comments of mine to WinMain's code:
BOOL TranslateMessage(lpmsg)
const MSG FAR* lpmsg; /* address of MSG structure */
;prepares for GetMessage if it was a "virtual key" msg
LONG DispatchMessage(lpmsg)
const MSG FAR* lpmsg; /* address of structure with message */
BOOL GetMessage(lpmsg, hwnd, uMsgFilterMin, uMsgFilterMax)
MSG FAR* lpmsg; /* address of structure with message */
HWND hwnd; /* handle of the window */
UINT uMsgFilterMin; /* first message */
UINT uMsgFilterMax; /* last message */
Here is the code of WinMain:
:continue_WinMain_after_InitInstance
:0040105F 8D44240C lea eax, [esp + 0C] ;lpmsg
:00401063 6A00 push 00000000
:00401065 6A00 push 00000000
:00401067 8B3500B34400 mov esi, [0044B300] ;GetMessage
:0040106D 6A00 push 00000000
:0040106F 50 push eax
:00401070 FFD6 call esi ;GetMessage(lpmsg,NULL,0,0)
:00401072 85C0 test eax, eax ;WM_QUIT? Should we die?
:00401074 742B je 004010A1 ;Yes, so ret and terminate
:00401076 8B3D94B24400 mov edi, [0044B294] ;TranslateMessage
:0040107C 8B1D8CB24400 mov ebx, [0044B28C] ;DispatchMessage
:continue_WinMain_message_chain
:00401082 8D44240C lea eax, [esp + 0C] ;get lpmsg
:00401086 50 push eax ;push lpmsg
:00401087 FFD7 call edi ;TranslateMessage(lpmsg)
:00401089 8D44240C lea eax, [esp + 0C] ;get lpmsg
:0040108D 50 push eax ;push lpmsg
:0040108E FFD3 call ebx ;DispatchMessage (lpmsg)
:00401090 8D44240C lea eax, [esp + 0C] ;get lpmsg
:00401094 6A00 push 0 ;push 0
:00401096 6A00 push 0 ;push 0
:00401098 6A00 push 0 ;push NULL
:0040109A 50 push eax ;push lpmsg
:0040109B FFD6 call esi ;GetMessage(lpmsg,NULL,0,0)
:0040109D 85C0 test eax, eax ;ret and terminate if it is WM_QUIT,
:0040109F 75E1 jne 00401082 ;else go chain_start: find out which
;other message has disturbed us
:exit WinMain with return value of the PostQuitMessage function
:004010A1 8B442414 mov eax, [esp + 14] ;get msg.wParam;
:004010A5 5F pop edi ;pop the hell out of them
:004010A6 5E pop esi
:004010A7 5B pop ebx
:004010A8 83C41C add esp, 1C ;re-adjust
:004010AB C21000 ret 0010 ;bye WinMain
Keep in mind that the WM_QUIT message indicates a request to terminate an application and is generated when the application calls the PostQuitMessage function. It causes the GetMessage function to return zero. Therefore you could read the above code like this: Initialise our app, initialise our instance and get messages until we want to quit or something funny happens or user clicks something or whatever... There is no direct connection now from WinMain to the other functions of our target, other that the messages themselves... It's InitInstance that calls MainWndProc, the last (and most important) function of our target (that we have still to investigate). Let's see now what happens at the good old entry point of our target when WinMain closes...
:0040262B 50 push eax
:0040262C E80F160000 call 00403C40;GetCurrentProcess (retrieve task)
;TerminateProcess (ends task)
;ExitProcess (close self loading app)
:00402631 EB27 jmp 0040265A ;ret
Well that was it... WinMain is just a "running wheel", continuously looking for messages in order to get, translate and dispatch them to our target, which WinMain has initialized before entering its master loop. The INITIALISATION part of WinMain triggers InitApp and InitInstance, the rest is just the messages loop until a WM_QUIT message kills it. WM_QUIT is so terrible that an extra windows function exists only in order to deliver it: PostQuitMessage(nExitCode). This is called in our target from three obvious "termination" points: our Abort function (of course) and from the WM_CLOSE and WM_DESTROY switches of the message tree in MainWndProc (of course). This is interesting too, from a reverse engineering point of view, because this "death" chain can be very intersting to approach a difficult target... even cloaked code must react to a termination command, and in a difficult session you'll be able to re-mount (until you find your bearings) starting from the termination routines and from the ExitCode returned to PostQuitMessage.
Return values
You should keep in mind the following about return values: 8 and 16 bit values are always returned in AX; 32 bit values are returned in dx:ax; floating point values are in the 8087 TOS register and structures are returned in VARIOUS WAYS, according to their size. Returned structures are indeed a bit more complex: 1 or 2 bytes length structures are returned in ax and 4 bytes length structures are returned in dx:ax, but THREE or MORE THAN FOUR bytes structures must be stored in a static data area, the function will return a POINTER to that data area... near pointers in ax and far pointers in dx:ax... (well, just keep it basic: return value always in ax, at times in dx:ax :-)
We are done with WinMain, yet we have still the "Main Chunk" of filemon to examine:
MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam), here it is: as you will immediately see, the initial switch is based in esi and gives following options: WM_=F; WM_=1; WM_=2; WM=5; WM=more than F etc... since we KNOW the meaning of WM_tags, we can easily understand the whole function...
TAKE NOTE! All what windows programs do is react to messages... the whole os turns around waiting for messages to process... in (almost) all windows targets you'll have somewhere the switch tree of the message processing function (which is always the "real" main function of your targets, by the way). We come now to see it, in this essay, only after having examined for teaching purposes all the others functions, BUT IN THE REALITY OF REVERSE ENGINEERING you'll often start from this "windows messages" switch tree, because you'll immediately understand what your target will do when it gets the WM_CREATE (aka 1) and the WM_COMMAND (aka 111) messages, the two most important ones for reverse engineering purposes.
:MainWndProc_Let's see which WM_ case we have
:00401190 81EC44010000 sub esp, 144 ;adjust
:1196 53 push ebx ;will pop
:1197 56 push esi ;will pop
:1198 8BB42454010000 mov esi, [esp + 154] ;which WM_ do we have?
:119F 57 push edi ;will pop
:11A0 83FE0F cmp esi, F ;is it WM_ = 000F = WM_PAINT?
:11A3 771F ja 004011C4 ;go more than F
:11A5 0F8485020000 je 00401430 ;go WM_PAINT
:11AB 83FE01 cmp esi, 1 ;is it WM_= 0001 = WM_CREATE?
:11AE 7466 je 00401216 ;go WM_CREATE
:11B0 83FE02 cmp esi, 2 ;is it WM_= 0002 = WM_DESTROY?
:11B3 0F843F020000 je 004013F8 ;go WM_DESTROY
:11B9 83FE05 cmp esi, 5 ;is it WM_= 0005 = WM_SIZE?
:11BC 0F8443020000 je 00401405 ;go WM_SIZE
:11C2 EB2E jmp 004011F2 ;else go WM_DEFAULT
:WM_more than F
:11C4 83FE4E cmp esi, 4E ;is it WM_ = 004E = WM_NOTIFY?
:11C7 7711 ja 004011DA ;more than 4E
:11C9 0F84E7020000 je 004014B6 ;go WM_NOTIFY
:11CF 83FE10 cmp esi, 10 ;is it WM_ = 0010 = WM_CLOSE?
:11D2 0F84AE020000 je 00401486 ;go WM_CLOSE
:11D8 EB18 jmp 004011F2 ;else go WM_DEFAULT
:WM_ more than 4E
:11DA 81FE11010000 cmp esi, 111 ;is it WM_ = 0111 = WM_COMMAND?
:11E0 0F84F7020000 je 004014DD ;go WM_COMMAND
:11E6 81FE13010000 cmp esi, 113 ;is it WM_ = 0113 = WM_TIMER?
:11EC 0F84AB040000 je 0040169D ;go WM_TIMER
;else fall through to WM_DEFAULT
:WM_DEFAULT (not 1,2,5,F,10,4E,111 or 113)
:calls the default window procedure: every message is processed!
:11F2 8B942460010000 mov edx, [esp + 00000160]
:11F9 8B8C245C010000 mov ecx, [esp + 0000015C]
:1200 52 push edx
:1201 51 push ecx
:1202 56 push esi
:1203 8BB42460010000 mov esi, [esp + 00000160]
:120A 56 push esi
:120B FF15D4B24400 Call dword ptr [0044B2D4] ; DefWindowProcA
:1211 E9F0040000 jmp 1706_keep ax_&_ret
:WM_ = OOO1 = WM_CREATE
The WM_CREATE message is sent when an application requests that a window be
created by calling the CreateWindowEx or CreateWindow function. The window
procedure for the new window receives this message after the window is
created but before the window becomes visible. The message is sent to the
window before the CreateWindowEx or CreateWindow function returns.
:1216 68027F0000 push 00007F02 ;pszCursor
:121B 6A00 push 00000000 ;hinst
:121D FF15F8B24400 Call dword ptr [0044B2F8] ;LoadCursor(hinst, pszCursor)
:1223 8BB42454010000 mov esi, [esp + 154]
:122A A3B0964000 mov [004096B0], eax ;handle of the newly loaded cursor
:122F 56 push esi ;hWnd
:1230 FF15B4B24400 Call dword ptr [0044B2B4] ;SetCapture(hWnd)
:1236 A1B0964000 mov eax, [004096B0] ;handle of the newly loaded cursor
:123B 8B3DB8B24400 mov edi, [0044B2B8] ;SetCursor
:1241 50 push eax ;handle of cursor
:1242 FFD7 call edi ;SetCursor
:1244 56 push esi ;hWnd
:1245 A3BC964000 mov [004096BC], eax ;handle of previous cursor
:124A E801090000 call 1B50_CreateListView(hWnd)
:124F 83C404 add esp, 4
:1252 A3B8964000 mov [004096B8], eax ;save hWndList
:1257 85C0 test eax, eax ;error?
:1259 7511 jne 0040126C ;continue_WM_CREATE
:125B 6A00 push 00000000
:125D 6A00 push 00000000
:125F 6874814000 push 00408174 ;->"List not created!"
:1264 6A00 push 00000000
:1266 FF1590B24400 Call dword ptr [0044B290] ; MessageBoxA
:continue WM_CREATE (Initialise_filters)
:126C 6870814000 push 00408170 ;->"*"
:1271 68C0974000 push 004097C0
:1276 E8650F0000 call 004021E0
:127B 83C408 add esp, 00000008
:127E 6870814000 push 00408170 ;->"*"
:1283 68E0974000 push 004097E0
:1288 E8530F0000 call 004021E0
:128D 83C408 add esp, 00000008
:1290 686C814000 push 0040816C
:1295 68E0984000 push 004098E0
:129A E8410F0000 call 004021E0
:129F 8D4C2458 lea ecx, [esp + 58]
:12A3 83C408 add esp, 00000008
:12A6 B801000000 mov eax, 00000001
:12AB 51 push ecx
:12AC 6800010000 push 00000100
:12B1 A3E0994000 mov [004099E0], eax ;TRUE
:12B6 A3E4994000 mov [004099E4], eax ;TRUE
:continue WM_CREATE (open device handle)
:12BB FF15E0B14400 Call dword ptr [0044B1E0] ;GetCurrentDirectoryA
:12C1 8D4C2450 lea ecx, [esp + 50]
:12C5 685C814000 push 0040815C ;->"\\.\FILEVXD.VXD"
:12CA 6858814000 push 00408158 ;->"\%s"
:12CF 51 push ecx
:12D0 FF15D4B14400 Call dword ptr [0044B1D4] ;KERNEL32.lstrlenA
:12D6 8D4C0458 lea ecx, [esp + eax + 58]
:12DA 8B1DBCB24400 mov ebx, [0044B2BC] ;wsprintf
:12E0 51 push ecx
:12E1 FFD3 call ebx ;wsprintf
:12E3 83C40C add esp, 0000000C
:12E6 6A00 push 00000000
:12E8 6800000044 push 44000000
:12ED 6A00 push 00000000
:12EF 6A00 push 00000000
:12F1 6A00 push 00000000
:12F3 6A00 push 00000000
:12F5 685C814000 push 0040815C ;->"\\.\FILEVXD.VXD"
:12FA FF15D8B14400 Call dword ptr [0044B1D8] ;CreateFileA
:1300 A360804000 mov [00408060], eax
:1305 83F8FF cmp eax, FFFFFFFF ;ax=-1?
:1308 7527 jne 00401331 ;no, continue_1331
:130A 6850814000 push 00408150 ;->"FILEVXD"
:130F 6834814000 push 00408134 ;->"%s is not loaded properly."
:1314 68A0944000 push 004094A0
:1319 FFD3 call ebx ;wsprintf
:131B 83C40C add esp, 0000000C
:131E 68A0944000 push 004094A0
:1323 56 push esi
:1324 E8D7FCFFFF call 1000_Abort_funct ;ABORT function
:1329 83C408 add esp, 00000008
:132C E9D3030000 jmp 1704_ax=0_&_ret
:continue WM_CREATE driver zero information
:1331 8D44240C lea eax, [esp + 0C]
:1335 6A00 push 00000000
:1337 50 push eax
:1338 8B0D60804000 mov ecx, [00408060]
:133E 6A00 push 00000000
:1340 8B1DE4B14400 mov ebx, [0044B1E4] ;DeviceIoControl
:1346 6A00 push 00000000
:1348 6A00 push 00000000
:134A 6A00 push 00000000
:134C 6A01 push 00000001
:134E 51 push ecx
:134F FFD3 call ebx ;DeviceIoControl
:1351 85C0 test eax, eax ;ERROR?
:1353 7518 jne 0040136D ;jump tell_driver_filter_136D
:1355 6814814000 push 00408114 ;->"Couldn't access device driver"
:135A 56 push esi
:135B E8A0FCFFFF call 1000_Abort
:1360 83C408 add esp, 00000008
:1363 B801000000 mov eax, 00000001
:1368 E999030000 jmp 1706_keep ax_&_ret
: continue_WM_CREATE_tell_driver_filter
:136D 8D44240C lea eax, [esp + 0C]
:1371 6A00 push 00000000
:1373 50 push eax
:1374 8B0D60804000 mov ecx, [00408060]
:137A 6A00 push 00000000
:137C 6A00 push 00000000
:137E 6828020000 push 00000228
:1383 68C0974000 push 004097C0
:1388 6A05 push 00000005
:138A 51 push ecx
:138B FFD3 call ebx ;DeviceIoControl
:138D 85C0 test eax, eax ; ERROR?
:138F 750E jne 0040139F ;jump_start_filtering_139F
:1391 6814814000 push 00408114 ;->"Couldn't access device driver"
:1396 56 push esi
:1397 E864FCFFFF call 1000_Abort_funct ; ABORT function
:139C 83C408 add esp, 00000008
:continue_WM_CREATE_start_filtering
:139F 8D44240C lea eax, [esp + 0C]
:13A3 6A00 push 00000000
:13A5 50 push eax
:13A6 8B0D60804000 mov ecx, [00408060]
:13AC 6A00 push 00000000
:13AE 6A00 push 00000000
:13B0 6A00 push 00000000
:13B2 6A00 push 00000000
:13B4 6A04 push 00000004
:13B6 51 push ecx
:13B7 FFD3 call ebx ;DeviceIoControl
:13B9 85C0 test eax, eax ;ERROR?
:13BB 7518 jne 004013D5 ;jump start_timer_13D5
:13BD 6814814000 push 00408114 ;->"Couldn't access device driver"
:13C2 56 push esi
:13C3 E838FCFFFF call 1000_Abort_funct ;ABORT function
:13C8 83C408 add esp, 00000008
:13CB B801000000 mov eax, 1 ;RETURN TRUE
:13D0 E931030000 jmp 1706_keep ax_&_ret
:continue_WM_CREATE_start_timer
The SetTimer function installs a system timer. A time-out value is specified,
and every time a time-out occurs, the system posts a WM_TIMER message to
the installing application's message queue or passes the message to an
application-defined TimerProc callback function.
:13D5 6A00 push 0 ;zero=to queue
:13D7 68F4010000 push 1F4 ;time out duration: 500 milliseconds
:13DC 6A01 push 1 ;timer identifier
:13DE 56 push esi ;hWnd
:13DF FF15C0B24400 Call dword ptr [0044B2C0] ;USER32.SetTimer
:13E5 A1BC964000 mov eax, [004096BC] ;get cursor
:13EA 50 push eax ;push cursor
:13EB FFD7 call edi ;SetCursor
:13ED FF15C4B24400 Call dword ptr [0044B2C4] ;ReleaseCapture
:13F3 E90C030000 jmp 1704_ax=0_&_ret ;ret, bye WM_CREATE
:WM_ = 0002 = WM_DESTROY
The WM_DESTROY message is sent when a window is being destroyed. It
is sent to the window procedure of the window being destroyed after
the window is removed from the screen.
:13F8 6A00 push 0 ;typical response to a WM_DESTROY:
:13FA FF1588B24400 Call dword ptr [0044B288] ;PostQuitMessage
:1400 E9FF020000 jmp 1704_ax=0_&_ret
:WM_ = 0005 = WM_SIZE WM_SIZE
fwSizeType = wParam; /* sizing-type flag */
nWidth = LOWORD(lParam); /* width of client area */
nHeight = HIWORD(lParam); /* height of client area */
The WM_SIZE message is sent to a window after its size has changed.
:1405 8B942460010000 mov edx, [esp + 00000160]
:140C 6A01 push 1 ;repaint flag true
:140E 8BC2 mov eax, edx
:1410 C1E810 shr eax, 10
:1413 0FB7D2 movzx word ptr edx, edx
:1416 0FB7C8 movzx word ptr ecx, eax
:1419 51 push ecx ;height
:141A A1B8964000 mov eax, [004096B8]
:141F 52 push edx ;width
:1420 6A00 push 0 ;new top
:1422 6A00 push 0 ;new left
:1424 50 push eax ;hWnd
:1425 FF15C8B24400 Call dword ptr [0044B2C8] ;MoveWindow
:142B E9D4020000 jmp 1704_ax=0_&_ret ;ret, bye WM_SIZE
:WM_ = 000F = WM_PAINT
The WM_PAINT message is sent when Windows or an application makes a
request to repaint a portion of an application's window. The message
is sent when the UpdateWindow or RedrawWindow function is called or
by the DispatchMessage function.
:1430 803D6C80400000 cmp byte ptr [806C=Deleting], 00 ;are we deleting?
:1437 7429 je 00401462 ;no, return DefWindowProc, else
:1439 8D442410 lea eax, [esp + 10] ;prepare window for painting
:143D 8BB42454010000 mov esi, [esp + 00000154]
:1444 50 push eax ;structure with paint info
:1445 56 push esi ;hWnd
:1446 FF15CCB24400 Call dword ptr [0044B2CC] ;BeginPaint,
:144C 8D442410 lea eax, [esp + 10] ;get paint info structure
:1450 50 push eax ;push paint info structure
:1451 56 push esi ;hWnd
:1452 FF15D0B24400 Call dword ptr [0044B2D0] ;EndPaint
:1458 B801000000 mov eax, 1 ;return true
:145D E9A4020000 jmp 1706_keep ax_&_ret ; bye WM_PAINT
:WM_PAINT_return DefWindowProc
The DefWindowProc function calls the default window procedure. The
default window procedure provides default processing for any window
messages that an application does not process. This function ensures
that every message is processed. It should be called with the same
parameters as those received by the window procedure.
:1462 8B942460010000 mov edx, [esp + 160]
:1469 8B8C245C010000 mov ecx, [esp + 15C]
:1470 52 push edx
:1471 51 push ecx
:1472 56 push esi ;type of msg
:1473 8BB42460010000 mov esi, [esp + 160]
:147A 56 push esi
:147B FF15D4B24400 Call dword ptr [0044B2D4] ;DefWindowProcA
:1481 E980020000 jmp 1706_keep ax_&_ret
:WM_ = 0010 = WM_CLOSE
The WM_CLOSE message is sent as a signal that a window or an application
should terminate. An application can prompt the user for confirmation
prior to destroying the window by processing the WM_CLOSE message and
calling the DestroyWindow function only if the user confirms the choice.
:1486 A160804000 mov eax, [00408060]
:148B 50 push eax
:148C FF15DCB14400 Call dword ptr [0044B1DC] ;.CloseHandle,
:1492 8B942460010000 mov edx, [esp + 00000160]
:1499 8B8C245C010000 mov ecx, [esp + 0000015C]
:14A0 52 push edx
:14A1 51 push ecx
:14A2 56 push esi
:14A3 8BB42460010000 mov esi, [esp + 00000160]
:14AA 56 push esi
:14AB FF15D4B24400 Call dword ptr [0044B2D4] ; DefWindowProcA
:14B1 E950020000 jmp 1706_keep ax_&_ret
:WM_ = 004E = WM_NOTIFY
:14B6 81BC245C010000E8030000 cmp dword ptr [esp + 15C], 3E8
:14C1 0F853D020000 jne 1704_ax=0_and_ret
:14C7 8B942460010000 mov edx, [esp + 00000160]
:14CE 837A0897 cmp [edx+08], FFFFFF97
:14D2 0F852C020000 jne 1704_ax=0_and_ret
:14D8 E920020000 jmp 004016FD ;return true
:WM_ = 0111 = WM_COMMAND
The WM_COMMAND message is sent to a window when the user selects an item
from a menu, when a control sends a notification message to its parent
window, or when an accelerator keystroke is translated. As soon as this
part of the code runs, it checks for three eventualities:
1) is it IDM_ABOUT? (which in our home-made menu is "300", i.e. 0x12C.
2)
Is it a Menu option? (And if yes, which one? 40007,40012, 40018, 40019, 40020 or 40023?)
3) is it IDM_EXIT? (i.e. "104" = 0x68)
:14DD 8B8C245C010000 mov ecx, [esp + 0000015C]
:14E4 0FB7C1 movzx word ptr eax, ecx
:14E7 3D2C010000 cmp eax, 12C ;is it IDM_= 0x12C = 300 = IDM_ABOUT?
:14EC 7F24 jg 00401512 ;go IDM > 12C: it's our MENU
:14EE 7454 je 00401544 ;go IDM_ABOUT
:14F0 83F868 cmp eax, 68 ;is it IDM_ = 0x68 = 104 = IDM_EXIT?
:14F3 7436 je 0040152B ;go IDM_EXIT
;else knows the cuckoo... fall through to
:continue WM_COMMAND: DEFAULT EXIT (DefWindowProc and ret)
:14F5 8B942460010000 mov edx, [esp + 00000160]
:14FC 52 push edx
:14FD 51 push ecx
:14FE 56 push esi
:14FF 8BB42460010000 mov esi, [esp + 00000160]
:1506 56 push esi
:1507 FF15D4B24400 Call dword ptr [0044B2D4] ;DefWindowProcA,
:150D E9F4010000 jmp 1706_keep ax_&_ret
:continue WM_COMMAND: IDM_ > 12C... it's a MENU option
:1512 2D479C0000 sub eax, 9C47 ;magic subtract
:1517 83F810 cmp eax, 10
:151A 77D9 ja 004014F5 ;DEFAULT exit
:151C 33D2 xor edx, edx
:151E 8A9030174000 mov dl, [eax+00401730]
:1524 FF249514174000 jmp dword ptr [4*edx + 00401714]
;Call all other IDMs following table at 1714
;and ret
A short digression about "menu tables"
As you can see, each dword pointer points to a different menu option... how do you get them?, Well, look at the code, it SUBTRACTs first of all from the menu item the "magic" 9C47, which corresponds to 40007 (the first menu value for "Save"). Look at the menu "values": (through BRW: Borland's resources workshop):
MENUITEM "&Save...", 40007
MENUITEM "Save &As...", 40012
MENUITEM SEPARATOR
MENUITEM "E&xit", 104
MENUITEM "&Filter...", 40023
MENUITEM SEPARATOR
MENUITEM "&Capture Events", 40018, CHECKED
MENUITEM "&Auto Scroll", 40019, CHECKED
MENUITEM SEPARATOR
MENUITEM "C&lear Display", 40020
You see? 40007=0=Save; 40012=+5=SaveAs; 40018=+B=CaptureEvents; 400019=+C=AutoScroll; 40020=+D=ClearDisplay; 40023=+10=filter... Now look at line :151E mov dl, [eax+00401730]In your dead listing is probably bad interpreted by wdasm... it is a simple "relocation table" here you are:
:1730 00 06 06 06 06 01 06 06 Save Exit Exit Exit Exit SaveAs Exit Exit
:1738 06 06 06 02 03 04 06 06 Exit Exit Exit Capture Autoscll Clear Exit Exit
:1740 05
Fltr
Therefore 0=0; 5=1; B=2; C=3; D=4; 10=5 and more than 10 is out and all the rest is 6... this is VERY IMPORTANT if you want to understand alien "compiled" code you do not know the purpose of... this means that the "relocation" of the various ID_TAGS of a menu after subtracting it with the magic (hexadecimal) number (saveas is +5 in relation to Save, which is 0) are used to fetch the CORRECT multiplication factor (in the case of "SaveAs" is 01, in the case of "Filter" is 05... and these 01 and 05 are used as relocators to fetch the corresponding routine from the table:
Have a look, you'll notice immediately that there are seven possible jumps:
:1714 69154000 DWORD 00401569 ;IDM_SAVE relocator = 00 (40007-40007=0)
:1718 86154000 DWORD 00401586 ;IDM_SAVEAS relocator = 01 (40012-40007=5)
:171C A3154000 DWORD 004015A3 ;IDM_Captre relocator = 02 (40018-40007=B)
:1720 E0154000 DWORD 004015E0 ;IDM_Atscll relocator = 03 (40019-40007=C)
:1724 1D164000 DWORD 0040161D ;IDM_Clear relocator = 04 (40020-40007=D)
:1728 7B164000 DWORD 0040167B ;IDM_Filter relocator = 05 (40023-40007=10)
:172C F5144000 DWORD 004014F5 ;IDM_Exit relocator = 06 (all other cases)
I think you understand now one of the "intricacies" of windows reverse engineering... the 6 lines of code we saw above:
:1512 2D479C0000 sub eax, 9C47 ;magic subtract 40007
:1517 83F810 cmp eax, 10 ;did we get more than maximum?
:151A 77D9 ja 004014F5 ;if so DEFAULT exit
:151C 33D2 xor edx, edx ;make sure it's zero
:151E 8A9030174000 mov dl, [eax+1730] ;get relocator 00-06
:1524 FF249514174000 jmp dword ptr [4*edx + 1714] ;jmp to relocator line
Are now pretty strightforward, d'you agree?
You may ask yourself why the programmers have chosen these values... well, they have not... that's Microsoft Developer Studio here... with
time you'll get acquainted with all different compiler peculiarities... have a look at
the "resource.h" text file inside the packahe of filemon you downloaded... everything is there. But you don't need to have a resource file to grasp this... good old WCB will show it to you, or, alternatively, just hexedit your windows target... look towards the end of the hexedited file... there you'll find all imported functions and
a little after, at 8720, for instance:
00000000000000000800529C26004300 ..........R.&.C.
61007000740075007200650020004500 a.p.t.u.r.e. .E.
760065006E007400730000000800539C v.e.n.t.s. S.
26004100750074006F00200053006300 &.A.u.t.o. S.c.
72006F006C006C000000000000000000 r.o.l.l.........
You understand what that means, don't you... 529C is the VALUE of the Capture Events option and 539C is the VALUE (0x9C53=40019) of the Autoscroll menu option... all windows targets carry these IMPORTANT information within themselves, you
just sit there fishing all these names out of the unnamed see until your dead listing makes more sense than the original source code of the programmer itself!
:continue WM_COMMAND: IDM_ = 68 = IDM_EXIT
:152B 8BB42454010000 mov esi, [esp + 00000154]
:1532 6A00 push 00000000
:1534 6A00 push 00000000
:1536 6A10 push 00000010 ;WM_CLOSE
:1538 56 push esi
:1539 FF15D8B24400 Call dword ptr [0044B2D8] ;SendMessage (WM_CLOSE)
:153F E9C0010000 jmp 1704_ax=0_&_ret ;bye bye everybody
:continue WM_COMMAND: IDM_ = 12C IDM_ABOUT
The DialogBoxParam function creates a modal dialog box from a dialog
box template resource. "dlgprc" specifies the procedure-instance address
of the dialog box procedure.
:1544 8BB42454010000 mov esi, [esp + 00000154]
:154B 6A00 push 00000000 ;lParamInit;
:154D 6890214000 push 00402190 ;dlgProc = Function ABOUT
:1552 A1E8994000 mov eax, [004099E8] ;get hInst
:1557 56 push esi ;hWndOwner
:1558 6800814000 push 00408100 ;lpszDlgTem: "AboutBox"
:155D 50 push eax ;hInst
:155E FF15E4B24400 Call dword ptr [0044B2E4] ;DialogBoxParam
:1564 E99B010000 jmp 1704_ax=0_&_ret ;bye WM_COMMAND
:IDM_SAVE, we land here through the relocation table
:1569 6A00 push 00000000
:156B A1B8964000 mov eax, [004096B8]
:1570 8BB42458010000 mov esi, [esp + 00000158]
:1577 50 push eax
:1578 56 push esi
:1579 E8A2060000 call 00401C20=savefile
:157E 83C40C add esp, C
:1581 E97E010000 jmp 1704_ax=0_&_ret
:IDM_SAVEAS, we land here through the relocation table
:1586 6A01 push 00000001
:1588 A1B8964000 mov eax, [004096B8]
:158D 8BB42458010000 mov esi, [esp + 00000158]
:1594 50 push eax
:1595 56 push esi
:1596 E885060000 call 00401C20=savefile
:159B 83C40C add esp, C
:159E E961010000 jmp 1704_ax=0_&_ret
:IDM_Capture, we land here through the relocation table
;since the first thing it does is checking TRUE a
;memory location, we'll call it 8064_Capture
:15A3 803D6480400001 cmp byte ptr [8064_Capture], 01 ;if capture
:15AA 1AC0 sbb al , al ;let's have
:15AC F6D8 neg al ;no capture
:15AE A264804000 mov [8064_Capture], al ;in 8064 or
:15B3 3C01 cmp al, 01 ;the other way
:15B5 B800000000 mov eax, 00000000 ;round
:15BA 8BB42454010000 mov esi, [esp + 00000154]
:15C1 83D0FF adc eax, FFFFFFFF
:15C4 83E008 and eax, 00000008
:15C7 50 push eax
:15C8 68529C0000 push 00009C52 ;LISTMENU, Item: "Capture Events"
:15CD 56 push esi
:15CE FF15DCB24400 Call dword ptr [0044B2DC] ;GetMenu,
:15D4 50 push eax
:15D5 FF15E0B24400 Call dword ptr [0044B2E0] ;CheckMenuItem,
:15DB E924010000 jmp 1704_ax=0_&_ret
The CheckMenuItem function selects (places a check mark next to) or clears (removes a check mark from) a specified menu item in the given pop-up menu.
:IDM_Autoscroll, we land here through the relocation table
;since the first thing it does is checking TRUE a memory
;location, we'll call it 8068_Atscll... see how important
;are these "WM_COMMAND's IDMs" for tagging purposes?
:15E0 803D6880400001 cmp byte ptr [8068_Atscll], 01 ;see above
:15E7 1AC0 sbb al , al ;the same as
:15E9 F6D8 neg al ;for capture
:15EB A268804000 mov [8068_Atscll], al
:15F0 3C01 cmp al, 01
:15F2 B800000000 mov eax, 00000000
:15F7 8BB42454010000 mov esi, [esp + 00000154]
:15FE 83D0FF adc eax, FFFFFFFF
:1601 83E008 and eax, 00000008
:1604 50 push eax
:1605 68539C0000 push 00009C53 ;"Auto Scroll"
:160A 56 push esi
:160B FF15DCB24400 Call dword ptr [0044B2DC] ;GetMenu
:1611 50 push eax ;and places or remove checkmark
:1612 FF15E0B24400 Call dword ptr [0044B2E0] ;CheckMenuItem;
:1618 E9E7000000 jmp 1704_ax=0_&_ret
:IDM_CLEAR, we land here through the relocation table
:161D 8D44240C lea eax, [esp + 0C]
:1621 6A00 push 00000000
:1623 50 push eax
:1624 8B0D60804000 mov ecx, [00408060]
:162A 6A00 push 00000000
:162C 6A00 push 00000000
:162E 6A00 push 00000000
:1630 6A00 push 00000000
:1632 6A01 push 00000001
:1634 51 push ecx
:1635 FF15E4B14400 Call dword ptr [0044B1E4] ;DeviceIoControl
:163B 85C0 test eax, eax ;lost device?
:163D 751F jne 0040165E ;works, go update
:163F 8BB42454010000 mov esi, [esp + 00000154]
:1646 6814814000 push 00408114 ;->"Couldn't access device driver"
:164B 56 push esi
:164C E8AFF9FFFF call 1000_Abort
:1651 83C408 add esp, 00000008
:1654 B801000000 mov eax, 00000001
:1659 E9A8000000 jmp 1706_keep ax_&_ret
:continue IDM_CLEAR: Update
:165E 6A01 push 00000001
:1660 A1B8964000 mov eax, [004096B8]
:1665 8BB42458010000 mov esi, [esp + 00000158]
:166C 50 push eax
:166D 56 push esi
:166E E8CD030000 call 00401A40 ;UpdateStatistic
:1673 83C40C add esp, 0000000C
:1676 E989000000 jmp 1704_ax=0_&_ret
:IDM_FILTER, we land here through the relocation table
;Calls FilterProc (same as for the About case)
:167B 8BB42454010000 mov esi, [esp + 00000154]
:1682 6A00 push 00000000
:1684 68D01E4000 push 00401ED0 ;FilterProc!
:1689 A1E8994000 mov eax, [004099E8]
:168E 56 push esi
:168F 680C814000 push 0040810C ;->"Filter"
:1694 50 push eax
:1695 FF15E4B24400 Call dword ptr [0044B2E4] ;USER32.DialogBoxParamA
:169B EB67 jmp 1704_ax=0_&_ret
:WM_ = 0113 = WM_TIMER
The WM_TIMER message is posted to the installing application's message
queue or sent to the appropriate TimerProc callback function after each
interval specified in the SetTimer function used to install a timer. Location
[8064] is already called 8064_Capture here, because we already investigated it
above.
How do we know, below, that "44B1E4" corresponds to DeviceIoControl?
There are two possibilities: luck (search for 44B1E4 inside the dead listing,
may be you are lucky and the programmer has called it DIRECTLY somewhere else) or a little "zen" feeling (number of parameters, context, various feelings). Here it is luck :-)
:169D 803D6480400000 cmp byte ptr [8064_Capture], 00
:16A4 745E je 00401704 ;return if no capture
:16A6 8B1DE4B14400 mov ebx, [0044B1E4] ;DeviceIoControl
:16AC 8BB42454010000 mov esi, [esp + 154]
:16B3 33FF xor edi, edi ;make sure edi is zero!
:loop_16B5
:16B5 57 push edi ;0
:16B6 A160804000 mov eax, [00408060]
:16BB 68B4964000 push 004096B4 ;push Statslen loc
:16C0 6800000400 push 00040000 ;SizeofStats
:16C5 68F0994000 push 004099F0
:16CA 57 push edi ;0
:16CB 57 push edi ;NULL
:16CC 6A02 push 00000002
:16CE 50 push eax
:16CF FFD3 call ebx ;DeviceIoControl
:16D1 85C0 test eax, eax ;ERROR?
:16D3 741A je 004016EF ;Abort
:16D5 393DB4964000 cmp [004096B4], edi ;Statslen=0?
:16DB 7427 je 00401704 ;exit loop return FALSE
:16DD 57 push edi
:16DE A1B8964000 mov eax, [004096B8]
:16E3 50 push eax
:16E4 56 push esi
:16E5 E856030000 call 00401A40 ;UpdateStatistic
:16EA 83C40C add esp, C
:16ED EBC6 jmp 004016B5 ;loop_16B5
:abort
:16EF 6814814000 push 00408114 ;->"Couldn't access device driver"
:16F4 56 push esi
:16F5 E806F9FFFF call 1000_Abort_funct
:16FA 83C408 add esp, 00000008
:16FD_return true
:16FD B801000000 mov eax, 1 ;flag return value TRUE
:1702 EB02 jmp 1706_keep ax_&_ret
:1704_ax=0_and_ret
:1704 33C0 xor eax, eax
:1706_keep_ax_and_ret
:1706 5F pop edi
:1707 5E pop esi
:1708 5B pop ebx
:1709 81C444010000 add esp, 00000144
:0040170F C21000 ret 0010 ;bye MainWndProc
:1712 8BFF mov edi, edi
:IDM_jump table for WM_COMMAND
:1714 69154000 DWORD 00401569 ;IDM_SAVE
:1718 86154000 DWORD 00401586 ;IDM_SAVEAS
:171C A3154000 DWORD 004015A3 ;IDM_CAPTURE
:1720 E0154000 DWORD 004015E0 ;IDM_AUTOSCROLL
:1724 1D164000 DWORD 0040161D ;IDM_CLEAR
:1728 7B164000 DWORD 0040167B ;IDM_FILTER
:172C F5144000 DWORD 004014F5 ;IDM_EXIT
:IDM_ relocation table for WM_COMAND, rewritten, since Wdasm
;does not interpret this snippet correctly
:1730 00 06 06 06 06 01 06 06
:1738 06 06 06 02 03 04 06 06
:1740 05
Well, we are almost DONE! We'll tackle the device driver in the next (and last) part.
(c) fravia+ 1997. All rights reserved.
You are deep inside fravia's page of reverse engineering,
choose your way out:
filemon1
filemon2
filemon3
filemon5
homepage
links
anonymity
+ORC
students' essays
tools cocktails
antismut
search_forms
mailFraVia
is reverse engineering legal?