安卓 dex 通用脱壳技术研究
来源:互联网 发布:张伯伦场均数据 编辑:程序博客网 时间:2024/05/17 09:35
0x01 背景介绍
安卓 APP 的保护一般分为下列几个方面:
JAVA/C代码混淆
dex文件加壳
.so文件加壳
反动态调试技术
其中混淆和加壳是为了防止对应用的静态分析;代码混淆会增加攻击者的时间成本, 但并不能从根本上解决应用被逆向的问题;而加壳技术一旦被破解,其优势更是荡然无存;反调试用来对抗对APP的动态分析;
昨天看雪zyqqyz同学发了一个Android应用程序通用自动脱壳方法:DexHunter,详见Github;通过定制Dalvik虚拟机实现,并对目前国内6款主流加固产品的进行了测试,效果良好;下面我们会讲解Dalvik解释器实现原理,并分析DexHunter代码实现;
目前来看,要对抗这种脱壳方法,最好的办法应该是APP启动时hook被修改的几处函数,并还原之;对开发者来说,应当将涉及资产、创新的关键代码放入Native层实现,并通过.so加壳和反调试进行保护;
上次与梆梆的交流,他们提到梆梆3.0正在研发类似PC上的VMP保护壳技术,实现APK保护;但个人认为要在兼容性方面做的工作实在太多,短时间内估计很难实现了;
下面开始,分3个方面介绍通过定制Dalvik的通用脱壳方案:1.Dalvik 解释器原理分析,2.DexHunter代码分析,3.测试
0x02 Dalvik 解释器原理分析
解释器是Dalvik虚拟机的执行引擎,它负责解释执行Dalvik字节码。在字节码加载完毕后,Dalvik虚拟机调用解释器开始取指解释字节码,解释器跳转到解释程序处执行。目前安卓解释器有两种,Portable和Fast解释器,分别使用C和汇编实现;优势分别是兼容性和性能,具体使用哪个可以自己来指定,因此本着简单的原则,我们分析并使用Portable解释器;
获取字节码并分析与解释执行是Dalvik虚拟机解释器的主要工作。Dalvik虚拟机的入口函数是vm/interp下的dvmInterpret函数;外部通过调用dvmInterpret函数进入解释器执行,其流程为dvmCallMethod->dvmCallMethodV->dvmInterpret。
在外部函数调用解释器以后,解释器执行的主要流程有以下几个步骤。
初始化解释器执行环境
根据系统参数,选择使用Portable或Fast解释器
跳转到相应解释器执行
取指及指令检查
执行字节码对应程序段
dvmInterpret函数作为解释器的入口函数,主要完成整个流程的前三部分,执行流程如下:
由于前三部分与我们定制Dalvik无关,这里不做详细介绍;
根据解释器的功能,可以想像的到,最简单的模型就是一个大的switch语句,对每条指令进行判断,然后case到相应的代码进行解释,解释完成后又回到switch顶部,如下:
while
(insn) {
switch
(insn) {
case
NOP:
break
;
case
MOV:
do
something;
break
;
...
case
OP:
do
something;
break
;
default
:
break
;
}
取指;
}
然而当解释完成一条指令后,再重新判断指令类型是个昂贵的开销。因为对于每条指令,都将从switch顶部开始判断,也就是从NOP指令开始判断,直到找到相应的指令为止,这使得解释器的执行效率十分低下。
这类问题的解决方法就是空间换时间,Dalvik就采用了这个思路;它为每条指令分配一个对应的标签(Label),标签标示的是该指令解释程序的开始,每条指令的解释程序末尾,有取指动作,可以取下一条要执行指令;Dalvik具体使用GCC的Threaded Code技术来实现,它使用了一个静态的标签数组,用来存储各个字节码解释程序对应的标签地址,其具体以一个宏来定义:
dalvik/libdex/DexOpcodes.h
/*
* Macro used to generate a computed goto table for use in implementing
* an interpreter in C.
*/
#define DEFINE_GOTO_TABLE(_name) \
static
const
void
* _name[kNumPackedOpcodes] = { \
/* BEGIN(libdex-goto-table); GENERATED AUTOMATICALLY BY opcode-gen */
\
H(OP_NOP), \
H(OP_MOVE), \
H(OP_MOVE_FROM16), \
H(OP_MOVE_16), \
H(OP_MOVE_WIDE), \
...
}
下面看下H宏实现:
#define H(_op) &&op_##_op
那如何根据指令得到相应的Label地址呢?Dalvik中使用了索引号:
enum
Opcode {
// BEGIN(libdex-opcode-enum); GENERATED AUTOMATICALLY BY opcode-gen
OP_NOP = 0x00,
OP_MOVE = 0x01,
OP_MOVE_FROM16 = 0x02,
OP_MOVE_16 = 0x03,
OP_MOVE_WIDE = 0x04,
OP_MOVE_WIDE_FROM16 = 0x05,
OP_MOVE_WIDE_16 = 0x06,
OP_MOVE_OBJECT = 0x07,
....
}
因此整个执行流程就是:取指令->取索引号->取Label得到解释程序地址->执行指令,并取下一条指令。
上面分析了解释器的基本模型,下面看Dalvik Portable的执行流程。其解析流程如图:
首先进行相关变量的声明,保存当前正在解释的方法curMethod、程序计数器pc、栈桢指针fp、当前指令inst、指令译码的相关部分包括保存寄存器值vsrc1,vsrc2,vdst、设置方法调用指针methodToCall等。
通过DEFINE_GOTO_TABLE(handlerTable)宏进行GOTO Label的绑定,获取并拷贝self->interpSave里保存的当前状态,包括方法method、程序计数器pc、堆栈帧curFrame、返回值retval、要分析的Dex文件的类对象信息curMethod->clazz->pDvmDex等已声明的变量。其代码如下:
dalvik/vm/mterp/out/InterpC-portable.cpp
/* copy state in */
curMethod = self->interpSave.method;
pc = self->interpSave.pc;
fp = self->interpSave.curFrame;
retval = self->interpSave.retval;
/* only need for kInterpEntryReturn? */
methodClassDex = curMethod->clazz->pDvmDex;
最后通过FINISH(0)来取得第一条指令开始执行字节码解析。
在Dalvik Portable中,解释程序是由一系列宏控制,以对应的Label来表示,以NOP操作为例,其定义如下:
dalvik/vm/mterp/out/InterpC-portable.cpp
/*--- start of opcodes ---*/
/* File: c/OP_NOP.cpp */
HANDLE_OPCODE(OP_NOP)
FINISH(1);
OP_END
/* File: c/OP_MOVE.cpp */
HANDLE_OPCODE(OP_MOVE
/*vA, vB*/
)
vdst = INST_A(inst);
vsrc1 = INST_B(inst);
ILOGV(
"|move%s v%d,v%d %s(v%d=0x%08x)"
,
(INST_INST(inst) == OP_MOVE) ?
""
:
"-object"
, vdst, vsrc1,
kSpacing, vdst, GET_REGISTER(vsrc1));
SET_REGISTER(vdst, GET_REGISTER(vsrc1));
FINISH(1);
OP_END
/* File: c/OP_MOVE_FROM16.cpp */
HANDLE_OPCODE(OP_MOVE_FROM16
/*vAA, vBBBB*/
)
vdst = INST_AA(inst);
vsrc1 = FETCH(1);
ILOGV(
"|move%s/from16 v%d,v%d %s(v%d=0x%08x)"
,
(INST_INST(inst) == OP_MOVE_FROM16) ?
""
:
"-object"
, vdst, vsrc1,
kSpacing, vdst, GET_REGISTER(vsrc1));
SET_REGISTER(vdst, GET_REGISTER(vsrc1));
FINISH(2);
OP_END
HANDLE_OPCODE(OP_NOP)表示对应的是OP_NOP操作,紧接其后的是解释程序的具体实现。到OP_END结束。而在Portable中,所以有解释程序都由C语言编写。NOP操作中的HANDLE_OPCODE、FINISH和OP_END都是宏定义。其中HANDLE_OPCODE和OP_END是成对出现的,OP_END什么也不做:
#define OP_END
所以
HANDLE_OPCODE(OP_NOP)
FINISH(1);
OP_END
可以翻译为:
op_OP_NOP:
FINISH(1);
对于NOP指令,其完成的工作就是什么也不做。因此,对应的解释程序就是直接取下一条将要执行的指令,也就是FINISH(1)所完成的工作。在FINISH()宏里,虚拟机获取下一条指令,并从指令中提取操作码号,根据该操作码号到指令解释程序查找表中得到相应的标签,然后跳转到该处理程序执行。其定义如下:
# define FINISH(_offset) { \
ADJUST_PC(_offset); \
inst = FETCH(0); \
if
(self->interpBreak.ctl.subMode) { \
dvmCheckBefore(pc, fp, self); \
} \
goto
*handlerTable[INST_INST(inst)]; \
}
0x03 DexHunter代码分析
DexHunter 实现中,只需要修改一处文件:dalvik\vm\native\dalvik_system_DexFile.cpp
下面是BeyondCompare比对:
首先看一下DexHunter的设计原理:
APP 启动时,通过freature string定位dex在内存中位置,并读取classdef块之前的内存为part1,读取classdef之后的内存为data。遍历class_def_item结构,生成文件classdef,并通过code_item_off判断具体的类方法是否在dex范围内,若不在,则写extra文件。
描述几个问题:
从哪里dump出dex文件
dex文件打开时
类加载时
类初始化时
类方法调用时
DexHunter中,我们关注,ClassLoader.loadClass->Dalvik_dalvik_system_DexFile_defineClassNative这个函数,它实现了类的加载,实现过程如下:
选择脱壳的时机应是在APP的第一个类加载的时候,为什么呢?
类加载之前,类的内容是在内存当中的
当类初始化时,该内存的内容可能会被动态修改
在一个类方法被调用前,code_item或指令肯定是可用的
那如何做呢?
我们要主动加载并初始化所有的类;
因此,我们代码的注入点,应该是Dalvik_dalvik_system_DexFile_defineClassNative()函数的clazz = dvmDefineClass(pDvmDex, descriptor, loader);语句之前;即在APP加载第一个类之前完成;通过dvmDefineClass主动遍历class_def_item加载每个类,并调用dvmIsClassInitialized和dvmInitClass函数初始化之。
初始化完成之后,内存中的就是将执行的代码,像梆梆加固针对每个方法进行的加密,会在运行时解密、运行完成后清理内存并再次加密,通过这种方法就可以过掉;因为我们模拟了这样一次调用过程;
下面是我加入注释的代码:
//------------------------added begin----------------------//
#include <asm/siginfo.h>
#include "libdex/DexClass.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
static
char
dexname[100]={0};
//feature string
static
char
dumppath[100]={0};
//dump的文件路径
static
bool
readable=
true
;
static
pthread_mutex_t read_mutex;
static
bool
flag=
true
;
static
pthread_mutex_t mutex;
static
bool
timer_flag=
true
;
static
timer_t timerId;
struct
arg{
DvmDex* pDvmDex;
Object * loader;
}param;
void
timer_thread(sigval_t)
{
timer_flag=
false
;
timer_delete(timerId);
ALOGI(
"GOT IT time up"
);
}
void
* ReadThread(
void
*arg){
FILE
*fp = NULL;
while
(dexname[0]==0||dumppath[0]==0) {
fp=
fopen
(
"/data/dexname"
,
"r"
);
if
(fp==NULL) {
sleep(1);
continue
;
}
fgets
(dexname,99,fp);
//读feature string
dexname[
strlen
(dexname)-1]=0;
fgets
(dumppath,99,fp);
dumppath[
strlen
(dumppath)-1]=0;
//取dump路径
fclose
(fp);
fp=NULL;
}
struct
sigevent sev;
sev.sigev_notify=SIGEV_THREAD;
sev.sigev_value.sival_ptr=&timerId;
sev.sigev_notify_function=timer_thread;
sev.sigev_notify_attributes = NULL;
timer_create(CLOCK_REALTIME,&sev,&timerId);
struct
itimerspec ts;
ts.it_value.tv_sec=5;
ts.it_value.tv_nsec=0;
ts.it_interval.tv_sec=0;
ts.it_interval.tv_nsec=0;
timer_settime(timerId,0,&ts,NULL);
return
NULL;
}
/*
这里是class_data_item的前4项,称为ClassDataHeader
Dex File->class_defs->class_def_item(class_data_offset)->class_data_item->ClassDataHeader
*/
void
ReadClassDataHeader(
const
uint8_t** pData, DexClassDataHeader *pHeader)
{
pHeader->staticFieldsSize = readUnsignedLeb128(pData);
pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
pHeader->directMethodsSize = readUnsignedLeb128(pData);
pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
}
/*
下面两个函数,分别读class_data_item Header下的内容,分Field和Method
*/
void
ReadClassDataField(
const
uint8_t** pData, DexField* pField)
{
pField->fieldIdx = readUnsignedLeb128(pData);
pField->accessFlags = readUnsignedLeb128(pData);
}
void
ReadClassDataMethod(
const
uint8_t** pData, DexMethod* pMethod)
{
pMethod->methodIdx = readUnsignedLeb128(pData);
pMethod->accessFlags = readUnsignedLeb128(pData);
pMethod->codeOff = readUnsignedLeb128(pData);
}
/*
解析class_data_item结构,使用到上面3个函数,分别解析,Header、Field和Method部分
*/
DexClassData* ReadClassData(
const
uint8_t** pData)
{
DexClassDataHeader header;
if
(*pData == NULL) {
return
NULL;
}
//读取 class_data_item的Header
ReadClassDataHeader(pData, &header);
size_t
resultSize =
sizeof
(DexClassData) + (header.staticFieldsSize *
sizeof
(DexField)) + (header.instanceFieldsSize *
sizeof
(DexField)) + (header.directMethodsSize *
sizeof
(DexMethod)) + (header.virtualMethodsSize *
sizeof
(DexMethod));
DexClassData* result = (DexClassData*)
malloc
(resultSize);
//result指向class_data_item并返回
if
(result == NULL) {
return
NULL;
}
uint8_t* ptr = ((uint8_t*) result) +
sizeof
(DexClassData);
//指向class_data_item的staic_fields偏移
result->header = header;
//以下依次读class_data_item的staticFields,instanceFields,directMethods和virtualMethods域大小————————begain
if
(header.staticFieldsSize != 0) {
result->staticFields = (DexField*) ptr;
ptr += header.staticFieldsSize *
sizeof
(DexField);
}
else
{
result->staticFields = NULL;
}
if
(header.instanceFieldsSize != 0) {
result->instanceFields = (DexField*) ptr;
ptr += header.instanceFieldsSize *
sizeof
(DexField);
}
else
{
result->instanceFields = NULL;
}
if
(header.directMethodsSize != 0) {
result->directMethods = (DexMethod*) ptr;
ptr += header.directMethodsSize *
sizeof
(DexMethod);
}
else
{
result->directMethods = NULL;
}
if
(header.virtualMethodsSize != 0) {
result->virtualMethods = (DexMethod*) ptr;
}
else
{
result->virtualMethods = NULL;
}
//以下依次读class_data_item的staticFields,instanceFields,directMethods和virtualMethods域大小————————end
//以下依次读staticFields,instanceFields,directMethods,virtualMethods域内容————————begain
for
(uint32_t i = 0; i < header.staticFieldsSize; i++) {
ReadClassDataField(pData, &result->staticFields[i]);
}
for
(uint32_t i = 0; i < header.instanceFieldsSize; i++) {
ReadClassDataField(pData, &result->instanceFields[i]);
}
for
(uint32_t i = 0; i < header.directMethodsSize; i++) {
ReadClassDataMethod(pData, &result->directMethods[i]);
}
for
(uint32_t i = 0; i < header.virtualMethodsSize; i++) {
ReadClassDataMethod(pData, &result->virtualMethods[i]);
}
//以下依次读staticFields,instanceFields,directMethods,virtualMethods域内容————————end
return
result;
}
/*
class_data_item中的一些域是用LEB128算法编码的
*/
void
writeLeb128(uint8_t ** ptr, uint32_t data)
{
while
(
true
) {
uint8_t out = data & 0x7f;
if
(out != data) {
*(*ptr)++ = out | 0x80;
data >>= 7;
}
else
{
*(*ptr)++ = out;
break
;
}
}
}
/*
此函数读取class_data_item,并将内容用writeLeb128转码后返回
*/
uint8_t* EncodeClassData(DexClassData *pData,
int
& len)
{
len=0;
len+=unsignedLeb128Size(pData->header.staticFieldsSize);
len+=unsignedLeb128Size(pData->header.instanceFieldsSize);
len+=unsignedLeb128Size(pData->header.directMethodsSize);
len+=unsignedLeb128Size(pData->header.virtualMethodsSize);
if
(pData->staticFields) {
for
(uint32_t i = 0; i < pData->header.staticFieldsSize; i++) {
len+=unsignedLeb128Size(pData->staticFields[i].fieldIdx);
len+=unsignedLeb128Size(pData->staticFields[i].accessFlags);
}
}
if
(pData->instanceFields) {
for
(uint32_t i = 0; i < pData->header.instanceFieldsSize; i++) {
len+=unsignedLeb128Size(pData->instanceFields[i].fieldIdx);
len+=unsignedLeb128Size(pData->instanceFields[i].accessFlags);
}
}
if
(pData->directMethods) {
for
(uint32_t i=0; i<pData->header.directMethodsSize; i++) {
len+=unsignedLeb128Size(pData->directMethods[i].methodIdx);
len+=unsignedLeb128Size(pData->directMethods[i].accessFlags);
len+=unsignedLeb128Size(pData->directMethods[i].codeOff);
}
}
if
(pData->virtualMethods) {
for
(uint32_t i=0; i<pData->header.virtualMethodsSize; i++) {
len+=unsignedLeb128Size(pData->virtualMethods[i].methodIdx);
len+=unsignedLeb128Size(pData->virtualMethods[i].accessFlags);
len+=unsignedLeb128Size(pData->virtualMethods[i].codeOff);
}
}
uint8_t * store = (uint8_t *)
malloc
(len);
if
(!store) {
return
NULL;
}
uint8_t * result=store;
writeLeb128(&store,pData->header.staticFieldsSize);
writeLeb128(&store,pData->header.instanceFieldsSize);
writeLeb128(&store,pData->header.directMethodsSize);
writeLeb128(&store,pData->header.virtualMethodsSize);
if
(pData->staticFields) {
for
(uint32_t i = 0; i < pData->header.staticFieldsSize; i++) {
writeLeb128(&store,pData->staticFields[i].fieldIdx);
writeLeb128(&store,pData->staticFields[i].accessFlags);
}
}
if
(pData->instanceFields) {
for
(uint32_t i = 0; i < pData->header.instanceFieldsSize; i++) {
writeLeb128(&store,pData->instanceFields[i].fieldIdx);
writeLeb128(&store,pData->instanceFields[i].accessFlags);
}
}
if
(pData->directMethods) {
for
(uint32_t i=0; i<pData->header.directMethodsSize; i++) {
writeLeb128(&store,pData->directMethods[i].methodIdx);
writeLeb128(&store,pData->directMethods[i].accessFlags);
writeLeb128(&store,pData->directMethods[i].codeOff);
}
}
if
(pData->virtualMethods) {
for
(uint32_t i=0; i<pData->header.virtualMethodsSize; i++) {
writeLeb128(&store,pData->virtualMethods[i].methodIdx);
writeLeb128(&store,pData->virtualMethods[i].accessFlags);
writeLeb128(&store,pData->virtualMethods[i].codeOff);
}
}
free
(pData);
return
result;
}
uint8_t* codeitem_end(
const
u1** pData)
{
uint32_t num_of_list = readUnsignedLeb128(pData);
for
(;num_of_list>0;num_of_list--) {
int32_t num_of_handlers=readSignedLeb128(pData);
int
num=num_of_handlers;
if
(num_of_handlers<=0) {
num=-num_of_handlers;
}
for
(; num > 0; num--) {
readUnsignedLeb128(pData);
readUnsignedLeb128(pData);
}
if
(num_of_handlers<=0) {
readUnsignedLeb128(pData);
}
}
return
(uint8_t*)(*pData);
}
/*
此为DexHunter实现的主要功能,进行内存dump,将class_def_items中dump出classdef和extra部分
*/
void
* DumpClass(
void
*parament)
{
while
(timer_flag) {
sleep(5);
}
DvmDex* pDvmDex=((
struct
arg*)parament)->pDvmDex;
//pDvmDex代表一个dex文件
Object *loader=((
struct
arg*)parament)->loader;
DexFile* pDexFile=pDvmDex->pDexFile;
MemMapping * mem=&pDvmDex->memMap;
u4
time
=dvmGetRelativeTimeMsec();
ALOGI(
"GOT IT begin: %d ms"
,
time
);
char
*path =
new
char
[100];
strcpy
(path,dumppath);
strcat
(path,
"classdef"
);
//构造classdef文件路径
FILE
*fp =
fopen
(path,
"wb+"
);
strcpy
(path,dumppath);
strcat
(path,
"extra"
);
//构造extra文件路径
FILE
*fp1 =
fopen
(path,
"wb+"
);
uint32_t mask=0x3ffff;
char
padding=0;
const
char
* header=
"Landroid"
;
unsigned
int
num_class_defs=pDexFile->pHeader->classDefsSize;
//dex文件的Header域,class_defs_size,标示了class_def域有几个class_def_item
uint32_t total_pointer = mem->length-uint32_t(pDexFile->baseAddr-(
const
u1*)mem->addr);
uint32_t rec=total_pointer;
while
(total_pointer&3) {
total_pointer++;
}
int
inc=total_pointer-rec;
uint32_t start = pDexFile->pHeader->classDefsOff+
sizeof
(DexClassDef)*num_class_defs;
uint32_t end = (uint32_t)((
const
u1*)mem->addr+mem->length-pDexFile->baseAddr);
for
(
size_t
i=0;i<num_class_defs;i++)
//遍历所有class_def_item
{
bool
need_extra=
false
;
ClassObject * clazz=NULL;
const
u1* data=NULL;
DexClassData* pData = NULL;
bool
pass=
false
;
const
DexClassDef *pClassDef = dexGetClassDef(pDvmDex->pDexFile, i);
const
char
*descriptor = dexGetClassDescriptor(pDvmDex->pDexFile,pClassDef);
if
(!
strncmp
(header,descriptor,8)||!pClassDef->classDataOff)
{
pass=
true
;
goto
classdef;
}
clazz = dvmDefineClass(pDvmDex, descriptor, loader);
//First Step:class加载
if
(!clazz) {
continue
;
}
ALOGI(
"GOT IT class: %s"
,descriptor);
if
(!dvmIsClassInitialized(clazz)) {
//Second Step:class初始化,遍历所有类进行初始化
if
(dvmInitClass(clazz)){
//与dvmIsClassInitialized()函数共同完成初始化
ALOGI(
"GOT IT init: %s"
,descriptor);
}
}
if
(pClassDef->classDataOff<start || pClassDef->classDataOff>end)
{
need_extra=
true
;
//是不是需要extra这一块,当存在class_data_off不在dex范围内时,就需要
}
data=dexGetClassData(pDexFile, pClassDef);
pData = ReadClassData(&data);
if
(!pData) {
continue
;
}
if
(pData->directMethods) {
for
(uint32_t i=0; i<pData->header.directMethodsSize; i++) {
Method *method = &(clazz->directMethods[i]);
uint32_t ac = (method->accessFlags) & mask;
ALOGI(
"GOT IT direct method name %s.%s"
,descriptor,method->name);
if
(!method->insns||ac&ACC_NATIVE) {
if
(pData->directMethods[i].codeOff) {
need_extra =
true
;
pData->directMethods[i].accessFlags=ac;
pData->directMethods[i].codeOff=0;
}
continue
;
}
u4 codeitem_off = u4((
const
u1*)method->insns-16-pDexFile->baseAddr);
if
(ac != pData->directMethods[i].accessFlags)
{
ALOGI(
"GOT IT method ac"
);
need_extra=
true
;
pData->directMethods[i].accessFlags=ac;
}
if
(codeitem_off!=pData->directMethods[i].codeOff&&((codeitem_off>=start&&codeitem_off<=end)||codeitem_off==0)) {
ALOGI(
"GOT IT method code"
);
need_extra=
true
;
pData->directMethods[i].codeOff=codeitem_off;
}
if
((codeitem_off<start || codeitem_off>end) && codeitem_off!=0) {
//如果code_item_off不在dex文件范围内,则写入extra; fp1为extra的句柄
//这里使用的是slider.pptx,中的第二种方案,见p42
need_extra=
true
;
pData->directMethods[i].codeOff = total_pointer;
DexCode *code = (DexCode*)((
const
u1*)method->insns-16);
uint8_t *item=(uint8_t *) code;
int
code_item_len = 0;
if
(code->triesSize) {
const
u1 * handler_data = dexGetCatchHandlerData(code);
const
u1** phandler=(
const
u1**)&handler_data;
uint8_t * tail=codeitem_end(phandler);
code_item_len = (
int
)(tail-item);
}
else
{
code_item_len = 16+code->insnsSize*2;
}
ALOGI(
"GOT IT method code changed"
);
fwrite
(item,1,code_item_len,fp1);
fflush
(fp1);
total_pointer+=code_item_len;
while
(total_pointer&3) {
fwrite
(&padding,1,1,fp1);
fflush
(fp1);
total_pointer++;
}
}
}
}
if
(pData->virtualMethods) {
for
(uint32_t i=0; i<pData->header.virtualMethodsSize; i++) {
Method *method = &(clazz->virtualMethods[i]);
uint32_t ac = (method->accessFlags) & mask;
ALOGI(
"GOT IT virtual method name %s.%s"
,descriptor,method->name);
if
(!method->insns||ac&ACC_NATIVE) {
if
(pData->virtualMethods[i].codeOff) {
need_extra =
true
;
pData->virtualMethods[i].accessFlags=ac;
pData->virtualMethods[i].codeOff=0;
}
continue
;
}
u4 codeitem_off = u4((
const
u1 *)method->insns - 16 - pDexFile->baseAddr);
if
(ac != pData->virtualMethods[i].accessFlags)
{
ALOGI(
"GOT IT method ac"
);
need_extra=
true
;
pData->virtualMethods[i].accessFlags=ac;
}
if
(codeitem_off!=pData->virtualMethods[i].codeOff&&((codeitem_off>=start&&codeitem_off<=end)||codeitem_off==0)) {
ALOGI(
"GOT IT method code"
);
need_extra=
true
;
pData->virtualMethods[i].codeOff=codeitem_off;
}
if
((codeitem_off<start || codeitem_off>end)&&codeitem_off!=0) {
need_extra=
true
;
pData->virtualMethods[i].codeOff = total_pointer;
DexCode *code = (DexCode*)((
const
u1*)method->insns-16);
uint8_t *item=(uint8_t *) code;
int
code_item_len = 0;
if
(code->triesSize) {
const
u1 *handler_data = dexGetCatchHandlerData(code);
const
u1** phandler=(
const
u1**)&handler_data;
uint8_t * tail=codeitem_end(phandler);
code_item_len = (
int
)(tail-item);
}
else
{
code_item_len = 16+code->insnsSize*2;
}
ALOGI(
"GOT IT method code changed"
);
fwrite
(item,1,code_item_len,fp1);
fflush
(fp1);
total_pointer+=code_item_len;
while
(total_pointer&3) {
fwrite
(&padding,1,1,fp1);
fflush
(fp1);
total_pointer++;
}
}
}
}
classdef:
DexClassDef temp=*pClassDef;
uint8_t *p = (uint8_t *)&temp;
if
(need_extra) {
ALOGI(
"GOT IT classdata before"
);
int
class_data_len = 0;
uint8_t *out = EncodeClassData(pData,class_data_len);
if
(!out) {
continue
;
}
temp.classDataOff = total_pointer;
fwrite
(out,1,class_data_len,fp1);
fflush
(fp1);
total_pointer+=class_data_len;
while
(total_pointer&3) {
fwrite
(&padding,1,1,fp1);
fflush
(fp1);
total_pointer++;
}
free
(out);
ALOGI(
"GOT IT classdata written"
);
}
else
{
if
(pData) {
free
(pData);
}
}
if
(pass) {
temp.classDataOff=0;
temp.annotationsOff=0;
}
ALOGI(
"GOT IT classdef"
);
fwrite
(p,
sizeof
(DexClassDef), 1, fp);
fflush
(fp);
}
fclose
(fp1);
fclose
(fp);
//目前已经准备好了part1,classdef,data和extra 4部分文件
strcpy
(path,dumppath);
strcat
(path,
"whole.dex"
);
//组合4部分,并写入whole.dex
fp =
fopen
(path,
"wb+"
);
rewind
(fp);
int
fd=-1;
int
r=-1;
int
len=0;
char
*addr=NULL;
struct
stat st;
strcpy
(path,dumppath);
strcat
(path,
"part1"
);
fd=open(path,O_RDONLY,0666);
if
(fd==-1) {
return
NULL;
}
r=fstat(fd,&st);
if
(r==-1){
close(fd);
return
NULL;
}
len=st.st_size;
addr=(
char
*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
fwrite
(addr,1,len,fp);
fflush
(fp);
munmap(addr,len);
close(fd);
strcpy
(path,dumppath);
strcat
(path,
"classdef"
);
fd=open(path,O_RDONLY,0666);
if
(fd==-1) {
return
NULL;
}
r=fstat(fd,&st);
if
(r==-1){
close(fd);
return
NULL;
}
len=st.st_size;
addr=(
char
*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
fwrite
(addr,1,len,fp);
fflush
(fp);
munmap(addr,len);
close(fd);
strcpy
(path,dumppath);
strcat
(path,
"data"
);
fd=open(path,O_RDONLY,0666);
if
(fd==-1) {
return
NULL;
}
r=fstat(fd,&st);
if
(r==-1){
close(fd);
return
NULL;
}
len=st.st_size;
addr=(
char
*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
fwrite
(addr,1,len,fp);
fflush
(fp);
munmap(addr,len);
close(fd);
while
(inc>0) {
fwrite
(&padding,1,1,fp);
fflush
(fp);
inc--;
}
strcpy
(path,dumppath);
strcat
(path,
"extra"
);
fd=open(path,O_RDONLY,0666);
if
(fd==-1) {
return
NULL;
}
r=fstat(fd,&st);
if
(r==-1){
close(fd);
return
NULL;
}
len=st.st_size;
addr=(
char
*)mmap(NULL,len,PROT_READ,MAP_PRIVATE,fd,0);
fwrite
(addr,1,len,fp);
fflush
(fp);
munmap(addr,len);
close(fd);
fclose
(fp);
delete
path;
time
=dvmGetRelativeTimeMsec();
ALOGI(
"GOT IT end: %d ms"
,
time
);
return
NULL;
}
//------------------------added end----------------------//
- 安卓 dex 通用脱壳技术研究
- 安卓 dex 通用脱壳技术研究(一)
- 安卓 dex 通用脱壳技术研究(二)
- 安卓 dex 通用脱壳技术研究(三)
- 安卓 dex 通用脱壳技术研究(四)
- Android dex文件通用自动脱壳器
- 安卓dex反编译工具
- ]发现一个安卓万能脱壳方法
- Redex安卓Apk优化技术研究
- 【原创】一个dex脱壳脚本
- 安卓dex 文件结构简要说明
- 安卓dex的编译和反编译
- 360加固保的dex脱壳方法
- 360加固保的dex脱壳方法
- 利用IDA学习一个简单的安卓脱壳
- 安卓逆向之基于Xposed-ZjDroid脱壳
- 安卓逆向之基于Xposed-ZjDroid脱壳
- 171025 逆向-安卓脱壳(补充实验)
- mysql分页丢数据的分析
- Linux 时钟和ntp服务
- 第一行代码总结:10网络:10.3.2 SAX解析方式
- 为什么接口中变量要用final修饰
- RPG Maker MV之如何创建NPC
- 安卓 dex 通用脱壳技术研究
- iOS开发 - 处理不等高TableViewCell的招术
- shell从1加到100
- Android 软件开发规范说明
- c语言递归函数
- Sencha Touch入门:Sencha Touch开发环境搭建及使用 Sencha Cmd 自动创建项目框架
- 第一行代码总结:10网络:10.4解析JSON格式数据10.4.1使用JSONObject
- 近视手术?医学界的一个阴谋
- 突破select的FD_SETSIZE限制