41

Wondering if anyone has a tutorial or working code for the new Mac App Store's receipt validation? About the only references I've been able to find so far are Apple's stellar documentation on the topic and one open source project which compiles but doesn't have a lot of inline comments so it's hard to understand unless you are a crypto whiz.

Apple docs for registered devs only:

https://developer.apple.com/devcenter/mac/documents/validating.html

Roddi's ValidateStoreReceipt (looks promising, but sparsely documented):

https://github.com/roddi/ValidateStoreReceipt

Also wondering why Apple does not just provide working code for validation?

Any other good references out there?

Duck
  • 34,902
  • 47
  • 248
  • 470
Rei
  • 1,148
  • 2
  • 12
  • 16
  • 8
    The reason Apple does not provide full code is that if everybody used the same code then all apps would be equally easy to crack. If everyone does it slightly differently it is supposed to give crackers a harder time. – Nick Moore Nov 25 '10 at 09:52
  • Bounty for anyone who explains this well enough that I can actually get it to work. In particular I tried to use Alan Quartermain's code and ran into compile errors . . . see my comment below Koregan's answer. – William Jockusch Jan 31 '11 at 01:55
  • 6
    @Nick I think 99% of the developers don't care about hackers, if they want to hack your app they will. I just want to make sure that if a regular user copy my app from his/her friend computer it does not work. – Tibidabo Oct 26 '11 at 09:39
  • @Tibidabo I agree with you. I was only answering the question "why Apple does not just provide working code for validation?", not making an argument for it. I use Roddi's :) – Nick Moore Oct 26 '11 at 15:36

13 Answers13

31

It is hard to provide a generic solution for Mac App Store receipt validation, mainly because this is a very sensitive piece of code that must be hard to bypass (cf. Apple documentation).

These GitHub projects are very good starting points to learn about what steps must be performed in receipt validation:

Once you have understood what must be done, here is some advice:

  • Don't use Objective-C classes or methods. Objective-C carries a lot of metadata, and its dynamic nature exposes it to runtime injection.
  • Only use C function calls. Even if you need more lines of code with the CoreFoundation framework, you can perfectly do what the Foundation framework can do (NSString, NSArray, NSDictionary, ...).
  • Don't link dynamically with the OpenSSL library as it has been deprecated in Mac OS X Lion. If you want to go with OpenSSL, link it statically to be sure to have the latest release.
  • Use system functions for cryptography. Mac OS X ships with equivalent functions since 10.5. For example, to compute a SHA-1 hash, you can use the CC_SHA1 function.
  • Don't put strings in plaintext in your code. Encode them or encrypt them. If you fail to do so, you give a hint about the location of your code.
  • Don't use numeric constants in your code. Compute them at runtime, with some simple operations (+, -, / or *). Again, if you fail to do so, you give a hint about the location of your code.
  • Avoid simple tests for validation by embedding your tests and the call to NSApplicationMain into a complex loop.
  • Avoid calling NSApplicationMain directly. Use a function pointer to hide the invocation. If you fail to do so, you give a hint about the location of your code.
  • For each release of your application, slightly modify the validation code so it is never the same.

Remember that receipt validation is necessary and is not simple as it seems. It can consume a lot of time that you may better spend on your application.

So I suggest you to take a look at this application: Receigen (Disclaimer: I am the developer of this application).

