MyBlog

Android DEX通用脱壳技术研究

last modified: 2017-04-11 01:07

注:以下4篇博文中,部分图片引用自DexHunter作者zyqqyz在slide.pptx中的图片,版本归原作者所有;

0x01 背景介绍

安卓 APP 的保护一般分为下列几个方面:

  1. JAVA/C代码混淆
  2. dex文件加壳
  3. .so文件加壳
  4. 反动态调试技术

其中混淆和加壳是为了防止对应用的静态分析;代码混淆会增加攻击者的时间成本, 但并不能从根本上解决应用被逆向的问题;而加壳技术一旦被破解,其优势更是荡然无存;反调试用来对抗对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。

在外部函数调用解释器以后,解释器执行的主要流程有以下几个步骤。

  1. 初始化解释器执行环境
  2. 根据系统参数,选择使用Portable或Fast解释器
  3. 跳转到相应解释器执行
  4. 取指及指令检查
  5. 执行字节码对应程序段

dvmInterpret函数作为解释器的入口函数,主要完成整个流程的前三部分,执行流程如下:

dexhunter1

由于前三部分与我们定制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得到解释程序地址->执行指令,并取下一条指令。

dexhunter2

上面分析了解释器的基本模型,下面看Dalvik Portable的执行流程。其解析流程如图:

dexhunter3

首先进行相关变量的声明,保存当前正在解释的方法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比对:

dexhunter4

首先看一下DexHunter的设计原理:

dexhunter5

APP 启动时,通过freature string定位dex在内存中位置,并读取classdef块之前的内存为part1,读取classdef之后的内存为data。遍历class_def_item结构,生成文件classdef,并通过code_item_off判断具体的类方法是否在dex范围内,若不在,则写extra文件。

dexhunter6

描述几个问题:

从哪里dump出dex文件?

  1. dex文件打开时
  2. 类加载时
  3. 类初始化时
  4. 类方法调用时

DexHunter中,我们关注,ClassLoader.loadClass->Dalvik_dalvik_system_DexFile_defineClassNative这个函数,它实现了类的加载,实现过程如下:

dexhunter7

选择脱壳的时机应是在APP的第一个类加载的时候,为什么呢?

  1. 类加载之前,类的内容是在内存当中的
  2. 当类初始化时,该内存的内容可能会被动态修改
  3. 在一个类方法被调用前,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----------------------//

/*
    当第一个类执行到此函数时,我们在dvmDefineClass执行之前,也就是第一个类加载之前
    注入我们的dump代码;即DumpClass()函数
*/
 
static void Dalvik_dalvik_system_DexFile_defineClassNative(const u4* args,
    JValue* pResult)
{
    StringObject* nameObj = (StringObject*) args[0];
    Object* loader = (Object*) args[1];
    int cookie = args[2];
    ClassObject* clazz = NULL;
    DexOrJar* pDexOrJar = (DexOrJar*) cookie;
    DvmDex* pDvmDex;
    char* name;
    char* descriptor;
 
    name = dvmCreateCstrFromString(nameObj);
    descriptor = dvmDotToDescriptor(name);
    ALOGV("--- Explicit class load '%s' l=%p c=0x%08x",
        descriptor, loader, cookie);
    free(name);
 
    if (!validateCookie(cookie))
        RETURN_VOID();
 
    if (pDexOrJar->isDex)
        pDvmDex = dvmGetRawDexFileDex(pDexOrJar->pRawDexFile);
    else
        pDvmDex = dvmGetJarFileDex(pDexOrJar->pJarFile);
 
    /* once we load something, we can't unmap the storage */
    pDexOrJar->okayToFree = false;
 
//------------------------added begin----------------------//
    int uid=getuid();
 
    if (uid) {
        if (readable) {
            pthread_mutex_lock(&read_mutex);
            if (readable) {
                readable=false;
                pthread_mutex_unlock(&read_mutex);
 
                pthread_t read_thread;
                pthread_create(&read_thread, NULL, ReadThread, NULL);
 
            }else{
                pthread_mutex_unlock(&read_mutex);
            }
        }
    }
 
    //每个APP都对应一个Thread
    if(uid && strcmp(dexname,"")) { //dexname非空
        char * res=strstr(pDexOrJar->fileName, dexname);
        if (res && flag) {
            pthread_mutex_lock(&mutex);
            if (flag) {
                flag = false;
                pthread_mutex_unlock(&mutex);
  
                DexFile* pDexFile=pDvmDex->pDexFile;    //取dex file
                MemMapping * mem=&pDvmDex->memMap;  //取memmory map 
//part1区,classdef前内容
                char * temp=new char[100];
                strcpy(temp,dumppath);
                strcat(temp,"part1");
                FILE *fp = fopen(temp, "wb+");
                const u1 *addr = (const u1*)mem->addr;
                int length=int(pDexFile->baseAddr+pDexFile->pHeader->classDefsOff-addr);
                fwrite(addr,1,length,fp);
                fflush(fp);
                fclose(fp);
//data区,classdef后内容
                strcpy(temp,dumppath);
                strcat(temp,"data");
                fp = fopen(temp, "wb+");
                addr = pDexFile->baseAddr+pDexFile->pHeader->classDefsOff+sizeof(DexClassDef)*pDexFile->pHeader->classDefsSize;
                length=int((const u1*)mem->addr+mem->length-addr);
                fwrite(addr,1,length,fp);
                fflush(fp);
                fclose(fp);
                delete temp;
 
                param.loader=loader;
                param.pDvmDex=pDvmDex;
 
                pthread_t dumpthread;
                dvmCreateInternalThread(&dumpthread,"ClassDumper",DumpClass,(void*)&param);      
//DumpClass用来生成classdef 和 extra内容                
 
            }else{
                pthread_mutex_unlock(&mutex);
            }
        }
    }
//------------------------added end----------------------//
 
    clazz = dvmDefineClass(pDvmDex, descriptor, loader);    //加载类。当APP第一个类加载之前,调用我们的脱壳代码
    Thread* self = dvmThreadSelf();
    if (dvmCheckException(self)) {
        /*
         * If we threw a "class not found" exception, stifle it, since the
         * contract in the higher method says we simply return null if
         * the class is not found.
         */
        Object* excep = dvmGetException(self);
        if (strcmp(excep->clazz->descriptor,
                   "Ljava/lang/ClassNotFoundException;") == 0 ||
            strcmp(excep->clazz->descriptor,
                   "Ljava/lang/NoClassDefFoundError;") == 0)
        {
            dvmClearException(self);
        }
        clazz = NULL;
    }
 
    free(descriptor);
    RETURN_PTR(clazz);
}