LaZ-Calc
Adding functionality to the Windows Calculator
Advanced
Advanced
14 September 1999
by LaZaRuS
Courtesy of Fravia's page of reverse engineering
fra_00xx
980914
LaZaRuS
0010
AD
PC
A "must read" reversing essays for all true Fravias out there: adding functionalities to programs is the sublimest reversing art, like Sung-style ink painting, and if LaZaRuS will bless us with more essays along these lines I will open a special (and I am sure most coveted) section for this kind of essays.
Feedback on this essay is MOST important. I hope many will work on this and contribute.
There is a crack, a crack in everything That's how the light gets in
Rating
( )Beginner ( )Intermediate (x)Advanced ( )Expert

A small example to the (for me) most interesting part of reverse engineering: Adding functionality to a program.
LaZ-Calc
Adding functionality to the Windows calculator
Written by LaZaRuS


Introduction
You surely know this: You enter huge formula into the Windows Calculator and see that the result must be wrong. You made a mistake and this means: Reentering the complete formula once more (this time with more attention). In this essay I will describe how to add an edit field to the calculator, where you can enter this formula, so you can easily correct it, when a problem should appear. Well, what should I say: An interesting essay for guys that feel like I do :)

Tools required
Let's see, I used quite many tools this time:
Soft-Ice (for debugging and assembling in memory)
W32Dasm (for deadlistings)
Hex-Workshop (for applying changes to the file)
Borland Ressource Workshop (for adding objects/strings)
Customizer (for simulating different things)
MASM (for compiling some ASM code)
A Win-API reference is necessary

Target's URL/FTP
Target is the wrong expression, as this is no cracking essay, but we deal with the Windows calculator which was shipped with Win95. You will find the English original (which I used) inside lazcalc.zip.

Program History
Don't think there's something important to say here.

Essay
At first I define what exactly we want to have when this essay is finished: We need an edit box,
a checkbutton, a normal button and a new menu point "LaZ-Calc" with the sub-menus "Center 
Window", "Stay on Top", "Start Notepad", "Help", "About", "Quit". Look at the screenshot 
scr1.gif or at lazcalc.exe that comes shipped inside lazcalc.zip to see how it should look like.
We add the components (buttons, menus...) with the Borland Ressource workshop. I won't explain
how this works, only the most important facts about the components:
The edit field, the check button and the standard button are added to the ressource "DIALOG"-
"SC", the Menus are added to "MENU"-"SM". It is important to add the "LaZ-Calc"-Menu after the
last menu (which is "Help"). If you don't do this, the menu will screw up, as the (original)
menus are not checked or enabled through the ident, but through the position.
The "Center Window" menu has the ID 666, the "Stay on Top" has 667, "Start Notepad" is 668, 
"Help" is 669, "About" has 670 and "Quit" has 672. Why not 671? I don't know, just saw it right
in this moment. Must have been a typo, when I first created it. You can use 671 if you want, but
don't forget to apply the change to the sourcecode that comes later.
Now we need to add some bytes where we can add code. There are some "caves" (many following 
00-Bytes), that cannot be used, as they are too small. Now comes the first weakness of my essay:
As I still can't "mess" with the PE-header, I needed another way to enlarge the file size. I
just added a huge string with BRW to the file. I guessed the size of this string and was lucky
at the end, as I nearly ran out of space. I needed 392h (=914) bytes, so you gotta create one
or more strings with a length of 457d chars, as BRW adds them in Unicode-Format with a 00 
between two chars and you get double the amount of bytes of the string length. To see how my 
file looked like at the end, see hexdump.txt. If you say that this is a lame way, at least 
recocgnize that I found a nice workaround. :)

Now, the edit field. We have to be able to enter a serial. That is a small problem, as when you
try to enter something, the key is interpreted as hotkey and immediately a message to the
corresponding button is send. That's why I have created the check box. If it is checked, it
should be possible to enter formulas and if it is not checked, the keys you press should be
immediately interpreted as hotkeys. So, we have to find the code that does check, if a hotkey 
is pressed. What API functions can be used? Don't even try to look for "Hotkey" in your 
API-reference. The thing we are dealing with is called "Accelerator". The "Accelerator Table" 
can be viewed in BRW, but at this moment, we cannot do anything useful with it. Here comes a 
short extract from my API reference about the fuction we will deal with: TranslateAccelerator

