如今,不同系统平台都有专属的商店应用,Android平台有Google Play,Windows平台有Windows Store,iOS与macOS平台则有App Store。苹果公司的成功,很大程度上得益于该软件的生态环境App Store。
如何让系统上的软件开发人员真正地受益,是操作系统开发商需要关注的问题。只有系统平台上的软件丰富了,才能吸引更多的用户去使用该操作系统,而只有开发人员在系统上开发的软件能够赚到钱,他们才有动力去为系统开发更多更好的软件。苹果公司的Apple Store就曾经创造了无数软件开发人员的成功神话。而这一切背后,苹果公司首创的软件购买、免费软件内付费,都是它成功的关键所在。
App Store的内购又称为IAP(In-app Purchase),它是所有苹果商店内应用内付费软件使用的基础设施。对于软件开发人员,了解其使用方法与运行机制,对开发高质量的商业软件是很有帮助的。苹果公司没有给出IAP的具体技术细节,但在WWDC大会与SDK的开发文档中详细讲解了如何在软件中集成它。IAP技术基于苹果SDK中的Store Kit,它是系统中的一个框架StoreKit.framework。开发人员通过使用StoreKit提供的API来完成IAP的集成工作。整个框架的工作方式如图1所示。
图1 框架工作方式
苹果的应用内付费支持使用多种类型的程序。
为基本功能的软件提供付费后的功能更强大的专业版。
杂志类App购买成功后,支持订阅与下载。
免费游戏提供付费后等级解锁。
在线游戏通过付费购买道具或虚拟财产。
测试应用内付费软件的最简单方法就是下载应用内付费的应用,然后观察它们与其他应用之间的区别。由于集成了应用内付费功能的App,只能通过App Store来发布,因此在测试时,需要先从App Store中下载App。可以发现,通过App Store下载的程序与网络发布的程序最直观的不同是:在App Store中下载的程序,在app的Contents/_MASReceipt目录下会有一个receipt文件。其实,这是一个“凭证文件”,软件通过App Store发布成功后,苹果公司会为它维护一份凭证(Receipt),凭证信息以文件形式进行存储,该文件记录了以下信息。
Purchase Information。存放的软件的购买信息。包括软件的Bundle标识符、版本号、唯一标识以及这些属性值的SHA1哈希值。除此之外,它还包含软件的信用记录(Trusted record)与购买记录(PurchaseRecord)。这些数据使用ASN.1进行编码存放。所有这些信息被称为Receipt Payload。
Certificates。存放的AppleRoot CA。用于验证Receipt的签名信息。
Signature。签名信息。验证签名,可以检测当前的Receipt是否有效,或者是否已经更新了。苹果在开发文档中指出,开发人员应该在程序启动时,检测Receipt是否有效。如果无效,程序应该调用exit(173)退出,系统收到173退出码后,会自动联网请求去刷新Receipt。相应的Objective-C代码如下:
接下来看看如何在程序中集成StoreKit。图2所示是应用内付费的步骤,整个应用内付费的开发都围绕它展开。
图2 应用内付费的步骤
使用iTunes Connect创建并配置好产品信息后,就可以使用StoreKit提供的API与App Store进行交互了。从图中可以看出,整个交互过程一共发送了两次请求:一次是Makes Products Request,也就是构建产品请求。调用Store Kit向App Store发送产品请求,方法是构建一个SKProductsRequest对象的实例,该对象的作用是接收来自App Store返回的本地化产品信息列表。这些本地化的信息中包含了产品的本地化描述以及价格信息,用来展示给用户。
SKProductsRequest 构建完成后,再为它设置一个代理delegate,用来处理返回的信息。最后调用它的start()方法。从App Store中下载一个App,查看相应的伪代码,如下所示:
SKProductsRequest的setDelegate()设置了数据返回处理的代理。它是一个SKProducts RequestDelegate协议,用来处理服务器返回的SKProductsResponse对象。该协议有一个接口方法-productsRequest:didReceiveResponse:,用来处理返回的SKProductsResponse。调用它的products属性会返回一个SKProduct列表,解析列表中的产品信息,然后展示给用户。App Store中一个App的相应伪代码如下:
解析完产品信息,展示给用户。当用户选择好产品点击购买时,就会发出第2次请求:Makes Payment Request,也就是构建付款请求。该请求通过调用SKPaymentQueue的addPayment()方法,添加一个SKPayment对象。例如,某产品点击购买某功能选项的伪代码如下:
defaultQueue()方法返回一个单例的SKPaymentQueue实例,它是一个队列结构,由App Store去处理。操作完成后,产品支付请求就加入到支付队列中了。要想处理支付的状态,例如购买成功、购买失败、购买取消等处理的逻辑,就需要为队列添加一个观察者。当队列中交易的状态被更新,或者当交易从队列中删除的时候,观察者应该能正确及时地处理所有的交易信息,并根据交易的结果为购买成功的用户提供相应的功能。添加观察者的操作要在addPayment()调用前完成,通常是在程序的初始化时完成的,代码如下所示:
添加观察者对象使用addTransactionObserver()方法,它传入的是一个SKPaymentTransaction Observer协议对象,SKPaymentTransactionObserver协议有一系列方法被SKPaymentQueue调用。下面我们分别进行介绍。
1. 处理交易
处理交易包括-paymentQueue:updatedTransactions:与-paymentQueue:removedTransactions:方法。前者在一个或多个交易状态更新时被调用,在目标程序中,它必须实现;后者则在交易移除时被调用,在目标程序中,它的实现是可选的。
这两个方法传入的参数都是一个SKPaymentTransaction类型的数组,每一个SKPayment- Transaction代表着一个支付交易对象,应用程序要明确地处理每个交易对象的返回结果,根据它的transactionState属性来判断交易是否成功。如果transactionState的值是SKPayment- TransactionStatePurchased,则表示交易成功,此时程序应该向用户提供收费成功后的功能;如果transactionState的值为SKPaymentTransactionStateFailed,则表示交易失败,应用程序应该获取交易失败的错误信息并反馈给用户。交易的状态是一个枚举值,定义如下:
一个典型的 -paymentQueue:updatedTransactions: 代码的逻辑如下所示:
2. 处理恢复交易
恢复交易有两个方法,一个是交易成功后的处理,另一个是失败后的处理。它们分别是-paymentQueueRestoreCompletedTransactionsFinished:与-paymentQueue:restoreCompletedTransactionsFailedWithError:。恢复失败的原因通常是网络或本地的Receipt验证失败。一段典型的处理代码如下:
3. 处理下载动作
处理下载动作只有一个方法-paymentQueue:updatedDownloads:,而且它是可选的,只有提供付费下载与订阅的程序才需要实现它。
了解了API的使用方法,再来分析如何破解它就没那么困难了。首先,应该考虑的是如何做到通用破解,即破解IAP的机制后,可以将同类型的产品一次全部破解!这种想法并不是异想天开,在macOS 10.9系统以前,就曾经出现过这样的破解工具与方法。例如2012年7月,一位名叫Alexy的俄罗斯黑客公布了一个针对macOS系统上的IAP的破解方法。在本地系统中安装两张证书,然后使用一款名为Grim Receiper(死神)的工具,就可以一次性破解App Store中大量支持内购的程序。它的原理是将App Store内购请求的通信地址转向自己搭建的内购验证服务器上,使交易发生变化时transactionState的值永远是SKPaymentTransactionStatePurchased。这类破解行为对苹果公司与软件开发人员的打击是巨大的。之后,苹果公司为了阻止这类行为发生,采取了不少措施,包括联系Alexy网站的ISP关闭Alexy的网站,联系Paypal拒绝为Alexy的公开账号提供转帐服务,修补App Store的程序验证漏洞等。10.9版本后,Grim Receiper变得无效,但Alexy似乎并没有放弃对IAP破解的尝试。其实这位黑客还开发出了针对Android与iOS系统内购的破解工具。之后,Alexy使用比特币来收取世界各地人员的开发捐助,网站的ISP也改为了一个地下服务商。在macOS系统10.11初期,Alexy甚至成功开发出了内购破解工具。不过苹果公司一直没放弃对他的关注,很快就为破解的漏洞打上了补丁。目前,Alexy还在积极地尝试如何破解最新的IAP机制。GrimReceiper运行效果如图3所示。
图3 GrimReceiper运行效果
虽然做到通用破解有些难度,但针对个体内购机制的App破解,难度可能就没这么大了。一个典型的破解思路是:修改-paymentQueue:updatedTransactions:方法的代码逻辑,将交易transactionState为SKPaymentTransactionStateFailed时的代码逻辑改成SKPaymentTransaction- StatePurchased时的代码就可以了。而在具体执行破解操作时,可以使用爆破的手段在程序中进行修改,或者使用Hook技术,对方法的返回结果进行Hook。
内容摘自
作者:丰生强,邢俊杰
定价:79.00元
mac安全第一书,注重实战,干货满满
全面深入地分析了macOS系统中最新的软件安全、逆向分析与加密解密技术
软件安全专家一线实战经验,安全与开发人员案头必备技术专著
BAT、360等众多互联网公司的知名软件安全专家联袂推荐
广受读者喜爱的《Android软件安全与逆向分析》姊妹篇
本书共12 章,系统地讲解了软件安全相关的环境搭建、语言基础、文件格式、静态分析、 动态调试、破解与防破解、游戏安全、恶意软件等多个主题。深入介绍了macOS系统的软件安全、逆向分析与加密解密技术。
本书提倡读者多动手实践,除了系统地介绍命令,还使用了八十多个实用的第三方工具。便于读者理论与实践相结合。
书中会辅以大量实例,每个实例都是笔者精心编写、反复调试过的,并且都是开放源代码的,读者可以通过阅读这些实例的源代码来加深对技术的理解。
丰生强
网名非虫,独立软件安全研究员,资深安全专家,ISC2016安全训练营独立讲师,有着丰富的软件安全实战经验。自2008年起,在知名安全杂志《黑客防线》上发表多篇技术文章,从此踏上软件安全道路,常年混迹于国内各大软件安全论坛。著有畅销安全图书《Android软件安全与逆向分析》。
邢俊杰
资深程序员,软件安全爱好者,C++ Web框架Cinatra开发者,对编译器与调试器开发有着深入的研究。现就职于国内某互联网公司。业余时间喜欢研究软件与系统的底层,热爱读书与动画。
京东:
https://item.jd.com/12137171.html
亚马逊:
https://www.amazon.cn/dp/B074323CHX
当当:
http://product.dangdang.com/25119898.html
长按二维码跳转到京东购买
赠书活动
macOS系统的关注度在程序员群体中应该是非常高的。尤其是近年来,随着macOS系统的进一步普及,其安全形势也发生了一些变化。
这本书不仅仅是写给系统底层开发人员、逆向工程师和病毒分析师,它的更多读者是关注信息安全的程序员、学生群体等。
你关注过macOS系统的安全吗,或者你遇到过什么安全问题,或者你正在使用哪些特别值得推荐的第三方工具?评论区分享给读者。
精选评论挑选 3位 读者,每人在《macOS软件安全与逆向分析》《Android软件安全与逆向分析》中任选一本作为奖品。另外,根据后台时间先后,留言 第49楼 的读者将获得一本图书(少于此留言数,空缺)。截止8月3日14:00。
☟【阅读原文】查看源代码