Some of you may know of sites either selling or distribution pirated versions of apps, that have had the encryption taken out of them and are available on any iPhone (as long as its jailbroken). Well the first step towards preventing the piracy of your apps is detecting the piracy on those apps, and then taking steps to either monetize your freeloading traffic or disabling your app altogether. A very basic check some iPhone applications apply is this:
if([[[NSBundle mainBundle] infoDictionary] objectForKey:@"SignerIdentity"] != nil)
{
// The app is pirated
}
While this actually works if you just use the software designed to strip encryption from the app to make a pirated copy, it can easily be overcome by simply scanning the binary for the string "SignerIdentity", and changing it to a random string forcing the objectForKey to throw a null at the if statement, so it's not as secure as I would like. So I'd like to recap, and say that I like the method of checking the SignerIdentity, but not how the string is in full display in the binary, but with some very, very basic cryptography and honeytraps we can fix that. Now we need a string that isn't like "SignerIdentity", but can be translated into "SignerIdentity" on the fly, now since I simply want to use strings, and not any data, I would start with either a substitution or transposition cipher, since they are incredibly easy to implement and still manage to hide the text from the plain site of the binary. Both are fairly simple to implement, but I'll start off with a substitution cipher:
NSLog(@"Substitution Cipher:");
char symCipher[] = { '(', 'H', 'Z', '[', '9', '{', '+', 'k', ',', 'o', 'g', 'U', ':', 'D', 'L', '#', 'S', ')', '!', 'F', '^', 'T', 'u', 'd', 'a', '-', 'A', 'f', 'z', ';', 'b', '\'', 'v', 'm', 'B', '0', 'J', 'c', 'W', 't', '*', '|', 'O', '\\', '7', 'E', '@', 'x', '"', 'X', 'V', 'r', 'n', 'Q', 'y', '>', ']', '$', '%', '_', '/', 'P', 'R', 'K', '}', '?', 'I', '8', 'Y', '=', 'N', '3', '.', 's', '<', 'l', '4', 'w', 'j', 'G', '`', '2', 'i', 'C', '6', 'q', 'M', 'p', '1', '5', '&', 'e', 'h' };
char cfile[256];
[[[NSString alloc] initWithString:@"SignerIdentity"] getCString:cfile maxLength:sizeof(cfile) encoding:NSUTF8StringEncoding];
NSLog(@"%s",cfile);
for(int i=0;i<strlen(cfile);i++)
cfile[i] = symCipher[cfile[i]-0x21];
NSLog(@"%s",cfile);
for(int i=0;i<strlen(cfile);i++)
{
for(int j=0;j<sizeof(symCipher);j++)
{
if(cfile[i] == symCipher[j])
{
cfile[i] = j+0x21;
break;
}
}
}
NSLog(@"%s",cfile);
This code may seem complicated, but it's not. Basically it contains a lookup array of characters to substitute for other characters, like rearranging the alphabet. The output here will be:
Substitution Cipher:
SignerIdentity
V.NwY2*8YwC.C1
SignerIdentity
So as you can see, it encrypts the string SignerIdentity to the string V.NwY2*8YwC.C1, the decrypts it back to SignerIdentity. So getting back to what we wanted to do, which was disguise our string, we could quite easily do this to disguise our piracy check:
char symCipher[] = { '(', 'H', 'Z', '[', '9', '{', '+', 'k', ',', 'o', 'g', 'U', ':', 'D', 'L', '#', 'S', ')', '!', 'F', '^', 'T', 'u', 'd', 'a', '-', 'A', 'f', 'z', ';', 'b', '\'', 'v', 'm', 'B', '0', 'J', 'c', 'W', 't', '*', '|', 'O', '\\', '7', 'E', '@', 'x', '"', 'X', 'V', 'r', 'n', 'Q', 'y', '>', ']', '$', '%', '_', '/', 'P', 'R', 'K', '}', '?', 'I', '8', 'Y', '=', 'N', '3', '.', 's', '<', 'l', '4', 'w', 'j', 'G', '`', '2', 'i', 'C', '6', 'q', 'M', 'p', '1', '5', '&', 'e', 'h' };
char csignid[] = "V.NwY2*8YwC.C1";
for(int i=0;i<strlen(csignid);i++)
{
for(int j=0;j<sizeof(symCipher);j++)
{
if(csignid[i] == symCipher[j])
{
csignid[i] = j+0x21;
break;
}
}
}
NSString* signIdentity = [[NSString alloc] initWithCString:csignid encoding:NSUTF8StringEncoding];
The NSString signIdentity now contains the string "SignerIdentity", without us having to declare it in the binary! Excellent, another obstacle for piracy. It would be a good idea to generate your own symCipher array, and generating your own encrypted strings, to do this I've made a small PHP script that simply outputs your decrypted string and the substitution array needed to generate it here. I'll also discuss the other simple cipher method of string encryption, a transitional cipher. The principal is really simple, just replacing a letter in the ASCII table with one a defined amount above or below it, so if I wanted -1, B would be A, A would be Z etc. An objective-C implementation would look like this:
NSLog(@"Transpositional Cipher:");
char csignid[] = "SignerIdentity";
NSLog(@"%s",csignid);
for(int i=0;i<strlen(csignid);i++)
csignid[i] = csignid[i]-3;
NSLog(@"%s",csignid);
for(int i=0;i<strlen(csignid);i++)
csignid[i] = csignid[i]+3;
NSLog(@"%s",csignid);
This will give us the log:
Transpositional Cipher:
SignerIdentity
PfdkboFabkqfqv
SignerIdentity
So now to do a basic decryption into the SignerIdentity string we need, I we just use the decryption method with our encrypted string:
char csignid[] = "PfdkboFabkqfqv";
for(int i=0;i<strlen(csignid);i++)
csignid[i] = csignid[i]+3;
NSString* signIdentity = [[NSString alloc] initWithCString:csignid encoding:NSUTF8StringEncoding];
Now as you can see this contains a lot less code, but with the drawback being its a lot more crackable. So now we can hide our string from simple hex edits, we can lay a honeytrap in our code. Let's go back to the code we used at the beginning of the article, we used the string in full sight back then. Now what if we added a small boolean in there to return true if its been executed if the objectForKey is null?
bool checked = false;
if([[[NSBundle mainBundle] infoDictionary] objectForKey:@"SignerIdentity"] == nil || [[[NSBundle mainBundle] infoDictionary] objectForKey:@"SignerIdentity"] != nil)
{
checked = true;
}
if(!checked)
{
// This app be hacked!
}
In this code, the variable checked will be false if someone hex edits out SignerIdentity, a nice little honeytrap. Now what you do after you have detected this piracy is up to you, I choose to display ads and turn the pirates into monetized traffic, but other developers choose to crash their apps, exit them normally, or restrict the functionality, that part of this however, is up to you.