The TranslateAccelerator function processes accelerator keys for menu commands. The function 
translates a WM_KEYDOWN or WM_SYSKEYDOWN message to a WM_COMMAND or WM_SYSCOMMAND message 
(if there is an entry for the key in the specified accelerator table) and then sends the 
WM_COMMAND or WM_SYSCOMMAND message directly to the appropriate window procedure.

int TranslateAccelerator(
    HWND hWnd,	        // handle of destination window
    HACCEL hAccTable,	// handle of accelerator table
    LPMSG lpMsg 	// address of structure with message
   );

So, this function checks, if the key (or keycombination) that was pressed is an Accelerator. If
so, it sends a WM_COMMAND to the button, that is associated with that key, which means the 
button gets pressed and the function that this button has is executed. Now it is time to get a
disassembling of calc.exe for searching the place TranslateAccelerator is called. You will find
no direct call, but the following code which moves the address where the function is located
is moved to edi.

* Reference To: USER32.TranslateAcceleratorA, Ord:020Ch
                                  |
:00401307 8B3DFCE34000            mov edi, dword ptr [0040E3FC]

Few lines below, you will find that call to edi.

:0040132B 8D45DC                  lea eax, dword ptr [ebp-24]  ;; eax gets addr of MSG struct
:0040132E 50                      push eax

:0040132F FF3598B44000            push dword ptr [0040B498] ;; address of AcceleratorTable
:00401335 FF35D8B54000            push dword ptr [0040B5D8] ;; Handle of main window
:0040133B FFD7                    call edi ;; call TranslateAccelerator
:0040133D 85C0                    test eax, eax ;; was key an Accelerator (eax=1) ?
:0040133F 7510                    jne 00401351 ;; if so, then jump

Now we have to redirect the flow of instructions to an area of code we have created in order
to find out, if the checkbutton is checked and executes what to do then. My solution looks like
this:

:0040132B E900050100              jmp 00411830
:00401330 90                      nop
:00401331 90                      nop
:00401332 90                      nop
:00401333 90                      nop
:00401334 90                      nop
:00401335 90                      nop
:00401336 90                      nop
:00401337 90                      nop
:00401338 90                      nop
:00401339 90                      nop
:0040133A 90                      nop
:0040133B 90                      nop
:0040133C 90                      nop
:0040133D 85C0                    test eax, eax
:0040133F 7510                    jne 00401351

It is not necessary to nop out all the pushes and the call, but I had aesthetical reasons for
doing this. I prefer compact code where the pushes are directly in front of the call and not 
somewhere in our redirected code. Well, the jump at :004132B goes to offset DA30h in the file.
Here I added the following code:

:00000000 60                      pushad ;; save all registers
:00000001 669C                    pushf ;; save all flags
:00000003 68AEEA4000              push 0040EAAE ;; push "USER32.DLL"
:00000008 FF1590E24000            call dword ptr [0040E290] ;; call GetModuleHandleA
:0000000E 682D1B4100              push 00411B2D ;; push "IsDlgButtonChecked"
:00000013 50                      push eax ;; push Handle of User32.dll
:00000014 FF15DCE24000            call dword ptr [0040E2DC] ;; call GetProcAddress
:0000001A 68A3020000              push 000002A3 ;; push handle of check box (2A3=675)
:0000001F FF35D8B54000            push dword ptr [0040B5D8] ;; push handle of main window
:00000025 FFD0                    call eax ;; call IsDlgButtonChecked
:00000027 0BC0                    or eax, eax ;; check eax
:00000029 751A                    jne 00000045 ;; if eax == 1 (Button checked), then jump
:0000002B 669D                    popf ;; get all flags from stack
:0000002D 61                      popad ;; get all registers from stack
:0000002E 8D45DC                  lea eax, dword ptr [ebp-24] ;; here comes exactly
:00000031 50                      push eax                    ;; the same code
:00000032 FF3598B44000            push dword ptr [0040B498]   ;; that was there 
:00000038 FF35D8B54000            push dword ptr [0040B5D8]   ;; before I redirected
:0000003E FFD7                    call edi                    ;; the instructions
:00000040 E9BBFAFEFF              jmp FFFEFB00                ;; jump back
:00000045 669D                    popf ;; get all flags from stack
:00000047 61                      popad ;; get all registers from stack
:00000048 33C0                    xor eax, eax ;; IMPORTANT: set eax to 0
:0000004A E9B1FAFEFF              jmp FFFEFB00 ;; jump back

