您现在的位置是:网站首页>文章详情文章详情
安卓虚拟机及基础逆向的核心
inlike2019-08-06【 原创文章 】 浏览(1497) 评论(0) 喜欢(28)
简介言归正传,这篇主要讲apk的运行机制,包括虚拟机、smali语法;dex文件是虚拟机的可执行文件,dex文件的反编译后得到的就是smali文件,smali文件已经可以看出java程序的逻辑,但smali语法又有些许不同。 虚拟机
又来了,这是安卓逆向的第二篇,上一篇是《安卓逆向概述》!最近更新频率比较低,一方面是在学一些东西;另一方面是发现躺着耍手机,颈椎不痛了、腿也不酸了,整个人都舒服了。
言归正传,这篇主要讲apk的运行机制,包括虚拟机、smali语法;dex文件是虚拟机的可执行文件,dex文件的反编译后得到的就是smali文件,smali文件已经可以看出java程序的逻辑,但smali语法又有些许不同。
虚拟机
运行java的虚拟机有三种:java虚拟机、Dalvik虚拟机、Art虚拟机;java虚拟机是执行java字节码,基于栈架构;Dalvik虚拟机使用jit机制,在Android5.0以下使用,使用的Dalvik字节码体积更小,基于寄存器的架构;Art虚拟机基于Aot机制,在Android5.0及其以上版本中使用。
那么他们是怎么兼容的呢?java虚拟机提供了统一的接口,用来兼容Dalvik虚拟机和Art虚拟机:
java虚拟机和Dalvik虚拟机、Art虚拟机内部提供了JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM、JNI_GetCreatedJavaVMs分别来获取虚拟机默认初始化参数、在进程中创建虚拟机实例、获取进程中的虚拟机实例;java虚拟机通过参数Persist.sys.dalvik.vm.lib来控制兼容那个虚拟机,当其值为Libadvm.so时兼容Dalvik虚拟机,为libart.so时兼容Art虚拟机。
smali语法
1.smali变量类型与java类型对照表
B—byte C—char D—double F—float I—int S—short V—void J—long Z—boolean [-数组 L-对象
2.运算符
Smali | 含义 | 符号 |
---|---|---|
eq | equals | == |
ne | not equals | != |
lt | less than | < |
gt | bigger than | > |
le | less and equals | <= |
ge | bigger and equals | >= |
eqz | equals zero | =0 |
nez | not equals zero | !=0 |
ltz | less than zero | <0 |
btz | bigger than zero | >0 |
gez | bigger and equals zero | >=0 |
lez | less and equals zero | <=0 |
3.类描述信息
.class <访问权限> [修饰关键字]<类名> 如:.class public Lcom/zk/demo/MainActivity; L是修饰关键字代表类
.super<父类名>
.source<原文件名> 对应源码所在文件名
4.静态字段描述
# .static fields
.field <访问权限> <静态> <不可更改> <变量名> : <变量类型> = <赋值>如:
# static fields .field private static final LOGIN_TYPE:J=1
5.实例字段
# instance fields
.field <访问权限> [修饰关键字] <字段名>:<字段类型>如:
# instance fields .field private isPayTest:Z
6.直接方法
# direct methods .method <访问权限> [修饰关键字] <方法原型> <.locals> # 指定使用局部变量个数 [.parmeter] # 指定方法的参数 [.prologue] # 代码开始的标记 [.line] # 指定在原文件中的行号 <code> # 逻辑实现处 .end method # 声明方法结束
如:
# direct methods .method static constructor <clinit>()V .locals 2 .prologue .line 16 invoke-static {}, Ljava/lang/System;->currentTimeMillis()J move-result-wide v0 sput-wide v0, Lcom/example/tex/MainActivity;->LOGIN_TYPE:J return-void .end method
直接方法是当前类所有的,不可被覆写的方法 一般是 私有/静态方法、及静态构造方法
7.虚方法
虚方法同直接方法类似,但是注解为:virtual methods
虚方法由当前类实现,但是可以被 子类覆写的方法,多为 public/protect/inteface 的方法
8.接口声明
# interfaces
.implements <接口名>
# interfaces .implements Ljava/lang/Runnable;
接口声明一般在文件顶部
.class public Lcom/disney/WMW/WMWActivity; .super Lcom/disney/common/BaseActivity; .source "WMWActivity.java" # 1-3行定义的是基本信息:这是一个由WMWActivity.java编译得到的smali文件(第3行) # 它是com.disney.WMW这个package下的一个类(第1行),继承自com.disney.common.BaseActivity(第2行)。 # interfaces .implements Lcom/burstly/lib/ui/IBurstlyAdListener; # 5-6行定义的是接口信息:这个WMWActivity实现了一个com.burstly.lib.ui这个package下的IBurstyAdListener接口。 # annotations .annotation system Ldalvik/annotation/MemberClasses; value = { Lcom/disney/WMW/WMWActivity$MessageHandler;, Lcom/disney/WMW/WMWActivity$FinishActivityArgs; } .end annotation # 8-14行定义的则是内部类:它有两个成员内部类——MessageHandler和FinishActivityArgs,内部类将在后面小节中会有提及。
9.注解
# annotations
.annotation [注解属性] <注解类名>
[注解字段等于值]
.end annotation
案例如上面案例最后几行:
# annotations .annotation system Ldalvik/annotation/MemberClasses; value = { Lcom/disney/WMW/WMWActivity$MessageHandler;, Lcom/disney/WMW/WMWActivity$FinishActivityArgs; } .end annotation # 8-14行定义的则是内部类:它有两个成员内部类——MessageHandler和FinishActivityArgs。
综合8、9点,我们可以分析出这段案例的java源码为:
class WMWActivity extends BaseActivity implements IBurstlyAdListener{ //... class MessageHandler { //... } class FinishActivityArgs{ //... } }
10.字段赋值与获取
获取:sget-object 变量名,对象类型 -> out: : 获取对象类型
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; //获取参数值 变量名,对象类型 -> 获取方法 : 获取对象类型 iput-boolean v0, p0, Lcom/example/zhaokai/demo/Test;->isTest:Z //给参数赋值sget/sput: static 对已标识的静态字段执行已确定的对象静态字段运算 iget/iput: instance 对已标识的字段执行已确定的对象实例字段运算 aget/aput: array 在给定数组的已标识索引处执行已确定的数组运算 sget-类型 [wide/boolean/object/byte/char/short]
11.方法调用
//返回值 move-result-object v3 //调用指定的方法。所得结果(如果有的话)可能与紧跟其后的相应 move-result* 变体指令一起存储。 invoke-virtual {v0, v1}, Ljava/io/PrintStream;->print(Ljava/lang/String;)V //使用 invoke-virtual 调用正常的虚方法(该方法不是 private、static 或 final,也不是构造函数) invoke-direct {v0, v1}, Lcom/example/zhaokai/demo/Test;->print(Ljava/lang/String;)Ljava/lang/String; //invoke-direct 用于调用非 static 直接方法(也就是说,本质上不可覆盖的实例方法, // 即 private 实例方法或构造函数) invoke-static {v2}, Lcom/example/zhaokai/demo/Test;->setVERSION(Ljava/lang/String;)V //invoke-static 用于调用 static 方法(该方法始终被视为直接方法)。 invoke-interface {v0}, Ljava/lang/Runnable;->run()V //invoke-interface 用于调用 interface 方法,也就是说,在具体类未知的对象上,使用引用 interface 的 method_id。 invoke-super {p0}, Ljava/lang/Object;->toString()Ljava/lang/String; move-result-object v0 //invoke-super 来调用在该接口上定义的该方法的最具体、未被覆盖版
案例:
smali中的函数和成员变量一样也分为两种类型,但是不同成员变量中的static和instance之分,而是direct和virtual之分。那么direct method和virtual method有什么区别呢?直白地讲,direct method就是private函数,其余的public和protected函数都属于virtual method。所以在调用函数时,有invoke-direct,invoke-virtual,另外还有invoke-static、invoke-super以及invoke-interface等几种不同的指令。当然其实还有invoke-XXX/range 指令的,这是参数多于4个的时候调用的指令,比较少见,了解下即可。 (1)、invoke-static:顾名思义就是调用static函数的,因为是static函数,所以比起其他调用少一个参数,例如: invoke-static {}, Lcom/disney/WMW/UnlockHelper;->unlockCrankypack()Z 这里注意到invoke-static后面有一对大括号“{}”,其实是调用该方法的实例+参数列表,由于这个方法既不需参数也是static的,所以{}内为空,再看一个例子:copy const-string v0, "fmodex" invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V 这个是调用static void System.loadLibrary(String)来加载NDK编译的so库用的方法,同样也是这里v0就是参数"fmodex"了。 (2)、invoke-super:调用父类方法用的指令,在onCreate、onDestroy等方法都能看到,略。 (3)、invoke-direct:调用private函数的,例如:opy invoke-direct {p0}, Lcom/disney/WMW/WMWActivity;->getGlobalIapHandler()Lcom/disney/config/GlobalPurchaseHandler; 这里GlobalPurchaseHandler getGlobalIapHandler()就是定义在WMWActivity中的一个private函数,如果修改smali时错用invoke-virtual或invoke-static将在回编译后程序运行时引发一个常见的VerifyError(更多错误汇总可参照APK反编译之番外三:常见错误汇总)。 (4)、invoke-virtual:用于调用protected或public函数,同样注意修改smali时不要错用invoke-direct或invoke-static,例子:opy sget-object v0, Lcom/disney/WMW/WMWActivity;->shareHandler:Landroid/os/Handler; invoke-virtual {v0, v3}, Landroid/os/Handler;->removeCallbacksAndMessages(Ljava/lang/Object;)V 这里相信大家都已经明白了,主要搞清楚v0是shareHandler:Landroid/os/Handler,v3是传递给removeCallbackAndMessage方法的Ljava/lang/Object参数就可以了。 (5)、invoke-xxxxx/range:当方法的参数多于5个时(含5个),不能直接使用以上的指令,而是在后面加上“/range”,使用方法也有所不同:copy invoke-static/range {v0 .. v5}, Lcn/game189/sms/SMS;->checkFee(Ljava/lang/String;Landroid/app/Activity; Lcn/game189/sms/SMSListener; Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z 这个是电信SDK中的付费接口,需要传递6个参数,这时候大括号内的参数需要用省略形式,且需要连续(未求证是否需要从v0开始)。 有人也许注意到,刚才看到的例子都是“调用函数”这个操作而已,貌似没有取函数返回的结果的操作? 在Java代码中调用函数和返回函数结果是一条语句完成的,而在smali里则需要分开来完成,在使用上述指令后,如果调用的函数返回非void,那么还需要用到move-result(返回基本数据类型)和move-result-object(返回对象)指令: const/4 v2, 0x0 invoke-virtual {p0, v2}, Lcom/disney/WMW/WMWActivity;->getPreferences(I)Landroid/content/SharedPreferences; move-result-object v1 v1保存的就是调用getPreferences(int)方法返回的SharedPreferences实例。view plaincopy invoke-virtual {v2}, Ljava/lang/String;->length()I move-result v2 v2保存的则是调用String.length()返回的整型。
12.smali中逻辑语句
逻辑语句含义 if-eq vx,vy,目标 如果vx==vy,跳转到目标 if-ne vx,vy,目标 如果vx!=vy,跳转到目标 if-lt vx,vy,目标 如果vx < vy,跳转到目标 if-ge vx,vy,目标 如果vx>=vy,跳转到目标 if-gt vx,vy,目标 如果vx > vy,跳转到目标 if-le vx,vy,目标 如果vx<=vy,跳转到目标 if-eqz vx,目标 如果vx==0,跳转到目标 if-nez vx,目标 如果vx!=0,跳转到目标 if-ltz vx,目标 如果vx < 0,跳转到目标 if-gez vx,目标 如果vx>=0,跳转到目标 if-gtz vx,目标 如果vx > 0,跳转到目标 if-lez vx,目标 如果vx<=0,跳转到目标 if-eqz vx,vy,目标 如果vx==vy,跳转到目标 movevx,vy 将vx的值赋给vy move-result vx 将上一次调用方法的返回值赋给vx return-void 返回空值 const/4 vx,值 存入4位常量到vx new-instance vx,类型ID 创建一个类型ID的对象 aget vx,vy,vz 从int数组vy中获取索引为vz的值存到vx aget-wide vx,vy,vz 从long/double数组vy中获取索引为vz的值存到vx aget-boolean vx,vy,vz 从boolean数组vy中获取索引为vz的值存到vx aget-object vx,vy,vz 从对象数组vy中获取索引为vz的值存到vx aput vx,vy,vz 将vx的值存入int数组vy的vz索引处 aput-object vx,vy,vz 将vx的值存入对象数组vy的vz索引处 iget vx,vy,x 将引用vy获取int类型字段x的值赋给vx iget-object vx,vy,x 将引用vy获取字段x的值赋给vx iput vx,vy, x 通过引用vy将vx的值赋给int类型的字段x iput-object vx,vy,x 通过引用vy将vx的值赋给对象x sget vx,字段ID 获取某个类的int类型的静态字段的值赋给vx sget-object vx,字段ID 获取某个类的静态对象赋给vx int-to-float vx,vy 将int类型的vy的值强转成float类型存入vx float-to-int vx,vy 将float类型的vy的值强转成int类型存入vx add-int vx,vy,vz vx = vy + vz (vx,vy,vz都为int类型) sub-int vx,vy,vz vx = vy - vz mul-int vx,vy,vz vx = vy * vz p-int vx,vy,vz vx = vy / vz rem-int vx,vy,vz vx = vy % vz and-int vx,vy,vz vx = vy & vz or-int vx,vy,vz vx = vy add-int/2addr vx,vy vx = vx + vy sub-int/2addr vx,vy vx = vx - vy mul-int/2addr vx,vy vx = vx * vy p-int/2addr vx,vy vx = vx / vy neg-float vx, vy vx = -vy (将vy值变成负值给vx) monitor-enter vx 为指定的对象vx获取锁 monitor-exit vx 释放指定的对象vx的锁
完整smali源码案例:
<code>.class public Lcom/example/tex/MainActivity;//类的全名 .super Landroid/app/Activity;//此类的父类 .source "MainActivity.java"//文件名 # annotations//内部类ProtocolState声明 .annotation system Ldalvik/annotation/MemberClasses; value = { Lcom/example/tex/MainActivity$ProtocolState; } .end annotation # static fields .field private static final LOGIN_TYPE:J //long类型的静态变量 # instance fields .field private bb:I//int类型的成员变量bb .field d:J//long类型的成员变量d .method static constructor <clinit>()V .locals 2 //表示此方法里用了2个寄存器,寄存器是用来存局部变量的值 .prologue //表示方法开始 .line 16 //行数 invoke-static {}, Ljava/lang/System;->currentTimeMillis()J //获取系统当前时间 move-result-wide v0 //将系统当前时间存入v0 sput-wide v0, Lcom/example/tex/MainActivity;->LOGIN_TYPE:J //将v0赋值给静态变量LOGIN_TYPE return-void //空返回 .end method //方法结束标志 # direct methods .method public constructor <init>()V //构造函数 .locals 2//表示此方法里用了2个寄存器,寄存器是用来存局部变量的值 .prologue //表示方法开始 .line 12 //行数 invoke-direct {p0}, Landroid/app/Activity;-><init>()V//调用init()方法 .line 14 const/4 v0, 0x1 //定义一个局部变量v0 值为1(十六进制) iput v0, p0, Lcom/example/tex/MainActivity;->bb:I//将v0的值赋给成员变量bb .line 48 const-wide/32 v0, 0x87eccb //将0x87eccb存入32位常量 iput-wide v0, p0, Lcom/example/tex/MainActivity;->d:J//将v0的值赋给成员变量d .line 12 return-void //空返回 .end method //方法结束标志</init></init></clinit></code>
smali常用的语法基础就到此,Android的逆向比js逆向的难度大几个级别,涉及的东西也更多后面还有java语法、c、c++语法的涉及;smali文件作为java源码的翻译版,读懂smali文件离java逆向的核心就不远了,更高技术的混淆还原、脱壳、解密都是为了得到smali文件,从而读懂代码的逻辑。
本文部分代码及知识点参考下面文章:
https://www.jianshu.com/p/04c3c8566ee1
https://www.2cto.com/kf/201704/622597.html
https://blog.csdn.net/pinksofts/article/details/82791806
上一篇:python获取异常文件及其行号
相关文章
本栏推荐
标签云
猜你喜欢
站点信息
- 建站时间:2019-5-24
- 网站程序:like in love
- 主题模板:《今夕何夕》
- 文章统计:104条
- 文章评论:***条
- 微信公众号:扫描二维码,关注我们