In a previous tutorial we had a look at the application Pixelmator and made a simple krack for it so it
accepted anything we typed in as a license code.
Wouldnʼt it be great..if we could instead get it to accept our own valid license code?
Yes my friends, in this tutorial we will be attempting a serial sniff! :D Boom!
-(BOOL)[PXApplicationDelegate isLicenseCodeValid:]
+0 0006f8ec 7c0802a6 mfspr r0,lr
+4 0006f8f0 bda1ffb4 stmw r13,0xffb4(r1)
+8 0006f8f4 7cbc2b79 or. r28,r5,r5
+12 0006f8f8 90010008 stw r0,0x8(r1)
+16 0006f8fc 9421ff20 stwu r1,0xff20(r1)
+20 0006f900 41820664 beq 0x6ff64 return;
Okay, so what we see here is a call to -[r3 length] at line and then what it returns is compared to 0 (cmpwi
cr7, r3, 0x0). If the length is equal to 0 then the app branches to the end of this function (beq - branch if
equal). We can presume that the app simply checks if the user has entered anything in the license code
textfield here.
Going a bit further down in the code, past all the “replaceOccurrencesOfString:withString:options:range:”
stuff, which doesnʼt really do anything for us, we get a call to -[r3 length] again. This time it is compared to
0x27. Since everything in our output is written in hexadecimal values 0x27 doesnʼt mean itʼs compared to 27,
but 39. So now we know that the serial has to be 39 characters long!
+480 0006facc 3c80005e lis r4,0x5e
+484 0006fad0 3ca00055 lis r5,0x55
+488 0006fad4 7fe3fb78 or r3,r31,r31
+492 0006fad8 8084670c lwz r4,0x670c(r4) hasPrefix:
+496 0006fadc 38a5d484 addi r5,r5,0xd484 PIXEL-100
+500 0006fae0 4bfeff03 bla 0xfffeff00 -[r3 hasPrefix:]
+504 0006fae4 2f830000 cmpwi cr7,r3,0x0
+508 0006fae8 419e047c beq cr7,0x6ff64 return;
Now it checks if the prefix of our license code is “PIXEL-100”. This letʼs us know that the code should start
with PIXEL-100, followed by 30 more characters.
What this next part does is that it checks how many parts of the license code we enter is separated by
“-” (dashes) and compares this to 7. Since we already now the serial has to start with PIXEL-100, we already
got two of those parts. So with five more to go the code should look something like this PIXEL-100-XXXXX-
XXXXX-XXXXX-XXXXX-XXXXX. Itʼs not sure that code will have this exact format but this is just something
we can do if I know the length and number of dashes.
In the next part of our output is a lot of calls to -[r3 characterAtIndex:]. What this does is that it takes the
character at the given index and returns it. Worth noting is that this index starts at 0, so if the index is 4, this
will be the 5th character in the string.
The number of which index it takes the character from is stored in the register r5. You can see that 0xa (10)
is loaded into r5 (li r5,0xa) before the first call to -[r3 characterAtIndex:], so this first character it takes from
our serial is the 11th, PIXEL-100-[X]XXXX-XXXXX-XXXXX-XXXXX-XXXXX.
You might have noticed the one line that contains a lot of letters and numbers. Weʼll ignore that as it doesnʼt
really help us right now.
Now moving further down the output we get a lot of calls to -[r3 characterAtIndex:], but nothing really
happens after those calls, no values get compared. What we can figure out however is the exact format that
our license code will use since it will skip all the places where the dashes are supposed to be.
+1068 0006fd18 38a00026 li r5,0x26
+1072 0006fd1c 7c741b78 or r20,r3,r3
+1076 0006fd20 7fe3fb78 or r3,r31,r31
+1080 0006fd24 4bfeff03 bla 0xfffeff00 -[r3 characterAtIndex:]
+1084 0006fd28 7cbdc896 mulhw r5,r29,r25
+1088 0006fd2c 7fa0fe70 srawi r0,r29,31
+1092 0006fd30 809e0000 lwz r4,0x0(r30) characterAtIndex:
+1096 0006fd34 7c7f1b78 or r31,r3,r3
+1100 0006fd38 7f43d378 or r3,r26,r26
+1104 0006fd3c 7ca51e70 srawi r5,r5,3
+1108 0006fd40 7ca02850 subf r5,r0,r5
+1112 0006fd44 1ca50024 mulli r5,r5,0x24
+1116 0006fd48 7ca5e850 subf r5,r5,r29
+1120 0006fd4c 4bfeff03 bla 0xfffeff00 -[r3 characterAtIndex:]
+1124 0006fd50 7f9b1800 cmpw cr7,r27,r3
+1128 0006fd54 409e0210 bne cr7,0x6ff64 return;
This is where we start seeing something interesting. It does some math and at the end there r27 and r3 are
compared. They have to be equal or the app will branch to the end of the function (bne - branch if not equal).
We can assume that it checks one character of our entered license code here, compares it to an expected
value and doesnʼt branch if both values are the same.
It might be a good idea load Pixelmator in gdb now, if you havenʼt already done so. Enter PIXEL-100-
XXXXX-XXXXX-XXXXX-XXXXX-XXXXX in the textfield and before pressing the “License Now” button, set a
breakpoint at 0x06fd50 and we will have a look at which values are compared.
When the program stops at our breakpoint we do the following to see what r27 and r3 contains. What we do
here is use the p (print) command in gdb to see what the registers contain. The values that get returned are
ASCII character codes. 88 is the ASCII code for “X”, the character in our serial. 55 is the ASCII code for “7”,
the expected character it gets compared too.
You might be asking yourself which of the Xʼs we are comparing here. There are two ways to get past this
problem. The first and the way I prefer to do it is to simply change the Xʼs in the serial we enter to something
that is easier to find, for example PIXEL-100-ABCDE-FGHIJ-KLMNO-PQRST-UVWXY. This way we know
that if the X gets compared here we only have one of those in our serial and that means the 7 has to go
where the X is.
The other method (if we decide to keep all the Xʼs) would be to find out which of the Xʼs gets stored in r27.
To do that we have to go back in our output and find which one of the -[r3 characterAtIndex:] calls stores its
character in r27.
Going back a bit you will find this. First we know that it stores the index of the character it is going to get in
r5. So the index is 0x10 (16). It then calls -[r3 characterAtIndex:] and the character it gets is stored in r3. We
then see “or r27,r3,r3”. What basically does is moves the value in r3 to r27. So we now know that the 17th
character in the license code we entered is the X that got compared to 7.
Now getting back to that breakpoint...Now that we know what was expected at the character that was getting
compared we can either let gdb continue the execution of the app, get a beep because we entered an
incorrect license code, change the value to a correct one and click “License Now” to once again hit our
breakpoint, but this time have our entered character and the expected character to be the same.
Or..we can consume a little less time and let gdb help us:
This simple command in gdb will set the register r27 (our character) to have the same value as r3 (the
expected character). Doing this we wont have switch to the app and change the license code. Since weʼve
printed out the expected value earlier we can just write it down and then set r27 to be the same as r3 and
continue.
Next up we get this. It looks familiar doesnʼt it? ;-) Yup, thats right. We have to repeat the same procedure
again, only a different character gets compared this time. In fact, going further down our output we see the
same procedure is gone through 10 times in total. So 10 characters of our entered serial will get compared to
expected values.
Depending on which method you used you should have ended up with either one of the following two license
codes:
PIXEL-100-XXXXX-77777-XXXXX-77777-XXXXX
PIXEL-100-ABCDE-7RG6F-KLMNO-VRG6F-UVWXY
Homework
1. Both of the license codes we sniffed are kinda “boring”. Sniff one that has a more “personal” touch to it.
Hereʼs an example: PIXEL-100-MATOR-V1VE4-KRACK-EN6V2-ISFUN (Easy)
2. Using what youʼve learned in this tutorial + looking up how different branches (beq, bne, ble, bgt, etc etc)
that are used in PPC asm work (use the internets for this!), you should be able to sniff a serial for
MacFamilyTree (http://www.synium.de/products/macfamilytree/) (Advanced)
This tutorial was written by Pushit in March 2008. I hope you liked it. Share The Wealth.