Well, let's analyse:
The pushad/pushf is absolutely necessary as we have to pass all register and flags and their
current states to the call edi" (TranslateAccelerator) and they are messed up, when we call 
all the other functions before. We have to find out, if the checkbox is checked. We can use 
the API-function "IsDlgItemChecked" for this purpose. Here starts the first problem: You can
assemble directly in the RAM using SICE, but if you enter "a"-"call IsDlgButtonChecked" SICE
will create a *direct* call to the Kernel of your system. That means it will create Opcodes
that are only valid on your machine. So we have to find another way, an indirect way: I 
demonstrate with GetModuleHandleA, as IsDlgButtonChecked will have another problem. Get into
your disassembling in W32Dasm and search for "GetModuleHandleA" somewhere in the middle of the
code. The first appearance will be here:


Remark 1:
Don't think I guessed the source I present you here. I coded a test app, that should simulate 
the Windows calculator. Here I developed all functions first. You should do this, too as it
is quite annoying to code larger part directly in SICE (as I experienced many bugs in my V3.23).
End of Remark


* Reference To: KERNEL32.GetModuleHandleA, Ord:010Eh
                                  |
:0040544C FF1590E24000            Call dword ptr [0040E290]

Here we see an example for an indirect call. The function GetModuleHandleA can be called by
a call to dword ptr [0040E290]. We can use this value for our purposes, too. But wait, why do
we deal with this function? We need the dword ptr address for "IsDlgItemChecked". Well, search
for it and you'll find nothing. This function isn't automatically loaded, so we have to do it.
For this purpose we need GetModuleHandleA and GetProcAddress. GetModuleHandleA gets the handle
of a module (read: DLL-file in RAM). As IsDlgItemChecked is part of USER32.DLL (see W32Dasm
API definitions of another file, that uses this function) we have to find out the handle of 
this DLL. The string User32.dll is surely to be found somewhere inside calc.exe - So search for
it and you'll find it at offset BEAEh which is :0040EAAE during runtime (s 00400000 l ffffffff
'USER32.dll' to find this address). So we need to "push" this string and then call dword ptr
[0040E290] which is an indirect call to GetModuleHandleA. Then we need to call the function
GetProcAddress to find out where the IsDlgItemChecked it located. We need to push a string with
the name of that function. As there is no such string, we have to create it: I located it at
offset DD2Dh. The second push (push eax) is the handle of USER32.DLL which was given back from
the first call. After GetProcAddress eax contains the location of IsDlgButtonChecked and we
can use it with "call eax". IsDlgButtonChecked wants two parameters. The first one is the handle
of the checkbox, the second one the handle of the main window. Now watch out: There are two
possible opcodes that represent "push 2A3" (for the checkbox handle) - If you assemble with 
SICE, the wrong one will be chosen and the API function will only return crap. The reason seems
to be that the API wants a dword as parameter, but the "wrong" SICE-opcode pushes a word. A
small workaround is pushing a dword at first (like "push 11111") and then changing the opcode
with the "e"-command. The rest of the above code should be easy to understand. If the
checkbox is checked, eax gets cleared and the program will keep on running, as if no key was
pressed. If the checkbox is unchecked, TranslateAcceleratorA (call edi) will be called and the
return value will be used for the further execution of the MessageLoop. It is important to clear
eax when the button is checked, or we will mess up the MessageLoop!

Now we have changed the MessageLoops in a way, that we should be able to enter chars into the
edit field, when the checkbox is checked. If it is unchecked, the keys we press are interpreted
as hotkeys. Let's turn to the next thing-to-do now. We have to find the code that is executed
when a menu is chosen by the user. In W32Dasm I have chosen "Menu Reference" and then "Menu: 
SM, Item: "Copy   Ctrl+C"" - Look where this appears first time any you'll see:

* Possible Ref to Menu: SM, Item: "Paste   Ctrl+V"
                                  |
:00403161 3D2D010000              cmp eax, 0000012D
:00403166 7725                    ja 0040318D

* Possible Ref to Menu: SM, Item: "Copy   Ctrl+C"
                                  |
:00403168 3D2C010000              cmp eax, 0000012C
:0040316D 0F83A1060000            jnb 00403814

This code looks is a first part of the routine, that checks which Menu was chosen. 12D and 12C
are the handles of the menupoints. If you put a breakpoint on :00403161 you and choose a menu
you will see that it breaks. So, we need to insert a jump to another self-coded part here.
I made it this way:

