When coding anti-piracy prevention measures, your goal should be to keep honest users honest.
It is important to make the user experience pleasing and simple for your paid customers.

While in an ideal world, people would buy everything legally, reality is very different.
Most people, if tempted with an easy free way to get your app, will pirate even though they know it is wrong.

Here are three easy tips that will help your app resist to binary cracks, but do not take much work to implement.

You can grab the sample code for the first two tips.

Strip debug symbols

Stripping debug symbols will remove all the method names from the executable, which makes it a lot harder for anyone to reverse-engineer your app using gdb. (I showed how this is usually done here) Now, instead of seeing all your method names, which method the app is currently in, and instead of being able to breakpoint the app at that spot, they’ll only see hexadecimal addresses.

You can still get around this by class-dumping the executable and getting new method’s hex addresses from there, (although they might be off by a certain difference which you’d have to calculate) but this already makes it a lot harder for crackers to attack.

So, without further ado, here’s how you do this: Apple’s documentation has a detailed explanation, so instead of rewriting it myself, I’ll just copy-paste it here:

Xcode provides several built-in options for stripping executables of their debugging symbols. One of these is the Strip Linked Product build setting. While typically set, it has no effect unless the Deployment Postprocessing setting is also set. Deployment Postprocessing is a master switch that enables the action of a host of other build settings. It’s approximately analogous to running the xcodebuild tool with the install command.

Again, open the target build settings and turn on debugging symbols for the Release configuration. Open the project build settings; in the Release configuration, enable both Strip Linked Product (if it isn’t on already) and Deployment Postprocessing. Your project settings should now resemble those shown in Table 2.

Build Setting Value
Deployment Postprocessing YES
Strip Linked Product YES


Behind this barbaric name is a very useful flag which lets you prevent gdb from attaching to your app. Ever tried debugging iTunes? Give it a try now, but be prepared for a disappointment, it crashes gdb when it tries to attach to it.

One step further in protecting your app after striping debug symbols is to activate PT_DENY_ATTACH. Note that this doesn’t make stripping debug symbols useless. While in theory it does, there are ways to get around it.

Activating this protection is really easy, and only involves editing your main.m.

//  main.m
//  ProtectionSample
//  Created by Kenneth Ballenegger on 2008/06/27.
//  Copyright Azure Talon 2008. All rights reserved.

#import <Cocoa/Cocoa.h>
#include <sys/ptrace.h>

int main(int argc, char *argv[])
    //Build settings -> Other C Flags: -DDEBUG
#ifdef DEBUG
    //do nothing
    ptrace(PT_DENY_ATTACH, 0, 0, 0);
    return NSApplicationMain(argc,  (const char **) argv);

This code should be pretty self-explanotary…

You need to have a flag that differs for release build and debug builds. You want to be able to debug your app while you code it. That’s why I set Other C Flags for the Debug build settings to “-DDEBUG” and used an #ifdef to activate it only for release builds.

Test it if it works quickly, fire up terminal and try to use gdb.

