Smali汇编基础
基本类型
寄存器
寄存器命名方式有两种:
.method static add(II)I
.locals 2
const/4 v0, 0x4
const/4 v1, 0x5
add-int/2addr p0, p1
return p0
.end method
.method add(II)I
.locals 0
add-int/2addr p1, p2
return p1
.end method
基础指令
const/4 v0, 0x1 # 将值0x1存到寄存器v0
const-string v0, "HelloSmali" # 将字符串"HelloSmali"存到寄存器v0
invoke-static {v0, v1}, Lcom/smali/test/Test;->test(II)I
move-result v0 # 将方法调用的结果值存储的v0
return v0 # 函数返回寄存器v0中存储的值
# 构造指定类型对象并将引用赋值给寄存器p1
new-instance p1, Lcom/smali/test/Test;
const/4 v0, 0x5 # 定义数组元素个数
new-array v1, v0, [Ljava/lang/String; # 创建字符串数组
# if条件语句和goto跳转示例
.method static test(I)V
if-eq p0, v0, :cond_1
.....
:cond_1
.....
goto :goto_0
.....
:goto_0
return-void
.end method
# packed-switch示例
.packed-switch v0 # packed-switch解析v0, 根据不同解析值则走相应的分支
:pswitch_0
:pswitch_1
:pswitch_2
.end packed-switch
语法修饰
.class <访问权限修饰符> [非权限修饰符] <类名>
.super <父类名>
.source <源文件名称>
示例代码
.class public Lcom/smali/test/Test;
.super Ljava/lang/Object;
.source "Test.java"
Nage接口描述,如果该类实现了某个接口,则会通过.implements定义,其格式如下:
#interfaces
.implements <接口名称>
.implements Landroid/view/View$OnClickListener;
.annotation [注解的属性] <注解类名>
[注解字段=值]
...
.end
.annotation runtime Ljava/lang/Deprecated;
.end annotation
#instance fields
.field <访问权限修饰符> [非权限修饰符] <字段名>:<字段类型>
举例说明:
.field private TAG:Ljava/lang/String;
#static fields
.field <访问权限> static [修饰词] <字段名>:<字段类型>
# static fields
.field private static final pi:F = 3.14f
.class public Lcom/smali/test/Test;
.super Ljava/lang/Object;
.source "Test.java"
.method public constructor <init>()V
.locals 2
.line 8
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
const-string v0, "Smali"
const-string v1, "This is constructor method!"
.line 9
invoke-static {v0, v1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
return-void
.end method
.method static add(II)I
.locals 0
.annotation runtime Ljava/lang/Deprecated;
.end annotation
add-int/2addr p0, p1
add-int/lit8 p0, p0, 0x1
return p0
.end method
函数调用
.method public/private [static] method()<返回类型>
<.locals>
[.parameter]
[.prologue]
[.line]
<代码逻辑>
.end method
# direct methods
.method public constructor ()V
.registers 2
.prologue
.line 8
invoke-direct {p0}, Landroid/app/Activity;->()V
.line 10
const-string v0, "MainActivity"
iput-object v0, p0, Lcom/test/demo/MainActivity;->TAG:Ljava/lang/String;
.line 13
const/4 v0, 0x0
iput-boolean v0, p0, Lcom/test/demo/MainActivity;->running:Z
return-void
.end method
invoke-指令类型 {参数1, 参数2,...}, L类名;->方法名
invoke-direct {p0, v0, v1}, Lcom/smali/test/Test;->start(II)I
invoke-virtual {p1}, Lcom/smali/test/Test;->start()V
invoke-static {}, Lcom/smali/test/Test;->test()V
invoke-super {p0, p1}, Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
invoke-interface {v0}, Ljava/util/List;->size()I
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static start()I
.locals 2
const/4 v0, 0x4
const/4 v1, 0x5
invoke-static {v0, v1}, Lcom/smali/test/Test;->test(II)I
move-result v0
return v0
.end method
.method public test()Ljava/lang/String;
.locals 2
invoke-virtual {p0, v0}, Lcom/smali/test/Test;->test()
Ljava/lang/String;
move-result-object v0
return-object v0
.end method
ARM汇编基础
寄存器
分组寄存器R8~R14,分组寄存器的访问与当前处理器模式相关,如果想不依赖处理器模式访问特定的寄存器则需要使用规定的寄存器名字。
执行指令如下:
MOV PC,LR
BX LR
STMFD SP!,{<rsgisters>,LR}
LDMFD SP!,{<registers>,PC}
状态寄存器,程序状态寄存器有包含1个当前程序状态寄存器CPSR和5个备份的程序状态寄存器SPSR。当前程序状态器CPSR在任何处理器模式下均可被访问,CPSR用于标记当前程序的运算结果、处理器状态、当前运行模式等。
SPSR寄存器用来备份当前的程序状态寄存器,当程序触发异常中断时,可将CPSR的值存放到SPSR。异常处理程序执行结束返回时,再将SPSR中存放地的当前程序状态值恢复至CPSR。CPSR和SPSR格式是相同,具体如下图所示。
基础指令
MNEMONIC {S} {condition} {Rd}, Operand1, Operand2
-
比较两个值大小,C代码如下:
-
if (a > b)
{
a++;
}else{
b++;
}
CMP R0, R1 ;R0与R1比较
ADDHI R0,R0,#1 ;若R0>R1,则R0=R0+1
ADDLS R1,R1,#1 ;若R0<=R1,则R1=R1+1
if ((a != 10) && (b != 20))
{
a = a + b;
}
CMP R0,#10 ;比较R0是否为10
CMPNE R1,#20 ;若R0不为10,则比较R1是否为20
ADDNE R0,R0,R1 ;若R0不为10且R1不为20,则执行 R0 = R0+R1
if ((a != 10) || (b !=20 ))
{
a = a + b;
}
CMP R0,#10 ;比较R0是否为10
CMPEQ R1,#20 ;若R0值为10,则比较R1是否为20
ADDNE R0,R0,R1 ;若R0不为10或R1不为20,则执行 R0 = R0+R1
int main()
{
int max = 0;
int a = 2;
int b = 3;
if(a < b)
{
max = b;
}
else {
max = a;
}
return max;
}
main:
MOV R1, #2 ;设置初始变量a的值为2
MOV R2, #3 ;设置初始变量b的值为3
CMP R1, R2 ;比较a和b值看哪个更大
BLT lower ;因为a < b 跳转到lower程序段
MOV R0, R1 ;如果a > b,则将a的值存储到R0
B end ;跳转到程序末尾
lower:
MOV R0, R2 ;因为 a < b跳转到此处继续执行, 将b的值存储到R0
B end ;跳转到程序末尾
end:
BX LR ;程序执行结束,返回值由R0返回
int main()
{
int count = 0;
while(count < 10)
{
count++;
}
return count;
}
main:
MOV R0, #0 ;设置初始变量count
loop:
CMP R0, #10 ;比较 count==10
BEQ end ;如果 count==10,循环执行结束
ADD R0, R0, #1 ;否则将R0中的值递增1
B loop ;跳转到loop开始位置
end:
BX LR ;程序执行结束
函数调用
STMFD SP!, {FP, LR} ;将栈帧指针FP和LR压入栈中,保存当前函数执行环境
ADD R11, SP, #0 ;设置栈帧的起始位置
SUB SP, SP, #16 ;在栈中为程序中的变量分配存储空间
MOV R3, #5 ;设置sum函数的第5个参数暂存入到R3寄存器
STR R3, [SP] ;将sum的第5个参数值存储到栈空间
MOV R0, #1 ;将sum函数的第1个参数,存入R0寄存器
MOV R1, #2 ;将sum函数的第2个参数,存入R1寄存器
MOV R2, #3 ;将sum函数的第3个参数,存入R2寄存器
MOV R3, #4 ;将sum函数的第4个参数,存入R3寄存器
BL SUM ;跳转到sum函数执行
MOV R0, R0 ;获取sum函数的返回值
MOV R0, R3 ;设置调用函数的返回值
SUB SP, FP, #4 ; 恢复原来的栈指针
LDMFD SP!, {FP, PC} ;将栈帧指针和LR出栈,用于恢复现场
SUM:
STR FP, [SP, #-4]! ;设置SUM函数空间中栈底指针
ADD FP, SP, #0 ;设置栈帧指针
SUB SP, SP, #20 ;为传入的参数在栈上分配存储空间
STR R0, [FP, #-8] ;将传入的参数1存储到栈上分配的空间
STR R1, [FP, #-12] ;将传入的参数2存储到栈上分配的空间
STR R2, [FP, #-16] ;将传入的参数3存储到栈上分配的空间
STR R3, [FP, #-20] ;将传入的参数4存储到栈上分配的空间
LDR R2, [FP, #-8] ;将传入的参数1存储到寄存器R2
LDR R3, [FP, #-12] ;将传入的参数2存储到寄存器R3
ADD R2, R2, R3 ;将参数1和参数2相加,结果存储到寄存器R2
LDR R3, [FP, #-16] ;将传入的参数3存储到寄存器R3
ADD R2, R2, R3 ;将参数3和和之前的结果进行累加
LDR R3, [FP, #-20] ;将传入的参数4存储到寄存器R3
ADD R2, R2, R3 ;将参数4和和之前的结果进行累加
LDR R3, [FP, #4] ;将传入的参数5存储到寄存器R3
ADD R3, R2, R3 ;将参数5和和之前的结果进行累加
MOV R0, R3 ;将执行结果作为函数sum的返回值返回
SUB SP, FP, #0 ;调整栈指针地址
LDR FP, [SP], #4 ;恢复原来的栈指针
BX LR ;函数执行完跳转回main函数
MAIN:
STMFD SP!, {FP, LR} ;将栈帧指针和LR压入栈中,用于现场保护
ADD FP, SP, #4 ;设置栈底指针
SUB SP, SP, #8 ;在栈中为程序中的变量分配存储空间
MOV R3, #5 ;设置SUM函数的第5个参数暂存入到R3寄存器
STR R3, [SP] ;将SUM的第5个参数值存储到栈空间
MOV R0, #1 ;将SUM函数的第1个参数,存入R0寄存器
MOV R1, #2 ;将SUM函数的第2个参数,存入R1寄存器
MOV R2, #3 ;将SUM函数的第3个参数,存入R2寄存器
MOV R3, #4 ;将SUM函数的第4个参数,存入R3寄存器
BL SUM ;跳转到SUM函数执行
MOV R0, R0 ;获取SUM函数的返回值
MOV R0, R3 ;设置MAIN函数的返回值
SUB SP, FP, #4 ;恢复原来的栈指针
LDMFD SP!, {FP, PC} ;将栈帧指针和LR出栈,用于恢复现场
int sum(int arg1, int arg2, int arg3, int arg4, int arg5)
{
return arg1+arg2+arg3+arg4+arg5;
}
int main()
{
sum(1,2,3,4,5);
return;
}
ARM64位汇编
int sum(int arg1, int arg2)
{
return arg1+arg2;
}
int main()
{
return sum(1,2);
}
SUM:
SUB SP, SP, #16
STR W0, [SP,12]
STR W1, [SP,8]
LDR W1, [SP,12]
LDR W0, [SP,8]
ADD W0, W1, W0
ADD SP, SP, 16
RET
MAIN:
STP X29, X30, [SP, -16]!
ADD X29, SP, 0
MOV W0, 1
MOV W1, 2
BL SUM
LDP X29, X30, [SP], 16
RET
看雪ID:FIGHTING安
https://bbs.kanxue.com/user-home-452101.htm
# 往期推荐
3、安卓加固脱壳分享
球分享
球点赞
球在看
原文始发于微信公众号(看雪学苑):移动应用安全与风控——汇编基础