Write-up-josamont-j666
Description
This crackme is available here: http://crackmes.de/users/josamont/j666/. It is an 32-bit ELF written in assembly, with stripped symbols.
File | ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, stripped |
---|---|
MD5 | 60b7c56f174faf4b617af4d724fda88d |
SHA1 | 2d2d85f649822cc42c57f091e1f9449f0c0451a5 |
SHA256 | 60b7971688eaf90b287f981b01fd59378d60930d33235cb9d7dbeead5620de0e |
When run, it prompts for a password and returns "No" if it is incorrect.
$ ./j666 Crackme 666 Josep Password: aldeid No
Analysis
Graph overview
The start function is as follows:
Analysis of the start function
At offset 0x048091, there is a call to sub_048074 (renamed f_compute_checksum) that updates a checksum at memory location 0x8049292 but this variable will never be accessed. So we won't spend time on this function.
LOAD:08048091 start proc near
LOAD:08048091 E8 DE FF FF FF call f_compute_checksum
Then, starting from offset 0x8048096, the program displays a banner and prompts for a password.
LOAD:08048096
LOAD:08048096 loc_8048096:
LOAD:08048096 B9 9A 91 04 08 mov ecx, offset aCrackme666Jose ; "Crackme 666 Josep\n"
LOAD:0804809B BA 13 00 00 00 mov edx, 13h
LOAD:080480A0 E8 70 00 00 00 call f_printf
LOAD:080480A5 B9 AD 91 04 08 mov ecx, offset aPassword ; "Password: "
LOAD:080480AA BA 0B 00 00 00 mov edx, 0Bh
LOAD:080480AF E8 61 00 00 00 call f_printf
LOAD:080480B4 B8 03 00 00 00 mov eax, 3 ; sys_read
LOAD:080480B9 BB 01 00 00 00 mov ebx, 1 ; fd
LOAD:080480BE B9 D8 91 04 08 mov ecx, offset my_password ; addr
LOAD:080480C3 BA 08 00 00 00 mov edx, 8 ; len
LOAD:080480C8 CD 80 int 80h ; LINUX - sys_read
At offset 0x80480CA, the program calls a function that:
- ensures that no breakpoint has been set in the interesting section (repe cmpsb at offset 0x80480E8)
- ensures that the provided password is exclusively composed of hexadecimal characters in uppercase
- builds an array (checksum_) in little-endian from the password
LOAD:080480CA E8 53 00 00 00 call f_check_my_password_hex
LOAD:080480CF B9 09 00 00 00 mov ecx, 9
Then, it calls another function (sub_8048122 renamed f_check_my_password_hex) at offset 0x80480D4 but it's useless for the rest of the analysis, so we won't analyze it. Then, it saves 4 bytes of memory location 0x8048096 in ESI at offset 0x80480DE and saves the checksum_ array in EDI at offset 0x80480E3:
LOAD:080480D4 E8 AC 00 00 00 call f_do_nothing_interesting
LOAD:080480D9 B9 04 00 00 00 mov ecx, 4
LOAD:080480DE BE 96 80 04 08 mov esi, offset loc_8048096 ; esi = [B9 9A 91 04]
LOAD:080480E3 BF 89 92 04 08 mov edi, offset checksum_ ; password = 04919AB9?
sub_8048122 (f_check_my_password_hex)
The function starts by checking whether the code at offset 0x8048E8 has been modified. What this section of code does is to ensure that no software breakpoint has been set at this location. If the bytes at location 0x8048E8 are not equal to 0x1174A6F3, the program will exit.
LOAD:08048122 f_check_my_password_hex proc near
LOAD:08048122 8B 2D 96 92 04 08 mov ebp, dword_8049296 ; ebp = 0x1174A6F3
LOAD:08048128 BB E8 80 04 08 mov ebx, offset loc_80480E8 ; ebx = mem_80480E8
LOAD:0804812D BF 01 00 00 00 mov edi, 1 ; edi = 1
LOAD:08048132 8B 03 mov eax, [ebx] ; eax = mem_80480E8
LOAD:08048134 31 E8 xor eax, ebp ; integrity check (loc_80480E8)
LOAD:08048136 75 D4 jnz short exit ; exit if code has been modified (e.g. software breakpoint set)
;... [SNIP] ...
LOAD:080480E8 loc_80480E8:
LOAD:080480E8 F3 A6 repe cmpsb ; opcode F3 A6
LOAD:080480EA 74 11 jz short loc_80480FD ; opcode 74 11
Then starting from offset 0x8048160, the program loops through all characters of the password and ensures that they are hexadecimal characters (in the range 0x30 - 0x46) and save the password in a little-endian array that will be saved into memory location 0x8049289 (checksum) at offset 0x804817E:
Solution
Static approach
Back to the start function, there is a check at offset 0x80480E8 (where the integrity check was previously performed) that compares bytes of ESI (set to [0xB9, 0x9A, 0x91, 0x04]) and EDI (set to our little-endian password). If both value match, the program jumps to the good boy.
LOAD:080480DE BE 96 80 04 08 mov esi, offset loc_8048096 ; esi = [B9 9A 91 04]
LOAD:080480E3 BF 89 92 04 08 mov edi, offset checksum_ ; password little endian
LOAD:080480E8
LOAD:080480E8 loc_80480E8:
LOAD:080480E8 F3 A6 repe cmpsb
LOAD:080480EA 74 11 jz short good_boy
LOAD:080480EC B9 57 81 04 08 mov ecx, offset loc_8048157
LOAD:080480F1 BA 04 00 00 00 mov edx, 4
LOAD:080480F6 E8 1A 00 00 00 call f_printf
LOAD:080480FB EB 0F jmp short exit
LOAD:080480FD ; ---------------------------------------------------------------------------
LOAD:080480FD
LOAD:080480FD good_boy:
LOAD:080480FD B9 42 81 04 08 mov ecx, offset loc_8048142
LOAD:08048102 BA 04 00 00 00 mov edx, 4
LOAD:08048107 E8 09 00 00 00 call f_printf
With all that in mind, we can easily guess that our password should be [0xB9, 0x9A, 0x91, 0x04] in reverse order: 04919AB9.
Dynamic approach
Note that we might also have solved this crackme with a hardware breakpoint, as follows:
$ gdb -q j666 Reading symbols from j666...(no debugging symbols found)...done. (gdb) b *0x8048091 Breakpoint 1 at 0x8048091 (gdb) r Starting program: j666 Breakpoint 1, 0x08048091 in ?? () (gdb) hbreak *0x80480E8 Hardware assisted breakpoint 2 at 0x80480e8 (gdb) c Continuing. Crackme 666 Josep Password: aldeid Breakpoint 2, 0x080480e8 in ?? () (gdb) x/x $esi 0x8048096: 0x04919ab9
At offset 0x8048096, ESI contains the value of the expected password (04919ab9) that you will just need to put in upper case.
Verification
Now, let's verify our password:
$ ./j666 Crackme 666 Josep Password: 04919AB9 OK
Comments
Keywords: crackme reverse-engineering josamont j666 elf keygen