Android 逆向代码调试及smali文件结构

Smali语法详解

apk文件通过apktool反编译出来的都有一个smali文件夹,里面都是以.smali结尾的文件。
smali语言是Davlik的寄存器语言,语法上和汇编语言相似,Dalvik VM与JVM的最大的区别之一就是Dalvik VM是基于寄存器的。基于寄存器的意思是,在smali里的所有操作都必须经过寄存器来进行。

寄存器是什么

在smali所有的操作都必须经过寄存器来进行:本地寄存器用v开头数字结尾的符号来表示,如v0,v1,v2…,参数寄存器用p开头数字结尾表示,如:p0,p1,p2…。特别注意的是,p0不一定是函数中的第一个参数,在非static函数中,p0代指“this”,p1表示函数的第一个参数,p2表示第二个参数,而在static函数中p0才对应第一个参数(因为java的static方法中没有this方法)

  const/4 v0, 0x0
  iput-boolean v0,p0,Lcom/aa;->IsRegistered:Z
  上段表示的是:把0x0存寄到v0中,把这个值存放进com.aa.IsRegistered这个变量中,相当于this.IsRegistered=true,在非静态方法中p0代表的是this,不是参数。

基本类型定义

Java Type Type descriptor
boolean Z
char C
byte B
short S
int I
float F
long J
double D
Object Ljava/lang/Object;
int[] [I
Object[][] [[Ljava/lang/object;
内部类 Lpackagename/objectname&subobjectname

数组表示方式在 基本数据类型前加“[”符号,对象的则表示以L作为开头格式为Ljava/lang/Object; 分号在在后面。 Ljava/lang/String;其中java/lang对应java.lang包,String就是对应的该包中的对象。

方法定义:FuncName(para-type para-type1)ReturnType

  1. hello(III)I ->int hello(int,int,int)方法返回的是int型
  2. hello(J[I[Ljava/lang/String;ZLjava/lang/String) Z->blooean hello(long,string[],blooean,string) 方法返回的是blooean型

smali基本语法

  • .field private isflag:Z 定义变量为 blooean isflag
  • .method 方法
  • .parameter 方法参数
  • .prologue 方法开始 序幕
  • .line123 此方法位于123行
  • const/high16 v0,0x7fo3 把0x7fo3赋值给v0
  • invoke-direct 调用private函数
  • invoke-virtual 调用除private其它函数
  • invoke-static 调用静态函数
  • invoke-super 调用父函数
  • invoke-interface 调用接口
  • return-void 函数返回void
  • .end method 函数结束
  • new-instance 创建实例
  • iput-object 对象赋值
  • iget-object 调用对象

条件跳转分支

  • "if-eq vA,vB, :cond_ ** " 如果vA等于vB,则跳转到:cond_ **
  • "if-ne vA,vB, :cond_ ** " 如果vA不等于vB,则跳转到:cond_ **
  • "if-It vA,vB, :cond_ ** " 如果vA小于vB,则跳转到:cond_ **
  • "if-ge vA,vB, :cond_ ** " 如果vA大于等于vB,则跳转到:cond_ **
  • "if-gt vA,vB, :cond_ ** " 如果vA大于vB,则跳转到:cond_ **
  • "if-le vA,vB, :cond_ ** " 如果vA小于等于vB,则跳转到:cond_ **
  • "if-eqz vA, :cond_ ** " 如果vA等于0,则跳转到:cond_ **
  • "if-nez vA, :cond_ ** " 如果vA不等于0,则跳转到:cond_ **
  • "if-Itz vA, :cond_ ** " 如果vA小于0,则跳转到:cond_ **
  • "if-gez vA, :cond_ ** " 如果vA大于等于0,则跳转到:cond_ **
  • "if-gtz vA, :cond_ ** " 如果vA大于0,则跳转到:cond_ **
  • "if-lez vA, :cond_ ** " 如果vA小于等于0,则跳转到:cond_ **
if-eq p1, v0, :cond_8 
    :cond_8
    invoke-direct {p0}, Lcom/paul/test/a;->d()V
    上段表示如果p1和v0相等,则执行cond_8的流程:调用com.paul.test.a的d()方法
    
if-ne p1, v0, :cond_b 
    :cond_b
    const/4 v0, 0x0
    invoke-virtual {p0, v0}, Lcom/paul/test/a;->setPressed(Z)V
    invoke-super {p0, p1, p2}, Landroid/view/View;->onKeyUp(ILandroid/view/KeyEvent;)Z
    move-result v0
    上段表示p1不等于p0,跳转到:cond_b。把0x0赋值给v0,执行com/paul/test/a类中的void setPressed(blooean )公有方法。再调用View中的onkeyUp方法。返回一个基本数据类型。
    move-result(返回基本数据类型)和move-result-object(返回对象)指令获取返回结果
    

包信息:

  • .class public Lcom/test/reversetest/MainActivity;
  • .super Landroid/support/v7/app/AppCompatActivity;
  • .source “MainActivity.java”
    这是一个由MainActivity.java类编译得到smali文件,它是com/test/reversetest/MainActivity下的一个类同时该类继承了android/support/v7/app/AppCompatActivity

samli声明:

  • #annotations
  • .annotation system Ldalvik/annotation/MemberClasses;
    values={
    Lcom/aaa&qqq;,
    Lcom/aaa&www;
    }
    .end annotation

这个声明是内部类的声明:aaa 这个类有2个成员内部类qqq和www

成员变量格式:.field public /private[static][final]varName:<类型>。

  • 对于不同的成员变量也有不同的指令
  • 获取的指令有:iget,sget,iget-boolean,sget-boolean,iget-object,sget-object。
  • 操作的指令有:iget,sput,iput-boolean,sput-boolean,iput-object,sput-boolean,iput-object,sput-object等。
  • 没有 “-object” 表示成员变量对象是基本数据类型,带了就是对象类型。特别的是,boolean类型则带-boolean。
sget-object v0,Lcom/aaa;->ID:Ljava/lang/String;
sget-object用来获取变量值并保存到挨着的参数寄存器中,它获取的ID这个String类型的成员变量放到v0中。
注意:前面需要该变量所属的类的类型,后面需要加冒号和该成员变量的类型,中间是 "->"表示所属关系。

iget-object v0,p0,Lcom/aaa;->ID:Ljava/lang/String;
iget-object 比sget-object多了一个p0参数表示“this”,获取array我们用aget和aget-object和上述指令一致。

put 指令和get指令是统一的:
const/4 v3, 0x0
sput-object v3,Lcom/aaa;->timer:Lcom/aaa/timer;
相当于:this.timer=null
这是因为是object所以是null,,
sput-boolean v3,Lcom/aaa;->timer:Z;
相当于:this.timer=true

.local v0,args:Landroid/os/Message;
const/4 v1,0x12
iput v1,v0,Landroid/os/Message;->what:I
相当于 args.what=18;

函数的调用

smali 中的函数与变量分为2种类型,分别为direct和virtual。direct method就是private函数,其余的函数都是virtual method。

  1. invoke-static:调用static函数。例如:
invoke-static{},Lcom/aaa;->Check()Z,
这里invoke-static后有一对“{}”,其实调用的是该方法的实例+参数列表,由于这个方法既不需参数也是static的,所以{}为空。  

const-string v0,"NDK"
invoke-static{v0},Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
这个调用static void  System.loadLibrary(v0)来编译NDK的。

2.invoke-super:调用父类方法的指令,通常是onCreat,Ondestory。
3.invoke-direct:调用private函数。
4.invoke-virtual :调用非private函数。修改smali中不要错用direct/virtual函数。
5.invoke-xxx/range:当方法的参数多余5个(含5个),不能直接使用以上指令,而是在后面加上"/range",表示范围。 invoke-direct/range{v0…v5},Lcom.aaa;->h(IIIIIZ)Z

函数返回的结果调用

在java代码中,调用函数和返回函数结果可以用一条语句完成,而在Smali里则需要分开来完成,在上面指令后,如果调用的函数返回非void,那么还需要用move-result(返回基本数据类型)和move-result-object(返回对象)指令:

const-string v0,"eric"
invoke-static{v0},Lcom/aaa;->t(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2
v2 保存的是t方法中的返回的String字符串

分析一段代码

.method private ifRegister()Z
.locals 2 //有2个本地寄存器
.prologue //开始
const/4 v0, 0x1 //v0赋值为1
.locals v0,tempFlag:Z
if-eqz v0,:cond_0
const/4 v1,0x1
:goto_0 //标签
return v1  //返回v1的值
:cond_0 //标签
const/4 v1,0x0 
goto :goto_0  //跳到goto_0执行,返回v1的值,这里可以改成retutn v1
.end method
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章