admin管理员组

文章数量:817304

DexExtractor的原理分析和使用说明

本文博客链接:

周末有空就写下博客了,今天来扯一扯Android平台的脱壳工具DexExtractor。DexExtractor工具的使用比较简单,脱壳的原理也比较简单,工具的作者bunnyrene讲这个android脱壳工具只要是针对bangbang (Bangcle)和ijiami加固的脱壳工具,应该是这两类加固的免费版吧。之前在分析android病毒的时候使用过,但是有时候脱壳还是不成功,需要人工手工脱壳来完成或者说这个工具需要进一步完善一下,不管怎么说谢谢作者大牛。


i.DexExtractor脱壳工具的使用说明和讨论链接

看雪论坛:.php?t=201799

github地址:


ii.作者编译好的镜像文件system.img的下载地址

网盘地址:


iii.DexExtractor脱壳工具的使用说明截图





iiii.通过阅读DexExtractor作者提供的使用说明,写下需要注意的地方:

1.DexExtractor脱壳工具的原理是修改了android源码的DexFile.cpp的dexFileParse函数也就是原来的脱壳点函数处进行dexdump的处理,作者提供了Android 4.4---api 19版本系统脱壳的system.img文件(推荐android模拟器用)和libdvm.so文件(android模拟器和android真机都可以使用)。



2.由于DexExtractor工具需要不断的向sdcard写文件,因此特别是android模拟器使用该脱壳工具一定设置Android模拟的sdcard的空间大小。

3.由于DexExtractor工具需要不断的向sdcard写文件,因此被脱壳的apk需要有向sdcard写文件的权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>。

4.DexExtractor工具的system.img文件(Android 4.4.2版本)使用比较简单--直接替换Android 4.4.2版本系统的system.img文件文件即可,Android模拟器的system.img文件的替换比较简单,Android真机的替换可能需要是Android真机进入recovery模式进行替换;DexExtractor工具的libdvm.so文件使用--需要使用abd的push命令将libdvm.so推送到android系统中然后在su权限将libdvm.so文件拷贝或者备份到Android系统的/system/lib/libdvm.so或者Android真机在root的情况下,使用rootexplore.apk(RE管理器工具)将libdvm.so文件拷贝到手机系统的/system/lib/libdvm.so下,替换掉原来Android系统的的/system/lib/libdvm.so文件,并赋予刚才拷贝替换的/system/lib/libdvm.so文件以0775权限然后重启Android真机即可。

adb push xxx/libdvm.so /data/local/tmp/libdvm.so
adb shell
su
mount -r -w -o remount/system
mount -o remount,rw /system
dd if=/system/lib/libdvm.so of=/data/local/tmp/libdvm.bak
rm /system/lib/libdvm.so 
cat /data/local/tmp/libdvm.so >/system/lib/libdvm.so
chmod 0775 /system/lib/libdvm.so
mount -o remount,ro /system
exit
exit
adb reboot


iiiii.使用DexExtractor工具的system.img文件配置Android 4.4.2的android模拟器进行脱壳实验

使用前提条件

1.如果是android模拟器使用脱壳镜像文件system.img,一定要配置sdcard参数

2.需要脱壳的apk如果没有写权限,需要添加上写sdcard的权限。
如:
apk没有写权限的反编译了加上write权限

说明:
<!--往sdcard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--sdcard创建/删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

需要使用的权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
#如果脱壳apk没有文件的写权限,反编译以后加上sdcard文件的写权限,然后重新打包apk程序。


DexExtractor脱壳实例(使用Android模拟器):

1.找到Android 4.4.2对应的API 19的系统文件的目录,如下:

E:\BaiduYunDownload\adt-bundle-windows-x86_64-20140702\sdk\system-images\android-19\default\armeabi-v7a
备份原来的system.img文件,然后将修改的system.img文件解压后拷贝到这个目录里。

2.在android模拟器上创建API为19的android模拟器,千万不能忘了为模拟器添加sdcard的内存空间参数。

3.用反编译工具查看被脱壳的apk是否有-----写文件的权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>没有就添加上写文件的权限并重新打包apk程序。

4.adb install xxx.apk 安装需要脱壳的apk程序到上面创建的android模拟器上。

5.打开logcat的logcat 监视log  "被加固的包名" 和 "dvmtag" ,当看到 "create file end",说明脱壳成功;从eclipse中导出log日志文件并保存。(这里请参考作者提供的脱壳成功的说明)



6.adb pull  /sdcard/classes_xxx.dex 从android模拟器的sdcard中导出脱壳后但是经过base64加密的dex文件。
示例:
adb  pull  /sdcard/tx.qq898507339.bzy9_classes_5528.dex

