type
status
date
slug
summary
tags
category
icon
password

以xxx跑为例的iOS逆向

效果展示:这是xxx跑的OSS服务器,我们可以自己上传内容进去

1.目的:

完全解析加解密算法,文件上传算法,最终重写客户端。

2.抓包分析

直接用appdump砸壳,拿到可执行程序,脱进ida。
抓包可以看到关键的请求只有那么几个,这篇文章主要探讨如何获取OSS签名实现对服务器的文件上传。
通过抓包发现,跑步记录、用户头像等等内容均存储于OSS服务器上,跑步记录是txt,里面是base64编码。而跑步封面截图直接就是存的图片。
比如跑步记录txt:
解码后发现不可见。其实是进行了一层加密。我们先不管这个。
notion image
要逆这个地方非常简单,直接找base64解码的交叉引用,就会找到对应的加密方法和密钥。这里就不公开了。
上传文件的逻辑是,先请求它自己的api服务器,获取一次性签名(有时间和指定文件的限制)。用签名去oss服务器上传内容。
抓包发现发送给服务器请求签名的请求是key={<base64>},其中的内容也是加密过的,加密算法与刚才提到的一致,但密钥不同。
服务器返回base64编码,解码解密,算法依旧相同,密钥还是不同。
由此便可得到格式为
“OSS LTAI5tRbKExxxxxxxxx:nGmHxxxxxxxx”的签名,可以在一小段时间内用来访问服务器并上传内容。
抓包发现每次上传之前都给这个地址发送请求
内容如下

3.静态分析

解密刚才我们说到的请求字符串,得到类似内容
其中,nonce是随机数,除了用户信息之外,content是要进行上传的地址,有两个md5值,分别是sign和lpp2.
打开我们的ida,查找getOSSSign的交叉引用,发现用于构造http请求的函数是
[HttpRequest postWithURLString:parameters:success:failure:]
在这里面看了一圈,用于构造签名的函数是
sortDictionary:bySignAPPKey
代码块 loc_10046D73C 是上传文件用到的
还有另外一个类似的代码块,是用来上传用户头像的,我们不看。
notion image
取出机器码,保存到寄存器X22中
从字典中移除机器码这个键值对
获取iphone型号。调用的是iphoneType函数。这个函数对于新款iPhone没法获取型号,只能获取代号。但老iPhone没问题。
比如iPhone6s,但13pm只能得到iPhone14,3 应该是太老了没更新的问题
然后取子字符串 分别是iPhoneType的第二个字母,时间戳的第四个字符。二者与lp%@%@构造字符串。取小写。
所以目前构造出来的是lpp2,差不多一个月,时间戳变了,就会变成lpp3,以此类推
notion image
接着往下看。取出时间戳的值(又取了一次) 转换成整数类型 左移一位(x2),格式化字符串为长无符号整型,计算md5,设置为键lpp2对应的值。
然后将x3设置为常量,x20中是整个字典,这样做参数传递,传递给签名函数
因为arm64的参数传递是一套固定的规则,是由ARM的ABI(Application Binary Interface)定义的,主要通过寄存器和栈来传递参数,如下:
  1. 寄存器传递
      • 前8个整型参数(包括指针)使用x0到x7寄存器传递。
      • 前8个浮点参数(包括单精度和双精度)使用v0到v7寄存器传递。
      • 如果函数有混合的整型和浮点参数,寄存器是按顺序分配的,不会跨类型混合使用。
  1. 栈传递
      • 当参数超过8个整型或8个浮点寄存器时,剩余的参数将被传递到栈上。
      • 栈上的参数按照从右到左的顺序传递,并且栈需要16字节对齐。
  1. 寄存器和栈的混合使用
      • 对于那些不能完全由寄存器传递的参数(例如超过8个整型或浮点参数),多余的参数将存储在栈上。前8个整型参数在x0到x7寄存器中传递,前8个浮点参数在v0到v7寄存器中传递,其他参数则放在栈上。
  1. 返回值
      • 返回值一般通过寄存器x0返回(对于整型和指针类型),对于浮点型返回值则通过v0返回。
      • 如果返回值是一个复合类型(例如结构体或联合体),并且结构体超过16字节,则返回值通常通过指针传递,指针在x8寄存器中传递。
  1. 特殊情况
      • 大型数据结构(如超过16字节的结构体)通过内存传递,调用者需要分配内存并传递指向该内存的指针。
      • 变长参数函数(如printf)中的变长参数按照上述规则先通过寄存器传递,不够用时再使用栈。
 