Laurent Etiemble
  • 27,111
  • 5
  • 56
  • 81
  • So all those project that are using Obj-C code are not useful, since they are exposed to runtime injection, instead your Receigen it's pure C code. I have a question now, how to check your app before buying it? You can understand that, due to its nature (code generator), it could be used as a demo before buying it (I don't see any review to check app users reviews also). I would pay for that, only if I could check the app before. – loretoparisi Dec 09 '11 at 11:20
  • You have a good point. I still don't know how to offer a free version of Receigen so people can test the code generation (should it only generate a part of code, or obfuscate the result, ...). Can you tell what would be sufficient for you to evaluate the application ? – Laurent Etiemble Dec 12 '11 at 11:15
  • @LaurentEtiemble This post is helpful when it comes to selling from the App Store, but can you help me understand how I protect an app I sell from my own website (i.e. zip file download)? http://security.stackexchange.com/questions/21226/mac-app-purchase-validation – Hope4You Oct 06 '12 at 15:43
  • 1
    I have no relation with Laurent, but do use Receigen. I can confirm that it works, but how "unhackable" it is?? For me it is good enough. Given the fact that any SW can be hacked eventually. Btw: I am also using Receigen with Swift: http://swiftrien.blogspot.com/2015/05/osx-receipt-validation-in-swift-part-8.html – Rien Jun 05 '15 at 08:45
  • Actually, you should *not* use system libraries for crypto if you are concerned about making it hard to circumvent your checks. Those are a really obvious target for attacks. And there's not much you can do to hide your invocation of NSApplicationMain. It is typically the only function called in your main() function. Instead, perform the validation in more than one spot in your code—not just in main(). – dgatwood Jun 08 '15 at 23:01
  • @Rien No software is unhackable given enough time and means. You can only make it very painful to reverse engineer and/or to patch so it is not worth the price to hack it. – Laurent Etiemble Sep 06 '15 at 16:31
  • @dgatwood A good way to hide the function calls is to dereference them through function pointer and/or variable offset that is computed. And you are right that the validation must be performed in several place to increase the robustness of the validation. – Laurent Etiemble Sep 06 '15 at 16:34
  • Hiding the function calls won't help if they're your app's only direct use of crypto. It takes just minutes to create a library that interposes the function itself so that your dyld lookup returns the attacker's function instead. That attacker can then inspect the payload before deciding whether to lie about the validity of some signature or whatever, based on whether the calling stack frame is in your app or in (for example) the networking stack. There's a reason Apple's documentation recommends using inlined functions. – dgatwood Sep 07 '15 at 21:15
  • The problem with Receigen is 3 years without update. I would not risk my money on that! – Duck Nov 19 '17 at 21:47
6

In order to validate against the real receipt after testing, change this line of code in your main.m file:

if (!validateReceiptAtPath(@"~/Desktop/receipt"))

to

#ifdef USE_SAMPLE_RECEIPT   // defined for debug version
    NSString *pathToReceipt = @"~/Desktop/receipt";
#else
    NSString *pathToReceipt = [[[NSBundle mainBundle] bundlePath]
        stringByAppendingPathComponent:@"Contents/_MASReceipt/receipt"];
#endif  
    if (!validateReceiptAtPath(pathToReceipt))
        exit(173); //receipt did not validate

and in your compiler settings, "Other C Flags" for your Debug Configuration should include -DUSE_SAMPLE_RECEIPT

courtesy http://jesusagora.org/groups/futurebasic/0::53562:get:1read.html

snibbe
  • 2,715
  • 1
  • 27
  • 34
6

Be sure to check that you are validating a receipt for your app. Easy to do all the crypto and verification of signatures for the wrong receipt.

See http://pastebin.com/1eWf9LCg where it looks like Angry Birds missed this bit and left them open to people substituting in a receipt from a free app.

Alan Quatermain also has code to do this up on github. https://github.com/AlanQuatermain/mac-app-store-validation-sample

It should not be used as-is to avoid automated removal.

