行业新闻

某APP sig3 48位算法逆向分析

某APP sig3 48位算法逆向分析

1、unidbg调用sig3算法

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

2、libksgmain.so去花

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

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

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

去花后可以快乐的f5。

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

[23:44:46 552]  INFO [com.github.unidbg.linux.AndroidElfLoader] (AndroidElfLoader:459) - libkwsgmain.so load dependency libc++_shared.so failedstack  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  tsstack  tsJNIEnv->GetStringUtfChars("/data/app/com.smile.gifmaker-oyRnT1esU1Pf5iDY6JKtjA==/base.apk") was called from RX@0x400451a5[libkwsgmain.so]0x451a5JNIEnv->ReleaseStringUTFChars("/data/app/com.smile.gifmaker-oyRnT1esU1Pf5iDY6JKtjA==/base.apk") was called from RX@0x4000e305[libkwsgmain.so]0xe305stack  tsstack  tsstack  tsstack  tsJNIEnv->GetStringUtfChars("d7b7d042-d4f2-4012-be60-d97ff2429c17") was called from RX@0x40051e53[libkwsgmain.so]0x51e53stack  tsJNIEnv->GetStringUtfChars("com.smile.gifmaker") was called from RX@0x4000de87[libkwsgmain.so]0xde87JNIEnv->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=1003msException 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]0xe305result: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。

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

 

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

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

 

可以看到结果一致,再次成功还原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的数据)。

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

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

一个数和0xfffffff进行异或?

 

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

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

0xfffffba3 & 0xFF = 0xa3

0x100000000 - 0x45d = 0xfffffba3

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

5、验证算法正确性

5.1hook+postern进行抓包

具体参考短视频最新版通用quic协议解决方案(https://bbs.pediy.com/thread-268651.htm)

5.2对请求重发进行抓包

 

关闭