Tutorial by dihux
____________________________________
Target.: vReal's Crackme 3

Remarks: N/A
         
Task...: Serial
         
TutDate: 14.nov.2002

Tools..: IDA, MASM

Little nice crackme with a hardcoded serial protection. But it isn't
that simple. The serial is encrypted and we will have to decrypt it.
Sounds hard, though, piece a shit. Well anyway. We will be using IDA
(Interactive DisAssembler) to crack this crackme. And when we know what
really happens, we will be coding an app with the encryption\decrypt-
ion routine to generate the good serial. So here we go...

First of all start the crackme. Take a good look on it. Try to notice
if there is anything spectacular with it. No, not really. Try pressing
the check button. Incorrect. Well, that is ok for now. But remember
that message. Start IDA. Select new file and disassemble the crackme.

Now we want to know where that string is used so we can trace it down.
And hopefully be as close to the algo as posible. So to search for 
text we just do ALT+T in IDA and a little search dialog pops up. Or
you could select it on MENU-Search\Text... Now type "Incorrect" in the
search dialog. You should end up here.

.data:0040302F dword_40302F    dd 0                    ; DATA XREF: sub_40104A+FEw
.data:0040302F                                         ; sub_40104A+108r ...
.data:00403033 aBuhxsnyr       db 'BuhXsnyr',0         ; DATA XREF: sub_40104A+13Bo
.data:0040303C aVrealCrackme3  db 'vReal Crackme 3',0  ; DATA XREF: sub_40104A+154o
.data:0040303C                                         ; sub_40104A+169o
.data:0040304C aCorrect        db 'Correct!',0         ; DATA XREF: sub_40104A+16Eo
.data:00403055 aIncorrect      db 'Incorrect!',0       ; DATA XREF: sub_40104A+159o
.data:00403060 ; HINSTANCE hInstance
.data:00403060 hInstance       dd 0                    ; DATA XREF: start+7w
.data:00403060                                         ; start+1Br ...
.data:00403064 dword_403064    dd 0                    ; DATA XREF: start+13r
.data:00403068 ; LPARAM lParam
.data:00403068 lParam          dd 0                    ; DATA XREF: sub_40104A+52w
.data:00403068                                         ; sub_40104A+57r
.data:0040306C                 align 200h
.data:0040306C _data           ends
.data:0040306C 
.data:0040306C 
.data:0040306C                 end start

Lotsa strings here. Some interesting and some not so interesting.
The 'BuhXsnyr',0 looks like a string which is encrypted. My thoughts
are that this string is our serial, but in encrypted condition. And it
is. So we need to decrypt it in some way. But first we will look some
things up. Move your blinking cursor on the 'aIncorrect' and hit X.
All the references should now be shown in a little dialog. Only one
ref, go there.

.text:0040119C                 push    0               ; uType
.text:0040119E                 push    offset aVrealCrackme3 ; lpCaption
.text:004011A3                 push    offset aIncorrect ; lpText
.text:004011A8                 push    0               ; hWnd
.text:004011AA                 call    MessageBoxA
.text:004011AF                 jmp     short loc_4011C4

Bad message. Let us take a look on the code above this messagecall.