koregan
  • 10,054
  • 4
  • 23
  • 36
  • After downloading it . . . how do I get it to run? I don't see an .xcodeproj there, and gcc main.m gave me errors. – William Jockusch Jan 30 '11 at 15:22
  • Just to elaborate . . . I tried importing his source into my project. But whenever I try to compile any of the files in the asn1 directory, I get at least 22000 compile errors in AppKit.h. Could I be missing a setting somewhere? – William Jockusch Jan 31 '11 at 02:07
  • 1
    The sample was culled directly from a regular OS X 10.6 Cocoa app. You can replace your own main.m file with this one and add the other files within their subfolder directly and it should Just Work. You will need to link in Security.framework to get the Keychain stuff to compile, but otherwise it will only need the Cocoa libraries IIRC. – Jim Dovey Feb 01 '11 at 02:23
  • Security.framework is a good start. It seems I also need Xcode Application Tools. Still working on this. Lots of missing symbols at the link stage still. – William Jockusch Feb 02 '11 at 00:56
  • This is driving me absolutely mad. I cannot get rid of missing symbol errors. Here are the first two not-found symbols: _BIO_new and _BIO_ctrl. Can anyone clue me in. I have installed MacOS 10.6.7 and developer tools 1.1 and included the security and IOKit frameworks. No help. – William Jockusch Feb 03 '11 at 04:02
  • 1
    The symbols you’ve listed are exported by libcrypto, which is part of OpenSSL. Have you tried linking libcrypto as well? You can do that by editing your project settings, Build tab, Other Linker Flags, and specifying `-lcrypto` — http://stackoverflow.com/questions/4893333/mac-app-store-trying-to-get-encryption-to-work-what-am-i-missing –  Feb 04 '11 at 01:25
  • 1
    When you say don't use 'As is' what part of it can be altered changed? – strange Feb 06 '11 at 13:15
4

You could try NPReceiptVerification. It's the easiest way to add receipt verification to your app. You just add the class files to your project, set the version and bundle identifier, and everything else is handled automatically.

indragie
  • 18,002
  • 16
  • 95
  • 164
3

You can Refer the RVNReceiptValidation it is easy to implement. Just you have to set the Bundle id in RVNReceiptValidation.m file and version of your App. Remember to get the receipt from the apple you have to launch the app from the Finder. This Class also helps in the implementation of InApp Purchase.

Aravindhan
  • 15,608
  • 10
  • 56
  • 71
3

I reviewed Alan Quartermain's code and it looks good. Something to think about:

the last parameter here could/should be a compiled requirement stating that the code must be signed by YOUR certificate and no-one else's.

When the developer submits an app to the store for approval, the signing certificates are as follows:

3rd Party Mac Developer Application: me
Apple Worldwide Developer Relations Certification Authority
Apple Root CA

After the app is delivered from the App Store to the end user, the signing certificates are as follows:

Apple Mac OS Application Signing
Apple Worldwide Developer Relations Certification Authority
Apple Root CA

Also, I suggest only exit(173) when the receipt is missing, but everything else is in order.

C0C0AL0C0
  • 365
  • 3
  • 8
  • I'd really recommend doing some form of certificate checking, since that will help protect against hex editing the binary for you. OS X doesn't kill an app if its signature is invalid, it'll just restrict access to things like the keychain until a user agrees to such access. You canalways check whether the certificate matches either yours or the official one Apple uses to publish the app. – Jim Dovey Feb 01 '11 at 02:28
2

RVNReceiptValidation is great and it uses CommonCrypto rather than the now deprecated by Apple, openssl. you will have to attach a valid receipt to your project to debug it. Do this by getting a valid receipt from another app bundle and create a build phase in your test environment to add it to your bundle. I suggest the following techniques for obfuscation:

Encrypt the kRVNBundleID and kRVNBundleVersion and decrypt them when you compare them to the CFBundleIdentifier and CFBundleShortVersionString.

I create an array of function pointers with random values and change them to valid pointers to the functions in RVNReceiptValuation at run time before executing them using code like this:

static void testFunction(void);

typedef void (*functionPtr)(void);

functionPtr obfuscationArray[8] = {
    (functionPtr)0xA243F6A8,
    (functionPtr)0x885308D3,
    (functionPtr)0x13198A2E,
    (functionPtr)0x03707344,
    (functionPtr)0xA4093822,
    (functionPtr)0x299F31D0,
    (functionPtr)0x082EFA98,
    (functionPtr)0xEC4E6C89};

