Investigation of Uninstall Manager v3.00

First run the program and take a look of what we're going to deal with.. You'll see that it has a nag-screen and a name/serial-protection. So, what's our next step? Try some breakpoints? Right!
Press CTRL-D to get to SI, and enter "bpx getwindowtexta" and "bpx getdlgitemtexta".. CTRL-D again will bring you back to the box that asks us for registration-info.. Enter your name and a fake serial (I used "123456"), press OK, and a messagebox pops up.. "Sorry not a correct reg key".. Damned.. The breakpoints we set didn't work.. Okey, so what about hmemcpy then? Let's try it! 
Open up the reg-box again, enter some bogus info and  press CTRL-D to get to SI. Type "bc*" to remove all old breakpoints and type "bpx hmemcpy" to set a new one. Press CTRL-D once more to get to windows, and press OK to test the new breakpoint. This time SI should pop up instead of the messagebox.. Now, hmemcpy has one good side and one dark side. The reason for trying getwindowtexta and getdlgitemtexta first is because when a program uses these, you'll often land directly in the right spot. With hmemcpy, there are quite much tracing that needs to be done after that SI has poped up. The good thing is that it works 99.9% of the times.
First of all, type "bd*" to disable the hmemcpy-breakpoint so that we'll get to the right spot a little quicker. Now, press F11 once and then press F10 until you reach this code:

:0047A9F3 E87064FBFF              call 00430E68				<-- 1
:0047A9F8 837DF400                cmp dword ptr [ebp-0C], 00000000	<-- 2
:0047A9FC 7417                    je 0047AA15				<-- 3	
:0047A9FE 8D55F0                  lea edx, dword ptr [ebp-10]
:0047AA01 8B45FC                  mov eax, dword ptr [ebp-04]
:0047AA04 8B80F8020000            mov eax, dword ptr [eax+000002F8]
:0047AA0A E85964FBFF              call 00430E68				<-- 4
:0047AA0F 837DF000                cmp dword ptr [ebp-10], 00000000	    	
:0047AA13 7512                    jne 0047AA27

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0047A9FC(C)
|
:0047AA15 8B45FC                  mov eax, dword ptr [ebp-04]
:0047AA18 C7802C02000002000000    mov dword ptr [ebx+0000022C], 00000002
:0047AA22 E93A030000              jmp 0047AD61

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0047AA13(C)
|
:0047AA27 8B45FC                  mov eax, dword ptr [ebp-04]
:0047AA2A E851F9FFFF              call 0047A380				<-- 5
:0047AA2F 3C01                    cmp al, 01				<-- 6
:0047AA31 0F8551020000            jne 0047AC88				<-- 7

First, look over the code.. The first call (1) is the one that you came from. The second thing this code does is to compare the value at [ebp-0C] to zero (2), and if they were equal ([epb-0C] was zero) it jumps to 47AA15 (3). Now, look at [ebp-0C]. If you don't know how to do that, type "?*(ebp-c)" to see the VALUE of [ebp-0C] in hex, decimal and ASCII.. We could directly see that it's not zero, and therefore the program won't jump. So... What could this value be? A pointer to something? Type "d*(ebp-c)" to see what's at the ADDRESS of [ebp-0c]. In the data-window, you should see the name that you entered..
So why did it check if this value was zero then? I haven't investigated this, because I'm pretty sure that it has something to do with faults-checking, like if the call at 430E68 couldn't allocate memory to hold the name or something like that. Then the function would return NULL in the pointer where the name would have been stored, and thus, the program would have jumped out of the routine. 
Okey, done investing that. Use F10 to trace over these instructions, until you reach point 4. Look at the address that it's about to call. Look at the address of the call you came from (1). They are the same, and since we know that that's the call that fetches a string from the data we entered, we don't step into it. Notice the NULL-test after the call and that the jump is taken if the value at [ebp-10] is not zero. After you've F10'ed the call, type "d*(ebp-10)" and you'll see that the serial you entered landed there..
Step on to the call at point 5. What we see is a call (5), a comparition of the return-value (6) and a conditional jump, depending on the returnvalue (7). This is always suspicious. Try to step over the call. You'll see that eax will be zero, the jump will be taken, and you'll soon land up in the code that displays the error-message. We could directly see that the call is important, so let's trace through it.. Type "bpx 47aa2a", press CTRL-D and repeat the procedure of filling in the name and serial-boxes.. Press OK and you should land in SI at location 47AA2A. Press F8 to trace into the call. First you'll see some code to initialize the call, and then you'll see some other calls. Most of them are insignificant, but there are one of them you better notice. It turns the name you entered to lowercase-letters only. Keep on tracing with F10 until you reach this:

