Reverseme Windows Keygen

This one was challenging for me, and took me several hours, but was fun. I got caught up on certain parts that may not have been too difficult, but, yeah…

http://crackmes.de/users/tripletordo/ice9/

You can download the executable here Ice9.zip.

The first thing I noticed is probably the ‘trick’ which was simply a call to isdebuggerpresent. I modified the assembly immediately after from JNE to JE so that it only runs if a debugger is present, allowing me to attach my debugger.

00401071 74 0A JE SHORT Ice9.0040107D

This took a lot of trial and error. My strategy was to replicate the logic. Once I got to the point ‘ecx at 0040119c’ I was home free.

#include <iostream>
#include <string>
using namespace std;

void main (int argc, char *argv[]) {
  if ( argc != 2) {
    cout<<"Bad usage, enter a name > 4 letters"<<endl;
	return;
  }
  string name = argv[1];
  string ostring = name;
  int i;
  //first reverse the string
  for (i=0; i<name.length(); i++) {
    name[i] = ostring [name.length()-i-1];
  }
  
  if (name.length() < 4) {
    cout << "name must be more than 4 letters chief"<<endl;
	return;
  }
  

  int v1 = 0;
  int cum = 0;
  for (i=1; i<name.length(); i++) {
    v1 = name[i];
	if (name[i] <= 90) {
	  if (v1 >= 65)
	    v1 += 44;
	}
	cum += v1;
  } //ecx at 0040119C
  
  cum = 9 * (12345 * (cum + 666) - 23);
  
  char chr_403119 [122];
  unsigned int v;
  i=0;
  //no bounds checking
  do {
    v = cum;
	cum /= 0xA;
	chr_403119[i++] = v % 10 + 48;
  } while (v / 10);
  chr_403119[i] = '\0';
  
  printf ("%s", chr_403119);
  string serial = "";

  //reverse the string
  for (; i >= 0; --i) {
    serial += chr_403119[i];
  }
  cout<<serial<<endl;
  
  //append all chars except the 'first' three to the end 
  for (i=3; i< ostring.length(); i++) {
    serial += ostring[i];
  }
  
  cout<<serial<<endl;

}

My plan on this one, since it was interesting enough and because it’s relatively easy to break at the final value, is to break this a completely different way. I’d like to write a python debugging script that bypasses the isdebuggerpresent and just grabs the final value in the compare at 004011FF. This should be relatively straightforward, and hopefully a good ‘hello, world’ to the world of python debugging. Stay tuned.

Windows reverseme – nothing tricky

Windows reverseme – nothing tricky, just a sort of complicated validation process. This was originally from crackmes.de, mirror of the executable here.

The easiest thing in the world is to get this thing to validate.  Just run it and put a breakpoint at 00401288, and look at the value in 00406749.  That’s it! You’re validated.

A lot more tricky was writing the keygen.  To do it, I just stepped through the code very slowly, and duplicated the logic. ugh.

Ok, now here is my keygen of the executable.  To simplify things, I just considered usernames that are 5 chacters long.  Note all the mods.  Longer usernames will work, but will require minor mods to the keygen (and I didn’t have the patience to step through the code again).

