0xf001's TUTORIAL FOR THE 'TRYTHIS' CRACKME =========================================== Crackme: trythis Author: Detten Language: C++, gtkmm Protection: serial algo Difficulty: rated 4/10 Source: http://biw.rult.at Requirements: understaning assembly language , open mind :) TOC --- - Intro - Used Tools - The Crackme - How To Find The Check_Serial() Function - Analysis Of The Check_Serial Function - Reversing The Calculate_Hash Function - Serial Generator in C - Final Words Intro ===== Welcome to this crackme tutorial! Well, this is a really indepth tutorial I think, I hope I leave nothing unexplained. This tutorial should not only guide you how to solve such crackmes, but also act as a small case study to show you how to work with the linux's tools of our trade. Additionally not everything here is done very thechnical, some zen shows to be very helpfull you will see ... Used Tools ========== Disassembler: lida Debugger: gdb Essential: mammon's .gdb_init file Of course you can also use IDA (it works with wine, or on a second windows box :) ), or any other disassembler. This crackme uses no "anti tool stuff", no anti debugging, no anti disassembling, so you could also use LDasm or objdump for disassembly. The Crackme =========== This crackme is coded in C++, using the gtkmm library, which serves as a C++ wrapper for the gtk functions. gtk is the gimp toolkit and provides nice GUI programming functions for GNOME. According to the crackmes readme file, the job is to find a valid serial for this crackme. But where to start? Without knowing anything about this binary a good tip is always to just run the crackme and play around with it, see how it behaves, and what you have to deal with. So let's do it! When starting the crackme, it shows a little GUI containing of a text entry and 5 "LEDs". There are 2 buttons: "Check Serial" and "Quit". So we can enter some text into the entry box, and click "Check Serial". The 5 LEDs then indicate how "good" your serial number is. An interesting feature :) At the moment we do know nothing about how the serial number should look like. How long should it be? What are valid characters? Numbers only? Hex or Dec? Field delimiters "-" or blanks, or ..... So how do we get our serial number? A good point to start is to load the file into your disassembler and look at the code, look if you find symbolic names which will tell you something, look at any suspicious strings, to get a first impression / overview of what you have to deal with. Obviously we need to find the "Check Serial Function" and see what is behind it. How To Find The Check_Serial() Function ======================================= So I have loaded the file into lida (you can also use LDasm for this). We know we have to deal with GTK GUI programming. This is nothing to be afraid of, in fact no GTK knowledge at all is necessary to solve this crackme. By just looking around, especially at function names (we want to find a function :) ) one immediately comes into my eyes: _ZN7window112check_serialEv Is this the Check_Serial() function we are looking for? Could it be that easy? When looking into the function there are functions called within named "_ZN4GlibeqERKNS_7ustringEPKc", "_ZNK4Glib7ustring6substrEjj", and "_ZN7window113switch_led_onEPN3Gtk5ImageE". So actually it is very likely we have found the right function. Well as we probably all are, I am lazy so I do not look any further and possibly confuse my brain, its time to run the crackme in the debugger and verify! Note: As debugger I do use gdb. gdb out of the box is not so suitable to debug applications without source on the assembly level. Very essential to work with gdb is _mammon's .gdb_init file! It provides you with a little softice look and feel. let's debug! 0xf001@xxx:> gdb ./trythis Now we set a breakpoint inside the "_ZN7window112check_serialEv" function: gdb> bp 0x08052f66 We can then start the crackme by typing gdb> run Nothing special happens yet, of course. Just the crackmes window pops up. So I enter the string "12345" into the text entry and click on the "Check Serial" button. Boom! gdb breaks ... Breakpoint 2, 0x08052f66 in window1::check_serial() ... so we were right. We have found the check_serial function. gdb automatically demangled even the correct name for us :) Analysis Of The Check_Serial Function ===================================== As we have found the function we are interested in we can now study what is going on in the crackme. using gdb we can single step now each instruction by entering gdb> n for next instruction. As we soon find out the first few instructions set up the pointer to our entered serial. But not too fast :) My serial so far is "12345". When stepping through and looking at the registers nothing really interesting happens until address 0x08052F80: [0073:08052F80]---------------------------------------------------------[ code] 0x8052f80 <_ZN7window112check_serialEv+26>: push %eax 0x8052f81 <_ZN7window112check_serialEv+27>: call 0x804e81c 0x8052f86 <_ZN7window112check_serialEv+32>: add $0x10,%esp 0x8052f89 <_ZN7window112check_serialEv+35>: cmp $0x7,%eax 0x8052f8c <_ZN7window112check_serialEv+38>: jbe 0x80532be <_ZN7window112check_serialEv+856> 0x8052f92 <_ZN7window112check_serialEv+44>: sub $0x8,%esp ------------------------------------------------------------------------------ Obviously a function call is executed, and the only parameter which is pushed is held in eax. So lets examine the eax register to see if we can quickly find out what is going on: gdb> dd 0x807ED10 [007B:0807ED10]---------------------------------------------------------[ data] 0807ED10 : 1C 1A 14 08 7C 5E 05 08 - 28 97 08 08 20 5A 58 40 ....|^..(... ZX@ gdb> dd *0x807ED10 [007B:08141A1C]---------------------------------------------------------[ data] 08141A1C : 31 32 33 34 35 00 00 00 - 21 00 00 00 01 00 00 00 12345...!....... hohoo! eax holds a pointer to our entered serial number! Now I still use the lazy approach and do not step into the function. I just let it execute and watch the changes in the registers, or look what registers are checked in the code afterwards. By looking at the code snipped above and using zen I allready have an idea what this function could be. The eax register quite often holds the return value and this value is compared against the constant number 7. Could this be a strlen function? Well guessing is good, but let's check the details: After executing the function via "n", the eax register obviously has changed to the value 5. Hmmm. This is a strlen function I am sure :) - I just restart the crackme by saying gdb> run again, and well, after entering "123456" into the entry box, the eax register changes to the value 6 at address 0x8052f86. After 2 other tries I safely assume the function is a strlen function :) Now as we know that it is allready time to document our disassembly. After hours of looking into disassembly and hexdumps probably nobody will remember each function by address. It is very very very helpful to name the functions which we allready know in our disassembly. Using lida I do that by "L 0804E81C strlen". Of course you can do that in IDA as well, and you can also use objdump -M intel -d trythis > trythis.asm and load the resulting file into vi or any other editor. Then do a search and replace of ALL occurences of "0x804e81c" into "strlen" or what name you like. It is very essencial to replace ALL occurences, so next time the strlen function occurs in the disassembly you will immediately recognize it. This really is extremely helpful and improves readability a lot! Of course there could be some false replacements, when in the somehow unlikely case the numeric constant 0x804e81c is used in the program. Now, this is why I use a disassembler, where I just name this function :) Here in this tutorial I very often copy and paste from gdb so I just add comments here. Such comments are also very helpful during your reversing session, additional to function naming: [0073:08052F80]---------------------------------------------------------[ code] 0x8052f80 <_ZN7window112check_serialEv+26>: push %eax ; EAX holds ptr to entered serial "12345",0 0x8052f81 <_ZN7window112check_serialEv+27>: call 0x804e81c ; strlen 0x8052f86 <_ZN7window112check_serialEv+32>: add $0x10,%esp 0x8052f89 <_ZN7window112check_serialEv+35>: cmp $0x7,%eax 0x8052f8c <_ZN7window112check_serialEv+38>: jbe 0x80532be <_ZN7window112check_serialEv+856> ; end of function 0x8052f92 <_ZN7window112check_serialEv+44>: sub $0x8,%esp ------------------------------------------------------------------------------ OK lets continue with the crackme. Quickly checking the code at address 0x80532be we see the function exits. So we know that if the lentgth of our serial is <=7 the function will exit. Looking at the code without taking the branch to address 0x80532be, we see some instructions and a call to _ZN7window113switch_led_onEPN3Gtk5ImageE. This means the first LED gets switched on when the serial number is > 7 characters. Verifying by rerunning the program and using 7, and then 8 char length of serial we see when length > 7, the first led gets switched on, hehe! :) so first check definately is: strlen(serial) > 7 my serial so far is now "12345678" Continuing single stepping, the next function call we see at address 0x8052fb5 [0073:08052FB5]---------------------------------------------------------[ code] 0x8052fb5 <_ZN7window112check_serialEv+79>: push %eax 0x8052fb6 <_ZN7window112check_serialEv+80>: lea 0xffffffe8(%ebp),%eax 0x8052fb9 <_ZN7window112check_serialEv+83>: push %eax 0x8052fba <_ZN7window112check_serialEv+84>: call 0x804df6c As before I look into the contents of the registers before and after the call. This function takes 2 parameters as we can see by the 2 push instructions above. The first one pushes again a pointer to our serial "12345678". Behind the 2nd argument I do not see any purpose what it could be. Well not yet :) After the call, by examining the memory locations, it turns out that the entered serial is now also found behind the address of the 2nd pushed argument. so this call is a strcpy! No need to dig into further. I document it in the disassembly. It is also good to be carefull and remember the memory location where the 2nd occurence of our serial is stored. It probably was duplicated for a reason :) By further single stepping we reach the next function: [0073:08052FC5]---------------------------------------------------------[ code] 0x8052fc5 <_ZN7window112check_serialEv+95>: push %eax 0x8052fc6 <_ZN7window112check_serialEv+96>: pushl 0x8(%ebp) 0x8052fc9 <_ZN7window112check_serialEv+99>: call 0x8053588 <_ZN7window110is_spiegelEN4Glib7ustringE> 0x8052fce <_ZN7window112check_serialEv+104>: add $0x10,%esp 0x8052fd1 <_ZN7window112check_serialEv+107>: mov %al,0xffffffb7(%ebp) 0x8052fd4 <_ZN7window112check_serialEv+110>: jmp 0x8052ff9 <_ZN7window112check_serialEv+147> ------------------------------------------------------------------------------ by verifying the pushed arguments we see that this function receives our "original" and the copy of our serial. Strange. But OK, let it execute and after the call we see: eax:00000000 ebx:40591D7C ecx:00000004 edx:40952B30 eflags:00200286 esi:08140538 edi:080A1858 esp:BFFFE5A0 ebp:BFFFE618 eip:08052FCE cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a P c [007B:BFFFE5A0]---------------------------------------------------------[stack] BFFFE5D0 : 7C 24 24 40 B0 29 09 08 - 58 18 0A 08 3B CC 13 40 |$$@.)..X...;..@ BFFFE5C0 : 28 FD 09 08 58 18 0A 08 - E8 E5 FF BF 0D 00 00 00 (...X........... BFFFE5B0 : 28 FD 09 08 B0 04 A2 40 - E8 E5 FF BF B5 91 9D 40 (......@.......@ BFFFE5A0 : A8 EC 07 08 00 E6 FF BF - B8 E5 FF BF 71 A8 9F 40 ............q..@ [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:08052FCE]---------------------------------------------------------[ code] 0x8052fce <_ZN7window112check_serialEv+104>: add $0x10,%esp 0x8052fd1 <_ZN7window112check_serialEv+107>: mov %al,0xffffffb7(%ebp) ; <=== return value 0x8052fd4 <_ZN7window112check_serialEv+110>: jmp 0x8052ff9 <_ZN7window112check_serialEv+147> ------------------------------------------------------------------------------ Aha. eax was set to 0. The stack gets cleaned up, then the value in eax gets stored to a local variable. Hmm. By following the execution flow in the disassembly we see that exactly this return value is compared later similar to the strlen check above: 0x8053008 <_ZN7window112check_serialEv+162>: cmpb $0x0,0xffffffb7(%ebp) ; <=== return value 0x805300c <_ZN7window112check_serialEv+166>: je 0x80532be <_ZN7window112check_serialEv+856>; end of function This just to show upfront, here the check for the 2nd led is done :) Now, for me the check failed. The LED did not get activated. After call 0x8053588 <_ZN7window110is_spiegelEN4Glib7ustringE>, eax was 0. Now again zen, the term "is_spiegel" is containing the german word spiegel which means mirror. and it takes 2 times the same string as input. hmm. I also see ecx == 4 here. ecx is often used as a counter register. hmmm. is_spiegel, hmmm. well, lets mirror my serial after the 4th character and see what's happening. so I change my serial to "12344321". aaaand? yehaa! eax becomes 1 and the 2nd led gets enabled! So the 2nd check is: serial must be mirrored, i checked also with 1234554321, same result. That means we need a symmetric serial! My serial so far is "12344321" Probably you got the point now, that not always it is necessary to step into every single function :) Not always it is so easy or we are that lucky that we can guess a functions purpose of course. Also just to warn you there are also often some tricks hidden inside of functions. Like some memory locations changed which will be checked later. By not looking into the function I would have missed that. But going into detail we still can do if things do not behave like we expect them ... :) Single stepping, single stepping, here is the next function: 0x805302f <_ZN7window112check_serialEv+201>: push $0x8055060 ; "BiW-" 0x8053034 <_ZN7window112check_serialEv+206>: lea 0xffffffe8(%ebp),%edx 0x8053037 <_ZN7window112check_serialEv+209>: sub $0x4,%esp 0x805303a <_ZN7window112check_serialEv+212>: push $0x4 0x805303c <_ZN7window112check_serialEv+214>: push $0x0 0x805303e <_ZN7window112check_serialEv+216>: mov 0x8(%ebp),%eax 0x8053041 <_ZN7window112check_serialEv+219>: add $0x68,%eax 0x8053044 <_ZN7window112check_serialEv+222>: push %eax 0x8053045 <_ZN7window112check_serialEv+223>: push %edx 0x8053046 <_ZN7window112check_serialEv+224>: call 0x8053944 <_ZNK4Glib7ustring6substrEjj>; edx = substr(Serial,0,4) Looking at this code snipped - I shorten this tutorial a little bit - I did exactly as with all other functions. It turned out - and not a big surprise when seeing the function name - this is a substr function. The string which is cut (the substring) is strored in the address which is pushed via the edx register. The substring for my current serial is "1234". In the above snippet you see the instruction push $0x8055060 ; "BiW-" Your disassembler might have told you that the address 0x8055060 holds the string "BiW-". Actually this address got pushed but is not used by the substr function. The reason for that is, that this is also a common used techique: parameters which you allready know you will use for the next (or the next after the next, and so on ...) called function - you can allready push onto the stack. No need to do it exactly before the function call. Indeed this can be used to confuse people watching a deadlisting in the first place :) Compilers do so by intention when calling functions to calculate parameters for another function on the same statement or when if statements contain multiple criterias. So let's see when this string "BiW-" which still resides on the stack will be used: [0073:08053044]---------------------------------------------------------[ code] 0x8053044 <_ZN7window112check_serialEv+222>: push %eax 0x8053045 <_ZN7window112check_serialEv+223>: push %edx 0x8053046 <_ZN7window112check_serialEv+224>: call 0x8053944 <_ZNK4Glib7ustring6substrEjj> eax:0807ED10 ebx:40591D7C ecx:00000003 edx:BFFFE5F0 eflags:00200312 esi:08140538 edi:080A1858 esp:BFFFE588 ebp:BFFFE608 eip:08053044 cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I T s z A p c [007B:BFFFE588]---------------------------------------------------------[stack] BFFFE5B8 : D8 E5 FF BF 0D 00 00 01 - 7C 24 24 40 B0 29 09 08 ........|$$@.).. BFFFE5A8 : D8 E5 FF BF B5 91 9D 40 - 40 7D 14 08 58 18 0A 08 .......@@}..X... BFFFE598 : A8 E5 FF BF 71 A8 9F 40 - 40 7D 14 08 B0 04 A2 40 ....q..@@}.....@ BFFFE588 : 00 00 00 00 04 00 00 00 - A8 EC 07 08 60 50 05 08 ............`P.. [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:08053044]---------------------------------------------------------[ code] "BiW-" still on stack 0x8053044 <_ZN7window112check_serialEv+222>: push %eax ; 0x8053045 <_ZN7window112check_serialEv+223>: push %edx ; 0x8053046 <_ZN7window112check_serialEv+224>: call 0x8053944 <_ZNK4Glib7ustring6substrEjj> ; -- see above -- 0x805304b <_ZN7window112check_serialEv+229>: add $0x10,%esp ; cleanup stack 0x805304e <_ZN7window112check_serialEv+232>: lea 0xffffffe8(%ebp),%eax 0x8053051 <_ZN7window112check_serialEv+235>: push %eax ; ; Now pushing our ; substring 0x8053052 <_ZN7window112check_serialEv+236>: call 0x805396a <_ZN4GlibeqERKNS_7ustringEPKc> ; string equal? 0x8053057 <_ZN7window112check_serialEv+241>: add $0x10,%esp 0x805305a <_ZN7window112check_serialEv+244>: mov %al,0xffffffaf(%ebp) ; store to local var 0x805305d <_ZN7window112check_serialEv+247>: jmp 0x8053082 <_ZN7window112check_serialEv+284> ------------------------------------------------------------------------------ so the function _ZN4GlibeqERKNS_7ustringEPKc gets the string "BiW-" as parameter and our substring "1234", the first 4 characters of the entered serial. The name of this function also seems to tell what it is and it is easy to verify this function compares 2 strings. When they are equal eax is set to 1, else 0. The result again is stored in a local variable on the stack. And after the jmp we see: [0073:08053091]---------------------------------------------------------[ code] 0x8053091 <_ZN7window112check_serialEv+299>: cmpb $0x0,0xffffffaf(%ebp) 0x8053095 <_ZN7window112check_serialEv+303>: je 0x80532be <_ZN7window112check_serialEv+856> ; end of function 0x805309b <_ZN7window112check_serialEv+309>: sub $0x8,%esp 0x805309e <_ZN7window112check_serialEv+312>: mov 0x8(%ebp),%eax 0x80530a1 <_ZN7window112check_serialEv+315>: pushl 0x2c(%eax) 0x80530a4 <_ZN7window112check_serialEv+318>: pushl 0x8(%ebp) 0x80530a7 <_ZN7window112check_serialEv+321>: call 0x80536ee <_ZN7window113switch_led_onEPN3Gtk5ImageE> ------------------------------------------------------------------------------ So our serial has to start with "BiW-" !! Let's summarize: The serial must be > 7 characters, symmetric, and start with "BiW-". So my serial so far is BiW-1221-WiB. I have played a little with the app, and saw I get 4 leds for this serial. (This was check 3, for the 3rd led) Also for BiW--WiB. But not for BiW-aa-WiB. So between the "-" characters there must be a number as I assume this will be the next (4th) check then ... we will see... :) The next function call: [0073:080530BF]---------------------------------------------------------[ code] 0x80530bb <_ZN7window112check_serialEv+341>: push 0xffffffff ; push -1 0x80530bd <_ZN7window112check_serialEv+343>: push 0x2d ; '-' 0x80530bf <_ZN7window112check_serialEv+345>: mov 0x8(%ebp),%eax 0x80530c2 <_ZN7window112check_serialEv+348>: add $0x68,%eax 0x80530c5 <_ZN7window112check_serialEv+351>: push %eax ; eax is ptr to entered serial 0x80530c6 <_ZN7window112check_serialEv+352>: call 0x804e45c and after the call: eax:00000008 ebx:BFFFE5F0 ecx:00000008 edx:00000008 eflags:00200286 esi:08140538 edi:080A1858 esp:BFFFE590 ebp:BFFFE608 eip:080530CB cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a P c So eax == 8. Hmmm. Try to think a little bit. What could have happened here? Possibly this is a find character routine, probabbly pushed value -1 is search direction from end of string, pushed 2d ="-", and our string as first parameter. This is what immediately came to my mind. It returns 8, so giving the index of the first occurence of "-" from end of string BiW-1221-WiB | 0123456789ab position I must admit this thime I did not verify anymore I just assumed it is like this. And it turned out I was right. Now in this case you might ask: "How the hell can you know?????". I must admit - I do not know how. It is just thinking in the context that we have to deal with serial checking, mainly string manipulation routines, and alltogether with the previously seen syntax rules for the serial this came into my mind. The trigger was the '-' constant I think. Then seeing return value 8 and ... viola :) You can also say you get a feeling during the time while reversing many applications. This is a nice thing you will experience. Of course you will be wrong as well! Then you have to dig in :) Ok what's next? _______________________________________________________________________________ eax:00000008 ebx:BFFFE5F0 ecx:00000008 edx:00000008 eflags:00200386 esi:08140538 edi:080A1858 esp:BFFFE5A0 ebp:BFFFE608 eip:080530CE cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I T S z a P c [007B:BFFFE5A0]---------------------------------------------------------[stack] BFFFE5D0 : C8 3E 08 08 7C 1D 59 40 - 58 E6 FF BF 83 09 58 40 .>..|.Y@X.....X@ BFFFE5C0 : 7C 24 24 40 B0 29 09 08 - 58 18 0A 08 3B CC 13 40 |$$@.)..X...;..@ BFFFE5B0 : 28 FD 09 08 58 18 0A 01 - D8 E5 FF BF 0D 00 00 01 (...X........... BFFFE5A0 : 28 FD 09 08 B0 04 A2 40 - D8 E5 FF BF B5 91 9D 40 (......@.......@ [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:080530CE]---------------------------------------------------------[ code] 0x80530ce <_ZN7window112check_serialEv+360>: sub $0x4,%eax ; 8-4 = 4 0x80530d1 <_ZN7window112check_serialEv+363>: push %eax ; 4 0x80530d2 <_ZN7window112check_serialEv+364>: push $0x4 ; 4 0x80530d4 <_ZN7window112check_serialEv+366>: mov 0x8(%ebp),%eax 0x80530d7 <_ZN7window112check_serialEv+369>: add $0x68,%eax 0x80530da <_ZN7window112check_serialEv+372>: push %eax ; our serial 0x80530db <_ZN7window112check_serialEv+373>: push %ebx ; tmp buffer 0x80530dc <_ZN7window112check_serialEv+374>: call 0x8053944 <_ZNK4Glib7ustring6substrEjj> ebx = substr(serial,4,4); Now look at this code. The example I chose with my current serial seems to be bad, because 8-4 is also 4 :) but what happens here is: the substring between the "-" signs is cut out. the instruction "sub $0x4,%eax" at 0x80530ce obviously subtracts 4 from the previous result. You can see the previous result as the length of the string from the beginning to the '-' sign. Now we know allready our serial starts with "BiW-" which are 4 characters. We also see that this value 8-4=4 gets stored on stack first so it is the last parameter. Typically for a substr function this is the length of the substring to cut. The next, the constant 4 that gets pushed at address 0x80530d2 is the start of the number "1221". Which is always the same, also if we have BiW-12344321-WiB. You see? The little "figure" below could help you understanding: BiW-1221-WiB | | 0123456789ab position 123456789abc length in characters Now let's check if we are right: registers after call: eax:BFFFE5F0 ebx:BFFFE5F0 ecx:00000000 edx:BFFFE5F0 eflags:00200282 esi:08140538 edi:080A1858 esp:BFFFE594 ebp:BFFFE608 eip:080530E1 cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a p c gdb> dd *$eax [007B:08146234]---------------------------------------------------------[ data] 08146234 : 31 32 32 31 00 00 00 00 - 21 00 00 00 01 00 00 00 1221....!....... so this is what we expected it cuts out the string between the "-" signs. to remember and with the new knowledge, the serial must be in this syntax: BiW-xyyx-WiB, I currently have BiW-1221-WiB so let's continue ... _______________________________________________________________________________ eax:BFFFE5F0 ebx:BFFFE5F0 ecx:00000000 edx:BFFFE5F0 eflags:00200396 esi:08140538 edi:080A1858 esp:BFFFE5A0 ebp:BFFFE608 eip:080530E4 cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I T S z A P c [007B:BFFFE5A0]---------------------------------------------------------[stack] BFFFE5D0 : C8 3E 08 08 7C 1D 59 40 - 58 E6 FF BF 83 09 58 40 .>..|.Y@X.....X@ BFFFE5C0 : 7C 24 24 40 B0 29 09 08 - 58 18 0A 08 3B CC 13 40 |$$@.)..X...;..@ BFFFE5B0 : 28 FD 09 08 58 18 0A 01 - D8 E5 FF BF 0D 00 00 01 (...X........... BFFFE5A0 : 28 FD 09 08 B0 04 A2 40 - D8 E5 FF BF B5 91 9D 40 (......@.......@ [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:080530E4]---------------------------------------------------------[ code] 0x80530e4 <_ZN7window112check_serialEv+382>: sub $0x8,%esp 0x80530e7 <_ZN7window112check_serialEv+385>: lea 0xffffffe8(%ebp),%eax 0x80530ea <_ZN7window112check_serialEv+388>: push %eax ; "1221" 0x80530eb <_ZN7window112check_serialEv+389>: lea 0xffffffd8(%ebp),%eax ; buffer 0x80530ee <_ZN7window112check_serialEv+392>: push %eax 0x80530ef <_ZN7window112check_serialEv+393>: call 0x804df6c ; remember our strcpy? Hmmm. So here the string "1221" gets duplicated. This indicates some check will occur soon. As mentioned earlier we could expect a check for if this string reperesents a number. And it seems to be right: ------------------------------------------------------------------------------ eax:BFFFE5E0 ebx:BFFFE5F0 ecx:BFFFE560 edx:08146234 eflags:00200286 esi:08140538 edi:080A1858 esp:BFFFE590 ebp:BFFFE608 eip:080530F4 cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a P c [007B:BFFFE590]---------------------------------------------------------[stack] BFFFE5C0 : 7C 24 24 40 B0 29 09 08 - 58 18 0A 08 3B CC 13 40 |$$@.)..X...;..@ BFFFE5B0 : 28 FD 09 08 58 18 0A 01 - D8 E5 FF BF 0D 00 00 01 (...X........... BFFFE5A0 : 28 FD 09 08 B0 04 A2 40 - D8 E5 FF BF B5 91 9D 40 (......@.......@ BFFFE590 : E0 E5 FF BF F0 E5 FF BF - 04 00 00 00 04 00 00 00 ................ [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:080530F4]---------------------------------------------------------[ code] 0x80530f4 <_ZN7window112check_serialEv+398>: add $0x8,%esp 0x80530f7 <_ZN7window112check_serialEv+401>: lea 0xffffffd8(%ebp),%eax 0x80530fa <_ZN7window112check_serialEv+404>: push %eax ; "1221" 0x80530fb <_ZN7window112check_serialEv+405>: pushl 0x8(%ebp) ; ? 0x80530fe <_ZN7window112check_serialEv+408>: call 0x8053486 <_ZN7window18is_digitEN4Glib7ustringE> ; is digit 0x8053103 <_ZN7window112check_serialEv+413>: add $0x10,%esp 0x8053106 <_ZN7window112check_serialEv+416>: mov %al,0xffffffae(%ebp) ------------------------------------------------------------------------------ Here is the check for number, result 1 is stored in [ebp-52] (mov %al,0xffffffae(%ebp)) code continues, and switches on the next led, until here: _______________________________________________________________________________ eax:0807ED10 ebx:BFFFE5F0 ecx:00000003 edx:40952B2C eflags:00200312 esi:08140538 edi:080A1858 esp:BFFFE584 ebp:BFFFE608 eip:08053182 cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I T s z A p c [007B:BFFFE584]---------------------------------------------------------[stack] BFFFE5B4 : 58 18 01 01 D8 E5 FF BF - 0D 00 00 01 7C 24 24 40 X...........|$$@ BFFFE5A4 : B0 04 A2 40 D8 E5 FF BF - B5 91 9D 40 E0 E5 FF BF ...@.......@.... BFFFE594 : 48 F3 09 08 04 00 00 00 - 04 00 00 00 28 FD 09 08 H...........(... BFFFE584 : F0 E5 FF BF 08 E6 FF BF - 4E 31 05 08 A8 EC 07 08 ........N1...... [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:08053182]---------------------------------------------------------[ code] 0x8053182 <_ZN7window112check_serialEv+540>: push %eax ; our full entered serial: "BiW-1221-WiB" 0x8053183 <_ZN7window112check_serialEv+541>: call 0x804e81c ; eax=strlen(eax); 0x8053188 <_ZN7window112check_serialEv+546>: add $0x10,%esp ; eax = 0x0C 0x805318b <_ZN7window112check_serialEv+549>: shr %eax ; len / 2: eax=6 0x805318d <_ZN7window112check_serialEv+551>: push %eax ; 6 0x805318e <_ZN7window112check_serialEv+552>: push $0x0 ; 0 0x8053190 <_ZN7window112check_serialEv+554>: mov 0x8(%ebp),%eax 0x8053193 <_ZN7window112check_serialEv+557>: add $0x68,%eax 0x8053196 <_ZN7window112check_serialEv+560>: push %eax ; "BiW-1221-WiB" 0x8053197 <_ZN7window112check_serialEv+561>: pushl 0xffffffa8(%ebp) ; buffer 0x805319a <_ZN7window112check_serialEv+564>: call 0x8053944 <_ZNK4Glib7ustring6substrEjj> eax=substr(Serial,0,6); ------------------------------------------------------------------------------ The code with comments is quite self explaining I think. So the first half of the entered serial is cut into a new string. result is "BiW-12" gdb> dd *$eax [007B:0814199C]---------------------------------------------------------[ data] 0814199C : 42 69 57 2D 31 32 00 00 - 19 00 00 00 67 74 6B 2D BiW-12......gtk- And now it is really getting interesting: this substring is passed to a routine called "Calculate_Hash()": _______________________________________________________________________________ eax:BFFFE5E0 ebx:BFFFE5F0 ecx:00000000 edx:BFFFE5E0 eflags:00200282 esi:08140538 edi:080A1858 esp:BFFFE594 ebp:BFFFE608 eip:080531A6 cs:0073 ds:007B es:007B fs:0000 gs:0033 ss:007B o d I t S z a p c [007B:BFFFE594]---------------------------------------------------------[stack] BFFFE5C4 : B0 29 09 08 58 18 0A 08 - 3B CC 13 40 C8 3E 08 08 .)..X...;..@.>.. BFFFE5B4 : 58 18 01 01 D8 E5 FF BF - 0D 00 00 01 7C 24 24 40 X...........|$$@ BFFFE5A4 : B0 04 A2 40 D8 E5 FF BF - B5 91 9D 40 E0 E5 FF BF ...@.......@.... BFFFE594 : E0 E5 FF BF 04 00 00 00 - 04 00 00 00 28 FD 09 08 ............(... [007B:08140538]---------------------------------------------------------[ data] 08140538 : 70 1A 59 40 01 00 00 00 - 00 00 00 00 00 00 00 00 p.Y@............ 08140548 : 00 00 00 00 20 06 14 08 - 15 00 00 00 00 00 00 00 .... ........... [0073:080531A6]---------------------------------------------------------[ code] 0x80531a6 <_ZN7window112check_serialEv+576>: pushl 0x8(%ebp) ; "BiW-12" 0x80531a9 <_ZN7window112check_serialEv+579>: call 0x80534f2 <_ZN7window114calculate_hashEN4Glib7ustringE> 0x80531ae <_ZN7window112check_serialEv+584>: add $0x10,%esp 0x80531b1 <_ZN7window112check_serialEv+587>: mov %eax,0xffffffa4(%ebp); 1st HASH stored here 0x80531b4 <_ZN7window112check_serialEv+590>: sub $0x8,%esp 0x80531b7 <_ZN7window112check_serialEv+593>: lea 0xffffffb8(%ebp),%eax ------------------------------------------------------------------------------ I do not analyze the hash function yet, as with all functions, I want to see on a higher level what is done to the serial and what criterias must be met. the calculated hash of "BiW-12" is BE770AC1, which is returned by the eax register. in the same manner, the serial of the 2nd half of the string is generated: [0073:080531F4]---------------------------------------------------------[ code] 0x80531f4 <_ZN7window112check_serialEv+654>: call 0x8053944 <_ZNK4Glib7ustring6substrEjj> 0x80531f9 <_ZN7window112check_serialEv+659>: add $0x14,%esp 0x80531fc <_ZN7window112check_serialEv+662>: lea 0xffffffb8(%ebp),%eax 0x80531ff <_ZN7window112check_serialEv+665>: push %eax 0x8053200 <_ZN7window112check_serialEv+666>: pushl 0x8(%ebp) 0x8053203 <_ZN7window112check_serialEv+669>: call 0x80534f2 <_ZN7window114calculate_hashEN4Glib7ustringE> ------------------------------------------------------------------------------ gdb> dd *$eax [007B:08141B9C]---------------------------------------------------------[ data] 08141B9C : 32 31 2D 57 69 42 00 00 - 11 00 00 00 00 00 00 00 21-WiB.......... calculated hash of "21-WiB" is C2B58814 following the disassembly we see those 2 hash values are then xored against eachother: the first hash was stored in 0xffffffa4(%ebp). So after the call to calculate the 2nd hash returned in eax is xored with the first: [0073:08053203]---------------------------------------------------------[ code] 0x8053203 <_ZN7window112check_serialEv+669>: call 0x80534f2 <_ZN7window114calculate_hashEN4Glib7ustringE> 0x8053208 <_ZN7window112check_serialEv+674>: add $0x10,%esp 0x805320b <_ZN7window112check_serialEv+677>: xor 0xffffffa4(%ebp),%eax ; <====== here XOR 0x805320e <_ZN7window112check_serialEv+680>: mov %eax,0xffffffd4(%ebp) ; result stored here 0x8053211 <_ZN7window112check_serialEv+683>: jmp 0x805322d <_ZN7window112check_serialEv+711> now, looking further following the jumps, we see that the result of the XOR must be: [0073:08053267]---------------------------------------------------------[ code] 0x8053267 <_ZN7window112check_serialEv+769>: cmpl $0x807ee3f4,0xffffffd4(%ebp) 0x805326e <_ZN7window112check_serialEv+776>: jne 0x805328a <_ZN7window112check_serialEv+804> 0x8053270 <_ZN7window112check_serialEv+778>: sub $0x8,%esp 0x8053273 <_ZN7window112check_serialEv+781>: mov 0x8(%ebp),%eax 0x8053276 <_ZN7window112check_serialEv+784>: pushl 0x34(%eax) 0x8053279 <_ZN7window112check_serialEv+787>: pushl 0x8(%ebp) ------------------------------------------------------------------------------ so the result of the xor must be: 0x807ee3f4! so what we know is: hash(1st half serial) ^ hash(2nd half serial) = 0x807ee3f4 looking how the code continues is that if the xor == 0x807ee3f4, the last led will be enabled. So finally we know now how the format of the serial must be. And we also know that the first and second half of the serial get hashed by the same hash function and that both of them XORed together must result in the value 0x807ee3f4. so we can stop here as the last LED we will not so fast enable as the others :) and look how the hash is calculated. Reversing The Calculate_Hash Function ===================================== so i place a breakpoint at the hash calculation routine: 0x80534F2, and rerun with the same serial "BiW-1221-WiB" the disassembly is as following (copied from lida output): in assembler: ------------- _ZN7window114calculate_hashEN4Glib7ustringE: 080534F2 55 push ebp ; xref ( 080531A9 08053203 ) 080534F3 89 E5 mov ebp, esp 080534F5 57 push edi ; 080534F6 56 push esi ; save used registers 080534F7 81 EC 10 04 00 00 sub esp, 0x410 ; allocate space for locals: ; ARRAY and HashValue 080534FD 8D BD F8 FB FF FF lea edi, [ebp-00000408] ; 08053503 BE 74 50 05 08 mov esi, 0x8055074 ; init pointers for copy ARRAY 08053508 FC cld ; setup copy direction to forward 08053509 B8 00 01 00 00 mov eax, 0x100 ; 0805350E 89 C1 mov ecx, eax ; copy counter: 0x100 values 08053510 F3 A5 rep:movsd ; copy ARRAY 100 dwords = 400 bytes 08053512 C7 85 F0 FB FF FF 00 00 00 00 mov [ebp-00000410], 0x0 ; init HashValue to 0 0805351C C7 85 F4 FB FF FF 00 00 00 00 mov [ebp-0000040C], 0x0 ; Counter for byte of Serial Label_08053526: 08053526 83 EC 0C sub esp, 0xC ; xref ( 08053579 ) ; 08053529 FF 75 0C push [ebp+0C] ; 0805352C E8 EB B2 FF FF call ___StrLen (0804E81C) ; (near - 0x4D15) ; strlen(serial) 08053531 83 C4 10 add esp, 0x10 08053534 39 85 F4 FB FF FF cmp [ebp-0000040C], eax ; counter < strlen? 0805353A 72 02 jc Label_0805353E (0805353E) ; (near + 0x2) ; yes -> loop again 0805353C EB 3D jmp Label_0805357B (0805357B) ; (near + 0x3D) ; FIN Label_0805353E: 0805353E 83 EC 08 sub esp, 0x8 ; xref ( 0805353A ) 08053541 FF B5 F4 FB FF FF push [ebp-0000040C] ; Our Counter 08053547 FF 75 0C push [ebp+0C] ; Passed string ie "BiW-12" 0805354A E8 7D AA FF FF call Get_Next_Byte_From_Serial ; so: get next character from serial, ; into eax ("CHAR") 0805354F 83 C4 10 add esp, 0x10 08053552 33 85 F0 FB FF FF xor eax, [ebp-00000410] ; xor CHAR, HashValue 08053558 0F B6 D0 movzx edx, al ; low nibble into EDX 0805355B 8B 85 F0 FB FF FF mov eax, [ebp-00000410] ; 08053561 C1 E8 08 shr eax, 0x8 ; HashValue >> 8 08053564 33 84 95 F8 FB FF FF xor eax, [ebp+(04*edx)-00000408] ; XOR eax, ARRAY[4*edx] 0805356B 89 85 F0 FB FF FF mov [ebp-00000410], eax ; store as new HashValue 08053571 8D 85 F4 FB FF FF lea eax, [ebp-0000040C] ; 08053577 FF 00 inc [eax] ; INC Counter for byte of Serial 08053579 EB AB jmp Label_08053526 (08053526) ; (near 0xAB) Label_0805357B: 0805357B 8B 85 F0 FB FF FF mov eax, [ebp-00000410] ; xref ( 0805353C ) ; return value is HashValue 08053581 8D 65 F8 lea esp, [ebp-08] ; 08053584 5E pop esi ; 08053585 5F pop edi ; 08053586 5D pop ebp ; restore stack, registers 08053587 C3 ret Now for luck we see this algorythm first is not very long. going through and commenting line by line is absolute necessary to understand what is going on. Especially all the memory locations should be taken care of. By doing so, we see what the algorythm does: in Words: --------- Initialization - reserve space on stack for local variables: array, counter, hashvalue - copy array with hardcoded constants to space on stack (0x100 DWORDs) - set HashValue to 0 Hashing Loop loop strlen( string ) times. string is ie "BiW-12" or "21-WiB" - get the next byte from the string first loop 'B', then 'i', then 'W', ... - calculate the index for the array lookup: lowest nibble of byte XOR HashValue - HashValue = (HashValue >> 8) XOR ARRAY[index] - increase counter in C ---- int HashValue = 0; for( i=0; i < strlen( serial ); i++ ) HashValue = ( HashValue >> 8 ) ^ ARRAY[ 4 * ( (int) serial[i] ^ HashValue ) ]; So the idea is to recode the algorythm and use it in an own little program, that tries all serial combinations and checks if HashValue(first half of serial) XOR HashValue(second half of serial) equals to 0x807ee3f4 To reduce the number of checks, of course it should be taken care about the symmetry of the serial. But before we start coding, we miss the array. Now this array is easy to get. At address 08053503 BE 74 50 05 08 mov esi, 0x8055074 we see esi is setup with the address of the array. We can either dump it from here, or dump it from the binary (calculating the file offset from the VA), or - a quick and also working way that I choose: I used gdb and just displayed the data via the dd command. copied the values into a file, and used some regular expressions to insert the "," and the leading "0x" to the values. Then copied it into the C source... Serial Generator in C ===================== Here follows the source of the serial generator. The first version created serials like "BiW-xyyx-WiB". It turned out, that no serial number matched. So I just extended the length of the serial by 2 numbers (must be symmetric) tho the scheme: "BiW-xyzzyx-WiB" Then a match was found: BiW-795597-WiB The main loop just takes 2 prepared strings: "BiW-000" and "000-WiB" and loops 999 times. the digits x y z in the serial are independently increased and placed into the 1st and 2nd half of the serial symmetrically. Of course this needs to be done, it would be stupid to not take care about the symmetry and try all numbers from BiW-000000-WiB to BiW-999999-WiB - it would take 1000 times longer :) /* serial generator for dettens "trythis" crackme 2005 0xf001 */ unsigned int ARRAY[] = { 0x00, 0x00, 0x00, 0x00, 0x96, 0x30, 0x07, 0x19, 0x9C, 0x67, 0x0E, 0xEE, 0xBA, 0x51, 0x09, 0x99, 0x19, 0xC4, 0x6D, 0x07, 0x8F, 0xF4, 0x6A, 0x70, 0x35, 0xA5, 0x29, 0xE9, 0xA3, 0x95, 0x64, 0x9E, 0x32, 0x88, 0xDB, 0x0E, 0x94, 0xB7, 0xDC, 0x79, 0x1E, 0xE9, 0xD5, 0xE0, 0x88, 0xD9, 0xD2, 0x97, 0x2B, 0x4C, 0xB6, 0x09, 0xBD, 0x7C, 0xB1, 0x7E, 0x07, 0x2D, 0xB8, 0xE7, 0x91, 0x1D, 0xBF, 0x90, 0x64, 0x10, 0xB7, 0x1D, 0xF2, 0x20, 0xB0, 0x6A, 0x98, 0x76, 0xB9, 0xF3, 0xDE, 0x41, 0xBE, 0x84, 0x7D, 0xD4, 0x39, 0x1A, 0xEB, 0xE4, 0xDD, 0x6D, 0x51, 0xB5, 0x94, 0xF4, 0xC7, 0x85, 0xD3, 0x83, 0x56, 0x98, 0x6C, 0x13, 0xC0, 0xA8, 0x6B, 0x64, 0x7A, 0xF9, 0x62, 0xFD, 0xEC, 0xC9, 0x65, 0x8A, 0x4F, 0x5C, 0x01, 0x14, 0xD9, 0x6C, 0x06, 0x63, 0x63, 0x3D, 0x0F, 0xFA, 0xF5, 0x0D, 0x08, 0x8D, 0x98, 0x25, 0x6E, 0x3B, 0x5E, 0x10, 0x69, 0x4C, 0xE4, 0x41, 0x60, 0xD5, 0x72, 0x71, 0x67, 0xA2, 0xD1, 0xE4, 0x03, 0x3C, 0x47, 0xD4, 0x04, 0x4B, 0xFD, 0x85, 0x0D, 0xD2, 0x6B, 0xB5, 0x0A, 0xA5, 0xFA, 0xA8, 0xB5, 0x35, 0x6C, 0x98, 0xB2, 0x42, 0xD6, 0xC9, 0xBB, 0xDB, 0x40, 0xF9, 0xBC, 0xAC, 0xE3, 0x6C, 0xD8, 0x32, 0x75, 0x5C, 0xDF, 0x45, 0xCF, 0x0D, 0xD6, 0xDC, 0x59, 0x3D, 0xD1, 0xAB, 0xAC, 0x30, 0xD9, 0x26, 0x3A, 0x00, 0xDE, 0x51, 0x80, 0x51, 0xD7, 0xC8, 0x16, 0x61, 0xD0, 0xBF, 0xB5, 0xF4, 0xB4, 0x21, 0x23, 0xC4, 0xB3, 0x56, 0x99, 0x95, 0xBA, 0xCF, 0x0F, 0xA5, 0xBD, 0xB8, 0x9E, 0xB8, 0x02, 0x28, 0x08, 0x88, 0x05, 0x5F, 0xB2, 0xD9, 0x0C, 0xC6, 0x24, 0xE9, 0x0B, 0xB1, 0x87, 0x7C, 0x6F, 0x2F, 0x11, 0x4C, 0x68, 0x58, 0xAB, 0x1D, 0x61, 0xC1, 0x3D, 0x2D, 0x66, 0xB6, 0x90, 0x41, 0xDC, 0x76, 0x06, 0x71, 0xDB, 0x01, 0xBC, 0x20, 0xD2, 0x98, 0x2A, 0x10, 0xD5, 0xEF, 0x89, 0x85, 0xB1, 0x71, 0x1F, 0xB5, 0xB6, 0x06, 0xA5, 0xE4, 0xBF, 0x9F, 0x33, 0xD4, 0xB8, 0xE8, 0xA2, 0xC9, 0x07, 0x78, 0x34, 0xF9, 0x00, 0x0F, 0x8E, 0xA8, 0x09, 0x96, 0x18, 0x98, 0x0E, 0xE1, 0xBB, 0x0D, 0x6A, 0x7F, 0x2D, 0x3D, 0x6D, 0x08, 0x97, 0x6C, 0x64, 0x91, 0x01, 0x5C, 0x63, 0xE6, 0xF4, 0x51, 0x6B, 0x6B, 0x62, 0x61, 0x6C, 0x1C, 0xD8, 0x30, 0x65, 0x85, 0x4E, 0x00, 0x62, 0xF2, 0xED, 0x95, 0x06, 0x6C, 0x7B, 0xA5, 0x01, 0x1B, 0xC1, 0xF4, 0x08, 0x82, 0x57, 0xC4, 0x0F, 0xF5, 0xC6, 0xD9, 0xB0, 0x65, 0x50, 0xE9, 0xB7, 0x12, 0xEA, 0xB8, 0xBE, 0x8B, 0x7C, 0x88, 0xB9, 0xFC, 0xDF, 0x1D, 0xDD, 0x62, 0x49, 0x2D, 0xDA, 0x15, 0xF3, 0x7C, 0xD3, 0x8C, 0x65, 0x4C, 0xD4, 0xFB, 0x58, 0x61, 0xB2, 0x4D, 0xCE, 0x51, 0xB5, 0x3A, 0x74, 0x00, 0xBC, 0xA3, 0xE2, 0x30, 0xBB, 0xD4, 0x41, 0xA5, 0xDF, 0x4A, 0xD7, 0x95, 0xD8, 0x3D, 0x6D, 0xC4, 0xD1, 0xA4, 0xFB, 0xF4, 0xD6, 0xD3, 0x6A, 0xE9, 0x69, 0x43, 0xFC, 0xD9, 0x6E, 0x34, 0x46, 0x88, 0x67, 0xAD, 0xD0, 0xB8, 0x60, 0xDA, 0x73, 0x2D, 0x04, 0x44, 0xE5, 0x1D, 0x03, 0x33, 0x5F, 0x4C, 0x0A, 0xAA, 0xC9, 0x7C, 0x0D, 0xDD, 0x3C, 0x71, 0x05, 0x50, 0xAA, 0x41, 0x02, 0x27, 0x10, 0x10, 0x0B, 0xBE, 0x86, 0x20, 0x0C, 0xC9, 0x25, 0xB5, 0x68, 0x57, 0xB3, 0x85, 0x6F, 0x20, 0x09, 0xD4, 0x66, 0xB9, 0x9F, 0xE4, 0x61, 0xCE, 0x0E, 0xF9, 0xDE, 0x5E, 0x98, 0xC9, 0xD9, 0x29, 0x22, 0x98, 0xD0, 0xB0, 0xB4, 0xA8, 0xD7, 0xC7, 0x17, 0x3D, 0xB3, 0x59, 0x81, 0x0D, 0xB4, 0x2E, 0x3B, 0x5C, 0xBD, 0xB7, 0xAD, 0x6C, 0xBA, 0xC0, 0x20, 0x83, 0xB8, 0xED, 0xB6, 0xB3, 0xBF, 0x9A, 0x0C, 0xE2, 0xB6, 0x03, 0x9A, 0xD2, 0xB1, 0x74, 0x39, 0x47, 0xD5, 0xEA, 0xAF, 0x77, 0xD2, 0x9D, 0x15, 0x26, 0xDB, 0x04, 0x83, 0x16, 0xDC, 0x73, 0x12, 0x0B, 0x63, 0xE3, 0x84, 0x3B, 0x64, 0x94, 0x3E, 0x6A, 0x6D, 0x0D, 0xA8, 0x5A, 0x6A, 0x7A, 0x0B, 0xCF, 0x0E, 0xE4, 0x9D, 0xFF, 0x09, 0x93, 0x27, 0xAE, 0x00, 0x0A, 0xB1, 0x9E, 0x07, 0x7D, 0x44, 0x93, 0x0F, 0xF0, 0xD2, 0xA3, 0x08, 0x87, 0x68, 0xF2, 0x01, 0x1E, 0xFE, 0xC2, 0x06, 0x69, 0x5D, 0x57, 0x62, 0xF7, 0xCB, 0x67, 0x65, 0x80, 0x71, 0x36, 0x6C, 0x19, 0xE7, 0x06, 0x6B, 0x6E, 0x76, 0x1B, 0xD4, 0xFE, 0xE0, 0x2B, 0xD3, 0x89, 0x5A, 0x7A, 0xDA, 0x10, 0xCC, 0x4A, 0xDD, 0x67, 0x6F, 0xDF, 0xB9, 0xF9, 0xF9, 0xEF, 0xBE, 0x8E, 0x43, 0xBE, 0xB7, 0x17, 0xD5, 0x8E, 0xB0, 0x60, 0xE8, 0xA3, 0xD6, 0xD6, 0x7E, 0x93, 0xD1, 0xA1, 0xC4, 0xC2, 0xD8, 0x38, 0x52, 0xF2, 0xDF, 0x4F, 0xF1, 0x67, 0xBB, 0xD1, 0x67, 0x57, 0xBC, 0xA6, 0xDD, 0x06, 0xB5, 0x3F, 0x4B, 0x36, 0xB2, 0x48, 0xDA, 0x2B, 0x0D, 0xD8, 0x4C, 0x1B, 0x0A, 0xAF, 0xF6, 0x4A, 0x03, 0x36, 0x60, 0x7A, 0x04, 0x41, 0xC3, 0xEF, 0x60, 0xDF, 0x55, 0xDF, 0x67, 0xA8, 0xEF, 0x8E, 0x6E, 0x31, 0x79, 0xBE, 0x69, 0x46, 0x8C, 0xB3, 0x61, 0xCB, 0x1A, 0x83, 0x66, 0xBC, 0xA0, 0xD2, 0x6F, 0x25, 0x36, 0xE2, 0x68, 0x52, 0x95, 0x77, 0x0C, 0xCC, 0x03, 0x47, 0x0B, 0xBB, 0xB9, 0x16, 0x02, 0x22, 0x2F, 0x26, 0x05, 0x55, 0xBE, 0x3B, 0xBA, 0xC5, 0x28, 0x0B, 0xBD, 0xB2, 0x92, 0x5A, 0xB4, 0x2B, 0x04, 0x6A, 0xB3, 0x5C, 0xA7, 0xFF, 0xD7, 0xC2, 0x31, 0xCF, 0xD0, 0xB5, 0x8B, 0x9E, 0xD9, 0x2C, 0x1D, 0xAE, 0xDE, 0x5B, 0xB0, 0xC2, 0x64, 0x9B, 0x26, 0xF2, 0x63, 0xEC, 0x9C, 0xA3, 0x6A, 0x75, 0x0A, 0x93, 0x6D, 0x02, 0xA9, 0x06, 0x09, 0x9C, 0x3F, 0x36, 0x0E, 0xEB, 0x85, 0x67, 0x07, 0x72, 0x13, 0x57, 0x00, 0x05, 0x82, 0x4A, 0xBF, 0x95, 0x14, 0x7A, 0xB8, 0xE2, 0xAE, 0x2B, 0xB1, 0x7B, 0x38, 0x1B, 0xB6, 0x0C, 0x9B, 0x8E, 0xD2, 0x92, 0x0D, 0xBE, 0xD5, 0xE5, 0xB7, 0xEF, 0xDC, 0x7C, 0x21, 0xDF, 0xDB, 0x0B, 0xD4, 0xD2, 0xD3, 0x86, 0x42, 0xE2, 0xD4, 0xF1, 0xF8, 0xB3, 0xDD, 0x68, 0x6E, 0x83, 0xDA, 0x1F, 0xCD, 0x16, 0xBE, 0x81, 0x5B, 0x26, 0xB9, 0xF6, 0xE1, 0x77, 0xB0, 0x6F, 0x77, 0x47, 0xB7, 0x18, 0xE6, 0x5A, 0x08, 0x88, 0x70, 0x6A, 0x0F, 0xFF, 0xCA, 0x3B, 0x06, 0x66, 0x5C, 0x0B, 0x01, 0x11, 0xFF, 0x9E, 0x65, 0x8F, 0x69, 0xAE, 0x62, 0xF8, 0xD3, 0xFF, 0x6B, 0x61, 0x45, 0xCF, 0x6C, 0x16, 0x78, 0xE2, 0x0A, 0xA0, 0xEE, 0xD2, 0x0D, 0xD7, 0x54, 0x83, 0x04, 0x4E, 0xC2, 0xB3, 0x03, 0x39, 0x61, 0x26, 0x67, 0xA7, 0xF7, 0x16, 0x60, 0xD0, 0x4D, 0x47, 0x69, 0x49, 0xDB, 0x77, 0x6E, 0x3E, 0x4A, 0x6A, 0xD1, 0xAE, 0xDC, 0x5A, 0xD6, 0xD9, 0x66, 0x0B, 0xDF, 0x40, 0xF0, 0x3B, 0xD8, 0x37, 0x53, 0xAE, 0xBC, 0xA9, 0xC5, 0x9E, 0xBB, 0xDE, 0x7F, 0xCF, 0xB2, 0x47, 0xE9, 0xFF, 0xB5, 0x30, 0x1C, 0xF2, 0xBD, 0xBD, 0x8A, 0xC2, 0xBA, 0xCA, 0x30, 0x93, 0xB3, 0x53, 0xA6, 0xA3, 0xB4, 0x24, 0x05, 0x36, 0xD0, 0xBA, 0x93, 0x06, 0xD7, 0xCD, 0x29, 0x57, 0xDE, 0x54, 0xBF, 0x67, 0xD9, 0x23, 0x2E, 0x7A, 0x66, 0xB3, 0xB8, 0x4A, 0x61, 0xC4, 0x02, 0x1B, 0x68, 0x5D, 0x94, 0x2B, 0x6F, 0x2A, 0x37, 0xBE, 0x0B, 0xB4, 0xA1, 0x8E, 0x0C, 0xC3, 0x1B, 0xDF, 0x05, 0x5A, 0x8D, 0xEF, 0x02, 0x2D }; unsigned int Hash(char *c) { unsigned int i, index, HashValue = 0; for( i=0; i < strlen( c ); i++ ) { index = 4 * ( ( HashValue ^ c[i] ) & 0x000000FF ); HashValue = ( ARRAY[ index ] + (ARRAY[ index+1 ] << 8) + (ARRAY[ index+2 ] << 16) + (ARRAY[ index+3 ] << 24) ) ^ ( HashValue >> 8 ); } return HashValue; } void main(int argc, char **argv) { char Serial1[] = { 'B', 'i', 'W', '-', '0', '0', '0', 0 }; char Serial2[] = { '0', '0', '0', '-', 'W', 'i', 'B', 0 }; unsigned int Hash1; unsigned int Hash2; unsigned char i,j,k; for( i='0'; i <= '9'; i++ ) for( j='0'; j <= '9'; j++ ) for( k='0'; k <= '9'; k++ ) { Serial1[4] = i; Serial1[5] = j; Serial1[6] = k; Serial2[0] = k; Serial2[1] = j; Serial2[2] = i; if( ( Hash( Serial2 ) ^ Hash( Serial1 ) ) == 0x807ee3f4 ) printf("Serial: %s%s\n", Serial1, Serial2); } } Final Words =========== Thank you for your patience if you have read until here! :) I hope you enjoyed this tutorial, there were not really many linux specifics in, but some. If there was nothing new for you then I just want to give you the tip - don't be scared by things where you think you do not know them like linux GUIs, gtkmm, ... as you can see we solved that without even mentioning anything about how gtk programming works. Happy hacking, 0xf001