通过逆向未来学到了很多的知识,为了感谢法总,回馈逆向未来社区,把自己逆向的iOS内购破解的过程分享给大家。
如果你指望这篇文章能带来灰色收入,看到这里就不用往下继续了,如果想学习逆向技术和分析思路,可以尽情提问,我尽量回答。
这篇文章不是iOS逆向入门教程,需要有一定的iOS逆向思路才能看的明白。下面进入正文。
昨天晚上玩了一个捕鱼的游戏,里面有从APPStore购买金币的功能。我想了想看看能不能成功欺骗客户端的数据,然后让服务器给我加点金币。
晚上想了想思路,今天上班的时候凑空闲搞了搞。废话不多说,直接上过程。
1.首先把游戏下载到手机,然后使用dumpdecryted进行砸壳。
2.把砸好壳的文件拖入到电脑中,使用class-dump 导出头文件。
3.熟悉iOS内购流程的人都应该知道,StoreKit去APPStore 请求商品信息,然后会通过paymentQueue:updatedTransactions:这个函数回调给客户端,于是搜索了一下导出的头文件,发现iAPTransactionObserver 该类中实现了paymentQueue:updatedTransactions:方法,该方法第一个参数不用管,第二个参数是一个NSArray,里面放的是SKPaymentTransaction 对象。SKPaymentTransaction对象中有几个比较关键的信息如下图所示:
[Objective-C] 纯文本查看 复制代码 // Only set if state is SKPaymentTransactionFailed
@property(nonatomic, readonly, nullable) NSError *error NS_AVAILABLE_IOS(3_0);
// Only valid if state is SKPaymentTransactionStateRestored.
@property(nonatomic, readonly, nullable) SKPaymentTransaction *originalTransaction NS_AVAILABLE_IOS(3_0);
@property(nonatomic, readonly) SKPayment *payment NS_AVAILABLE_IOS(3_0);
// Available downloads (SKDownload) for this transaction
@property(nonatomic, readonly) NSArray<SKDownload *> *downloads NS_AVAILABLE_IOS(6_0);
// The date when the transaction was added to the server queue. Only valid if state is SKPaymentTransactionStatePurchased or SKPaymentTransactionStateRestored.
@property(nonatomic, readonly, nullable) NSDate *transactionDate NS_AVAILABLE_IOS(3_0);
// The unique server-provided identifier. Only valid if state is SKPaymentTransactionStatePurchased or SKPaymentTransactionStateRestored.
@property(nonatomic, readonly, nullable) NSString *transactionIdentifier NS_AVAILABLE_IOS(3_0);
// Only valid if state is SKPaymentTransactionStatePurchased.
@property(nonatomic, readonly, nullable) NSData *transactionReceipt NS_DEPRECATED_IOS(3_0, 7_0, "Use -[NSBundle appStoreReceiptURL]");
@property(nonatomic, readonly) SKPaymentTransactionState transactionState NS_AVAILABLE_IOS(3_0);
transactionState这个状态值是客户端判断的关键,该状态值是枚举定义,如果该值为SKPaymentTransactionStatePurchased(1),这表示购买成功,那么现在已经找到突破点了,就是原本点击购买然后取消操作之后,返回的状态值为SKPaymentTransactionStateFailed(2),将其修改为成功标志,基本就可以讲客户端欺骗了。但是我发现SKPaymentTransaction对象的属性基本都是readonly的,于是想到了苹果的runtime机制。既然直接复制不行,那我只能用setValue:forKeyPath: 这个方法来对其属性进行修改了。为了便于测试,我加入了UIAlertView来判断是否被成功调用
下面贴上tweak的代码:
[C++] 纯文本查看 复制代码 %hook iAPTransactionObserver
- (void)paymentQueue:(id)queue updatedTransactions:(id)transactions{
for (int i = 0; i < [(NSArray *)transactions count]; ++i){
id transaction = [transactions objectAtIndex:i];
NSNumber *status = [transaction valueForKeyPath:@"transactionState"];
NSString *title = [NSString stringWithFormat:@"title: %d",[status intValue]];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:@"Money" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[alertView show];
[alertView release];
if([status intValue] == 2){
[transaction setValue:@"1" forKeyPath:@"transactionState"];
NSString *dataString = @"success!";
NSData *data = [NSData dataWithBytes:[dataString UTF8String] length:[dataString length]];
[transaction setValue:data forKeyPath:@"transactionReceipt"];
}
}
%orig;
}
%end
运行结果如下图:
刚开始调用StorKit的时候:
弹出APPStore支付信息,点击取消的时候:
同时,对方的服务器和APPStore进行了校验,最后的结果是:
哎呀~~发财梦白做了哈哈。以上就是整个逆向分析的过程,其中一部分arm汇编的东西没往上贴,感觉意义不大,看着还头疼。希望大家能从这里借鉴到思路。
|