.text:0040112B                 push    27h             ; nMaxCount
.text:0040112D                 push    offset unk_403007 ; lpString
.text:00401132                 push    0CAh            ; nIDDlgItem
.text:00401137                 push    [ebp+hDlg]      ; hDlg
.text:0040113A                 call    GetDlgItemTextA
.text:0040113F                 cmp     eax, 8
.text:00401142                 jz      short loc_401146
.text:00401144                 jmp     short loc_40119C
.text:00401146 ; ---------------------------------------------------------------------------
.text:00401146 
.text:00401146 loc_401146:                             ; CODE XREF: sub_40104A+F8j
.text:00401146                 xor     eax, eax
.text:00401148                 mov     dword_40302F, eax
.text:0040114D 
.text:0040114D loc_40114D:                             ; CODE XREF: sub_40104A+139j
.text:0040114D                 mov     esi, offset unk_403007
.text:00401152                 add     esi, dword_40302F
.text:00401158                 xor     eax, eax
.text:0040115A                 lodsb
.text:0040115B                 xor     ebx, ebx
.text:0040115D                 mov     ebx, 1Ah
.text:00401162                 xor     al, bl
.text:00401164                 mov     esi, offset unk_403007
.text:00401169                 add     esi, dword_40302F
.text:0040116F                 mov     [esi], al
.text:00401171                 xor     edx, edx
.text:00401173                 mov     edx, dword_40302F
.text:00401179                 inc     edx
.text:0040117A                 mov     dword_40302F, edx
.text:00401180                 cmp     edx, 8
.text:00401183                 jnz     short loc_40114D
.text:00401185                 mov     esi, offset aBuhxsnyr ; "BuhXsnyr"
.text:0040118A                 mov     edi, offset unk_403007
.text:0040118F                 mov     ecx, 8
.text:00401194                 cld
.text:00401195                 repe cmpsb
.text:00401197                 cmp     ecx, 0
.text:0040119A                 jz      short loc_4011B1


Ok. Lotsa code here, but very basic. First it uses the GetDlgItemTextA
api to get everything that is in the editbox. Maxcount 27h which is 39
in decimal. That means that it'll take the 38(bytes) first characters
in the editbox and store them in a buffer. The rest will be ignored.
But when the maxcount is 39 why does it only take the 38 first? 
Well, that is because every string is has to be finalized and to do that
it adds 00 to the next byte. e.g "dihux" = 64 69 68 75 78 00...Therefor
one extra byte is needed, just to finish the string.

The getdlgitemtext api works like this:
UINT GetDlgItemText(
    HWND hDlg,		// handle of dialog box
    int nIDDlgItem,	// identifier of control
    LPTSTR lpString,	// address of buffer for text
    int nMaxCount 	// maximum size of string
   );

NOTE: In disassembled code this will appear in reversed order

That means:
push nMaxCount
push lpString
push nIDDlgItem
call GetDlgItemText

That shouldn't be too hard to understand. The returnvalue is the length
of the text it has taken from the editbox. The returnvalue is always
stored in EAX. After the getdlgitemtext call, it compares eax with eight:
cmp eax,8

if eax is eigth, continue else go away
jz continue
jmp away
That means: we have to have a serial of 8 characters.

.text:00401146 loc_401146:                             ; CODE XREF: sub_40104A+F8j
.text:00401146                 xor     eax, eax
.text:00401148                 mov     dword_40302F, eax
.text:0040114D 

clears eax(eax=0) and puts 0(eax) to dword_40302F

.text:0040114D loc_40114D:                             ; CODE XREF: sub_40104A+139j
.text:0040114D                 mov     esi, offset unk_403007
.text:00401152                 add     esi, dword_40302F
.text:00401158                 xor     eax, eax
.text:0040115A                 lodsb
.text:0040115B                 xor     ebx, ebx
.text:0040115D                 mov     ebx, 1Ah
.text:00401162                 xor     al, bl
.text:00401164                 mov     esi, offset unk_403007
.text:00401169                 add     esi, dword_40302F
.text:0040116F                 mov     [esi], al
.text:00401171                 xor     edx, edx
.text:00401173                 mov     edx, dword_40302F
.text:00401179                 inc     edx
.text:0040117A                 mov     dword_40302F, edx
.text:00401180                 cmp     edx, 8
.text:00401183                 jnz     short loc_40114D

This is the encryption\decryption routine. Explanation:

mov esi,offset unk_403007 	: move to esi our serial which is stored at 403007
add esi,dword_40302F		: esi + the value in dword_40302F
xor eax,eax			: set eax to 0
lodsb				: load string byte. loads from esi and 'saves' in eax
xor ebx,ebx			: set ebx to 0
mov ebx,1Ah			: move 1Ah to ebx, which is 26 in decimal
xor al,bl			: xor al with bl. al^bl
mov esi,offset unk_403007	: move our serial to esi again
add esi,dword_40302F		: esi + dword_40302F
mov [esi],al			: move al to byte pointer in esi
xor edx,edx			: sets edx to 0
mov edx,dword_40302F		: edx = dword_40302F
inc edx				: edx+1(edx is acting like a counter)
mov edx,dword_40302F		: edx = dword_40302F