:00403161 E91AE70000              jmp 00411880 ;; this jump leads to our next section
:00403166 7725                    ja 0040318D

* Possible Ref to Menu: SM, Item: "Copy   Ctrl+C"
                                  |
:00403168 3D2C010000              cmp eax, 0000012C
:0040316D 0F83A1060000            jnb 00403814

Well, we have to make thoughts what to do in the section we jump to. Btw: We still have the
problem, that we have to handle clicks on the button and the checkbox. No, we don't have this
problem. When you push the button (with breakpoint on :00403161) you will see that it'll break
there, too. So we can use the jump to :00411880 for this purpose, too :)

Now let's look at the first code I inserted at :00411880 (which is at offset DA80h):
At first I handle the click at the checkbox. If the checkbox is checked, the button should be
enabled, if not it should be disabled.

:00000000 3DA3020000              cmp eax, 000002A3 ;; is eax the handle of the checkbox?
:00000005 753D                    jne 00000044 ;; if not, then jump
:00000007 803558B6400001          xor byte ptr [0040B658], 01 ;; see explanation below
:0000000E 90                      nop ;; see explanation below
:0000000F 90                      nop
:00000010 90                      nop
:00000011 90                      nop
:00000012 90                      nop
:00000013 90                      nop
:00000014 90                      nop
:00000015 90                      nop
:00000016 90                      nop
:00000017 90                      nop
:00000018 90                      nop
:00000019 90                      nop
:0000001A 90                      nop
:0000001B 90                      nop
:0000001C 90                      nop
:0000001D 90                      nop
:0000001E 90                      nop
:0000001F 90                      nop
:00000020 68A1020000              push 000002A1 ;; ID of the button
:00000025 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of the main window
:0000002B FF1540E34000            call dword ptr [0040E340] ;; call GetDlgItem
:00000031 8B1D58B64000            mov ebx, dword ptr [0040B658] ;; get Enable-flag value in ebx
:00000037 53                      push ebx ;; Enable-flag
:00000038 50                      push eax ;; handle of the button
:00000039 FF15DCE34000            call dword ptr [0040E3DC] ;; call EnableWindow
:0000003F E9A218FFFF              jmp FFFF18E6 ;; jump to :00403166

So, let's analyze the code. At first I wanted to use the API-function IsDlgButtonChecked, to
find out, if the button has to be enabled. As this API function is not loaded by calc.exe I
have chosen to set a flag for the status of the checkbox. The checkbox is not checked by 
default, so I searched a byte that is 0 by default and that is unused by the Calculator.
I found it at 40B658. If this byte is 0, the checkbox is unchecked, if it is 1 the button is
checked. At first I changed the state of this flag with something like this:

if (flag==0)
  flag=1;
elseif (flag==1)
  flag=0;

That resulted in a bunch of crappy code (where the NOPs are now). That was unacceptable for me,
and I made some thoughts how to change the flag shorter. The solution is XORing the flag with 1.
When it is 0 (at the start) it will become 1, if it is 1 it will become 0. Removing the NOPs
would only work if I rewrite my code again and update the jumps, but I am too lazy for this.
The rest should be clear (at least with an API reference). I get the current handle of the
button with GetDlgItem and then I enable it with EnableWindow where I use the flag in 40B5D8
as parameter for the API.

Now the most important section, the interpretation of the formula in the edit box. The first
thought that has to be done is, if the formula should be entered as "correct" formula, or as
a formula made created with the hotkey of the Windows calculator. I decided for the second one,
as this is *much* easier to code. The formula must be entered into the edit field in exactly
the same way you would enter it directly. Here is an example:

Correct formula: (3*2+sin(5))^2 (result=37,05346503647)
Hotkey formula : (3*2+5s)@ (result=37,05346503647)

The deal should be pretty easy: Read the text of the edit field char by char and then... Yeah,
what then? I tried several possibilities. At first I wanted to send a BN_CLICK to the button
that is associated with the key, but this caused several problems. It would have been quite
a huge if-elseif-else construct to find out the related button. Second problem: The calc.exe
buttons are no real buttons. Neither SICE, nor Customizer can find a handle for them. Next thing
I tried: Simulating a key with WM_CHAR (and WM_KEYDOWN). Didn't work, too. You can easily see 
what happens, if you use Customizer to send a WM_CHAR message. Now I decided the correct 
solution: A direct call to TranslateAcceleratorA should work. And it worked! See the next part
of the source below:

