                      Creating Keygens for Cocoa Applications            
                                    by whimsy

0: Preface

This entire document is, of course, only meant for educational purposes.  It
should not be used for *actually* cracking applications, but only as a guide
for developers wishing to write secure applications blah blah blah.

I: Introduction

This document is an attempt at giving the reader an overview of basic keygen
techniques as they apply to Cocoa applications on the Macintosh. To accomplish
this, we will go through the steps necessary to create a keygen for the
"Pixadex" application, version 1.5.5, which is like iPhoto for icons.

II: Tools

The tools I will be using are all command-line based. If you don't know how to
use the Terminal, then I suggest you stop reading right now and play with it.
I use the BASH shell, which is the default for Terminal if you are using OS X
10.3 or later.

GDB:
The Gnu Debugger comes standard with XCode Tools, which is available free from
Apple's developer website. GDB is the mainstay of any cracker, and you will be
spending most of your time using it. It is a program that allows you to step
through the assembly language instructions of any program, one by one. In order
to defeat the protection of an application, you need to know how the
application works first, and GDB is your private porthole into the deepest
bowels of the target application's inner workings. If you are used to MacsBug
(and even more so if you are not), then getting to know GDB can be a pain in
the ass, but it's worth it. I suggest reading a sizeable portion of its
reference manual and familiarizing yourself with it's "online" help, accessed
from within GDB by typing "help <command>". The best way to learn gdb, of
course, is to use it.

otool:
The otool program is a disassembler that dumps the raw assembly and function
headers of an application to your terminal. This program will be very important
for determining an entry point into the application's registration scheme, as
it allows you to look for suspicious functions like "isRegistered" (if the
developer was stupid enough to call it that) or "_Z34validateSerial" or
whatever. 

gcc:
Also part of the XCode Tools package, gcc is the standard GNU C compiler. We
will use this to compile our keygen once we've written it.

III: Let's get started!

The first thing to do with any application that you want to crack is to open it
up and look around at how it works. This may seem obvious, but a lot of people
(myself included) try to blindly apply their know-how without even taking note
of the basic functionality of the registration scheme.

So open Pixadex. Choosing the "Register..." option from the Pixadex menu, we
are confronted with a standard registration dialog with two text fields: name
and serial number. There are also several buttons, one of which is relevant
(the "Register" button...). Now try typing in some bogus values. After typing
in my info and clicking the register button, I see a new alert dialog informing
me that I've entered incorrect info. Nothing out of the ordinary.

In the instructions that follow, I'll assume the Pixadex application bundle is
stored on your desktop.

OK, so now we need to find out how to get our foot in the door! To do this,
we'll use otool and the nice command line utilities that came with our Mac.
Open up a terminal and create a new directory somewhere convenient to hold all
of our cracking info, and then go into that directory:

$ cd
$ mkdir pixadex_crack
$ cd pixadex_crack

Now we'll let otool dump all the assembly inside the pixadex binary into a
file:

$ otool -tVv ~/Desktop/Pixadex.app/Contents/MacOS/Pixadex > pixadex.otool

We just otool-ed Pixadex and directed the output of the command (the assembly
dump) to the file "pixadex.otool". Opening this file, you should see output
like this:

Pixadex.app/Contents/MacOS/Pixadex:
(__TEXT,__text) section
start:
000077f0        or      r26,r1,r1
000077f4        addi    r1,r1,0xfffc
000077f8        rlwinm  r1,r1,0,0,26
000077fc        li      r0,0x0
00007800        stw     r0,0x0(r1)
00007804        stwu    r1,0xffc0(r1)
00007808        lwz     r3,0x0(r26)
0000780c        addi    r4,r26,0x4
00007810        addi    r27,r3,0x1
00007814        rlwinm  r27,r27,2,0,29
00007818        add     r5,r4,r27
0000781c        lis     r11,0x0
00007820        ori     r11,r11,__start
00007824        mtspr   ctr,r11
00007828        bctrl
0000782c        trap
__start:
00007830        mfspr   r0,lr
00007834        stmw    r28,0xfff0(r1)
etc...

If we scroll further down, we see things like:

