资讯动态

Frida动态调试实战:在Android API 29小尺寸模拟器上部署与参数修改

发布时间:2026/10/21 20:54:56 来源:尧图企业网站定制
1. 老规矩先把这个事是什么说清楚如果你是从前面两篇一路跟过来的应该已经知道我在折腾什么了在一台轻量Android虚拟机里装上Frida环境用它来挂钩目标App的内部方法实现运行时的参数监测与修改。这篇是第三篇标题里带了个后缀“small phone android Google Api 29低版本版”说白了就是——在小尺寸安卓模拟器上用Android 10API 29这套低版本镜像来跑整套流程。可能有人要问前两篇不是已经跑通了吗为什么又专门写一篇低版本镜像的因为这里有个很现实的差异——不同API等级的安卓系统对Frida的行为、SELinux策略、内存布局甚至adb的交互方式都不一样。API 29虽然是Android 10但它在模拟器里的表现和现代的高版本API比如33、34有明显区别踩坑点也不同。这篇就是专门针对API 29这个“老系统”场景把操作流程重新捋一遍顺手把“怎么把一个测试APK装进这台小虚拟机”这一步也讲了。这个内容比较适合谁看呢一是想做动态调试但手头只有低性能电脑的人小屏模拟器占用资源少二是需要测试旧版本Android兼容性的开发/测试人员三是对Frida实战有兴趣但不想在真机上折腾的新手。不同基础的读者不用紧张——涉及命令我会逐条解释涉及原理会用大白话讲清楚你只要会敲命令就能照做。废话少说直接进入正题。2. 为什么是“小虚拟机 API 29”这套组合2.1 低版本镜像的真正价值在哪里很多人一上来就用最新版Android镜像觉得“越新越强”。但做动态调试和参数修改这件事情况恰恰相反。新版Android系统的SELinux策略越来越严Frida的server端经常要处理权限拒绝问题而API 29Android 10刚好在“功能完整”和“策略宽松”之间取得了一个比较舒服的平衡点。从实操角度讲API 29有几个我实测下来非常满意的特点对Frida的进程注入容忍度高不会频繁触发因SELinux上下文异常导致的注入失败。系统服务比如ActivityManager、PackageManager的接口结构比高版本简单hook的时候写类名和方法签名不容易找错。模拟器镜像体积小冷启动速度快不占内存一台8G内存的老笔记本也能流畅跑。所以如果你是做App兼容性测试的或者是学逆向分析想练手一台API 29的小虚拟机完全够用甚至比高版本更好练——干扰少你能把注意力全放在Frida本身。2.2 small phone 这种小尺寸模拟器对调试的隐形帮助这里说的“小尺寸”指的是屏幕分辨率小、比例接近早期安卓手机形态的那种模拟器配置。常见的有QVGA240x320、WQVGA240x400这类看起来很“复古”但它对调试工作有个意外的好处渲染压力小系统动画少整个系统运行更“安静”减少偶然卡顿带来的超时误判。小分辨率下Frida注入后的UI刷新负载很低你改完参数看效果时反馈非常快。对小尺寸界面适配逻辑的测试更方便很多App在小屏上的布局和点击区域跟大屏完全不同这种缺陷在常规模拟器上根本暴露不出来。我在实际调试中就发现某目标App的大屏版本一切正常但切到小分辨率后一个使用新窗口尺寸API的地方直接拉了跨。这种“偶发性”问题用Frida去hook相关方法一看就能定位。2.3 镜像版本与Frida版本的匹配问题这一点必须单独拎出来讲因为90%的“怎么我连不上server”问题都出在这。Frida分为主机端你电脑上跑的frida、frida-tools和客户端虚拟机里跑的frida-server这两个的版本必须严格一致至少大版本要一样。比如你主机端装的是15.2.2那虚拟机里跑的frida-server也必须是15.2.2。版本不一致的时候主机端连接时会报类似于“unable to communicate with frida-server”的错误排查半天你可能根本想不到是这个原因。在API 29这个场景下Frida版本还有一个选择维度——老版本Frida对老系统的兼容性反而更好。如果你在API 29镜像里用的Frida版本过新比如17.x偶尔会遇到在新内核上没问题的脚本在Android 10上反而执行异常。我的建议是别追新选一个稳定的老版本比如15.x系列对这个场景来说非常稳当。3. 环境清单与前期准备3.1 你需要准备的工具如果你已经看过我前两篇环境应该是齐的。没看过的也没关系这里重新给一份清单照着准备即可项目说明主机操作系统Windows / Linux / macOS 均可本文以Windows为例Android模拟器任选一款支持创建AVDAndroid Virtual Device的模拟器API 29镜像ADB工具官方调试桥用来连接模拟器和管理安装包Frida工具箱主机端frida frida-toolsfrida-server对应版本的Android服务器端程序目标APK你自己开发的测试App或用于合法调试的应用这里额外强调一句搞Frida调试的目标App请务必使用自己开发的、或你有明确授权的程序。用它来做灰度测试、内部回归、行为验证都是常规操作但别拿去做任何违反协议的事情。技术是工具用在哪完全取决于你。3.2 创建API 29的“小屏”模拟器第一步是在模拟器管理器中创建一个新虚拟机。关键是系统镜像选Android 10.0API 29设备定义选一个小尺寸型号。以我自己的习惯为例设备定义我一般选一个自带的小尺寸方案没有现成的就自定义屏幕尺寸3.7英寸左右分辨率480x800属于比较常见的小尺寸组合内存2GB就够虚拟机分配再高反而浪费宿主机资源存储4GB左右足够装几个测试APK启动之后先别急着装东西打开模拟器的系统设置确认Android版本显示的是10。同时在设置里找到“开发者选项”把USB调试打开。这一步如果错过后面adb会找不到设备。3.3 一个小坑API 29镜像的Root权限机制API 29的Google Play版镜像是没有root的但如果你创建的是不带Play Store的普通AOSP镜像比如叫“Google APIs”或“AOSP”的标签默认就是带root权限的——也就是你可以在adb shell里直接执行su切换root。这一点对Frida-server特别关键因为Frida-server在Android上经常需要root权限来完成进程注入。真机上你还要折腾magisk模拟器里直接自带root省了不知道多少事。实操验证方法很简单启动模拟器后打开命令行窗口执行adb shell su如果你看到shell提示符从$变成了#就说明root权限可用环境没问题。4. 部署frida-server到虚拟机里的完整操作4.1 下载正确的frida-server包这一步很多人会下错。先在主机端确认Frida版本frida --version假设输出是15.2.2那么你需要下载的frida-server版本也必须是15.2.2。另外还要确认的是模拟器的CPU架构。大多数在PC上跑的Android模拟器用的都是x86_64架构模拟器对宿主机CPU做二进制转译。少数基于ARM的模拟器例外但那种性能通常较差不推荐。查看模拟器架构的方式adb shell getprop ro.product.cpu.abi输出如果是x86_64那你的frida-server就下载frida-server-15.2.2-android-x86_64.xz这个文件。注意如果你有多个模拟器同时在跑adb命令记得指定设备序号。否则它会报“more than one device”的错误。用adb devices先看一下设备列表然后每个命令都带上-s 设备序号参数。下载完的.xz文件需要先解压。Windows下可以用7-ZipLinux下直接xz -d即可。解压后你会得到一个没有扩展名的可执行文件这就是frida-server本体。4.2 推送和权限设置接着我们把frida-server推送到虚拟机的/data/local/tmp目录。这个目录是Android系统里允许普通/root用户执行可执行文件的临时目录非常适合放调试工具。adb push frida-server-15.2.2-android-x86_64 /data/local/tmp/frida-server推送之后先给它加执行权限否则运行会报“Permission denied”adb shell chmod 755 /data/local/tmp/frida-server这里有一个很多入门者会踩的坑有的教程会让你把权限设成777也就是所有用户都可执行。这确实能用但没必要。755就够了而且更安全——毕竟这是一个root级别的调试工具权限给得越大被误操作的风险越大。4.3 启动frida-server的两种方式及差异启动frida-server有两种方式root方式和普通方式。root方式adb shell su /data/local/tmp/frida-server 普通方式frida-server在部分系统上可以不root运行但功能受限adb shell /data/local/tmp/frida-server 在API 29上我建议用root方式。因为虽然普通方式也能跑起来但后续做跨进程hook或者是hook系统服务时普通权限根本不够用。比如你要hook某个系统广播的发送过程非root状态下连目标进程都附不上。启动之后正常情况下你会看到类似下面这样的输出或者至少不会报错。frida-server是以后台模式一直驻留的没有任何输出往往就是好事。4.4 端口转发这个关键动作frida-server启动后它会在虚拟机的27042端口监听但你的主机不能直接访问虚拟机的这个端口必须通过adb做端口转发adb forward tcp:27042 tcp:27042这条命令的意思是把主机上的27042端口桥接到虚拟机里的27042端口。这样主机端的frida工具连localhost:27042就能连上虚拟机里的frida-server了。很多人第一次跑都会漏掉这一步然后看到这样的报错Failed to connect to remote frida-server: connection refused别慌先检查这一步做了没有。5. 把安卓实例程序安装进虚拟机5.1 安装APK的常规方式这一步反而是最简单的。把你要调试的APK放到一个容易记住的路径下比如C盘的test目录然后执行adb install C:\test\demo.apk等它输出Success就说明装好了。安装完成后你可以在模拟器桌面或应用列表里看到这个App的图标。如果你是在写自动化脚本可能需要静默安装那就在install命令后面加-r -g参数adb install -r -g C:\test\demo.apk-r表示允许覆盖安装-g表示在安装后直接授予所有运行时权限。这两个参数在反复调试的场景下特别有用否则每次重装后你都要手动点权限弹窗烦都烦死。5.2 确认包名和启动Activity安装完以后要做的第一件事是不要急着点开App而是确认它的包名和启动Activity。这两个信息在Frida操作里至关重要——当你指定要attach到哪个进程时你得知道它的包名。查看已安装应用的包名adb shell pm list packages | grep 关键词执行后你会看到类似package:com.demo.test的输出中间那个com.demo.test就是完整包名。如果是你自己开发的App启动Activity通常能在项目的配置清单里直接看到。如果你是从外部拿到的APK可以用第三方工具来查比如某些APK分析工具也可以用aaptaapt dump badging demo.apk | grep launchable-activity拿到包名和活动名后就可以手动启动App了adb shell am start -n com.demo.test/.MainActivity这样启动的好处是你能确保App处于最干净的前台状态方便后续用Frida去attach。5.3 在API 29上安装APK时可能遇到的兼容性提示API 29系统在安装APK时会对目标SDK版本做校验。如果你的测试APK是给Android 13以上编译的它可能会在模拟器上提示“此应用专为最新版Android设计”但这只是一个警告并不会阻止安装。真正会让安装失败的原因通常是APK的架构不匹配。比如模拟器是x86_64你的APK里只有arm64的so库那么安装会成功但运行时会直接崩溃。遇到这种情况要么换一个包含x86_64库的构建版本要么在模拟器设置里开启“arm兼容模式”——但后者的性能损耗很大不推荐用作Frida调试。6. Frida连接验证与参数监测实操6.1 先确认Frida能连上虚拟机部署完成、App也装好了现在开始验证整条链路是否通畅。首先执行frida-ps -U-U的意思是连接USB设备模拟器也走这个通道。如果你能看到一长串进程列表说明主机端Frida已经成功连接上了虚拟机里的frida-server。这一步通了后面就是纯脚本工作。如果frida-ps -U卡住不动或者报错优先查四个地方frida-server有没有在虚拟机里跑ps -ef | grep frida端口转发做没做adb forward --list主机端frida版本和server版本一致不一致模拟器本身是否处于正常启动状态这四步检查完了95%的问题都能解决。6.2 用命令行附加进程先看能不能监控活动验证通过后直接附加到目标App进程frida-trace -U -i java.lang.String.* com.demo.test这个命令的意思是跟踪目标App进程中所有java.lang.String类的方法调用。虽然它可能刷屏但它的价值在于验证“Frida到底能不能看到这个进程里发生的Java方法调用”。如果屏幕上一行行弹出方法调用记录说明Frida的注入已经完全成功了。这时候你就能做真正的“监测”了——观察它在运行时到底调用了什么方法、传了什么参数、返回了什么结果。这种信息比静态看代码要直观得多尤其是当你面对混淆过的代码时运行时行为是最诚实的语言。6.3 用JS脚本实现参数修改的演示监测只是第一步修改参数才是标题里的核心诉求。下面分享一个我常用的基础脚本模板可以直接保存成hook.jsJava.perform(function () { var targetClass Java.use(com.demo.test.MainActivity); targetClass.calculateInfo.implementation function (original) { console.log([*] calculateInfo called); console.log([*] original param: original); var newParam changed-value; console.log([*] new param: newParam); var result this.calculateInfo(newParam); console.log([*] original result: result); return hooked-result; }; });这个脚本先把目标App里MainActivity类的calculateInfo方法替换成自定义实现打印原始参数、仿造新参数调用原方法、打印原始返回值、最后返回一个自定义结果。执行方式frida -U -f com.demo.test -l hook.js-f表示启动时自动注入-l表示加载脚本。启动后你会看到App被拉起然后控制台输出你打印的那些调试信息。这里要提醒一点修改参数时返回值类型一定要和原方法定义匹配。如果原方法返回的是int你返回一个字符串运行时会直接抛类型转换异常。我自己就吃过这个亏在某个方法里返回了错误类型导致App当场闪退排查了快半小时。6.4 把脚本升级成“可复用工具”的思路上面那个脚本只是演示功能实际使用的时候你可能需要更灵活的方案。比如说你想改的方法不在MainActivity里而在一个深层工具类里或者你需要在App运行过程中动态切换参数值。我自己的做法是把参数改成一个全局JavaScript变量再配合Frida的rpc.exports这样主机端的Python脚本就能动态调用JS里的修改函数随时换参数而不需要重新注入var mode default; Java.perform(function () { var targetClass Java.use(com.demo.test.ConfigManager); targetClass.getConfig.implementation function () { if (mode hooked) { return test-config; } return this.getConfig(); }; }); rpc.exports { setMode: function (newMode) { mode newMode; console.log([*] mode switched to: newMode); } };配合的主机端Python脚本import frida device frida.get_usb_device() session device.attach(com.demo.test) script session.create_script(open(hook.js, encodingutf-8).read()) script.load() script.exports.set_mode(hooked)这样一来你就不用每次改参数都重新注入一次脚本直接在主机端调函数就行。这个思路在批量测试多个参数组合时非常高效能省下大量时间。7. 常见问题与排查技巧实录7.1 frida-server启动后立刻退出这个问题在API 29镜像上偶尔会出现。原因是frida-server的二进制文件在运行时需要特定的系统库但一些精简过的模拟器镜像并没有把它包含进去。排查方式adb shell cd /data/local/tmp ./frida-server如果它报了CANNOT LINK EXECUTABLE之类的错误那就说明缺库。解决方式是换一个低版本的frida-server再试通常可以绕过这个库依赖问题。我实测过某些高版本frida-server对API 29的库要求反而更苛刻退回到15.1.x就可以跑了。7.2 明明连上了但很多进程attach不上这种情况在API 29上也有特别是当你用普通权限跑frida-server的时候。症状是frida-ps -U能看到进程列表但执行frida -U -f 包名之后卡在“Waiting for application to start”毫无响应。解决方案就是重启frida-server并切换为root方式adb shell su killall frida-server /data/local/tmp/frida-server 再重新做一次端口转发执行附加命令即可。如果还是不行就看模拟器的“开发者选项”里是否打开了root权限的开关有的模拟器需要你在系统设置里先开root。7.3 安装在API 29模拟器上的App无法启动如果App安装成功但点击后立刻闪退最常见的原因我刚才提过——APK架构不匹配模拟器架构。你可以在App启动瞬间抓一下logcatadb logcat -c adb shell monkey -p com.demo.test 1 adb logcat -d | grep -i fatal\|crash如果看到和dlopen failed相关的错误十有八九就是so库架构问题。这个时候只能换一个包含x86_64 so库的构建版本没有别的捷径。7.4 端口转发做了一次但过一会儿就不通了这个问题挺阴间的因为不是每次都出现。原因通常是模拟器在网络切换比如Wi-Fi断开重连或休眠唤醒后重置了内部端口状态。你只需要重新执行一次端口转发命令即可adb forward tcp:27042 tcp:27042如果你在写自动化脚本我建议把端口转发逻辑做进启动流程里每次跑任务前强制执行一次不要只在开始时做省得脚本跑到一半莫名断连。7.5 JavaScript脚本执行后没有任何日志输出排除掉注入失败的可能性后最可能的原因是脚本里的类名或方法名写错了。Frida在无法找到目标类时不会报错它会保持静默——这算是一个设计上的“坑”。我的排查方法是先用反射遍历一下目标进程里到底有哪些可用类确认类名正确后再写hook脚本Java.perform(function () { Java.enumerateLoadedClasses({ onMatch: function (className) { if (className.indexOf(demo) ! -1) { console.log(className); } }, onComplete: function () {} }); });打印出来的类名和你脚本里写的类名要一字不差。特别是内部类的写法经常变化比如MainActivity$InnerClass这种少一个$符号整个脚本就失效了。8. 这轮实操给到的几个经验输出写到这里这套“虚拟机里跑frida-serversmall phone Android API 29 安装目标实例程序”的全流程基本就闭环了。回头梳理一下有几个体会想强烈表达一下第一API 29这个低版本镜像反而是练手利器。它没有高版本那么多安全限制root权限来得直接对Frida生态的兼容性也足够好。如果你还在为高版本系统上装Frida各种卡权限烦恼完全可以先退回到API 29把整个流程跑通再去挑战高版本镜像。这种由易到难的学习路径就是我为新手推荐的路线。第二小尺寸模拟器的价值被严重低估。在低配电脑上它跑得顺滑你能把全部注意力放在Frida钩子的逻辑上而不是等模拟器加载等半天。而且小屏幕对界面适配类Bug的暴露效果非常显著比默认大屏更容易发现实际问题。第三最常见的问题永远不是技术深度问题而是版本匹配和权限。前后折腾的所有案例中八成问题的根源不是某个高级API不会写而是frida-server版本和主机端不匹配、或者忘了用root启动、或者端口转发没做。把这些基建环节固化成习惯后面的调试效率能提升一个量级。第四关于后续扩展我这套环境还可以做不少事情比如用Python写一个批量参数扫描器自动化跑接口兼容测试或者把Frida脚本里的日志输出接到本地文件用来做长时间运行时的数据采集。这套低版本虚拟机方案虽然在配置上不起眼但它是一个极其稳定的试验台无论是调UI还是调逻辑都能给你最干净的反馈。最后再分享一个我自己的小习惯每次启动新项目调试时先行把包名、主要类名和方法名记录下来写进一张对照表然后再开始写脚本。这种记笔记的做法在项目初期看起来有点“笨”但当你的hook方法数量超过十个以后这张表就是你的救命稻草没有它你迟早会在修改参数的时候记错名字然后花几个小时去排查一个根本不存在的问题。好了这篇就先到这。有问题的话评论区或者私信聊都行我看到会回。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