How to protect your application against a keygen
This first article introduces a basic protection scheme based on IMSI or IMEI and how a cracker can defeat it.
Identifying a device
A mobile phone is typically identified by two unique numbers:
the IMEI, short for International Mobile Equipment Identity, which represents the device hardware itself. This number and its format is described in Retrieving the device IMEI code.
the IMSI, International Mobile Subscriber Identity which represents your subscription. The number contains two parts: the first identifies the GSM network operator with whom the subscriber has an account. The second part of the number is allocated by the network operator to identify uniquely the subscriber.
When registering your application, you can use one of these two codes - or a combination of both - to generate an lock/unlock code for your application. :
if you choose the IMEI, you bound the application to the device: the user will only be able to run the application on its current phone. He can change and move to another operator without problem but if he has a new phone, he will have to buy a new license.
if you choose the IMSI, you bound the application to the user. He can install the application on several devices but only the device he is currently using (i.e. which contains the proper SIM card) will be able to run it. This is useful when - like me - you have a single subscription but several devices.
you can be a lot more restrictive and use a combination of both and then lock your application on the IMSI and the IMEI.
you may also generate your own unique code based on other number (server generated, time based, etc....).
Note that, while there are some APIs to retrieve the IMEI on all Symbian OS phone (check Retrieving the device IMEI code), this is not always true for the IMSI (read How to retrive the IMSI number).
Let's suppose that you are a shareware author. You have written a nice little apps and decide to sell it through Handango, Symbos, or any other channel. It is very likely that you have designed a protection method based on one of these two codes, let's say the IMEI. The issue is that a cracker knows that too... And this is a first weakness in your application. As a matter of fact, with a little bit of knowledge of ARM assembly language and the proper tools, it is fairly easy to reverse-engineer part or whole of the application and identify where each system calls are made. A potential cracker will probably try to check whether you are calling MBasicGsmPhoneId::GetGsmPhoneId or PlpVariant::GetMachineIdL. The following is a real world example, based on a cracking tutorial written by a (bad!) guy called 18+2 which describes, how, from this entry point he has been able to break the protection of an application.
Finding an entry point: locating where the code is retrieved
In our example, the cracker has found that the second function was called there:
.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.
Finding the code calculation
Now we have to find the place where we enter our code and where the app calculates and compares the code. So he 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. Here we have our code calc routine:
.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+2Aj
.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+4Aj
.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+52j
.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: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
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: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 to find out what is in R7. Take a look 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...
Cracking the code
Here is the first part of the calc func:
.text:10002416 MOV R4, #0 ; R4 = 0
.text:10002418
.text:10002418 loc_10002418 ; CODE XREF: sub_1000240C+2Aj
.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: 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:1000243A
.text:1000243A loc_1000243A ; CODE XREF: sub_1000240C+4Aj
.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: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)... Writing the proper keygen is now easy as the algorithm is known.
Conclusion
As you can see, designing a good protection scheme is not as simple as it looks and is definitely not as simple as writing a good ciphering code. You need to hide as much as possible your treatment and multiply the checks (and then the roads to explore for the cracker). Is the work worth it ? Is not a good freeware better than a badly protected shareware that will not sell ? You only have the answer....






> How to crack a symbian application: writing a keygen
— Mayur.
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
Lol Eric copy hacker tutorial... It's not his document, it's from K**S
:-)
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen
Yes, the last sentence in the first block of text is quite clear that someone else wrote the body of the article itself.
Eric is usually VERY careful about that kind of stuff.
> How to crack a symbian application: writing a keygen
> How to crack a symbian application: writing a keygen