seoMac:~ kenneth$ gdb /Users/kenneth/Desktop/ProtectionSample/build/Release/ProtectionSample.app/Contents/MacOS/ProtectionSample
GNU gdb 6.3.50-20050815 (Apple version gdb-952) (Sat Mar 29 03:33:05 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"...Reading symbols for shared libraries ..... done

(gdb) r
Starting program: /Users/kenneth/Desktop/ProtectionSample/build/Release/ProtectionSample.app/Contents/MacOS/ProtectionSample
Reading symbols for shared libraries ++++....................................................................... done

Program exited with code 055.

Congratulations, it works! Your app cannot be loaded into gdb anymore. (Note: there are workarounds. Experienced hackers who really want to will still manage to get in.)

Checksum your binary

The last tip for today is of a different kind, and it is probably the most effective of the three.

Basically, all you need to do is to checksum your binary. Put the md5 somewhere in your .app, preferably well hidden. Preferably use a salted hash, or double-hash it. Make it hard for a potential hacker to figure out how to get the correct hash for a given hash. Hide the file in which you store this hash well, or store it in your Info.plist. Where you store it doesn’t matter, but you can’t put it in the binary. Preferably set up a build script that will re-generate the new hash at every build (when your binary changes), so you don’t have to do it yourself.

In your code, check the stored hash against the binary, and if they are different, it means the binary has been modified. In that case, display an error message asking to re-download the app from your site, and quit.

This entry was posted on Friday, June 27th, 2008 at 1:14 pm and is filed under Cocoa, English, Hacking. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Add your thoughts!
ctwise asserts
June 27th, 2008 at 1:24 pm

Please avoid PT_DENY_ATTACH. Not only is it easily circumventable (http://landonf.bikemonkey.org/code/macosx/Leopard_PT_DENY_ATTACH.20080122.html) but it makes monitoring and understanding YOUR OWN MACHINE impossible.

scrod whispers
June 27th, 2008 at 11:44 pm

When running on leopard you can use code signing and force a static validation of your app at launch time or set the KILL flag and wait for it to become dynamically invalid as resources and pieces of the executable are paged-in. This will be less work and significantly more thorough than implementing your own system from the ground up.

June 29th, 2008 at 7:59 am

I tried this but gdb still attached. Maybe it’s because I’m using Xcode 3.0, with an earlier version of gdb than you have?

GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Tue Oct 2 04:07:49 UTC 2007)

I’m pretty sure I did it correctly. To make sure that the #ifdef was working (actually I used #ifndef), I logged a compiler #warning just above the call to ptrace(), and it did log. If you send me, or post, your ProtectionSample, I’ll see if my gdb can attach to it.

kenneth proclaims
June 29th, 2008 at 10:37 am

Jerry, I just emailed you the .app.

Note that gdb still loads, but won’t let you run. So if you load gdb with the executable, it will work, read symbols and everything. However, it’s when you “run” that the app exits with code 055 every time.

Jerry Krinock speaks of
June 29th, 2008 at 2:41 pm

Ah, Kevin, you are correct. Being an Xcode GUI user, I missed the tiny letter ‘r’ in your article. Also, I was fooled by the fact that when I run gdb it does that “Reading symbols for shared libraries ++++++” twice, both before and after I ‘run’. Oh, well.

So, I re-ran the test, typed ‘r’ and got an 055 exit. Then I unzipped an old version of my app which was not compiled with the call to ptrace PT_DENY_ATTACH, attached to it, typed ‘r’, and gdb continued to work. So, I take back what I said yesterday.

Thanks, Kenneth.

Also, regarding the original comment by ctwise, I don’t believe that the “makes…impossible” applies to your machine. #ifdef DEBUG, everything is back to normal. Regarding the workaround, of course, if one hacks the system, anything is possible. PT_DENY_ATTACH just sets up another hurdle for some hacker who may be seriously thinking of not drinking another Coke and going to bed instead.

ctwise sings
June 30th, 2008 at 2:55 pm

PT_DENY_ATTACH doesn’t work to stop hackers. The people who want to hack your code will have the knowledge and know-how to load the kext. The only people who will be affected by the PT_DENY_ATTACH will be the people who have a legitimate desire to understand their systems and incorrect behaviors that they are trying to track down. Those people will be forced to either uninstall your application, live with the problems or install a kext they don’t actually want or need in order to solve their issues. In other words, PT_DENY_ATTACH doesn’t stop hackers and pisses off some of your customers.

Sign your code instead. Use techniques and tools that obfuscate and encrypt your code. Just don’t make the system opaque to understanding. That path takes us back to Windows.

Mecki surmises
July 17th, 2008 at 2:06 pm

Codesigning buys you nothing! If the binary is modified, the signature becomes invalid… that’s it. That’s all that happens. It won’t have any effect other than that. Who cares for an invalid signature? Apps with invalid sig will run just fine!!! And almost nobody understood the KILL flag. An app with invalid sig will run just fine, even with KILL flag. The KILL flag only means it will be killed if the running app in memory somehow loses its identity (e.g. it would be killed if a process was modifying the app in memory), but it will still start when modifying it on disk (try it, if you don’t believe me).

Apple has no plans to change that either! They say “Why would we want to do this? A hacker could just patch your app and then resign it with his own signature and it will have a valid sig again. Okay, you could write code that checks if the signature is valid *AND* if the signature is signed by your cert, but the hacker could just replace this piece of code, too”

ran6110 pronounces
February 13th, 2009 at 8:48 pm

OK, you really don’t have much of a chance at stopping the really serious hacker.

These items like most of the others stop the casual hacker and the opportunistic thief. And make the code author feel a little safer.

What I’ve done to delay the hack is to require a serial number with the users email. The serial number is NOT generated by a script. When we get the confirmation email we generate the serial number and sent it out.

When the correct information is entered the program is allowed to run. When the incorrect information is entered the program is allowed to run!

Yeah, that sounds weird but the twist is the program doesn’t report the error/problem until days, weeks or months later! The point being, you don’t have to stop and report the error the milli-second it happens, that only helps the hacker.

It won’t stop them but it makes it really hard to debug and patch.

Also, never rely on only one validation check and don’t always use the same call. Sometimes I check in the awakeFromNib other time I check in one or more views. I have one program that check when it’s closing and reports a corruption error that requires a re-install!

The point being, you have to work just as hard to hinder (notice I didn’t say stop) the hacker as he does cracking your program.

Popin proclaims
July 14th, 2010 at 11:25 pm

As long as a program runs it can be hacked. It just takes skill and extream patiance to crack the safest software. If the system can read and run the program then the hacker can break in. If you want to be safe then take twice as long as it took to make the program to encode and protect it. In the long run expect a hacker to get in. You have to give them credit though… They go through the lowest level of programs and have to find all the weakpoints just to get in ONCE. Then they might have to do it again! >.>

Have something to say?