iPhone: Preventing Piracy

13 Feb 2009, 12:38 PST

There's a lot of talk lately about preventing application piracy on the iPhone. Automated tools have been released to strip Apple's DRM, and a new anti-piracy product (which charges royalty fees on your sales) has been released.

As an iPhone developer, you have no access to the purchasing process. You can't issue (or revoke) serial numbers, implement an activation scheme, or provide any other fully independent copy protection. The only way to differentiate between a purchased copy of your application and a pirated one is to implement your own code to introspect the DRM that Apple has applied to your application.

On the phone, purchased applications are shipped to the user with a variety of meta-data that is readable by the application. The information potentially useful for implementing additional copy protection includes:

Using this information, it is possible to implement additional copy protection. The signature can be checked, the application encryption can be verified, etc. However, there's a problem -- none of this is documented by Apple. While most of the APIs and file formats are public, the actual distribution format is not. Apple could change the signature format, the meta-data plist, or any other distribution component at any time, at which point your copy protection may raise a false positive, and your paying customers will be wondering why you're wasting their time.

Given the risks, I've decided against implementing any further copy protection in my applications -- I believe it's Apple's problem to solve, and don't think it's worth risking false positives and annoyed customers. That said, if you'd like to take the plunge, here is some example code to get you started. For further reading, I suggest starting with Apple's Mac OS X Code Signing In Depth and Amit Singh's Understanding Apple's Binary Protection in Mac OS X.

The current process of cracking an application relies on stripping the application of encryption by attaching a debugger to the application on a jailbroken phone, dumping the text section containing the program code, and reinserting it into the original binary. The below code checks for the existence of LC_ENCRYPTION_INFO, and verifies that encryption is still enabled. There are, of course, a number of ways to defeat this check, but that's the nature of copy protection:

#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>
 
/* The encryption info struct and constants are missing from the iPhoneSimulator SDK, but not from the iPhoneOS or
 * Mac OS X SDKs. Since one doesn't ever ship a Simulator binary, we'll just provide the definitions here. */
#if TARGET_IPHONE_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
#define LC_ENCRYPTION_INFO 0x21
struct encryption_info_command {
    uint32_t cmd;
    uint32_t cmdsize;
    uint32_t cryptoff;
    uint32_t cryptsize;
    uint32_t cryptid;
};
#endif
  
int main (int argc, char *argv[]);
  
static BOOL is_encrypted () {
    const struct mach_header *header;
    Dl_info dlinfo;
      
    /* Fetch the dlinfo for main() */
    if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL) {
        NSLog(@"Could not find main() symbol (very odd)");
        return NO;
    }
    header = dlinfo.dli_fbase;
 
    /* Compute the image size and search for a UUID */
    struct load_command *cmd = (struct load_command *) (header+1);
     
    for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {
        /* Encryption info segment */
        if (cmd->cmd == LC_ENCRYPTION_INFO) {
            struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;
            /* Check if binary encryption is enabled */
            if (crypt_cmd->cryptid < 1) {
                /* Disabled, probably pirated */
                return NO;
            }
             
            /* Probably not pirated? */
            return YES;
        }
         
        cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
    }
     
    /* Encryption info not found */
    return NO;
}