抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

基于dalvik 字节码解释器。

dex文件中真正包含一条条的Smali指令流的结构是每一个Java函数的Codeltem部分(Dalvik时代叫DexCode),右图为该部分对应的结构体,其中指令流最前面的16个字节的长度是固定的,表示了当前函数需要的虚拟寄存器个数、参数个数以及指令条数、异常梳理、调试信息等。

DexCode 是 Android DEX 文件格式中用于存储方法代码的数据结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct DexCode {
u2 registersSize; // 使用的寄存器数量
u2 insSize; // 传入参数的个数
u2 outsSize; // 调用其他方法时使用的寄存器数量
u2 triesSize; // Try/Catch 代码块的个数
u4 debugInfoOff; // 调试信息的偏移量(指向 debug_info_item)
u4 insnsSize; // 指令集大小(以 2 字节为单位)
u2 insns[1]; // 指令集(Dalvik 字节码)

// 可选字段
u2 padding; // 可选字段,用于对齐
try_item tries[triesSize]; // Try/Catch 处理结构
encode_catch_handler_list_handlers; // 异常处理程序列表
};

Smali指令集
1、Dalvik中的寄存器为虚拟寄存器,而不是CPU上的真实寄存器
2、Smali指令集和汇编指令集一样,包括操作码和操作数两个部分
3、Smali指令集中的操作码部分为一个字节,因此可最大表示256种指令(并没有全部使用)

Dalvik 可执行指令格式 | Android Open Source Project

Dalvik opcodes

实现Smali指令集的取指、译码、执行的过程即为解释器。可以将这个解释器理解为一颗虚拟的CPU即可,自然就包含有寄存器、pc等
Dalvik时代:由c语言实现。ART的解释器继承至Dalvik的c时代,原理一致
ART时代:由C++实现

Smali指令集的解释器实现:只需要支持对一个字节的opcode,即总数最多为256个指令的每一条指令的解释执行即可。

[原创]Dalvik解释器源码到VMP分析-Android安全-看雪-安全社区|安全招聘|kanxue.com

switch类型解释器源码。

Cross Reference: /art/runtime/interpreter/interpreter_switch_impl.cc

INVOKE 代表 方法调用指令,用于执行 Java 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
switch (inst->Opcode(inst_data)) {
case Instruction::NOP:
PREAMBLE();
inst = inst->Next_1xx();
break;
case Instruction::MOVE:
PREAMBLE();
shadow_frame.SetVReg(inst->VRegA_12x(inst_data),shadow_frame.GetVReg(inst->VRegB_12x(inst_data)));
inst = inst->Next_1xx();
break;
case Instruction::INVOKE_VIRTUAL: {
PREAMBLE();
bool success = DoInvoke<kVirtual, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}
case Instruction::INVOKE_VIRTUAL_RANGE: {
PREAMBLE();
bool success = DoInvoke<kVirtual, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}

解释器的实现
为了解决VMP实现的兼容性和复杂性问题,VMP使用标准JNI调用java
函数的流程来实现对invoke类型指令的解释执行.
1.解释invoke指令的参数部分,得到Methodldx
2.解析dex结构,获得当前要调用的Methodldx的类名和函数属性
3.调用JNI的FindClass函数,获得对应类的JCIass
4.调用JNI的GetMethodID或GetStaticMethodID得到函数的Method
5.调用JNI的CalIXXXMethod,完成对函数的调用

Smali指令集
1、Dalvik中的寄存器为虚拟寄存器,而不是cPU上的真实寄存器
2、Smali指令集和汇编指令集一样,包括操作码和操作数两个部分
3、Smali指令集中的操作码部分为一个字节,因此可最大表示256指令(并没有全部使用)

ADVMP

chago/ADVMP: 大自然的搬运工-Android虚拟机保护Demo

AdvmpTest:测试用的项目。

base:Java项目。里面是一些工具类代码。

control-centre:Java项目。控制加固流程。

separator:Java项目。抽离方法指令,然后将抽离的指令按照自定义格式输出,并同时输出C文件。

template/jni:C代码。里面包含了解释器的代码。

ycformat:自定义的文件格式,用于保存抽取出来指令等数据。

2、定制通用反调试rom
优点:一劳永逸,可以通过定制内核、各种hook技术(syscall hook、so hook、javahook)、定制ART等来实现。缺点是需要对AndroidAPP生命周期以及ART源码十分了解

其他

四个重要概念

1
VM_DATA -> 虚拟字节码,即要被执行的虚拟指令VM_EIP  -> 指向VM_DATA的地址,虚拟机从这里获取指令VM_CONTEXT -> 虚拟机寄存器VM_STACK -> 虚拟栈

分析时一般需要分析以下

1
1.虚拟寄存器在哪里2.dispatcher在哪里,找到vm_eip3.解析每条handler

定制内核,体验数据流分析驱动控制流分析的威力
软件断点:软件断点是通过在代码中设置特殊指令的方式来实现的(如中断)。当需要在某地址代码处设置软件断点的时候,调试器会先将此处代码进行备份保护,然后将预先设定好的断点指令写入此地址,覆盖原来的代码数据。当程序运行到此地址处时,会暂停并由调试器接管,当取消断点时,之前受保护的代码信息会被自动恢复。软件断点数量可以不受限制。
硬件断点:硬件断点需要目标CPU的硬件支持,当前流行ARM手机CPU基本都支持,但厂商默认并没有开启内核支持;硬件断点可以设置在内存中的任何位置,因此也就支持了内存访问断点;

硬件断点的使用:
虽然当前主流的ARM核基本都支持硬件断点,但是需要内核开启硬件断点功能,因此,就需要重新编译内核并刷入手机。

手机的选择:由于第三方厂商的rom往往在kernel中并没有开启硬件断点支持,同时相关源码和工具链并不开放,因此不推荐;Google的亲儿子完全开源,如pixel系列,因此推荐使用Google亲儿子来研究,相关的AOSP源码以及内核源码很容易获取到。

评论