Saturday, 10 October 2009

crackme12 - part1

This crackme starts by getting the number of cycles, and then storing the high order 32 bits into a varible (I called highOrder32BitsTSC):
CODE:004012B0 start proc far
CODE:004012B0 rdtsc
CODE:004012B2 mov ds:highOrder32BitsTSC, edx

The crackme procedes to the calculation of a filename, based on a string:
CODE:00401000 sub_401000
CODE:00401000 push ebx
CODE:00401001 push esi
CODE:00401002 push edi
CODE:00401003 xor ecx, ecx
CODE:00401005 jmp short loc_401036
CODE:00401007 ; ---------------------------------------------------------------------------
CODE:00401007
CODE:00401007 calc_fileName:
CODE:00401007 mov eax, ds:lpFileName
CODE:0040100C cmp bl, 2Eh
CODE:0040100F jz short loc_401035
CODE:00401011 mov edx, ds:lpFileName
CODE:00401017 movsx eax, bl
CODE:0040101A mov edx, ds:lpFileName
CODE:00401020 mov ebx, 1Ah
CODE:00401025 movsx edx, byte ptr [edx+ecx+1]
CODE:0040102A imul edx
CODE:0040102C cdq
CODE:0040102D idiv ebx
CODE:0040102F add dl, 61h
CODE:00401032 mov [esi+ecx], dl
CODE:00401035
CODE:00401035 loc_401035:
CODE:00401035 inc ecx
CODE:00401036
CODE:00401036 loc_401036:
CODE:00401036 mov esi, ds:lpFileName
CODE:0040103C mov bl, [esi+ecx]
CODE:0040103F test bl, bl
CODE:00401041 jnz short calc_fileName

The result from these calculations is always the same, since the input is always the same (the string xor.key:
DATA:00402000 lpFileName      dd offset aXor_key
DATA:00402010 aXor_key    db 'xor.key',0 )

so, until the end of the string 'xor.key' calc_fileName is going to be called as we can see from:

CODE:00401036 mov     esi, ds:lpFileName
CODE:0040103C mov     bl, [esi+ecx]
CODE:0040103F test    bl, bl
CODE:00401041 jnz     short calc_fileName

and what does calc_fileName do?

CODE:00401007  calc_fileName:
CODE:00401007  mov     eax, ds:lpFileName
CODE:0040100C  cmp     bl, 2Eh
CODE:0040100F  jz      short loc_401035
CODE:00401011  mov     edx, ds:lpFileName
CODE:00401017  movsx   eax, bl
CODE:0040101A  mov     edx, ds:lpFileName
CODE:00401020  mov     ebx, 1Ah
CODE:00401025  movsx   edx, byte ptr [edx+ecx+1]
CODE:0040102A  imul    edx
CODE:0040102C  cdq
CODE:0040102D  idiv    ebx
CODE:0040102F  add     dl, 61h
CODE:00401032  mov     [esi+ecx], dl
CODE:00401035
CODE:00401035 loc_401035:
CODE:00401035  inc     ecx

move the address of aXor_key to eax (401007)
compare bl (the current byte) with 2Eh (the hexa for '.')
if bl equals '.' proceed to the following byte (40100F)
move the address of aXor_key to edx (401011)
move with sign extend the current byte to eax (401017)
move the address of aXor_key to edx (40101A)
move 1Ah to ebx 401020
move with sign extend the next byte to edx (401025)
eax = eax * edx (40102A)
extend the high order bit of EAX throughout EDX (40102C)
intiger division of eax by ebx (40102D)
eax = eax / ebx (eax = eax / 1Ah)
edx = eax % ebx (edx = eax % 1Ah)
add 61h to edx (the ascii code of 'a')
and store it in the first byte of the string aXor_key (401032)
note:
Since we are dividing eax by 1A, the remainder will be between 0 and 25, by adding 61h
will guarantee that the final file name will be between 'a' and 'z'.

After this is done, the string aXor_key will hold the value 'iss.rba'.

Aftewards a few checks are going to take place:
The crackme tries to get the file attributes from a file named 'iss.rba', if it fails to do so, a control variable (named control_variable) will be set to 1:

CODE:00401043  mov     eax, ds:lpFileName
CODE:00401043                                         
CODE:00401048  push    eax                           
CODE:00401049  call    GetFileAttributesA            
CODE:0040104E  cmp     eax, 0FFFFFFh                 
CODE:00401053  jnz     short isHidden                
CODE:00401055  mov     ds:control_variable, 1
CODE:0040105F  jmp     short loc_40106F

if it succeeds, then we will test to see if the file is hidden:

CODE:00401061 isHidden:
CODE:00401061 test    al, 2 ; FILE_ATTRIBUTE_HIDDEN
CODE:00401063  jnz     short loc_40106F
CODE:00401065   ds:control_variable, 1

Afterwards it tries to open the file, and read 27h bytes to a variable named file_buffer (location 40205C)

