cxxjackie 发表于 2022-11-6 12:41:27

记一次逆向拆包的过程(二)

前文:https://bbs.tampermonkey.net.cn/thread-3560-1-1.html

用jd-gui打开合并后的jar,搜索encryptString,可以把下面的选项全勾上,逐个查看搜索结果,确定加密函数应该是这个:
`com.cmccit.webview.jssdk.encrypt.EncryptString.invoke`
其中关键加密是这个:`EncryptionLogin.getEncryptTelAndURLEncoder`
直接点击跳转(这也是文件合并的好处),看到代码是先getEncryptTel一下,然后URLEncoder.encode(url编码,相当于js里的encodeURIComponent)。我们跟进getEncryptTel,最终可以定位到这个地方:
!(data/attachment/forum/202211/06/122159zzcm00t77k8k0t33.png)
注意这里用了native关键字,说明该函数来自外部,代码里还有一句System.loadLibrary("jni"),这是加载动态链接库的意思,即这个函数被封装在一个叫jni的动态链接库里。Windows系统的动态链接库通常是dll文件,而安卓则是so文件,位于lib目录下(apk解压后的目录),且命名前会加一个lib,所以我们要找的文件就是libjni.so。
so文件的反编译没有特别好用的工具,一般是用IDA打开,里面一堆汇编指令,按F5转为伪代码帮助阅读(C语言)。IDA有两个快捷方式,这个so是32位,不要用x64的打开,否则按F5没反应。这里就不细说IDA的使用方法了,有兴趣可以自行研究。搜索EncryptTEL,定位到以下代码:
!(data/attachment/forum/202211/06/122439puu9vy94jpdee5pj.png)
这个相对比较难阅读,需要一定C语言基础。翻译一下就是用leadeon+明文+时间(sub_75D0是生成时间的函数)拼接成新的明文,再调用j_algo_encrypt加密。双击j_algo_encrypt可以跟进,不过后面代码比较抽象,其实从各种函数命名上可以看出,这是RSA加密,我们当然不是来探讨RSA加密原理的, 只需知道密钥就够了。考虑到这些APP通常用的公共加密库,代码的分析可以偷个懒,搜几个有代表性的函数名来反推他用的库,于是我搜到了[这篇文章](https://www.cnblogs.com/emyueguang/p/4072906.html)。注意对比这两句代码:
!(data/attachment/forum/202211/06/122641mu3tlgxxxdanudhh.png)
r是密钥,对应&rsa_s1;plaintext是明文,对应拼接后的结果;明文长度是原长度+21,因为“leadeon”7位,时间14位(年4月2日2时2分2秒2),加起来正好21。后面的参数不用管了,我们可以大胆推断,加密公钥就是rsa_s1。双击rsa_s1,按ctrl+x查看调用,找到以下定义处:
!(data/attachment/forum/202211/06/122839mzeolirrrrly7elk.png)
这是一个Elf32_Sym结构,结构的元素是name(aRsaS1)、value(内存地址,1DD68,IDA已帮我们替换为rsa_s1)、size(内存大小)、info、other、shndx。其他元素不用管,只需记住内存大小即可(0x2000对应十进制8192)。双击rsa_s1跳转到内存地址处,看到是一堆0,这是哪里出错了吗?让我们回到前面的分析中,已知加密库为polarssl,那么密钥结构是怎样的?在上面那篇文章中,generate_rsa用于生成密钥,注意这段代码:
!(data/attachment/forum/202211/06/123048emmmgamdygsuugzm.png)
密钥被分为8部分,每个部分是等长的BUFFER_SIZE,8192除以8等于1024,所以密钥是1024位(RSA密钥不是1024就是2048),不足1024位的部分会在前面用0填充,所以真正的密钥应该在后面。验证一下,将1DD68这个地址偏移1024位(0x400)得到1E168,往下拉到这个地址看看:
!(data/attachment/forum/202211/06/123302ghlv0hhh0cbbpkw2.png)
rsa_s1被分为8段,第一段N的取值范围就是1DD68~1E167,可读出N的值为(保留十六进制数):
`A3123079DD30EAF54C8CDC1677576ABD8BEE25938116F64F22F0D5B5D70F0FD6FF40895D6CF554A63F7707B6A5A6EA5C7B114E2D611E2F675726961D0E5CF289B70331E3DDCAFC096E888E686E252E52DBE01D3F3970F4D7F7C44AF1AF01931925798CEEB1CC89239BA8885DF3ED5C79DEF4D71BC578959DCACC70539A2C230D`
同理可得E为`10001`,后面的都是0,这其实就是典型的RSA公钥对,加密只需要n和e就够了(完整的私钥在服务端,非对称加密的特点)。取得公钥后我们其实已经完成了这个算法的破解,即leadeon+UID+时间拼接成明文,然后用公钥做RSA加密,最后encodeURIComponent一下。
js做RSA加密有一个现成的库(https://github.com/travist/jsencrypt),但这个库只能直接设置pem格式的密钥,可以先用别的工具把(n, e)密钥对转成pem格式,不过JSEncrypt的源码其实内含了转换的算法,我们可以利用一下,用这种方式来设置密钥:
```js
const encrypt = new JSEncrypt();
encrypt.setKey();
encrypt.key.setPublic(N, E);
```
setKey实例化一个空密钥,该实例属于库内部的私有类RSAKey,调用这个私有类的setPublic方法直接传入n和e即可。有兴趣的话可以自己研究下这个库的源码。
还有个细节是拼接用的时间是UTC时间,js里的时间是计算本地时区的,用这个函数简单处理一下:
```js
function getUTCTime() {
    return new Date().toISOString().replace(/[-T:]|\..*$/g, '');
}
```
至此我们完成了UID的加密破解,剩下的问题就是怎么得到UID了,后面的分析大部分基于Java源码,就留待下文分解吧。

tfsn20 发表于 2022-11-6 14:16:58

ggnb{:4_94:}

mcroxf 发表于 2022-12-14 22:09:55

能留个联系方式吗,想交流一下技术

cxxjackie 发表于 2022-12-14 23:37:21

mcroxf 发表于 2022-12-14 22:09
能留个联系方式吗,想交流一下技术

有什么问题在论坛里交流呗,我也是班门弄斧哈哈,论坛这方面有研究的人应该不少。

mcroxf 发表于 2022-12-14 23:59:44

cxxjackie 发表于 2022-12-14 23:37
有什么问题在论坛里交流呗,我也是班门弄斧哈哈,论坛这方面有研究的人应该不少。 ...

哈哈,有些需求想给你沟通一下,想有偿请您帮写一下代码

cxxjackie 发表于 2022-12-15 20:22:39

mcroxf 发表于 2022-12-14 23:59
哈哈,有些需求想给你沟通一下,想有偿请您帮写一下代码

我不接单的,代码方面有什么问题可以交流一下,帮写就算了。

sam770 发表于 2023-7-17 12:41:48

我用ida 只能用64的打开jni的库,而且和你的也不一样,这个确认难阅读,感觉要很很多门语言,看来要还补一下C了

cxxjackie 发表于 2023-7-18 22:21:43

sam770 发表于 2023-7-17 12:41
我用ida 只能用64的打开jni的库,而且和你的也不一样,这个确认难阅读,感觉要很很多门语言,看来要还补一 ...

可能apk的版本不一样,也可能你的ida跟我版本不一样,不过看起来加密算法是一样的。这个倒也不用完全读懂,把代码编译一下再套几个字符串进去运行,猜也能猜出来。

sam770 发表于 2023-7-18 23:33:46

cxxjackie 发表于 2023-7-18 22:21
可能apk的版本不一样,也可能你的ida跟我版本不一样,不过看起来加密算法是一样的。这个倒也不用完全读懂 ...

全是靠你的大胆猜测么😂

cxxjackie 发表于 2023-7-20 22:45:44

sam770 发表于 2023-7-18 23:33
全是靠你的大胆猜测么😂

哈哈,说好听点就叫经验之谈了,这个要是一个个深究太费劲了,你看我很多操作都是搜索、猜测、验证,主要还是找对方向。
页: [1] 2
查看完整版本: 记一次逆向拆包的过程(二)