title: Crackme 0x1d01ebcc author: depierre published: 2013-04-18 categories: Reverse Engineering keywords: trick, elf, challenge, crackme, reverse engineering, ida, sigsev, handler, exception 0x1d01ebcc ========== Last week, [jvoisin](https://dustri.org/) came up with a home made crackme. Since I'm really interested in reverse engineering but really lame, I took this opportunity to learn some stuffs. So let's take a look :) Overview ======== Basic strategy, I apply [file](http://linux.about.com/library/cmd/blcmdl1_file.htm) and [readelf](http://linux.about.com/library/cmd/blcmdl1_readelf.htm) on the binary. :::bash depierre$ file 0x1d01ebcc 0x1d01ebcc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), stripped depierre$ readelf -h 0x1d01ebcc En-tête ELF: Magique: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Classe: ELF32 Données: complément à 2, système à octets de poids faible d'abord (little endian) Version: 1 (current) OS/ABI: UNIX - System V Version ABI: 0 Type: EXEC (fichier exécutable) Machine: Intel 80386 Version: 0x1 Adresse du point d'entrée: 0x80492d0 Début des en-têtes de programme: 52 (octets dans le fichier) Début des en-têtes de section: 57005 (octets dans le fichier) Fanions: 0x0 Taille de cet en-tête: 52 (bytes) Taille de l'en-tête du programme: 32 (bytes) Nombre d'en-tête du programme: 9 Taille des en-têtes de section: 40 (bytes) Nombre d'en-têtes de section: 57007 Table d'indexes des chaînes d'en-tête de section: 47806 readelf: ERREUR: Incapable de lire 0x22cb58 octets de En-têtes de section Well, it seems that the section headers are screwed. He might have applied [this trick from his blog](https://dustri.org/b/?p=832). He explains that with few modifications in the elf header, GDB will not be able to debug the crackme. "Qu'à cela ne tienne", I'm going to use IDA instead. Since I have the 6.4 version, [I'm not concerned by this possible trick](http://blog.ioactive.com/2012/12/striking-back-gdb-and-ida-debuggers.html), so let's give a try! Reverse with IDA ================ First, I start the crackme in debug mode and it's quite funny to see how many errors there are. ![Error from IDA because of the malformed header](/static/images/crackme_0x1d01ebcc/screwed_up_header.png) ![Error from IDA because of the sigsev](/static/images/crackme_0x1d01ebcc/ida_sigsev.png) ![Error from IDA because of the sigtrap](/static/images/crackme_0x1d01ebcc/ida_sigtrap.png) Eventhough we have a segment fault, we can continue the execution and the GUI appears. ![GUI crackme](/static/images/crackme_0x1d01ebcc/gui_crackme.png) Now, it's time to take a look at the code. With the init function, we can find the main at the offset _sub_8048D90_. We know that [jvoisin](https://dustri.org/) used [GTK+](http://www.gtk.org/) and with the design of the crackme, we look for the [callback function](https://developer.gnome.org/gtk-tutorial/2.22/x159.html) of the button 'Ok'. :::nasm ; ... mov dword ptr [esp+14h], 0 mov dword ptr [esp+10h], 0 mov dword ptr [esp+0Ch], 0 mov dword ptr [esp+8], offset byte_8049400 ; callback function when 'Ok' is clicked mov dword ptr [esp+4], offset aClicked ; "clicked" mov [esp], eax call sub_8048C50 ; ... The callback function of 'Ok' is _byte_8049400_. Wait, byte?! :::nasm LOAD:08049400 byte_8049400 db 83h, 0ECh, 3Ch ; DATA XREF: sub_8048D90+43Eo LOAD:08049403 dword_8049403 dd 14A165h ; DATA XREF: sub_8048D90+4FFr LOAD:08049407 align 4 LOAD:08049408 dd 24448900h, 0C7C0312Ch, 1C2444h, 0C7000000h, 202444h LOAD:08049408 dd 0E8000000h, 0FFFFF8DCh, 14158Bh, 44890807h, 14890424h LOAD:08049408 dd 0F74AE824h, 489FFFFh, 0F752E824h, 4489FFFFh, 1EB2424h LOAD:08049408 dd 2404C71Dh, 0Bh, 0FFF70FE8h, 1D01EBFFh, 0FFF8A7E8h, 10158BFFh LOAD:08049408 dd 89080700h, 89042444h, 15E82414h, 89FFFFF7h, 1DE82404h LOAD:08049408 dd 89FFFFF7h, 0C7282444h, 182444h, 0EB000000h, 24548B19h LOAD:08049408 dd 24448B18h, 0FD00124h, 0BE0F00B6h, 244401C0h, 24448320h LOAD:08049408 dd 548B0118h, 448B1824h, 0D0012424h, 8400B60Fh, 0EBD675C0h LOAD:08049408 dd 44C71D01h, 1824h, 1CEB0000h, 1824548Bh, 2824448Bh, 0B60FD001h LOAD:08049408 dd 0C0BE0F00h, 130E883h, 831C2444h, 1182444h, 1824548Bh LOAD:08049408 dd 2824448Bh, 0B60FD001h, 75C08400h, 0CA1D3h, 44290807h LOAD:08049408 dd 448B1C24h, 0C2892024h, 0C11FFAC1h, 0D0011AEAh, 293FE083h LOAD:08049408 dd 244489D0h, 24448B20h, 24443B1Ch, 0E8297520h, 0FFFFF63Ch ; ... Hum... New trick here. IDA is not smart enough to see the code so let's help by pressing 'c' then 'p'. We precize to the debugger that we are dealing with code and not data and everything comes back to normal. ![IDA convert to code](/static/images/crackme_0x1d01ebcc/ida_convert_code.png) Below, the flow chart of the function. ![Flow chart of the crackme](/static/images/crackme_0x1d01ebcc/flow_chart_ida.png) We have two subroutines (likely hash routines) and, depending on a comparison of their results, we fail or not. Subroutines =========== Hash nickname ------------- The first subroutine proceeds the nickname. Since I don't have the skill to understand what it staticly does, I analysed it dynamicaly. ![Hash the nickname](/static/images/crackme_0x1d01ebcc/hash_nickname_crackme.png) It sums the hexadecimal values of each character composing the nickname. For instance, with _depierre_ we have: + 64h + 65h + 70h + 69h + 65h + 72h + 72h + 65h = 350h Hash key -------- The second subroutine proceeds the key. ![Hash the key](/static/images/crackme_0x1d01ebcc/hash_key_crackme.png) It proceeds each digit of the key as it and sums them. For instance, with '42' we have: 4h + 2h = 6h Compare routine --------------- The hashes of the nickname and the key are not compared yet. Something happens just before that. :::nasm ; ... mov eax, ds:dword_807000C sub [esp+3Ch+var_20], eax mov eax, [esp+3Ch+var_1C] mov edx, eax sar edx, 1Fh shr edx, 1Ah add eax, edx and eax, 3Fh sub eax, edx mov [esp+3Ch+var_1C], eax mov eax, [esp+3Ch+var_20] cmp eax, [esp+3Ch+var_1C] jnz short loc_8049538 ; ... Right in the middle, we have a modulo! I had some trouble to spot it. First because I'm a newbie and second because the modulo has been weirdly translated. I find the value of the module after a couple of dynamic tests: 40h (64d). Inside IDA ========== In order to be sure of my assumptions, I first try to find one valid key for the nickname _depierre_: + hexsum_nick = 350h + hexsum_nick mod 40h = 10h Let's take 88d as the key: + sum_key = 8h + 8h = 10h It works inside IDA but not outside :/ It seems that he has written a sneaky silent feature against debugging to change the result when the crackme is debugged. Sneaky handle! ============== Maybe you have noticed these two instructions in the compare routine. :::nasm mov eax, ds:dword_807000C sub [esp+3Ch+var_20], eax When they are executed, the value saved in _807000C_ is always 0. It's weird and useless to substract 0 to _EAX_. I try to look for its references somewhere else in the code but nothing interesting. ![Xreferences from IDA](/static/images/crackme_0x1d01ebcc/ida_xref.png) What is the difference between executing the binary with and without a debugger? __The handlers__! Do you remember the SIGSEV we have when we start the crackme? We should take a look. So this time, I precize to IDA that every SIGSEV raised will be handled by the application. ![IDA interrupt handlers](/static/images/crackme_0x1d01ebcc/ida_interrupt_handlers.png) Also, I set up a trace on the offset _807000C_ to see when the value is modified. Last, I break on the SIGSEV interruption's call and I follow the instructions to see when the value is modified. ![Break point when SIGSEV is raised](/static/images/crackme_0x1d01ebcc/call_sigsev.png) A couple of instructions later, I finally found the new handler. According to the trace, this piece of code adds the interrupt's value to _807000C_. ![The new handle Trace's results on the value](/static/images/crackme_0x1d01ebcc/new_handle.png) This new handle function is set up in the middle of the main. You can see the process below. ![Init handle](/static/images/crackme_0x1d01ebcc/init_handle.png) The trick he applied here is kind of cool, in my opinion. He wrote a handler for the _SIGSEV_ interrupt. When the segmentation fault is raised, the handler will simply add the interrupt value to _807000C_. Then, the compare routine will substract it to the the hash of the key (here 11d for SIGSEV). Keygen ====== We have so far the algorithm to find if a key is correct or not, depending on the nickname. On a hand, the crackme sums the hexadecimal value of the characters composing the nickname. Then it applies a modulo 40h (64d) on it. On the other hand, it sums the digit composing the key and it substracts Bh (11d). Finally, it compares both hashes. Hence I've written a little keygen for that crackme (in python). :::python #! /usr/bin/env python2 import sys import random from sets import Set def keygen(nick): diff = (sum([ord(c) for c in nick]) % 64) + 11 key = '' while diff > 0: newdigit = random.randint(1, min(diff, 9)) key += str(newdigit) diff -= newdigit return key def get_keys(nick, keys_count): keys = Set() while len(keys) < keys_count: keys.update([keygen(nick)]) return keys if __name__ == '__main__': print 'Keygen 0x1d01ebcc' keys_count = 1 if len(sys.argv) == 3: keys_count = int(sys.argv[2]) elif len(sys.argv) != 2: print 'Usage: %s nickname [count]' % (sys.argv[0]) sys.exit(1) print 'nickname %s' % (sys.argv[1]) random.seed() for key in get_keys(sys.argv[1], keys_count): print 'key: %s' % (key) Which works like a charm :) :::bash depierre$ ./keygen.py depierre 10 Keygen 0x1d01ebcc nickname depierre key: 9623412 key: 59661 key: 1247841 key: 31878 key: 3718116 key: 95712111 key: 72783 key: 67137111 key: 6428322 key: 1529411121 ![Success Key](/static/images/crackme_0x1d01ebcc/success_key.png) __\o/__ Conclusion ========== In my opinion, this crackme was really interesting for many reasons. First, with the messed up header, I had to use IDA instead of GDB. I usually prefer the second one so it was interesting to go 'deeper' with a tool that I am not familiar with. Second, the crackme were not too hard nor too easy. I mean that it was the right level for someone like me (i.e. who is learning), even if jvoisin helped me on many points (for the init handle for instance). Finally, using the handle on SIGSEV to add its interrupt value was a really nice idea. A sneaky silent one but still a nice one. To be honest, I'm already waiting for the next one :) jvoisin told me that he has increased a lot the difficulty so I'm kind of impatient! That's all folks!