尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Android逆向学习路线:从零基础到协议还原的实战指南

Android逆向学习路线:从零基础到协议还原的实战指南 很多人找我聊Android逆向上来第一句话基本都一样能不能给我一份学习路线图。我每次都会反问一句你为什么要学逆向不是抬杠是真的会影响你后面几个月的路线选择。网上一搜“android”和“逆向”两个字配合“学习路线”能翻出一大堆脑图颜色标得密密麻麻知识点排到三四十个但真按那些图从头啃我见过大多数人三个月后还卡在环境配置和工具安装上。这篇文章我不给你画漂亮脑图只讲一条我自己踩过不少坑之后觉得“真正走得通”的学习路线。我会把每个阶段该学什么、学到什么程度、用什么方式检验自己是否达标都说清楚也会解释每个环节背后的原理而不是让你死记命令。内容主要适合三类人零基础想转安全方向的技术爱好者、想理解自己App如何被分析的移动开发者、以及准备从黑盒测试往白盒方向走的测试朋友。如果你已经能熟练用Frida跑脚本、能独立还原签名算法可以直接跳到第五章看协议层逆向的思路。1. 先想清楚为什么学Android逆向这直接决定你要走哪条路线1.1 给目标分类再谈学习顺序同样是逆向有人是为了做合规检测有人是想做安全研究有人只是想让某款单机游戏的体力变成无限。目标不同学习路线的权重差距非常大。我把最常见的几类目标拆开说安全研究、渗透测试方向重点在四大组件暴露面、WebView漏洞、导出组件风险、Native层漏洞利用以及反调试对抗。这类人课程表里最重的是“漏洞发现思路”和“调用链分析”。App合规检测、隐私审计方向重点在第三方SDK收集了哪些数据、关键API在什么时机被调用、有没有在用户不知情的情况下上传个人信息。这类需求在近两年增长很快但网上专门讲这个路线的资料反而少。个人开发者想做技术研究比如分析竞品App的接口签名、学习某款优秀App的实现重点在协议还原、算法定位、请求参数构造。这一类是大多数人对“逆向”的第一印象也是吸引最多人入坑的方向。CTF选手方向重点在混淆对抗、VM保护、加密算法识别、反调试绕过。题目更纯粹不需要考虑商业App的复杂架构。这四种画像对应的学习顺序是不一样的。目标模糊的时候最容易犯的错是把所有知识点平均分配时间结果就是不精通。我建议你动手前先花半小时想清楚自己的最终目标拿张纸写下来贴在电脑边上。1.2 用“目标倒推法”设计自己的学习顺序我自己带人时比较喜欢一种方法目标倒推。别从第一行脑图开始而是先定义“学完之后我要能独立完成什么事情”然后反推需要哪些技能。举个例子假设你的目标定成能独立分析一款App的登录接口签名算法最终能脱离设备在脚本里复现出有效的sign值。那么倒推出来的技能链路大致是Java/Kotlin基础 → 能读懂反编译代码 → 能看懂Smali控制流 → 会用jadx做静态分析 → 会用Frida做动态Hook → 能定位签名函数 → 能识别算法类型并复现。你会发现这条链路里根本不需要一开始就啃ARM汇编也不需要先学脱壳。那些知识到后续阶段才会用到等你把主要链路走通再补效率会高很多。这个“从目的地倒着找入口”的思路比顺着教程一路往下学要好用得多因为每个知识点在你脑子里都有明确的位置它是用来解决最终目标中哪一环问题的。2. 打地基Java/Kotlin、Smali、ARM汇编学到什么程度才算够用2.1 Java/Kotlin目标不是写代码而是能读懂反编译后的代码很多零基础朋友一听说要先学Java就去买了一整套入门书结果还没看到集合就放弃了。这里要说清楚你做逆向需要学的Java和做开发需要学的Java交集只有一部分。你需要重点掌握的是这些面向对象的基本概念类、继承、接口、匿名内部类、反射机制、泛型、集合类、多线程和Handler机制、JNI接口的调用方式、注解的基本含义。为什么是这些因为在反编译工具里你看到的代码大量以反射调用来实现动态加载集合类贯穿所有数据处理逻辑而JNI接口是用Java层进入Native层的唯一入口。Kotlin也要能看懂。现在上架的App用Kotlin写的比例越来越高反编译出来的代码和Java差异很大会有大量的Intrinsics.checkNotNullParameter之类空安全检查协程相关的Continuation参数堆满整个方法签名。别慌你不需要能写Kotlin只需要能识别出“这是Kotlin反编译后的冗余代码”并忽略掉它们找到真正干活的业务逻辑。检验标准拿一个开源的Android项目反编译你能快速说出某个页面从入口到数据获取的完整调用链就算过关。2.2 Smali看着难抓主线就好Dex文件反编译成Smali之后很多新手会被大量.method、iget、invoke-virtual给吓住。其实Smali的定位很简单它是Dalvik虚拟机使用的寄存器汇编语言和普通汇编的思维方式类似只是指令集不同。你不需要背指令手册但需要能读懂这些高频指令方法调用类的invoke-virtual、invoke-static、invoke-direct字段读写类的iget、iput、sget、sput条件跳转类的if-eq、if-ne、if-gtz返回类的return-void、return-object。给你一个最直观的对比// Java 层 if (a 0 b.equals(ok)) { Log.d(TAG, pass); } else { Log.e(TAG, fail); }对应的Smali结构大致是if-gtz v0, :cond_0 const-string v1, ok invoke-virtual {v2, v1}, Ljava/lang/String;-equals(Ljava/lang/Object;)Z move-result v3 if-eqz v3, :cond_0 # 条件成立do something invoke-static ... Log.d goto :cond_1 :cond_0 # 条件不成立do other invoke-static ... Log.e :cond_1 return-void你看其实逻辑并不复杂把寄存器里的值当作普通变量就行。Smali里还能看到字符串常量最基础的算法定位就是从搜字符串开始的。这里有个经验不要急着学“写Smali改逻辑”那是二次打包方向的技能和你后面分析协议关系不大。先做到“能阅读懂”就行。2.3 ARM汇编前期可以不深挖但要知道什么时候必须学很多人都被说自己得会逆向汇编结果买了几本大部头学了两个星期连寄存器名字都没记住。实际上ARM汇编在Android逆向里的出场时机很晚。当你发现核心算法不在Java层而在lib/arm64-v8a下面的某个.so文件里时才需要真正上手。在此之前Java层分析加Frida Hook已经能解决掉至少七八成的业务逻辑问题。但你现在不深学不代表不需要懂基本概念。至少要知道这几个fd、x0到x30这些通用寄存器的作用BL和BR指令代表跳转LDR/STR是内存读写函数调用时前几个参数放在寄存器里而不是全压栈。理解这些你在Ghidra或IDA里看反汇编代码时至少能跟着大概走通一个函数的结构。ARM汇编该怎么练我的建议是等你在实际样本里遇到“这个Java层函数是空的逻辑全在native里”的状况时再去系统补。那时候你带着真实问题学效率比你空学高好几倍。2.4 基础阶段的自我检验清单我建议基础阶段不要超过二十天用三个小练习来检验练习一下载任意一个体积适中的APK比如一个工具类应用用jadx打开找到它的MainActivity说清楚启动时做了什么。练习二同样这个APK用apktool反编译出Smali找到任意一个带invoke-super的onCreate方法画出它的执行流程。练习三在代码里搜索String类型硬编码中的“sign”、“key”、“token”相关关键词看能不能找到疑似签名相关的函数。这三个练习做完你对Android应用的基本代码结构就有感觉了。不要追求完美能走通流程就是胜利。这个阶段最容易掉的坑是在工具安装和Android SDK配置上死磕几天。我的建议是环境装不动就先别装了用在线反编译也好找一台已经装好工具的环境也好先把分析思路跑通回头再补环境。3. 静态分析完整链路拿到APK之后你能看到什么、该看哪里3.1 先认识APK的“解剖图”APK本质上是一个ZIP压缩包你先用解压思维理解它AndroidManifest.xml描述应用组件信息classes.dex存放Dalvik字节码也就是编译后的Java/Kotlin代码lib/目录按CPU架构存放C/C编译出的.so文件assets/和res/存放资源和原始文件META-INF/存放签名信息。静态分析的第一件事就是用工具把这份二进制翻译成人类能读的东西。我常用的工具就五款各有分工工具定位适用场景jadxJava反编译GUI日常读代码首选直接把Dex还原成接近Java的源码apktool资源/Smali反编译与回编译需要看AndroidManifest、资源文件、或者二次打包时使用GDA国产免费的综合性反编译器不想装界面复杂的大型工具时轻量顶上来JEB商业级综合逆向平台大型App、NativeJava交叉分析调试功能强Ghidra / IDANative层反汇编分析.so文件时必备IDA更贵但行业标准Ghidra免费开源我用jadx的频率最高因为它对中小型App的还原效果最好界面点开即用。JEB功能更强能同时看Java反编译结果和Native反编译结果但价格不友好新手前期不建议投入。遇到的App一多你会发现没有哪个工具是万能的更合理的组合是用jadx先快速定位到目标函数如果函数是Native的再用Ghidra或IDA跟进so文件。3.2 入口定位不要漫无目的翻代码新手最常见的问题是打开jadx之后看到左侧几千个类瞬间不知道看哪里。记住这个原则逆向阅读是从“入口”开始的不是从“代码中间”开始的。定位入口我总结五步第一步看AndroidManifest.xml里有哪些组件设置了android:exportedtrue这些可被外部启动的组件是黑盒测试时注意力最集中的地方。第二步看Application子类的attachBaseContext和onCreate很多App在这里做初始化操作比如设置异常捕获、启动插件化框架、判断是否调试状态。第三步搜索特征字符串。任何App都有独特的硬编码字段比如签名算法里的appKey、请求头里的User-Agent、URL路径里的/api/login等。在jadx的搜索框里敲字符串你会发现比人肉翻代码快十倍。第四步反向追调用链。你发现目标函数后右键“find usage”看谁调用了它一层一层往上走业务逻辑的完整链路就出来了。第五步利用崩溃日志和错误提示反查。如果在测试中能触发某个报错日志里打印的类名和方法名可以直接当作搜索线索。3.3 静态分析中最常见的三堵墙实际分析中你几乎一定会遇到这三类障碍第一类是代码混淆。Android自带的R8/ProGuard会把类和成员重命名成a.a.a这种无意义的名称。遇到这种情况别试图记忆类名而是跟着字符串和函数调用关系走再从代码行为推断职责。第二类是字符串加密。很多App会用自定义函数对关键字符串做异或、Base64或AES加密防止你直接搜索。这个不复杂找到解密函数后手动调用一次把所有字符串解出来就能继续走。第三类是加固和VMP。如果样本是加固的onCreate里看不到业务代码全被抽取到壳的Native层。这种情况别硬在jadx里翻先识别壳的类型再考虑脱壳或动态dump这部分我在第五章会具体说。静态分析的核心能力不是会用工具而是能在一个陌生工程里快速建立“这个App做什么、信息从哪里来、到哪里去”的全局认知。多练几个不同行业的App你会明显感受到这种能力的提升。4. 动态调试与Hook从“看代码”到“让代码按你的意图跑”4.1 Android Studio调试最容易被忽略却最基础的一招静态分析能看到代码但看不到运行时变量具体是什么。动态分析要解决的就是这个问题。很多新手一上来就学Frida忽略了最基础的传统调试器。实际上用Android Studio附加到进程打断点观察变量变化是学习代码执行流最好的入门方式。前提是目标App带android:debuggabletrue或者你用调试模式启动。对绝大多数自家写的测试样本、开源项目这个条件是满足的。传统调试器的原理和概念断点、单步执行、查看栈帧、改变变量值是通用的。你在这里练熟了“程序执行到哪里、变量发生了哪些变化”的直觉后面学Frida时进步会快很多。如果一开始就盯着Frida脚本却不知道代码执行顺序很容易写出各种莫名其妙不起作用的脚本。4.2 Frida为什么现在大家都用它做动态插桩动态插桩框架里Frida几乎成了事实标准。原因很实际不用重打包改APK不用刷机装Xposed模块只要设备上跑一个frida-server电脑端写JS或Python脚本就能实现Hook。现代化的App保护手段很多比如反重打包检测、签名校验。传统方式改APK再安装非常容易被识别而Frida是在进程运行时注入不改文件不容易触发这类检测。一个最基础的Java层Hook脚本长这样Java.perform(function() { var SignUtil Java.use(com.example.security.SignUtil); SignUtil.getSign.implementation function(a, b) { var result this.getSign(a, b); console.log(arg a a); console.log(arg b b); console.log(result result); return result; }; });逐行解释Java.perform是进入Java运行时环境的入口Java.use(com.example.security.SignUtil)拿到目标类implementation替换原方法的实现this.getSign(a, b)调用原逻辑拿到返回值最后打印参数和返回值。这是逆向里最重要的一个“起手式”把它练到滚瓜烂熟。刚开始的时候可以在自己写的Demo上做各种Hook比如HookSharedPreferences的读写方法HookLog.d打印所有Debug日志Hook网络库的拦截器打印请求和响应。这些练习能让你快速建立“我能控制这个App行为”的信心。学到中期不要只停留在Java层Hook。Frida也支持Native层Hook通过Interceptor.attach可以hook so文件里的导出函数甚至用Memory.scan找内存特征。这需要你具备第五章说的ARM汇编基础。我的建议是先把Java层用到随时随地能写出来再往Native层扩展。4.3 流量分析怎么知道应用往外发了什么分析和复现App的协议时关键点在于拿到完整请求参数和加密逻辑。很多新手一上来就想抓全量网络流量但在复杂商业App里网络层数据是加密的就算抓到也看不懂。更有效的思路是在进程中“提前拦截”加密前的明文。绝大多数App使用OkHttp或Retrofit做网络请求这些库在Java层都有清晰的请求构建链。你可以Hook OkHttp的Interceptor接口在请求发出前拿到URL、Headers、Body。对于不用OkHttp的App就Hook底层Socket的write函数同样能拿到明文数据。这种方式的优势在于你看到的是App业务代码真正组装出来的参数而不是网络链路上被TLS加密后的乱码。等你把参数结构搞清楚了再去研究某个参数怎么生成的就顺理成章了。需要强调的是做这种研究一定要在你自己拥有或明确授权的设备上进行用合法授权的方式训练自己的技术水平后面我还会再强调。4.4 反调试不是劝退是学习路线的下一站当你开始尝试分析稍微硬核一点的App时会遇到各种检测模拟器检测给你弹个“请在真机运行”反调试检测直接让进程退出Frida检测让你无论如何都附加不进去。这些不是学习路线的终点反而是进阶标志。常见检测思路包括模拟器检测检查Build.FINGERPRINT、IMEI、CPU指令特征、网络状态。ptrace反调试检查进程是否被ptrace调试这种方式对老式调试器有效。Frida检测扫描/proc/self/maps里有没有frida的特征路径尝试连接默认端口27042或者检测线程名。完整性校验检测自身签名和安装包哈希防止重打包篡改。你要做的不是一上来就学绕过而是先理解每种检测的实现原理然后才知道对应方案的适用边界。比如检测maps特征本质是“找文件路径”那思路就是改frida-server文件名和端口检测ptrace本质是“不允许自己被附加”那可以自己先抢ptrace或者正确情况下用frida早期注入。理解原理后再用工具你的对抗能力会有本质提升。5. 协议层逆向从单点Hook到整体还原真正的分水岭在这里5.1 为什么说Hook只是过程协议层还原才是目的很多人在学会了Frida之后觉得“我已经能Hook目标函数拿到返回值了这就算逆向成功了吧”。这是典型的假象。有个很直观的例子你Hook了某个App的登录接口看到它在返回一个sign值。可一旦离开Hook环境比如你想写个脚本模拟这个请求发现根本构造不出合法的sign。这就像你考试时带了本答案书但考试规则是不允许带进去——你只会“抄”不会“算”。真正的协议层逆向是把App内部的那套“算法”完整拿下来它用什么参数、按什么顺序拼接、用了什么哈希或加密算法、key是什么、有没有额外加盐。做完这些你不需要再Hook能在自己的代码里独立生成合法的请求参数。这才是“从单点Hook到整体还原”的分水岭。你平时会刷到很多热门词条比如AKAMAI逆向、企查查API接口逆向、淘宝App逆向、得物App逆向、大麦逆向之类。这些场景本质都是一类问题把客户端请求中的签名、风控参数和加密协议还原出来。商业App的风控体系通常包含几部分设备指纹、时间戳、随机数、业务参数、自定义签名算法缺一不可。掌握了协议层逆向的方法论换任何App只是换细节思路完全通用。5.2 一个虚构实例从定位到复现的完整路径好的方法论需要一个具体例子。假设你在分析一款虚构App登录接口有个sign参数长这样POST /api/login device_idabc-123 timestamp1710000000 signe10adc3949ba59abbe56e057f20f883e你试了把各个参数直接拼起来算MD5算出来对不上说明不是简单拼接。完整路径应该是这样第一步静态定位。搜索“sign”关键词或者搜索“MD5”找到负责签名的类。如果是加固过的这一步可能需要先脱壳或者动态dump才能看到代码。第二步动态验证。用Frida Hook签名函数的参数和返回值确认这个函数是否真的是生成sign的地方。多调几次记录不同参数下的返回值。第三步识别算法类型和魔改。观察返回值长度32位十六进制的可能是MD564位的可能是SHA-256如果两次相同入参的返回值不同说明加了随机数或时间戳。再通过对比Hook出的入参和返回值推算拼接格式是“参数时间戳key”还是“时间戳参数key盐”。第四步确认key来源。key可能在Java层硬编码也可能存在服务器下发后本地解密甚至存在so里。用Frida把内存里的key dump出来或者用Ghidra分析native函数。第五步代码复现并验证import hashlib import time import requests device_id abc-123 timestamp str(int(time.time())) app_secret com.example.secret_key # 通过分析得到 def make_sign(device_id, timestamp, app_secret): raw device_id timestamp app_secret return hashlib.md5(raw.encode(utf-8)).hexdigest() sign make_sign(device_id, timestamp, app_secret) resp requests.post( https://api.example.com/api/login, json{device_id: device_id, timestamp: timestamp, sign: sign} ) print(resp.status_code, resp.text)这一步如果返回结果和App内一致说明你把整个链路还原了。这个流程看起来简单实际中你会遇到各种变化比如同一函数既被MD5调用又被AES调用甚至用so里的OpenSSL加密后又拼接了一次。掌握不变的思路才是关键定位、验证、识别、复现每一步都做验证不猜。5.3 遇到加固和反调试先理解原理再找工具商业App的加固越来越普遍协议层逆向到一半经常发现关键代码在加固壳里。这时候网上能搜到各种脱壳工具但如果你不理解原理大概率会一头雾水甚至白白折腾几天。壳大体分三类DEX整体加壳把原Dex加密后藏进assets或so运行时在内存解密后加载。这类壳最简单用内存dump工具在运行时把解密后的Dex抓下来就行。函数抽取壳代码运行到某个函数之前才从服务器或本地解密该函数。这种壳导致内存中整包Dex不完整不能简单dump需要主动触发目标函数再抓取对应代码。VMP指令虚拟化把原始指令翻译成自定义字节码用虚拟机会解释执行。这类最难基本不能自动脱壳只能靠动态跟踪还原执行流程。理解这个分类后你的选择就清晰了先判断壳的类型再决定用哪种方案而不是随机下载一个工具乱试。需要提醒的是用脱壳工具研究自有或授权样本是正常的技术学习未经授权去脱壳分析别人的商业应用在多数情况下都属于越界行为。技术没有善恶用途一定要合法。6. 实战与成长路线图的最后一公里是什么6.1 用CTF压缩训练从babyre开始很多人觉得CTF和真实商业逆向差别大但它是性价比极高的训练场。CTF逆向题把算法逆向、混淆清除、反调试绕过浓缩在一个小样本里一道题就能让你把某一项技能练扎实效率比分析半个月真实App高得多。网上流传的babyre系列就是很好的入门起点。这类题目通常不会太刁难人但设计上能逼你掌握基本的Dex阅读、Smali分析和简单的算法识别能力。做题时别直接看Writeup先试着自己卡在哪个环节再对照别人的思路找差距进步会非常明显。CTF线上赛里每年都有大量新颖的逆向题目Python打包的exe、MCU固件、JS混淆代码、H5游戏逻辑都是真实场景的缩略版。打CTF积累的分析经验最终会反哺到你处理真实Android样本的能力上。6.2 在合规框架里做实战众测、SRC、自建靶场学习路线走到后期你要注意一件事真实环境里的“实战”和“非法破解”之间界限非常清楚。最稳妥的实战方式有三类安全应急响应中心SRC和众测平台很多厂商有公开的漏洞收集渠道你在授权范围内测试提交漏洞还有奖金。这是目前最健康、最可持续的成长方式。自己编译测试样本把开源项目改造成带漏洞的目标分析自己写出来的App再对比成熟加固方案完全合法还能学到系统知识。开源的恶意样本库和公开分析报告很多安全团队会把恶意样本脱敏后开源配合分析文章阅读是理解攻击链路的捷径。不要因为“某某App逆向”看起来热门就去碰没有授权的破解工程。技术能走多远往往取决于你给自己定的边界有多清晰。这条路要想做得久合规意识越早建立越好。6.3 逆向的尽头是风险对抗会打更要会守还有一个建议学到中后期一定要把视角从“攻击”翻到“防御”这一面。能逆向分析就一定能做对应的风险防护。你看过别人怎么Hook签名函数就知道怎么用So层做逻辑校验你看过别人怎么脱壳就知道函数抽取壳的弱点在哪会在设计加固方案时避开你看过别人怎么分析协议就知道在服务端做参数校验和风控策略比客户端藏算法更重要。现在很多公司招移动安全工程师实际工作要求就是“对手怎么破解我就怎么防”。你在逆向路上积累的技能会直接变成你的防护方案设计能力。能看懂攻击的人写出来的防御才有效果。最后我再说一点个人经验我带过不少新人发现学习逆向的最大卡点不是智商也不是基础而是信息噪音太多。今天看到这个工具、明天看到那个付费课收藏夹里一堆资料真正动手分析过一次完整样本的却没几个。这份路线不需要你找人带也不需要一开始就买几万块的课程把前三章的工具跑通、把Frida基础脚本写熟你已经超过了大多数收藏党。如果你今天真的想开始打开jadx随便挑一个APK走到入口函数就是很好的第一步。
返回列表