1. 先弄清楚你学安卓逆向到底为了什么在做任何路线规划之前我建议大家先回答一个问题你想通过安卓逆向解决什么具体问题这不是套话而是决定你后续几个月学习方向的关键分岔路口。我见过不少新手一上来就下载各种脱壳工具、hook框架对着屏幕兴致勃勃折腾一晚上结果第二天就放弃了。原因很简单——没有目标就没有反馈没有反馈就坚持不下去。我自己早期也是这样翻了一堆帖子收藏了一堆资料实际动手时还是不知道从哪下手。1.1 三种典型学习动机从我做安全研究这几年的观察来看想学安卓逆向的人大概分成三类第一类是安全测试从业者。做App渗透测试、漏洞挖掘、合规检测需要理解目标App的逻辑、接口、加密方式才能评估风险。这类人不需要把每个样本都破解得干干净净但必须熟练掌握静态分析、动态调试、协议分析这几板斧。第二类是恶意样本分析人员。拿到一个可疑APK要先弄清楚它是不是病毒、偷了什么数据、怎么通信的。这类人更看重脱壳、行为分析、协议还原能力需要在逆向深度上花更多功夫。第三类是纯粹的技术爱好者。单纯好奇App内部长什么样想看懂代码逻辑或者想研究某个功能是怎么实现的。这类人其实最需要一条循序渐进的路因为好奇心如果没有被阶段性成果喂养很容易被挫败感浇灭。这三种动机对应的学习侧重点完全不同。所以我建议你在开始前先坐下来写三行字我为什么学、我打算学到什么程度、我第一个想搞定的目标样本是什么。有了这三行字下面的路线才真正属于你。1.2 路线图总览五层递进不跳级我把整个学习过程拆成五个阶段每个阶段对应一类核心能力阶段核心能力典型产出预期周期第一阶段语言与Android基础能读懂APK的基本结构2-4周第二阶段静态分析能不看日志还原App核心逻辑3-6周第三阶段动态调试能hook任意方法、看调用栈、改返回值4-8周第四阶段协议分析能还原App的网络请求与加密参数4-6周第五阶段加固与混淆对抗能过基础壳、识别混淆规律长期这个顺序不是随便排的。静态分析是动态调试的地基动态调试又是协议分析的支撑。你如果连smali指令都不认识就用Frida乱hook出了错根本不知道问题出在哪——是把方法名写错了还是类加载时机不对或者是加固壳在作祟。很多人卡在原地不是工具不好用而是前面几层的基础不牢。2. 地基分两层语言基础与Android运行机制安卓逆向的本质是读懂运行在Android设备上的Java/Kotlin代码以及少量Native层代码然后想办法让这些代码按照你的意图暴露信息或被修改。所以语言基础和系统机制是你和代码之间的翻译器。2.1 Java与Kotlin不需要等到“特别熟”再动手很多人一听说要学Java就打退堂鼓觉得语法太多了。我的建议很明确只需要能读懂不需要能写大型工程。你需要重点掌握的Java知识点其实就这些面向对象的基本概念类、对象、继承、接口、多态集合框架List、Map、Set的使用反射机制尤其是Class.forName()、getMethod()、invoke()这些APIFrida hook里大量使用反射概念泛型看懂代码里的 T 声明匿名内部类与LambdaApp代码里lambda遍地都是混淆后又经常看到匿名类的痕迹异常处理try-catch的阅读和理解Kotlin方面因为现在新开发的App基本都是Kotlin写的你还得额外了解空安全语法?.和!!、协程suspend和launch、扩展函数、数据类这些特色语法。零基础的话先看两小时Kotlin基础语法视频再直接拿真实App的jadx反编译结果对照着看很快就能上手。这里有个小诀窍不用从头到尾啃完一本书再动手。我当时是找了个自己常用的工具类App反编译出来硬着头皮一格一格看代码。碰到看不懂的语法就查一下看多了自然就熟了。真实样本比教科书上的Demo有用得多。2.2 从APK到内存搞懂字节码与虚拟机的关系APK文件里的classes.dex存的是Dalvik字节码。Android系统运行App时会把dex中的字节码编译或解释成机器指令具体机制随Android版本变化5.0以前是Dalvik虚拟机以解释执行和JIT编译为主5.0以后换成ART运行时安装时通过AOT编译时提前编译或运行时JIT即时编译把字节码转为机器码。这对逆向意味着什么呢意味着你看到的smali代码和机器执行之间隔着一层编译优化直接读dex比直接从内存dump机器码要容易得多。但反过来如果代码被混淆、加固你只能看到一堆乱糟糟的smali就需要理解ART的类加载机制搞懂Config、ClassLoader这些概念才能脱壳还原。学习的时候我建议大家动手做一个很有意思的小实验用aapt dump badging查看APK的包信息用dexdump输出dex文件结构再用十六进制工具打开dex文件在文件头部找到dex\n035魔数。当你亲眼看到这些字节码文件的结构之后再去看文章里说的“dex解析”、“类加载”这些概念一下就通了。2.3 APK结构细读一个安装包到底装了些什么任何Android逆向学习路线都绕不开APK结构。一个标准的APK内部大致长这样AndroidManifest.xmlApp的“身份证”记录了包名、权限、四大组件、入口Activityclasses.dexJava/Kotlin代码编译后的字节码文件可能有多个resources.arsc资源索引表保存了字符串、布局、颜色等资源信息res/原始资源文件包括布局、图片、XML配置等lib/Native动态库.so文件arm64-v8a、armeabi-v7a等不同CPU架构assets/其他需要打包的资源比如证书文件、首页配置、加密规则等我强烈建议大家自己画一张APK结构图把每个部分的作用和对应的分析工具写上去。比如AndroidManifest.xml用jadx或者AXMLPrinter工具解析classes.dex用jadx或GDA反编译lib/里的so文件有用IDA Pro分析。这种基础看上去不起眼却是后面排查问题的重要思路来源。举个例子你在hook一个方法时发现找不到类如果你理解APK结构就会想到可能是MultiDex分包代码拆到classes2.dex了或者是反射动态加载Dex。不懂结构的话你只会觉得“这个工具是个垃圾”。3. 静态分析先学会“读代码”再谈“跑代码”静态分析是安卓逆向的基本功。它的意思是不运行App直接通过分析文件内容还原代码逻辑和调用关系。这个阶段的目标是——看到一个APK能大致说出它的功能模块、关键接口、加密逻辑。3.1 jadx把字节码还原成可读Javajadx目前是我在Android逆向里用的最多的静态工具没有之一。它可以把classes.dex还原成近乎原始的Java代码还能直接导出成Android Studio工程。它的好处在于反编译准确性高混淆处理后的代码也能看出大致语义自带搜索功能可以搜字符串、方法名、类名能直接查看APK包内的所有文件包括资源、so库、assets支持调试模式可以动态调试smali使用上有些小技巧。比如反编译结果里如果看到一堆.a()、 .b()这样的方法名说明代码被ProGuard或R8混淆了。此时不要直接放弃先用jadx的搜索功能找特定的系统API调用——比如 sendTextMessage、 getDeviceId、 put、 get、 exec这些方法名不会被混淆掉。通过API调用倒推功能比从头到尾逐行读代码效率高很多。 另一个实用操作是“交叉引用追踪”。在jadx里右键某个方法选择“Find Usage”可以快速定位到谁调用了这个方法。我从一个恶意样本里追踪加密函数时就是靠这个功能一层一层往回追最后发现所有加密调用都指向一个入口类而那个类就是核心逻辑所在。 ### 3.2 smali语法不依赖工具的另一种视角 虽然jadx能还原Java代码但smali仍然是绕不开的底层语言。为什么因为混淆器通常会混淆方法名、类名、字段名但不会改变smali层的指令逻辑。当你需要分析一段被特殊混淆处理的代码或者调试Native层时直接看Java代码可能不准甚至缺东西此时smali是无误的最底层视图。 smali语法并不难没有网上传的那么可怕。核心就是记住几种指令模式 - const/4 v0, 0x1把常数存入寄存器 - invoke-virtual、 invoke-static调用方法 - iget、 iput读取/写入实例字段 - sget、 sput读取/写入静态字段 - if-eqz v0, :cond_0条件跳转 - return-void、 return v0返回 学习smali有一个很适合新手的练习方式拿一个没有任何混淆的小App先看jadx的Java代码再对照看smali找对应关系。看个五六个函数你就明白了——smali只是把Java方法体里的表达式拆成了更细的指令序列逻辑没有本质变化。 ### 3.3 清单文件与资源定位判断入口的实用能力 静态分析第一步永远是先看 AndroidManifest.xml。你可以用jadx内置的“Manifest”标签页直接查看也可以命令行的方式解析。 重点看几个字段 - package包名 - manifest节点下的 uses-permission权限声明直接暴露App能力范围 - application节点下的 android:nameApplication入口类 - activity节点中包含 android.intent.action.MAIN的那一项启动入口Activity 这套方法在恶意样本分析中尤其管用。我见过有些样本会在 AndroidManifest.xml中申请几十个权限从权限列表基本能猜出功能意图 RECORD_AUDIO加 CAMERA加 ACCESS_FINE_LOCATION这大概率不是什么正经App。 游戏外挂类的样本通常会在清单文件里注册辅助功能服务 BIND_ACCESSIBILITY_SERVICE恶意Dex加载类则会注册某个特殊的Service。这些东西都是入口顺着入口往深处挖就能还原整体逻辑。 ## 4. 动态调试让App在你手里“透明化” 静态分析做到一定程度就会撞墙。尤其是遇到加密、混淆、反调试的App光靠读代码很难知道运行时的真实行为。这时候动态调试就该上场了——让App跑起来你附在它身上看它的一举一动。 ### 4.1 环境准备模拟器还是真机 Win稳妥的方案是准备一台可以root的Android设备。Pixel系列或者一些国产机都可以关键看能不能解锁bootloader并获取root权限。模拟器是一个更方便的选择比如常见的Android模拟器自带root权限虽然有一些高版本App会在模拟器里检测环境拒绝运行但大部分学习样本都没问题。 我个人的建议是**模拟器和真机各备一台**。模拟器主要用于跑普通样本方便快照、恢复环境真机用于跑那些做了环境检测的样本。真机上建议装Magisk做root管理它会隐藏root痕迹不是说要搞什么对抗而是很多App不管你有没有恶意行为只要检测到root就直接罢工这会给逆向分析造成很大干扰。 ### 4.2 Frida实操从“打印参数”到“修改返回结果” Frida是目前安卓动态分析中使用最广的框架它让你用JavaScript脚本去注入到目标进程里自由hook方法。核心逻辑不复杂你拿到一个类名和方法名在方法执行前后各挂一个回调函数读取参数、捕获返回值甚至直接修改返回值。 我用一个最简单的例子来说明操作流程。假设要hook某个App里的 com.example.module.LoginUtil类的 checkLogin方法 javascript Java.perform(function() { var LoginUtil Java.use(com.example.module.LoginUtil); LoginUtil.checkLogin.implementation function(username, password) { console.log(username: username); console.log(password: password); var result this.checkLogin(username, password); console.log(result: result); return result; }; });这段脚本做的事情很朴素在checkLogin被调用的时候把入参和返回结果打印出来。你可能会问这不就是加个日志吗对但关键点在于——你不需要修改App代码也不需要重新打包签名App在完全不知道的情况下就暴露了关键信息。脚本运行方式也简单电脑上先安装Frida工具然后手机端跑一个frida-server进程你的电脑端通过USB连过去执行脚本frida -U -f com.example.app -l hook.js这条命令的意思是以附加模式启动“com.example.app”包名的App并加载hook.js脚本。第一次跑通这个流程看到控制台里打印出自己写的日志那种感觉基本等于打开了新世界的大门。4.3 调试能力的分层swing低配到高配动态调试不是只有Frida一种方案。我会建议你按下面这个层次逐步扩展工具版图工具擅长场景上手难度adb命令查看进程、日志、截图、文件传输、Activity栈低Android Studio调试器可以下断点单步调试app的smali代码中Frida快速hook方法、打印调用栈、修改返回值中Xposed/LSPosed框架接管系统级钩子实现全局、持久化的hook中高IDA Pro分析Native层so库的汇编代码高很多人一开始就直奔Frida反而忽略了adb本身。其实adb是整个动态分析的地基——查看内存信息用adb shell dumpsys meminfo切换Activity用adb shell am start模拟用户输入用adb shell input tap。Frida有连接问题时排查的第一步往往就是检查adb服务正不正常。我早期学动态调试最大的坑是分不清“业务逻辑”和“调试框架”的报错。比如Frida脚本报找不到类其实是该类在App启动后才由插件动态加载的你得用Java.perform包裹并延迟调用或者在Java.choose里枚举实例。这种问题特别容易劝退新人因为报错信息看起来像是脚本写得不对其实是对App运行生命周期没概念。5. 网络协议层逆向的核心战场之一很多App的核心逻辑不在客户端代码里而藏在服务器端的接口逻辑上。你要是能把App发出的网络请求还原清楚很多问题就迎刃而解了。5.1 抓包环境搭建的基本思路抓包工具选择上面向HTTP/HTTPS协议Charles是常见的方案Burp Suite在安全测试里也很常用。二者的核心思路差不多把设备网络流量导向代理服务器通过中间人方式解密HTTPS流量。具体的搭建流程大概是电脑和手机连接同一个局域网在电脑上开启代理服务记住代理端口通常是8888手机上设置WiFi代理填入电脑IP和端口手机浏览器访问代理工具的证书下载页面安装并信任证书重启App开始抓包这一步最大的坑在于证书信任Android 7.0以上默认不再信任用户安装的证书你会看到抓到的HTTPS包里全是“无法解密”的状态。常见解决思路有两个一是把证书安装到系统证书目录需要root二是用一个支持直接把证书嵌入系统目录的模拟器环境。这不是什么高深技术但几乎每个人都会在这个步骤卡一下提前知道就好。5.2 从明文到加密参数与请求体的识别方法学习阶段最好用的训练样本是那些协议相对简单的Android App。一般它们的请求格式是JSON格式的明文或者base64编码一眼就能读出来。举个例子某天气类App请求体长这样{ city: beijing, date: 2024-05-20 }你在抓包里看到这种结构就很容易联想到App内部某个叫getWeather方法的参数列表。所以做逆向解密时不要闷头分析加密算法先看请求包——如果能直接看懂说明App根本没有对参数做加密那分析成本就很低。进阶一点的情况是App会把参数打包成一个带签名的字段。例如请求体变成{ city: YmVpamluZw, sign: a10c9ab63cd5d5d0a6b231 }这时你要顺着代码找sign是怎么生成的。通常会在某个工具类里发现getSign()方法方法内部逻辑可能是把参数拼接成字符串再加上一个固定盐值salt然后做MD5计算。找到这类逻辑整个请求就彻底透明了。5.3 真实App的协议反识别抓不到包怎么办现实要比教材复杂得多。大量App用了HTTPS证书固定Certificate Pinning即使你装了证书也不认。还有一部分App把请求数据做了自定义二进制序列化抓包看到的基本是乱码没法直接阅读。这两个问题的解决思路不一样。证书固定属于客户端的问题用Frida hook掉证书校验方法是最灵活的方式。你hook的基本都是这一类方法checkServerTrustedjavax.net.ssl里的X509TrustManager方法verifyHostnameVerifier里的方法OkHttp库里的CertificatePinner类二进制协议则需要你学会用Wireshark看TCP流或者直接在客户端内存里找序列化写入的逻辑。说实话这属于进阶内容新人先不用钻太深但你必须知道有这么一条路存在。6. 进阶关卡加固、混淆与系统性练习当你把前面五步都走顺了大概已经能用静态动态结合的方式分析大多数不再有防护的App。但真实世界里的主流App都不会裸奔加固和混淆已经成为标配。到了这个阶段学习内容才真正开始变得“硬核”。6.1 壳的认识App为什么要加固加固技术的本质就是把原本清晰的dex文件加密或者抽取运行时才在内存中还原。这样直接下载APK反编译只能看到壳本身的代码看不到业务逻辑。常见的加固方案基本都遵循类似流程APK打包完成后壳引擎接管了入口原dex被加密后放在assets目录中运行时壳先启动然后动态解密并加载原dex。对应的脱壳思路也就清晰了趁App运行起来、dex已经被解密到内存中的那一刻把内存里的dex dump出来再用------------------------------------------------------------静态工具分析。这个操作在技术社区里叫“内存脱壳”配合Frida的DexDump功能或专门的dump脚本不少加固方案都能处理。这个阶段我不建议直接去研究某一家加固产品的过壳方案太深且容易走偏。的打开方式是先找一个没有加固的小App自己把dex加工加密过一遍写一个解密loader跑起来完整理解“加密-还原-加载”这个过程然后你再去碰真实壳就会觉得它并不神秘。6.2 混淆策略不只是改名字那么简单混淆分为几个层次从最浅到最深分别是标识符混淆把checkLogin改成a通过jadx看就像在看天书控制流混淆把正常的顺序执行打乱插入大量无意义的跳转和分支字符串加密把代码里的敏感字符串比如URL、密钥加密成密文运行时才解密Java反射化把直接的函数调用改为反射调用隐藏调用链面对混淆核心策略就是“不求还原只求定位”。你不需要把整个App还原得和源码一样只需要定位到你关心的那个关键函数上。手段也很朴素搜索常量密钥、URL、加密算法名称如“AES”搜索系统API调用如网络请求库OkHttp、加密库javax.crypto动态hook打印调用栈让App自己告诉你它调了谁对比分析混淆前后的功能逻辑不变UI行为能帮你定位功能入口我个人觉得字符串加密是最值得花时间研究的领域。因为即使控制流再乱、再反射化只要字符串要显示出来、要发送出去就得在某个地方还原成明文。顺着字符串的解密调用往回追往往能一路追到核心算法。6.3 一个可落地的系统性练习路径学习路线如果没有练习项目等于白规划。这里我给出一个自己带过多人验证过的练习路径全程不用真实敏感服务只针对学习样本写一个简单的登录Demo带用户名/密码输入和服务端验证逻辑服务端用本地脚本模拟用jadx静态分析定位登录方法用Frida hook拿到入参给这个Demo加上签名校验和简单的字符串加密尝试脱壳、还原字符串找一个开源的、不做防护的历史版本App做全面分析画出它的模块与接口调用关系主动给某个没有防加固的小App做一层“模拟加固”自己研究解密加载过程同时写一个还原工具这个路径的好处是每一个阶段都有清晰的产出物不会让人产生“我学了但不知道学得怎么样”的焦虑。我经常跟学习者说逆向学习不是比谁收藏的资料多而是比谁能把一个样本从拿到手到讲清楚的全流程走下来能力是在一个个样本的完整分析中长出来的不是说出来的。7. 写在最后几个让我受益很久的复盘习惯路线规划到这儿基本就完整了但我还想分享几个关于学习方法本身的体会这些经验比具体工具更让我受益。第一是坚持写分析报告。每分析完一个样本花半小时整理成文档目标是什么、用了哪些工具和步骤、卡在哪里、怎么解决的、有没有更快的方法。这个过程看着费时间实则是把碎片知识结构化过了几个月回头翻你会发现自己已经走了很远。第二是切换难度梯度。不要连续几天啃同一个硬壳也不要一直只做简单的App。一天难一天易难度交替对保持学习状态特别有帮助。简单样本带来的成就感能维持信心难得样本带来的冲击感能拓宽思路。第三是多动手造轮子。静态分析工具用顺手了自己写一个小脚本批量提取APK信息Frida脚本写多了自己整理一个常用的hook模板库。造轮子的过程会让你对工具底层原理有更深的理解也会在你未来分析复杂样本时给足底气。安卓逆向这条路说不上轻松但绝对值得。它像一扇门打开之后你看到的不是一个孤立的技术点而是整个Android系统的运行脉络——从字节码到内存从客户端到服务器从底层指令到业务逻辑。希望这份路线记录能让你少走一些我曾经走过的弯路。
阅读完成 · 觉得有帮助?