7.脱壳后的dex文件被Base64编码了,需要解码。 
解码dex文件:
java -jar Decoder.jar  dex目录  
java -jar Decoder.jar   xxxx\tx.qq898507339.bzy9_classes_5528.dex

======================================================================================================
java环境的正确配置

安装JDK,比如目录在C:\Java

为了方便java程序的开发,需要配置一下环境变量,右击我的电脑->属性->高级->环境变量->用户变量中单击[新建(N)]添加以下环境变量

(假定你的JDK安装路径为C:\Java\jdk1.6.0_30)

JAVA_HOME
C:\Java\jdk1.6.0_30

PATH
C:\Java\jdk1.6.0_30\bin

CLASSPATH
.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;(注前面的点号和分号一定不能丢,还有中间的,后面的分号也不要丢了)

说明:CLASSPATH可以再增加一些第三方的jar文件,方便手工编译和运行程序。
======================================================================================================


iiiiii.DexExtractor脱壳工具的代码分析:
DexExtractor脱壳工具的脱壳原理是修改了android系统的dvm虚拟机模块的代码/dalvik/libdex/DexFile.cpp文件的dexFileParse函数,在系统调用dexFileParse函数之前将加固释放出的原始dex文件从内存dump出来。


但是呢,由于一些加固如梆梆加固,会对dex的内存dump脱壳进行检测,具体的就是hook修改当前进程的read、write读写相关的函数,一旦检测到read、write读写相关的函数的操作中有对dex文件的dump行为会有对抗的处理,防止dex的内存dump,因此呢,DexExtractor脱壳工具的作者为了过掉这种内存dump的对抗,需要先将原始的dex文件数据进行base64加密然后进行写文件到sdcard中进行输出,当pull导出拿到base64加密的dex文件时还需要进行base64的解密才能获取到原始的dex文件,具体的脱壳功能见DexHacker.cpp文件。

