Resisting App PiracyBackgroundYour apps may be pirated! • Google "appname.ipa", but also search Cydia and other darker areas • Flurry and Game Center still work on pirated apps. • Many bloggers have reported analytics pointing to high piracy rates; especially games.This talk is about what to do about it...
Resisting App PiracyPhilosophyMy Attitude • Its an arms race which ultimately cant be won. • I have better and more interesting things to do... But we can make it harder. • Fully automated "crackers" must be stopped, as a duty to society. • Jailbroken phones are okay, as long as they pay for my app.Concerns • No help from Apple; prohibited in Mac AppStore, but open issue on iOS. • False positives are huge PR disaster (paying customers affected). • Detection is focus, then behave differently: - do not: rm -rf / - keep plausible deniability (oops, silly bug) - be cute, it helps if you get it wrong
Resisting App PiracyMethodsWeak Methods • check running as root: ASSERT(getuid() != 0); - false positive on jailbroken devices - already worked-around, may be obsolete technique • looking at the bundle contents: - bundle is modified by apple after review, cant checksum before we submit - simple presence checks easily worked-aroundMy Approach • all crack methods involve decrypting the binary • GDB is used in the cracking process, deny them that tool. • I like checksums on binary code. It’s old-school but it works.
Resisting App PiracyisPirate() Steps 1) Deny them use of GDB. 2) Verify checksum of binary running in memory. 3) Verify binary running was encrypted when loaded.But... • repeat above at random times • make "isPirated" global flag non-trivial to force (ie. not just a flag) • check flag very often, but do checksum less often
Resisting App PiracyDeny GDB • call: ptrace(PT_DENY_ATTACH... • but hide ptrace() call, since its purpose is obvious • does not stop them from decrypting binary because they will put a breakpoint on the first instruction of your program; text has been decrypted into RAM already at that point. • point at which GDB is disabled very clear, not hard to find and patchChecksum • find the location and length of text segment at run time • important gotcha: - load address is random in signed app, but fixed for development builds - extern byte_t start __asm__("start"); • length easy to fetch from getsectbyname(), could also be approximated • MD5, CRC32, SHA256, whatever
Resisting App PiracyChecksum Pre-Knowledge • We need to know what the checksum should be. • Python script in XCode "Build Phase script" to calculate it - dump the binarys text segment using: otool -tX - write out a plist or other file which can be read at runtime • Dont just write the correct value to a file! - should be function of a shared secret between binary and build script • Consider hiding checksum value in a resource like image file.Check Encryption • Read mach-O headers, looking for structure that says files text segment is encrypted. • Difficult to test, since only the appstore seems to encrypt the file - perhaps uniquely for each buyer? • Published code keyword: LC_ENCRYPTION_INFO
Resisting App PiracyMore Specifics • All code must be inlined: __attribute__((always_inline)) • Sprinkle checks into lots of different spots, not just startup • Carefully choose when to re-verify checksum - it is relatively slow - harder to find a check if on code path that only runs once/rarelyExpected Results • Automated piracy tools will still work, but the binary they make should fail: - code checksum is right, but - well see that were not running from an encrypted file. (realistically, they will stop here for my apps, but if youre selling something they really want...) • They need to use GDB to find each spot where we disable GDB; NOP that out. • The checksum will fail at that point, because binary has changed. • Disable the checksum code and/or the piracy flag check. • ... but they will have to find each instance of that code.
Resisting App PiracyOther thoughts • When handling support issues, it often helps to ask a user to send a screen shot ofthe problem. If we see anything non-standard in the status bar, we say "we dont supportjailbroken devices–end of discussion". Its very hard for jailbreakers to resist personaliza-tion. • One problem: need two checksums if your apps support 3G and earlier devices. Yourbinary will be "fat", both armv6 and armv7. • Next level would be modulate the checking code so that its instruction sequence isdifferent each time it is included in the binary.