...etc
0000856c        lmw     r28,0xfff0(r1)
00008570        mtspr   lr,r0
00008574        blr
-[ImageThumbnailView acceptsFirstResponder]:
00008578        li      r3,0x1
0000857c        blr     
-[ImageThumbnailView addIndexToSelection:]:
00008580        mfspr   r0,lr
00008584        stmw    r29,0xfff4(r1)
00008588        lis     r4,0x5
0000858c        stw     r0,0x8(r1)
00008590        or      r30,r5,r5
00008594        or      r31,r3,r3 
00008598        stwu    r1,0xffb0(r1)
etc...

The things with the -[ ... ] are objective C message headers. For an
application like Pixadex that is developed in objective C, we are probably
correct in assuming that the registration scheme is accessible through
objective C routines like these. The goal is to find the name of a likely
registration header name like "-[RegisterWindow checkValidSerial:withName:]"
(for example -- that's made up). How do we do this? Well, one thing that would
help is if all that assembly code weren't in the way. It's no fun poring
through 63355 lines of assembly just to get at a function name.  That's where
the nice command line tools like "cat" and "awk" come in handy.

In our directory ~/pixadex_crack, enter the following command:

$ cat pixadex.otool | awk '/^[+-]/ {print}' - > pixadex_functions.otool

What this does is to take the contents of "pixadex.otool", assembly
instructions and all, and "pipe" them (the | is called a pipe) into the command
line utility "awk". This causes awk to process each line of the "pixadex.otool"
file. We've given awk some options so that it will filter out all of the
objective C headers.  The /^[+-]/ string tells awk to apply commands we specify
to all lines that begin with either a "+" or a "-". Everything inside the {}'s
is then executed on the lines that are filtered (in this case a simple "print"
command). The "-" after the awk command tells awk that it's going to get it's
input from a pipe (standard input), and the > pixadex_functions.otool part
tells it to direct all of its output to the file "pixadex_functions.otool".

After executing that command, take a look at the contents of
"pixadex_functions.otool":

-[ImageThumbnailView awakeFromNib]:
-[ImageThumbnailView initWithFrame:]:
-[ImageThumbnailView dealloc]:
-[ImageThumbnailView acceptsFirstResponder]:
-[ImageThumbnailView addIndexToSelection:]:
-[ImageThumbnailView adjustedFrameInFrame:]:
-[ImageThumbnailView becomeFirstResponder]:
-[ImageThumbnailView concludeInternalDragOperation]:
-[ImageThumbnailView copy:]:
-[ImageThumbnailView copyIconAndMask:]:
-[ImageThumbnailView delegate]:
-[ImageThumbnailView delete:]:
-[ImageThumbnailView deselectAll:]:
-[ImageThumbnailView draggedImage:endedAt:operation:]:
-[ImageThumbnailView draggedImage:movedTo:]:
-[ImageThumbnailView draggingEntered:]:
-[ImageThumbnailView draggingExited:]:
-[ImageThumbnailView draggingSourceOperationMaskForLocal:]:
-[ImageThumbnailView dragImage:at:offset:event:pasteboard:source:slideBack:]:
-[ImageThumbnailView drawRect:]:
-[ImageThumbnailView exportIcns:]:
etc...

So we see that the function headers were all properly parsed out of the
original otool output. Notice that now instead of 63355 lines in our file we
have 952 lines. Much better. Scrolling down a ways further, we come across some
headers that look like this:

...etc
-[RegistrationController init]:
+[RegistrationController sharedInstance]:
-[RegistrationController cancelRegistration:]:
-[RegistrationController findLostNumber:]:
-[RegistrationController isRegistered]:
-[RegistrationController isValidSerialNumber:forName:]:
-[RegistrationController stupidCrypt:]:
-[RegistrationController validateRegistration:]:
etc...

Well lookie here, It seems we've hit the jackpot. Finding this kind of thing
actually isn't that uncommon. Now, it wasn't very smart of the developer to put
that "isRegistered" function in there. Anyone can break that. What we are
interested in is finding out how a valid serial number is generated from a user
name of our choice. It looks like the function "isValidSerialNumber:forName:"
is our best bet, so it's time to fire up gdb and try to step into that
function.

First double-click the pixadex application and get to the registration dialog
with the "User Name" and "Serial Number" text fields.

Now, from your terminal, type:

$ gdb

You'll see the following scroll down your screen:

GNU gdb 6.1-20040303 (Apple version gdb-413) (Wed May 18 10:17:02 GMT 2005)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "powerpc-apple-darwin".
(gdb) 

Now type the following:

(gdb) attach Pix<TAB>

When you hit the <TAB> button, it should auto-complete the name to something
like this (the application name followed by it's process id, or PID):

(gdb) attach Pixadex.10292

Hit enter, and after a few seconds pixadex will be frozen in its tracks and
you'll be roaming around in its internal organs:

(gdb) attach Pixadex.10292 
Attaching to process 10292.
Reading symbols for shared libraries . done
Reading symbols for shared libraries .......................................................................................... done
0x9000a778 in mach_msg_trap ()
(gdb) 

Now, we're not even close to being inside any sort of registration subroutine.
What we're in right now is the main event loop of the application. That's
boring. We need to set a break for the function "isValidSerialNumber:forName:".
To do this, type:

(gdb) b isValidSerialNumber:forName:

Once again, you can probably hit <TAB> halfway through that and have gdb
auto-complete it for you. We then see as output:

(gdb) b isValidSerialNumber:forName: 
Breakpoint 1 at 0x39ec8
(gdb) 

That tells us that gdb found the address of the function we requested in memory
(it's at offset 0x39ec8), and that it put a break there so that the next time
the program tries to execute that function, we will drop into gdb and be able
to poke around.

Now that the breakpoint is set, we'll let the program resume execution and then
try to get it snagged at our breakpoint. Type:

(gdb) c

You should be able to fool around with the program registration dialog again.
Type in your username and some bogus password. I like to use the password
"123456789" because it catches my eye when I'm looking for it in memory
registers in gdb. Then click the "Register" button. The program should freeze
and you should be able to enter input into gdb again:

Breakpoint 1, 0x00039ec8 in -[RegistrationController isValidSerialNumber:forName:] ()
(gdb) 

Great, we're in our function. Now we want to find out how much of a pain in
the butt this is going to be. To get a clue about this, let's disassemble
our function and see how long and complicated it is:

(gdb) disas

When I do this, the last few lines that show up are:

0x00039fdc <-[RegistrationController isValidSerialNumber:forName:]+308>:       addi     r1,r1,80
0x00039fe0 <-[RegistrationController isValidSerialNumber:forName:]+312>:       lmw      r29,-12(r1)
0x00039fe4 <-[RegistrationController isValidSerialNumber:forName:]+316>:       mtlr     r0
0x00039fe8 <-[RegistrationController isValidSerialNumber:forName:]+320>:       blr
End of assembler dump.
(gdb) 

The "+320" you see at the end means that the function has 324 bytes worth of
instructions. Since each instruction is four bytes long for PowerPCs, that
means that there are 81 lines to reckon with. Easy. (At least, that appears to
be the case for now...) Looking at the contents of the entire function, the
following line should grab your eye:

0x00039fc0 <-[RegistrationController isValidSerialNumber:forName:]+280>:       bl       0x39bd8 <keyDecode>

This is a branch to another function called "keyDecode", and I'm guessing that
it takes the user name and serial number that you entered as arguments. So it
turns out that the reason why this function was so short is that it's just a
wrapper to feed your information to the "keyDecode" algorithm.

Now you can step through the execution of this function if you want. Before you
do so, it's very helpful to type:

(gdb) disp/i $pc

This tells gdb to show you where you are in the program after each instruction
that gets executed. Now type "si" to step through the function. I'll show you
some of my input and output for a few lines to give you an idea of some common
commands you'll be using a lot (note that when you don't see me type anything,
it's because I hit <ENTER> to repeat the previous command):

(gdb) si
0x00039ecc in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39ecc <-[RegistrationController isValidSerialNumber:forName:]+36>:        beq-    cr7,0x39fa4 <-[RegistrationController isValidSerialNumber:forName:]+252>
(gdb) i r $r5
r5             0x75bcd15        123456789
(gdb) po $r6
whimsy
(gdb) si
0x00039ed0 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39ed0 <-[RegistrationController isValidSerialNumber:forName:]+40>:        lis     r0,10
(gdb) 
0x00039ed4 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39ed4 <-[RegistrationController isValidSerialNumber:forName:]+44>:        ori     r0,r0,7072
(gdb)   

... there's a lot of boring stuff I'm stepping through here ...

0x00039f20 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39f20 <-[RegistrationController isValidSerialNumber:forName:]+120>:       addi    r5,r5,19784
(gdb) 
0x00039f24 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39f24 <-[RegistrationController isValidSerialNumber:forName:]+124>:       lwz     r4,0(r31)
(gdb) 
0x00039f28 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39f28 <-[RegistrationController isValidSerialNumber:forName:]+128>:       bl      0x51214 <dyld_stub_objc_msgSend>
(gdb) x/s $r4
0x90a0b850 <_errNewVars+319356>:         "isEqualToString:"
(gdb) po $r3
whimsy
(gdb) po $r5
cracked_by_Jakko
(gdb) 

Note that when I got to the instruction on line +128, "bl 0x51214
<dyld_stub_objc_msgSend>" I typed "x/s $r4" and got the output
"isEqualToString:". The "bl" stands for "branch and set link register" and the
<dyld_stub_objc_msgSend> means that it's going to send the message stored in
$r4 to the object stored at $r3, with the arguments stored in the registers $r5
through $r10, if there are that many arguments. I know that "isEqualToString:"
takes one argument because it has one ":" and that it sends a message to
another string. To look at a string object (or any object) in objective C, we
type "po" for print object, followed by the register that the object is stored
in. So we see that we're comparing my user name "whimsy" to the user name
"cracked_by_Jakko". It appears that the developer patrols cracking websites for
new cracks of their program and disables those cracks in new versions. Smart :P

You can continue stepping through if you want. I happen to know that the only
important part of this function is where it branches to "keyDecode". So I'm
going to set a breakpoint for right above where it branches there (you can type
"disas" again to find where that is...):

(gdb) b *0x39fb8 
Breakpoint 2 at 0x39fb8
(gdb) c
Continuing.

Breakpoint 2, 0x00039fb8 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39fb8 <-[RegistrationController isValidSerialNumber:forName:]+272>:       bl      0x51214 <dyld_stub_objc_msgSend>
(gdb) 

I set the breakpoint a little earlier because I want to show you how to avoid
stepping through an entire function that I'm about to branch to. To do this,
you can use the "n" command (for "next") but sometimes that doesn't work (it
didn't work in this case). The alternative is to do the following:

(gdb) tb *$pc+4
Breakpoint 3 at 0x39fbc
(gdb) c
Continuing.

Breakpoint 3, 0x00039fbc in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39fbc <-[RegistrationController isValidSerialNumber:forName:]+276>:       mr      r4,r29
(gdb) 

I set a temporary breakpoint for the memory offset four bytes ahead of where I
ccurrently am ($pc keeps track of where you are), and then let the program run
through the function until it got to that breakpoint. After it reaches that
breakpoint, the breakpoint disappears (that's why it's called temporary). OK,
so now I step using "si" down to the line where it branches to "keyDecode" and
check the arguments to "keyDecode". Any function call takes its arguments in
registers $r3 through $r10. You'll notice that the previous few lines only do
anything important to registers $r3 and $r4, so we take a look at those:

Breakpoint 3, 0x00039fbc in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39fbc <-[RegistrationController isValidSerialNumber:forName:]+276>:       mr      r4,r29
(gdb) si
0x00039fc0 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39fc0 <-[RegistrationController isValidSerialNumber:forName:]+280>:       bl      0x39bd8 <keyDecode>
(gdb) x/s $r3
0x3be740:        "whimsy"
(gdb) x/s $r4
0x75bcd15:       <Address 0x75bcd15 out of bounds>
(gdb) po $r4
Cannot access memory at address 0x75bcd15
(gdb) i r $r4
r4             0x75bcd15        123456789
(gdb) 

So my user name is stored in $r3 as a c-style string, and my serial number is
the value stored in $r4. Notice that I tried x/s $r4 and po $r4 and that didn't
work. The command "i r $r4" is short for "info register $r4" and just displays
the contents of $r4 directly. Both x/s and po examine the contents of the
memory *pointed to* by the value stored in the register.

So now that we know what arguments "keyDecode" takes, lets step into it and
find out how it works:

(gdb) si

After typing "disas", we see that the function is only 104 bytes long, or 26
lines long. Here it is:

(gdb) disas
Dump of assembler code for function keyDecode:
0x00039bd8 <keyDecode+0>:       mflr    r0
0x00039bdc <keyDecode+4>:       stmw    r30,-8(r1)
0x00039be0 <keyDecode+8>:       mr      r30,r3
0x00039be4 <keyDecode+12>:      stw     r0,8(r1)
0x00039be8 <keyDecode+16>:      mr      r31,r4
0x00039bec <keyDecode+20>:      stwu    r1,-80(r1)
0x00039bf0 <keyDecode+24>:      bl      0x51854 <dyld_stub_strlen>
0x00039bf4 <keyDecode+28>:      li      r11,0
0x00039bf8 <keyDecode+32>:      cmpw    cr7,r11,r3
0x00039bfc <keyDecode+36>:      bge-    cr7,0x39c28 <keyDecode+80>
0x00039c00 <keyDecode+40>:      mtctr   r3
0x00039c04 <keyDecode+44>:      li      r9,9
0x00039c08 <keyDecode+48>:      lbzx    r2,r30,r11
0x00039c0c <keyDecode+52>:      addi    r11,r11,1
0x00039c10 <keyDecode+56>:      extsb   r2,r2
0x00039c14 <keyDecode+60>:      mullw   r2,r2,r9
0x00039c18 <keyDecode+64>:      addi    r9,r9,9
0x00039c1c <keyDecode+68>:      subf    r2,r2,r31
0x00039c20 <keyDecode+72>:      addi    r31,r2,-77
0x00039c24 <keyDecode+76>:      bdnz+   0x39c08 <keyDecode+48>
0x00039c28 <keyDecode+80>:      lwz     r0,88(r1)
0x00039c2c <keyDecode+84>:      addi    r1,r1,80
0x00039c30 <keyDecode+88>:      mr      r3,r31
0x00039c34 <keyDecode+92>:      lmw     r30,-8(r1)
0x00039c38 <keyDecode+96>:      mtlr    r0
0x00039c3c <keyDecode+100>:     blr
End of assembler dump.
(gdb) 

This is pretty short and sweet. Let's see what it does. Just looking at it, we
can see that it puts the contents of $r3, our user name, into the register $r30
using the "mr", or "move register" instruction. Likewise, it puts our serial
number into $r31. After that, it calls a "strlen" function to compute the
length of the string stored in $r3 (it only takes one argument -- to see how
strlen works, type "man 3 strlen" at a standard terminal prompt). After strlen
returns, the length of the the string formerly stored in $r3 (our username)
will be stored in $r3. You can do an "i r $r3" on the line after the strlen
call to check this. I get 6, which is the length of "whimsy". Check.

After the strlen call, we see that it puts the number zero in to $r11 using the
"li" or "load immediate" instruction. Then it compares $r11 (0) to $r3 (the
length of your user name) and branches to line 80 if $r11 is greater than or
equal to $r3. That means that if your user name has a length of less than or
equal to zero, we branch to line 80. We see that after line 80, it assigns the
value in $r31 to $r3. Since we remember that we just put your serial number
into $r31, if there is no user name, we're just returning the original serial
number as output.

Luckily, our user name isn't of zero length, so it doesn't branch. Instead, the
counter register gets set to the value stored in $r3 (our user name length)
using the call "mtctr r3". This is where we can start thinking about our
keygen.

If all of those assembly instructions look like gibberish to you, then do a
google search for powerpc assembly instructions and look them up. I'm going to
try to explain some of them here, but I'm not going to use a huge amount of
detail:

mtctr   r3          ; load the value of r3 into the counter register
li      r9,9        ; put the value 9 into r9
lbzx    r2,r30,r11  ; put the r11'th byte of the string at r30 into r2
                    ; remember, r30 points to our user name
addi    r11,r11,1   ; increment r11 by 1
extsb   r2,r2       ; doesn't matter (extends the sign bit of r2)
mullw   r2,r2,r9    ; multiply r2 by r9 and store the result back in r2
addi    r9,r9,9     ; increment r9 by 9
subf    r2,r2,r31   ; subtract r2 from r31 and put the result in r2
addi    r31,r2,-77  ; add -77 to r2 and put the result into r31
bdnz+   0x39c08 <keyDecode+48>  ; decrement the counter and branch to line 48 if it's not equal to zero (48 is the lbzx instruction).

Now, if you go back and look at what was in $r31 and $r30 originally, you will
see that what this block of code is actually doing is taking each byte
(character) of your user name, and multiplying the integer value of that byte
by successively larger multiples of 9 (starting from 9), then subtracting each
of those values from your serial number. Each time it does this, it subtracts
77 from you serial number. We can express the final result as a mathematical
formula:

result = sn - (sum over i from 1 to namelength) { 9 * i * name_i } -77 * namelength

Now, when you look at what happens after we're done looping through this block,
you see that it then puts the result into $r3, which is what the function
returns. So now we have to finish execution of this function and go back to
where we came from to see what the "isValidSerialNumber:forName:" function does
with the result. To do this, I set a break for 0x39c3c, the last line of the
"keyDecode" function, and let the program run up to that point:

(gdb) tb *0x39c3c
Breakpoint 5 at 0x39c3c
(gdb) c
Continuing.

Breakpoint 5, 0x00039c3c in keyDecode ()
1: x/i $pc  0x39c3c <keyDecode+100>:    blr
(gdb) si
0x00039fc4 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39fc4 <-[RegistrationController isValidSerialNumber:forName:]+284>:       lis     r0,9
(gdb) 

So I reach the breakpoint, and "si" past the "blr" statement, which tells the
program to branch back to where the link register is set to. Since we got to
"keyDecode" through a "bl" statement, the link register will be pointing to the
instruction directly below that "bl" statement:

(gdb) si
0x00039fc4 in -[RegistrationController isValidSerialNumber:forName:] ()
1: x/i $pc  0x39fc4 <-[RegistrationController isValidSerialNumber:forName:]+284>:       lis     r0,9
(gdb) 

So what does "isValid..." do with the result of "keyDecode"? We could step
through and see, but all you'll probably get out of it is that it puts the
value 0 into $r3. That means it's going to return FALSE, since we entered a bad
user name/serial number combo at the outset.

Instead of stepping through, we are once again going to put on our thinking
caps and google goggles and read some more assembly. Here are the assembly
instructions that follow the return from "keyDecode":

lis     r0,9        ; put 0x00090000 into r0
ori     r0,r0,31388 ; or the bits of r0 with those of the number 31388 and put the result into r0
xor     r3,r3,r0    ; xor r3 with r0 and put the result into r3
subfic  r0,r3,0     ; subtract r3 from 0 and store the result into r0, setting the carry bit if necessary
adde    r3,r0,r3    ; add the value of r0 and r3 and the carry bit, and put the result into r3

This might look a little cryptic. You should look up the instructions you don't
understand at your favorite PowerPC instruction set reference.

The first line puts the value 9, but shifted by 2 bytes, into r0. Since PowerPC
instructions can only use 2-byte immediates, we need two instructions to put
the value "0x00090000 + 31388" into r0, which is what this does. The ori
instruction or's the bits of 31388 (which is 0x7a9c in hex) with the value
0x00090000. The final result is 0x00097a9c. The next line "xor"s this value
with the result returned by our friend "keyDecode" and puts the result into r3.
We'll come back to this. The line after that subtracts this value from 0 and
puts the result in r0, and then sets the carry bit if it needs to. The only way
that the carry bit will get set in this case (as far as I know) is if the value
in r3 at this point is 0 (that is, the 0-0 operation sets the carry bit). We
store the result of this subtraction (0 - r3) into r0. Then we add r0 to r3,
*plus the carry bit* and put that into r3. Since r0 = -r3, we know that r3
*will only contain a "1" if the carry bit was set earlier*. Since this value of
r3 is what our "isValid..." function will return, we see that it will only
return a "true" if the carry bit gets set in the "subfic" instruction. In turn,
the only way this will happen is if the result of the "xor" operation results
in "0". The xor operation will give us a zero if both of its operands have all
bits equal. Thus, we see that our serial will be marked as valid if the result
of the "keyDecode" function give us the value 0x97a9c. OK, phew! That was a lot
of paying attention.

To boil it all down, we need the result of the "keyDecode" function to return
the hex value 0x97a9c. We'll now need to do some algebra to figure out what
serial number we need to enter to get the "keyDecode" function to do this.

This algebra is pretty simple. Looking at our previous formula,

result = sn - (sum over i from 1 to namelength) { 9 * i * name_i } -77 * namelength

we just need to move all of the trash after "sn" over to the left hand side and
substiute 0x97a9c (or 621212 in decimal) into "result". Thus the formula for
our serial number is:

sn = 621212 + (sum over i from 1 to namelength) {9*i*name_i} + 77*namelength

We can write this out in C in the following way:

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

int main( int argc, char *argv[] )
{
        char *name = argv[1];
        int namelen = strlen( name );
        int i;
        int tmp = 0;
        int sn;
        for( i=0; i<namelen; i++ ) {
                tmp += name[i]*(i+1);
        }
        tmp *= 9;

        sn = 621212 + 77*namelen + tmp;

        printf( "%d\n", sn );

        return 0;
}

If you don't understand the C, then see

    http://www.justfuckinggoogleit.com

for assistance.

--whimsy--