/**************************************************************************
 * keygen.c
 *
 * This crackme, while easy to break (just look at the end value) took
 * quite awhile to step through the key generating process, which includes
 * a lot of xoring with prestored constants and with itself. It's almost
 * like a small hash or something...
 *
 * To simplify things I only consider Usernames of 5 characters.  Otherwise
 * the code needs to be modified slightly
 *
 * ************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void usage() {
  printf("Usage: keygen <5-letter-username>\n");
  exit(0);
}

int main(int argc, char*argv[]) {

  if (argc != 2 || strlen(argv[1]) != 5) {
    usage();
  }

  char* username = argv[1];
  int i;
  /*initialize xorconst and xorop */

  /*xorconst is stored at */
  unsigned char xorconst[5];
  xorconst[0] = 0xAA;
  xorconst[1] = 0x89;
  xorconst[2] = 0xC4;
  xorconst[3] = 0xFE;
  xorconst[4] = 0x46;

  unsigned char xorop[5];
  for (i =0; i< 5; i++) {
    xorop[i] = 0;
  }

  /* calculate xorop, which is the value eventually stored at 0x40634a
     when the code finishes it's for loop at 0x401197 */
  xorop[4] = 0x46;
  for (i = 0; i<strlen(username)-1; i++) {
    xorop[i%5] = (unsigned char)(xorconst[i%5] ^ username[i]);
  }

  /*start second loop */
  /*initialize second xorconst */
  unsigned char xorconst2[5];
  xorconst2[0] = 0x78;
  xorconst2[1] = 0xF0;
  xorconst2[2] = 0xD0;
  xorconst2[3] = 0x03;
  xorconst2[4] = 0xE7;

  /* take xorop (and some more since we may need more than 5) and xor with
     the 40632D constant.
     To simplify, we'll only deal with 5 letter long unames for now */

  unsigned char xorop2[5];
  for (i = 0; i<strlen(username); i++) {
    xorop2[strlen(username)-i-1] = xorop[strlen(username)-i-1] ^ xorconst2[i];
  }

  /*xorop2 now contains the string in 0x40634a at 0x004011BD
    though it does change more */

  /*now do the third loop, starting at 0x4011d1 */
  /*initialize the third xorconst */
  unsigned char xorconst3[5];
  xorconst3[0] = 0xF7;
  xorconst3[1] = 0xFD;
  xorconst3[2] = 0xF4;
  xorconst3[3] = 0xE7;
  xorconst3[4] = 0xB9;

  unsigned char xorop3[5];
  for (i = 0; i<strlen(username); i++) {
    xorop3[i] = xorop2[i] ^ xorconst3[i];
  }

  /* xorop3 now contains 0x40634a at the end of the third loop ~ 0x4011F0 */
  /* and it appears xorop2 is stored at 0x406334 */

  /*looks like there's one more loop, then some garbage */

  /*the fourth loop xors xorop3[end to start] with xorconst4[starttoend]
    the xored result is stored in (reverse order) 40634a
    xorop3 is preserved at 406336 */

  unsigned char xorconst4[5];
  xorconst4[0] = 0xB5;
  xorconst4[1] = 0x1B;
  xorconst4[2] = 0xC9;
  xorconst4[3] = 0x50;
  xorconst4[4] = 0x73;

  unsigned char xorop4[5];
  for (i = 0; i<5; i++) {
    xorop4[5-i-1] = xorop3[5-i-1] ^ xorconst4[i];
  }

  /*xorop4 is now stored in 0040634a at the end of the fourth loop */
  /*xorop3 (al) is now stored at 406336 */

  /*loop 5 */
  /*ecx = 406345 (which is initially 0) AND 3
    bx = xorop4[i + ecx] + 00406345[i]
    final iteration:
      xorop5[0-3] = xorop4[allbutlast]
      xorop5[0] = xorop[0] + xorop[4]
    then xorop5 is stored in 406345
  */
  unsigned char xorop5[4];
  /*first copy xorop4, but xorop5[0] = xorop4[0] + xorop4[4]*/

  for (i = 0; i<4; i++) {
    xorop5[i] = xorop4[i];
  }
  xorop5[0] = xorop4[0] + xorop4[4];

  /*ends fifth loop xorop5 is in 406345 right before xorop4*/

  unsigned int remainder;

  /*this could probably be done with an __asm__, but this is fine*/
  unsigned int thisint = xorop5[3]*16*16*16*16*16*16 + xorop5[2]*16*16*16*16
                + xorop5[1]*16*16 + xorop5[0];

  char finalpass [20];
  i = 0;
  while (thisint != 0) {

    remainder = (thisint % 10) + 0x30;
    thisint = thisint / 10;
    finalpass[i] = remainder;
    i++;
  }

  int length = i;
  /*finalpass is the final password, but in reverse order */
  for(i=length-1; i>=0; i--) {
    printf("%c",finalpass[i]);
  } 

  printf("\n");
  return 0;
}

Here is a dump of the applicable assembly instructions with comments.