//
//  DexHacker.cpp
//  
//
//  Created by BunnyBlue on 6/23/15.
//
//#include "DexHacker.h"
void DexHacker::writeDex2Encoded(unsigned char *data, size_t length){#ifdef CODE_DVMALOGE("--pacthed--  inject  .dex length %d  flag=%d",length,flags);char dexbuffer[64]={0};char dexbufferNamed[128]={0};char bufferProcess[256]={0};// 获取当前进程的名称字符串bufferProcess= getProcessName(bufferProcess);sprintf(dexbuffer, "classes_%d", length);strcat(dexbufferNamed,"/sdcard/");if (bufferProcess!=NULL) {strcat(dexbufferNamed, bufferProcess);strcat(dexbufferNamed, dexbuffer);}else{ALOGE("--pacthed-- , FAULT pid not  found\n");return;}// strcat(dexbufferNamed,dexbuffer);strcat(dexbufferNamed,".dex");ALOGE("--pacthed-- , %s\n", dexbufferNamed);ALOGE("--pacthed--  debug dalvikParse   find dex try write file  ");// 删除原来存在的文件int status = remove(dexbufferNamed);if( status == 0 )ALOGE("%s file deleted successfully.\n",dexbufferNamed);else{ALOGE("Unable to delete the file\n");}// 申请内存缓冲区备份信息u1* buffer_data_dest=(u1*)malloc((length+1)*sizeof(u1));// 拷贝内存中的dex文件的数据到内存缓冲区buffer_data_dest中备份memcpy(buffer_data_dest, data, length);// 创建文件FILE *fp = fopen(dexbufferNamed,"wb");if(NULL==fp) {ALOGE("--pacthed-- , can't create file ! maybe  you need mount sdcard again!");ALOGE( "%s data %s\n", strerror(errno),data);} else {ALOGE("--pacthed--   create file  %s ",dexbufferNamed);int dex_lem_local = length>2048? 1024:length;//  申请内存空间保存base64加密后的dex文件的数据unsigned char *dst=(unsigned char*)malloc(length*2.5);unsigned long dlen=length*2.5;// 将原始的dex文件的数据进行base64加密base64_encode(dst, &dlen, data, length);// 将base64加密后的dex文件的数据进行写文件输出到sdcard中fwrite(dst, dlen, 1, fp);//fwrite(data, sizeof(u1), length, fp);ALOGE("--pacthed--  create file  end ");//fflush(fp);fclose(fp);fp = NULL;}free(buffer_data_dest);
#else char dexbuffer[64]={0};char dexbufferNamed[128]={0};sprintf(dexbuffer, "classes_%d", length);//strcat(dexbufferNamed,");strcat(dexbufferNamed,dexbuffer);strcat(dexbufferNamed,".dex");int status = remove(dexbufferNamed);FILE *fp = fopen(dexbufferNamed,"wb");if(NULL==fp){} else {unsigned char *dst=(unsigned char*)malloc(length*2.5);unsigned long dlen=length*2.5;base64_encode(dst, &dlen, data, length);fwrite(dst, dlen, 1, fp);//fflush(fp);fclose(fp);fp = NULL;}#endif
}void DexHacker::writeEncodedDex2Dex(const char *encodedDex){FILE *srcDexFile=fopen(encodedDex,"rb");std::string outDexFile(encodedDex);outDexFile.append(".read.dex");FILE *outFile=fopen(outDexFile.c_str(), "wb");if (srcDexFile!=NULL) {fseek(srcDexFile,0,SEEK_END);long fsize =ftell(srcDexFile);rewind(srcDexFile);std::cout<<sizeof(char)<<"\nxxxxxxxx"<<fsize;unsigned  char *list=(unsigned char*)malloc(sizeof(char)*fsize);unsigned long  numread =fread(list,sizeof(char),fsize,srcDexFile);// blue_dump_data(list, (unsigned long )fsize);unsigned char *dst=(unsigned char*)malloc(numread);unsigned long dlen=numread;base64_decode(dst, &dlen, list, numread);fclose(srcDexFile);int ret=fwrite(dst, 1, dlen, outFile);fclose(outFile);//   std::cout<<"\n"<<repeate<<"numread"<<numread; }}char* itoa(int i, char b[]){char const digit[] = "0123456789";char* p = b;if(i<0){*p++ = '-';i *= -1;}int shifter = i;do{ //Move to where representation ends++p;shifter = shifter/10;}while(shifter);*p = '\0';do{ //Move back, inserting digits as u go*--p = digit[i%10];i = i/10;}while(i);return b;
}// 获取当前进程的名称
char * DexHacker::getProcessName(char * buffer){char path_t[256]={0};// char buffer[512]={0} ;// 获取当前进程的pidpid_t pid=getpid();char str[15];sprintf(str, "%d", pid);memset(path_t, 0 , sizeof(path_t));strcat(path_t, "/proc/");strcat(path_t, str);strcat(path_t, "/cmdline");//LOG_ERROR("zhw", "path:%s", path_t);int fd_t = open(path_t, O_RDONLY);if(fd_t>0){int read_count = read(fd_t, buffer, BUFLEN);if(read_count>0){return buffer;}}return NULL;
}


iiiiii.DexExtractor脱壳工具作者提供的文件的说明





关于DexExtractor脱壳工具的自行修改和扩展:根据自己的脱壳思路,编写额外的脱壳代码文件,添加自选Android系统版本的DexFile.cpp文件并将额外的脱壳代码文件添加到编译配置文件Android.mk中,然后在DexFile.cpp文件路径下进行android源码的局部模块的编译,具体编译参考见。


iiiiii.DexExtractor脱壳工具的其他代码文件

/** Copyright (C) 2008 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      .0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*//** Access the contents of a .dex file.*/#include "DexFile.h"
#include "DexOptData.h"
#include "DexProto.h"
#include "DexCatch.h"
#include "Leb128.h"
#include "sha1.h"
#include "ZipArchive.h"#include <zlib.h>#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "DexHacker.cpp"/** Verifying checksums is good, but it slows things down and causes us to* touch every page.  In the "optimized" world, it doesn't work at all,* because we rewrite the contents.*/
static const bool kVerifyChecksum = false;
static const bool kVerifySignature = false;/* (documented in header) */
char dexGetPrimitiveTypeDescriptorChar(PrimitiveType type) {const char* string = dexGetPrimitiveTypeDescriptor(type);return (string == NULL) ? '\0' : string[0];
}/* (documented in header) */
const char* dexGetPrimitiveTypeDescriptor(PrimitiveType type) {switch (type) {case PRIM_VOID:    return "V";case PRIM_BOOLEAN: return "Z";case PRIM_BYTE:    return "B";case PRIM_SHORT:   return "S";case PRIM_CHAR:    return "C";case PRIM_INT:     return "I";case PRIM_LONG:    return "J";case PRIM_FLOAT:   return "F";case PRIM_DOUBLE:  return "D";default:           return NULL;}return NULL;
}/* (documented in header) */
const char* dexGetBoxedTypeDescriptor(PrimitiveType type) {switch (type) {case PRIM_VOID:    return NULL;case PRIM_BOOLEAN: return "Ljava/lang/Boolean;";case PRIM_BYTE:    return "Ljava/lang/Byte;";case PRIM_SHORT:   return "Ljava/lang/Short;";case PRIM_CHAR:    return "Ljava/lang/Character;";case PRIM_INT:     return "Ljava/lang/Integer;";case PRIM_LONG:    return "Ljava/lang/Long;";case PRIM_FLOAT:   return "Ljava/lang/Float;";case PRIM_DOUBLE:  return "Ljava/lang/Double;";default:           return NULL;}
}/* (documented in header) */
PrimitiveType dexGetPrimitiveTypeFromDescriptorChar(char descriptorChar) {switch (descriptorChar) {case 'V': return PRIM_VOID;case 'Z': return PRIM_BOOLEAN;case 'B': return PRIM_BYTE;case 'S': return PRIM_SHORT;case 'C': return PRIM_CHAR;case 'I': return PRIM_INT;case 'J': return PRIM_LONG;case 'F': return PRIM_FLOAT;case 'D': return PRIM_DOUBLE;default:  return PRIM_NOT;}
}/* Return the UTF-8 encoded string with the specified string_id index,* also filling in the UTF-16 size (number of 16-bit code points).*/
const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,u4* utf16Size) {const DexStringId* pStringId = dexGetStringId(pDexFile, idx);const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;*utf16Size = readUnsignedLeb128(&ptr);return (const char*) ptr;
}/** Format an SHA-1 digest for printing.  tmpBuf must be able to hold at* least kSHA1DigestOutputLen bytes.*/
const char* dvmSHA1DigestToStr(const unsigned char digest[], char* tmpBuf);/** Compute a SHA-1 digest on a range of bytes.*/
static void dexComputeSHA1Digest(const unsigned char* data, size_t length,unsigned char digest[])
{SHA1_CTX context;SHA1Init(&context);SHA1Update(&context, data, length);SHA1Final(digest, &context);
}/** Format the SHA-1 digest into the buffer, which must be able to hold at* least kSHA1DigestOutputLen bytes.  Returns a pointer to the buffer,*/
static const char* dexSHA1DigestToStr(const unsigned char digest[],char* tmpBuf)
{static const char hexDigit[] = "0123456789abcdef";char* cp;int i;cp = tmpBuf;for (i = 0; i < kSHA1DigestLen; i++) {*cp++ = hexDigit[digest[i] >> 4];*cp++ = hexDigit[digest[i] & 0x0f];}*cp++ = '\0';assert(cp == tmpBuf + kSHA1DigestOutputLen);return tmpBuf;
}/** Compute a hash code on a UTF-8 string, for use with internal hash tables.** This may or may not be compatible with UTF-8 hash functions used inside* the Dalvik VM.** The basic "multiply by 31 and add" approach does better on class names* than most other things tried (e.g. adler32).*/
static u4 classDescriptorHash(const char* str)
{u4 hash = 1;while (*str != '\0')hash = hash * 31 + *str++;return hash;
}/** Add an entry to the class lookup table.  We hash the string and probe* until we find an open slot.*/
static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,int stringOff, int classDefOff, int* pNumProbes)
{const char* classDescriptor =(const char*) (pDexFile->baseAddr + stringOff);const DexClassDef* pClassDef =(const DexClassDef*) (pDexFile->baseAddr + classDefOff);u4 hash = classDescriptorHash(classDescriptor);int mask = pLookup->numEntries-1;int idx = hash & mask;/** Find the first empty slot.  We oversized the table, so this is* guaranteed to finish.*/int probes = 0;while (pLookup->table[idx].classDescriptorOffset != 0) {idx = (idx + 1) & mask;probes++;}//if (probes > 1)//    ALOGW("classLookupAdd: probes=%d", probes);pLookup->table[idx].classDescriptorHash = hash;pLookup->table[idx].classDescriptorOffset = stringOff;pLookup->table[idx].classDefOffset = classDefOff;*pNumProbes = probes;
}/** Create the class lookup hash table.** Returns newly-allocated storage.*/
DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
{DexClassLookup* pLookup;int allocSize;int i, numEntries;int numProbes, totalProbes, maxProbes;numProbes = totalProbes = maxProbes = 0;assert(pDexFile != NULL);/** Using a factor of 3 results in far less probing than a factor of 2,* but almost doubles the flash storage requirements for the bootstrap* DEX files.  The overall impact on class loading performance seems* to be minor.  We could probably get some performance improvement by* using a secondary hash.*/numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);allocSize = offsetof(DexClassLookup, table)+ numEntries * sizeof(pLookup->table[0]);pLookup = (DexClassLookup*) calloc(1, allocSize);if (pLookup == NULL)return NULL;pLookup->size = allocSize;pLookup->numEntries = numEntries;for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {const DexClassDef* pClassDef;const char* pString;pClassDef = dexGetClassDef(pDexFile, i);pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);classLookupAdd(pDexFile, pLookup,(u1*)pString - pDexFile->baseAddr,(u1*)pClassDef - pDexFile->baseAddr, &numProbes);if (numProbes > maxProbes)maxProbes = numProbes;totalProbes += numProbes;}ALOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"" total=%d max=%d",pDexFile->pHeader->classDefsSize, numEntries,(100 * pDexFile->pHeader->classDefsSize) / numEntries,allocSize, totalProbes, maxProbes);return pLookup;
}/** Set up the basic raw data pointers of a DexFile. This function isn't* meant for general use.*/
void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {DexHeader *pHeader = (DexHeader*) data;pDexFile->baseAddr = data;pDexFile->pHeader = pHeader;pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
}/** Parse an optimized or unoptimized .dex file sitting in memory.  This is* called after the byte-ordering and structure alignment has been fixed up.** On success, return a newly-allocated DexFile.*/
DexFile* dexFileParse(const u1* data, size_t length, int flags)
{//******************************添加的脱壳代码**************************// 构建脱壳工具实例DexHacker mDexHacker;// 从内存dump dex文件mDexHacker.writeDex2Encoded(data,(unsigned int)length);//******************************常规手动脱壳点**************************DexFile* pDexFile = NULL;const DexHeader* pHeader;const u1* magic;int result = -1;if (length < sizeof(DexHeader)) {ALOGE("too short to be a valid .dex");goto bail;      /* bad file format */}pDexFile = (DexFile*) malloc(sizeof(DexFile));if (pDexFile == NULL)goto bail;      /* alloc failure */memset(pDexFile, 0, sizeof(DexFile));/** Peel off the optimized header.*/if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {magic = data;if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {ALOGE("bad opt version (0x%02x %02x %02x %02x)",magic[4], magic[5], magic[6], magic[7]);goto bail;}pDexFile->pOptHeader = (const DexOptHeader*) data;ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);/* parse the optimized dex file tables */if (!dexParseOptData(data, length, pDexFile))goto bail;/* ignore the opt header and appended data from here on out */data += pDexFile->pOptHeader->dexOffset;length -= pDexFile->pOptHeader->dexOffset;if (pDexFile->pOptHeader->dexLength > length) {ALOGE("File truncated? stored len=%d, rem len=%d",pDexFile->pOptHeader->dexLength, (int) length);goto bail;}length = pDexFile->pOptHeader->dexLength;}dexFileSetupBasicPointers(pDexFile, data);pHeader = pDexFile->pHeader;if (!dexHasValidMagic(pHeader)) {goto bail;}/** Verify the checksum(s).  This is reasonably quick, but does require* touching every byte in the DEX file.  The base checksum changes after* byte-swapping and DEX optimization.*/if (flags & kDexParseVerifyChecksum) {u4 adler = dexComputeChecksum(pHeader);if (adler != pHeader->checksum) {ALOGE("ERROR: bad checksum (%08x vs %08x)",adler, pHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ adler32 checksum (%08x) verified", adler);}const DexOptHeader* pOptHeader = pDexFile->pOptHeader;if (pOptHeader != NULL) {adler = dexComputeOptChecksum(pOptHeader);if (adler != pOptHeader->checksum) {ALOGE("ERROR: bad opt checksum (%08x vs %08x)",adler, pOptHeader->checksum);if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ adler32 opt checksum (%08x) verified", adler);}}}/** Verify the SHA-1 digest.  (Normally we don't want to do this --* the digest is used to uniquely identify the original DEX file, and* can't be computed for verification after the DEX is byte-swapped* and optimized.)*/if (kVerifySignature) {unsigned char sha1Digest[kSHA1DigestLen];const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +kSHA1DigestLen;dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {char tmpBuf1[kSHA1DigestOutputLen];char tmpBuf2[kSHA1DigestOutputLen];ALOGE("ERROR: bad SHA1 digest (%s vs %s)",dexSHA1DigestToStr(sha1Digest, tmpBuf1),dexSHA1DigestToStr(pHeader->signature, tmpBuf2));if (!(flags & kDexParseContinueOnError))goto bail;} else {ALOGV("+++ sha1 digest verified");}}if (pHeader->fileSize != length) {ALOGE("ERROR: stored file size (%d) != expected (%d)",(int) pHeader->fileSize, (int) length);if (!(flags & kDexParseContinueOnError))goto bail;}if (pHeader->classDefsSize == 0) {ALOGE("ERROR: DEX file has no classes in it, failing");goto bail;}/** Success!*/result = 0;bail:if (result != 0 && pDexFile != NULL) {dexFileFree(pDexFile);pDexFile = NULL;}return pDexFile;
}/** Free up the DexFile and any associated data structures.** Note we may be called with a partially-initialized DexFile.*/
void dexFileFree(DexFile* pDexFile)
{if (pDexFile == NULL)return;free(pDexFile);
}/** Look up a class definition entry by descriptor.** "descriptor" should look like "Landroid/debug/Stuff;".*/
const DexClassDef* dexFindClass(const DexFile* pDexFile,const char* descriptor)
{const DexClassLookup* pLookup = pDexFile->pClassLookup;u4 hash;int idx, mask;hash = classDescriptorHash(descriptor);mask = pLookup->numEntries - 1;idx = hash & mask;/** Search until we find a matching entry or an empty slot.*/while (true) {int offset;offset = pLookup->table[idx].classDescriptorOffset;if (offset == 0)return NULL;if (pLookup->table[idx].classDescriptorHash == hash) {const char* str;str = (const char*) (pDexFile->baseAddr + offset);if (strcmp(str, descriptor) == 0) {return (const DexClassDef*)(pDexFile->baseAddr + pLookup->table[idx].classDefOffset);}}idx = (idx + 1) & mask;}
}/** Compute the DEX file checksum for a memory-mapped DEX file.*/
u4 dexComputeChecksum(const DexHeader* pHeader)
{const u1* start = (const u1*) pHeader;uLong adler = adler32(0L, Z_NULL, 0);const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);return (u4) adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
}/** Compute the size, in bytes, of a DexCode.*/
size_t dexGetDexCodeSize(const DexCode* pCode)
{/** The catch handler data is the last entry.  It has a variable number* of variable-size pieces, so we need to create an iterator.*/u4 handlersSize;u4 offset;u4 ui;if (pCode->triesSize != 0) {handlersSize = dexGetHandlersSize(pCode);offset = dexGetFirstHandlerOffset(pCode);} else {handlersSize = 0;offset = 0;}for (ui = 0; ui < handlersSize; ui++) {DexCatchIterator iterator;dexCatchIteratorInit(&iterator, pCode, offset);offset = dexCatchIteratorGetEndOffset(&iterator, pCode);}const u1* handlerData = dexGetCatchHandlerData(pCode);//ALOGD("+++ pCode=%p handlerData=%p last offset=%d",//    pCode, handlerData, offset);/* return the size of the catch handler + everything before it */return (handlerData - (u1*) pCode) + offset;
}/** Round up to the next highest power of 2.** Found on .html.*/
u4 dexRoundUpPower2(u4 val)
{val--;val |= val >> 1;val |= val >> 2;val |= val >> 4;val |= val >> 8;val |= val >> 16;val++;return val;
}