:00000044 3DA1020000              cmp eax, 000002A1 ;; handle of the button?
:00000049 7577                    jne 000000C2 ;; if not, then jump
:0000004B 68A2020000              push 000002A2 ;; ID of edit field
:00000050 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of main window
:00000056 FF1540E34000            call dword ptr [0040E340] ;; call GetDlgItem
:0000005C 6A32                    push 00000032 ;; push maximum length of text (32h=50)
:0000005E 6888B64000              push 0040B688 ;; buffer where to write text from edit
:00000063 50                      push eax ;; handle of edit field
:00000064 FF15A8E34000            call dword ptr [0040E3A8] ;; GetWindowTextA
:0000006A 3D00000000              cmp eax, 00000000 ;; Length of text = 0?
:0000006F 7451                    je 000000C2 ;; if so, then jump
:00000071 BE88B64000              mov esi, 0040B688 ;; buffer of text
:00000076 AC                      lodsb ;; get one char in al
:00000077 0AC0                    or al, al ;; is char=0?
:00000079 7447                    je 000000C2 ;; if so, jump
:0000007B 8AD8                    mov bl, al ;; move char to bl
:0000007D B880FD6400              mov eax, 0064FD80 ;; address of the MSG structure
:00000082 80FB61                  cmp bl, 61 ;; if BL < a
:00000085 7208                    jb 0000008F ;; then jump
:00000087 80FB7A                  cmp bl, 7A ;; if BL > z
:0000008A 7303                    jnb 0000008F ;; then jump
:0000008C 80EB20                  sub bl, 20 ;; convert small letter to capital letter
:0000008F 885808                  mov byte ptr [eax+08], bl ;; see below
:00000092 80FB41                  cmp bl, 41 ;; if BL < A
:00000095 720D                    jb 000000A4 ;; then jump
:00000097 80FB59                  cmp bl, 59 ;; if BL > Y
:0000009A 7708                    ja 000000A4 ;; then jump
:0000009C 66C740040001            mov [eax+04], 0100 ;; 0100=ASCII
:000000A2 EB06                    jmp 000000AA
:000000A4 66C740040201            mov [eax+04], 0102 ;; 0102=Virtual Key
:000000AA 50                      push eax ;; Here comes
:000000AB FF3598B44000            push dword ptr [0040B498] ;; the call
:000000B1 FF35D8B54000            push dword ptr [0040B5D8] ;; to TranslateAcceleratorA
:000000B7 FF15FCE34000            call dword ptr [0040E3FC] ;; again
:000000BD E9B4FFFFFF              jmp 00000076 ;; jump back and get next char

Getting the handle of the edit field and reading the text of it should be pretty clear. The
only question is how I found out where to save the Text. I just looked around in SICE until I 
found a vast area with 00-bytes. That became my buffer. Next question should comes at line 7D.
How did I find the value 64FD80? Take the unpatched calc.exe and set a breakpoint on Translate-
AcceleratorA. Eax will *always* have the value 64FD80, when it breaks regularly. This is the 
address of the MSG-structure that is passed to TranslateAcceleratorA. To understand what 
happens then, we have to look at the definition of the MSG structure:

typedef struct tagMSG {
    HWND   hwnd;	 // DWORD (eax)
    UINT   message;      // DWORD (eax+4)
    WPARAM wParam;       // DWORD (eax+8)
    LPARAM lParam; 
    DWORD  time; 
    POINT  pt; 
} MSG;