0040117A   > 8A0C16         MOV CL,BYTE PTR DS:[ESI+EDX]             ;  code to get 40634a stuff... has to do with length.
0040117D   . 8AD9           MOV BL,CL
0040117F   . 3298 28634000  XOR BL,BYTE PTR DS:[EAX+406328]          ;  xor with constant AA 89 C4 FE 46
00401185   . 40             INC EAX
00401186   . 83F8 05        CMP EAX,5                                ;  rehashes same 5 chars again and again
00401189   . 881C32         MOV BYTE PTR DS:[EDX+ESI],BL             ;  final compare value
0040118C   . 8888 27634000  MOV BYTE PTR DS:[EAX+406327],CL
00401192   . 75 02          JNZ SHORT crackme.00401196
00401194   . 33C0           XOR EAX,EAX
00401196   > 46             INC ESI
00401197   . 3BF5           CMP ESI,EBP                              ;  for i < len username
00401199   .^72 DF          JB SHORT crackme.0040117A
0040119B   . 33FF           XOR EDI,EDI
0040119D   . 33C9           XOR ECX,ECX
0040119F   . 85ED           TEST EBP,EBP
004011A1   . 76 26          JBE SHORT crackme.004011C9
004011A3   > 8A9F 2D634000  MOV BL,BYTE PTR DS:[EDI+40632D]
004011A9   . 8BF5           MOV ESI,EBP
004011AB   . 2BF1           SUB ESI,ECX
004011AD   . 4E             DEC ESI
004011AE   . 8A0432         MOV AL,BYTE PTR DS:[EDX+ESI]             ;  last character first in 40634a + srrlen?
004011B1   . 32D8           XOR BL,AL
004011B3   . 47             INC EDI
004011B4   . 881C32         MOV BYTE PTR DS:[EDX+ESI],BL
004011B7   . 8887 2C634000  MOV BYTE PTR DS:[EDI+40632C],AL
004011BD   . 83FF 05        CMP EDI,5
004011C0   . 75 02          JNZ SHORT crackme.004011C4
004011C2   . 33FF           XOR EDI,EDI
004011C4   > 41             INC ECX
004011C5   . 3BCD           CMP ECX,EBP
004011C7   .^72 DA          JB SHORT crackme.004011A3                ;  end loop
004011C9   > 33F6           XOR ESI,ESI
004011CB   . 33FF           XOR EDI,EDI
004011CD   . 85ED           TEST EBP,EBP                             ;  ebp begins as strlen?
004011CF   . 76 21          JBE SHORT crackme.004011F2
004011D1   > 8A043A         MOV AL,BYTE PTR DS:[EDX+EDI]             ;  0040634a + i
004011D4   . 8A8E 32634000  MOV CL,BYTE PTR DS:[ESI+406332]          ;  406332 constant???
004011DA   . 32C8           XOR CL,AL
004011DC   . 46             INC ESI
004011DD   . 880C3A         MOV BYTE PTR DS:[EDX+EDI],CL
004011E0   . 8886 31634000  MOV BYTE PTR DS:[ESI+406331],AL
004011E6   . 83FE 05        CMP ESI,5
004011E9   . 75 02          JNZ SHORT crackme.004011ED
004011EB   . 33F6           XOR ESI,ESI                              ;  esi = esi % 5
004011ED   > 47             INC EDI
004011EE   . 3BFD           CMP EDI,EBP
004011F0   .^72 DF          JB SHORT crackme.004011D1                ;  end loop
004011F2   > 33FF           XOR EDI,EDI                              ;  start fourth loop
004011F4   . 33C9           XOR ECX,ECX
004011F6   . 85ED           TEST EBP,EBP
004011F8   . 76 26          JBE SHORT crackme.00401220
004011FA   > 8A9F 37634000  MOV BL,BYTE PTR DS:[EDI+406337]
00401200   . 8BF5           MOV ESI,EBP
00401202   . 2BF1           SUB ESI,ECX
00401204   . 4E             DEC ESI
00401205   . 8A0432         MOV AL,BYTE PTR DS:[EDX+ESI]
00401208   . 32D8           XOR BL,AL
0040120A   . 47             INC EDI
0040120B   . 881C32         MOV BYTE PTR DS:[EDX+ESI],BL
0040120E   . 8887 36634000  MOV BYTE PTR DS:[EDI+406336],AL
00401214   . 83FF 05        CMP EDI,5
00401217   . 75 02          JNZ SHORT crackme.0040121B
00401219   . 33FF           XOR EDI,EDI                              ;  edi = edi%5
0040121B   > 41             INC ECX
0040121C   . 3BCD           CMP ECX,EBP
0040121E   .^72 DA          JB SHORT crackme.004011FA                ;  end fourth loop
00401220   > 8D3D 45634000  LEA EDI,DWORD PTR DS:[406345]            ;  is 406345 a constant???
00401226   . 33C0           XOR EAX,EAX                              ;  start fifth loop
00401228   . 85ED           TEST EBP,EBP
0040122A   . C705 45634000 >MOV DWORD PTR DS:[406345],0
00401234   . 76 17          JBE SHORT crackme.0040124D
00401236   > 8BC8           MOV ECX,EAX
00401238   . 83E1 03        AND ECX,3                                ;  ecx = eax%3
0040123B   . 8A1C0F         MOV BL,BYTE PTR DS:[EDI+ECX]
0040123E   . 8D340F         LEA ESI,DWORD PTR DS:[EDI+ECX]
00401241   . 8A0C02         MOV CL,BYTE PTR DS:[EDX+EAX]
00401244   . 02D9           ADD BL,CL                                ;  bl = bl + cl
00401246   . 40             INC EAX
00401247   . 3BC5           CMP EAX,EBP
00401249   . 881E           MOV BYTE PTR DS:[ESI],BL                 ;  eventually eax == this
0040124B   .^72 E9          JB SHORT crackme.00401236                ;  end fifth for loop
0040124D   > 5D             POP EBP
0040124E   . B9 0A000000    MOV ECX,0A                               ;  ecx = 10
00401253   . A1 45634000    MOV EAX,DWORD PTR DS:[406345]            ;  eax = ??
00401258   . 33DB           XOR EBX,EBX
0040125A   > 33D2           XOR EDX,EDX
0040125C   . F7F1           DIV ECX                                  ;  edx:eax = edx:eax/ecx
0040125E   . 80C2 30        ADD DL,30
00401261   . 8893 49654000  MOV BYTE PTR DS:[EBX+406549],DL          ;  move char of serial here
00401267   . 43             INC EBX
00401268   . 85C0           TEST EAX,EAX
0040126A   .^75 EE          JNZ SHORT crackme.0040125A
0040126C   . 68 49654000    PUSH crackme.00406549                    ; /String = ""
00401271   . E8 86010000    CALL <JMP.&kernel32.lstrlenA>            ; \lstrlenA
00401276   . 33DB           XOR EBX,EBX
00401278   > 8A88 48654000  MOV CL,BYTE PTR DS:[EAX+406548]
0040127E   . 888B 49674000  MOV BYTE PTR DS:[EBX+406749],CL
00401284   . 43             INC EBX
00401285   . 48             DEC EAX
00401286   .^75 F0          JNZ SHORT crackme.00401278
00401288   . 68 49674000    PUSH crackme.00406749                    ; /String2 = ""
0040128D   . 68 49654000    PUSH crackme.00406549                    ; |String1 = crackme.00406549
00401292   . E8 5F010000    CALL <JMP.&kernel32.lstrcpyA>            ; \lstrcpyA
00401297   . 68 00020000    PUSH 200                                 ; /Count = 200 (512.)
0040129C   . 68 49694000    PUSH crackme.00406949                    ; |Buffer = crackme.00406949
004012A1   . 6A 64          PUSH 64                                  ; |ControlID = 64 (100.)
004012A3   . FF75 08        PUSH DWORD PTR SS:[EBP+8]                ; |hWnd
004012A6   . E8 E5000000    CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA
004012AB   . 68 49654000    PUSH crackme.00406549                    ; /String2 = ""
004012B0   . 68 49694000    PUSH crackme.00406949                    ; |String1 = ""
004012B5   . E8 36010000    CALL <JMP.&kernel32.lstrcmpA>            ; \lstrcmpA
004012BA   . 0BC0           OR EAX,EAX
004012BC   . 75 16          JNZ SHORT crackme.004012D4
004012BE   . 6A 40          PUSH 40                                  ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
004012C0   . 68 DB624000    PUSH crackme.004062DB                    ; |Title = "Good boy..."
004012C5   . 68 AC624000    PUSH crackme.004062AC                    ; |Text = "Yep, thats the right code!
Go write a keygen!"
004012CA   . FF75 08        PUSH DWORD PTR SS:[EBP+8]                ; |hOwner
004012CD   . E8 CA000000    CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
004012D2   . EB 14          JMP SHORT crackme.004012E8
004012D4   > 6A 10          PUSH 10                                  ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
004012D6   . 68 06634000    PUSH crackme.00406306                    ; |Title = "Bad boy..."
004012DB   . 68 E7624000    PUSH crackme.004062E7                    ; |Text = "Nope, thats not it!
Try again"
004012E0   . FF75 08        PUSH DWORD PTR SS:[EBP+8]                ; |hOwner
004012E3   . E8 B4000000    CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA

