> 技术文档 > 逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

本次主要分析对象网址(base64):5b6u5L+h5bCP56iL5bqP77ya5Yib6KeBICjljY7liJvor4HliLjlrqLmiLfmnI3liqHlubPlj7Ap
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

一、工具准备
1、Fiddler(抓包)
2、微信(小程序)
3、KillWxapkg(反编译、解包、解密)
4、微信开发者工具(小程序代码调试)
4、PyCharm(逆向代码调试)

二、Fiddler抓包
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
三、反编译小程序 解包
1、找到目标小程序的包
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
打开文件夹,然后上一级目录Applet下,这些长长的一串就是打开过的小程序文件,全部删掉,重新打开我们要弄的小程序,就会出现一个文件夹,这个文件夹下就有小程序的包
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
2、解包

  • 用 KillWxapkg 程序进行解包,下面是用法
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

  • 执行命令:KillWxapkg_1.1.0_windows_amd64.exe -id=wx797d2853220c468c -in “F:\\test_files\\149” -out “F:\\test_files\\new_149” -restore -pretty

  • wx797d2853220c468c 小程序的id

  • F:\\test_files\\149 需要解包的目录

  • F:\\test_files\\new_149 解包后的的文件输出目录
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

  • 这是没有解包的样子
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

  • 这是解包后的样子
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译

ok,非常完美,后面就是到微信开发者工具里面运行这个小程序进行调试了

四、调试小程序

  • 因为我们用工具反编译的时候不能全部都编译出来,所以加载后会有很多地方报错,需要哪里报错点哪里进行解决
    1、导入项目
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    2、先把右上角详情-本地设置-去掉一些勾选,如下:
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    我们可以看到模拟器启动失败,这是因为编译的不完全导致的,需要一步步解决
    3、调试项目
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    现在报的是另一个错误,缺少组件,这些组件对于我们来说都没用,我们找到对应的地方删掉就好了
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    删除
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    继续报另一个json文件里面没有组件,继续定位到位置,删除
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    一直报错一直删,根本停不下来,完全停不下来,继续删。。。。。。。
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    这开发者视乎有自己的想法啊
    逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
    终于不报异常错误了,并且小程序的底部出现了正常的图标,下面就可以进行调试了

五、加密入口分析
1、通过刷新首页,可以看到所有接口都带有签名,我们只要找到生成签名的方法,就可以得到sign的值了
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
2、全局搜sign=,可以筛选出三个位置出现url拼接了签名,这就有可能是我们要研究的地方;全部打上断点,进行调试
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
3、通过测试发现这边打上三个断点在调试之后虽然生成了签名,但是最终和服务器校验的时候是失败的,并且服务器返回禁止访问
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
说明这三个地方的都不对,我们换个搜索条件sign: ,发现只有一个关键字出现sign: d,这个感觉就是获取签名的方法了,打上断点,刷新调试一下;
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
4、关键位置下断点,多次跳过断点发现c就是我们要传的参数
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
逆向实战(4)-微信 创见小程序 反编译 解包 接口签名参数研究_微信小程序反编译
这个c参数传的不就是一个翻页数据吗,那我们就只需要把这些代码扣下来缺啥补啥就行了

var c = a.data, l = (0, n.getTime)(), d = (0, n.getSign)(\"CJ\", c), g = o();var data = t(t({}, c), {}, { time: l, sign: d})

三、扣代码阶段
1)、a.data先用 {limit: 20, page: 1} 进行调试;n.getTime 就是o ;n.getSign 就是getSign

