My First Crackme!

I finally got around to trying to solve a crackme for OS X this morning and it was a blast!  For those who are interested the crackme can be found on this site (MSJ2009#1.zip).  The goal of the crackme (not surprisingly) is to successfully register the application using either a hex edit, serial sniff, or keygen.

Step 1: Recon!

Before I even ran the application I went and dug around int he application package contents.  I didn’t find anything all that interesting there so it was time to dig a little deeper.  To do this I decided to see what symbols were in the binary using the nm tool.

dean@Atlantis:~/Dropbox/Crackmes/MSJ 2009/Challenge #1.app/Contents/MacOS $ nm Challenge\ #1

Challenge #1 (for architecture i386):
0000251e t -[Level1 applicationDidFinishLaunching:]
00002a94 t -[Level1 applicationShouldTerminateAfterLastWindowClosed:]
00002433 t -[Level1 awakeFromNib]
000026d6 t -[Level1 cancelButton:]
000025ac t -[Level1 continueWelcomeButton:]
000027e3 t -[Level1 emailResults:]
00002a88 t -[Level1 isRegistered]
0000264e t -[Level1 okErrorSheetButton:]
00002692 t -[Level1 okIncorrectSerialButton:]
000025f0 t -[Level1 quitCorrectSerialButton:]
000026f7 t -[Level1 unregisterButton:]
00002a9e t -[Level1 validateSerial:forName:]
0000288e t -[Level1 verifyRegistration:]
...

Great! There is a function just for validation, now all we need to do is to get that to say we’ve passed in valid credentials.

Step 2: Validation

The first step I took in manipulating the validation function is to see what types the function expected.  You can get this information using otool as follows.

dean@Atlantis:~/Dropbox/Crackmes/MSJ 2009/Challenge #1.app/Contents/MacOS $ otool -Vo Challenge\ #1...	method_name 0x00002d7d validateSerial:forName:	method_types 0x00002cb6 c16@0:4@8@12	method_imp 0x00002a9e -[Level1 validateSerial:forName:]...

From this we know that the function expects two objects as arguments and it returns a char indicating whether or not the credentials have been successfully verified.  The next step is to see what the heck this function is doing.  Once again we use otool to disassemble the binary.  In order to save space I’ve only included the parts relevant to this crack.

dean@Atlantis:~/Dropbox/Crackmes/MSJ 2009/Challenge #1.app/Contents/MacOS $ otool -Vt Challenge\ #1...-[Level1 validateSerial:forName:]:
00002a9e        pushl   %ebp
00002a9f        movl    %esp,%ebp
00002aa1        pushl   %edi
00002aa2        pushl   %esi
00002aa3        pushl   %ebx
00002aa4        subl    $0x3c,%esp
00002aa7        movl    0x00004040,%eax
00002aac        movl    %eax,0x04(%esp)
00002ab0        movl    0x10(%ebp),%eax
00002ab3        movl    %eax,(%esp)
00002ab6        calll   0x0000505e      ; symbol stub for: _objc_msgSend
00002abb        cmpl    $0x08,%eax
00002abe        jne     0x00002c4e...00002c4e        xorl    %edx,%edx
00002c50        addl    $0x3c,%esp
00002c53        movl    %edx,%eax
00002c55        popl    %ebx
00002c56        popl    %esi
00002c57        popl    %edi
00002c58        leave
00002c59        ret

We see that this function begins as expected with setting up the stack, saving registers it will trash, and allocating space for local variables.  The next point of interest is the call to objc_msgSend.  This functions takes a variable number of arguments the first of which is the object to send a message too and the second of which is the message to be sent.  All remaining arguments to objc_msgSend are passed arguments for the message to the receiver.  If you were to set a breakpoint at the beginning of the validate function and run the program in gdb you can find out (by inspecting the memory) that the message being sent is the length message.  We can also see that the receiving object is the first parameter (serial number) to the validation function.  The next thing to notice is that if the length of the serial number is not eight characters then the function would return.  We can leverage the reliance on the serial number string length and the immediate return to cause the function to always return true regardless which serial number is entered.

Step 3: Hex Edit

For the sake of simplicity I decided to solve this crackme using a simple hex edit.  In order to do this successfully all we need to know is (1) where to make the change and (2) what to change the code too.  The standard calling convention on x86 (32-bit) mandates that return values from a function be placed in %eax, therefore our goal is to modify the value of %eax just before the validate function exits.  To do this I chose to change the xorl at address 0x2c4e to a movl instruction that placed a 0×1 in %edx.  You can make this change by opening the binary in your favourite hex editor and then changing the bytes at 0x2c4e to ba01000000.  Be sure to shift the remaining bytes in the function over to make space for the additional bytes.  Assuming the changes were made properly the next time you run the program it should register your serial number without a problem.

 

Comments are closed.