2020年 IOS 逆向 反编译 注入修改游戏或APP的调用参数新手系列教程——使用theos tweak 注入hook修改游戏执行代码上传动态头像

开篇

太懒这篇其实文章很早就该写的了,一方面记录一下自己的代码另一方面分享。

需求&最终效果

游戏本身不支持上传gif动态图片,上传前图片会客户端会做截图处理,通过注入修改代码,成功上传动图。

环境要求与即将使用的工具

之前我的MacOSX版本是10.10,只能安装xcode7以下的版本,xcode7以下的版本没有真机调试功能,于是升级了版本到MacOSX Catalina 10.15.4也安装了最新版xcode,但是这个版本太卡而且有些问题,很多软件兼容不了了,想以后降级。

环境 版本
操作系统 MacOSX Catalina 10.15.4 版本太新了不太好用很多工具用不了,我后面打算降级
手机系统 Iphone7 IOS11 需要越狱
mac上面的 theos 最新版
手机助手传输工具或SSH连接操作 -
xcode 11.5
手机端FLEXible(具备浏览appUI结构和APP抓包功能);或者用charles来抓包 -
mac 上面的 hopper disassembler反编译工具 -

工具介绍

theos是用来编写tweak代码并且打包成deb包的工具。
FLEXible是手机端调试神器,可以查看APP的UI层级和代码头文件的方法和变量,更有抓包的功能!

ios11的ssh本人用不了,从cydia安装了openssh,但是用命令行执行ssh报无法打开二进制文件的英文错误,不知道为何,谁能在ios11运行ssh并且电脑连接手机ssh的麻烦告知我一下谢谢。

实现过程

思路

思路a:首先我要找到上传图片的POST请求,然后把上传图片参数的图片url地址改掉。
思路a行不通,因为上传的时候游戏客户端对全部参数通过加密算法计算出了一个sign的参数提供给后台,如果修改了其中任意参数会导致后台验证签名失败。
思路b:在本地上传图片裁剪窗口点击保存图片的的时候把本地图片路径改成我指定的路径。
思路b可能可行但是我想还有更简单的做法。
思路c:在上传前对组装的参数数组的代码注入,强行改变图片参数的url。也不会影响sign的生成正确逻辑。
思路d:反编译看汇编代码逆向sign参数的加密过程。这个难度比较大,耗时,但是如果把sign的生成过程解密了基本所有请求就都可以通过中间人攻击来伪造数据。
思路c会简单一点。用思路c的做法来做

理清图片上传请求过程

可以先通过FLEXible来看看目录结构,有哪些控制器类和方法大致看看。



其实也可以不用看层级结构,我只要让图片上传触发抓包就行。
打开FLEXible的menu再选择Network History ,点击一下Settings把抓包功能开启。
然后返回游戏界面随便上传一张图片,再打开Network查看抓包列表。

可以看到两个比较可疑的POST请求就是最前面两个:upload.qiniup.com和update_user_info
打开详细查看

图片看不全,我还是copy成curl贴出来吧。
如果不知道curl的同学可以自行去其他地方了解一下。

curl -v -X POST ‘https://upload.qiniup.com’ -H ‘User-Agent: QiniuObject-C/7.3.2 (iPhone; iOS 11.2.6; ABE768DC-FA02-4CAB-9DF8-2720B0E7C890; GcvQmcsBpX-WqRIW_YTKKXua3PbSXh831RS_u2NW)’ -H ‘Content-Type: multipart/form-data; boundary=werghnvt54wef654rjuhgb56trtg34tweuyrgf’ -H ‘Content-Length: 27824’ -H ‘Cookie:’ [TOO MUCH DATA TO INCLUDE]

这一个请求是把图片流提交上去,当然这里的boundary无法写全,FLEXible工具无法显示图片流数据。Cookie数据也没补全。cookie里面是用户的sid等之类的信息。
其实这个请求还依赖列表第三的那个请求的token,需要先获取token再请求上传,后面我会写shell来体现。
成功上传的话最终返回的是图片的key和hash值,这一步仅仅只是把图片上传到了图床,还没有链接在游戏头像上。
返回:

{
  "key" : "sa/fc6861905d20200613100399.jpg",
  "hash" : "FifEYajsNl7aqU88gsOYSyewk199"
}

