声明:本文只作学习研究,禁止用于非法用途,否则后果自负。如果侵权到了您的权益,请立即联系我删除!
前言
案例为猿人学学员app第二题,主要学习各种逆向方法,最终目标脱机执行
一、准备
环境准备
- frida 12.11.18
- frida-tools 8.2.0
- pixel 1 手机
- ida 7.5
- jadx
- python 3.8.10
- c++ 14
- 010Editor
二、java
直接抓包,无抓包检测,charles设置wifi代理即可
只有一个data参数
使用jadx 打开apk,全局搜索url特征,/api/app2
找到唯一一段可疑的代码
右键OooOOo
查找用例,发现2处,一处为第一页的加载,另外就是其他页码的加载
分析出,f8535OooO0o0为页码,currentTimeMillis为13位时间戳,通过拼接,传入1NativeLib.encrypt1方法,并将结果转化base64 既是请求参数的data
使用frida hook NativeLib.encrypt验证分析
function hook_java() {
Java.perform(function () {
var nav = Java.use('com.yuanrenxue.challenge.two.NativeLib')
nav.encrypt.implementation = function (v, j) {
console.log("byte:", JSON.stringify(v))
console.log("str:", j)
var es = this.encrypt(v, j)
console.log("加密返回:", JSON.stringify(es))
return es
}
})
}
function main() {
hook_java()
}
setImmediate(main)
frida -U com.yuanrenxue.challenge -l t2.js 注入app,
手动进入第二题,触发hook,入参为第一个是拼接而来转字节的1:1693660604784
,第二个是时间戳,返回的结果通过base64 对的上抓包的结果
接下来找NativeLib.encrypt
方法,发现是调用libtwo.so
里面的方法
三、so
使用apktool 解压apk,在lib文件夹中,找到libtwo.so
文件,然后使用ida打开,搜索encrypt
关键字,只有一个结果,按f5
解析为伪代码,方便分析
方法前2个参数为ida给的,后面a3,a4是我们传入的参数,选中按y
把a1的类型改为JNIEnv*
,把所有用到a1的函数,进入函数更改其类型为JNIEnv*
,方便分析
进入函数sub_1c74
,只有一个GetByteArrayElements
,获取第一个参数的信息,不是加密的算法
进入函数sub_1CB0
,只有一个GetArrayLength
,提取长度,不是加密的算法
malloc
是开辟寄存器空间的方法,不用看
进入函数sub_C70
,凭感觉是很有可能的,继续看最后2个函数
sub_1CE4
新建一个字节数组
sub_1D18
把v7的赋值给新建的数组,流程就比较明朗了,v7
通过sub_C70
执行之后,是最后加密完毕的值
使用frida hook 验证一下分析结果
function hook_so() {
Java.perform(function () {
// var twoadd = Module.getExportByName('libtwo.so', "malloc") // 拿到地址,名字要重新搜索进入汇编解密拿取名字
var libtwoadd = Module.findBaseAddress("libtwo.so")
Interceptor.attach(libtwoadd.add(0xc70), { // hook 地址
onEnter: function (args) { // 进入时 hook
this.args0 = args[0]
this.args1 = args[1]
this.args2 = args[2]
this.args3 = args[3]
console.log("args1:", ptr(args[0]).readCString()) //1:1693456457984
console.log("args2:", args[1]) //
console.log("args3:", args[2]) // 47 长度
console.log("args4:", args[3]) // 1693456457984
},
onLeave: function (retval) { // 返回时 可以修改返回值
console.log(hexdump(this.args1))
return retval
}
})
})
}
结果证明分析是正确的,sub_c70,传入4个参数
- 页码和时间戳的拼接
- 开辟的空间指针地址
- 第一个参数的长度
- 时间戳
最后的结果 将16进制数据转10进制,会发现是对应上的,会有负数是因为使用的js 的JSON.stringify转化打印的结果
四、ida-trace
由于基础太差了,就算f5了很清晰,看的懂一点了,但是还是还原不了,所以就使用了ida trace的方法来还原
将ida dbgsrv
文件夹中的,android_server文件 (64就是android_server64)
传入设备,然后开启端口,巴拉巴拉的操作
adb forward tcp:11678 tcp:11678
然后 adb shell su模式 启动端口
chmod 777 android_server64
./android_server64 -p11678
frida 启动app
新开一个ida
ida --- debugger --- attach --- android debugger --- 设置刚刚的端口 11678 第一次保存打勾,然后等待加载 搜索需要trace 的包名,
file --- script file 导入trace脚本 (根据要hook的so文件,和函数地址 填入相关的信息, 脚本自动设置断点)
trace脚本是抄其他大佬的,这里就不方便贴了
然后可能会出现一些问题,提示这个,我是随便点的,有概率进入debug,也不知道怎么回事
导入trace脚本之后,先挂起其他线程,然后设置
stop_thread() // 挂起其他的线程 trace脚本的方法 用来处理上面的弹框
start_hook() //trace脚本的方法
关掉hook 的自动注入 // 就是frida hook 文件里的setImmediate(main) 这一句
start_thread() // 恢复执行线程 然后frida 主动调用 sub_c70方法
function call_c70() {
var jidizhi = Module.findBaseAddress("libtwo.so")
if (jidizhi) {
var addr_c70 = jidizhi.add(0xc70)
var func_c70 = new NativeFunction(addr_c70, "void", ["pointer", "pointer", "int", "long"])
var time_page = Memory.allocUtf8String("1:1693650922914")
var a2 = Memory.alloc(47)
console.log(a2)
var size = 47
func_c70(time_page, a2, size, 1693650922914)
console.log(hexdump(a2, {length: 47}))
}
}
ida 进行下一个断点,一直到trace脚本设置的断点中,即 sub_c70
的开始和结束的地址(c70,1130
),可通过ida 查看到,然后加上基地址就是设置断点的地址
断点在函数开始断住之后, 设置ida
debugger --- tracing --- tracing options
根据 下图做完之后
debugger --- tracing --- insturction tracing
设置完毕解开断点,执行过trace的代码背景就会变成黄色,一直等待到断点断在函数结束的位置,即可关闭ida
五、算法还原
使用010Editor
打开trace日志文件,长这样,大概9w+行
画起来的是我写的注释,第一行对应x0,x1,x2,x3
就是对应的传入参数的地址,已知第二个参数就是加密结果的地址,直接找00000071AD1FC280
对应的操作
最前面的操作感觉是不太可能,直接看最后面的操作,也就是
libtwo.so:sub_71ABA5CC70+460
这里
发现下一个操作就是赋值操作,翻译理解一下就是STRB
是汇编操作中一个赋值的操作码,
W9, [X10,X11]
就是将w9
赋值给 x10
的x11
,代码表示就大约是
x10[x11] = w9
也就是0x7c
赋值过去了,搜索该地址的所有操作,也就是w9的结果那一个地址 libtwo.so:sub_71ABA5CC70+45C
frida 主动调用该函数,验证一下,是不是这样的结果,固定好时间戳,发现是最后的15位的结果,先把这15位的结果尝试还原
那么我们的思路就是一个一个往上找,直到赋值的变量是我们传入的参数或者为固定值
所以,上图的w9的来源翻译过来就是
w9 = w9 ^ w10
常见的操作码如下
STRB
赋值ADD
&AND
+LDRB
拿取 例:x10[x10]MOV
=EOR
^SUBS
-CSEL
三目表达式
大概是这些了,ok,你已经获取了分析trace文件的的大部分知识,只需要一点点时间,可以分析出,这一块的内容为
发现第一行的w9
和1libtwo.so:sub_71ABA5CC70+428
的w9的值是直接拿出来的 没有实际计算出来,那么就需要去找对应的地址也就是00000071A948B040
的地方,看看有那些赋值操作给到了它
然后就是搞了俩三天,才把所有的逻辑追到了入参的地方,至此,分析完毕
接下来就是使用c来还原这些操作,为什么用c呢,因为不同语言的一些计算的差别,懒的去弄了,索性就用c来写,写完就是这样
然后直接打包,给python调用,开始请求
ok,莫得问题,下机
总结
学习了frida的各种api的使用,ida的操作,trace的操作,以及一定的trace文件分析能力