Something u have to know:
虽然Android平台使用Java来开发应用程序,但Android程序却不是运行在标准的Java虚拟机上,而是将Java字节码转换成Dalvik字节码,并打包到一个DEX可执行文档当中,Dalvik虚拟机通过解析DEX文件来执行这些字节码。
因此我们想了解安卓逆向,就要先了解一下Dalvik的语法。
目录
0x01 工具准备:
学习时我们可以用J2S2J(Java to Smali to Java转换工具):
0x02 语法了解:
本章节只录入部分,初次阅读快速浏览熟悉部分语法即可。
1、数据类型对应表:
smali类型 | java类型 |
---|---|
V | void (用于返回类型) |
Z | boolean |
B | byte |
S | short |
C | char |
I | int |
J | long (64 bits) |
F | float |
D | double (64 bits) |
2、运算符描述表:
smali运算符 | 描述 |
---|---|
add-int v0, p1, p2 | v0 = p1 + p2 |
sub-int v0, p1, p2 | v0 = p1 - p2 |
mul-int v0, p1, p2 | v0 = p1 * p2 |
div-int v0, p1, p2 | v0 = p1 / p2 |
rem-int v0, p1, p2 | v0 = p1 % p2 |
and-int v0, p1, p2 | v0 = p1 & p2 |
or-int v0, p1, p2 | v0 = p1 │ p2 |
xor-int v0, p1, p2 | v0 = p1 ^ p2 |
shl-int v0, p1, p2 | v0 = p1 << p2 |
shr-int v0, p1, p2 | v0 = p1 >> p2 |
ushr-int v0, p1, p2 | v0 = p1 >>> p2 |
add-int/2addr v0, v1 | v0 = v0 + v1 |
sub-int/2addr v0, v1 | v0 = v0 - v1 |
add-int/lit16 v0, v1, lit16 | v0 = v1 + lit16 |
sub-int/lit16 v0, v1, lit16 | v0 = v1 - lit16 |
add-int/lit8 v0, v1, lit8 | v0 = v1 + lit8 |
sub-int/lit8 v0, v1, lit8 | v0 = v1 - lit8 |
3、类名对照表:
smali类名 | java类名 |
---|---|
Ljava/lang/String; | java.lang.String |
Ljava/lang/Object; | java.lang.Object |
4、数组对照表:
smali数组 | Java数组 |
---|---|
[I | int[] |
[[I | int[][] |
[Ljava/lang/String; | String[] |
几维就在类型前加几个[号,最多255维 |
5、其他指令描述表:
常用指令 | 描述 |
---|---|
const/4 v0, 0x0 | 给v0寄存器赋值0(此格式为int4bit) |
move v0, v1 | 将v1的值移入v0中 |
invoke- | 调用某方法 |
if-eq v0, v1, :cond_0 | if (v0 == v1) cond_0 (eq==,ne!=,gt>,ge>=,lt<,le<=,eqz==0) |
iget | 取值(默认int,iget-类型) |
iput | 赋值(int,iget-类型) |
0x03 举例解读:
1、乘法运算:
J2S:
Java:
public class BaseData
{
public int add(int i, int j)
{
return i * j;
}
}
Smali:
.class public LBaseData; // 本类
.super Ljava/lang/Object; // 父类
.source "BaseData.java" // 文件名
# direct methods
// 构造方法, 方法名为<init>
.method public constructor <init>()V // 该段未在java代码中显示,java中默认有一个隐藏的无参数的构造方法
.registers 1 // 声明算上参数, 一共需要几个寄存器(寄存器用p) 不算参数使用locals(寄存器用v)
.prologue
.line 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
# virtual methods
// 自定义的add方法
.method public add(II)I
.registers 4
.prologue
.line 5
mul-int v0, p1, p2 // v0=p1*p2
return v0
.end method
2、for循环:
J2S:
Java:
public class ForData
{
public void forFuc()
{
for(int i = 0;i<10;i++)
{
System.out.println(i);
};
};
public static void main(String[] args)
{
ForData data = new ForData();
data.forFuc();
}
}
Smali:
.class public LForData; // 本类
.super Ljava/lang/Object; // 父类
.source "ForData.java" // 文件名
# direct methods
// 构造方法, 方法名为<init>
.method public constructor <init>()V // 该段未在java代码中显示,java中默认有一个隐藏的无参数的构造方法
.registers 1 // 声明算上参数, 一共需要几个寄存器(寄存器用p) 不算参数使用locals(寄存器用v)
.prologue
.line 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
// 主方法
.method public static main([Ljava/lang/String;)V
.registers 2
.prologue
.line 12
new-instance v0, LForData; //根据ForData类创建对象,保存在v0中
invoke-direct {v0}, LForData;-><init>()V //调用<init>构造方法
.line 13
invoke-virtual {v0}, LForData;->forFuc()V //调用自定义方法forFuc
.line 14
return-void
.end method
# virtual methods
// 自定义方法forFuc
.method public forFuc()V
.registers 3
.prologue
.line 5
const/4 v0, 0x0 // v0 = 0
:goto_1
const/16 v1, 0xa // v1 = 10
if-ge v0, v1, :cond_d // if ( v0 > v1 ) 跳转cond_d
.line 7
sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream; //获取PrintStream类成员变量out对象,并放到v1寄存器中 v1=out()
invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V //调用printLn方法中V1实例打印v0 println.out(v0)
.line 5
add-int/lit8 v0, v0, 0x1 // v0++
goto :goto_1 // 循环
.line 9
:cond_d
return-void // 直接返回、跳出
.end method
本篇基础引导,可以把自己的一些Java项目通过J2S2J工具转换并分析,熟练掌握Java代码和Smali代码的互相转换,为安卓逆向代码层打好基础。