首页 / 资讯中心 / 文章详情

小程序安全测试实战:接口收集、路由枚举与Frida注入全链路解析

小程序安全测试实战:接口收集、路由枚举与Frida注入全链路解析 ★ FEATURED ARTICLE
小程序挖洞这些年一直没有凉下来反而越来越多人往这个方向钻。原因很简单小程序是很多企业业务的第一入口开发迭代快、测试覆盖不全接口和逻辑暴露面比传统Web更宽出问题的概率自然高。但真上手做测试的人都知道小程序挖洞真正难的不是漏洞本身而是前面那段又臭又长的信息收集小程序包怎么拿、接口怎么扒干净、路由怎么枚举全、加解密逻辑怎么Hook。这些环节一旦靠人肉去做效率拉满也就那样。我前阵子把一套完整的小程序测试流程重新梳理了一遍也顺手把所有踩过的坑串成了工具链集成接口信息收集、路由枚举和Frida注入三个核心能力真正跑通了从静态分析到动态验证的完整链路。这篇就当一次阶段性总结把设计思路、实操细节和典型问题都摊开讲。1. 工具整体设计思路与选型逻辑1.1 传统小程序测试流程的痛点先说痛点。以前做小程序测试我经历过三个阶段现在回头看全是低效。第一个阶段是纯手工抓包代理一开小程序放手机里一顿点Burp里做一个标记一个。这个方法最直观但明显有问题你点的路径永远有限很多接口藏在深层页面或特定业务逻辑后面你压根触发不到。点了一个小时也就收集到几十个接口覆盖面全靠运气。第二个阶段是静态代码分析。把小程序包解开JS代码拿到手一把梭读文件、抠字符串能翻出不少接口地址和参数名。但同样是纯体力活代码量大一点、混淆深一点就非常痛苦而且静态看到的东西没法确认是否真实可用。第三个阶段是有人尝试把Frida拉进来做动态分析。思路没问题但Frida这玩意本身上手门槛不低写Hook脚本要时间要处理各种App加固对抗还得考虑进程、类名、方法签名一套搞下来真正留给漏洞分析的时间不多了。所以我做这个工具的核心逻辑很直接把上面三个阶段的活儿串成一条流水线静态部分自动化跑动态部分提供顺手可调的注入能力让测试员把精力放在漏洞判断上而不是耗在收集和Hook基建上。1.2 一体化设计前静态后动态的双阶段模型整个工具的设计我总结成一个双阶段模型先用静态手段把能拿到的信息全量扒下来再用动态手段去验证和补齐。这两步不是并列关系而是前后递进关系。静态阶段负责的是广度主要做三件事解密拿到小程序原始包、从JS里提取接口列表、按路由配置生成可枚举的路径集合。这个阶段追求的是宁可多抓不能漏掉。动态阶段负责的是深度核心就是Frida注入。静态阶段发现的加密参数、敏感函数、关键加解密逻辑都放到动态阶段去Hook验证。另外静态分析不到的内容比如运行时才生成的接口、动态拼接的URL、App内部加密的请求参数也得靠动态手段补上。为什么一定要集成而不是分开用因为分开用意味着数据要在多个工具之间手动搬运比如这边静态分析接口清单那边又要在Burp里对着找中间损耗极大。一体化的好处是静态阶段产出的接口列表自动进入测试面管理路由枚举的结果直接和Frida目标关联一次配置就能全链路推进。1.3 为什么选这三个模块组合接口信息收集、路由枚举、Frida注入这三者表面上互相独立实际是一条完整的知识链。接口信息收集解决的是有哪些数据通道的问题。小程序业务再复杂最后都要通过接口和后端交互把接口列表拉出来就是拿住了测试面的骨架。路由枚举解决的是有哪些页面入口的问题。小程序天然是路由驱动的页面的跳转依赖路径配置路由枚举就是把页面和接口串起来知道页面路径才能更容易推断哪些接口被业务调用了知道接口列表也能反推哪些路由是核心功能页。Frida注入解决的是逻辑到底怎么走的问题。接口和路由是静态世界的描述真实逻辑还得看运行时。加解密算法、签名生成、token更新、风控参数这些必须动态Hook才能拿下。所以这个三角色合体本质上是在解决同一个问题让被测小程序在测试者面前没有秘密。2. 三大核心能力拆解从收集到注入一条链2.1 接口信息收集模块静态提取与流量补全接口信息收集是整条链路的起点。这步做不好后面全是空中楼阁。先说静态提取的思路。现在的小程序大多是用微信开发者工具编译发布的包结构比较固定核心文件是wxapkg格式。工具先做包解密把wxapkg还原成可读的代码然后对JavaScript代码做全面扫描。扫描的对象不只是字面量URL字符串还包括常见的路径拼接模式比如baseUrl /api/user/info这种写法在代码里极其常见单纯匹配完整URL会漏掉很多。我的做法是同时维护一套正则规则和一套语义规则。正则规则负责找显式的接口地址语义规则负责找看起来在拼接接口的代码模式比如包含api、request、fetch、url等特征变量的字符串块。两条规则交叉结果再去重合并。但静态只是第一步流量补全才是接口收集闭环的关键。小程序在真实运行过程中会产生大量静态代码里看不出来的接口请求动态拼接的路径参数、服务端下发的接口地址、第三方SDK自带的请求。这些必须靠流量侧补齐。工具里的做法是把Burp或代理流量的请求日志导入自动和静态接口列表做比对已覆盖的标记为已验证未覆盖的追加进来标记为动态发现。这样最终生成的接口清单自带状态标签哪些静态确认、哪些流量发现、哪些待验证一眼就能看清楚。2.2 路由枚举模块不靠目录爆破靠代码路径看到路由枚举这四个字很多做Web测试的人第一反应是目录爆破——拿字典去扫路径。但小程序里的路由枚举完全是另一套逻辑扫字典效率极低因为小程序的路由路径不遵循通用目录规则通常是自定义的page路径配合分包、tabBar、登录态跳转判断纯爆破基本是盲人摸象。所以工具里的路由枚举本质是分析源码中的路由配置和页面跳转逻辑。小程序的路由信息主要藏在三个地方app.json里的pages字段、各分包配置的subPackages字段、以及代码中wx.navigateTo、wx.redirectTo、wx.switchTab等跳转API的目标路径参数。工具在静态阶段拿到这些信息后会做三件事第一步解析pages配置建立全量路由清单第二步扫描代码里的跳转API调用把没有被pages配置覆盖到、但确实存在的动态路由补进清单第三步结合分包逻辑把路由按业务模块分组形成一棵路由树。为什么要分组而不是平铺因为测试时要按业务边界去打点一个电商小程序可能同时有订单模块、支付模块、营销模块、售后模块它们的数据权限边界通常独立。路由树直接展示了模块边界测试员就能按模块分别做越权测试效率提升是肉眼可见的。路由枚举的输出格式上工具支持导出为JSON或表格字段包括路由路径、所在分包、跳转方式、对应页面文件、相关接口引用数。其中相关接口引用数是我特别加的一项一个页面引用的接口越多说明它的功能越复杂越值得优先测试。2.3 Frida注入模块动态验证的关键Frida注入是这条链路里技术含量最高、坑也最多的部分但它的价值无可替代。小程序虽然跑在宿主App的容器里但它的代码逻辑最终是在App的JS引擎中执行的。因为小程序页面和业务代码的运行确实是在App进程里运行时所有的JavaScript对象、方法调用、参数传递都在内存中发生。Frida能做到的就是attach到目标App进程拿到这个运行现场的访问权。实际测试中Frida注入的典型场景包括四类第一类Hook加密函数。很多小程序的请求体不是明文而是走了类似RSA、AES这类加密逻辑秘钥和IV可能硬编码在JS里也可能存在App的Native层。Hook住加密函数直接读参数和返回值加解密过程全透明。第二类Hook关键业务函数。比如登录、支付、分享等核心方法注入之后可以看到完整调用参数方便后续做参数篡改测试。第三类绕过SSL Pinning。小程序很多请求走了证书绑定用Burp中间人代理抓不到包这时就需要Hook掉底层验证逻辑让Burp能正常解密流量。第四类分析混淆代码。JS代码混淆后静态看基本没法读但运行时经过JS引擎处理后逻辑是展开的Hook关键方法把参数传进去、把返回值打出来能反推出代码在干什么。工具在这块的定位不是替代你写Hook脚本而是把常用的注入逻辑模板化。比如抓包绕过、加解密提取、通用参数打印内置几套常用脚本选中目标App进程就能跑出结果。遇到业务定制逻辑的时候再在这个基础上改。3. 实操落地完整跑通一次小程序测试3.1 基础环境准备PC端手机端按流程走之前先把环境搭好。这套流程我一般用一台Mac或Linux做主力分析机一台Android测试机做动态执行实测下来整体最顺。PC端需要准备的东西Python环境跑工具链的主逻辑一般3.8以上就够抓包工具用Burp任意版本都可以重点是把代理端口和证书配好如果是解包和做JS分析还会用到Node.js环境部分小程序代码需要靠Node跑起来才能做动态调试。Android测试机这边最重要的一件事是root。Frida注入基本上要求root权限非root设备虽然有一些变通方案比如用Magisk模块或者虚拟环境去跑但稳定性差很多。我个人的建议是准备一台老一点但系统版本稳定的root手机专门跑这个流程别用主力机折腾。手机端还要配Frida Server版本必须和PC端的frida库严格一致。这个点经常有人踩坑版本对不上直接报错Injecting failed。然后说抓包配置。在手机设置里把WiFi代理指到PC的Burp监听端口再把Burp的CA证书装进系统证书目录。这一步在小程序测试里尤其重要因为小程序和普通App的证书校验方式不同如果只装用户证书部分小程序请求能抓到但加载WebView里H5页面时可能校验通过不了。所以证书要放到系统级目录方法不复杂网上主流方案都能用重点是你得知道为什么要放系统目录。3.2 接口收集实操步骤环境就绪后开始跑接口收集我习惯分四步走。第一步拿包。PC端微信打开目标小程序让小程序完整加载一遍然后去微信的缓存目录里找对应的wxapkg文件。文件找出来后用工具自带的解密模块处理。为什么要解密因为小程序发布的包是加密过的不处理直接解压全是一堆密文。解密逻辑微信的算法是固定的工具内部已经处理好了你不用管细节拿到手的是一个可以正常展开的目录结构就行。第二步静态提取。把整个小程序代码目录扔给工具跑一遍接口提取。这一步主要依赖前面说的双规则扫描结束后会生成一个原始接口清单标记来源是静态扫描。我看过不少实际项目这一步基本能覆盖掉60%到70%的接口覆盖率已经相当可观。第三步流量补全。手机端打开小程序把核心业务路径都走一遍。这一步建议有点思路地走不要乱点按业务模块功能点逐个触发。比如一个外卖小程序你要把下单、支付、发券、评价、退款这些流程都跑完。跑的过程Burp会记录所有请求跑完导出流量导入工具和静态清单合并接口覆盖率能拉到85%以上。第四步人工复核。剩下15%左右的接口要么是特殊触发条件下才产生的要么是服务端动态下发地址。工具会把这些标成待验证测试员根据业务理解去判断是否需要补齐。这里有个小细节接口清单里可能会有大量静态分析出来的无用地址比如第三方统计SDK的接口、广告SDK的接口。我建议不要一看到接口就去做漏洞测试先按域名做一个分组把业务主域名和第三方域名分开。第三方域名的接口基本都是外部服务商的多半是通用接口价值不高。3.3 路由枚举与测试面打点接口收集完成之后接着做路由枚举。操作上分三步。第一步配置目标小程序。工具读取已经解包并完成解密的代码目录自动识别app.json和各分包配置文件。这里工具会做一个很关键的动作解析分包配置。分包的页面路径不能简单拼到主包后面因为分包加载是独立的路由归属也不同处理不好后面测试时就会漏掉整个模块。第二步生成路由树。工具把解析出的路由数据整理成树状结构并标注每个路由所属的分包、tabBar状态、页面文件路径。这一步做完你会看到整个小程序的功能模块结构测试覆盖面一目了然。第三步关联接口打点。工具根据页面文件里的代码引用把路由和接口关联起来。比如某个订单列表页引用了三个订单相关接口工具就把它们挂到这个路由下面。这样测试的时候按路由走链路能看到每个页面背后实际调了哪些接口越权测试的逻辑就清晰了。路由枚举的输出我会同时导出JSON和可视化表格。JSON留给工具链后续自动化流程用表格留着写报告时贴。表格大概长这样字段比Burp的Sitemap视图更适合直接做测试用例规划路由路径所属分包跳转方式页面文件关联接口数测试优先级pages/order/list主包switchTaborder_list.js3高packageA/pages/pay/confirm分包AnavigateTopay_confirm.js2高pages/user/coupon主包navigateTouser_coupon.js1中路由枚举的时候还要注意一个特殊情况部分小程序有条件编译的写法比如根据用户类型跳不同页面、根据环境切换路由前缀。这些动态跳转在静态分析里不容易全覆盖需要靠后续Frida阶段的运行时Hook来补。3.4 Frida动态注入实战示例前面拿到的静态成果到这一步终于派上大用场。先确认设备连接状态。手机USB连上PC跑一下设备列表命令确认Frida Server在线工作。然后启动注入指定目标App的进程名或包名。需要注意小程序运行在宿主App里注入对象不是小程序本身而是那个宿主App进程。基础注入成功后先做一次探针Hook。比如打印目标JS引擎的加载状态或者Hook一个已知会在启动时调用的系统方法验证注入链路是通的。这一步不能省很多环境问题会在早期暴露。接下来是WebView调试辅助脚本。小程序本身有调试通道Frida抓到了WebView的调试端口之后就能进JS控制台。这块做出来对测试来说是个关键节点你可以直接在控制台里运行JavaScript计算加密函数、打印变量值、直接调用内部方法。然后再上一个场景Hook一个业务接口。比如一个小程序的请求上有签名参数sign静态分析只能看到算法大概在哪个文件里但具体参数在处理时是什么状态静态看不出来。这时用Frida去抓脚本里定位到加密方法打印进入方法的参数值和返回结果。我直接给一个可参考的Hook脚本骨架# 伪代码动态查找目标方法并Hook import frida, sys target_app com.example.demoapp js_code Java.perform(function () { var targetClass com.example.request.SignHelper; var hookClass Java.use(targetClass); hookClass.generateSign.implementation function (params) { console.log([] generateSign called); console.log([] params:, params); var result this.generateSign(params); console.log([] sign result:, result); return result; }; }); def on_message(message, data): print(message) device frida.get_usb_device() pid device.spawn([target_app]) session device.attach(pid) script session.create_script(js_code) script.on(message, on_message) script.load() device.resume(pid)注意几个实操细节。第一目标类名和方法名要从静态分析结果里去对工具在接口收集阶段会把代码里可疑的加密函数类名都筛出来这时直接用就行。第二找不到类或者找不到方法名的时候用遍历的方式去找不依靠完整类路径也能命中Java.perform(function () { Java.enumerateLoadedClasses({ onMatch: function (className) { if (className.indexOf(sign) ! -1 || className.indexOf(crypto) ! -1) { console.log(className); } }, onComplete: function () {} }); });第三Hook时注意当前线程有时候默认的调用线程不对函数不触发需要切换线程Hook或者加上轮询补一下。第四SSL Pinning绕过。这类问题比较频繁部分小程序或内嵌WebView在证书校验上加了指纹绑定导致Burp无法解密HTTPS流量。绕过脚本的原理是Hook掉进行证书校验的底层API让校验结果始终返回成功。大多数情况下这样就能让Burp顺利抓包。这类脚本网上有很多开源参考改一改就能用核心就几行代码但从原理上搞懂它为什么生效远比自己复制粘贴一遍更值钱。4. 常见问题与排坑实录整个流程跑下来遇到问题的概率其实非常高。这节把我实际踩过的坑和同行反馈过的高频问题整理一下。4.1 高频问题排查表问题现象常见原因排查思路与解决办法拿不到小程序包部分平台把小程序包做二次加密或者小程序分包未触发下载先确认小程序完整加载过触发所有分包再去缓存目录找对应文件解密后代码乱码解密算法版本不匹配确认小程序的编译版本部分新版小程序改了包格式需要升级解密模块的策略JS代码重度混淆变量名全是乱码小程序做了混淆加固静态分析降级用主攻Frida动态Hook从运行时反推逻辑接口清单里无效地址太多静态规则把第三方SDK、统计代码都算进来了域名分组先过滤第三方域名主业务域名留全路由枚举不全分包页面缺失app.json解析不完整分包信息没读到检查分包解压是否完整必要时手动指定分包目录Frida注入失败报Error 1004Frida Server版本和PC端frida库不匹配强制统一两端版本重装Frida Server后重启注入Hook脚本里方法调用没输出目标方法名对不上或目标函数不在当前加载的类里先在类枚举里找到真实className再在Hook前打印实例确认Burp抓不到小程序的部分HTTPS请求WebView内页面证书校验和原生层不同把Burp证书装到系统证书目录或用Frida绕过SSL Pinning进程spawn后小程序启动黑屏注入时机太早小程序引擎还没初始化完毕换attach模式等App起来之后再注入或延迟加载Hook脚本4.2 我踩过比较深的几个坑第一个坑是版本对齐。Frida的版本问题看起来是小事但一旦出问题会让人怀疑人生。我遇到过一次PC端Frida是16.x手机端Server还是15.x结果所有脚本注入全部失败报错信息却千奇百怪一会报端口问题一会报协议不支持。排查了快两个小时才发现是版本错位。现在我的习惯是每次跑测试前先确认两端版本号一致用一段两分钟的脚本快速验证注入链路通不通再往下推进。第二个坑是拿包时机。小程序的包不是一打开就全量拉到本地的很多分包是按需加载的。你不触发对应页面包就不在缓存里。以前我犯过拿完主包就开始分析的错误结果路由枚举的时候少了整个分包测试面将近缺了一半。现在统一做法是先把小程序所有可见入口都点一遍确保所有分包加载过再去取包文件。第三个坑是盲目信动态结果。动态Hook看起来是权威的但有时会因为Hook时机、线程、方法重载选择不正确拿到不完整甚至错误的参数。我给的铁律是动态结果和静态代码交叉验证两边对得上才敢信。第四个坑是忽略第三方SDK的干扰。有些小程序接入了一堆第三方统计、推送、客服SDK它们会在代码里塞进去大量接口和逻辑。如果分析时不去区分主业务和第三方SDK接口清单会膨胀得非常厉害而且里面可能还夹杂着第三方SDK的加密逻辑白花一堆时间。现在我的做法是拿到代码后第一件事先做域名分组把第三方域名的接口全部隔离出去只保留主业务域名和相关CDN域名。5. 一些心得与工具链的后续扩展整个工具链跑完一遍之后我的体会是小程序挖洞的效率和成品率其实完全取决于信息收集的完整度和动态分析工具的顺手程度。接口收集、路由枚举、Frida注入三个环节任何一块短板都会拖慢整条链路。后续我准备在现有基础上下功夫的几个方向也简单聊聊。一个是把接口变动监控做成自动化定期对比线上小程序包的接口清单变化新接口一出现就自动标记适合在做可持续监控的场景。另一个是想把Frida脚本的模板库继续扩充比如微信支付类、直播类、电商交易类的常见加密模式做成更成熟的预制Hook减少每次测试重复写脚本的工作量。最后说一句工具再顺手测试思路才是决定产出质量的根本。接口收集得再全不清楚业务边界测试视野还是模糊的路由枚举再完整不懂权限模型依然会漏越权。工具只是把耗时的体力活分担掉真正值钱的判断和推理还是要靠测试员的脑子。希望这篇总结对正在做小程序测试的朋友有点参考价值。
阅读完成 · 觉得有帮助?
咨询建站