CODE:0040106F loc_40106F:
CODE:0040106F   mov     edx, ds:control_variable
CODE:00401075   test    edx, edx                    
CODE:00401077  jnz     short loc_401098            
CODE:00401079   push    0                           
CODE:0040107B   mov     ecx, ds:lpFileName
CODE:00401081   push    ecx                         
CODE:00401082   call    _lopen                      
CODE:00401087   mov     edi, eax
CODE:00401089   cmp     edi, 0FFFFFFFFh             
CODE:0040108C   jnz     short loc_401098            
CODE:0040108E  mov     ds:control_variable, 1
CODE:00401098
CODE:00401098 loc_401098:
CODE:00401098  mov     eax, ds:control_variable
CODE:0040109D  test    eax, eax                    
CODE:0040109F  jnz     short test_control_var      
CODE:004010A1  xor     ecx, ecx                     
CODE:004010A3  mov     eax, offset file_buffer
CODE:004010A8
CODE:004010A8 clearBufferAndReadFile:               
CODE:004010A8   mov     byte ptr [eax], 0
CODE:004010AB   inc     ecx                           
CODE:004010AC   inc     eax                           
CODE:004010AD   cmp     ecx, 28h                      
CODE:004010B0   jl      short clearBufferAndReadFile    )
CODE:004010B2   push    27h                           
CODE:004010B4   push    offset file_buffer            
CODE:004010B9   push    edi                           
CODE:004010BA   call    _lread                        
CODE:004010BF   push    edi                           
CODE:004010C0   call    _lclose                       
CODE:004010C5   xor     eax, eax                      
CODE:004010C7   xor     ecx, ecx                      
CODE:004010C9   mov     edx, offset file_buffer

And now, another test comes up:
CODE:004010CE loc_4010CE:
CODE:004010CE  mov     ebx, [edx]                    
CODE:004010D0  add     ebx, eax                      
CODE:004010D2  mov     eax, ebx
CODE:004010D4  add     ecx, 4                        
CODE:004010D7  add     edx, 4                        
CODE:004010DA  cmp     ecx, 25h                      
CODE:004010DD  jl      short loc_4010CE              
CODE:004010DF  test    eax, eax                      
CODE:004010E1  jz      short test_control_var        
CODE:004010E3  mov     ds:control_variable, 1
CODE:004010ED  mov     eax, ds:control_variable
CODE:004010F2  test    eax, eax                        ; Logical Compare
CODE:004010F4  jz      short not_bad

Assuming we have the following in our file iss.rba:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
equivalently:
414243444546474849...
what is happening here is that
44434241 + 48474645 ... & 0xFFFFFFFF = 0 so this loop will run 10 times.
Aditionally, the first byte, is going to be used, to XOR
4012AB - 401145 bytes of the program, starting at 401145
which will be the first executed instruction after the loop that XOR's all the bytes.
We can see that happening here:

CODE:00401112 not_bad:
CODE:00401112 C7 05 C4 20 40 00+mov     ds:dword_4020C4, offset loc_401145
CODE:0040111C C7 05 C8 20 40 00+mov     ds:dword_4020C8, offset loc_4012AB
CODE:00401126 EB 0E             jmp     short loc_401136                ; Jump
CODE:00401128                   ; ---------------------------------------------------------------------------
CODE:00401128
CODE:00401128 loc_401128:
CODE:00401128   mov     dl, ds:file_buffer
CODE:0040112E  xor     [eax], dl                     
CODE:00401130  inc     ds:dword_4020C4               
CODE:00401136 loc_401136:
CODE:00401136  mov     eax, ds:dword_4020C4
CODE:0040113B  mov     ecx, ds:dword_4020C8
CODE:00401141  cmp     eax, ecx                      
CODE:00401143  jb      short loc_401128              
CODE:00401145 loc_401145:                           
CODE:00401145  35 C8 B5 D5 95 xor     eax, 95D5B5C8h   (with opcodes)       

The information I provided above, is all I know about crackme12.
Currently, I'm trying to figure out if there are any other constraints enforced on the first byte of the file iss.rba.
The first idea that came to my mind was simply "we don't have to find a byte that XOR'ed with 358 bytes will generate legal instructions. If we XOR 35h with E9 and the following 4 bytes are a legal address, outside of the 358-xor'ed-bytes-region, we should be ok", again, it was over optimistic :P.

I think I will try with other flow changing instructions, and see where I can go from there, since I'm trying to find out a byte that generates legal instructions, and then build a string such that the addition-test mentioned previously succeeds.

As far as the number of cycles go, I can't say for sure, but the only possible reason I can see for the number of cycles to be stored in a variable, is for later comparision with the number of cycles executed at a later point in the program. One can do this for two reasons (at least, these are the only two reasons I can think of right now): benchmarking, and anti-debug. It will be interesting to see if/how the number of cycles will be used in this crackme.

No comments:

Post a Comment