I have features that need tone unlocked so I store them in my plist files.... a feature like a new avatar in a chat room could have the id "13891" and if it is unlocked I might assign it some key like "93" and if it's locked it might have any other key "37" for example.... So the plist will say: "13891" = "93" My question is can jailbroken phones edit the plist files easily and unlock features for themselves? What's a better way of storing this data? I don't want to have to check Apple's servers every time, it takes too long with low internet connection.
Edit: Current Answer:
4 measures to take:
1) Store it in the keychain. (Or plist I guess now that I've added measure #4)
2) Check Apple's servers every time but if you are worried about the lag that follows just check it in the background and in the meantime let the user use the app if it says they can.
3) Store your variables as encrypted keys in the keychain... don't store "FishingRod = unlocked" store "3dhk34D@HT% = d3tD@#".
4) Encrypt each key with the devices MAC address (these MAC addresses do NOT change and are available with or without WiFi connection... code below). That way if a user downloads a plist off of the internet and tries to use it, it won't work because when you decrypt it using their device ID you will can't random nonsense instead of the unlock key (in my examples case that would be "d3tD@#".)!!! -- Mac Address can no longer be accessed as of iOS7+, instead encrypt with other device unique things, such as the date the app was first launched
MAC address code (Just stick it in the view controllers ViewDidAppear... in the .H import )
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
-(void)viewDidAppear:(BOOL)animated {
int mgmtInfoBase[6];
char *msgBuffer = NULL;
NSString *errorFlag = NULL;
size_t length;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET; // Request network subsystem
mgmtInfoBase[1] = AF_ROUTE; // Routing table info
mgmtInfoBase[2] = 0;
mgmtInfoBase[3] = AF_LINK; // Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
// With all configured interfaces requested, get handle index
if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
errorFlag = @"if_nametoindex failure";
// Get the size of the data available (store in len)
else if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
errorFlag = @"sysctl mgmtInfoBase failure";
// Alloc memory based on above call
else if ((msgBuffer = malloc(length)) == NULL)
errorFlag = @"buffer allocation failure";
// Get system information, store in buffer
else if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
{
free(msgBuffer);
errorFlag = @"sysctl msgBuffer failure";
}
else
{
// Map msgbuffer to interface message structure
struct if_msghdr *interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
// Map to link-level socket structure
struct sockaddr_dl *socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
// Copy link layer address data in socket structure to an array
unsigned char macAddress[6];
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]];
NSLog(@"Mac Address: %@", macAddressString);
// Release the buffer memory
free(msgBuffer);
//return macAddressString;
NSLog(@"MAC: %@", macAddressString);
//F0:DC:E2:1D:EB:50
}
// Error...
NSLog(@"Error: %@", errorFlag);
}
Note: I got the MAC address code from a friend... I'm not claiming that I wrote the code... I don't know if he wrote it or got it from someone else as well.
4 measures to take:
1) Store it in the keychain. (Or plist I guess now that I've added measure #4)
2) Check Apple's servers every time but if you are worried about the lag that follows just check it in the background and in the meantime let the user use the app if it says they can.
3) Store your variables as encrypted keys in the keychain... don't store "FishingRod = unlocked" store "3dhk34D@HT% = d3tD@#".
4) Encrypt each key with the devices MAC address (these MAC addresses do NOT change and are available with or without WiFi connection... code below). That way if a user downloads a plist off of the internet and tries to use it, it won't work because when you decrypt it using their device ID you will can't random nonsense instead of the unlock key (in my examples case that would be "d3tD@#".)!!! -- Mac Address can no longer be accessed as of iOS7+, instead encrypt with other device unique things, such as the date the app was first launched
MAC address code (Just stick it in the view controllers ViewDidAppear... in the .H import )
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
-(void)viewDidAppear:(BOOL)animated {
int mgmtInfoBase[6];
char *msgBuffer = NULL;
NSString *errorFlag = NULL;
size_t length;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET; // Request network subsystem
mgmtInfoBase[1] = AF_ROUTE; // Routing table info
mgmtInfoBase[2] = 0;
mgmtInfoBase[3] = AF_LINK; // Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
// With all configured interfaces requested, get handle index
if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
errorFlag = @"if_nametoindex failure";
// Get the size of the data available (store in len)
else if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
errorFlag = @"sysctl mgmtInfoBase failure";
// Alloc memory based on above call
else if ((msgBuffer = malloc(length)) == NULL)
errorFlag = @"buffer allocation failure";
// Get system information, store in buffer
else if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
{
free(msgBuffer);
errorFlag = @"sysctl msgBuffer failure";
}
else
{
// Map msgbuffer to interface message structure
struct if_msghdr *interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
// Map to link-level socket structure
struct sockaddr_dl *socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
// Copy link layer address data in socket structure to an array
unsigned char macAddress[6];
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]];
NSLog(@"Mac Address: %@", macAddressString);
// Release the buffer memory
free(msgBuffer);
//return macAddressString;
NSLog(@"MAC: %@", macAddressString);
//F0:DC:E2:1D:EB:50
}
// Error...
NSLog(@"Error: %@", errorFlag);
}
You don't even need to jailbreak. Wherever you store a writable file, an application like iPhone Explorer can let the user grab and modify that file then write it back out to the device. It would only take one person purchasing to send the unlocked plist file out to the internet at large.
What I would do is store the unlocked items in the keychain on the device (just to obscure it a tiny bit more), and trust that on launch - but then also every time try to contact the Apple servers in the background to verify that the user really should have those unlocked items. That way they may have the items unlocked for a short time even if they can forge the keychain entries, but the ability will be removed if the device is connected to the internet at all while the app runs. Having to remember to disconnect a device from internet connectivity before each run is probably too annoying to make the stolen unlock worth it for the forger.
Since the keychain persists even across application deletion, you may also want to write out a plist file on first launch, if you do not detect that initially created plist file on later launches clear out the keychain.
If anything though, the risk of someone fiddling and unlocking things in your app is probably low. Always err on the side of giving user access when the situation is murky so you do not cause problems for real users.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With