//
//  DexHacker.h
//  
//
//  Created by BunnyBlue on 6/23/15.
//
//#ifndef ____DexHacker__
#define ____DexHacker__
#include <iostream>
#include<string.h>
#include<stdio.h>
#include "base64.h"
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#define BUFLEN 1024class DexHacker{
public:void  writeDex2Encoded(unsigned char * data,size_t length);void  writeEncodedDex2Dex(const char *dexPath);char * getProcessName(char * buffer);
};
#endif /* defined(____DexHacker__) */

/*** \file base64.h** \brief RFC 1521 base64 encoding/decoding**  Copyright (C) 2006-2010, Brainspark B.V.**  This file is part of PolarSSL ()*  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl>**  All rights reserved.**  This program is free software; you can redistribute it and/or modify*  it under the terms of the GNU General Public License as published by*  the Free Software Foundation; either version 2 of the License, or*  (at your option) any later version.**  This program is distributed in the hope that it will be useful,*  but WITHOUT ANY WARRANTY; without even the implied warranty of*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the*  GNU General Public License for more details.**  You should have received a copy of the GNU General Public License along*  with this program; if not, write to the Free Software Foundation, Inc.,*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/
#define  POLARSSL_BASE64_C
#define POLARSSL_SELF_TEST
#ifndef POLARSSL_BASE64_H
#define POLARSSL_BASE64_H#include <string.h>#define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL               -0x002A  /**< Output buffer too small. */
#define POLARSSL_ERR_BASE64_INVALID_CHARACTER              -0x002C  /**< Invalid character in input. */#ifdef __cplusplus
extern "C" {
#endif/*** \brief          Encode a buffer into base64 format** \param dst      destination buffer* \param dlen     size of the buffer* \param src      source buffer* \param slen     amount of data to be encoded** \return         0 if successful, or POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL.*                 *dlen is always updated to reflect the amount*                 of data that has (or would have) been written.** \note           Call this function with *dlen = 0 to obtain the*                 required buffer size in *dlen*/
int base64_encode( unsigned char *dst, size_t *dlen,const unsigned char *src, size_t slen );/*** \brief          Decode a base64-formatted buffer** \param dst      destination buffer* \param dlen     size of the buffer* \param src      source buffer* \param slen     amount of data to be decoded** \return         0 if successful, POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL, or*                 POLARSSL_ERR_BASE64_INVALID_CHARACTER if the input data is*                 not correct. *dlen is always updated to reflect the amount*                 of data that has (or would have) been written.** \note           Call this function with *dlen = 0 to obtain the*                 required buffer size in *dlen*/
int base64_decode( unsigned char *dst, size_t *dlen,const unsigned char *src, size_t slen );int base64_self_test( int verbose );#ifdef __cplusplus
}
#endif#endif /* base64.h */

