28、Android逆向基础:APK结构、dex2jar与jd-gui、Smali语法基础、Xposed框架

说到Android逆向,我刚开始接触的时候也是一头雾水。那时候公司接了个竞品分析的需求,对方App加了壳,我折腾了整整三天才找到突破口。今天我就把这套基础工具链和思路,掰开了讲给你听。

28.1 APK文件结构:你拆开看看

APK说白了就是一个压缩包,你把后缀改成.zip,解压就能看到里面的东西。但这里头每个文件都有它的使命。

文件/目录 作用
AndroidManifest.xml App的身份证,权限、组件、版本号全在这
classes.dex 核心代码,Dalvik字节码文件
resources.arsc 编译后的资源索引表
res/ 原始资源文件(布局、图片等)
lib/ Native层.so库,armeabi、arm64-v8a等
META-INF/ 签名信息,篡改后安装会报错

我个人习惯,拿到一个APK先看AndroidManifest.xml。为什么?因为这里暴露了App的入口Activity、用到的权限、还有有没有开启调试模式。我曾经遇到过一个App,它的核心逻辑藏在了一个不导出的Service里,但Manifest里漏了exported="false"——嗯,这就是突破口。

小技巧:用aapt dump badging app.apk可以快速查看Manifest信息,不用解压。

28.2 dex2jar + jd-gui:把字节码变回Java

classes.dex是Dalvik虚拟机跑的东西,人眼直接看很费劲。我们需要把它转成JAR,再用jd-gui打开,就能看到接近源码的Java代码了。

操作流程其实就三步:

  1. 把APK解压,拿到classes.dex
  2. 执行 d2j-dex2jar.sh classes.dex,生成classes-dex2jar.jar
  3. 用jd-gui打开这个JAR文件

你想想看,原本一堆乱码似的字节码,瞬间变成了可读的Java代码。虽然变量名可能被混淆成a、b、c,但逻辑结构是完整的。

注意:dex2jar对加壳的App无效。你需要先脱壳,拿到真正的dex文件。我曾经踩过这个坑,对着一个空壳子分析了一下午,后来才发现是没脱壳。

28.3 Smali语法基础:Dalvik的汇编语言

有时候jd-gui反编译出来的代码不完整,或者你想修改逻辑再回编译,那就得直接跟Smali打交道了。Smali是Dalvik字节码的人类可读形式,后缀是.smali。

我挑几个最常用的语法说:

Smali指令 含义
.class public Lcom/example/Test; 声明一个public类
.method public onClick()V 定义一个返回void的public方法
const/4 v0, 0x1 把整数1存入v0寄存器
invoke-virtual {v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V 调用虚方法,比如System.out.println
if-eqz v0, :cond_0 如果v0等于0,跳转到cond_0标签
return-void 返回空

说白了,Smali就是寄存器式的汇编。Android的Dalvik虚拟机有16个通用寄存器(v0-v15),参数寄存器(p0-pn)。p0是this,p1开始是方法参数。

举个例子,你想修改一个App的VIP判断逻辑。原Smali可能是这样:

.method public isVip()Z
    .registers 2
    const/4 v0, 0x0
    return v0
.end method

把const/4 v0, 0x0改成const/4 v0, 0x1,就变成永远返回true了。改完用apktool回编译,重新签名安装就行。

核心思路:逆向不是从头写代码,而是找到关键判断点,改一个字节就能改变整个逻辑。

28.4 Xposed框架:运行时Hook的艺术

Xposed跟上面那些静态分析不一样,它是动态的。你不需要改APK文件,直接在手机上装个框架,写个模块就能在运行时拦截、修改任何方法的调用。

我当年第一次用Xposed的时候,感觉就像开了上帝视角。你想想看,一个App的加密函数,你不需要知道它怎么实现的,直接在返回值上Hook一下,把结果打印出来就行了。

一个典型的Xposed模块长这样:

public class MainHook implements IXposedHookLoadPackage {
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {
        if (!lpparam.packageName.equals("com.target.app"))
            return;
        
        findAndHookMethod("com.target.app.LoginActivity", 
            lpparam.classLoader,
            "checkPassword", 
            String.class,
            new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) {
                    // 参数是param.args[0]
                    XposedBridge.log("密码是: " + param.args[0]);
                }
                
                @Override
                protected void afterHookedMethod(MethodHookParam param) {
                    // 返回值是param.getResult()
                    param.setResult(true); // 直接返回验证通过
                }
            });
    }
}

这里要注意几个点:

  • 包名必须写对,不然Hook不生效
  • 方法签名要精确,参数类型不能错
  • beforeHookedMethod在方法执行前触发,afterHookedMethod在之后触发
避坑指南:Xposed在Android 7.0以上需要处理SELinux策略,而且部分App会检测Xposed框架的存在。我曾经遇到过App检测到Xposed就直接闪退,后来用了隐藏模块才解决。

28.5 知识体系总览

下面这张图,我把这节的核心逻辑串起来了。你顺着箭头看,就能明白逆向分析的一条完整路径。

Android逆向基础:知识体系 APK结构分析 静态分析 动态分析 AndroidManifest classes.dex resources/lib dex2jar jd-gui Smali语法 Xposed框架 Hook方法 运行时修改 目标:理解App逻辑 → 定位关键点 → 修改或提取数据

你看,从APK结构入手,你能找到入口和权限信息。然后通过dex2jar和jd-gui做静态分析,把Java代码还原出来。如果遇到混淆或者想动态验证,就上Smali改字节码,或者用Xposed在运行时Hook。这三板斧下来,大部分App的逻辑都能摸清楚。

我的建议:初学者先别急着上Xposed,先把Smali和静态分析练熟。我见过太多人一上来就搞Hook,结果连目标方法在哪都找不到。基础打牢了,后面自然水到渠成。

公众号:蓝海资料掘金营,微信deep3321