so逆向
unidbg能跑了,接下就是结合ida进行逆向。
先跳转到函数的地址0xaa73c
很显然,so经过了混淆。实际上,里面很多函数都经过了ollvm平坦化。
接下来,我们要把它进行一个还原。
鼠标放在BX
语句上,Tab
切换到汇编界面
在0xAA7B4下断点
patch这条命令,让它直接跳转到0xaa7b8
变成了sub_AA7B8
,但它显然不是函数,在它上面按U
,把它解析成数据
再按C
,转成代码
在loc_AA7B8
这一段代码的最后按E
,标记其为函数结尾。
再转回到伪代码界面,按F5
可以看到,比之前稍微多了一些代码。继续下断点,patch。
0xAA9F4
下断点
patch,直接跳转0xaa9f8
处理一下
回到伪代码界面F5
又多了一些代码。
代码越来越多了。
0xaa9f8
,很熟悉的数字,实际上,从这里开始,之后都是会跳转到0xaa9f8
,因为这里是ollvm的分发块
接下来的工作,就是不断的把跳转指令BX R0
改为B 0xAA9F8
经过近1个小时的修改,sub_AA73C
变成了一个1000多行代码的函数。
想从里面找到加密的函数不太容易,先hook几个函数看看
看看sub_94DA4
也是被混淆了,先下断点看看(这个函数被调用了几次)
可以看到这里出现了加密结果的一部分数据,而且是关键的数据。
对0xbffff6a8
进行写跟踪,看看哪里对它进行了写入操作
emulator.traceWrite(0xbffff6a8L, 0xbffff6a8L+16);
跳转到写入0xc166a512
的地方,也就是0xaae64
下个断点看看
看看0xbffff6d8
的值
继续对0xbffff6d8
进行写跟踪,看看哪里对它进行了写入
emulator.traceWrite(0xbffff6d8L, 0xbffff6d8L+4);
跳转到0x97ce8
然后就跳转到了一个更多代码的函数,而且这个函数有接近50个传入参数,这显然是不对的
而且从指令看也不太对,没有push
操作。那就从这里往上面找,找到push的地方
最终是在0x9609C
找到了函数sub_9609C
。我们可以像之前一样,下断点,修复跳转的地方,把它修补成一个正常的函数。
也可以直接在函数入口下断点,先看看入参。
blr
在函数返回处下断点,然后c
执行到函数返回处,看看原r2
的值。
cyberchef看看是不是MD5
和unidbg能对上。
继续往下执行,发现sub_9609C
还被调用了,看看数据
这个是url+时间戳重排序+字符串常量
blr
在函数返回处下断点,然后c
执行到函数返回处,看看原r2
的值。
在cyberchef验证下
也能够对上。至此需要的字段都找到了来源,接下来就是算法实现
import hashlib
def calc_cs(url, header, ts):
ts = ts.to_bytes(4, 'big').hex()
print(ts)
data1 = ''.join((header, 'SoulPowerful'))
print(data1)
s1 = hashlib.md5(data1.encode()).hexdigest()
print(s1)
t2 = ''.join(ts[idx] for idx in [1, 4, 2, 7, 6, 0, 5, 3])
data2 = ''.join((url, t2, 'kG@yGB9'))
print(data2)
s2 = hashlib.md5(data2.encode()).hexdigest()
print(s2)
cs = ''.join([
'028f',
s1[0:2],
ts[3],
ts[1],
s1[2:4],
ts[6],
ts[5],
s1[4:6],
ts[4],
ts[0],
s1[6:8],
ts[7],
ts[2],
'776U',
s2[0:8],
'77ac'
])
return cs
def test_cs():
cs = calc_cs('hello', 'everhu', 0x01234567)
print(cs)
assert cs == '028f0b315a6533402772776U12a566c177ac'
总结
-
028f, 776U, 77ac
这几个暂时不完全清楚是怎么来的,只能说cs的逆向完成了一部分。 - ollvm的逆向分析太难了,如果不是刚好找到突破口,或者加密的逻辑再复杂点的话,感觉很难有所收获。暂时还没有能力去除控制流平坦化,以后学会了会再尝试分析。