查看bySignAPPKey函数,签名过程如下:排序字典并把键值对连接成字符串。判断传入参数X3是否为空字符串,如果是,则在尾部追加默认值。否则,将X3的内容连接到整个字符串的头部,计算MD5
notion image
 
得到了签名之后,保存到字典的sign字段中
 
notion image
继续往下看,这里将mobileDeviceId又放了回来,从x22寄存器取出来重新设置成键值对。然后把字典转换为Json格式,传入加密函数,得到的密文设置为key的键值对,存储下来。
实际上,并不是在此处进行加密的,后面根据条件判断,调用了另外一个算法相同密钥不同的加密函数
notion image
仍然设置为key:””键值对 即用来发送请求。
大部分逻辑已经清晰,可以自己进行签名了。但实测根据我们上面解密出来的内容进行签名并不能成功。其实是我解密算法写的有问题,但我并没有意识到,所以进行动态调试。

4.动态调试

iOS的动态调试有几种方案:
非越狱机型:monkeydev
越狱机型:monkeydev/debugserver重签名/直接装debugserver插件
使用monkeydev一定需要砸过壳的ipa
实测15.5无根越狱的情况下,重新签名debugserver会报错,要么是无法连接gdb,要么就是直接被kill。
monkeydev在xcode15下无法正常工作,可以在安装之后使用这个仓库里的内容把模板全部替换,不然会出现诸如libstdc之类的问题
但是!monkeydev在面临少数程序时,会出现无法签名以至于无法安装至真机的情况,步道乐跑就是这样,所以只能放弃。转而使用debugserver
 

debugserver插件

直接在Sileo内安装即可,使用也非常简单
debugserver 0.0.0.0:<端口号> -a <进程名>
查看正在运行的某进程的名称可以使用 ps -A | grep /Application
先启动软件,然后运行debugserver命令,附加上去。
启动mac,确保有lldb。直接进入lldb。使用
process connect connect://手机ip:<端口号> 即可连接。
我们只要在签名的时候下断点看一下就可以了。
首先需要这样几个命令
列出模块
下断点的命令
注意我们下断点的地址,一定是ASLR偏移量再加上我们在IDA内看到的地址才行。
比如 我们先查看ASLR偏移量
notion image
然后我们只下断这几个地方:
1.调用签名函数之前(也可以直接断这个函数,我这里断了调用前准备参数的地方)
notion image
断这个地址,X3内的内容是自定义的要附加的字符串,X20的内容就是整个字典
2.签名函数调用MD5计算之前
notion image
断这个地址,X27中是要计算的字符串
3.调用加密函数之前,因为进入加密的就是要发送的字符串了,不会再变,即最终要发送的结果。
notion image
 
然后开启一个自由跑,等一会结束跑步并且上传跑步记录,就会断到了。
notion image
这是进入签名函数之前,显然没有设备UUID
notion image
这是要进行MD5的字符串。此时我已经明白为什么我的签名不对了,因为我对换行符的处理,导致PUT txt Sat 以及GMT后的换行全部去掉了。这样就导致签出来的不一样。
这也印证了我们刚才静态调试的结果,即,给签名函数传入自定义的字符串,则会附加到最前面,进行MD5的计算。
notion image
显然最后进入加密函数的是有UUID的。
至此签名告破。
 
Relate Posts
iOS免越狱hook与patch框架和免越狱dylib注入打包iOS liapp bypass+Unity逆向+tweak开发
Loading...
Lynnette177
Lynnette177
建议开着梯子访问站点。图片是直接从Notion获取的,不开梯子容易看不见图片。
Latest posts
写一个Android Hook小框架
2025-6-23
一些macos常用软件破解记录
2025-6-22
iOS典型反调反越狱app分析
2025-6-22
iOS网易新闻登录算法逆向
2025-6-22
小红书shield Chomper模拟
2025-6-22
一加11 内核、ROM爆改+脱壳机 LineageOS 22.2 Android 15
2025-6-22
Announcement
🎉2024.6.9 上线🎉