63

[原创]**手910版本sig3 48位算法逆向分析

 2 years ago
source link: https://bbs.pediy.com/thread-271489.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
[原创]**手910版本sig3 48位算法逆向分析
2022-2-14 23:35 8746

目录

1、unidbg调用sig3算法

龙哥之前发布了使用unidbg调用sig3的demo,下载这个demo直接跑。

2、libksgmain.so去花

sig3总所周知实在libksgmain.so中,ida打开改so,Jni_onLoad函数初步预览下。

JNI_OnLoad

可以看到此处为花指令,插花的方式和大佬发的一篇ali的libsgmain.so中的花指令类似

花指令

函数sub_ce88 在pop时修改了pc指针的值。针对这类花指令可以写脚本进行修复,脚本如下:

import idc
from ida_bytes import patch_word
def put_unconditional_branch(source, destination):
offset = (destination - source - 4) >> 1
if offset > 2097151 or offset < -2097152:
raise RuntimeError("Invalid offset")
if offset > 1023 or offset < -1024:
instruction1 = 0xf000 | ((offset >> 11) & 0x7ff)
instruction2 = 0xb800 | (offset & 0x7ff)
patch_word(source, instruction1)
patch_word(source + 2, instruction2)
patch_word(source + 4, 0xbf00)
patch_word(source + 6, 0xbf00)
else:
instruction = 0xe000 | (offset & 0x7ff)
patch_word(source, instruction)
patch_word(source + 2, 0xbf00)
patch_word(source + 4, 0xbf00)
def patch(ea):
if idc.get_wide_word(ea) == 0xb503# PUSH {R0,R1,LR}
ea1 = ea + 2
# 目的寄存器是通用寄存器 and 目的寄存器是r0 and 源寄存器是立即数
if idc.get_operand_type(ea1, 0) == 1 and idc.get_operand_value(ea1, 0) == 0 and idc.get_operand_type(ea1, 1) == 2:
# get_operand_type 获取寄存器的类型
index = idc.get_wide_dword(idc.get_operand_value(ea1, 1))
print("index =", hex(index))
ea1 += 2
table = None
if idc.get_operand_type(ea1, 0) == 7:
print(222)
# BL              loc_80A30
# 获取tabel表
table = idc.get_operand_value(ea1, 0) + 4
if table is None:
print("Unable to find table")
else:
print("table =", hex(table))
offset = idc.get_wide_dword(table + (index << 2))
put_unconditional_branch(ea, table + offset)
# 0x2f150
if __name__ == '__main__':
# so中.text段的起始与结束位置
for i in range(0x7780, 0x8dda0):
patch(i)

去花后可以快乐的f5

去花后JNI_onLoad

对应汇编

3、unidbg 调用去花后的libsgmain.so

龙哥提供的unidbg调用的sig3中是调用apk内部的libsgmain

DalvikModule dm = vm.loadLibrary("kwsgmain", true);

我们是否可以使用调用外部libsgmain.so的方式来调用我们去花之后的libsgmain.so呢?说干就干,把这行给注释掉,换成

DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/ks910/libkwsgmain.so"), true);

RUN RUN RUN RUN RUN(蒙多,想怎么RUN就怎么RUN)

[23:44:46 552]  INFO [com.github.unidbg.linux.AndroidElfLoader] (AndroidElfLoader:459) - libkwsgmain.so load dependency libc++_shared.so failed
stack  ts
[23:44:46 633]  INFO [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:828) - pthread_clone child_stack=RW@0x403be930, thread_id=1, fn=RX@0x401837f5[libc.so]0x3f7f5, arg=RW@0x403be930, flags=[CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_THREAD, CLONE_SYSVSEM, CLONE_SETTLS, CLONE_PARENT_SETTID, CLONE_CHILD_CLEARTID]
stack  ts
stack  ts
JNIEnv->GetStringUtfChars("/data/app/com.smile.gifmaker-oyRnT1esU1Pf5iDY6JKtjA==/base.apk") was called from RX@0x400451a5[libkwsgmain.so]0x451a5
JNIEnv->ReleaseStringUTFChars("/data/app/com.smile.gifmaker-oyRnT1esU1Pf5iDY6JKtjA==/base.apk") was called from RX@0x4000e305[libkwsgmain.so]0xe305
stack  ts
stack  ts
stack  ts
stack  ts
JNIEnv->GetStringUtfChars("d7b7d042-d4f2-4012-be60-d97ff2429c17") was called from RX@0x40051e53[libkwsgmain.so]0x51e53
stack  ts
JNIEnv->GetStringUtfChars("com.smile.gifmaker") was called from RX@0x4000de87[libkwsgmain.so]0xde87
JNIEnv->ReleaseStringUTFChars("com.smile.gifmaker") was called from RX@0x4000e305[libkwsgmain.so]0xe305
[23:44:47 663]  WARN [com.github.unidbg.arm.AbstractARMEmulator] (AbstractARMEmulator$1:58) - memory failed: address=0x7084, size=1, value=0x0, PC=unidbg@0x7084, LR=RX@0x4004d68d[libkwsgmain.so]0x4d68d
[23:44:47 663]  WARN [com.github.unidbg.AbstractEmulator] (AbstractEmulator:389) - emulate RX@0x40053129[libkwsgmain.so]0x53129 exception sp=unidbg@0xbfffec88, msg=unicorn.UnicornException: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED), offset=1003ms
Exception in thread "main" java.lang.NullPointerException
at com.ks910.kwsgmain910.callInit(kwsgmain910.java:97)
at com.ks910.kwsgmain910.main(kwsgmain910.java:73)

3、1 替换外部so报错?尝试查找原因

想法是美好的,现实是残酷的报错了----

会不会是patch的so有问题?