这个key就是下面请求用到的headimgurl参数。
另外的一个POST请求:
下面我对于敏感参数用“某某”代替。

curl -v -X POST ‘https://某某.com/user_api/update_user_info’ -H ‘market: apple’ -H ‘Content-Type: application/x-www-form-urlencoded’ -H ‘某某Version: 4.3.31’ -H ‘某某UserID: 某某-5631-4446-某某-09da14d65cb9’ -H ‘某某Product: 某某’ -H ‘User-Agent: 某某/4.1.31 (iPhone; iOS 11.2.6; Scale/3.00)’ -H ‘Accept-Language: zh-Hans-CN;q=1’ -H ‘nonce: 1592013691’ -H ‘Cookie:’ -d ‘build_tag=4531.0&device_id=某某-A6FA-4A74-8CA3-某某-1779-0000008790C7E566&device_name=iPhone%207%20Plus&device_system_platform=iPhone9%2C2&headimgurl=https某某com/sa/fc6861905d20200613100399.jpg&idfa=0D79361D-C19F-4A77-84Bd-D725AA96B4D8&market=apple&os_version=11.2&platform=1&sid=某某&某某_sign=%2B70m/3s/1314dgNQ641mE9YmN6c%3D&uid=某某&version=4.1.31’

这个POST请求就是修改用户头像的请求,最关键的参数就是其中的headimgurl,前面提到如果直接通过修改url请求参数变更headimgurl这个地址的话是行不通的,因为某某_sign这个参数是通过所有请求参数计算出来的,除非知道算法计算修改后的sign,否则后台会提示签名失败。

shell实现上传自己的gif动态图片到图床

我要注入的代码其实就是update_user_info这个请求拼装的方法,拼装headimgurl参数的时候修改成我要的图片地址就ok了。
那么我要先自定义图片上传到图床,不是用游戏上传图片入口,而是用我自己写的shell脚本来模拟post提交图片到图床!最终获取上传图片后的url地址。

#! bin/shell#! bin/shell

# 内容,查找的关键词
getContentByKeyword() {
	content=$1
	keyword=$2
	echo $content | awk -F \"$keyword\"\:\" '{ print $2 }' | awk -F \" '{print $1}'
}
 
getHeaderByKeyword() {
	content=$1
	keyword=$2
	echo $content | grep $keyword | awk -F $keyword\:\  '{ print $2 }' | awk -F \  '{print $1}'
}

content=`curl -v -X POST 'https://某某.com/config/get_upload_token' -H 'market: apple' -H 'Content-Type: application/x-www-form-urlencoded' -H '某某Version: 4.1.26' -H '某某UserID: 某某' -H '某某Product: 某某' -H 'User-Agent: 某某/4.3.26 (iPhone; iOS 11.2.6; Scale/3.00)' -H 'Accept-Language: zh-Hans-CN;q=1' -H 'nonce: 1589783647' -H 'Cookie:' -d 'bucket=某某&build_tag=某某.0&device_id=某某-A4FA-4A64-某某-某某-1779-某某&device_name=iPhone%207%20Plus&device_system_platform=iPhone9%2C2&idfa=某某-C18F-4A77-84BC-某某&market=apple&os_version=11.2&platform=1&sid=某某&某某_sign=8HX%某某%3D&type=2&uid=某某-5495-439f-某某-某某&version=4.1.26'`
token=`getContentByKeyword "$content" "token"`
echo $token >> test2.txt


uploadResponse=`curl  -v -i https://upload.qbox.me  -F "token=$token"     -H 'User-Agent: QiniuObject-C/7.1.0 (iPhone; iOS 11.2.6; 某某-FA02-4CAB-某某-某某)' -H 'Content-Type: multipart/form-data' -H 'Accept-Language: zh-cn' -H 'Accept-Encoding: br, gzip, deflate' -H 'Cookie:'  -H 'Accept: */*' -F "file=@/Users/mac/Desktop/test4.gif"`
nonce=`getHeaderByKeyword "$uploadResponse" "EagleId"`
picPath=`getContentByKeyword "$uploadResponse" "key"`
echo $nonce >> test2.txt
echo $picPath >> test2.txt
nonce=${nonce:8:10}
timeStamp=`date +%s`   
nonce=timeStamp
echo $nonce >> test2.txt

