目录

frida内购破解

frida 是一个非常强大的 hook 框架,多平台通吃,这篇文章演示一下 iOS 平台简单的内购破解。

思路

简单来说每次内购都会在 SKPaymentQueue 中 push 一个 SKPaymentTransaction 用来记录当前这次内购的基本信息,其中就会包括 transactionState 这个属性,它是用来存储内购结果的,所以最简单的情况下我们就只用拦截并修改这个属性的值就可以实现内购的破解了。

transactionState 是一个枚举类型,它的定义如下:

typedef NS_ENUM(NSInteger, SKPaymentTransactionState) {
    SKPaymentTransactionStatePurchasing, // 事务正在添加到队列中
    SKPaymentTransactionStatePurchased,  // 事务在队列中,用户已被收费。客户端应完成交易
    SKPaymentTransactionStateFailed,     // 在添加到队列前事务被取消或者失败
    SKPaymentTransactionStateRestored,   // 从用户购买的历史记录中恢复的事务。客户端应完成交易
    SKPaymentTransactionStateDeferred,   // 事务在队列中,但其最终状态是外部操作而挂起的
};

iOS 会通过 _setTransactionState 方法来设置 transactionState,所以我们只需要拦截 _setTransactionState 方法就可以了。而在 frida 的 object-c 中,枚举类型其实是一个以指针类型存在的下标,也就是说我们只需要在 _setTransactionState 调用之前把参数设置成 ptr('0x1') 就可以了。

操作

首先通过 frida-trace 注入目标 app:

frida-trace -U -m "-[SKPaymentTransaction _setTransactionState:]" app

这条命令会自动生成 js 脚本,默认路径是 .\__handlers__\SKPaymentTransaction\_setTransactionState_.js

修改 js 脚本的 onEnter 函数:

onEnter(log, args, state) {
  args[2] = ptr('0x1');// 拦截并修改返回结果为 SKPaymentTransactionStatePurchased
  log(`-[SKPaymentTransaction _setTransactionState:${args[2]}]`);
}

ctrl-c 退出 frida-trace 后重新注入 app 加载修改之后的脚本,然后在 app 中点击购买,弹出付款界面之后直接取消就可以成功实现内购。

注意

本方法仅适用于不验证的内置模式内购,并不支持所有 app,仅供学习参考,请勿用于非法用途。