
The_NOP's CrackMe #1
XOR Reversing
Written by Sphinx
Introduction |
Tools required |
Target's URL |
http://royalaccezz.cjb.net
Essay |
Ok. As usual, snoop! Ahhh, packed by UPX! But no need to unpack here
just to get an external disassembly. SoftICE
would suffice :-) So run
it nah. I entered Andrea as name and 12345 as serial. Btw,
if you're technical (like me), the name
field has the property
"ES_AUTOHSCROLL" turned off. Meaning, the text you type in will be
limited by the size of the textbox. And the serial field has the property
"TAB STOP" also turned off, which is bothersome :-) Oh yeah, it
doesn't
accept anything else aside from numbers. A hint already :-)
Bah! Enough technical crap, let's proceed noh? Ok. Simply breakpoint
GetDlgItemTextA prior to hitting the "Validate" button
and you'll end up in the program's code. Right after
hitting F12, you'll
be at the line after the call to the API. But try noticing the previous
lines, it proves to be interesting, so
we backtrace there.
0137:004010BD PUSH EBP ; these two lines are
0137:004010BE MOV EBP,ESP ; equivalent to ENTER :)
0137:004010C0 ADD ESP,-08 ; init the stack a bit
0137:004010C3 PUSH 00
0137:004010C5 LEA EAX,[EBP-08]
0137:004010C8 PUSH EAX
0137:004010C9 PUSH 05
0137:004010CB PUSH DWORD PTR [00403044]
0137:004010D1 CALL USER32!GetDlgItemInt ; convert serial to hex to eax
0137:004010D6 CMP EAX,00 ; have we entered anything?
0137:004010D9 JZ 00401129 ; nope? jump badboy, else...
0137:004010DB MOV [EBP-08],EAX ; save for later (ie. 3039h)
0137:004010DE PUSH 14 ; (max length of string to snatch)-1
ie. 14-1 = 13 (19 chars)
0137:004010E0 PUSH 00403048 ; name buffer
0137:004010E5 PUSH 04 ; name field handle
0137:004010E7 PUSH DWORD PTR [00403044] ; dialog handle
0137:004010ED CALL USER32!GetDlgItemTextA ; read the name; eax = length
0137:004010F2 CMP EAX,00 ; have we entered anything?
0137:004010F5 JZ 00401129 ; nope? jump to badboy too, else...
0137:004010F7 CMP EAX,14 ; have we entered <= 14 (20 chars)?
0137:004010FA JLE 0040112C ; yep? jump to BUILDNAME
But notice that the name buffer will only accept 19 chars, so no matter what
happens, you'll always jump to BUILDNAME (which is just below this line).
BUILDNAME:
0137:0040112C ADD EAX,01 ; increment name ptr
0137:0040112F MOV BYTE PTR [EAX+00403048],90 ; move a 90h to it
0137:00401136 CMP EAX,14 ; is eax <= 14 nah?
0137:00401139 JLE 0040112C ; nope? loop until it is
0137:0040113B JMP 004010FC ; after, jump to CALC
So after, the username in memory will be:
DS:00403048 41 6E 64 72 65 61 00 90-90 90 90 90 90 90 90 90 Andrea..........
DS:00403058 90 90 90 90 90 90 00 00-00 00 00 00 00 00 00 00 ................
Note that after the name, there's a 00h, since it already incremented the name
ptr even before moving a 90h there.
CALC:
0137:004010FC MOV EAX,[EBP-08] ; eax = 3039 (usercode in hex)
0137:004010FF MOV EBX,[00403048] ; ebx = dword ptr [name], so ebx = 72646E41 ("rdnA")
0137:00401105 XOR EAX,EBX ; eax = eax xor ebx, so eax = 72645E78
0137:00401107 MOV EBX,[0040304C] ; ebx = dword ptr [name+4], so ebx = 90006165 (" ae")
0137:0040110D XOR EAX,EBX ; so, eax = E2643F1D
0137:0040110F MOV EBX,[00403050] ; ebx = dword ptr [name+8], so ebx = 90909090
0137:00401115 XOR EAX,EBX ; so, eax = 72F4AF8D
0137:00401117 MOV EBX,[00403054] ; ebx = dword ptr [name+0Ch], so ebx = 90909090
0137:0040111D XOR EAX,EBX ; so, eax = E2643F1D
0137:0040111F MOV EBX,[00403058] ; ebx = dword ptr [name+10h], so ebx = 90909090
0137:00401125 XOR EAX,EBX ; so, eax = 72F4AF8D
0137:00401127 LEAVE ; as opposed to the above :)
0137:00401128 RETF ; get out; ret to MAIN
0137:00401129 INC EAX ; eax = 1 (badflag!)
0137:0040112A LEAVE ; (same)
0137:0040112B RETF ; get out; ret to MAIN
MAIN:
...(some crap)...
0137:00401077 CALL 004010BD ; yep, calls the routine above
0137:0040107C CMP EAX,00 ; is eax = 0?
0137:0040107F JZ 0040109F ; yeah? jump goodboy, else...
0137:00401081 PUSH 10 ; style
0137:00401083 PUSH 00403009 ; caption
0137:00401088 PUSH 0040301D ; "bad" text
0137:0040108D PUSH DWORD PTR [EBP+08] ; handle
0137:00401090 CALL USER32!MessageBoxA ; draw it
0137:00401095 POP EBX
0137:00401096 MOV EAX,00000001
0137:0040109B LEAVE
0137:0040109C RET 0010
0137:0040109F PUSH 10 ; style
0137:004010A1 PUSH 00403009 ; caption
0137:004010A6 PUSH 00403030 ; "good" text
0137:004010AB PUSH DWORD PTR [EBP+08] ; handle
0137:004010AE CALL USER32!MessageBoxA ; draw it
0137:004010B3 POP EBX
0137:004010B4 MOV EAX,00000001
0137:004010B9 LEAVE
0137:004010BA RET 0010
So our main task is to enter a serial that, after those long mumbo-jumbo
calcs, eax should result to 0. How? See below :-) A xor B = C -> 1 xor 2 = 3 B xor C = A -> 2 xor 3 = 1 C xor A = B -> 3 xor 1 = 2 Simple eh? Well, it is. Anyway... The program went... 1. 00003039 xor 72646E41 = 72645E78 2. 72645E78 xor 90006165 = E2643F1D 3. E2643F1D xor 90909090 = 72F4AF8D 4. 72F4AF8D xor 90909090 = E2643F1D 5. E2643F1D xor 90909090 = 72F4AF8D So we could then say... 1. 90909090 xor 90006165 = 0090F1F5 2. 0090F1F5 xor 72646E41 = 72F49FB4 (1928634292 in dec)Which means that 1928634292 is the correct serial! Try it. Kewl noh? Basically, you just have to do it backwards.
CALC:That's it. Later, Sphinx.
movzx eax, [nLen] cmp al, 8 jg @@2 mov eax, dword ptr [NameBuf+8] mov ebx, dword ptr [NameBuf+4] xor eax, ebx mov ebx, dword ptr [NameBuf] xor eax, ebx jmp @@print @@2: mov eax, dword ptr [NameBuf+10h] mov ebx, dword ptr [NameBuf+0Ch] xor eax, ebx mov ebx, dword ptr [NameBuf+8] xor eax, ebx mov ebx, dword ptr [NameBuf+4] xor eax, ebx mov ebx, dword ptr [NameBuf] xor eax, ebx @@print: ...(convert eax to dec and display it)...
Final Notes |