安卓虚拟机_安卓虚拟机及基础逆向的核心

又来了,这是安卓逆向的第二篇,上一篇是《安卓逆向概述》!最近更新频率比较低,一方面是在学一些东西;另一方面是发现躺着耍手机,颈椎不痛了、腿也不酸了,整个人都舒服了。

dc6d034f14b67d789a141e2511667e70.png

言归正传,这篇主要讲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虚拟机:

93e96aa70bd5df0fc38082644f1f3751.png

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.运算符

272e56cea08e6ba57762884e14d7a45f.png

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文件,从而读懂代码的逻辑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QNX虚拟机(Neutrino 650)是一款基于QNX操作系统的虚拟机解决方案。QNX操作系统是一种实时操作系统,通常被用于嵌入式系统和实时应用场景。QNX虚拟机旨在提供一个安全、可靠的虚拟化环境,使用户能够在QNX操作系统上运行多个虚拟机实例。 QNX虚拟机(即Neutrino 650)使用虚拟化技术将物理资源分割成多个虚拟环境,每个虚拟机实例都具有独立的运行环境。这种隔离性使得每个虚拟机可以独立运行不同的应用程序,而不会相互干扰。 QNX虚拟机的灵活性和可扩展性也值得一提。它支持在虚拟机之间动态分配和调整资源,使系统能够根据需求进行灵活的资源管理。例如,可以根据应用程序的需要分配处理器、内存和网络带宽等资源,从而提供优化的性能和响应能力。 此外,QNX虚拟机还提供了一些高级功能,如安全隔离和资源管理。安全隔离确保每个虚拟机之间的应用程序和数据相互隔离,提高系统的安全性。资源管理功能使用户能够监控和控制系统资源的使用,从而保证每个虚拟机在运行时都能得到足够的资源。 总结来说,QNX虚拟机(Neutrino 650)是一款基于QNX操作系统的虚拟机解决方案,提供安全、可靠的虚拟化环境,可以在该操作系统上并行运行多个虚拟机实例。它具有灵活性、可扩展性以及安全隔离和资源管理等高级功能,适用于嵌入式系统和实时应用场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值