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