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 reversers 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 

: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 

: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?