:0047A3E0 33DB                    xor ebx, ebx
:0047A3E2 85FF                    test edi, edi
:0047A3E4 7E20                    jle 0047A406
:0047A3E6 B801000000              mov eax, 00000001

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0047A404(C)
|
:0047A3EB 8B55FC                  mov edx, dword ptr [ebp-04]
:0047A3EE 8A5402FF                mov dl, byte ptr [edx+eax-01]
:0047A3F2 80FA20                  cmp dl, 20
:0047A3F5 740B                    je 0047A402
:0047A3F7 8B4DFC                  mov ecx, dword ptr [ebp-04]
:0047A3FA 81E2FF000000            and edx, 000000FF
:0047A400 03DA                    add ebx, edx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0047A3F5(C)
|
:0047A402 40                      inc eax
:0047A403 4F                      dec edi
:0047A404 75E5                    jne 0047A3EB

When you reach 47A3E0, our name has been converted to lowercase-letters, and edi holds the lenght of our name. At the address of [ebp-04], you'll find the name. If you're not used to understand assembly, trace this loop a couple of times. Soon you'll notice that it adds the ASCII-values of the letters from the name we entered, with one exception, the spaces (020h==' '). The result lands up in ebx, and is modified a little more with the following code:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0047A3E4(C)
|
:0047A406 81F389000000            xor ebx, 00000089
:0047A40C 83F333                  xor ebx, 00000033
:0047A40F 43                      inc ebx
:0047A410 8D55F8                  lea edx, dword ptr [ebp-08]
:0047A413 8B86F8020000            mov eax, dword ptr [esi+000002F8]
:0047A419 E84A6AFBFF              call 00430E68
:0047A41E 8B45F8                  mov eax, dword ptr [ebp-08]
:0047A421 E8EEE4F8FF              call 00408914
:0047A426 3BD8                    cmp ebx, eax
:0047A428 7504                    jne 0047A42E

Ebx is xor'ed twice, first with 089h and then with 033h, and is then increased by one. The code between 47A410 and 47A421 takes the serial that you entered and converts it into a number. Finally, we have a comparition between eax (entered serial) and ebx (calculated serial).. 
So.. Now we've got everything we need to keygen this program.. A brief list of things we need to do is this:

1. Get the name from the user of the keygen
2. Turn all letters to lowercase
3. Add all ASCII-values of the name except 020h's
4. Xor the result by 089h
5. Xor the result by 033h
6. Increase the result by one
7. Turn the result to a string and show it to the user.

I've attached the code I used in my keygen at the end of this file.

So, what have we learned from this lesson? The first thing is that hmemcpy always will be there for you when getwindowtexta and getdlgitemtexta fails. The second thing is that it's good to notice where the calls goes, and if we've already been in them. Then it's no need of tracing them again. The third thing is to always be suspicious when you see a call followed by some kind of test on the result and a conditional jump. If you're not sure that it's the call you're looking for, step over it and take a look of where it brings you. I'm not sure if you noticed it, but let's learn one more thing from the above: high-level compilers is sometimes stupid. Look at 47A3F7. It assigns ecx a value that is never used, and it does it in a loop too. Let's say we did a loop that would iterate about a billion times. That would make a quite big difference in time, at least on a 486 or older (on a pentium+ the instructions would be executed at same time in different pipelines..). 

  Chafe / The Exterminators


szFormat	db	'%u',0
szName		db	30 dup(?)
GenerateKey proc        
        invoke  GetDlgItemText,hMain,IDC_NAME,offset szName,30		; Step 1
        test    eax,eax
        je      NotEnough
        push    eax
        invoke  CharLower,offset szName					; Step 2
        pop     ecx
        xor     ebx,ebx
        xor     esi,esi
        mov     edx,offset szName
AddLoop:								; Step 3
        xor     eax,eax
        mov     al,byte ptr [edx+esi]
        cmp     al,020h
        je      NoAdd
        add     ebx,eax
NoAdd:
        inc     esi
        dec     ecx
        jne     AddLoop
        xor     ebx,089h						; Step 4
        xor     ebx,033h						; Step 5
        inc     ebx							; Step 6
        push    ebx							; Step 7
        push    offset szFormat
        push    offset szName
        call    wsprintfA
        add     esp,12    
        invoke  SetWindowText,hKey,offset szName
        ret
NotEnough:
        invoke  SetWindowText,hKey,offset szNothingEntered
        ret
GenerateKey endp