Saving application purchase data in plist files?

NOTE ---- The answer below uses iOS6 methods, Apple has since removed the developer access to the MAC addresses. If you are developing the first response of iOS7 + disregaurd and simply encrypt your IAP unlock data based on other variables that will be unique for each device (for example, from the date the application was first launched)

I have functions that need tonal unlocking, so I store them in my plist files ... a function, such as a new chat avatar, can have the identifier "13891", and if it is unlocked, I can assign it several keys for example , "93", and if it is locked, it can have any other key "37", for example .... So plist will say: "13891" = "93" My question is whether jailbroken phones can easily edit plist files and unlock features for yourself? What is the best way to store this data? I do not want to check Apple servers every time, it takes too much time with a low Internet connection.

Edit: current answer:

4 measures to take:

1) Store it in the keychain. (Or plist, I think, now I have added measure No. 4)

2) Check Apple servers every time, but if you are concerned about the backlog that follows, just check it in the background and thereby let the user use the application if he says he can.

3) Store the variables as encrypted keys in the keychain ... do not store "FishingRod = unlocked" store "3dhk34D @ HT% = d3tD @ #".

4) Encrypt each key with the MAC address of the device (these MAC addresses are NOT changed and are available with or without WiFi ... below). Thus, if a user downloads a layer from the Internet and tries to use it, it will not work, because when you decrypt it using your device identifier, you will not be able to use random nonsense instead of the unlock key (in my example, for example, " d3tD @ # ".) !!! - The Mac address can no longer be accessed with iOS7 +, instead it is encrypted with other unique things on the device, such as the date the application was first launched

MAC address code (just paste it into ViewDidAppear ... view controllers in .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 received the MAC address code from a friend ... I do not claim to have written the code ... I do not know if he wrote it or received it from someone else.

+4
source share
3 answers

4 measures to take:

1) Store it in the keychain. (Or plist, I think, now I have added measure No. 4)

2) Check Apple servers every time, but if you are concerned about the backlog that follows, just check it in the background and thereby allow the user to use the application if he says he can.

3) Store the variables as encrypted keys in the keychain ... do not store "FishingRod = unlocked" store "3dhk34D @ HT% = d3tD @ #".

4) Encrypt each key with the MAC address of the device (these MAC addresses are NOT changed and are available with or without WiFi ... below). Thus, if a user downloads a layer from the Internet and tries to use it, it will not work, because when you decrypt it using your device identifier, you will not be able to use random nonsense instead of the unlock key (in my example, for example, " d3tD @ # ".) !!! - The Mac address can no longer be accessed with iOS7 +, instead it is encrypted with other unique things on the device, such as the date the application was first launched

MAC address code (just paste it into ViewDidAppear ... view controllers in .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); } 
+4
source

You don’t even need to jailbreak. When you store a file that is writable, an application like iPhone Explorer may allow the user to capture and modify the file and then write it back to the device. The buyer would only need one person to send the unlocked plist file to the Internet as a whole.

What I would do is to store the unlocked items in the keychain on the device (just to hide it a little more) and trust it at startup, but then also try to contact Apple servers in the background every time, make sure that the user really needs to have these unlocked elements. Thus, they can have items unlocked for a short time, even if they can fake entries in the keychain, but the ability will be deleted if the device is connected to the Internet while the application is running. Do not forget to disconnect the device from the Internet connection before each run, it is probably too annoying for a stolen unlock to cost it for a falsifier.

Since the key chain is preserved even when the application is uninstalled, you can also write the plist file the first time you run it, if you do not find that the originally created plist file on subsequent launches cleared the key chain.

If there is something else, the risk of someone messing around and unlocking things in your application is likely to be low. Always make mistakes on the side of providing access to the user when the situation is muddy, so you do not cause problems for real users.

+3
source

can jailbroken phones easily edit plist files and unlock functions for themselves?

Yes exactly.

What is the best way to store this data?

Perhaps a bunch of keys, but this can also be changed on jailbreak devices.

I do not want to check Apple servers every time, it takes too much time with a low Internet connection.

Too bad. If you want to be safe, at least to some extent, you better check out the Apple server or, better yet, your own server. (This also does not guarantee 100% that your game will not be hacked on a jailbroken phone, as the behavior of the application can be changed since you want to use MobileSubstrate, but at least it is a little more secure.)

+1
source

All Articles