DexHunter脱壳神器分析
0x00
这篇文章我们分析Android脱壳神器DexHunter的源码。
DexHunter作者也写了一篇介绍它的文章从Android执行时出发。打造我们的脱壳神器。DexHunter源码位于https://github.com/zyq8709/DexHunter。
0x01
DexHunter 实现中,仅仅须要改动一处文件:dalvik\vm\native\dalvik_system_DexFile.cpp
以下是BeyondCompare比对:
我们看到DexHunter的代码都位于系统源码vm文件夹下,所以要执行DexHunter须要dalvik\vm\native\dalvik_system_DexFile.cpp代码,然后在源码环境下编译。最后刷机执行。
0x02
核心原理请參考从Android执行时出发,打造我们的脱壳神器,简单的从代码角度说,在Dalvik_dalvik_system_DexFile_defineClassNative中插入代码来修复被破坏的dex,原理有两个:
1、在DVM中:
显式载入:
ClassLoader.loadClass相应Dalvik_dalvik_system_DexFile_defineClassNative
Class.forName相应Dalvik_java_lang_Class_classForName
隐式载入:
相应dvmResolveClass
例如以下图:
第一点说明了时机。也就是为什么在Dalvik_dalvik_system_DexFile_defineClassNative插入代码。
2、执行dvmDefineClass,形成的ClassObject结构体的变量都是有效的。dvmDefineClass会在我们插入的代码中调用,详见后面的分析。
0x03
以下用凝视的方式来分析源码。
分析之前先附上两张图。有助于分析。
图 1
当中baseAddr指向DexHeader的首地址。图中有错误。下文中常常看到mem->addr指向DexOptHeader的首地址。
再附上一张图。分析时会用到:
图 2
//------------------------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}; static char dumppath[100]={0}; 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);//从/data/dexname获取字符串赋值给dexname。github中已经给出了,是/data/data/com.example.seventyfour.tencenttest/files/libmobisecy1.zip。这是用来脱阿里壳子时使用的
dexname[strlen(dexname)-1]=0;
fgets(dumppath,99,fp);//这是生成4个文件的总文件夹/data/data/com.example.seventyfour.tencenttest/。后面我们会看到4个文件各自是part1,data,classdef。extra
dumppath[strlen(dumppath)-1]=0;
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;
} void ReadClassDataHeader(const uint8_t** pData,
DexClassDataHeader *pHeader) {
pHeader->staticFieldsSize = readUnsignedLeb128(pData);
pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
pHeader->directMethodsSize = readUnsignedLeb128(pData);
pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
} 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);
} DexClassData* ReadClassData(const uint8_t** pData) { DexClassDataHeader header; if (*pData == NULL) {
return NULL;
} 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); if (result == NULL) {
return NULL;
} uint8_t* ptr = ((uint8_t*) result) + sizeof(DexClassData); result->header = header; 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;
} 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]);
} return result;
} 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;
}
}
} 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);
} void* DumpClass(void *parament)
{
while (timer_flag) {
sleep(5);
} DvmDex* pDvmDex=((struct arg*)parament)->pDvmDex;
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");
FILE *fp = fopen(path, "wb+");//fp指向classdef文件 strcpy(path,dumppath);
strcat(path,"extra");
FILE *fp1 = fopen(path,"wb+");//fp1指向extra文件 uint32_t mask=0x3ffff;
char padding=0;
const char* header="Landroid";
unsigned int num_class_defs=pDexFile->pHeader->classDefsSize;//class_def的数量
uint32_t total_pointer = mem->length-uint32_t(pDexFile->baseAddr-(const u1*)mem->addr);//末尾地址相对DexHeader头部的偏移地址
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;//若干个class_def_item之后的地址相对于DexHeader头部的偏移地址
uint32_t end = (uint32_t)((const u1*)mem->addr+mem->length-pDexFile->baseAddr);//末尾地址相对DexHeader头部的偏移地址 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);//找到了相应的class_def_item,如图2
const char *descriptor = dexGetClassDescriptor(pDvmDex->pDexFile,pClassDef); if(!strncmp(header,descriptor,8)||!pClassDef->classDataOff)
{
pass=true;
goto classdef;
} clazz = dvmDefineClass(pDvmDex, descriptor, loader);//前面说过,脱壳程序可以执行的非常重要的原理是。这里生成的ClassObject结构体中的变量都是有效的。 if (!clazz) {
continue;
} ALOGI("GOT IT class: %s",descriptor); if (!dvmIsClassInitialized(clazz)) {
if(dvmInitClass(clazz)){
ALOGI("GOT IT init: %s",descriptor);
}
} if(pClassDef->classDataOff<start || pClassDef->classDataOff>end)//假设classDataOff的偏移落在范围外,那就须要生成extra部分,具体解释后面的代码
{
need_extra=true;
} data=dexGetClassData(pDexFile,pClassDef);//通过class_def获取class_data_item,具体解释图2
pData = ReadClassData(&data);//读取如今内存中的值。形成了DexClassData结构体pData,注意此时pData中的变量可能是错误的,非常easy的一个样例就是http://blog.csdn.net/jltxgcy/article/details/50581259,參考这篇文章 if (!pData) {
continue;
} if (pData->directMethods) {
for (uint32_t i=0; i<pData->header.directMethodsSize; i++) {
Method *method = &(clazz->directMethods[i]);//此时获取Method结构体method里面的变量是正确的
uint32_t ac = (method->accessFlags) & mask;//正确的accessFlags ALOGI("GOT IT direct method name %s.%s",descriptor,method->name); if (!method->insns||ac&ACC_NATIVE) {//正确的insns没有指令。所以要调整
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)//当前的accessFlag和正确的accessFlag不一致。所以要调整
{
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)) {//假设正确的codeitem_off不等于如今的codeitem_off。且正确的codeitem_off在范围内,则须要调整
ALOGI("GOT IT method code");
need_extra=true;
pData->directMethods[i].codeOff=codeitem_off;
} if ((codeitem_off<start || codeitem_off>end) && codeitem_off!=0) {//在http://blog.csdn.net/jltxgcy/article/details/50581259。所属的情况在这个推断中。真是的codeitem_off在范围(start--end)外。
need_extra=true;
pData->directMethods[i].codeOff = total_pointer;//让codeOff指向dex结尾的偏移
DexCode *code = (DexCode*)((const u1*)method->insns-16);//正确的DexCode
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;//正确的DexCode的大小
} ALOGI("GOT IT method code changed"); fwrite(item,1,code_item_len,fp1);//把DexCode写入extra中
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;//class_def的classDataOff指向了新生成的class_data_item
fwrite(out,1,class_data_len,fp1);//将这个class_data_item写入extra,此class_data_item中的codeOff已经改变了
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);//将class_def入classdef文件,改class_def中的classDataOff已经改变了
fflush(fp);
} fclose(fp1);
fclose(fp);
//最后执行完成后。把四个文件依照先后顺序合并在一起,依次是part1,classdef,data,extra,最后生成dex就是正确的dex
strcpy(path,dumppath);
strcat(path,"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----------------------// 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);//在新的线程中执行ReadThread,ReadThread在上面的代码中 }else{
pthread_mutex_unlock(&read_mutex);
}
}
} if(uid&&strcmp(dexname,"")){
char * res=strstr(pDexOrJar->fileName, dexname);//这个是dexname的用途,用来比較,仅仅有匹配的dex才会执行后面的代码
if (res&&flag) {
pthread_mutex_lock(&mutex);
if (flag) {
flag = false;
pthread_mutex_unlock(&mutex); DexFile* pDexFile=pDvmDex->pDexFile;
MemMapping * mem=&pDvmDex->memMap; 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);//class_defs之前的内容的长度。mem->addr,和baseAddr是理解这个长度的关键,在代码前的红字部分有说明和图解。
fwrite(addr,1,length,fp);//class_defs之前的内容写入part1
fflush(fp);
fclose(fp); 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);//class_defs之后的内容的长度
fwrite(addr,1,length,fp);//class_defs之后的内容写入data中
fflush(fp);
fclose(fp);
delete temp; param.loader=loader;
param.pDvmDex=pDvmDex; pthread_t dumpthread;
dvmCreateInternalThread(&dumpthread,"ClassDumper",DumpClass,(void*)param);//在新的线程中执行DumpClass,參数是param,这个函数的实如今前面的代码中 }else{
pthread_mutex_unlock(&mutex);
}
}
}
//------------------------added end----------------------// clazz = dvmDefineClass(pDvmDex, descriptor, loader);
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);
}
0x04
假设是执行时动态修复dex这篇文章执行的DynamicDex.apk,上面的代码解释也着重分析了,解析后会形成下图的结构(正确的dex)。
第一部分是part1。第二部分是classdef,第三部分是data,第四部分是extra。
第二部分的当中一个class_def_item中的classDataOff指向了extra中class_data_item。
extra中的class_data_item中的codeOff指向了extra中DexCode。
DexHunter脱壳神器分析的更多相关文章
- 源码分析 脱壳神器ZjDroid工作原理
0. 神器ZjDroid Xposed框架的另外一个功能就是实现应用的简单脱壳,其实说是Xposed的作用其实也不是,主要是模块编写的好就可以了,主要是利用Xposed的牛逼Hook技术实现的,下面就 ...
- DexHunter在ART虚拟机模式下的脱壳原理分析
本文博客地址: http://blog.csdn.net/qq1084283172/article/details/78494620 DexHunter脱壳工具在Dalvik虚拟机模式下的脱壳原理分析 ...
- DexHunter在Dalvik虚拟机模式下的脱壳原理分析
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/78494671 在前面的博客<DexHunter的原理分析和使用说明(一)&g ...
- DexHunter的原理分析和使用说明(一)
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53710357 Android通用脱壳工具DexHunter是2015年下半年,大牛 ...
- DexHunter的原理分析和使用说明(二)
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53715325 前面的博文<Android通用脱壳工具DexHunter的原理 ...
- UPX脱壳全程分析(转)
[文章标题]: UPX脱壳全程分析 [保护方式]: 本地验证 [使用工具]: OllyDBG [作者声明]: 只是感兴趣,没有其他目的.失误之处敬请诸位大侠赐教! ------------------ ...
- android 脱壳 之 dvmDexFileOpenPartial断点脱壳原理分析
android 脱壳 之 dvmDexFileOpenPartial断点脱壳原理分析 导语: 笔者主要研究方向是网络通信协议的加密解密, 对应用程序加固脱壳技术很少研究, 脱壳壳经历更是经历少之甚少. ...
- 安卓脱壳&&协议分析&&burp辅助分析插件编写
前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 前言 本文以一个 app 为例,演示对 app脱壳,然后分析其 协 ...
- android脱壳之DexExtractor原理分析[zhuan]
http://www.cnblogs.com/jiaoxiake/p/6818786.html内容如下 导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的 ...
随机推荐
- poj1321 棋盘问题(深搜dfs)
转载请注明出处:http://blog.csdn.net/u012860063? viewmode=contents 题目链接:id=1321">http://poj.org/prob ...
- Revit API创建标高,单位转换
一业内朋友让我写个快速创建标高的插件. ; ; i <= iNum; i++) { Level level = d ...
- DirectX - dds图片格式(DDSURFACEDESC2)
DDS是DirectDraw Surface的缩写,它是DirectX纹理压缩(DirectX Texture Compression,简称DXTC)的产物. DXTC减少了纹理内存消耗的50%甚至更 ...
- javascript: break跳出多重循环以及退出each循环
先来看一个小例子: <html> <body> <script type="text/javascript"> for(j=0;j<2;j ...
- Android之 MTP框架和流程分析
概要 本文的目的是介绍Android系统中MTP的一些相关知识.主要的内容包括:第1部分 MTP简介 对Mtp协议进行简单的介绍.第2部分 MTP框架 介绍 ...
- 字符串类型的变量,也要进行alloc内存分配之后才能用
nss_upperName =[[NSStringalloc]initWithString:labTopTitle.text]; nss_upperName =labTopTitle.text; // ...
- Eclipse 进入代码定位文件位置
- 【k8s】搭建步骤
搭建步骤 基础概念:https://www.cnblogs.com/sxdcgaq8080/p/10640879.html ====================================== ...
- FEC详解三
转自:http://blog.csdn.net/Stone_OverLooking/article/details/77752076 继续上文讲解: 3) 标准的RTP头结构如下所示: 其中第一个字节 ...
- Swift - EasingAnimation绘制圆环动画
Swift - EasingAnimation绘制圆环动画 效果 源码 https://github.com/YouXianMing/Swift-Animations // // CircleView ...