o = function () { return Date.parse(new Date) / 1e3}getSign = function (t) { var c = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, a = e(e({}, c), {}, { time: \"\".concat(o()) }), i = Object.keys(a).sort().reduce((function (e, t) { return \"\".concat(e).concat(a[t] ? \"&\".concat(t, \"|\").concat(a[t]) : \"\") }), \"\"), u = { CJ: \"\".concat(n.CJ_KEY).concat(i), WORK: \"\".concat(n.WORK_KEY).concat(i) }; return (0, r.hexMD5)(u[t])}var c = {limit: 20, page: 1}, l = (0, o)(), d = (0, getSign)(\"CJ\", c), g = o();var data = t(t({}, c), {}, { time: l, sign: d})

2)、执行js后报 “e is not defined”,我们一步步跟进取,把所有报错的都扣下来

o = function () { return Date.parse(new Date) / 1e3}function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter((function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable }))), t.push.apply(t, o) } return t}function _typeof(o) { return module.exports = _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) { return typeof o } : function (o) { return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o }, _typeof(o)}function _toPrimitive(r, t) { if (\"object\" !== _typeof(r) || null === r) return r; var e = r[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(r, t || \"default\"); if (\"object\" !== _typeof(i)) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\") } return (\"string\" === t ? String : Number)(r)}function toPropertyKey(r) { var t = _toPrimitive(r, \"string\"); return \"symbol\" === _typeof(t) ? t : String(t)}function defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e}function e(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach((function (r) { defineProperty(e, r, t[r]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach((function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)) })) } return e}getSign = function (t) { var c = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, a = e(e({}, c), {}, { time: \"\".concat(o()) }), i = Object.keys(a).sort().reduce((function (e, t) { return \"\".concat(e).concat(a[t] ? \"&\".concat(t, \"|\").concat(a[t]) : \"\") }), \"\"), u = { CJ: \"\".concat(\"A8-A7-C5-89-CB-33\").concat(i), WORK: \"\".concat(\"A8-A7-C5-89-CB-33\").concat(i) }; return (0, r.hexMD5)(u[t])}function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach((function (r) { defineProperty(e, r, t[r]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach((function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)) })) } return e}var c = {limit: 20, page: 1}, l = (0, o)(), d = (0, getSign)(\"CJ\", c), g = o();var data = _objectSpread2(_objectSpread2({}, c), {}, { time: l, sign: d})

3)、执行js后报 “r is not defined”;r.hexMD5 这很明显是一个md5的加密并以16进制输出;我们直接导个包const CryptoJS = require(“crypto-js”); 因为CryptoJS没有直接的hexMD5功能函数,所以我们使用CryptoJS.MD5().toString() 实现最终的输出

const CryptoJS = require(\"crypto-js\");o = function () { return Date.parse(new Date) / 1e3}function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter((function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable }))), t.push.apply(t, o) } return t}function _typeof(o) { return module.exports = _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (o) { return typeof o } : function (o) { return o && \"function\" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? \"symbol\" : typeof o }, _typeof(o)}function _toPrimitive(r, t) { if (\"object\" !== _typeof(r) || null === r) return r; var e = r[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(r, t || \"default\"); if (\"object\" !== _typeof(i)) return i; throw new TypeError(\"@@toPrimitive must return a primitive value.\") } return (\"string\" === t ? String : Number)(r)}function toPropertyKey(r) { var t = _toPrimitive(r, \"string\"); return \"symbol\" === _typeof(t) ? t : String(t)}function defineProperty(e, r, t) { return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e}function e(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach((function (r) { defineProperty(e, r, t[r]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach((function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)) })) } return e}getSign = function (t) { var c = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}, a = e(e({}, c), {}, { time: \"\".concat(o()) }), i = Object.keys(a).sort().reduce((function (e, t) { return \"\".concat(e).concat(a[t] ? \"&\".concat(t, \"|\").concat(a[t]) : \"\") }), \"\"), u = { CJ: \"\".concat(\"A8-A7-C5-89-CB-33\").concat(i), WORK: \"\".concat(\"A8-A7-C5-89-CB-33\").concat(i) }; return CryptoJS.MD5(u[t]).toString()}function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach((function (r) { defineProperty(e, r, t[r]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach((function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)) })) } return e}var c = {limit: 20, page: 1}, l = (0, o)(), d = (0, getSign)(\"CJ\", c), g = o();var data = _objectSpread2(_objectSpread2({}, c), {}, { time: l, sign: d})console.log(data)// 输出结果{ limit: 20, page: 1, time: 1749458130, sign: \'fb6ac10e0902a66cb82b89bed61b666a\'}

4)、代码扣完了,后续就是简化整合然后调用了

总结:扣js代码是一件枯燥的过程,只要我们一点一点的去分析,总会找到关键位置的,加油~~~

逆向实战(3)-雪球 金融韭菜网站研究(请求参数md5__1038加密、js混淆、逆向))