-------------------------------------------------------------------------
-------------------------------------------------------------------------
ARM Assembler
How to do a keygen for DataSafeS60
Version 1.0
Tools:
------
IDA Pro v4.30
Hex Editor
ARM Software Developer Tools v2.51
Target:
-------
DataSafe S60
Find it for yourself...
Recommended:
------------
For some live debugging you will need ARMulator which is included in the ARM SDT. This is not essential to understand how the prog works but to get a clearer view of what's going on. Since live debugging is always better than looking at a piece of code!
I also suggest to run the app.apj I included in this package while reading the tutorial! So it is easy to understand what I mean. Just click on Debug and the ARM Debugger (ARMulator) pops up. View the registers and step through the code. You'll find that I edited the code a bit from the original in DataSafe. It uses ARM 32Bit instructions not THUMB and on some instructions I edited register usage. But it does the same as the normal app!!
Startup:
--------
Hello guys!
In this tutorial you will learn how to do a keygen for our beloved Symbian phones. Our target will be DataSafe S60. I chose this app because it doesn't use much Symbian functions, so we can guess what the OS functions do!
First run this app in your phone. You'll see that it asks to register it on startup. Second disassemble this app in IDA. Since we want to do a keygen I'll not cover how to crack it here!
Remember to use ALT+G to enter the CODE16 disassembler state because the app consists of THUMB code!
Running:
--------
The regcode we have to enter is IMEI dependend. If that is the case the program uses one of these 2 functions provided by SymbianOS which retrieve the phones IMEI:
GetGsmPhoneId__C14RBasicGsmPhoneRQ216MBasicGsmPhon eId3TId
GetMachineIdL__10PlpVariantRt4TBuf1i128
If you search for them you'll find that the second function is used. If you then follow the refs you'll come to this code:
.text:10000284 MOV R0, SP ; mov stack pointer in R0
.text:10000286 BL sub_10004B6C ; get IMEI
.text:1000028A ADD R0, R6, #0 ;
.text:1000028C BL sub_100008DC ;
.text:10000290 ADD R0, #0x2C ; R0 = R0+0x2C
.text:10000292 MOV R1, SP ; R1 = SP
.text:10000294 BL sub_10003DB0 ; copy IMEI which is in memory location SP to R0+0x2C
First the Stack Pointer is moved to R0. Than the IMEI is retrieved. We know now that the IMEI is on the Stack.
After the next func the IMEI descriptor is copied to a specific memory location. You should know that a descriptor in Symbian is build up in the following way (I'm not 100% sure about this):
length,0,0,0,3,0,5,....
So the first byte of our IMEI descriptor is the length which is 0xF. Also a descriptor consists of Unicode characters.
Back to our target: Now we have to find the place where we enter our code and where the app calculates and compares the code. Since erl.exe doesn't work with the rsc files (If you're good at c++ consider to write a better rsc decompiler) we can only guess the resources.
So I did a search for "ADD R0, #0x2C" because this is the place where the IMEI descriptor is copied. This instruction is used 4 times in the code. We can guess that the 2 last are our code of interest. Another way to find special code is the AtC__C7TDesC16i function. This returns the ASCII value from a descriptor. And this function is also used 2 times. Here we have our code calc routine:
.text:1000240C PUSH {R4-R7,LR} ; Push registers
.text:1000240E ADD R6, R0, #0 ; Rd = Op1 + Op2
.text:10002410 ADD R7, R1, #0 ; Rd = Op1 + Op2
.text:10002412 STR R7, [R6,#0x5C] ; Store to Memory
.text:10002414 MOV R5, #0 ; Rd = Op2
.text:10002416 MOV R4, #0 ; Rd = Op2
.text:10002418
.text:10002418 loc_10002418 ; CODE XREF: sub_1000240C+2A j
.text:10002418 ADD R0, R6, #0 ; Rd = Op1 + Op2
.text:1000241A ADD R0, #0x2C ; Rd = Op1 + Op2
.text:1000241C ADD R1, R4, #0 ; Rd = Op1 + Op2
.text:1000241E BL sub_10003EF0 ; Branch with Link
.text:10002422 LDRH R0, [R0] ; Load from Memory
.text:10002424 SUB R0, #7 ; Rd = Op1 - Op2
.text:10002426 ADD R2, R4, #1 ; Rd = Op1 + Op2
.text:10002428 ADD R1, R0, #0 ; Rd = Op1 + Op2
.text:1000242A MUL R1, R2 ; Multiply
.text:1000242C LDR R0, =0x16F ; Load from Memory
.text:1000242E MUL R0, R1 ; Multiply
.text:10002430 ADD R5, R5, R0 ; Rd = Op1 + Op2
.text:10002432 ADD R4, R2, #0 ; Rd = Op1 + Op2
.text:10002434 CMP R4, #4 ; Set cond. codes on Op1 - Op2
.text:10002436 BLE loc_10002418 ; Branch
.text:10002438 MOV R4, #0xA ; Rd = Op2
.text:1000243A
.text:1000243A loc_1000243A ; CODE XREF: sub_1000240C+4A j
.text:1000243A ADD R0, R6, #0 ; Rd = Op1 + Op2
.text:1000243C ADD R0, #0x2C ; Rd = Op1 + Op2
.text:1000243E ADD R1, R4, #0 ; Rd = Op1 + Op2
.text:10002440 BL sub_10003EF0 ; Branch with Link
.text:10002444 LDRH R0, [R0] ; Load from Memory
.text:10002446 SUB R0, #7 ; Rd = Op1 - Op2
.text:10002448 SUB R1, R4, #4 ; Rd = Op1 - Op2
.text:1000244A MUL R1, R0 ; Multiply
.text:1000244C LDR R0, =0x16F ; Load from Memory
.text:1000244E MUL R0, R1 ; Multiply
.text:10002450 ADD R5, R5, R0 ; Rd = Op1 + Op2
.text:10002452 ADD R4, #1 ; Rd = Op1 + Op2
.text:10002454 CMP R4, #0xE ; Set cond. codes on Op1 - Op2
.text:10002456 BLE loc_1000243A ; Branch
.text:10002458 LSL R0, R5, #0x10 ; Logical Shift Left
.text:1000245A LSR R0, R0, #0x10 ; Logical Shift Right
.text:1000245C CMP R7, R0 ; Set cond. codes on Op1 - Op2
.text:1000245E BNE loc_10002464 ; Branch
.text:10002460 MOV R0, #1 ; Rd = Op2
.text:10002462 STR R0, [R6,#0x60] ; Store to Memory
.text:10002464
.text:10002464 loc_10002464 ; CODE XREF: sub_1000240C+52 j
.text:10002464 POP {R4-R7} ; Pop registers
.text:10002466 POP {R0} ; Pop registers
.text:10002468 BX R0 ; Branch to/from Thumb mode
.text:10002468 ; End of function sub_1000240C
This routine is called from the following code:
.text:10000D02 LDR R1, =0x16E8701B ; Enter reg code
.text:10000D04 LDR R2, [R0,#0x74] ;
.text:10000D06 ADD R0, R4, #0 ;
.text:10000D08 BL sub_10003D20 ; Get the code
.text:10000D0C CMP R0, #0 ;
.text:10000D0E BEQ loc_10000D98 ; If Cancel was pressed b
.text:10000D10 BL sub_10003DD4 ;
.text:10000D14 ADD R0, R6, #0 ;
.text:10000D16 BL sub_100008DC ;
.text:10000D1A LDR R1, [SP] ; Entered code moved to R1
.text:10000D1C BL sub_1000240C ; Call to code calc routine
.text:10000D20 ADD R0, R6, #0 ;
.text:10000D22 BL sub_100008DC ;
.text:10000D26 BL sub_10002470 ; Load Memory location to R0 = return value of code calc func
.text:10000D2A ADD R4, R0, #0 ; Put R0 -> R4
.text:10000D2C CMP R4, #0 ; Compare R4 to 0
.text:10000D2E BNE loc_10000D60 ; B if R4 is not 0 = reg code is right
So this code should be clear! First the resource ID is put in R1. Then a function is called to get the user input. After that the prog analyses which key was pressed. If it was Cancel it branches. If not it moves the number entered by the user in R1 (.text:10000D1A). Now the calc func is called. By looking at that function we see that the return value of the code calc func is stored to R6,#0x60 (.text:10002462). At .text:10000D26 the app loads that specific memory location to R0. Then R0 is moved to R4. And this is compared to 0. So here we have the first clue: The return value of the code calc func should not be 0! Let's search for that return value:
.text:1000245C CMP R7, R0 ; compare R7 = R0
.text:1000245E BNE loc_10002464 ; b if not equal
.text:10002460 MOV R0, #1 ; R0 = 1
.text:10002462 STR R0, [R6,#0x60] ; store R0 to memory location
By looking at the calc func we can see that R7 has to be equal to R0 in order not to take the bne. Then the return value is 1 which is stored to memory! As already mentioned this is loaded after the return and compared to 0 (.text:10000D2C).
Now it is interesting what is in R7. At the beginning of the code
.text:10002410 ADD R7, R1, #0 ; Rd = Op1 + Op2
R1 is moved to R7. So what is in R1??? Before the calc func is called the content of SP is moved to R1:
.text:10000D1A LDR R1, [SP] ; Entered code moved to R1
Now we know that the entered code should be equal to the code that is calculated by the app. This calculated code has to be entered by the user. We just have to calc it for ourself...
Here is the first part of the calc func:
.text:10002414 MOV R5, #0 ; R5 = 0
.text:10002416 MOV R4, #0 ; R4 = 0
.text:10002418
.text:10002418 loc_10002418 ; CODE XREF: sub_1000240C+2A j
.text:10002418 ADD R0, R6, #0 ;
.text:1000241A ADD R0, #0x2C ; R0 = Pointer to IMEI descriptor
.text:1000241C ADD R1, R4, #0 ; R1 = R4 (which number from IMEI descriptor)
.text:1000241E BL sub_10003EF0 ; Get ASCII value
.text:10002422 LDRH R0, [R0] ; load value in R0
.text:10002424 SUB R0, #7 ; R0 = R0 - 7
.text:10002426 ADD R2, R4, #1 ; R2 = R4 + 1
.text:10002428 ADD R1, R0, #0 ; R1 = R0
.text:1000242A MUL R1, R2 ; R1 = R2 * R1
.text:1000242C LDR R0, =0x16F ; R0 = 0x16F
.text:1000242E MUL R0, R1 ; R0 = R1 * R0
.text:10002430 ADD R5, R5, R0 ; R5 = R5 + R0
.text:10002432 ADD R4, R2, #0 ; R4 = R2
.text:10002434 CMP R4, #4 ; is R4 = 4
.text:10002436 BLE loc_10002418 ; b if R4 is less or equal 4
So what is done here? First R5, R4 are set to 0. Then a pointer to the IMEI descriptor is moved in R0. R1 is the number in the IMEI. So at the first run of this loop R4=R1 is 0. So the app starts to get the ASCII value of the first number in the IMEI. If that is maybe 3 the value in R0 after the LDRH instruction would be 0x33 (which is the ASCII code for 3). From this value 7 is substracted. Which gives for our first run 0x2C in R0 after SUB. Then 1 is added to R4 and the result put in R2 (R2 is now 1). R0 is then moved (ADD) to R1. Now both are multiplied. Now it is easy to guess what the result would be: R2 = 1 * R1 = 0x2C. That would be 0x2C in R1. Then R1 will be multiplied with 0x16F in R0. Would make 0x3F14 which will be added to R5. Finally R2 is moved (ADD) to R4 (which is now 1) and compared to 4. Since R4 is now 1 the BLE is taken and we start again from .text:10002418. But now the second number of the IMEI is retrieved (R4 = 1). This is maybe 5. After the LDRH which value will be in R0? Of course, 0x35! Then we have the -7 and the multiply. But look out now: R2 contains now 2 since it adds 1 to R4. So (0x35-7) * 2 would be 0x5C. We see that a single number from the IMEI is multiplied with it's position in the IMEI. After the * with 0x16F it is added to R5 (.text:10002430). But in R5 is the value from the last loop. So you see all values are added up to 1 special value! After our second run this value should be 0xC2F8.
Here is the code in plain c which is maybe easier to understand:
code = ((ASCIIvalueofIMEI - 7) * PositionofNumberinIMEI) * 0x16F;
Then all codes are added...
Ok! Now we have covered the first loop. Plz msg me and tell me the code after this loop when using 12345 and 00000 as the IMEI's first values.
After R4 is 5 the BLE is not taken and we continue here:
.text:10002438 MOV R4, #0xA ; R4 = 0xA
.text:1000243A
.text:1000243A loc_1000243A ; CODE XREF: sub_1000240C+4A j
.text:1000243A ADD R0, R6, #0 ;
.text:1000243C ADD R0, #0x2C ; R0 = Pointer to IMEI descriptor
.text:1000243E ADD R1, R4, #0 ; R1 = R4 (which number from IMEI descriptor)
.text:10002440 BL sub_10003EF0 ; Get ASCII value
.text:10002444 LDRH R0, [R0] ; load value in R0
.text:10002446 SUB R0, #7 ; R0 = R0 - 7
.text:10002448 SUB R1, R4, #4 ; R1 = R4 - 4
.text:1000244A MUL R1, R0 ; R1 = R0 * R1
.text:1000244C LDR R0, =0x16F ; R0 = 0x16F
.text:1000244E MUL R0, R1 ; R0 = R1 * R0
.text:10002450 ADD R5, R5, R0 ; R5 = R5 + R0
.text:10002452 ADD R4, #1 ; Rd = Op1 + Op2
.text:10002454 CMP R4, #0xE ; is R4 = 0xE
.text:10002456 BLE loc_1000243A ; b if R4 is less or equal 0xE
You can see that 0xA is moved in R4. So some numbers of the IMEI are not included in calculation of the final code and that would be number 5 till 9. So basically the same is done in this loop. Get ASCII value from the IMEI number at a specific position. Substract 7. But here (text:10002448) is some difference: SUB R1, R4, #4. So 4 is substracted from R4 and put in R1. For the first run in this loop R4 is 0xA. And then R1 would be 0xA-4 = 0x6. So the app does edit the position number! Then we continue as normal by multiply and adding the result to R5.
This loop is executed until we have reached the end of the IMEI that is the 0xE position (we started with 0x0 which counts as first position, 0x0 - 0xE = 0xF or 15 positions, that is the length of the IMEI)
So in c code:
code = ((ASCIIvalueofIMEI - 7) * PositionofNumberinIMEI - 4) * 0x16F;
And finally codes are added together.
Last we have the final step in the calculation
.text:10002458 LSL R0, R5, #0x10 ; Logical Shift Left
.text:1000245A LSR R0, R0, #0x10 ; Logical Shift Right
R5 is shifted left 0x10 times which just means it is multiplied by 65536 (R0 = R5 * 65536). Then it is devided by 65536 (shifted back, R0 = R0 / 65536). Now we have the final code in R0 which is compared to the user input in R7 (.text:1000245C)...
Did u calculate the right code for you? Remember to convert hex to decimal!
ARMulator:
----------
If you didn't already open the ARM Project Manager do it now. Open app.apj. Open the tree in ARM Executable Image, than Debug and open app.s and symbian.s. Do not bother about the code now just click the "red" button: Debug. The ARMulator pops up and you'll see the first instruction of the prog. If there is no register view, click View/Registers/Current Mode. Also Memory View is nice (View/Memory...).
No click STEP and the first instruction of the prog is executed (mov R0, SP). See in the register window that R0 changed it's content. R13 is also refered as SP. So the value in R13 was moved in R0. Now click STEP again (Step in is not needed only if you want to see how the IMEI is put on Stack). I did hardcode the IMEI in Symbian.s. If you want to change it go to Symbian.s in the Project Manager, scroll down and edit it (remember it is Unicode!).
You can debug the prog further by using the STEP button. When the code routine is ready the app will write the code to the Console Window. I was too lazy to add a routine to convert this code to decimal. So u have to do this manually. Or you add that and send me the code... :-)
Conclusion:
-----------
As you can see it is not difficult to make keygens for SymbianOS. The main problem is to know what the OS functions do.
I hope that you understood what was going on in the prog. You can see what is done if you live debug it in ARMulator.
Any questions just ask me...
---------------------------------------------
أبو هيثم 10