/**  RFC 1521 base64 encoding/decoding**  Copyright (C) 2006-2013, Brainspark B.V.**  This file is part of PolarSSL ()*  Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl>**  All rights reserved.**  This program is free software; you can redistribute it and/or modify*  it under the terms of the GNU General Public License as published by*  the Free Software Foundation; either version 2 of the License, or*  (at your option) any later version.**  This program is distributed in the hope that it will be useful,*  but WITHOUT ANY WARRANTY; without even the implied warranty of*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the*  GNU General Public License for more details.**  You should have received a copy of the GNU General Public License along*  with this program; if not, write to the Free Software Foundation, Inc.,*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/#define POLARSSL_BASE64_C
#define POLARSSL_SELF_TEST#if defined(POLARSSL_BASE64_C)#include "base64.h"#if defined(_MSC_VER) && !defined(EFIX64) && !defined(EFI32)
#include <basetsd.h>
typedef UINT32 uint32_t;
#else
#include <inttypes.h>
#endifstatic const unsigned char base64_enc_map[64] =
{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T','U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd','e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n','o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x','y', 'z', '0', '1', '2', '3', '4', '5', '6', '7','8', '9', '+', '/'
};static const unsigned char base64_dec_map[128] =
{127, 127, 127, 127, 127, 127, 127, 127, 127, 127,127, 127, 127, 127, 127, 127, 127, 127, 127, 127,127, 127, 127, 127, 127, 127, 127, 127, 127, 127,127, 127, 127, 127, 127, 127, 127, 127, 127, 127,127, 127, 127,  62, 127, 127, 127,  63,  52,  53,54,  55,  56,  57,  58,  59,  60,  61, 127, 127,127,  64, 127, 127, 127,   0,   1,   2,   3,   4,5,   6,   7,   8,   9,  10,  11,  12,  13,  14,15,  16,  17,  18,  19,  20,  21,  22,  23,  24,25, 127, 127, 127, 127, 127, 127,  26,  27,  28,29,  30,  31,  32,  33,  34,  35,  36,  37,  38,39,  40,  41,  42,  43,  44,  45,  46,  47,  48,49,  50,  51, 127, 127, 127, 127, 127
};/** Encode a buffer into base64 format*/
int base64_encode( unsigned char *dst, size_t *dlen,const unsigned char *src, size_t slen )
{size_t i, n;int C1, C2, C3;unsigned char *p;if( slen == 0 )return( 0 );n = (slen << 3) / 6;switch( (slen << 3) - (n * 6) ){case  2: n += 3; break;case  4: n += 2; break;default: break;}if( *dlen < n + 1 ){*dlen = n + 1;return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL );}n = (slen / 3) * 3;for( i = 0, p = dst; i < n; i += 3 ){C1 = *src++;C2 = *src++;C3 = *src++;*p++ = base64_enc_map[(C1 >> 2) & 0x3F];*p++ = base64_enc_map[(((C1 &  3) << 4) + (C2 >> 4)) & 0x3F];*p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];*p++ = base64_enc_map[C3 & 0x3F];}if( i < slen ){C1 = *src++;C2 = ((i + 1) < slen) ? *src++ : 0;*p++ = base64_enc_map[(C1 >> 2) & 0x3F];*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];if( (i + 1) < slen )*p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];else *p++ = '=';*p++ = '=';}*dlen = p - dst;*p = 0;return( 0 );
}/** Decode a base64-formatted buffer*/
int base64_decode( unsigned char *dst, size_t *dlen,const unsigned char *src, size_t slen )
{size_t i, n;uint32_t j, x;unsigned char *p;for( i = n = j = 0; i < slen; i++ ){if( ( slen - i ) >= 2 &&src[i] == '\r' && src[i + 1] == '\n' )continue;if( src[i] == '\n' )continue;if( src[i] == '=' && ++j > 2 )return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );if( src[i] > 127 || base64_dec_map[src[i]] == 127 )return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );if( base64_dec_map[src[i]] < 64 && j != 0 )return( POLARSSL_ERR_BASE64_INVALID_CHARACTER );n++;}if( n == 0 )return( 0 );n = ((n * 6) + 7) >> 3;if( dst == NULL || *dlen < n ){*dlen = n;return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL );}for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ){if( *src == '\r' || *src == '\n' )continue;j -= ( base64_dec_map[*src] == 64 );x  = (x << 6) | ( base64_dec_map[*src] & 0x3F );if( ++n == 4 ){n = 0;if( j > 0 ) *p++ = (unsigned char)( x >> 16 );if( j > 1 ) *p++ = (unsigned char)( x >>  8 );if( j > 2 ) *p++ = (unsigned char)( x       );}}*dlen = p - dst;return( 0 );
}#if defined(POLARSSL_SELF_TEST)#include <string.h>
#include <stdio.h>static const unsigned char base64_test_dec[64] =
{0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
};static const unsigned char base64_test_enc[] ="JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK""swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";/** Checkup routine*/
int base64_self_test( int verbose )
{size_t len;const unsigned char *src;unsigned char buffer[128];if( verbose != 0 )printf( "  Base64 encoding test: " );len = sizeof( buffer );src = base64_test_dec;if( base64_encode( buffer, &len, src, 64 ) != 0 ||memcmp( base64_test_enc, buffer, 88 ) != 0 ){if( verbose != 0 )printf( "failed\n" );return( 1 );}if( verbose != 0 )printf( "passed\n  Base64 decoding test: " );len = sizeof( buffer );src = base64_test_enc;if( base64_decode( buffer, &len, src, 88 ) != 0 ||memcmp( base64_test_dec, buffer, 64 ) != 0 ){if( verbose != 0 )printf( "failed\n" );return( 1 );}if( verbose != 0 )printf( "passed\n\n" );return( 0 );
}#endif#endif

# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      .0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.LOCAL_PATH:= $(call my-dir)dex_src_files := \CmdUtils.cpp \DexCatch.cpp \DexClass.cpp \DexDataMap.cpp \DexDebugInfo.cpp \DexFile.cpp \DexInlines.cpp \DexOptData.cpp \DexOpcodes.cpp \DexProto.cpp \DexSwapVerify.cpp \DexUtf.cpp \InstrUtils.cpp \Leb128.cpp \OptInvocation.cpp \sha1.cpp \SysUtil.cpp \ZipArchive.cpp\base64.cdex_include_files := \dalvik \external/zlib \external/safe-iop/include##
##
## Build the device version of libdex
##
##
ifneq ($(SDK_ONLY),true)  # SDK_only doesn't need device versioninclude $(CLEAR_VARS)
LOCAL_CFLAGS += -DCODE_DVM -fpermissive
LOCAL_CXXFLAGS += -DCODE_DVM -fpermissive
LOCAL_CPPFLAGS += -DCODE_DVM -fpermissive
#LOCAL_CFLAGS += -UNDEBUG -DDEBUG=1
LOCAL_SRC_FILES := $(dex_src_files)
LOCAL_C_INCLUDES += $(dex_include_files)
LOCAL_STATIC_LIBRARIES := liblog
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libdex
include $(BUILD_STATIC_LIBRARY)endif # !SDK_ONLY##
##
## Build the host version of libdex
##
##
include $(CLEAR_VARS)
LOCAL_CFLAGS += -DCODE_DVM
LOCAL_CXXFLAGS += -DCODE_DVM -fpermissive
LOCAL_CPPFLAGS += -DCODE_DVM -fpermissive
LOCAL_SRC_FILES := $(dex_src_files)
LOCAL_C_INCLUDES += $(dex_include_files)
LOCAL_STATIC_LIBRARIES := liblog
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libdex
include $(BUILD_HOST_STATIC_LIBRARY)

好啦,忙了一下午,一点点的积累和进步.........





本文标签: DexExtractor的原理分析和使用说明