
1. 项目概述与核心价值搞安卓逆向或者安全测试的朋友对Frida这个“瑞士军刀”肯定不陌生。它能让我们在运行时动态地注入JavaScript代码去Hook目标应用的方法、修改内存数据功能强大到没边。但很多时候我们手头并没有那么多实体测试机或者测试环境需要高度隔离和可重复。这时候虚拟机就成了我们的最佳拍档。今天要聊的这个场景就是在一个配置为“Small Phone”、搭载Google APIs 29也就是Android 10的安卓虚拟机里部署并运行frida-server最终实现对运行在该虚拟机内的目标应用进行监测和参数修改。听起来像是“套娃”——在虚拟机里监控虚拟机里的应用但这恰恰是移动安全研究、应用行为分析乃至自动化测试中一个非常经典且实用的沙箱环境搭建方案。它能让你在一个完全可控、可快照、可重置的环境里安全地进行各种“高危”操作而不用担心搞崩你的主力机或实体设备。这个方案特别适合哪些人呢如果你是移动应用安全研究员、安卓逆向工程师、或者是对应用底层行为感兴趣的开发者这个环境能为你提供一个纯净的、可任意折腾的实验室。即使你是个新手想学习Frida的基本用法和Hook技巧在虚拟机里操作也是最安全、成本最低的入门方式。我们这次选择的Android 10API 29算是一个承上启下的版本既不像老版本那样某些新特性不支持又不像最新版本那样可能遇到一些激进的沙箱限制对于大多数学习和研究场景来说兼容性和可操作性都刚刚好。2. 环境准备与虚拟机配置详解工欲善其事必先利其器。整个流程的基石就是一个配置得当的安卓虚拟机。这里我们选择Android Studio自带的AVDAndroid Virtual Device管理器因为它与开发环境集成度最高对Google APIs的支持也最原生。2.1 创建适配的安卓虚拟机镜像首先确保你已经安装了Android Studio。打开后进入“Tools” - “Device Manager”。点击“Create device”开始创建。选择硬件配置文件在硬件选择界面我们需要找的是“Small Phone”这个预设。它通常指的是屏幕尺寸较小、分辨率较低如4.0英寸 480x800的机型模拟。选择它的好处是虚拟机占用的系统资源尤其是GPU和内存相对较少运行起来更流畅。对于Frida这类更依赖CPU和系统交互的工具屏幕渲染压力小反而是优势。选择系统镜像这是最关键的一步。在“System Image”选项卡中我们需要筛选出“API 29”的版本。注意要选择带有“Google APIs”字样的镜像而不是普通的“Android API 29”或“Google Play”版本。为什么必须是Google APIs普通的Android系统镜像只包含AOSP安卓开源项目的基础功能。而“Google APIs”镜像在AOSP基础上还包含了Google移动服务GMS的API接口框架。许多应用特别是依赖Google地图、登录、推送等服务的应用在纯AOSP镜像上可能无法正常运行或功能不全。我们的目标是要模拟一个接近真实手机的、功能完备的环境因此“Google APIs”是必须的。ABI选择通常选择x86_64。因为我们的宿主机你的电脑大概率是Intel或AMD的64位处理器运行x86_64架构的虚拟机可以通过Intel HAXM或AMD Hyper-V进行硬件加速性能远超ARM架构的模拟。除非你有特殊需求比如测试纯ARM指令集的应用否则无脑选x86_64。验证与下载选中“Google APIs API level 29 x86_64”这样的镜像后点击“Download”进行下载。这个过程可能会比较耗时取决于你的网速。完成配置下载完成后为虚拟机命名例如SmallPhone_API29可以适当调整内存和存储空间。对于Frida调试建议内存RAM至少设置为2GB2048MB存储Internal Storage不少于4GB。然后点击“Finish”完成创建。注意有些教程会推荐使用Genymotion等第三方模拟器。它们性能可能更好但有时在Google服务兼容性、网络桥接或与Frida-server的适配性上会遇到一些玄学问题。对于追求稳定和原生兼容性的学习研究场景Android Studio AVD是更稳妥的选择。2.2 虚拟机的关键启动参数与网络设置虚拟机创建好后先别急着启动。为了后续Frida能够顺利连接我们需要关注两个关键点网络和可调试性。启动选项在Device Manager中找到你刚创建的虚拟机点击右侧的“Edit”按钮小铅笔图标。在弹出窗口的“Advanced Settings”中确保“Boot option”是“Cold boot”冷启动而不是“Quick boot”。虽然Quick boot启动更快但有时会导致系统状态不“干净”可能影响Frida-server的注入或网络连接在调试阶段建议使用Cold boot。网络模式这是Frida连接的生命线。确保虚拟机的网络模式是“NAT”或“Bridged”。默认的NAT模式通常就能满足需求它会让虚拟机共享宿主机的IP地址并处于同一个虚拟子网内宿主机可以访问虚拟机。绝对不要选择“Host-only”这个模式会隔离虚拟机与外部的网络导致宿主机无法通过TCP/IP连接到虚拟机内的frida-server。启动虚拟机等待它完全进入系统桌面。你可以先在里面随便点点确保系统运行正常。3. 获取并推送frida-server到虚拟机Frida分为两部分在目标机器这里就是我们的安卓虚拟机上运行的服务端程序frida-server以及在宿主机你的电脑上运行的控制端工具frida-tools。我们的第一步是把服务端弄进虚拟机。3.1 下载匹配架构与版本的frida-server确定架构我们的虚拟机是x86_64的所以必须下载对应架构的frida-server。去Frida的官方GitHub Releases页面https://github.com/frida/frida/releases 找到最新的稳定版通常版本号最高。选择文件在发布页的Assets列表里寻找名字格式为frida-server-xx.x.x-android-x86_64.xz的文件。其中xx.x.x是版本号android-x86_64指明了它是用于安卓x86_64平台的。下载与解压下载这个.xz压缩文件。你需要使用解压工具如7-Zip、Bandizip将其解压得到一个名为frida-server-xx.x.x-android-x86_64的二进制文件没有扩展名。为了方便我们可以将其重命名为简单的frida-server。3.2 使用ADB将文件推送至虚拟机ADBAndroid Debug Bridge是连接宿主机和安卓设备包括虚拟机的桥梁。Android Studio已经自带了ADB你可以在终端命令行中直接使用。检查连接首先确保你的虚拟机正在运行。打开终端Windows的CMD/PowerShell Mac/Linux的Terminal输入命令adb devices你应该能看到一个设备列表其中包含你的虚拟机状态为device。这证明ADB连接成功。如果显示unauthorized你可能需要在虚拟机屏幕上弹出的“允许USB调试”提示中点击确认。获取root权限至关重要frida-server需要以root权限运行才能注入到其他进程。安卓虚拟机默认的userdebug构建版本已经开启了root但我们仍需通过ADB主动切换到root shell。执行adb root这个命令会重启ADB守护进程并以root权限连接。再次执行adb devices你可能会看到设备状态变为offline然后恢复为device这是正常的。推送二进制文件将我们刚才下载并解压好的frida-server文件推送到虚拟机的可执行目录例如/data/local/tmp/。这个目录通常对所有用户可读写且路径在系统的PATH环境变量中方便我们直接运行。adb push /你的本地路径/frida-server /data/local/tmp/请将/你的本地路径/替换为你电脑上frida-server文件的实际存放路径。设置执行权限进入虚拟机的shell并给这个文件添加可执行权限。adb shell执行后命令行提示符会变成类似generic_x86_64:/ #表示你已经进入了虚拟机的root shell。cd /data/local/tmp chmod 755 frida-serverchmod 755命令赋予了文件所有者读、写、执行权限同组用户和其他用户读和执行权限。4. 在虚拟机中启动并验证frida-server文件准备就绪现在要让它在虚拟机后台跑起来。后台启动frida-server在刚才的ADB shell中执行./frida-server 末尾的符号表示让这个进程在后台运行。命令执行后shell会返回一个进程IDPID并且命令行提示符会立刻返回不会阻塞。验证服务是否运行方法一检查进程。在ADB shell中执行ps -A | grep frida你应该能看到一行包含frida-server的进程信息。方法二使用frida-ps。这是更推荐的验证方式因为它同时测试了服务运行和网络连通性。新开一个宿主机的终端窗口不要关闭之前那个运行着frida-server的shell在宿主机上安装frida-tools如果还没安装的话pip install frida-tools然后使用以下命令列出虚拟机中正在运行的进程frida-ps -U这里的-U参数代表通过USB连接设备ADB连接的设备也被Frida视为USB设备。如果一切正常这个命令会成功执行并列出虚拟机中所有的进程列表。如果出现Failed to enumerate processes: unable to connect to remote frida-server之类的错误请回到上一步检查frida-server是否真的在运行以及ADB连接是否稳定。实操心得有时候在后台启动frida-server后关闭ADB shell窗口会导致这个进程也被终止。一个更稳健的方法是使用nohup命令nohup ./frida-server /dev/null 21 这样即使关闭shell服务也会持续运行。要关闭它需要手动kill掉对应的进程ID。5. 安装目标实例程序并配置测试环境服务端跑起来了我们还需要一个“靶子”应用来练习我们的Hook技巧。5.1 向虚拟机安装APK假设我们有一个名为demoapp.apk的测试应用。安装过程非常简单# 在宿主机终端中退出adb shell如果还在的话执行 adb install /路径/to/你的/demoapp.apk安装成功后你可以在虚拟机屏幕上看到这个应用的图标。5.2 编写一个简单的Frida Hook脚本现在我们在宿主机上创建一个JavaScript文件用来编写我们的Hook逻辑。比如我们想Hook这个demo应用中的一个计算金额的方法calculateTotal并修改其返回值。创建一个名为hook_demo.js的文件内容如下Java.perform(function () { // 替换成你目标应用的实际包名和类名 var TargetClass Java.use(com.example.demoapp.MainActivity); // Hook calculateTotal方法 TargetClass.calculateTotal.implementation function (price, quantity) { console.log([] calculateTotal被调用); console.log([-] 原始参数 - price: price , quantity: quantity); // 调用原方法获取原始结果 var originalResult this.calculateTotal(price, quantity); console.log([-] 原始结果: originalResult); // 在这里进行我们的修改例如总是返回100 var modifiedResult 100; console.log([] 修改后结果: modifiedResult); // 返回修改后的值 return modifiedResult; }; });这个脚本的作用是当calculateTotal方法被调用时先打印日志和原始参数然后执行原方法得到原始结果接着我们强行将结果改为100最后返回这个修改后的值。5.3 运行Hook脚本在宿主机上使用frida命令注入我们的脚本到目标进程frida -U -f com.example.demoapp -l hook_demo.js --no-pause-U: 使用USB设备我们的虚拟机。-f com.example.demoapp: 以spawn方式启动应用包名为com.example.demoapp的应用。--no-pause表示启动后立即执行脚本不暂停。-l hook_demo.js: 加载我们编写的JavaScript脚本。命令执行后虚拟机会自动启动目标应用并且你的宿主机终端会进入Frida的交互模式。当你在虚拟机中操作应用触发calculateTotal方法时就能在终端看到我们脚本中打印的日志并且方法的行为已经被我们修改。6. 常见问题、排查技巧与深度优化实录搭建和运行过程中你几乎一定会遇到一些问题。下面是我踩过坑后总结的一些常见情况和解决办法。6.1 连接与权限问题排查表问题现象可能原因排查步骤与解决方案adb devices列表为空1. 虚拟机未运行。2. ADB服务未启动或异常。3. 虚拟机网络模式为Host-only。1. 确认虚拟机已开机并进入系统。2. 重启ADB服务adb kill-serveradb start-server。3. 检查虚拟机网络设置改为NAT或Bridged。adb devices显示unauthorized虚拟机未授权宿主机进行USB调试。查看虚拟机屏幕是否有“允许USB调试”的弹窗点击“始终允许”。adb root失败提示adbd cannot run as root in production builds你使用的系统镜像是“Google Play”版本而非“Google APIs”版本。Play版本是生产构建通常禁用了root。唯一解决方案重新创建一个使用“Google APIs”系统镜像的虚拟机。frida-ps -U报错unable to connect1.frida-server未运行。2.frida-server架构版本不匹配。3. 虚拟机防火墙或SELinux阻止。1. 进入adb shell用ps -A | grep frida检查进程。2. 确认下载的server是android-x86_64版本。3. 在adb shell中临时关闭SELinuxsetenforce 0。注意这只是调试手段重启后失效。Frida脚本注入后无任何日志输出1. 脚本语法错误静默失败。2. 包名/类名/方法名错误未找到目标。3. 方法签名参数类型、返回值类型不匹配。1. 在脚本开头加console.log(“Script loaded!”)验证脚本是否加载。2. 使用frida-trace快速验证类和方法是否存在frida-trace -U -i “*calculate*” com.example.demoapp。3. 使用Java.choose()或Java.enumerateMethods()动态查找确认方法的准确签名。6.2 性能优化与稳定性提升技巧使用高版本Frida始终使用Frida官方发布的最新稳定版。新版本通常修复了旧版的bug并提升了稳定性和性能。在服务端和客户端frida-tools尽量保持版本一致。为虚拟机分配更多资源如果Hook复杂应用或频繁操作时感觉卡顿可以编辑虚拟机配置增加CPU核心数2-4个和内存4GB-8GB。在AVD Manager中点击“Edit”在“Advanced Settings”里可以调整。脚本编写的注意事项避免阻塞主线程在Hook的回调函数implementation中不要执行耗时操作如网络请求、大量文件IO。这会导致应用卡死或无响应。如果需要应该启动新的线程来处理。及时释放资源如果你在脚本中创建了Java对象或分配了内存要注意在适当的时候如脚本卸载时进行清理避免内存泄漏。虽然Frida的垃圾回收会处理一部分但良好的习惯很重要。使用setImmediate对于不需要立即执行的初始化代码可以包裹在setImmediate函数中让Frida先完成注入流程避免超时。应对应用反调试一些安全意识较强的应用会检测Frida的存在。常见的检测手段包括检查特定端口默认27042、检查进程名、检查加载的库文件等。对策包括修改frida-server名称和端口在启动frida-server时可以指定监听端口./frida-server -l 0.0.0.0:8080并在frida命令中用-H 127.0.0.1:8080连接。也可以将二进制文件改名。使用定制化的Frida社区有一些修改版的Frida移除了某些特征可以绕过简单检测。动态对抗在Frida脚本中可以Hook应用自身的反调试检测函数使其永远返回错误的结果。6.3 进阶端口转发与无线连接默认情况下Frida通过ADB的USB通道通信。但有时你可能希望虚拟机像一台独立设备一样通过WiFi连接。在虚拟机中设置WiFi确保虚拟机网络是NAT或Bridged并连接到宿主机所在的局域网通常桥接模式更容易获得独立IP。获取虚拟机IP在ADB shell中执行ifconfig或ip addr找到wlan0或eth0网卡的IP地址通常是192.168.x.x或10.0.x.x。端口转发frida-server默认监听TCP端口27042。我们需要通过ADB将这个端口转发到宿主机adb forward tcp:27042 tcp:27042使用网络模式连接现在你可以在宿主机上使用虚拟机的IP地址来连接Frida而不再依赖-U参数frida -H 192.168.x.x:27042 -f com.example.demoapp -l hook_demo.js这种方式的好处是即使你拔掉ADB线对于真机只要设备和电脑在同一个网络调试依然可以继续。整个流程从搭建沙箱、部署工具到实际注入修改形成了一条完整的学习和实践路径。在虚拟机的安全沙盒里你可以大胆尝试各种Hook思路不用担心系统崩溃或数据丢失快照功能让你随时可以回到一个干净的状态。这种可反复、可追溯的实验环境对于深入理解安卓应用运行机制和安全技术来说价值远超一台实体测试机。