第一步获取token,通过用户uid sid等参数请求get_upload_token得到。
第二步请求upload.qbox.me把本地gif动态图片传上去
例如上面代码中 -F “file=@/Users/mac/Desktop/test4.gif”
上传成功得到类似的数据,key就是headimgurl要拼装的图片地址。

{
  "key" : "sa/fc6861905d20200613100399.gif",
  "hash" : "FifEYajsNl7aqU88gsOYSyewk199"
}

反编译分析汇编代码找出注入的方法(函数)

先砸壳,本文略过,详情查看我文章:用bfinject脱壳、注入自己的动态framework、cycript的使用
的到Mach-O文件后,用 hopper disassembler打开,查找关键词。
例如我查找修改头像的api关键词:update_user_info


右键进入谁调用这个地址,跳转切换成伪代码一看。

基本是这个方法了userApiUpdateUserInfoWithDict。
意思是通过字典变量来修改用户信息。headimgurl就是其中一个字典键,只要我们修改headimgurl的值就达到换图片地址的目的。当然只是猜想。

编写tweak

验证猜想。
我是这样做的,tweak写hook这个方法,打印一下这个方法的实参,看看是什么内容。
可以先用class-dump把头文件导出来看看参数类型是啥,这里省略。
写tweak hook。mac 执行命令nic.pl来新建个tweak工程。


choose a template 填11。
MobileSubstrate Bundle filter 要填写对应APP的bundleID,可以通过手机连接电脑查看电脑控制台来观察到相关关键字。
其他选项回车就行。
最终生成目录:

修改一下Makefile,在include $(THEOS… 上面新增两行代码

ARCHS = armv7 arm64
TARGET = iphone:latest:8.0

指定arm版本和编译手机的ios版本

打开Tweak.xm写代码:

%hook SnakeAPI
+ (void)userApiUpdateUserInfoWithDict:(id)arg1 success:(id)arg2 failure:(id)arg3 {
	NSLog(@"打印。。。%@", arg1);
 	%log;
	%orig; 
}
%end

保存,mac执行make package最终生成一个deb包。把deb通过ssh或者手机助手工具拷贝到手机安装,如果手机有Filza文件浏览器,可以通过Filza打开deb安装;如果没有只能通过terminal命令行工具安装,命令如下:

dpkg -i 包名.deb

手机连接mac,打开mac控制台软件。
手机打开游戏,上传一下头像,观察mac控制台是否有触发到日志,日志要自己筛选。最终我看到了日志有输出。
内容类似:

[headimgurl] = 'sa/234234234234234.jpg'

证明arg1是一个字典。
只要修改arg1的headimgurl就能实现图片自定义。

因为arg1是一个不可变字典,直接修改key对应值是行不通的,所以我简单粗暴,销毁arg1从新定义一个。
最终tweak代码如下:

%hook SnakeAPI
+ (void)userApiUpdateUserInfoWithDict:(id)arg1 success:(id)arg2 failure:(id)arg3 {
	NSLog(@"打印前。。。%@", arg1);
	arg1 =nil;
	arg1 = [NSDictionary dictionaryWithObjectsAndKeys:
    	@"https://某某/sa/2345345345345345.jpg", @"headimgurl",nil];
	NSLog(@"打印后。。。%@", arg1);
 	%log;
  	%orig; 
}
%end

图片地址我写死了,就是我用shell上传图片返回的地址,是一张动图。
再次编译tweak代码生成deb安装到手机。
手机去上传一下头像。
上传成功,但是头像并不是我们的动态图,我猜是本地缓存,重启游戏,哟嚯,头像刷新了,变成我们要的动图:

结束

此教程仅做学习交流和知识记录方便以后查看使用,如果涉及到利益相关的请告知本人进行删帖处理。

本人想通过这些博客记录自己这两周内折腾的过程以及分享最后成功的成果。另外想结交志同道合对IT行业感兴趣的盆友,互相交流学习。可以通过博客联系我或加QQ号:1321691245
只交朋友不接受接单之类的骚扰。

博文主索引目录入口

我会把这系列的文章更新到这个入口里面,分享我的心得,大家互相学习。
2020年 IOS 逆向 反编译 注入修改游戏或APP的调用参数新手系列教程主目录入口

2 Likes

微信是是不是也可以搞这个

我想搞,抓包数据是加密的,目前还没分析他的调用过程