Windows Password, geygen, password reverseme

Sat for an evenin’ o’ fun this holiday season.  I like these easy ones.  Last month I tried a harder one and found it discouraging.  I don’t have the sort of time to work on these for a full day, so these couple hour ones are a lot more fun to me at this point.

I found the binary on crackmes.de, here is a mirror.

Part 1: Find the Password.

Really easy.  Even me a beginner figured this out in about half an hour.  First thing it does is make sure the input is 8 characters long.  If it’s not it exits the program.

004013B7  |. 890424         MOV DWORD PTR SS:[ESP],EAX               ; |
004013BA  |. E8 B1060000    CALL &lt;JMP.&amp;msvcrt.strlen&gt;                ; strlen
004013BF  |. 83F8 08        CMP EAX,8
004013C2  |. 74 05          JE SHORT CrackMe#.004013C9               ;  if strlen(pass) == 8 (if it's not, 'something went wrong')
004013C4  |. E9 2C010000    JMP CrackMe#.004014F5

Next it goes through a little loop which compares each letter of the password you entered with the real password

004013C9  |> C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0
004013D0  |> 837D F4 07     /CMP DWORD PTR SS:[EBP-C],7              ;  looks like for (i =0; i<7; i++) probably
004013D4  |. 7F 20          |JG SHORT CrackMe#.004013F6
004013D6  |. 8D45 F8        |LEA EAX,DWORD PTR SS:[EBP-8]
004013D9  |. 0345 F4        |ADD EAX,DWORD PTR SS:[EBP-C]
004013DC  |. 8D50 E0        |LEA EDX,DWORD PTR DS:[EAX-20]           ;  edx = ptr(ebp - 8) + ptr(ebp - c)
004013DF  |. 8D45 F8        |LEA EAX,DWORD PTR SS:[EBP-8]
004013E2  |. 0345 F4        |ADD EAX,DWORD PTR SS:[EBP-C]
004013E5  |. 83E8 20        |SUB EAX,20                              ;  eax = edx - 20
004013E8  |. 0FB600         |MOVZX EAX,BYTE PTR DS:[EAX]
004013EB  |. FEC0           |INC AL
004013ED  |. 8802           |MOV BYTE PTR DS:[EDX],AL
004013EF  |. 8D45 F4        |LEA EAX,DWORD PTR SS:[EBP-C]
004013F2  |. FF00           |INC DWORD PTR DS:[EAX]
004013F4  |.^EB DA          \JMP SHORT CrackMe#.004013D0
004013F6  |> 8D45 C8        LEA EAX,DWORD PTR SS:[EBP-38]            ; ||||||||
004013F9  |. 8D55 D8        LEA EDX,DWORD PTR SS:[EBP-28]            ; ||||||||
004013FC  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX             ; ||||||||
00401400  |. 891424         MOV DWORD PTR SS:[ESP],EDX               ; ||||||||
00401403  |. E8 58060000    CALL <JMP.&msvcrt.strcmp>                ; |||||||\strcmp
00401408  |. 85C0           TEST EAX,EAX                             ; |||||||if this doesn't = 0 exit 'something went wrong'
0040140A  |. 0F85 E5000000  JNZ CrackMe#.004014F5                    ; |||||||
00401410  |. C70424 FE31400>MOV DWORD PTR SS:[ESP],CrackMe#.004031FE ; |||||||ASCII 0A,"Stage 1 co"

