好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 xiaoweigege 于 2022-12-1 19:35 编辑
ios逆向爬虫-入门保姆级-实战某博APP
设备
- iPhone11,系统14.2
- Windows11
工具
- Frida15.2.2
- frida-ios-dump
- Ida7.7
流程
- 手机越狱
- 爱思助手安装目标APP
- Cydia安装Frida
- frida-ios-dump进行脱壳
- app分析
- ida+frida动静态分析
手机越狱
采用爱思助手一键越狱
爱思助手安装目标APP
略
Cydia安装Frida
-
Cydia添加frida源
" alt="avatar" />
-
在cydia添加frida源后,搜索frida,根据iOS设备版本安装对应的frida服务端,如下所示
" alt="avatar" />
-
frida一些简单的使用
- frida-ls-devices 查看电脑链接设备信息
- frida-ps -U 查看通过USB连接设备上运行的程序
- frida-ps -Ua 查看正在运行的程序
- frida-ps -Uai 查看iOS设备中已经安装的应用程序
- frida-ps -D \<UDID\> 通过iOS设备的UDID查看iOS设备中应用程序中的pid、进程名
- frida-trace -U -f 包名 -i 'CC_MD5' 用于跟踪ios方法的调用
frida-ios-dump进行脱壳
ios脱壳的方式有很多种:
- frida-iod-dump
- dumpdecrypted
- flexdecrypt
- Clutch
- AppCrackr
- Crackulous
个人觉得frida-ios-dump比较方便,快速。接下来讲解frida-ios-dump的使用步骤:
- 拉取代码,地址: frida-ios-dump
- 安装依赖:
pip install -r requirements.txt
- 手机端口转发
iproxy 2222 22 ; iproxy 本地端口 远程目标端口
- ./dump.py 目标app名称, 会将ipa包拉取在本地当前目录
- 将ipa文件中的主包文件拖进ida分析(app比较大,ida会分析好久好久好久);如图所示:
" alt="avatar" />
app分析
抓包分析
" alt="avatar" />
我们的目标是得到 s 值得算法,从 s 这个值得名称来看,大概是一个签名,那么根据以往的经验签名函数大概是一个 hash 操作。
过程:
- 合并请求数据
- 加上盐值
- hash计算得到签名
frida-trace 跟踪hash函数
- 打开目标app
- 输入命令:
frida-trace -UF -i "CC_MD5" 跟踪ios的md5函数
- 在当前目录下会生成
__headlers/xxx/CC_MD5.js 文件
-
修改对应js更好的显示输出结果
{
onEnter(log, args, state)
{
log('CC_MD5():', args[0].readUtf8String());
}
,
onLeave(log, retval, state)
{
log('CC_MD5()--return--=');
var md5_digest = hexdump(retval, {length: 16});
var hexified = " ";
var raw_array = md5_digest.split("\n");
for (var a = 0; a < raw_array.length; a++) {
var line_array = raw_array[a].split(" ");
for (var b = 1; b < line_array.length - 1; b++) {
if (line_array[b].length === 2) {
hexified += line_array[b];
hexified = hexified.trim();
}
}
}
log(hexified + "\n");
}
}
- trace结果如下:
" alt="avatar" />
执行上述流程,并没有hook到关键数据,同理尝试 CC_SHA1 , CC_SHA256 , CC_SHA512 , CCHmac 。均没有结果
接着使用FridaDev中的ios-trace.js 脚本进行跟踪。该脚本比较强大,可以模糊跟踪ios中的函数,入参,出参都有比较美观的输出。
现在我们根据经验进行一些跟踪,脑海里面想到一些关键词 sign ,crypt , Signature 等等。使用该脚本得到结果如下:
" alt="avatar" />
找到关键信息数据, 得到s的返回值。
找到目标函数,现在需要耐心等待ida分析完成,我们要去看看c的伪代码了。
ida+frida动静态分析
ida一些基本操作
x 快捷键获取函数,变量的调用处
shift + F12 获取字符串窗口
...
ida静态分析
- ida静态分析目标函数
" alt="avatar" />
sub_107717A5C 函数是sha256 算法,为什么我们之前trace sha256 函数的时候没有得到结果,因为这部分算法是C写的导致hook不到。
怎么分析出是sha256 算法的?
- 经验
- 推荐一个hash计算的网站 hash计算, 把加密前内容拿去计算,对比计算出来的结果,推断是什么算法
frida hook obj-c的两种方法
-
oc方法
// 类名 方法
var hook = ObjC.classes.NSMutableURLRequest["- setHTTPBodyStream:"];
Interceptor.attach(hook.implementation, {
onEnter: function(args) {
// 转换 objc 对象
var receiver = new ObjC.Object(args[0]);
// 将选择器转换sel为 JavaScript 字符串
var sel = ObjC.selectorAsString(args[1]);
var data = ObjC.Object(args[2]);
var string = ObjC.classes.NSString.alloc();
send(" HTTP Request via [ "+receiver+" " +sel+" ] => DATA: " + string.initWithData_encoding_(data,4));
}
});
-
sub方法
像上图这种无符号函数,我们需要根据地址来进行hook
if (ObjC.available) {
const baseOffset = 0x100000000;
const base = Module.findBaseAddress('主包名称');
console.log('Base:', base);
const idaBase = base.add(-baseOffset);
console.log('Real Base:', idaBase);
const sub = base.add(0x107720938 - baseOffset);
Interceptor.attach(sub, {
onEnter: function (args) {
console.log('进入:' + sub.toString() +':', args[0], args[1], args[3])
print_dump(args[0], 100)
// console.log('args[0]=', args[0].readCString())
print_dump(args[1], 100)
// console.log(args[3].readInt())
// print_dump(args[2], 100)
},
onLeave: function (retval) {
console.log('retval=', retval)
print_dump(retval, 200)
}
})
} else {
console.log('非ios环境')
}
常用的一些模板
该脚本来自慕白
- 打印堆栈,堆栈地址相对ida无偏移
function logBacktrace(context, methodName = "", threadId = "") {
log((methodName.length > 0 ? methodName + " " : "") + "Backtrace:");
var backtraces = Thread.backtrace(context, Backtracer.ACCURATE);
for (var backtrace of backtraces) {
var symbol = DebugSymbol.fromAddress(backtrace);
var memAddr = symbol.address;
var subAddr = memAddr.add(baseOffset - base);
log("\t" + threadId + " " + subAddr + " " + symbol.moduleName + "!" + symbol.name + " " + symbol.fileName + " " + (symbol.lineNumber > 0 ? symbol.lineNumber : ""));
}
}
function logInterceptor(interceptor, methodName = "") {
var info = "[t" + interceptor.threadId + "] " + (methodName.length > 0 ? methodName : "");
logBacktrace(interceptor.context, info, interceptor.threadId);
}
// 使用方法
var A = eval('ObjC.classes.A["- A:"]');
Interceptor.attach(A.implementation, {
onEnter: function (args) {
logInterceptor(this, "A:");
}, onLeave: function (ret) {
}
});
2. json2str
```js
function jsonTostr(ocobj) {
var NSString = ObjC.classes.NSString
var str = NSString.alloc().initWithData_encoding_(ocobj, 4)
return str
}
- 数组转16进制字符串
function print_dump(addr, size, methodName = "") {
if (addr == null || addr == 0 || size == 0) {
return;
}
var buf = Memory.readByteArray(addr, size)
log((methodName.length > 0 ? methodName + " " : "") + "[dump]: " + addr.toString() + " " + "length: " + size.toString() + "\n[data]")
log(hexdump(buf, {offset: 0, length: size, header: true, ansi: false}));
log("\n")
}
- 打印对象
function print_obj(obj) {
var description = "";
for (var i in obj) {
description += i + " = " + obj[i] + "\n";
}
log(`info:\n${description}`);
}
- 打印原生bytes
function print_raw_bytes(addr, size) {
log("Reading from address: " + addr);
var byteArray = addr.readByteArray(size);
var int8Array = new Uint8Array(byteArray);
var str = "";
for (var i = 0; i < int8Array.length; i++) {
var value = int8Array[i];
str += (value > 16 ? value.toString(16) : ("0" + value.toString(16)));
str += " ";
}
return str;
}
|
免费评分
-
查看全部评分
|