在开始复杂类分析前,让我们先回顾一些Smali基础知识:
基本指令:
move
:寄存器间数据移动const
:加载常量invoke
:方法调用if
:条件跳转return
:方法返回数据类型表示:
V
:voidZ
:booleanI
:intJ
:longF
:floatD
:doubleL
:对象引用.class public Lcom/example/ComplexClass;
.super Ljava/lang/Object;
.source "ComplexClass.java"
# 接口实现
.implements Ljava/lang/Runnable;
.class
:定义类名和访问权限.super
:父类信息.implements
:实现的接口# 静态字段
.field private static final TAG:Ljava/lang/String; = "ComplexClass"
# 实例字段
.field private mCounter:I
static
标记final
标记.method public constructor <init>()V
.registers 2
# 方法体
return-void
.end method
<init>
.registers
声明使用的寄存器数量if-eqz v0, :cond_0
# 条件为false时执行的代码
:cond_0
# 条件为true或后续执行的代码
:label
)用于跳转目标:loop_start
# 循环体
if-ge v0, v1, :loop_end
goto :loop_start
:loop_end
:try_start_0
# 可能抛出异常的代码
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
:catch_0
# 异常处理代码
.catch
指令定义的异常类型和处理范围new-instance v0, Ljava/lang/StringBuilder;
invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V
new-instance
创建对象实例invoke-direct
调用构造方法# 虚方法调用
invoke-virtual {v0, v1}, Lcom/example/Class;->methodName(II)I
# 静态方法调用
invoke-static {v0}, Lcom/example/Class;->staticMethod(Ljava/lang/String;)V
# 接口方法调用
invoke-interface {v0}, Ljava/lang/Runnable;->run()V
# 创建数组
new-array v0, v1, [I
# 数组元素访问
aget v2, v0, v1
aput v2, v0, v1
.method private encrypt([B)[B
.registers 10
array-length v0, p1
new-array v1, v0, [B
const/4 v2, 0x0
:loop_start
if-ge v2, v0, :loop_end
aget-byte v3, p1, v2
const/16 v4, 0x20
xor-int/2addr v3, v4
int-to-byte v3, v3
add-int/lit8 v4, v2, 0x1
rem-int/lit8 v4, v4, 0x8
add-int/2addr v3, v4
int-to-byte v3, v3
aput-byte v3, v1, v2
add-int/lit8 v2, v2, 0x1
goto :loop_start
:loop_end
return-object v1
.end method
分析要点: 1. 这是一个简单的XOR加密结合位置相关变异的算法 2. 对每个字节: - 先与0x20异或 - 然后加上(位置+1)%8的值 3. 结果存储在新建的数组中返回
.class public Lcom/example/ThreadManager;
.super Ljava/lang/Object;
.field private mThread:Ljava/lang/Thread;
.method public startThread()V
.registers 4
new-instance v0, Lcom/example/ComplexClass;
invoke-direct {v0}, Lcom/example/ComplexClass;-><init>()V
new-instance v1, Ljava/lang/Thread;
invoke-direct {v1, v0}, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
iput-object v1, p0, Lcom/example/ThreadManager;->mThread:Ljava/lang/Thread;
invoke-virtual {v1}, Ljava/lang/Thread;->start()V
return-void
.end method
分析要点: 1. 创建了一个实现了Runnable的ComplexClass实例 2. 将其传递给Thread构造器 3. 启动线程并将引用保存在mThread字段中
日志注入:在关键位置插入Log输出
const-string v0, "DEBUG_TAG"
const-string v1, "Reached point X"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
动态调试:使用JDB或Android Studio附加到进程调试
方法Hook:使用Xposed或Frida拦截方法调用
分析复杂Smali类时,建议采用以下步骤: 1. 先理清类结构和继承关系 2. 分析重要字段及其用途 3. 从入口方法开始,逐步跟踪关键流程 4. 注意对象创建、方法调用和数据流动 5. 对加密/混淆部分单独重点分析 6. 必要时使用动态调试验证分析结果
通过系统化的分析和适当的工具辅助,即使面对高度混淆的Smali代码,也能逐步理清其逻辑和功能。