首页
社区
课程
招聘
5
[原创]微信小程序逆向
发表于: 2025-3-26 11:29 2447

[原创]微信小程序逆向

2025-3-26 11:29
2447

首先我用一道CTF题目来说说微信小程序逆向是什么,以及如何去解密,拆包

题目的附件是这两个

1.微信小程序的架构

1.1 小程序的文件组成

  • WXML:微信标记语言,用于描述页面结构,类似 HTML。
  • WXSS:微信样式表,类似 CSS,用于定义页面样式。
  • JS:业务逻辑代码,处理交互和数据处理。
  • JSON:配置文件,描述页面配置、路由、全局配置等。

1.2 开发与编译流程

  • 开发阶段:开发者编写上述文件,通过微信开发者工具进行调试。
  • 编译阶段:开发者点击上传或预览时,微信开发者工具将源代码经过压缩、混淆、整合等处理,生成一个或多个 .wxapkg 文件。
    • JS 文件会被压缩合并到 app-service.js 中;
    • WXML 文件与 WXSS 文件经过处理整合到 page-frame.html 或其他文件中;
    • 配置文件合并为 app-config.json 等。

加密与混淆手段

压缩与混淆:代码经过压缩,变量名缩短,格式被压缩成一行。

模板转换:WXML 模板会被编译为一段 JavaScript 代码,通过特殊的模板解析函数(例如 $gwx 等)还原页面视图。

加密算法:部分逻辑(如 CTF 题目)可能嵌入自定义加密算法,例如使用类似 RC4 的 S-box 生成、异或操作等。


1.解密微信小程序

https://github.com/Angels-Ray/UnpackMiniApp

首先下载这个工具

之后会看到这个应用程序,首次使用会有点卡,之后双击选择你要解密的小程序


因为这个题目是没有加密的,所以会显示无需解密

2.拆包

https://github.com/Fickley/wxappUnpacker

之后下载这个工具,将wxapkg程序包复制到反编译脚本目录wxappUnpacker里面,依次安装以下依赖

这是用来拆包的可以理解,但是首次使用需要配置一下

npm install esprima
npm install css-tree
npm install cssbeautify
npm install vm2
npm install uglify-es
npm install js-beautify

之后就可以开始反编译了


node wuWxapkg.js 123.wxapkg

使用这个命令之后会在这个目录里面看到一个同名文件夹,打开就会看到代码了

之后咱们就可以来做题了

首先打开这个wxml的文件


<!-- index.wxml -->
  <navigation-bar title="Weixin" back="{{false}}" color="black" background="#FFF"></navigation-bar>
  <scroll-view class="scrollarea" scroll-y="true" scroll-with-animation="true" style="height: 100vh;">
  <view class="container">
  <view animation="{{animationData}}" class="input-container">
  <input 
  class="cool-input" 
  placeholder="请输入内容..." 
bindinput="onInputChange"
value="{{inputValue}}"
  />
  <icon type="search" class="input-icon"></icon>
  </view>
  <button animation="{{animationData}}" class="check-button" bindtap="onCheck" style="position: relative; left: -1rpx; top: -981rpx">Check</button>
  </view>
  </scroll-view>

发现逻辑在index.js代码下面,之后我们把刚刚的文件夹拖进webstorm

但是大家可以发现这个js代码的格式好像不太对,这里可以用

https://beautifier.io/

这个工具进行美化处理

可以看到十分的清晰明了,之后分析发现就是一个rc4加密,直接上脚本

def generate_sbox(key):
    # 初始化数组 a 和辅助数组 e
    a = list(range(256))
    n = len(key)
    e = [ord(key[i % n]) for i in range(256)]
    i = 0
    for r in range(256):
        i = (i + a[r] + e[r]) % 256
        # 交换 a[r] 和 a[i]
        a[r], a[i] = a[i], a[r]
        # 同时交换 a[(r+1)%256] 和 a[(i+1)%256]
        r1 = (r + 1) % 256
        i1 = (i + 1) % 256
        a[r1], a[i1] = a[i1], a[r1]
    return a

def keystream(sbox, length):
    # 复制 sbox 以避免改变原数组
    n = sbox[:]
    e = 0
    o = 0
    stream = []
    for i in range(length):
        o = (o + n[i % 256]) % 256
        e = (e + n[o]) % 256
        # 交换 n[o] 与 n[e]
        n[o], n[e] = n[e], n[o]
        s = n[(n[o] + n[e]) % 256]
        stream.append(s)
    return stream

if __name__ == '__main__':
    key = "NSSCTF2025"
    # 固定的目标数组
    fixed = [216, 156, 159, 86, 8, 143, 254, 92, 113, 3, 228, 74, 37, 80, 146, 68, 71, 42, 137, 132, 170, 85, 13, 196, 226, 152, 120, 176, 184, 36, 195, 233, 123, 230, 89, 10, 121, 180, 5, 219]
    L = len(fixed)
    # 生成 S-box
    sbox = generate_sbox(key)
    # 生成 keystream 对应长度
    stream = keystream(sbox, L)
    # 根据公式: input_char = output_char XOR keystream_value
    flag_chars = [chr(c ^ s) for c, s in zip(fixed, stream)]
    flag = "".join(flag_chars)
    print("Flag is:", flag)
#Flag is: NSSCTF{c6e67111c1aadd1bdc4dad6d99c254e7}



[注意]看雪招聘,专注安全领域的专业人才平台!

收藏
免费 5
支持
分享
赞赏记录
参与人
雪币
留言
时间
sinker_
为你点赞!
2025-4-4 00:05
Ally Switch
+1
谢谢你的细致分析,受益匪浅!
2025-4-3 16:40
mb_ayzyvxye
期待更多优质内容的分享,论坛有你更精彩!
2025-3-29 09:18
AStephen
谢谢你的细致分析,受益匪浅!
2025-3-27 08:46
L_dd
为你点赞!
2025-3-26 19:06
最新回复 (2)
雪    币: 6585
活跃值: (5802)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
把测试样本给一下
2025-3-26 21:07
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
支持你楼主,我学识太浅,看不太懂,只能仰望你们这些大佬
2025-3-27 07:31
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册