If you look closely it compares every letter + 1 to QbTTx1sE.  Meaning of course that the password is simply: PaSSw0rD

Part 2: KeyGen

The next part was a little more tricky for me.

On the screen you enter a name and a corresponding serial.

STAGE 2
00401423  |. E8 68060000    CALL <JMP.&msvcrt.printf>                ; |||||\printf
00401428  |. C70424 D331400>MOV DWORD PTR SS:[ESP],CrackMe#.004031D3 ; |||||ASCII "*******
0040142F  |. E8 5C060000    CALL <JMP.&msvcrt.printf>                ; ||||\printf
00401434  |. C70424 1E32400>MOV DWORD PTR SS:[ESP],CrackMe#.0040321E ; ||||ASCII 0A,"Name [2<=c"
0040143B  |. E8 50060000    CALL <JMP.&msvcrt.printf>                ; |||\printf
00401440  |. 8D45 B8        LEA EAX,DWORD PTR SS:[EBP-48]            ; |||
00401443  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX             ; |||ebp -48 or esp +4 = name
00401447  |. C70424 FB31400>MOV DWORD PTR SS:[ESP],CrackMe#.004031FB ; |||ASCII "%s"
0040144E  |. E8 2D060000    CALL <JMP.&msvcrt.scanf>                 ; ||\scanf
00401453  |. C70424 3632400>MOV DWORD PTR SS:[ESP],CrackMe#.00403236 ; ||ASCII "Serial : "
0040145A  |. E8 31060000    CALL <JMP.&msvcrt.printf>                ; |\printf
0040145F  |. 8D45 B0        LEA EAX,DWORD PTR SS:[EBP-50]            ; |
00401462  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX             ; |ebp -50 = serial
00401466  |. C70424 4132400>MOV DWORD PTR SS:[ESP],CrackMe#.00403241 ; |ASCII "%d"
0040146D  |. E8 0E060000    CALL <JMP.&msvcrt.scanf>                 ; \scanf
00401472  |. C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0
00401479  |. C745 F4 000000>MOV DWORD PTR SS:[EBP-C],0
00401480  |> 8D45 B8        /LEA EAX,DWORD PTR SS:[EBP-48]           ; |
00401483  |. 890424         |MOV DWORD PTR SS:[ESP],EAX              ; |strlen (name)
00401486  |. E8 E5050000    |CALL <JMP.&msvcrt.strlen>               ; \strlen
0040148B  |. 3945 F4        |CMP DWORD PTR SS:[EBP-C],EAX            ;  while loop (for i = len(name)
0040148E  |. 77 1A          |JA SHORT CrackMe#.004014AA
00401490  |. 8D45 F8        |LEA EAX,DWORD PTR SS:[EBP-8]
00401493  |. 0345 F4        |ADD EAX,DWORD PTR SS:[EBP-C]
00401496  |. 83E8 40        |SUB EAX,40
00401499  |. 0FBE00         |MOVSX EAX,BYTE PTR DS:[EAX]
0040149C  |. 0345 B4        |ADD EAX,DWORD PTR SS:[EBP-4C]
0040149F  |. 48             |DEC EAX
004014A0  |. 8945 B4        |MOV DWORD PTR SS:[EBP-4C],EAX
004014A3  |. 8D45 F4        |LEA EAX,DWORD PTR SS:[EBP-C]
004014A6  |. FF00           |INC DWORD PTR DS:[EAX]                  ;  pointer to 0,1,...,length
004014A8  |.^EB D6          \JMP SHORT CrackMe#.00401480
004014AA  |> 8B45 B4        MOV EAX,DWORD PTR SS:[EBP-4C]            ; |||||eax calculated in loop, compared with serial
004014AD  |. 3B45 B0        CMP EAX,DWORD PTR SS:[EBP-50]            ; |||||
004014B0  |. 75 43          JNZ SHORT CrackMe#.004014F5              ; |||||
004014B2  |. C70424 4432400>MOV DWORD PTR SS:[ESP],CrackMe#.00403244 ; |||||ASCII 0A,"Stage 2 Co"

Although it can be a bit hard to read because of all the pointers (at least for me), it is definitely possible to discern the for loop and figure out it’s computing some value based on the name and writing to the same space (ebp – 4c) at every iteration.  This value is then compared to the serial to determine if the serial is valid or not.  After setting some breakpoints it becomes clear that  every value of the name -1 is added together and compared to the serial.  Here is a working keygen.

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string name = "";
    cout << "name: ";
    cin >> name;
    int total = 0;
    for(int i=0; i<name.length(); i++)
    {
            total += name[i];
    }
    total -= (name.length() + 1);
    cout << "Serial should be " << total << endl;

    system("PAUSE");
    return 0;
}

Part 3

This part was theoretically easy, but a bit hard for me to execute.  The goal is to remove a console nag.

004014DD  |. E8 AE050000    CALL <JMP.&msvcrt.printf>                ; |\printf

That line needs to turn to a nop.  In ollydbg just right click as say fill with nop, and it should be good.