Announcement

Collapse
No announcement yet.

Writing Basic Keygens for Fun and Profit

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Writing Basic Keygens for Fun and Profit

    Writing Basic Keygens for Fun and Profit
    v1.0 - Biomechanica


    TL;DR WARNING
    Contrary to my preview tutorial, this one will not have pretty screenshots. Everything was done through a text-based debugger, I figured a screenshot of a black terminal window with white text does not really have any advantages over a grey quote box with white text. This means the tutorial looks like one big wall of text. For a general idea of the methods used, you can skip over the assembly code if you want. However, it helps to kinda read through this code as it'll be easier to see what made me draw certain conclusion and how everything comes together to form our program.


    So, finally, here is the sequel to http://www.torrent-invites.com/tutor...un-profit.html :)
    In this guide, I will try to broaden your knowledge of code analysis and reverse-engineering. Whereas the cracking tutorial, which was about binary patching, only required you to recognise important segments in disassembled code, this tutorial will require a deeper understanding of the code so you can mimick it's exact functionality. You will essentially be restoring the disassembled, compiler-generated and optimized 'mess' of assembly code back to higher-level code. I will try to make you more familiar with (static) code analysis and recognizing things that may be of help.
    In this example, I'll be using code I wrote a while ago, when I first came up with the idea of writing a keygenning tutorial. I just compiled the code, without looking at it, so it was kinda black box-ish for me as well, making it more realistic. All in all, I'd say this is a realistic representation of a very basic application that requires a serial. Again, this is very basic: the algorithm is easy to understand without much advanced tricks and the binary itself is not obfuscated, crypted, packed or protected in any other way.
    The analysis has been performed on a Linux system using GDB as a disassembler/debugger. The same techniques, however, apply to keygenning on Windows-based system as well. Contrary to the previous tutorial, this one focusses on code analysis. Though there are some minor debugger-related things, like using breakpoints to check how memory is manipulated throughout functions, this is all used to confirmed assumptions made based on the disassembled code. The techniques used in this tutorial apply to any disassembler on any platform and you can use any debugger to aid you.

    Requirements:
    • A basic understanding of assembly language.
      Though you do not need to be fluent in assembly for this tutorial, but basic understanding of the most basic commands will most certainly be required to understand what is going on.
    • A basic understanding of a high(er)-level language.
      You will have to rewrite the disassembled code to another language. In this tutorial, I will be using C, but you can pick whatever language you prefer. Kudo's to the first person rewriting the keygen in Brainfuck!
    • A basic understanding of how computers handle memory and how registers are used.
      In the end, all a program does is manipulate and execute data in memory, registers hold information currently of importance: ranging from a simple value of a counter to the location in memory of a serial. Therefore, this understanding is required to fully comprehend what is going on in your program. Again, you do not need to know the more advanced stuff, but you should at least be aware of how registers are used in a program and exactly what the heap and the stack is and how they are used. It'll also come in very handy if you're able to read or recognise ASCII and know how to use hexadecimal numbers.
    • Terminology
      I'm not going into basic terms such as 'packers', 'crypters', 'debuggers', 'disassembly', et cetera. These are easy to look up and would just make this guide unnecessarily long and complicated while a quick Google search could probably provide you with better information than I could ever give you without going into too much details.
    • A basic understanding of any disassembler or debugger you're using
      I will be using GDB in this tutorial, which is a text-based debugger for Linux. You will be able to pick up some basic commands from this tutorial, but that's not what this tutorial focusses on. The techniques shown here are applicable on a wide variety of debuggers and disassemblers, so get familiar with whatever software you chose to use before diving into this kind of stuff.


    Now, without further ado, let's get started :)
    [email protected]:~/Desktop$ file keygenme
    keygenme: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
    Looks good :) Now, let's run our program for the first time.
    [email protected]:~/Desktop$ ./keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: AAAAAAAAAA
    Please enter your serial: BBBBBBBBBB

    FAIL - Invalid serial DUURRR HURRR
    Alright, well there's not much we can deduct from this, let's just fire up a debugger. Seeing as GDB uses the AT&T syntax by default, and I prefer the Intel-syntax, I'll change that settings first.
    [email protected]:~/Desktop$ gdb -q ./keygenme
    Reading symbols from /home/entropy/Desktop/keygenme...(no debugging symbols found)...done.
    (gdb) set disassembly-flavor intel
    Let's take at the code's entry point (main() function in C):
    (gdb) disas main
    Dump of assembler code for function main:
    0x080485c4 <main+0>: push ebp
    0x080485c5 <main+1>: mov ebp,esp
    0x080485c7 <main+3>: and esp,0xfffffff0
    0x080485ca <main+6>: sub esp,0x20
    0x080485cd <main+9>: mov eax,0x8048910
    0x080485d2 <main+14>: mov DWORD PTR [esp+0x8],0x804894a
    0x080485da <main+22>: mov DWORD PTR [esp+0x4],0x804894f
    0x080485e2 <main+30>: mov DWORD PTR [esp],eax
    0x080485e5 <main+33>: call 0x80484b4 <[email protected]>
    0x080485ea <main+38>: mov DWORD PTR [esp],0x32
    0x080485f1 <main+45>: call 0x80484d4 <[email protected]>
    0x080485f6 <main+50>: mov DWORD PTR [esp+0x10],eax
    0x080485fa <main+54>: mov DWORD PTR [esp],0x32
    0x08048601 <main+61>: call 0x80484d4 <[email protected]>
    0x08048606 <main+66>: mov DWORD PTR [esp+0x14],eax
    0x0804860a <main+70>: mov eax,0x8048954
    0x0804860f <main+75>: mov DWORD PTR [esp],eax
    0x08048612 <main+78>: call 0x80484b4 <[email protected]>
    0x08048617 <main+83>: mov eax,ds:0x8049b68
    0x0804861c <main+88>: mov DWORD PTR [esp+0x8],eax
    0x08048620 <main+92>: mov DWORD PTR [esp+0x4],0x31
    0x08048628 <main+100>: mov eax,DWORD PTR [esp+0x10]
    0x0804862c <main+104>: mov DWORD PTR [esp],eax
    0x0804862f <main+107>: call 0x8048474 <[email protected]>
    0x08048634 <main+112>: mov eax,0x8048971
    0x08048639 <main+117>: mov DWORD PTR [esp],eax
    0x0804863c <main+120>: call 0x80484b4 <[email protected]>
    0x08048641 <main+125>: mov eax,ds:0x8049b68
    0x08048646 <main+130>: mov DWORD PTR [esp+0x8],eax
    0x0804864a <main+134>: mov DWORD PTR [esp+0x4],0x31
    0x08048652 <main+142>: mov eax,DWORD PTR [esp+0x14]
    0x08048656 <main+146>: mov DWORD PTR [esp],eax
    0x08048659 <main+149>: call 0x8048474 <[email protected]>
    0x0804865e <main+154>: mov eax,DWORD PTR [esp+0x10]
    0x08048662 <main+158>: mov DWORD PTR [esp],eax
    0x08048665 <main+161>: call 0x80484a4 <[email protected]>
    0x0804866a <main+166>: cmp eax,0x9
    0x0804866d <main+169>: jbe 0x8048680 <main+188>
    0x0804866f <main+171>: mov eax,DWORD PTR [esp+0x14]
    0x08048673 <main+175>: mov DWORD PTR [esp],eax
    0x08048676 <main+178>: call 0x80484a4 <[email protected]>
    0x0804867b <main+183>: cmp eax,0x9
    0x0804867e <main+186>: ja 0x8048693 <main+207>
    0x08048680 <main+188>: mov DWORD PTR [esp],0x8048990
    0x08048687 <main+195>: call 0x80484e4 <[email protected]>
    0x0804868c <main+200>: mov eax,0xffffffff
    0x08048691 <main+205>: jmp 0x80486f2 <main+302>
    0x08048693 <main+207>: mov eax,DWORD PTR [esp+0x10]
    0x08048697 <main+211>: mov DWORD PTR [esp],eax
    0x0804869a <main+214>: call 0x80486f4 <getSerial>
    0x0804869f <main+219>: mov DWORD PTR [esp+0x18],eax
    0x080486a3 <main+223>: mov eax,DWORD PTR [esp+0x10]
    0x080486a7 <main+227>: mov DWORD PTR [esp],eax
    0x080486aa <main+230>: call 0x80484a4 <[email protected]>
    0x080486af <main+235>: sub eax,0x1
    0x080486b2 <main+238>: mov DWORD PTR [esp+0x8],eax
    0x080486b6 <main+242>: mov eax,DWORD PTR [esp+0x18]
    0x080486ba <main+246>: mov DWORD PTR [esp+0x4],eax
    0x080486be <main+250>: mov eax,DWORD PTR [esp+0x14]
    0x080486c2 <main+254>: mov DWORD PTR [esp],eax
    0x080486c5 <main+257>: call 0x8048454 <[email protected]>
    0x080486ca <main+262>: test eax,eax
    0x080486cc <main+264>: je 0x80486e1 <main+285>
    0x080486ce <main+266>: mov DWORD PTR [esp],0x80489cc
    0x080486d5 <main+273>: call 0x80484e4 <[email protected]>
    0x080486da <main+278>: mov eax,0xffffffff
    0x080486df <main+283>: jmp 0x80486f2 <main+302>
    0x080486e1 <main+285>: mov DWORD PTR [esp],0x80489f4
    0x080486e8 <main+292>: call 0x80484e4 <[email protected]>
    0x080486ed <main+297>: mov eax,0x0
    0x080486f2 <main+302>: leave
    0x080486f3 <main+303>: ret
    End of assembler dump.
    Reading through the code, you should notice some points of interest.

    0x08048659 <main+149>: call 0x8048474 <[email protected]>
    0x08048659 <main+149>: call 0x8048474 <[email protected]>
    These calls to fgets() are, judging by their location in the code, used to get the username and user serial. This is important because that means we can easily retrieve the addresses at which they're stored. This will come in handy later on, as it'll make it easier to identify function that modify or use those specific memory addresses.
    0x08048665 <main+161>: call 0x80484a4 <[email protected]>
    0x0804866a <main+166>: cmp eax,0x9
    0x0804866d <main+169>: jbe 0x8048680 <main+188>
    0x0804866f <main+171>: mov eax,DWORD PTR [esp+0x14]
    0x08048673 <main+175>: mov DWORD PTR [esp],eax
    0x08048676 <main+178>: call 0x80484a4 <[email protected]>
    0x0804867b <main+183>: cmp eax,0x9
    0x0804867e <main+186>: ja 0x8048693 <main+207>
    This looks suspiciously like some kind of string length check, meaning the length of our strings (presumably username and serial) are restricted. This is an important condition, what's the point of creating a valid serial if the username is too short to be accepted for instance?
    0x0804869a <main+214>: call 0x80486f4 <getSerial>
    This sounds like a rather interesting function. Disassemblers like IDA will also pick up function names like these, though I'm not sure whether Olly can find them as well. We'll dive into this one later on.
    0x080486c5 <main+257>: call 0x8048454 <[email protected]>
    0x080486ca <main+262>: test eax,eax
    0x080486cc <main+264>: je 0x80486e1 <main+285>
    This code snippet is most likely used to check our serial.

    Let's start by retrieving the addresses of the username. As you will probably know, a function's return value is stored in EAX. This means fgets() will store the address of the string it has stored in EAX after it has been called. I'll retrieve it by placing a breakpoint right after the first call to fgets():
    (gdb) b *0x08048634
    Breakpoint 1 at 0x8048634
    (gdb) r
    Starting program: /home/biomechanica/Desktop/keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: AAAAAAAAAA

    Breakpoint 1, 0x08048634 in main ()
    (gdb) i r
    eax 0x804a008 134520840
    ecx 0x0 0
    edx 0xf7fb8334 -134511820
    ebx 0xf7fb6ff4 -134516748
    esp 0xffffd430 0xffffd430
    ebp 0xffffd458 0xffffd458
    esi 0x0 0
    edi 0x0 0
    eip 0x8048634 0x8048634 <main+112>
    eflags 0x246 [ PF ZF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    (gdb) x $eax
    0x804a008: 0x41414141
    0x41 = "A"; jackpot, we found the location of our username: 0x804a008. Let's do the same thing to retrieve the location of our serial.
    Please enter your serial: BBBBBBBBBB

    Breakpoint 2, 0x0804865e in main ()
    (gdb) i r
    eax 0x804a040 134520896
    ecx 0x0 0
    edx 0xf7fb8334 -134511820
    ebx 0xf7fb6ff4 -134516748
    esp 0xffffd430 0xffffd430
    ebp 0xffffd458 0xffffd458
    esi 0x0 0
    edi 0x0 0
    eip 0x804865e 0x804865e <main+154>
    eflags 0x246 [ PF ZF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    (gdb) x/20x $eax
    0x804a040: 0x42424242 0x42424242 0x000a4242 0x00000000
    0x42 = "B", so we now know the serial is stored at 0x804a040. To get an idea of what our serial will look like, let's try to fish it out. The memcmp() call near the end of main() would seem like a logical place to start, so I placed a breakpoint at 0x080486c5 <main+257>: call 0x8048454 <[email protected]>.
    Breakpoint 3, 0x080486c5 in main ()
    (gdb) i r
    eax 0x804a040 134520896
    ecx 0x3 3
    edx 0xfff8 65528
    ebx 0xf7fb6ff4 -134516748
    esp 0xffffd430 0xffffd430
    ebp 0xffffd458 0xffffd458
    esi 0x0 0
    edi 0x0 0
    eip 0x80486c5 0x80486c5 <main+257>
    eflags 0x206 [ PF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    EAX will look familiar, we already know 0x804a040 holds the user submitted serial. The other registers aren't of much help though. Let's delve into memcmp to see how it works, we should be able to derive an address from there.
    (gdb) disas memcmp
    Dump of assembler code for function memcmp:
    0xf7ee8f40 <memcmp+0>: push ebx
    0xf7ee8f41 <memcmp+1>: mov eax,DWORD PTR [esp+0x8]
    0xf7ee8f45 <memcmp+5>: mov edx,DWORD PTR [esp+0xc]
    0xf7ee8f49 <memcmp+9>: mov ecx,DWORD PTR [esp+0x10]
    0xf7ee8f4d <memcmp+13>: cmp ecx,0x1
    [.. a lot more code here ..]
    I pretty much stopped reading here. You can see it's doing some pointer arithmetics in which the stack pointer (ESP) plays an important role. Let's investigate what ESP holds.
    (gdb) x/20x $esp
    0xffffd430: 0x0804a040 0x0804a078 0x0000000a 0xffffd458
    0xffffd440: 0x0804a008 0x0804a040 0x0804a078 0xf7fb6ff4
    0xffffd450: 0x08048860 0x00000000 0xffffd4d8 0xf7e8ac76
    0xffffd460: 0x00000001 0xffffd504 0xffffd50c 0xf7fde9e0
    0xffffd470: 0x08048510 0xffffffff 0xf7ffcff4 0x08048338
    The first two blocks seem to hold pointers. Seeing as the first one points to our user submitted serial and the other one is placed relatively close to it in memory, we might well have hit the jackpot. Let's see what 0x0804a078 has in store for us.
    (gdb) x/20x 0x0804a078
    0x804a078: 0x36373435 0x30313839 0x3234322d 0x31363135
    0x804a088: 0x3139302d 0x34373238 0x3732382d 0x39393131
    0x804a098: 0x00000000 0x00000000 0x00000000 0x00000000
    0x804a0a8: 0x00000000 0x00000000 0x00000000 0x00000000
    0x804a0b8: 0x00000000 0x00000000 0x00000000 0x00000000
    This looks like a classical NULL terminated string. I noticed all the values where in the 0x3*-range. As you might know, 0x30 to 0x39 is the ASCII range for numbers, with 0x30 being 0, 0x31 being 1, 0x32 being 2, etc.. 0x2d is a simple dash ("-"). This means the string here is a bunch of numbers, seperated by a couple of dashes. Sounds like a valid candidate for a serial, right? Let's check.
    This is where the proper serial is stored
    0x804a078: 0x36373435 0x30313839 0x3633322d 0x31323233
    0x804a088: 0x3532392d 0x38363235 0x3733352d 0x33323132
    So the individual bytes would be ...
    36 37 34 35 30 31 38 39 36 33 32 2d 31 32 32 33
    35 32 39 2d 38 36 32 35 37 33 35 2d 33 32 31 32
    Keep in mind, we're running on an Intel machine, so the byte order is little endian. If this sounds unfamiliar, read up on endianness here: Endianness - Wikipedia, the free encyclopedia
    With the proper corrections, we get this:
    35 34 37 36 39 38 31 30 2d 32 33 36 33 32 32 31
    2d 39 32 35 35 32 36 38 2d 35 33 37 32 31 32 33

    ... which becomes ...
    35 34 37 36 39 38 31 30 2d 32 33 36 33 32 32 31 2d 39 32 35 35 32 36 38 2d 35 33 37 32 31 32 33
    ASCII: 54769810-2363221-9255268-5372123
    Again, let's test our assumption.
    [email protected]:~/Desktop$ ./keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: AAAAAAAAAA
    Please enter your serial: 54769810-2363221-9255268-5372123

    WIN - Good boy, your serial is valid, now go write a keygen :)
    There you have it, we've found the valid serial. Of course, this is of little use as it only applies to the username you've entered. A keygen should be able to generate a valid serial for every valid username, so now we need to figure out how this serial has been formed.

    We do however now have a list of important addresses:
    • Username: 0x804a008
    • User serial: 0x804a040
    • Valid serial: 0x804a078

    We also know a valid serial consists of 32 characters which are only numbers seperated by dashes in the following form: 11111111-2222222-3333333-4444444. All of this will come in handy later on.

    Now, it's time to dive into the getSerial() function and get to the interesting part.
    (gdb) disas getSerial
    Dump of assembler code for function getSerial:
    0x080486f4 <getSerial+0>: push ebp
    0x080486f5 <getSerial+1>: mov ebp,esp
    0x080486f7 <getSerial+3>: push ebx
    0x080486f8 <getSerial+4>: sub esp,0x24
    0x080486fb <getSerial+7>: mov DWORD PTR [esp],0x64
    0x08048702 <getSerial+14>: call 0x80484d4 <[email protected]>
    0x08048707 <getSerial+19>: mov DWORD PTR [ebp-0x18],eax
    0x0804870a <getSerial+22>: mov DWORD PTR [esp],0x0
    0x08048711 <getSerial+29>: call 0x80484c4 <[email protected]>
    0x08048716 <getSerial+34>: mov DWORD PTR [esp],eax
    0x08048719 <getSerial+37>: call 0x8048444 <[email protected]>
    0x0804871e <getSerial+42>: mov eax,DWORD PTR [ebp+0x8]
    0x08048721 <getSerial+45>: mov DWORD PTR [esp],eax
    0x08048724 <getSerial+48>: call 0x80484a4 <[email protected]>
    0x08048729 <getSerial+53>: sub eax,0x1
    0x0804872c <getSerial+56>: mov DWORD PTR [ebp-0x14],eax
    0x0804872f <getSerial+59>: cmp DWORD PTR [ebp-0x14],0x20
    0x08048733 <getSerial+63>: jle 0x804873c <getSerial+72>
    0x08048735 <getSerial+65>: mov DWORD PTR [ebp-0x14],0x20
    0x0804873c <getSerial+72>: mov DWORD PTR [ebp-0xc],0x0
    0x08048743 <getSerial+79>: jmp 0x80487a0 <getSerial+172>
    0x08048745 <getSerial+81>: mov eax,DWORD PTR [ebp-0xc]
    0x08048748 <getSerial+84>: add eax,DWORD PTR [ebp+0x8]
    0x0804874b <getSerial+87>: movzx eax,BYTE PTR [eax]
    0x0804874e <getSerial+90>: movsx eax,al
    0x08048751 <getSerial+93>: mov ecx,eax
    0x08048753 <getSerial+95>: xor ecx,DWORD PTR [ebp-0xc]
    0x08048756 <getSerial+98>: mov edx,0x66666667
    0x0804875b <getSerial+103>: mov eax,ecx
    0x0804875d <getSerial+105>: imul edx
    0x0804875f <getSerial+107>: sar edx,0x2
    0x08048762 <getSerial+110>: mov eax,ecx
    0x08048764 <getSerial+112>: sar eax,0x1f
    0x08048767 <getSerial+115>: mov ebx,edx
    0x08048769 <getSerial+117>: sub ebx,eax
    0x0804876b <getSerial+119>: mov eax,ebx
    0x0804876d <getSerial+121>: mov DWORD PTR [ebp-0x10],eax
    0x08048770 <getSerial+124>: mov edx,DWORD PTR [ebp-0x10]
    0x08048773 <getSerial+127>: mov eax,edx
    0x08048775 <getSerial+129>: shl eax,0x2
    0x08048778 <getSerial+132>: add eax,edx
    0x0804877a <getSerial+134>: add eax,eax
    0x0804877c <getSerial+136>: mov edx,ecx
    0x0804877e <getSerial+138>: sub edx,eax
    0x08048780 <getSerial+140>: mov eax,edx
    0x08048782 <getSerial+142>: mov DWORD PTR [ebp-0x10],eax
    0x08048785 <getSerial+145>: mov eax,DWORD PTR [ebp-0x10]
    0x08048788 <getSerial+148>: movsx eax,al
    0x0804878b <getSerial+151>: add eax,0x30
    0x0804878e <getSerial+154>: mov DWORD PTR [ebp-0x10],eax
    0x08048791 <getSerial+157>: mov eax,DWORD PTR [ebp-0xc]
    0x08048794 <getSerial+160>: add eax,DWORD PTR [ebp-0x18]
    0x08048797 <getSerial+163>: mov edx,DWORD PTR [ebp-0x10]
    0x0804879a <getSerial+166>: mov BYTE PTR [eax],dl
    0x0804879c <getSerial+168>: add DWORD PTR [ebp-0xc],0x1
    0x080487a0 <getSerial+172>: mov eax,DWORD PTR [ebp-0xc]
    0x080487a3 <getSerial+175>: cmp eax,DWORD PTR [ebp-0x14]
    0x080487a6 <getSerial+178>: jl 0x8048745 <getSerial+81>
    0x080487a8 <getSerial+180>: jmp 0x80487e4 <getSerial+240>
    0x080487aa <getSerial+182>: mov eax,DWORD PTR [ebp-0xc]
    0x080487ad <getSerial+185>: mov ebx,eax
    0x080487af <getSerial+187>: add ebx,DWORD PTR [ebp-0x18]
    0x080487b2 <getSerial+190>: call 0x80484f4 <[email protected]>
    0x080487b7 <getSerial+195>: mov ecx,eax
    0x080487b9 <getSerial+197>: mov edx,0x66666667
    0x080487be <getSerial+202>: mov eax,ecx
    0x080487c0 <getSerial+204>: imul edx
    0x080487c2 <getSerial+206>: sar edx,0x2
    0x080487c5 <getSerial+209>: mov eax,ecx
    0x080487c7 <getSerial+211>: sar eax,0x1f
    0x080487ca <getSerial+214>: sub edx,eax
    0x080487cc <getSerial+216>: mov eax,edx
    0x080487ce <getSerial+218>: shl eax,0x2
    0x080487d1 <getSerial+221>: add eax,edx
    0x080487d3 <getSerial+223>: add eax,eax
    0x080487d5 <getSerial+225>: mov edx,ecx
    0x080487d7 <getSerial+227>: sub edx,eax
    0x080487d9 <getSerial+229>: mov eax,edx
    0x080487db <getSerial+231>: add eax,0x30
    0x080487de <getSerial+234>: mov BYTE PTR [ebx],al
    0x080487e0 <getSerial+236>: add DWORD PTR [ebp-0xc],0x1
    0x080487e4 <getSerial+240>: cmp DWORD PTR [ebp-0xc],0x1f
    0x080487e8 <getSerial+244>: jle 0x80487aa <getSerial+182>
    0x080487ea <getSerial+246>: mov eax,DWORD PTR [ebp-0x18]
    0x080487ed <getSerial+249>: add eax,0x8
    0x080487f0 <getSerial+252>: mov DWORD PTR [esp+0x8],0x1
    0x080487f8 <getSerial+260>: mov DWORD PTR [esp+0x4],0x2d
    0x08048800 <getSerial+268>: mov DWORD PTR [esp],eax
    0x08048803 <getSerial+271>: call 0x8048484 <[email protected]>
    0x08048808 <getSerial+276>: mov eax,DWORD PTR [ebp-0x18]
    0x0804880b <getSerial+279>: add eax,0x10
    0x0804880e <getSerial+282>: mov DWORD PTR [esp+0x8],0x1
    0x08048816 <getSerial+290>: mov DWORD PTR [esp+0x4],0x2d
    0x0804881e <getSerial+298>: mov DWORD PTR [esp],eax
    0x08048821 <getSerial+301>: call 0x8048484 <[email protected]>
    0x08048826 <getSerial+306>: mov eax,DWORD PTR [ebp-0x18]
    0x08048829 <getSerial+309>: add eax,0x18
    0x0804882c <getSerial+312>: mov DWORD PTR [esp+0x8],0x1
    0x08048834 <getSerial+320>: mov DWORD PTR [esp+0x4],0x2d
    0x0804883c <getSerial+328>: mov DWORD PTR [esp],eax
    0x0804883f <getSerial+331>: call 0x8048484 <[email protected]>
    0x08048844 <getSerial+336>: mov eax,DWORD PTR [ebp-0x18]
    0x08048847 <getSerial+339>: add esp,0x24
    0x0804884a <getSerial+342>: pop ebx
    0x0804884b <getSerial+343>: pop ebp
    0x0804884c <getSerial+344>: ret
    Now, you might be thinking "OMG WTF WER DOES I START?!!??!!1". I can't blame you, a long list of abstract commands such as these can be quite daunting, but try to break it up in smaller sections that are related. This makes it easier to analyse as you can add up all the smaller pieces to figure out how the algorithm itself works.

    Let's start by checking if this function does indeed return the valid serial. Again, functions will store their return value in EAX. If you look at the end of the function, you'll notice there is one last instruction which writes to EAX: 0x08048844 <getSerial+336>: mov eax,DWORD PTR [ebp-0x18]. I'll place a breakpoint at 0x08048844 and have a look to see what value ebp-0x18 holds.
    (gdb) x $ebp-0x18
    0xffffd410: 0x0804a078
    0x0804a078 is the address that holds our proper serial :) GG, looks like we found the function that generates the serial. Now let's see how exactly the serial is generated.

    Quickly scanning through the code, I noticed the following:
    0x0804870a <getSerial+22>: mov DWORD PTR [esp],0x0
    0x08048711 <getSerial+29>: call 0x80484c4 <[email protected]>
    0x08048716 <getSerial+34>: mov DWORD PTR [esp],eax
    0x08048719 <getSerial+37>: call 0x8048444 <[email protected]>
    [.. snip ..]
    0x080487b2 <getSerial+190>: call 0x80484f4 <[email protected]>
    A C coder should immediatley notice the classic time() and srand() functions, combined with the rand() function. Srand() sets its argument as the seed for randomly generated numbers which are rand()'s output. Generally, this is combined with time(0); note the instruction moving 0x0 onto the stack. This means there will be a line along the lines of srand(time(0)); in the original source. Also, it means there's probably some random-numbers used in the serial.
    I also noticed this:
    0x08048721 <getSerial+45>: mov DWORD PTR [esp],eax
    0x08048724 <getSerial+48>: call 0x80484a4 <[email protected]>
    0x08048729 <getSerial+53>: sub eax,0x1
    This means there's some code like int x = strlen(str)-1 in this code. Time to see exactly what 'str' will be.
    (gdb) b *0x08048721
    Breakpoint 4 at 0x8048721
    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/biomechanica/Desktop/keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: AAAAAAAAAA
    Please enter your serial: BBBBBBBBBB

    Breakpoint 4, 0x08048721 in getSerial ()
    (gdb) i r
    eax 0x804a008 134520840
    ecx 0x6a28015e 1781006686
    edx 0xf7fb7044 -134516668
    ebx 0xf7fb6ff4 -134516748
    esp 0xffffd400 0xffffd400
    ebp 0xffffd428 0xffffd428
    esi 0x0 0
    edi 0x0 0
    eip 0x8048721 0x8048721 <getSerial+45>
    eflags 0x292 [ AF SF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    0x804a008 is the location of our username! EAX holds a function's first argument, that means the code takes the length of our username and subtracts one. This is often used in loops; time to look for evidence to support this.
    0x0804872c <getSerial+56>: mov DWORD PTR [ebp-0x14],eax
    0x0804872f <getSerial+59>: cmp DWORD PTR [ebp-0x14],0x20
    0x08048733 <getSerial+63>: jle 0x804873c <getSerial+72>
    0x08048735 <getSerial+65>: mov DWORD PTR [ebp-0x14],0x20
    0x0804873c <getSerial+72>: mov DWORD PTR [ebp-0xc],0x0
    0x08048743 <getSerial+79>: jmp 0x80487a0 <getSerial+172>
    remember, eax = strlen(username)-1, this is copied to [ebp-0x14]; let's call this strlen for now. Strlen is compared to 0x20 (32), if strlen is lower or equal to 0x20,
    it jumps over the next instruction which moves 0x20 into [ebp-0x14] (strlen). So this code will look something like..
    int strlen = strlen(username);
    if( strlen > 32 )
    strlen = 32;
    The jumps leads us here:
    0x080487a0 <getSerial+172>: mov eax,DWORD PTR [ebp-0xc]
    0x080487a3 <getSerial+175>: cmp eax,DWORD PTR [ebp-0x14]
    0x080487a6 <getSerial+178>: jl 0x8048745 <getSerial+81>
    Hmm, [ebp-0xc], we've seen that address before at the start of our function: 0x0804870a <getSerial+22>: mov DWORD PTR [esp],0x0
    also, right before the check, it's value is increased by one: 0x0804879c <getSerial+168>: add DWORD PTR [ebp-0xc],0x1
    Sounds suspiciously like a counter, doesn't it? Combine this with the strlen code, throw in the srand() and time() functions and we can start filling in part of our
    serial generating function...

    function genSerial(char *username) {

    char *serial;
    int len, cnt;

    srand(time(0));

    len = strlen(username)-1;
    if( len > 32 )
    len = 32;

    for( cnt = 0; cnt < len; ++cnt ) {
    // do stuff here
    }

    // other code here

    return serial;

    }
    The jump if lower (jl) statement takes us to getSerial+81, which is at 0x08048745.
    That means this is where the loops starts, and our for() loop takes place between 0x08048733 and 0x080487a6. Let's check the code this loop holds:
    0x08048745 <getSerial+81>: mov eax,DWORD PTR [ebp-0xc]
    0x08048748 <getSerial+84>: add eax,DWORD PTR [ebp+0x8]
    0x0804874b <getSerial+87>: movzx eax,BYTE PTR [eax]
    0x0804874e <getSerial+90>: movsx eax,al
    0x08048751 <getSerial+93>: mov ecx,eax
    0x08048753 <getSerial+95>: xor ecx,DWORD PTR [ebp-0xc]
    0x08048756 <getSerial+98>: mov edx,0x66666667
    0x0804875b <getSerial+103>: mov eax,ecx
    0x0804875d <getSerial+105>: imul edx
    0x0804875f <getSerial+107>: sar edx,0x2
    0x08048762 <getSerial+110>: mov eax,ecx
    0x08048764 <getSerial+112>: sar eax,0x1f
    0x08048767 <getSerial+115>: mov ebx,edx
    0x08048769 <getSerial+117>: sub ebx,eax
    0x0804876b <getSerial+119>: mov eax,ebx
    0x0804876d <getSerial+121>: mov DWORD PTR [ebp-0x10],eax
    0x08048770 <getSerial+124>: mov edx,DWORD PTR [ebp-0x10]
    0x08048773 <getSerial+127>: mov eax,edx
    0x08048775 <getSerial+129>: shl eax,0x2
    0x08048778 <getSerial+132>: add eax,edx
    0x0804877a <getSerial+134>: add eax,eax
    0x0804877c <getSerial+136>: mov edx,ecx
    0x0804877e <getSerial+138>: sub edx,eax
    0x08048780 <getSerial+140>: mov eax,edx
    0x08048782 <getSerial+142>: mov DWORD PTR [ebp-0x10],eax
    0x08048785 <getSerial+145>: mov eax,DWORD PTR [ebp-0x10]
    0x08048788 <getSerial+148>: movsx eax,al
    0x0804878b <getSerial+151>: add eax,0x30
    0x0804878e <getSerial+154>: mov DWORD PTR [ebp-0x10],eax
    0x08048791 <getSerial+157>: mov eax,DWORD PTR [ebp-0xc]
    0x08048794 <getSerial+160>: add eax,DWORD PTR [ebp-0x18]
    0x08048797 <getSerial+163>: mov edx,DWORD PTR [ebp-0x10]
    0x0804879a <getSerial+166>: mov BYTE PTR [eax],dl
    0x0804879c <getSerial+168>: add DWORD PTR [ebp-0xc],0x1
    0x080487a0 <getSerial+172>: mov eax,DWORD PTR [ebp-0xc]
    The first part shouldn't be too hard to understand.
    0x08048745 <getSerial+81>: mov eax,DWORD PTR [ebp-0xc]
    0x08048748 <getSerial+84>: add eax,DWORD PTR [ebp+0x8]
    0x0804874b <getSerial+87>: movzx eax,BYTE PTR [eax]
    0x0804874e <getSerial+90>: movsx eax,al
    0x08048751 <getSerial+93>: mov ecx,eax
    0x08048753 <getSerial+95>: xor ecx,DWORD PTR [ebp-0xc]
    DWORD PTR [ebp-0xc] = counter (as seen before), now let's see what value is held at DWORD PTR [ebp+0x8]:
    Breakpoint 1, 0x08048748 in getSerial ()
    (gdb) i r
    eax 0x0 0
    ecx 0x5 5
    edx 0xffe0 65504
    ebx 0xf7fb6ff4 -134516748
    esp 0xffffd400 0xffffd400
    ebp 0xffffd428 0xffffd428
    esi 0x0 0
    edi 0x0 0
    eip 0x8048748 0x8048748 <getSerial+84>
    eflags 0x297 [ CF PF AF SF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    (gdb) x $ebp+0x8
    0xffffd430: 0x0804a008
    So, DWORD PTR [ebp+0x8] holds the location of our username. This means the code would look something like this in C:
    char a = username+counter; // 0x08048745 to 0x08048751 (this is essentially the same as username[counter]!)
    a ^= counter; // 0x08048753
    We are now left with this code:
    0x08048756 <getSerial+98>: mov edx,0x66666667
    0x0804875b <getSerial+103>: mov eax,ecx
    0x0804875d <getSerial+105>: imul edx
    0x0804875f <getSerial+107>: sar edx,0x2
    0x08048762 <getSerial+110>: mov eax,ecx
    0x08048764 <getSerial+112>: sar eax,0x1f
    0x08048767 <getSerial+115>: mov ebx,edx
    0x08048769 <getSerial+117>: sub ebx,eax
    0x0804876b <getSerial+119>: mov eax,ebx
    0x0804876d <getSerial+121>: mov DWORD PTR [ebp-0x10],eax
    0x08048770 <getSerial+124>: mov edx,DWORD PTR [ebp-0x10]
    0x08048773 <getSerial+127>: mov eax,edx
    0x08048775 <getSerial+129>: shl eax,0x2
    0x08048778 <getSerial+132>: add eax,edx
    0x0804877a <getSerial+134>: add eax,eax
    0x0804877c <getSerial+136>: mov edx,ecx
    0x0804877e <getSerial+138>: sub edx,eax
    0x08048780 <getSerial+140>: mov eax,edx
    0x08048782 <getSerial+142>: mov DWORD PTR [ebp-0x10],eax
    0x08048785 <getSerial+145>: mov eax,DWORD PTR [ebp-0x10]
    0x08048788 <getSerial+148>: movsx eax,al
    0x0804878b <getSerial+151>: add eax,0x30
    0x0804878e <getSerial+154>: mov DWORD PTR [ebp-0x10],eax
    0x08048791 <getSerial+157>: mov eax,DWORD PTR [ebp-0xc]
    0x08048794 <getSerial+160>: add eax,DWORD PTR [ebp-0x18]
    0x08048797 <getSerial+163>: mov edx,DWORD PTR [ebp-0x10]
    0x0804879a <getSerial+166>: mov BYTE PTR [eax],dl
    0x0804879c <getSerial+168>: add DWORD PTR [ebp-0xc],0x1
    0x080487a0 <getSerial+172>: mov eax,DWORD PTR [ebp-0xc]
    Now, I won't lie. When I first saw this, I was all 'LOLWUT, WTF'. The first part appeared to be made up of some random maths and quite frankly I couldn't be arsed to check what value each register held at a certain time. The hardcoded values like 0x66666667 and the numerous shifts and mov's didn't help either. However, I couldn't help but notice this line: 0x0804878b <getSerial+151>: add eax,0x30.
    0x30 huh? Sounds familiar? Remember our serial was completely made up of numbers, and numbers in ASCII are in the 0x30 - 0x39 range? Here, we see 0x30 being added to the result of whatever happens between 0x08048756 and 0x08048788. So, in order to get a valid ASCII number, EAX will have to hold a value of 0x0 to 0x9. The easiest way to sure you get a value between 0 and 9 is by dividing your result by 10 (0xa). This will leave a remainder between 0 and 9 :) So, could all of this code really be the assembly representation of the %-operator in C? Let's find out:
    (gdb) b *0x08048756
    Breakpoint 14 at 0x8048756
    (gdb) b *0x08048788
    Breakpoint 15 at 0x8048788
    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) y
    Starting program: /home/biomechanica/Desktop/keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: AAAAAAAAAA
    Please enter your serial: BBBBBBBBBB

    Breakpoint 14, 0x08048756 in getSerial ()
    (gdb) i r
    eax 0x41 65
    ecx 0x41 65
    edx 0xfff8 65528
    ebx 0xf7fb6ff4 -134516748
    esp 0xffffd400 0xffffd400
    ebp 0xffffd428 0xffffd428
    esi 0x0 0
    edi 0x0 0
    eip 0x8048756 0x8048756 <getSerial+98>
    eflags 0x206 [ PF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    (gdb) c
    Continuing.

    Breakpoint 15, 0x08048788 in getSerial ()
    (gdb) i r
    eax 0x5 5
    ecx 0x41 65
    edx 0x5 5
    ebx 0x6 6
    esp 0xffffd400 0xffffd400
    ebp 0xffffd428 0xffffd428
    esi 0x0 0
    edi 0x0 0
    eip 0x8048788 0x8048788 <getSerial+148>
    eflags 0x216 [ PF AF IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    As you can see, EAX holds 0x41 (decimal: 65) at the start of this snippet of code. This makes sense, as it's the first character of our string which means the counter is set at 0: N xor 0 = N. At the end, it holds 0x5 (decimal: well... 5). 0x30 Will then be added to the value in EAX, meaning it would hold 0x35. OH SNAP SON; this is also the first character of our serial! NO WAI, IT MUST BE HAX!!
    If you aren't sure yet, you can let the code continue a few times and you'll see that it indeed divides username[counter] by 10 and saves the remainder in EAX: our assumption was correct. We are now left with this:
    0x0804878e <getSerial+154>: mov DWORD PTR [ebp-0x10],eax
    0x08048791 <getSerial+157>: mov eax,DWORD PTR [ebp-0xc]
    0x08048794 <getSerial+160>: add eax,DWORD PTR [ebp-0x18]
    0x08048797 <getSerial+163>: mov edx,DWORD PTR [ebp-0x10]
    0x0804879a <getSerial+166>: mov BYTE PTR [eax],dl
    0x0804879c <getSerial+168>: add DWORD PTR [ebp-0xc],0x1
    0x080487a0 <getSerial+172>: mov eax,DWORD PTR [ebp-0xc]
    The following code just looks like some pointer arithmetics, it's probably used to save the result (0x35) of the last code snippet, let's check.
    (gdb) b *0x080487a0
    Breakpoint 16 at 0x80487a0
    (gdb) r
    The program being debugged has been started already.
    Start it from the beginning? (y or n) n
    Program not restarted.
    (gdb) c
    Continuing.

    Breakpoint 16, 0x080487a0 in getSerial ()
    (gdb) i r
    eax 0x804a078 134520952
    ecx 0x41 65
    edx 0x35 53
    ebx 0x6 6
    esp 0xffffd400 0xffffd400
    ebp 0xffffd428 0xffffd428
    esi 0x0 0
    edi 0x0 0
    eip 0x80487a0 0x80487a0 <getSerial+172>
    eflags 0x202 [ IF ]
    cs 0x23 35
    ss 0x2b 43
    ds 0x2b 43
    es 0x2b 43
    fs 0x0 0
    gs 0x63 99
    (gdb) x $eax
    0x804a078: 0x00000035
    0x35 = 5 = the first key of our valid serial! Also, 0x804a078 is the location we determined the proper serial would be located. I'd say it's safe to assume we've analysed this loop well enough to rewrite it's functionality in C.
    First, the username's character currently pointed to by the counter is saved in EAX and than XOR'ed with the counter. In C, this would look something like this: int a = username[n] ^ n
    After that, it's divided by 10 and the remainders gets 0x30 added to it.
    a %= 10;
    a += 0x30;

    Now, the result is saved in our serial.
    serial[n] = a;

    So, let's see how all this works out in our loop:
    function genSerial(char *username) {

    char *serial;
    int len, cnt, a;

    srand(time(0));

    len = strlen(username)-1;
    if( len > 32 )
    len = 32;

    for( cnt = 0; cnt < len; ++cnt ) {
    a = username[cnt] ^ cnt;
    a %= 10;
    a += 0x30;
    serial[cnt] = a;
    }


    // other code here

    return serial;

    }
    Ok, that makes sense :) However, as you can see the valid serial is 32 bytes long (32 ASCII characters) while this loop will only generate a string as long as the username! Let's have a look at the next snippet of code to see how the rest of the serial is formed. We still have to plow through this code:
    0x080487a8 <getSerial+180>: jmp 0x80487e4 <getSerial+240>
    0x080487aa <getSerial+182>: mov eax,DWORD PTR [ebp-0xc]
    0x080487ad <getSerial+185>: mov ebx,eax
    0x080487af <getSerial+187>: add ebx,DWORD PTR [ebp-0x18]
    0x080487b2 <getSerial+190>: call 0x80484f4 <[email protected]>
    0x080487b7 <getSerial+195>: mov ecx,eax
    0x080487b9 <getSerial+197>: mov edx,0x66666667
    0x080487be <getSerial+202>: mov eax,ecx
    0x080487c0 <getSerial+204>: imul edx
    0x080487c2 <getSerial+206>: sar edx,0x2
    0x080487c5 <getSerial+209>: mov eax,ecx
    0x080487c7 <getSerial+211>: sar eax,0x1f
    0x080487ca <getSerial+214>: sub edx,eax
    0x080487cc <getSerial+216>: mov eax,edx
    0x080487ce <getSerial+218>: shl eax,0x2
    0x080487d1 <getSerial+221>: add eax,edx
    0x080487d3 <getSerial+223>: add eax,eax
    0x080487d5 <getSerial+225>: mov edx,ecx
    0x080487d7 <getSerial+227>: sub edx,eax
    0x080487d9 <getSerial+229>: mov eax,edx
    0x080487db <getSerial+231>: add eax,0x30
    0x080487de <getSerial+234>: mov BYTE PTR [ebx],al
    0x080487e0 <getSerial+236>: add DWORD PTR [ebp-0xc],0x1
    0x080487e4 <getSerial+240>: cmp DWORD PTR [ebp-0xc],0x1f
    0x080487e8 <getSerial+244>: jle 0x80487aa <getSerial+182>
    0x080487ea <getSerial+246>: mov eax,DWORD PTR [ebp-0x18]
    0x080487ed <getSerial+249>: add eax,0x8
    0x080487f0 <getSerial+252>: mov DWORD PTR [esp+0x8],0x1
    0x080487f8 <getSerial+260>: mov DWORD PTR [esp+0x4],0x2d
    0x08048800 <getSerial+268>: mov DWORD PTR [esp],eax
    0x08048803 <getSerial+271>: call 0x8048484 <[email protected]>
    0x08048808 <getSerial+276>: mov eax,DWORD PTR [ebp-0x18]
    0x0804880b <getSerial+279>: add eax,0x10
    0x0804880e <getSerial+282>: mov DWORD PTR [esp+0x8],0x1
    0x08048816 <getSerial+290>: mov DWORD PTR [esp+0x4],0x2d
    0x0804881e <getSerial+298>: mov DWORD PTR [esp],eax
    0x08048821 <getSerial+301>: call 0x8048484 <[email protected]>
    0x08048826 <getSerial+306>: mov eax,DWORD PTR [ebp-0x18]
    0x08048829 <getSerial+309>: add eax,0x18
    0x0804882c <getSerial+312>: mov DWORD PTR [esp+0x8],0x1
    0x08048834 <getSerial+320>: mov DWORD PTR [esp+0x4],0x2d
    0x0804883c <getSerial+328>: mov DWORD PTR [esp],eax
    0x0804883f <getSerial+331>: call 0x8048484 <[email protected]>
    0x08048844 <getSerial+336>: mov eax,DWORD PTR [ebp-0x18]
    0x08048847 <getSerial+339>: add esp,0x24
    0x0804884a <getSerial+342>: pop ebx
    0x0804884b <getSerial+343>: pop ebp
    0x0804884c <getSerial+344>: ret
    again, we see a familiar structure:
    0x080487a8 <getSerial+180>: jmp 0x80487e4 <getSerial+240>
    0x080487aa <getSerial+182>: mov eax,DWORD PTR [ebp-0xc]
    [.. snip ..]
    0x080487e0 <getSerial+236>: add DWORD PTR [ebp-0xc],0x1
    0x080487e4 <getSerial+240>: cmp DWORD PTR [ebp-0xc],0x1f
    0x080487e8 <getSerial+244>: jle 0x80487aa <getSerial+182>
    We see 1 being added to a value, which is than compared to 0x1f (31 decimal). If the result is lower or equal, it jumps back to an early piece of code: looks
    like another loop! Also, remember our valid serial holds 32 characters? Let's keep this in mind and explore our code further.
    In the earlier code we saw this snippet: 0x0804873c <getSerial+72>: mov DWORD PTR [ebp-0xc],0x0
    This set the value of our counter, located at [ebp-0xc] to 0. However, in this piece of code we see the same address being used without it being set to 0. This means,
    the counter will have the same value it got in the previous loop, which is strlen(username)-1. As you can see, this loop will fill in the other characters so the serial
    will be 32 bytes long (serial[0] to serial[31])!

    We can conclude the loop must have looked something like this:
    for(; cnt < 32; ++cnt ) {
    a = rand();
    a %= 10;
    a += 0x30;
    serial[cnt] = a;
    }
    We can see there's a bit of code left that needs to be analysed. But, we also haven't explained the dashes in the valid serial! Looks like our analysis is coming to an end! Here's what we still need to go through:
    0x080487ea <getSerial+246>: mov eax,DWORD PTR [ebp-0x18]
    0x080487ed <getSerial+249>: add eax,0x8
    0x080487f0 <getSerial+252>: mov DWORD PTR [esp+0x8],0x1
    0x080487f8 <getSerial+260>: mov DWORD PTR [esp+0x4],0x2d
    0x08048800 <getSerial+268>: mov DWORD PTR [esp],eax
    0x08048803 <getSerial+271>: call 0x8048484 <[email protected]>
    0x08048808 <getSerial+276>: mov eax,DWORD PTR [ebp-0x18]
    0x0804880b <getSerial+279>: add eax,0x10
    0x0804880e <getSerial+282>: mov DWORD PTR [esp+0x8],0x1
    0x08048816 <getSerial+290>: mov DWORD PTR [esp+0x4],0x2d
    0x0804881e <getSerial+298>: mov DWORD PTR [esp],eax
    0x08048821 <getSerial+301>: call 0x8048484 <[email protected]>
    0x08048826 <getSerial+306>: mov eax,DWORD PTR [ebp-0x18]
    0x08048829 <getSerial+309>: add eax,0x18
    0x0804882c <getSerial+312>: mov DWORD PTR [esp+0x8],0x1
    0x08048834 <getSerial+320>: mov DWORD PTR [esp+0x4],0x2d
    0x0804883c <getSerial+328>: mov DWORD PTR [esp],eax
    0x0804883f <getSerial+331>: call 0x8048484 <[email protected]>
    The rest of the code looks like your standard function epilogue, no need to worry about that.
    You should notice a few things here:
    First, the code calls memset, which means there's some memory values being changed. Furthermore, we see the value 0x2d, which either means the decimal value 45 or.. a
    dash in ASCII! Looks like we found the code that sets our dashes :)
    The code is fairly self explanatory. A pointer to the address of our serial, located at [ebp-0x18] as we previously found out, is saved in eax. eax is then incremented
    and the value it currently points to is set to 0x2d using memset. We can see the 0x8'th, 0x10'th and 0x18'th values being changed, that's the 8'th, 16'th and 24'th values, which would means our serial looks something like this: XXXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX
    Compare this to our previously fished serial:
    54769810-2363221-9255268-5372123
    XXXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX

    It's a match! So, it's time to conclude our function:

    function genSerial(char *username) {

    char *serial;
    int len, cnt, a;

    srand(time(0));

    len = strlen(username)-1;
    if( len > 32 )
    len = 32;

    for( cnt = 0; cnt < len; ++cnt ) {
    a = username[cnt] ^ cnt;
    a %= 10;
    a += 0x30;
    serial[cnt] = a;
    }

    while( cnt < 32 ) {
    a = rand();
    a %= 10;
    a += 0x30;
    serial[cnt] = a;
    ++cnt;
    }

    memset(serial+8, 0x2d, 1); // memset requires an integer so we can't use the string "-"!
    memset(serial+16, 0x2d, 1);
    memset(serial+24, 0x2d, 1);

    return serial;

    }
    There you have it! In the end, this is the full source code to my keygen:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <time.h>

    int main(int argc, char **argv) {

    printf("Keygen for Biomechanica's keygenme v1.0\n");
    printf("Biomechanica 2010\n\n");

    srand(time(0));

    if( argc != 2 ) {
    printf("Usage: %s <username>\n", argv[0]);
    return -1;
    }

    char *serial = malloc(100), *username = malloc(100);
    int len, cnt, a;

    username = argv[1];

    len = strlen(username);
    if( len < 10 ) {
    printf("Username must be atleast 10 characters!\n");
    return -1;
    }

    if( len > 32 )
    len = 32;

    for( cnt = 0; cnt < len; ++cnt ) {
    a = (int)username[cnt] ^ cnt;
    a %= 10;
    a += 0x30;
    serial[cnt] = a;
    }

    for( ; cnt < 32; ++cnt ) {
    a = rand();
    a %= 10;
    a += 0x30;
    serial[cnt] = a;
    }


    memset(serial+8, 0x2d, 1);
    memset(serial+16, 0x2d, 1);
    memset(serial+24, 0x2d, 1);

    printf("Username: %s\n", username);
    printf("Serial: %s\n\n", serial);

    return 0;
    }
    You may have noticed I changed one small detail in this code; I didn't subtract 1 from strlen(username). The reason for this is simple; I did have that line in the
    reversed code because I tried mimicking it's functionality as well as I could, but we don't need it here. This is because of the way the program recieves it's input. Remember those calls to fgets()? This means the keygenme binary uses fgets() to get it's input. However the user will terminate the input with a press of the return key. The return key essentially adds a newline to the string, which fgets() retains. This means a string like "AAAA" would be saved in memory as 414141410a00 instead of 4141414100 as you would expect. By subtracting 1 from the string's length, the loop stops before reaching the newline. The keygen code uses a command line argument (argv[1]) as it's source of input, in which case the string is NULL terminated. If we also subtract 1 from the input string's length, the keygen would never generate a valid serial as the correct value for the last character of the string would never be calculated.

    Now, let's give it a try :)

    $ ./keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: Biomechanica
    Please enter your serial: Idunnololsumserialorsumthinlolol

    FAIL - Invalid serial DUURRR HURRR

    $ ./keygen
    Keygen for Biomechanica keygenme v1.0
    Biomechanica 2010

    Usage: ./keygen <username>
    $ ./keygen Biomechanica
    Keygen for Biomechanica keygenme v1.0
    Biomechanica 2010

    Username: Biomechanica
    Serial: 64907202-6563232-1820118-3609114

    $ ./keygenme

    Biomechanica KeygenMe v1.0
    For Torrent-Invites.com

    Please enter your name: Biomechanica
    Please enter your serial: 64907202-6563232-1820118-3609114

    WIN - Good boy, your serial is valid, now go write a keygen :)
    Looks like we did it! w00t \o/

    Here's the Keygenme's source code:
    /*
    *
    * keygenme.c
    * Biomechanica Keygenme v1.0 - 2010
    * Just a simple Linux keygenme, perfect for beginners and tutorials.
    * Shoutout to torrent-invites.com! I assume there's no need for any
    * comments as the code is fairly straightforward :)
    *
    * ~ Biomechanica
    *
    */



    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <time.h>

    #define BLD "\033[1m" // Bold text
    #define RST "\033[0m" // Reset text style
    #define BUFSIZE 50

    char *getSerial(char *str);

    int main(int argc, char **argv) {

    printf("\n%sBiomechanica KeygenMe v1.0%s\n"
    "For Torrent-Invites.com\n\n", BLD, RST);

    char *usrName, *usrSerial, *ctlSerial;
    int len;
    usrName = malloc(BUFSIZE);
    usrSerial = malloc(BUFSIZE);

    printf("Please enter your name: ");
    fgets(usrName, BUFSIZE-1, stdin);
    printf("Please enter your serial: ");
    fgets(usrSerial, BUFSIZE-1, stdin);

    if( strlen(usrName) < 10 || strlen(usrSerial) < 10 ) {
    printf("Please check the length of your username and/or serial.\n\n");
    return -1;
    }

    ctlSerial = getSerial(usrName);

    if( memcmp(usrSerial, ctlSerial, strlen(usrName)-1 ) ) {
    printf("\n\tFAIL - Invalid serial DUURRR HURRR\n\n");
    return -1;
    } else {
    printf("\n\tWIN - Good boy, your serial is valid, now go write a keygen :)\n\n");
    return 0;
    }

    }

    char *getSerial(char *str) {

    char *ret = malloc(100);
    int len, tmp, n;

    srand(time(0));
    len = strlen(str)-1;
    if( len > 32 )
    len = 32;

    for( n = 0; n < len; ++n ) {
    tmp = ((int)str[n] ^ n) % 10;
    tmp = (char)tmp + 0x30;
    ret[n] = tmp;
    }

    while( n < 32 ) {
    ret[n] = rand() % 10 + 0x30;
    ++n;
    }

    memset(ret+8, 0x2d, 1);
    memset(ret+16, 0x2d, 1);
    memset(ret+24, 0x2d, 1);

    // printf("getSerial - ret = %s\n", ret); // DEBUG
    return ret;

    }
    I'm pretty sure it'll compile on Windows just fine as well. You see we did a pretty good job on the keygen's code as it's nearly exactly the same, though this also has to do with the fact I coded both pieces of code myself so they both reflect my personal coding style. Either way, it's not the cleanest code ever as it was coded in a hurry, but it does show a basic method of generating serials. Sadly, the identation is lost when posting it on here (crappy quote boxes instead of proper code boxes are to blame!), but I'm sure it's understandable regardless.
    You can view the sources with proper identation and syntax highlighting here:


    If you feel I missed something important or if you find a mistake, please let me know. I'll fix it and give you credit of course.

  • #2
    This looks interesting, I will have to try it someday.

    Comment


    • #3
      Hmm, I hoped this tut would get more responses.. Oh well, I just finished my notes for a new tutorial, it's about writing remote exploits for basic stack overflow vulnerabilities, allowing a hacker to remote take over a computer. I hope to get it online tonight :)

      Comment


      • #4
        Does look interesting indeed. I've delved in to this sort of thing in the past but always been sidetracked by other things. When I get some time I'll definitely have a shot at running through this and seeing how well it relates to/expands my existing knowledge.

        Anyway, cheers for the detailed writeup. Hope it gets some more well-deserved attention! :D
        Looking for: DB9

        Current Giveaways
        33%: nil
        10%: PTM
        , IPT, UG|MyAnon|HDME|Swarm|Demonoid
        Bonuses
        :

        Comment


        • #5
          This is so far over my head yet so incredibly interesting. I'm going to save this for down the road when I'm more educated in code. This looks so much fun.

          Comment


          • #6
            My new tutorial will be posted in a few minutes :)
            EDIT: Oh snap: http://www.torrent-invites.com/tutor...overflows.html
            Last edited by Biomechanica; October 17, 2010, 07:02 PM.

            Comment


            • #7
              Got it, now how do I integrate a trojan?

              >.>

              Comment


              • #8
                I assume you mean the buffer overflow tutorial? I would prefer you post there so things don't get mixed up. Quite frankly, the sheer fact you ask that question proves by itself you can't code and have absolutely no idea what you're talking about :p I don't mean to insult you, but anyone who has the knowledge to write a half decent trojan can figure out how to automate this process and write, or at the very least generate, shellcode that will download and execute the trojan binary on a victim's machine. I suggest you either keep on studying or stick to writing 1337 batch scripts to hax0rzor your 'skewl'.
                I will not help you write malware.


                EDIT: Wait, I just realised you could very well have meant integrating a trojan into your keygen so you can use that as a vector to spread your malicious binary. Again, I will not help you write malware nor distribute it, but I sure hope the term 'EXE binder' rings a bell :P I wouldn't normally even give you that hint, but I figure it's safe now: most public binders are backdoored, contain trojans themselves and/or are picked up by any self respecting AV. So you'll pretty much have to code one yourself, if you knew how to do so you probably wouldn't be asking this question. It might be an interesting project though. Keep on learning :)
                However, again, I will not help you do anything malicious. My tutorials are here for educational purposes and I myself do not condone such illegal activities. I do however believe it's important to read up on subjects like these to keep programmers aware and to deepen your knowledge of how computers work.
                Last edited by Biomechanica; October 17, 2010, 07:24 PM. Reason: Epiphany, oh snap

                Comment


                • #9
                  Originally posted by Biomechanica View Post
                  I assume you mean the buffer overflow tutorial? I would prefer you post there so things don't get mixed up. Quite frankly, the sheer fact you ask that question proves by itself you can't code and have absolutely no idea what you're talking about :p I don't mean to insult you, but anyone who has the knowledge to write a half decent trojan can figure out how to automate this process and write, or at the very least generate, shellcode that will download and execute the trojan binary on a victim's machine. I suggest you keep on studying or stick to 31337 batch scripts :D
                  I will not help you write malware.

                  Sorry, was a bad attempt at a joke on my part :(

                  Comment


                  • #10
                    took awhile....haha but ty x)

                    Comment


                    • #11
                      Originally posted by teh_pwn View Post
                      Sorry, was a bad attempt at a joke on my part :(
                      Meh I just took it to serious I suppose :D I get kinda paranoid sometimes due to all the OMG HOW DOES I HAX MAH SKEWL and HOW2HAX MSN posts you see on a lot of forums..

                      Comment


                      • #12
                        Now, I don't mean to double post, but I see my last tutorial has been deleted :') lol, yet another case of fear for skids I suppose? If we outlaw such knowledge, only outlaws will have that knowledge and the skills to write exploits; then how are we going to defend ourselves? I'd say raising awareness among programmers isn't a bad thing. Oh well..

                        EDIT:
                        Also, doesn't anyone see the irony here? It's an entire forum about torrenting (= pirating, in this case) and tutorials about keygenning and binary patching (cracking) are fine, but oh noes; exploiting bugs is a no-go. Whereas the warez-scene and hacker-scene have always been so close and intertwined.. Where do you think those first crackers came from?

                        Meh, that's enough ranting. Who am I to judge, right?
                        Last edited by Biomechanica; October 20, 2010, 07:06 PM. Reason: Yet another epiphany && Yet another mod edit && it's settled :)

                        Comment


                        • #13
                          I for one good Sir am pleased with your texts/tutorials.
                          I do like them and they are written in much detail. I hope I'll get a chance and talk to you on IRc sometimes just for teh lulz.
                          I always enjoy good tutorial, expecially this kind. This was not omfg wtf I can haxors somewherez tutorial.

                          Comment


                          • #14
                            That's good to hear Sidithuss :) I'm glad you like it and I hope others do as well! They take some time to write, but I enjoy doing so, it's interesting to do and refreshes my knowledge of the subjects at hand.
                            Well, the buffer overflow tut was obviously hacking related, as it described how to hijack processes through memory corruption basically, here's the exploit being written in that tutorial in an attack on one of my VM's: Link
                            So yeah, that was pretty.. different.
                            However, the admins apparently do not realise exploiting a class of vulnerabilities that's hardly ever found in the wild in the way I described it and writing an exploit for a piece of software that's over ten years old (!) does not create evil blackhat hackers but does help programmers be more aware of the various bugs and vulnerabilities that plague every day software: if you do not know about the offensive side, how will you ever defend yourself?

                            Either way, it's okay. My rants won't change anything. I should get on the TI IRC channel sometime btw :p
                            Last edited by Biomechanica; October 18, 2010, 05:58 AM. Reason: Typo

                            Comment


                            • #15
                              Excellent mate, like it and it functions obviusly.
                              That is only ethical, nothing blackhat in it.

                              I'm looking forward on chit chatting on IRc :)

                              Comment

                              Working...
                              X