├── .gitignore ├── .travis.yml ├── CIScripts └── InstallTheos.sh ├── LICENSE ├── Makefile ├── README.md ├── ReceiptServer ├── HTTPHandler.py ├── README.md ├── ReceiptServer.py ├── ReceiptValidation.py └── XAPFree.db ├── SKUtils.h ├── SKUtils.m ├── StoreKit.xm ├── ThirdPartyFWs.xm ├── Tweak.xm ├── XAPFree.plist ├── XAPSKDB.h ├── XAPSKDB.m ├── XAPSKPaymentTransactionObserver.h ├── XAPSKPaymentTransactionObserver.m ├── control └── layout └── Library └── PreferenceLoader └── Preferences ├── XAPFree.plist ├── XAPFree.png └── XAPFreePreferences.plist /.gitignore: -------------------------------------------------------------------------------- 1 | packages/* 2 | packages/ 3 | *.dylib 4 | *.o 5 | .theos/*.* 6 | .theos/* 7 | .theos/ 8 | obj/* 9 | *.pyc 10 | obj/ 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | sudo: true 3 | os: osx 4 | osx_image: xcode7.2 5 | before_install: 6 | - brew update 7 | - brew install ldid 8 | - brew install dpkg 9 | - clear 10 | - export THEOS=/opt/theos 11 | 12 | script: "./CIScripts/InstallTheos.sh &&make" -------------------------------------------------------------------------------- /CIScripts/InstallTheos.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | echo "export THEOS=/opt/theos" >>~/.bash_profile 3 | source ~/.bash_profile 4 | sudo git clone --recursive https://github.com/theos/theos.git $THEOS 5 | sudo git clone https://github.com/theos/headers.git $THEOS/Headers 6 | sudo cp -r $THEOS/Headers/ $THEOS/include 7 | sudo rm -rf $THEOS/Headers/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include $(THEOS)/makefiles/common.mk 2 | export ARCHS=armv7 armv7s arm64 3 | export CFLAGS=-Wp,"-DHostName=@\"$(hostname -s).local\"" 4 | TWEAK_NAME = XAPFree 5 | SUBSTRATE ?= yes 6 | XAPFree_FILES = Tweak.xm StoreKit.xm XAPSKPaymentTransactionObserver.m XAPSKDB.m ThirdPartyFWs.xm SKUtils.m 7 | ADDITIONAL_CFLAGS = -Wno-#warnings,-Wno-unused-function 8 | XAPFree_USE_SUBSTRATE = $(SUBSTRATE) 9 | include $(THEOS_MAKE_PATH)/tweak.mk 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #XAPFree 2 | 3 | This is my personal effort on making a IAP-Cracker for Mac OS X & iOS. This project is still at its very early stage. 4 | 5 | *Needs [Parasite](https://github.com/ParasiteTeam) For CodeInjection on OS X* 6 | 7 | **Make In-App-Purchase Great Again** 8 | 9 | #To-Do: 10 | 1. Local Receipt Validation Patch 11 | 2. ~~Hijack Requests To Apple Validation Server~~ *Half-Way Done. Only Hooked Older API* 12 | 3. Cross-Platform 13 | 4. Patch General IAP-Check Libraries 14 | 5. ~~Support Dumping/~~Reusing Legit Receipts (Replay Attack) 15 | 6. Daemon&IPC Techniques For 5 16 | 17 | #Build-Notes 18 | 19 | By default: 20 | 21 | ``` 22 | $(hostname -s).local 23 | 24 | ``` 25 | 26 | is used as the server address for XAPFree to POST Receipt data to. This can be overwritten by editing Makefile's CFLAGS Accordingly, or by editing *XAPSKDB.m* -------------------------------------------------------------------------------- /ReceiptServer/HTTPHandler.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import json 3 | import ReceiptValidation 4 | from SimpleHTTPServer import SimpleHTTPRequestHandler 5 | class HTTPHandler(SimpleHTTPRequestHandler): 6 | Connection=None 7 | def __init__(self,req,client_addr,server): 8 | SimpleHTTPRequestHandler.__init__(self,req,client_addr,server) 9 | def do_POST(self): 10 | print "Received POST Request" 11 | SQL = self.headers.getheader('SQL-Command', 0) 12 | BundleID=self.headers.getheader('BundleID', 0) 13 | Receipt=self.headers.getheader('Receipt', 0) 14 | if(ReceiptValidation.VerifyReceipt(Receipt)==False): 15 | print "Receipt Not Valid. Ignored" 16 | return; 17 | 18 | Info=self.headers.getheader('Info', 0) 19 | if(self.Connection==None): 20 | self.Connection=sqlite3.connect("XAPFree.db") 21 | self.Connection.cursor().execute(str(SQL)) 22 | DeleteSQL="DELETE FROM Receipts WHERE BundleID=\"{0}\" AND Receipt=\"{1}\"".format(BundleID,Receipt) 23 | self.Connection.cursor().execute(str(DeleteSQL)) 24 | InsertSQL="INSERT INTO Receipts(BundleID,Receipt,Info) VALUES(\"{0}\",\"{1}\",\"{2}\")".format(BundleID,Receipt,Info) 25 | self.Connection.cursor().execute(str(InsertSQL)) 26 | self.Connection.commit() 27 | def do_GET(self): 28 | print "Received GET Request" 29 | bundleID=self.headers.getheader('BundleID', 0) 30 | if(self.Connection==None): 31 | self.Connection=sqlite3.connect("XAPFree.db") 32 | ResultList=list() 33 | SQL = self.headers.getheader('SQL-Command', 0) 34 | self.Connection.cursor().execute(str(SQL)) 35 | 36 | SQLQuery='SELECT * from Receipts WHERE BundleID=\"'+bundleID+"\"" 37 | for item in self.Connection.cursor().execute(str(SQLQuery)): 38 | TempDict=dict() 39 | TempDict["Receipt"]=item[1] 40 | TempDict["Info"]=item[2] 41 | ResultList.append(TempDict) 42 | self.send_response(200) 43 | self.send_header("Content-type", "text/html") 44 | self.end_headers() 45 | self.wfile.write(json.dumps(ResultList)) 46 | -------------------------------------------------------------------------------- /ReceiptServer/README.md: -------------------------------------------------------------------------------- 1 | #RemoteLogger 2 | 3 | Run ./LoggingServer.py Before Starting WTFJH 4 | -------------------------------------------------------------------------------- /ReceiptServer/ReceiptServer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import SimpleHTTPServer 3 | import SocketServer 4 | import HTTPHandler 5 | 6 | PORT = 8000 7 | 8 | Handler = HTTPHandler.HTTPHandler 9 | 10 | print "XAPFree ReceiptServer Loading" 11 | 12 | httpd = SocketServer.TCPServer(("", PORT), Handler) 13 | httpd.serve_forever() -------------------------------------------------------------------------------- /ReceiptServer/ReceiptValidation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import json 3 | import base64 4 | from sys import argv 5 | import sys 6 | import requests 7 | import plistlib 8 | def VerifyReceipt(Receipt): 9 | Request=dict() 10 | Request["receipt-data"]=base64.b64encode(Receipt) 11 | HTTPBody=json.dumps(Request) 12 | rEQ = requests.post('https://buy.itunes.apple.com/verifyReceipt', data=HTTPBody) 13 | if(int(rEQ.json()["status"])==0): 14 | return True 15 | else: 16 | return False -------------------------------------------------------------------------------- /ReceiptServer/XAPFree.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Naville/XAPFree/f92b3c684d84e4cfb0ba0b774fe7f44a272bc574/ReceiptServer/XAPFree.db -------------------------------------------------------------------------------- /SKUtils.h: -------------------------------------------------------------------------------- 1 | #import 2 | @interface SKUtils:NSObject 3 | +(instancetype)sharedInstance; 4 | -(void)RefreshReceipt; 5 | @end -------------------------------------------------------------------------------- /SKUtils.m: -------------------------------------------------------------------------------- 1 | #import "SKUtils.h" 2 | @implementation SKUtils 3 | +(instancetype)sharedInstance{ 4 | static dispatch_once_t p = 0; 5 | __strong static id _sharedObject = nil; 6 | dispatch_once(&p, ^{ 7 | _sharedObject = [[self alloc] init]; 8 | }); 9 | return _sharedObject; 10 | } 11 | - (void)requestDidFinish:(SKRequest *)request{ 12 | NSLog(@"SKUtils-requestDidFinish:%@",[(SKReceiptRefreshRequest*)request receiptProperties]); 13 | 14 | } 15 | -(void)RefreshReceipt{ 16 | SKReceiptRefreshRequest* SKRR=[[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil]; 17 | SKRR.delegate=self; 18 | [SKRR start]; 19 | 20 | } 21 | @end -------------------------------------------------------------------------------- /StoreKit.xm: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "XAPSKPaymentTransactionObserver.h" 4 | #import "SKUtils.h" 5 | extern void init_ThirdPartyFWs(); 6 | static XAPSKPaymentTransactionObserver* Observer=nil; 7 | static NSData* DefaultValidReceipt=[[NSData alloc] initWithBase64EncodedString:@"ewoJInNpZ25hdHVyZSIgPSAiQXBkeEpkdE53UFUyckE1L2NuM2tJTzFPVGsyNWZlREthMGFhZ3l5UnZlV2xjRmxnbHY2UkY2em5raUJTM3VtOVVjN3BWb2IrUHFaUjJUOHd5VnJITnBsb2YzRFgzSXFET2xXcSs5MGE3WWwrcXJSN0E3ald3dml3NzA4UFMrNjdQeUhSbmhPL0c3YlZxZ1JwRXI2RXVGeWJpVTFGWEFpWEpjNmxzMVlBc3NReEFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NHVVVrVTNaV0FTMU1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEE1TURZeE5USXlNRFUxTmxvWERURTBNRFl4TkRJeU1EVTFObG93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNclJqRjJjdDRJclNkaVRDaGFJMGc4cHd2L2NtSHM4cC9Sd1YvcnQvOTFYS1ZoTmw0WElCaW1LalFRTmZnSHNEczZ5anUrK0RyS0pFN3VLc3BoTWRkS1lmRkU1ckdYc0FkQkVqQndSSXhleFRldngzSExFRkdBdDFtb0t4NTA5ZGh4dGlJZERnSnYyWWFWczQ5QjB1SnZOZHk2U01xTk5MSHNETHpEUzlvWkhBZ01CQUFHamNqQndNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVOaDNvNHAyQzBnRVl0VEpyRHRkREM1RllRem93RGdZRFZSMFBBUUgvQkFRREFnZUFNQjBHQTFVZERnUVdCQlNwZzRQeUdVakZQaEpYQ0JUTXphTittVjhrOVRBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUVhU2JQanRtTjRDL0lCM1FFcEszMlJ4YWNDRFhkVlhBZVZSZVM1RmFaeGMrdDg4cFFQOTNCaUF4dmRXLzNlVFNNR1k1RmJlQVlMM2V0cVA1Z204d3JGb2pYMGlreVZSU3RRKy9BUTBLRWp0cUIwN2tMczlRVWU4Y3pSOFVHZmRNMUV1bVYvVWd2RGQ0TndOWXhMUU1nNFdUUWZna1FRVnk4R1had1ZIZ2JFL1VDNlk3MDUzcEdYQms1MU5QTTN3b3hoZDNnU1JMdlhqK2xvSHNTdGNURXFlOXBCRHBtRzUrc2s0dHcrR0szR01lRU41LytlMVFUOW5wL0tsMW5qK2FCdzdDMHhzeTBiRm5hQWQxY1NTNnhkb3J5L0NVdk02Z3RLc21uT09kcVRlc2JwMGJzOHNuNldxczBDOWRnY3hSSHVPTVoydG04bnBMVW03YXJnT1N6UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREV5TFRBM0xURXlJREExT2pVME9qTTFJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakV6TkRJd09UYzJOelU0T0RJaU93b0pJbTl5YVdkcGJtRnNMWFJ5WVc1ellXTjBhVzl1TFdsa0lpQTlJQ0l4TnpBd01EQXdNamswTkRrME1qQWlPd29KSW1KMmNuTWlJRDBnSWpFdU5DSTdDZ2tpWVhCd0xXbDBaVzB0YVdRaUlEMGdJalExTURVME1qSXpNeUk3Q2draWRISmhibk5oWTNScGIyNHRhV1FpSUQwZ0lqRTNNREF3TURBeU9UUTBPVFF5TUNJN0Nna2ljWFZoYm5ScGRIa2lJRDBnSWpFaU93b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGJYTWlJRDBnSWpFek5ESXdPVGMyTnpVNE9ESWlPd29KSW1sMFpXMHRhV1FpSUQwZ0lqVXpOREU0TlRBME1pSTdDZ2tpZG1WeWMybHZiaTFsZUhSbGNtNWhiQzFwWkdWdWRHbG1hV1Z5SWlBOUlDSTVNRFV4TWpNMklqc0tDU0p3Y205a2RXTjBMV2xrSWlBOUlDSmpiMjB1ZW1Wd2RHOXNZV0l1WTNSeVltOXVkWE11YzNWd1pYSndiM2RsY2pFaU93b0pJbkIxY21Ob1lYTmxMV1JoZEdVaUlEMGdJakl3TVRJdE1EY3RNVElnTVRJNk5UUTZNelVnUlhSakwwZE5WQ0k3Q2draWIzSnBaMmx1WVd3dGNIVnlZMmhoYzJVdFpHRjBaU0lnUFNBaU1qQXhNaTB3TnkweE1pQXhNam8xTkRvek5TQkZkR012UjAxVUlqc0tDU0ppYVdRaUlEMGdJbU52YlM1NlpYQjBiMnhoWWk1amRISmxlSEJsY21sdFpXNTBjeUk3Q2draWNIVnlZMmhoYzJVdFpHRjBaUzF3YzNRaUlEMGdJakl3TVRJdE1EY3RNVElnTURVNk5UUTZNelVnUVcxbGNtbGpZUzlNYjNOZlFXNW5aV3hsY3lJN0NuMD0iOwoJInBvZCIgPSAiMTciOwoJInNpZ25pbmctc3RhdHVzIiA9ICIwIjsKfQ==" options:0]; 8 | static NSData* GenerateAppleVerifyResponse(NSURLRequest* Request){ 9 | #ifdef DEBUG 10 | NSLog(@"Start GenerateAppleVerifyResponse"); 11 | #endif 12 | if(Request==nil){ 13 | return nil; 14 | } 15 | @autoreleasepool{ 16 | NSMutableDictionary* GeneratedDict=[NSMutableDictionary dictionary]; 17 | NSError* Err=nil; 18 | [GeneratedDict setObject:[NSNumber numberWithInt:0] forKey:@"status"]; 19 | [GeneratedDict setObject:@"Production" forKey:@"environment"]; 20 | NSMutableDictionary* ReceiptDict=[NSMutableDictionary dictionary]; 21 | [ReceiptDict setObject:[[NSBundle mainBundle] bundleIdentifier] forKey:@"bundle_id"]; 22 | #ifdef TARGET_OS_MAC 23 | [ReceiptDict setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] forKey:@"application_version"]; 24 | [ReceiptDict setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] forKey:@"original_application_version"]; 25 | #elif defined TARGET_OS_IOS 26 | [ReceiptDict setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:@"application_version"]; 27 | [ReceiptDict setObject:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] forKey:@"original_application_version"]; 28 | #endif 29 | 30 | [GeneratedDict setObject:ReceiptDict forKey:@"receipt"]; 31 | 32 | if(Err!=nil){ 33 | #ifdef DEBUG 34 | NSLog(@"GenerateAppleVerifyResponse Error:%@",Err.localizedDescription); 35 | #endif 36 | return nil; 37 | } 38 | else{ 39 | #ifdef DEBUG 40 | NSLog(@"GenerateAppleVerifyResponse Successfully"); 41 | #endif 42 | return [NSJSONSerialization dataWithJSONObject:GeneratedDict 43 | options:0 44 | error:nil]; 45 | } 46 | }//End AutoReleasePOOL 47 | } 48 | 49 | 50 | %group UniversalSK 51 | %hook SKPaymentTransaction 52 | -(SKPaymentTransactionState)transactionState{ 53 | return SKPaymentTransactionStatePurchased; 54 | } 55 | -(NSData *)transactionReceipt{ 56 | return DefaultValidReceipt; 57 | } 58 | -(NSDate*)transactionDate{ 59 | return [NSDate date]; 60 | 61 | } 62 | -(NSString *)transactionIdentifier{ 63 | return @"SheisMyWiFi"; 64 | } 65 | %end 66 | %hook NSURLConnection 67 | +(NSData*)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse*)response error:(NSError **)Error{ 68 | #ifdef DEBUG 69 | NSLog(@"[NSURLConnection )sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse*)response error:]"); 70 | #endif 71 | NSData *origResult = %orig(request, response, Error); 72 | if([request.URL.absoluteString isEqualToString:@"https://buy.itunes.apple.com/verifyReceipt"]){ 73 | NSData* GeneratedResponse=GenerateAppleVerifyResponse(request); 74 | if(GeneratedResponse==nil || GeneratedResponse.length==0){ 75 | #ifdef DEBUG 76 | NSLog(@"NSURLLevelHook-GeneratedResponse isEmpty"); 77 | #endif 78 | [GeneratedResponse release]; 79 | return origResult; 80 | } 81 | else{ 82 | #ifdef DEBUG 83 | NSLog(@"NSURLLevelHook-Returning GeneratedResponse"); 84 | #endif 85 | [origResult release]; 86 | return GeneratedResponse; 87 | } 88 | } 89 | else{ 90 | #ifdef DEBUG 91 | NSLog(@"NSURLLevelHook- %@ Is Not Apple Verification Server",request.URL.absoluteString); 92 | #endif 93 | return origResult; 94 | } 95 | } 96 | %end 97 | 98 | 99 | %end 100 | void init_StoreKit(BOOL Preferences){ 101 | if(Preferences==YES){ 102 | #ifdef DEBUG 103 | NSLog(@"init_StoreKit. Dumping Receipts Only"); 104 | #endif 105 | //Init Legit-Receipt Dumps 106 | [[SKUtils sharedInstance] RefreshReceipt]; 107 | Observer=[XAPSKPaymentTransactionObserver sharedInstance]; 108 | [[SKPaymentQueue defaultQueue] addTransactionObserver:Observer]; 109 | return ; 110 | } 111 | else{ 112 | #ifdef DEBUG 113 | NSLog(@"init_StoreKit. Moving Old Receipt"); 114 | #endif 115 | [[NSFileManager defaultManager] moveItemAtURL:[[NSBundle mainBundle] appStoreReceiptURL] 116 | toURL:[NSURL URLWithString:[[[NSBundle mainBundle] appStoreReceiptURL].path stringByAppendingString:@"XAPFreeBackup"]] 117 | error:nil]; 118 | #ifdef DEBUG 119 | NSLog(@"init_StoreKit. Init Universal Hooks"); 120 | #endif 121 | %init(UniversalSK);//Apply Receipt URL hooks before writing 122 | init_ThirdPartyFWs(); 123 | #ifdef DEBUG 124 | NSLog(@"init_StoreKit. Writing DefaultValidReceipt"); 125 | #endif 126 | GenerateAppleVerifyResponse(nil);//Because Compiler Won't shut up 127 | [DefaultValidReceipt writeToURL:[[NSBundle mainBundle] appStoreReceiptURL] atomically:YES]; 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /ThirdPartyFWs.xm: -------------------------------------------------------------------------------- 1 | %group TPFW 2 | %hook IAPManager 3 | - (BOOL)hasPurchased:(NSString *)productID{ 4 | return YES; 5 | } 6 | %end 7 | %end 8 | 9 | 10 | 11 | 12 | 13 | void init_ThirdPartyFWs(){ 14 | 15 | %init(TPFW); 16 | } -------------------------------------------------------------------------------- /Tweak.xm: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | #define preferenceFilePath @"/var/mobile/Library/Preferences/naville.xapfree.plist" 4 | extern void init_StoreKit(BOOL Preferences); 5 | /* 6 | NSMutableArray* FindClassForProtocal(Protocol* Proto){ 7 | @autoreleasepool{ 8 | NSMutableArray* NSClassList=[NSMutableArray array]; 9 | int ClassCount=objc_getClassList(NULL, 0); 10 | 11 | Class *classList = (Class*)malloc( ClassCount * sizeof(Class)); 12 | objc_getClassList( classList, ClassCount ); 13 | for (int Index=0;Index 2 | @interface XAPSKDB:NSObject 3 | +(instancetype)sharedInstance; 4 | -(NSArray*)LoadReceiptForBundleID:(NSString*)bundleID; 5 | -(void)SaveReceiptForBundleID:(NSString*)bundleID withInfoArray:(NSArray*)InfoArray; 6 | @end -------------------------------------------------------------------------------- /XAPSKDB.m: -------------------------------------------------------------------------------- 1 | #import "XAPSKDB.h" 2 | @implementation XAPSKDB 3 | +(instancetype)sharedInstance{ 4 | static dispatch_once_t p = 0; 5 | __strong static id _sharedObject = nil; 6 | dispatch_once(&p, ^{ 7 | _sharedObject = [[self alloc] init]; 8 | }); 9 | return _sharedObject; 10 | } 11 | -(NSArray*)LoadReceiptForBundleID:(NSString*)bundleID{ 12 | NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:8000",HostName]]; 13 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; 14 | [request setHTTPMethod:@"GET"]; 15 | [request setValue:@"CREATE TABLE IF NOT EXISTS Receipts (BundleID TEXT,Receipt TEXT,Info TEXT)" forHTTPHeaderField:@"SQL-Command"]; 16 | [request setValue:bundleID forHTTPHeaderField:@"BundleID"]; 17 | NSData* ReplyData=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; 18 | 19 | NSArray* RetVal=[NSJSONSerialization JSONObjectWithData:ReplyData options:0 error:nil]; 20 | 21 | 22 | [request release]; 23 | [url release]; 24 | return RetVal; 25 | return nil; 26 | } 27 | -(void)SaveReceiptForBundleID:(NSString*)bundleID withInfoArray:(NSArray*)InfoArray{ 28 | for (NSDictionary* Item in InfoArray){ 29 | NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:8000",HostName]]; 30 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; 31 | [request setHTTPMethod:@"POST"]; 32 | [request setValue:@"CREATE TABLE IF NOT EXISTS Receipts (BundleID TEXT,Receipt TEXT,Info TEXT)" forHTTPHeaderField:@"SQL-Command"]; 33 | [request setValue:bundleID forHTTPHeaderField:@"BundleID"]; 34 | [request setValue:[(NSData*)[Item objectForKey:@"transactionReceipt"] base64EncodedStringWithOptions:0] forHTTPHeaderField:@"Receipt"]; 35 | 36 | NSMutableDictionary* InfoDict=[NSMutableDictionary dictionaryWithDictionary:Item]; 37 | [InfoDict removeObjectForKey:@"transactionReceipt"]; 38 | NSString* JSON=[[NSJSONSerialization dataWithJSONObject:InfoDict options:NSJSONWritingPrettyPrinted error:nil] base64EncodedStringWithOptions:0]; 39 | [request setValue:JSON forHTTPHeaderField:@"Info"]; 40 | [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; 41 | [request release]; 42 | [JSON release]; 43 | [InfoDict release]; 44 | [url release]; 45 | } 46 | [InfoArray release]; 47 | } 48 | @end -------------------------------------------------------------------------------- /XAPSKPaymentTransactionObserver.h: -------------------------------------------------------------------------------- 1 | #import 2 | @interface XAPSKPaymentTransactionObserver:NSObject 3 | +(instancetype)sharedInstance; 4 | @end -------------------------------------------------------------------------------- /XAPSKPaymentTransactionObserver.m: -------------------------------------------------------------------------------- 1 | #import "XAPSKPaymentTransactionObserver.h" 2 | 3 | #import "XAPSKDB.h" 4 | @implementation XAPSKPaymentTransactionObserver 5 | 6 | - (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue{ 7 | #ifdef DEBUG 8 | NSLog(@"[XAPSKPaymentTransactionObserver paymentQueueRestoreCompletedTransactionsFinished:%@]",queue); 9 | #endif 10 | @autoreleasepool{ 11 | NSMutableArray* GlobalArray=[NSMutableArray array]; 12 | for (SKPaymentTransaction * SKPT in queue.transactions){ 13 | NSMutableDictionary* PerTransactionDict=[NSMutableDictionary dictionary]; 14 | SKPaymentTransaction * OriginalSKPT=nil; 15 | if(SKPT.transactionState==SKPaymentTransactionStatePurchased){ 16 | OriginalSKPT=SKPT; 17 | } 18 | else if(SKPT.transactionState==SKPaymentTransactionStateRestored){ 19 | OriginalSKPT=SKPT.originalTransaction; 20 | } 21 | if(OriginalSKPT!=nil){//Restored/Purchased. Save Receipts 22 | @try{ 23 | [PerTransactionDict setObject:OriginalSKPT.transactionReceipt forKey:@"transactionReceipt"]; 24 | } 25 | @catch (NSException *exception){ 26 | NSData* ReceiptData=[NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; 27 | if(ReceiptData!=nil){ 28 | [PerTransactionDict setObject:ReceiptData forKey:@"transactionReceipt"]; 29 | } 30 | } 31 | [PerTransactionDict setObject:OriginalSKPT.transactionIdentifier forKey:@"transactionIdentifier"]; 32 | [PerTransactionDict setObject:OriginalSKPT.transactionDate forKey:@"transactionDate"]; 33 | [GlobalArray addObject:PerTransactionDict]; 34 | 35 | } 36 | 37 | 38 | }//End For Loop 39 | [[XAPSKDB sharedInstance] SaveReceiptForBundleID:[[NSBundle mainBundle] bundleIdentifier] withInfoArray:GlobalArray]; 40 | }//End AutoReleasePool 41 | 42 | } 43 | - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{ 44 | @autoreleasepool{ 45 | #ifdef DEBUG 46 | NSLog(@"[XAPSKPaymentTransactionObserver paymentQueue:%@ updatedTransactions:%@]",queue,transactions); 47 | #endif 48 | NSMutableArray* GlobalArray=[NSMutableArray array]; 49 | for (SKPaymentTransaction * SKPT in transactions){ 50 | NSMutableDictionary* PerTransactionDict=[NSMutableDictionary dictionary]; 51 | SKPaymentTransaction * OriginalSKPT=nil; 52 | if(SKPT.transactionState==SKPaymentTransactionStatePurchased){ 53 | #ifdef DEBUG 54 | NSLog(@"[XAPSKPaymentTransactionObserver paymentQueue: updatedTransactions:] Status:SKPaymentTransactionStatePurchased"); 55 | #endif 56 | OriginalSKPT=SKPT; 57 | } 58 | else if(SKPT.transactionState==SKPaymentTransactionStateRestored){ 59 | #ifdef DEBUG 60 | NSLog(@"[XAPSKPaymentTransactionObserver paymentQueue: updatedTransactions:] Status:SKPaymentTransactionStateRestored"); 61 | #endif 62 | OriginalSKPT=SKPT.originalTransaction; 63 | } 64 | if(OriginalSKPT!=nil){//Restored/Purchased. Save Receipts 65 | [PerTransactionDict setObject:OriginalSKPT.transactionIdentifier forKey:@"transactionIdentifier"]; 66 | [PerTransactionDict setObject:OriginalSKPT.transactionDate forKey:@"transactionDate"]; 67 | if(OriginalSKPT.transactionReceipt!=nil){ 68 | [PerTransactionDict setObject:OriginalSKPT.transactionReceipt forKey:@"transactionReceipt"]; 69 | } 70 | else{ 71 | [PerTransactionDict setObject:[NSData data] forKey:@"transactionReceipt"]; 72 | } 73 | [GlobalArray addObject:PerTransactionDict]; 74 | 75 | } 76 | 77 | 78 | }//End For Loop 79 | [[XAPSKDB sharedInstance] SaveReceiptForBundleID:[[NSBundle mainBundle] bundleIdentifier] withInfoArray:GlobalArray]; 80 | }//End AutoReleasePool 81 | } 82 | /*- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue{ 83 | 84 | }*/ 85 | +(instancetype)sharedInstance{ 86 | static dispatch_once_t p = 0; 87 | __strong static id _sharedObject = nil; 88 | dispatch_once(&p, ^{ 89 | _sharedObject = [[self alloc] init]; 90 | }); 91 | return _sharedObject; 92 | } 93 | 94 | 95 | @end -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: naville.xapfree 2 | Name: XAPFree 3 | Depends: mobilesubstrate 4 | Version: 1.0.1 5 | Architecture: iphoneos-arm 6 | Description: Open-Source In-App-Purchase Hacking ToolKit 7 | Maintainer: Naville 8 | Author: Naville 9 | Section: Tweaks 10 | -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/XAPFree.plist: -------------------------------------------------------------------------------- 1 | entry = { 2 | bundle = AppList; 3 | cell = PSLinkCell; 4 | icon = "/Library/PreferenceLoader/Preferences/XAPFree.png"; 5 | isController = 1; 6 | label = "XAPFree - Apps"; 7 | ALSettingsPath = "/var/mobile/Library/Preferences/naville.xapfree.applist.plist"; 8 | ALSettingsKeyPrefix = ""; 9 | ALChangeNotification = "naville.xapfree"; 10 | ALAllowsSelection = 1; 11 | ALSectionDescriptors = ( 12 | { 13 | title = "User Applications Profiling"; 14 | predicate = "isSystemApplication = FALSE"; 15 | "cell-class-name" = "ALSwitchCell"; 16 | "icon-size" = 29; 17 | "suppress-hidden-apps" = 1; 18 | } 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/XAPFree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Naville/XAPFree/f92b3c684d84e4cfb0ba0b774fe7f44a272bc574/layout/Library/PreferenceLoader/Preferences/XAPFree.png -------------------------------------------------------------------------------- /layout/Library/PreferenceLoader/Preferences/XAPFreePreferences.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | entry 6 | 7 | cell 8 | PSLinkCell 9 | icon 10 | XAPFree.png 11 | label 12 | XAPFree - Settings 13 | 14 | items 15 | 16 | 17 | cell 18 | PSSwitchCell 19 | default 20 | 21 | defaults 22 | naville.xapfree 23 | key 24 | StoreKit 25 | label 26 | Enable CoreXAPFree 27 | 28 | 29 | cell 30 | PSSwitchCell 31 | default 32 | 33 | defaults 34 | naville.xapfree 35 | key 36 | ReceiptDump 37 | label 38 | Enable Dumping Legit Receipts 39 | 40 | 41 | cell 42 | PSGroupCell 43 | isStaticText 44 | 45 | label 46 | XAPFree © NavilleZhang 47 | 48 | 49 | title 50 | WTFJH 51 | 52 | 53 | --------------------------------------------------------------------------------