一
起手准备
GetByteArrayElements
,GetArrayLength
,GetStringUTFChars
等,因为我们在JNI Native中知道SS函数传入的参数是一个字节数组和一个字符串,所以我们推断出此处跟我们要找的函数入口有关联。我们看一下sub_7d4b4的网状结构。二
Trace - Frida Stalker
Stalker
我在上一篇中已经介绍过了,包括对msaoaidsec.so的anti操作。我们直接跳到使用。Java.perform(function () {
let SecurityUtil = Java.use("ctrip.android.security.SecurityUtil");
SecurityUtil["simpleSign"].implementation = function (bArr, str) {
console.log('simpleSign is called' + ', ' + 'bArr: ' + bArr + ', ' + 'str: ' + str);
let ret = this.simpleSign(bArr, str);
console.log('simpleSign ret value is ' + ret);
return ret;
};SecurityUtil["init"].overload('android.content.Context', 'int').implementation = function (context, i2) {
console.log(`SecurityUtil.init is called: context=${context}, i2=${i2}`);
this["init"](context, i2);
};});
三
分析前32位
sub_D1DB4
的参数a2是用来存放前32位的地址,我们验证一下v5的记过是否是5-8位,鼠标放在v5处,Tab切换到汇编代码,根据其地址在trace日志中搜索。libscmain.so d23dc ldr x16, [sp, #0x50] ; x16 = 0x7190e94e3d --> 0x7189db0948 (1e0f3c2d5a4b7869)
libscmain.so d23e0 ldr w18, [x16, #4] ; x18 = 0x7 --> 0x64326333
libscmain.so d23e4 eor w15, w15, w18 ; x15 = 0x57765407 --> 0x33443734
X
键,查看交叉引用,但是如果存在过多的调用情况排查起来其实略麻烦,配合trace日志能更方便的节省一点时间,但是也有可能存在跳转指令是处在花指令的范围内,如果这样的话那根据日志排查起来就略微有一点点麻烦。还有就是可以用frida打印调用栈,这个方法略微有一些看脸。libscmain.so d5f2c ldp x0, x30, [sp], #0x10 ; x0 = 0xc4 --> 0x71edd1f2f0 (���*�g�Iԥxt.��) sp = 0x7189db0860 --> 0x7189db0870
libscmain.so d5f30 ldur x0, [x29, #-8] ;
libscmain.so d5f34 ldr x1, [sp, #0x10] ;
libscmain.so d5f38 ldr x2, [sp, #8] ;
libscmain.so d5f3c bl #0x7190d1adb4 ;
libscmain.so d1db4 stp x0, x30, [sp, #-0x10]! ; sp = 0x7189db0870 --> 0x7189db0860 (����q)
libscmain.so d1db8 ldr w0, #0x7190d1adc0 ; x0 = 0x71edd1f2f0 --> 0xd1
libscmain.so d1dbc bl #0x7190d1ae60 ;
libscmain.so d1e60 sub x0, x0, #0x11 ; x0 = 0xd1 --> 0xc0
libscmain.so d1e64 eor x0, x0, #0xc0 ; x0 = 0xc0 --> 0x0 (null)
libscmain.so d1e68 add x0, x0, #1 ; x0 = 0x0 --> 0x1
libscmain.so d1e6c ldr w0, [x30, x0, sxtx #2] ; x0 = 0x1 --> 0xb8
libscmain.so ed09c ldr w0, #0x7190d360a4 ; x0 = 0x71edd1f2f0 --> 0xec
libscmain.so ed0a0 bl #0x7190d35dec ;
libscmain.so ecdec bl #0x7190d35ecc ;
libscmain.so ececc eor x0, x0, #0xc0 ; x0 = 0xec --> 0x2c
libscmain.so eced0 lsr x0, x0, #0 ;
libscmain.so eced4 add x0, x0, #1 ; x0 = 0x2c --> 0x2d
libscmain.so eced8 ldr w0, [x30, x0, sxtx #2] ; x0 = 0x2d --> 0x748
libscmain.so ecedc add x30, x30, x0 ;
libscmain.so ecee0 ret ;
libscmain.so ed538 ldp x0, x30, [sp], #0x10 ; x0 = 0x748 --> 0x71edd1f2f0 (���*�g�Iԥxt.��) sp = 0x7189db0890 --> 0x7189db08a0
libscmain.so ed53c mov w3, wzr ; x3 = 0x7190e94d78 --> 0x0 (null)
libscmain.so ed540 bl #0x7190d1ee5c ;
libscmain.so d5e5c stp x0, x30, [sp, #-0x10]! ; sp = 0x7189db08a0 --> 0x7189db0890 (����q)
libscmain.so d5e60 ldr w0, #0x7190d1ee68 ; x0 = 0x71edd1f2f0 --> 0x536
libscmain.so d5e64 bl #0x7190d1ee80 ;
libscmain.so d5e80 sub x0, x0, #0x3a ; x0 = 0x536 --> 0x4fc
libscmain.so d5e84 eor x0, x0, #0xfc ; x0 = 0x4fc --> 0x400
libscmain.so d5e88 lsr x0, x0, #0xa ; x0 = 0x400 --> 0x1
libscmain.so efcf8 eor x0, x0, #0xe0 ; x0 = 0xf6 --> 0x16
libscmain.so efcfc sub x0, x0, #0x10 ; x0 = 0x16 --> 0x6
libscmain.so efd00 add x0, x0, #1 ; x0 = 0x6 --> 0x7
libscmain.so efd04 ldr w0, [x30, x0, sxtx #2] ; x0 = 0x7 --> 0x1e0
libscmain.so efd08 add x30, x30, x0 ;
libscmain.so efd0c ret ;
libscmain.so efe78 ldp x0, x30, [sp], #0x10 ; x0 = 0x1e0 --> 0x71edc30020 (TracerPid) sp = 0x7189db0a00 --> 0x7189db0a10
libscmain.so efe7c adrp x0, #0x7190eb7000 ; x0 = 0x71edc30020 --> 0x7190eb7000
libscmain.so efe80 add x0, x0, #0x1bc ; x0 = 0x7190eb7000 --> 0x7190eb71bc (ed756e23400710596bbd71988670248va4c8db2ae867a149d4a578742e90ec)
libscmain.so efe84 sub x1, x29, #0x10 ; x1 = 0x20 --> 0x7189db0a80 ( )
libscmain.so efe88 bl #0x7190d35de4 ;
libscmain.so ecde4 stp x0, x30, [sp, #-0x10]! ; sp = 0x7189db0a10 --> 0x7189db0a00 (�q��q)
libscmain.so ecde8 ldr w0, #0x7190d35df0 ; x0 = 0x7190eb71bc --> 0xd4
libscmain.so ecdec bl #0x7190d35ecc ;
libscmain.so ececc eor x0, x0, #0xc0 ; x0 = 0xd4 --> 0x14
libscmain.so eced0 lsr x0, x0, #0 ;
add x0, x0, #0x1bc
此处需要注意的一点是,他的汇编代码与IDA的反汇编并不一致,道理是相同的,粗俗一点理解其实就是根据某个偏移取到了内存空间中的某个值,这个值从哪里来其实我们目前暂时没办法确定,在ida的反汇编中,他的呈现是这样的ADRL X0, unk_26E1BC
,在一个未处理字,暂时推测是某个代码块中应该向其赋了值。v7 = qword_270030(10L)
如果我们点击进去发现并没有什么,因为他是一个数据段,我们点击qword_270030
再点击X
会发现他其实是指向的是某个函数,这里我们发现他是在so init时候进行了定义。qword_270030 = (__int64 (__fastcall *)(_QWORD))dlsym(handle, "malloc");
qword_270038 = (__int64)dlsym(handle, "calloc");
qword_270028 = (__int64 (__fastcall *)(_QWORD))dlsym(handle, "free");
qword_270040 = (__int64 (__fastcall *)(_QWORD, _QWORD))dlsym(handle, "realloc");
v17 = sub_D0404("f0e1d2c3b4a59687", 128LL, v22)。
f0e1d2c3b4a59687
,第二个是128
,第三个是v22。
四
分析第33-40位
ldr x0, [sp, #0xf8] ; x0 = 0x34 --> 0x7189db0d6c (3469dc64E0AA3D74F268AE*****************)
,但是我们无法找到计算或者生成的地方,但是我们之前说过有怀疑这里是时间戳,那么我们对其进行转换,转换成10进制然后再时间戳转换试试,具体过程不细说了,直接说结果,转换的10进制并不符合时间戳,因为在这里要处理端续,转换成0x64dc6934再去匹配发现转换成时间戳就对得上我们trace的时间了。五
后32位的逻辑
搜索到日志的第一条
libscmain.so eef28 ldur x11, [x29, #-0x10] ; x11 = 0x7189db0d40 --> 0x716d81ec00 (825b340c)
825b
发现也是一样,都是出现在了0xeef28
位置上,那么我们就需要去分析一下,此位置是一个什么样的结构或者功能。v3 -> v4 -> v8 -> result = param1(第一个参数)。
X
键查看sub_EEE38
的交叉引用,发现其恰好都在我们上一级sub_F10C0
中,花一分钟去trace日志,我们基本可以定位到具体哪里调用了eee38。sub_EEE38
的param1 ->v32
,观察其规律,发现sub_F0E04
中有关联,我们试着用frida看一下v32的变化。sub_F0E04
在调用的时候,a1的值应该就是后32位的值了,但是函数执行结束时,a1的值是有变化的,而变化后的值恰恰就是最终生成的simplesign的后32位。那么我们假设,后32位计算后,会经过f0e04这个函数对后10位进行了某些变化。sub_F10C0
,观察v32的轨迹,在IDA中我们观察v32并没有操作什么,那么问题很有可能出在了上一篇中,scmain存在的花指令混淆的原因,试着去修复会很费时间,有没有其他方式能展现出各函数的执行流程呢?我们试一下Binary Ninja
去反汇编,看看能不能比IDA展示的更好。sub_f0f0c
-> IDA的sub_F0E04
sub_F77A4
-> IDA的sub_F2794
(Binary Ninja识别出了跳转)sub_F2794
进行有效的反编译,所以我们使用Binary Ninja来分析。sub_F0E04
的参数来自sub_F77A4
的第三个参数(param3),而且param3在BN中也没有发现有其他函数参与修改、计算,那么我们推测,F77A4是计算后32位的函数,点击进入。MD5
中的K表
,还有位移数
数量也不多不少,正好64个。825B340C
因为我们知道了后面是由MD5生成的,所以端续我们可以确定,去trace日志搜索0xC345B82
,第一条结果。0x1041b4
,在BN
按G
输入跳转,发现会跳转到函数头部,因为BN的逻辑跟IDA不太一样,定位不到具体变量或者参数的位置,以结果所在的寄存器为地址,那么我们试试将我们的指令地址+4或者-4。001041b0 int32_t x18_75 = ror.d(x8_1091 + 0x70363da3 + x13_196 + ((x13_367 | not.d(x9_673)) ^ x8_1070), 0x1a) + x13_367
A
所在的位置。入参
以及初始化ABCD(魔数),试着在MD5第一行计算中找规律,因为A、B、C、D一定会参与到前4行的运算中。int32_t x8_1104 = ror.d(x19_10 + 0x500fe759 + (((var_158.d ^ var_168.d) & var_170.d) ^ var_268.d) + var_178.d, 0x19) + var_270.d
int32_t x9_810 = ror.d(x8_330 + 0x6fa2f477 + var_158.d + (((var_168.d ^ var_170.d) & x8_1104) ^ var_168.d), 0x14) + x8_1104
int32_t x0_127 = ror.d(x0_34 - 0x5cbacc06 + var_168.d + (((var_170.d ^ x8_1104) & x9_810) ^ var_170.d), 0xf) + x9_810
int32_t x8_1220 = ror.d(x18_156 + 0x46d88dcf + var_170.d + ((x9_810 & x0_127) | (x8_1104 & not.d(x0_127))), 0xa) + x0_127
line1 = ror( x_19 + 0x500fe759 + var_178 + ((var_158 ^ var_168) & var_170) ^ var_268 , 0x19) + var_170(var_270=var_170)
x_19
就是传入的参数M[0]
,继续简化公式。line1=ror(M[0]+k[0]+A+(异或与运算),移位数)+魔数之一
var_178
,var_268 = var_158
,var270 = var_170
,var_168。
var_178
与var_168。
var_178
的值,在trace日志中搜索得到如下内容,我们跳转到了另一个分支中。var_e0
,继续逆推。
data_24c0c0
值得我们关注。sub_F01AC
的param2 对应sub_F77A4
的param1, F01AC的param3对应F77A4的param2。sub_167424
函数结束时的param3对应地址的内容。sub_F01AC
的入参param1进行了运算。SimpleSign入参 -> F01AC(SimpleSign字节数组作为参数) -> 167424 -> F01AC 再计算一次 -> F77A4 MD5计算生成后32位
qword_26FD40
qword_26FE38
byte_26E010
等,因为这些地址的内容中有一些是在so init时赋值,有一些是其他环境影响内容变换,所以,要搞清楚这些是做什么的,怎么做的,才能决定最后16位的内容是怎样的。JNI_OnLoad
的时间差的十六进制,高位为0则为0,与原simplesign计算的后16位的前8为逐个异或。byte_26E010
是否有改动。v18[7] = (v9 << 7) + 8 * v10 + 4 * v11 + 2 * v12 + v13;
sub_ED574
我们得知,此函数的结果是由获取当前time再减去qword_26FD40
得到的。JNI_OnLoad
时被写入了内容。STR
操作也在JNI_OnLoad
中,赋值了255L,那么其内容为0xff。