Now, for simulating an Accelerator, we have to change the message and the wParam to our needs.
The wParam is the key that should be simulated ([eax+8]), the message ([eax+4] contains either 
"VKey" or "ASCII". See the AcceleratorTable in BRW and you should understand what to use for
which key. At the end we just have to "push" the parameters and call TranslateAcceleratorA. 
The hardest things are done now, let's head for the menus.
The first menu I will handle is "Center Window". The menupoint has the handle 29Ah.

:000000C2 3D9A020000              cmp eax, 0000029A ;; is "Center Window" chosen?
:000000C7 756E                    jne 00000137 ;; if not, then jump
:000000C9 8D45AC                  lea eax, dword ptr [ebp-54] ;; eax=adress of RECT structure
:000000CC 50                      push eax ;; RECT
:000000CD FF35D8B54000            push dword ptr [0040B5D8] ;; handle of main window
:000000D3 FF151CE34000            call dword ptr [0040E31C] ;; GetWindowRect
:000000D9 68AEEA4000              push 0040EAAE ;; "USER32.DLL"
:000000DE FF1590E24000            call dword ptr [0040E290] ;; GetModuleHandle
:000000E4 681C1B4100              push 00411B1C ;; "GetSystemMetrics"
:000000E9 50                      push eax ;; handle of User32.dll
:000000EA FF15DCE24000            call dword ptr [0040E2DC] ;; GetProcAdress
:000000F0 50                      push eax ;; save adress of GetSystemMetrics
:000000F1 6A00                    push 00000000 ;; SM_CXSCREEN
:000000F3 FFD0                    call eax ;; GetSystemMetrics
:000000F5 2B45B4                  sub eax, dword ptr [ebp-4C] ;; these few lines
:000000F8 0345AC                  add eax, dword ptr [ebp-54] ;; calculate the
:000000FB D1E8                    shr eax, 1 ;; new position of the right border
:000000FD 8BD8                    mov ebx, eax ;; and save it in ebx
:000000FF 58                      pop eax ;; load adress of GetSystemMetrics
:00000100 6A01                    push 00000001 ;; SM_CYSCREEN
:00000102 FFD0                    call eax ;; GetSystemMetrics
:00000104 2B45B8                  sub eax, dword ptr [ebp-48] ;; these few line
:00000107 0345B0                  add eax, dword ptr [ebp-50] ;; calculate the
:0000010A D1E8                    shr eax, 1 ;; new position of the upper border
:0000010C 50                      push eax ;; and save it 
:0000010D 8B45B0                  mov eax, dword ptr [ebp-50] ;; these two lines calculate
:00000110 2945B8                  sub dword ptr [ebp-48], eax ;; the width of the window
:00000113 8B45AC                  mov eax, dword ptr [ebp-54] ;; these two lines calculate
:00000116 2945B4                  sub dword ptr [ebp-4C], eax ;; the height of the window
:00000119 58                      pop eax ;; load position of upper border again
:0000011A 6A40                    push 00000040 ;; SWP_SHOWWINDOW
:0000011C FF75B8                  push [ebp-48] ;; right border
:0000011F FF75B4                  push [ebp-4C] ;; upper border
:00000122 50                      push eax ;; width
:00000123 53                      push ebx ;; heigth
:00000124 6A00                    push 00000000 ;; HWND_TOP
:00000126 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of the window
:0000012C FF1558E34000            call dword ptr [0040E358] ;; call SetWindowPos
:00000132 E9AF17FFFF              jmp FFFF18E6 ;; jump back to MessageLoop

Actually nothing special here:
This code-snippet gets the current coordinates of the window first. The address of the RECT
structure (ebp-54) is found accidentely, but works good. Then we have to load the address of
GetSystemMetrics in the way that was mentioned earlier, again as this API-function is not 
loaded, too. With the help of the coordinates we get from a call to GetSystemMetrics, the new
position (Centered) of the window is calculated, with the help of the GetWindowRect call the
size of the window is calculated for the SetWindowPos call in the end.

The next part of code is the one that makes the calculator window stay on top. It is quite
similar to the last snippet, as the only changes are, that we needn't get new coordinates, but
only change one parameter of the SetWindowPos call. Furthermore we have to check/uncheck the
menu which decides, if the window stays on top, or not:


Remark 2:
The follwing code is little sloppy, but it works and recoding it in a better way would take
more time as the result will improve it, as neither speed nor size matter here.
End of Remark

:00000137 3D9B020000              cmp eax, 0000029B  ;; is it "Stay on top" ?
:0000013C 0F85C2000000            jne 00000204 ;; if not, then jump
:00000142 8D45AC                  lea eax, dword ptr [ebp-54] ;; prepare the GetWindowRect
:00000145 50                      push eax

:00000146 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of main window
:0000014C FF151CE34000            call dword ptr [0040E31C] ;; GetWindowRect
:00000152 8B45B4                  mov eax, dword ptr [ebp-4C] ;; the next lines calculate
:00000155 2B45AC                  sub eax, dword ptr [ebp-54] ;; the width of the window
:00000158 8945B4                  mov dword ptr [ebp-4C], eax ;; and save it in EBP-4C
:0000015B 8B45B8                  mov eax, dword ptr [ebp-48] ;; the next lines calculate
:0000015E 2B45B0                  sub eax, dword ptr [ebp-50] ;; the heigth of the window
:00000161 8945B8                  mov dword ptr [ebp-48], eax ;; and save it in EBP-48
:00000164 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of the main window
:0000016A FF1554E34000            call dword ptr [0040E354] ;; GetMenu
:00000170 50                      push eax ;; save the handle of the menu
:00000171 50                      push eax ;; save the handle of the menu
:00000172 50                      push eax ;; save the handle of the menu
:00000173 90                      nop ;; part of later improvement
:00000174 90                      nop ;; same
:00000175 68AEEA4000              push 0040EAAE ;; "USER32.DLL"
:0000017A FF1590E24000            call dword ptr [0040E290] ;; GetModuleHandle
:00000180 90                      nop ;;part of later improvement
:00000181 90                      nop ;;part of later improvement
:00000182 90                      nop ;;part of later improvement
:00000183 680F1B4100              push 00411B0F ;; "GetMenuState"
:00000188 50                      push eax ;; handle of User32.dll
:00000189 FF15DCE24000            call dword ptr [0040E2DC] ;; GetProcAddress
:0000018F 5B                      pop ebx ;; load handle of the menu
:00000190 6A00                    push 00000000 ;; MF_BYCOMMAND
:00000192 689B020000              push 0000029B ;; ID of the menu point
:00000197 53                      push ebx ;; handle of the menu
:00000198 FFD0                    call eax ;; call GetMenuState
:0000019A 3D08000000              cmp eax, 00000008 ;; is checked?
:0000019F 7533                    jne 000001D4 ;; if not, then jump
:000001A1 90                      nop ;; fell away when optimizing
:000001A2 90                      nop ;; fell away when optimizing
:000001A3 90                      nop ;; fell away when optimizing
:000001A4 58                      pop eax ;; load handle of menu
:000001A5 6A00                    push 00000000 ;; MF_UNCHECKED
:000001A7 689B020000              push 0000029B ;; ID of Menupoint
:000001AC 50                      push eax ;; handle of Menu
:000001AD FF154CE34000            call dword ptr [0040E34C] ;; call CheckMenuItem
:000001B3 6A40                    push 00000040 ;; The following lines
:000001B5 FF75B8                  push [ebp-48] ;; prepare the
:000001B8 FF75B4                  push [ebp-4C] ;; call to
:000001BB FF75B0                  push [ebp-50] ;; SetWindowPos
:000001BE FF75AC                  push [ebp-54] ;; still preparing
:000001C1 6AFE                    push FFFFFFFE ;; HWND_NOTOPMOST
:000001C3 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of window
:000001C9 FF1558E34000            call dword ptr [0040E358] ;; SetWindowPos
:000001CF E91217FFFF              jmp FFFF18E6 ;; return to messageloop
:000001D4 5B                      pop ebx ;; load handle of menu
:000001D5 6A08                    push 00000008 ;; MF_CHECKED
:000001D7 689B020000              push 0000029B ;; ID of Menupoint
:000001DC 53                      push ebx ;; push handle of menu
:000001DD FF154CE34000            call dword ptr [0040E34C] ;; CheckMenuItem
:000001E3 6A40                    push 00000040 ;; and preparing
:000001E5 FF75B8                  push [ebp-48] ;; SetWindowPos
:000001E8 FF75B4                  push [ebp-4C] ;; once again
:000001EB FF75B0                  push [ebp-50]
:000001EE FF75AC                  push [ebp-54]
:000001F1 6AFF                    push FFFFFFFF ;; HWND_TOPMOST
:000001F3 FF35D8B54000            push dword ptr [0040B5D8] ;; handle of window
:000001F9 FF1558E34000            call dword ptr [0040E358] ;; SetWindowPos
:000001FF E9E216FFFF              jmp FFFF18E6 ;; back to MessageLoop

At first we have to call GetWindowRect again. It will return (after some calculation) the
parameters we use for the SetWindowPos call. The well-known GetModuleHandle/GetProcAddress
for getting the address of GetMenuState. In combination of GetMenu we can find out, whether the
menupoint is checked or not. Then, after the conditional jump (condition: menu checked?) at 
:19A we set the new position of the window using the parameters HWND_NOTOPMOST and HWND_TOPMOST.

The code for "Start Notepad":
:00000204 3D9C020000              cmp eax, 0000029C ;; is "Start Notepad" chosen?
:00000209 7525                    jne 00000230 ;; if not, then jump
:0000020B 68ACE64000              push 0040E6AC ;; push "KERNEL32.DLL"
:00000210 FF1590E24000            call dword ptr [0040E290] ;; "GetModuleHandle"
:00000216 68071B4100              push 00411B07 ;; "WinExec"
:0000021B 50                      push eax ;; handle of Kernel32.dll
:0000021C FF15DCE24000            call dword ptr [0040E2DC] ;; GetProcAddress
:00000222 6A01                    push 00000001 ;; SW_SHOW
:00000224 68EA174100              push 004117EA ;; "Notepad.exe"
:00000229 FFD0                    call eax ;; call WinExec
:0000022B E9B616FFFF              jmp FFFF18E6 ;; back to MessageLoop

You see: It's getting easier :) This time just finding out the address of WinExec and then
passing the string "Notepad.exe" to WinExec which I wrote at offset D9EAh into lazcalc.exe.

I won't explain the rest of my changes, as they can easily be understood just reading the
comments (and perhaps your API reference).

The code for "Help":
:00000230 3D9D020000              cmp eax, 0000029D ;; is "Help" chosen ?
:00000235 7516                    jne 0000024D ;; if not, then jump
:00000237 6A00                    push 00000000 ;; no additional data
:00000239 6A03                    push 00000003 ;; HELP_INDEX
:0000023B 68F6174100              push 004117F6 ;; "lazcalc.hlp"
:00000240 6A00                    push 00000000 ;; didn't specify a window handle
:00000242 FF1514E34000            call dword ptr [0040E314] ;; WinHelpA
:00000248 E99916FFFF              jmp FFFF18E6 ;; back to messageloop

The code for "About":
:0000024D 3D9E020000              cmp eax, 0000029E ;; is "About" chosen?
:00000252 7519                    jne 0000026D ;; if not, then jump
:00000254 6A00                    push 00000000 ;; MB_OK
:00000256 68E1174100              push 004117E1 ;; Caption of the box
:0000025B 68AE174100              push 004117AE ;; Text of the box
:00000260 6A00                    push 00000000 ;; no window specified
:00000262 FF150CE44000            call dword ptr [0040E40C] ;; MessageBoxA
:00000268 E97916FFFF              jmp FFFF18E6 ;; back to messageloop

The code for "Quit":
:0000026D 3DA0020000              cmp eax, 000002A0 ;; is "Quit" chosen?
:00000272 750D                    jne 00000281 ;; if not, jump
:00000274 6A00                    push 00000000 ;; Exit code = 0
:00000276 FF1500E34000            call dword ptr [0040E300] ;; ExitProcess
:0000027C E96516FFFF              jmp FFFF18E6 ;; back to MessageLoop
:00000281 E96016FFFF              jmp FFFF18E6 ;; back to MessageLoop

The only thing that is worth to ask is why my code ends with two jumps: Well, in this case
it is easier to add new functions, as the second jump is just to be replaced by a "cmp eax,..."
if you want to add more.


Final Notes
Wow, you have come so far, despite my writing style. Either you are hard to bore, or you only
read Introduction and Last Words of the essay.
OK, now the last words:
Don't even think about, that this was an easy task. Also the essay is written as if I needed
only short more than half an hour, I in fact needed nearly two weeks, but nevertheless I believe
that the result is quite acceptable and I felt really good when I finished :)
My thanx go to #cracking4newbies for (most time) quick help when I was stuck. For detailed
greetings consult the helpfile that comes shipped with lazcalc.zip

If you have the desire to add more functions, or to improve my code, please send your result to
lazarus_hf@hotmail.com If you want to tell me something else, use this address, too.

Possible functions you could add: 1. Make LaZ-Calc work in Standard-mode or at least auto-uncheck the checkbox
2. Center window at startup 3. Change the caption of the window
4. Get rid of the checkbox and auto-choose LaZ-Calc options if the edit field is enabled 5. Add hotkeys for LaZ-Calc buttons/menus... 6. Make it possible to use the buttons that need a key combination with STRG or ALT...
7. ... (a million more)
If you need some more information to realize this, don't hesitate to contact me.
Ob Duh
Doesn't apply, does it?

You are deep inside Fravia's page of reverse engineering, choose your way out:


redhomepage redlinks redsearch_forms red+ORC redhow to protect redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_Fravia
redIs reverse engineering legal?