某苏人社异步JS逆向加解密分析(sm2+sm3+sm4)
前期准备[*]网址:aHR0cHM6Ly9ycy5qc2hyc3MuamlhbmdzdS5nb3YuY24vaW5kZXgv
[*]目标:
[*]接口请求内容加密和响应内容解密
[*]请求头加密参数 Web-Encrypt-Response-Encrypt-Key和Web-Encrypt-Sign
[*]涉及内容:
[*]js worker 多线程通信
[*]axios网络请求库
[*]国密sm2、sm3、sm4
Axios
网址:Axios
这个网站用到了 axios,可以先了解一下 axios 这个网络请求库,主要是interceptor这块
拦截器
在请求或响应被 then 或 catch 处理前拦截它们。
// 添加请求拦截器axios.interceptors.request.use(function (config) { // 在发送请求之前做些什么return config;},function (error) { // 对请求错误做些什么return Promise.reject(error);});// 添加响应拦截器axios.interceptors.response.use(function (response) { // 2xx 范围内的状态码都会触发该函数。 // 对响应数据做点什么return response;},function (error) { // 超出 2xx 范围的状态码都会触发该函数。 // 对响应错误做点什么return Promise.reject(error);});分析流程
先来看一下要逆向的内容
如上图所示,获取考试列表(getExamTaskIM)这个接口的请求和返回数据都是加密的
请求加密分析
老规矩直接看堆栈,进去第一个下个断点,刷新页面
成功断下后发现 xhr send 发送已经完成了加密
往上找一下堆栈,发现一个axios的请求拦截器,直接下断点刷新
断住之后可以追进去看一下
有四个拦截器,分别进去下一个断点去查看,最终在最后一个拦截器发现关键数据
这是一个与worker进行通信的异步函数,我们往下看看其他逻辑
发现有一个onmessage的回调方法,我猜测加密流程是这样的:
[*]主线程发送明文数据(postMessage)
[*]work 线程接收并开始加密(onMessage)
[*]work 线程加密完后发送加密数据给主线程(postMessage)
[*]主线程接收加密数据(onMessage)
异步方法大都有一个回调的过程
下图主线程为postMessage一方,其它线程则需要使用onMessage接受数据
接着分析流程,我们先在这个onmessage回调方法内下段,再跟堆栈就容易找到调用位置了
找到postMessage后往上看看就能发现关键加密逻辑
[*]源流程代码,大量的 promise
self.onmessage = function(t) { var r, e, o, h, a, f, c; (r = t.data.prefix, e = t.data.data, o = e._s1, h = e._s2, a = e.requestData, f = Math.floor(Date.now() / 1e3).toString(), c = function() { return 2 === s } , u.timestamp = f, new Promise((function(t, e) { new Promise((function(t, e) { try { t({ key: r + i.default._s2EN(o, h, 0) })... }... new Promise((function(t, e) { try { var o = r + i.default._s2EN(JSON.stringify(a), h, 0) , s = f + o; t({ content: o, signature: (0, n.default)(s) })... }... }... }进到这里了直接单步调试就是了,直接跟到_s2EN 函数里,这里就是加密函数了,加密函数用到了两次,一次加密 sign,一次加密 payload
这个其实就是sm2标准算法,看方法名就能猜出个大概了,就懒得写过程了,直接给出个大概的代码吧
var prefix = "04";// 解密response的keyvar _s1 = "862344dec7e0907a2b215c37a57caf95";// 加密data的publicKeyvar _s2 = "04fc439405f925df23510517e1e5a8078d19b23b24d62190c40e632f1d0bcd784fc6fcf1a8c3b5cf7f422815c6b322176e89f56f781ccd3c36aa02e5d31400090a";// 计算keykey = prefix + sm2(_s1, _s2, 0);// 加密payload,计算signpayload = { "bge304": 202432990000309, "bge316": "320199"}var timestamp = Math.floor(Date.now() / 1000).toString();var content = prefix + sm2(JSON.stringify(payload), _s2, 0)var sign = sm3(timestamp + o);headers= { "Web-Encrypt-Response-Encrypt-Key": key, "Web-Encrypt-Sign": sign , "Web-Encrypt-Timestamp": timestamp}sign 加密过程忘记截图了,实际上就是个sm3加密
完成以上请求头和请求数据的加密后,就可以正常请求拿到返回数据了
响应解密分析
跟找加密过程差不多,直接找到响应拦截器就行
挨个下断点,发现第一个很像了
这个web-encrypt-sign是响应头里的一个值,往下翻翻会发现熟悉的postMessage和onMessage
这里过程跟加密差不多就不详细写过程了,直接找关键点
看方法名能猜出个大概是sm4解密,解密的 key 就是之前传入的_s1
随便抓个包解密试一下,可以看到正常解密成功,解密出来的 response 还需要 html 实体解码一下才行
Python 还原实现
[*]代码:
[*]成果:
大功告成!
微信公众号
公众号更新比较快,欢迎关注!
页:
[1]