int main(int argc, const char * argv[]) {
    functionPtr myFuncPtr;

    obfuscationArray[3] = &testFunction;
    myFuncPtr = obfuscationArray[3];
    (myFuncPtr)();

    return 0;
}

static void testFunction(void){
    printf("function executed\n");
}
Billy Bob
  • 21
  • 2
2

I'd propose to implement the code verification routines as C functions, not ObjC methods.

This technique makes it (a bit) harder to locate receipt checking code, since fewer method-names get compiled into the binary.

SteAp
  • 11,853
  • 10
  • 53
  • 88
1

I'll elaborate on priller's answer. If Apple provided a code sample for the validation process then it would be very easy for a Bad Guy to take your compiled app and scan through it for the code corresponding to the validation process. The Bad Guy would know exactly what the compiled code looks like if you use a standard code sample from Apple. Once the Bad Guy has found that section of the code it is pretty trivial to modify the app's compiled code to just skip the receipt verification stage, rendering the entire thing useless.

All that said, a determined cracker is probably going to get around any copy protection you put in place regardless of what you do. The games industry (for example) spends a lot of time trying to protect their software, and cracked versions seem to always be available.

Pete Hodgson
  • 15,644
  • 5
  • 38
  • 46
1

When creating the sample receipt from Apple Docs, be sure not to include any extra characters after 'end' else the uudecode will fail.

pygar
  • 11
  • 1
0

roddi's ValidateStoreReceipt worked for me before, but it does not work any more. I wrote a blog post about the solution: http://vinceyuan.blogspot.com/2012/07/validate-mac-app-store-receipt-2012.html

Copied here: roddi's code is still working. You need not change it. (Just need to get the latest version) Follow these steps (internet required):

  1. Log out from Mac App Store app.
  2. Remove USE_SAMPLE_RECEIPT flag from your project settings -> Preprocessor Macros.
  3. Compile your project
  4. Find this app in Finder
  5. Double click it in Finder to run. Do not run it in Xcode.
  6. The OS will ask you to log in with your Apple ID. Do not log in with your real iTunes account. You need to log in with the test account. Find it or create it in the iTunesconnect website.
  7. The OS will say something like "Your app is broken. Download it in App Store". Ignore this message. If you "Show Package Contents" of this app in Finder, you will see there is a file _MASReceipt/receipt. The OS installed a development receipt. We will not need the old sample receipt any more. That's why we remove USE_SAMPLE_RECEIPT debugging flag.

Done. You can debug your app now.

Vince Yuan
  • 10,533
  • 3
  • 32
  • 27
0

Yes, in their docs it says, "It is important that you employ a solution that is unique to your application."

priller
  • 17
  • 1
0

Even with NPReceiptValidation you still should validate the security of your application bundle including the signing certificates. This is documented in the WWDR recommendations for developers.

A solution: http://itunes.apple.com/us/app/apptight-pro-app-store-code/id427083596?mt=12

One potential problem with NPReceiptValidation is that method selectors on Cocoa objects are very easy to hijack. It's the most popular way of extending apps.

Here's another tool for assisting with In-App purchase parsing:

http://itunes.apple.com/us/app/pkcs-7viewer/id547539804?mt=12

C0C0AL0C0
  • 365
  • 3
  • 8
  • You mention validating signing certificates mentioned in WWDR recommendations for developers. Can you please point me to these WWDR recommendations and how this is done. – AmaltasCoder Oct 20 '11 at 08:41
  • Starting with the Lion SDK there are APIs for getting the signing certificate and (under Mountain Lion) the trust certificate chain. Evaluate the SHA1 hash of the signing/trust certificates and compare them to the known (published) values of Apple's bona fide certificates. Your app won't be subject to the bypass done by the Russian. – C0C0AL0C0 Sep 02 '12 at 20:10