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