cmp edx,8			: if not edx = 8
jnz short loc_40114D		: jump to 40114D

Hope I didn't forget something here. I know, alot of deadlisting. But
it's sometimes the best method to explain certain things. I try not to
use so much of it.

.text:00401185                 mov     esi, offset aBuhxsnyr ; "BuhXsnyr"
.text:0040118A                 mov     edi, offset unk_403007
.text:0040118F                 mov     ecx, 8
.text:00401194                 cld
.text:00401195                 repe cmpsb
.text:00401197                 cmp     ecx, 0
.text:0040119A                 jz      short loc_4011B1

Still very logical. Move the BuhXsnyr string to esi. Move our serial
to edi. Set the counter to 8. Compare each byte in esi and edi with
each other. If a compare fails the comparingloop ends and ecx won't
be zero. If ecx isn't zero we've failed. This can be hard to understand.

I can put it in other words too:
repe cmpsb : compares each byte with each other in edi and esi
byte 2 in edi would have been compared to byte 2 in esi.
byte 5 in edi would have been compared to byte 5 in esi... and so on.
each time it loops ecx is decremeted with one. And since our serial is
8 chars long, the ecx register is set to 8.So if the compare succeeds,
ecx=0 and we jz/je to the good location which gives us the good message.
'Correct!'. Remember that if a compare doesn't match the loop ends!
And ecx won't be zero which means that we're bad.

Conclusion:
Take our serial, encrypt it, and compare it with BuhXsnyr.

To get a perfect match, we'd have to decrypt "BuhXsnyr". I've coded a
small app in asm that decrypts "BuhXsnyr". Let us take a little look
on the code. Actually we don't need to reverse the encryption routine
at all, which make it easier for us of course. We only have to copy
the routine and do some small changes to it.
---------------------------------------------------------------------
COMMENT %
Batfile:
\masm32\bin\ml /c /coff /Cp asm.asm
\masm32\bin\link /subsystem:windows asm.obj
pause
%
.386
 .model flat, stdcall
        option casemap :none
        include \masm32\include\windows.inc
        include \masm32\include\user32.inc
        include \masm32\include\kernel32.inc
        includelib \masm32\lib\user32.lib
        includelib \masm32\lib\kernel32.lib
 .data
	sSerial	db "BuhXsnyr",0
	sCap	db "The hardcoded serial is:",0
 .data?
	len 	db ?
 .code

start:	
	mov len,0
lop:
	mov esi, offset sSerial
	add esi, len
	lodsb
	mov ebx,01Ah
	xor al,bl
	mov esi, offset sSerial
	add esi, len
	mov [esi],al
	xor edx,edx
	mov edx,len
	inc edx
	mov len, edx
	cmp edx,8
	jne lop
	invoke MessageBoxA,0,addr sSerial,addr sCap,MB_OK
	invoke ExitProcess,0
end start
---------------------------------------------------------------------
This tiny app 'decrypts' "BuhXsnyr" and messageboxes it on the screen.
I've explained all those instructions before except the invokes.

invoke MessageBoxA,0,addr sSerial,addr sCap,MB_OK:
call messageboxa,no hwnd,messagetext,caption,messagebox type

invoke ExitProcess,0:
call exitprocess,exitcode

[CUT FROM W32HELP]

The MessageBox function creates, displays, and operates a message box. The message box contains an application-defined message and title, plus any combination of predefined icons and push buttons. 
int MessageBox(
    HWND hWnd,	// handle of owner window
    LPCTSTR lpText,	// address of text in message box
    LPCTSTR lpCaption,	// address of title of message box  
    UINT uType 	// style of message box
   );

The ExitProcess function ends a process and all its threads. 
VOID ExitProcess(

    UINT uExitCode 	// exit code for all threads  
   );

[END CUT]

Over and out...
______________________
EFnet @ #New2Cracking
dihux - 2002