为了验证这一点,我们使用apk原始so,使用加载外部so的方式来进行加载,结果也出错 ,无奈只有换个思路加载外部so

3、2 换个思路加载外部so

竟然demo只能加载apk内部so的方式才能运行成功,我们是否可以通过把apk lib目录下的原始so替换成我们已经patch之后的so,来让demo程序加载内部so的时候加载我们patch的so?

答案是可以,sig3已被计算出来

JNIEnv->ReleaseStringUTFChars("d7b7d042-d4f2-4012-be60-d97ff2429c17") was called from RX@0x4000e305[libkwsgmain.so]0xe305
result:d3c2b2915f3804b59b9b9899f95a9abe878c3ae0868a8492

4、trace还原算法

上一步为什么去花?就是为了trace分析算法的时候能少些干扰,另外还可以减少trace后的指令。

4、1trace配置

在get_NS_sig3函数开始trace,过滤掉一些非so的trace。 开始trace!

4、2 从trace文件中寻找输入

已知unidbg中的输入为:

/rest/n/comment/list/firstPagefcac84fe7071434ad19cc4771890acef

使用谷歌浏览器的hackbar插件对输入的数据进行hexdump

2f726573742f6e2f636f6d6d656e742f6c6973742f6669727374506167656663616338346665373037313433346164313963633437373138393061636566

用01edit打开trace文件,以4位一组进行搜索,即搜索0x2f726573

trace搜索结果

用ida打开libsgmain.so 跳转到0x2d4b6 地址,f5 该方法发现有明显的sha256算法特征

之前逆向过老版本的sha256,这是魔改的sha256算法,直接用原来的算法,改掉两个数组的值

把a6、a7地址的值dump下来就行。

ida中结果位置

trace中结果

算法还原结果

可以看到结果一致,再次成功还原sha256

4、3 抽丝剥茧寻找下一步

为了减少trace文件对我们的干扰,把结果之前的代码全部删除,再次搜索sha256之后的结果,无果。我们尝试搜索下字节。

找到算法部分

hook一下 a2

内容就是对sha256进行乱序并扩张内容为0x10个0x10字节.v24 是什么? 交叉引用看下

算法明了了,不能说和mt的动态计算sha256的key的算法有什么不同,只能说完全一摸一样。。。 把数据dump下来,开始对比结果

4、4 通过结果进行回溯

每次运行demo发现sig3的结果都不同,r1和r0的值进行了改变。使用unidbg traceWrite 0xbffff6b8处的地址,查看内存情况进行对比

emulator.traceWrite(0xbffff6b8L, 0xbffff6b8L + 0x16);
one
### Memory WRITE at 0xbffff6bc, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6c0, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6c4, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6c8, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6cc, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6b8, data size = 4, data value = 0x5141
### Memory WRITE at 0xbffff6ba, data size = 2, data value = 0x22
### Memory WRITE at 0xbffff6bc, data size = 4, data value = 0x2acf568f
### Memory WRITE at 0xbffff6c4, data size = 4, data value = 0x2306c567         // same
### Memory WRITE at 0xbffff6c8, data size = 4, data value = 0x61ba1ea6
### Memory WRITE at 0xbffff6c0, data size = 4, data value = 0x1
### Memory WRITE at 0xbffff6cc, data size = 4, data value = 0xd00
### Memory WRITE at 0xbffff6cc, data size = 4, data value = 0x2c000d00
two
### Memory WRITE at 0xbffff6bc, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6c0, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6c4, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6c8, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6cc, data size = 4, data value = 0x0
### Memory WRITE at 0xbffff6b8, data size = 4, data value = 0x5141
### Memory WRITE at 0xbffff6ba, data size = 2, data value = 0x22
### Memory WRITE at 0xbffff6bc, data size = 4, data value = 0x6c4d7d4f
### Memory WRITE at 0xbffff6c4, data size = 4, data value = 0x2306c567    //same
### Memory WRITE at 0xbffff6c8, data size = 4, data value = 0x61ba1ec1
### Memory WRITE at 0xbffff6c0, data size = 4, data value = 0x1
### Memory WRITE at 0xbffff6cc, data size = 4, data value = 0xd00
### Memory WRITE at 0xbffff6cc, data size = 4, data value = 0x6a000d00

可发现有些值一样有些不一样,通过更改参数进行对比 可总结(只记录不为0的数据)

地址 值 固定/or改变 附加

0xbffff6b8 0x5141 固定

0xbffff6ba 0x22 固定

0xbffff6bc 0x6c4d7d4f 随机

0xbffff6c4 0x2306c567 输入相同固定

0xbffff6c8 0x61ba1ec1 随机

0xbffff6c0 0x1 固定 每调用一个sig3加1

0xbffff6cc 0xd00 固定

0xbffff6cc 0x6a000d00 固定 6a位sig3最后两位

(经后面发现,如果连续调用两次sig3,则随机部分都不会改变,只有0xbffff6c0处的值会根据调用次数而增加)

通过这张表,最主要的工作就是0x2306c567和0x6a数据的来源,在trace文件中搜索

一个数和0xfffffff进行异或???

最终根据trace文件查找如下函数。

接下来就是0x6a的值 同样在trace文件中搜索。

0xfffffba3 & 0xFF = 0xa3
0x100000000 - 0x45d = 0xfffffba3

0x45d 不就是之前0xbffff6b8 数据之和吗?

5、验证算法正确性

5、1hook+postern进行抓包

具体参考短视频最新版通用quic协议解决方案

5、2对请求重发进行抓包

原始sig3

算法计算后的sig3

替换后结果

【公告】看雪团队招聘安全工程师,将兴趣和工作融合在一起!看雪20年安全圈的口碑,助你快速成长!

最后于 2022-2-14 23:47 被hczhong编辑 ,原因:

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK