上一篇咱们讲了apk防止反编译技术中的加壳技术,若是有不明白的能够查看个人上一篇博客http://my.oschina.net/u/2323218/blog/393372。接下来咱们将介绍另外一种防止apk反编译的技术-运行时修改字节码。这种方法是在工做中在实现app wrapping时,看到国外的一篇关于android 安全的介绍实现的而且首创。下面咱们来介绍一下这种方法。html
咱们知道apk生成后全部的java生成的class文件都被dx命令整合成了一个classes.dex文件,当apk运行时dalvik虚拟机加载classes.dex文件而且用dexopt命令进行进一步的优化成odex文件。咱们的方法就是在这个过程当中修改dalvik指令来达到咱们的目的。java
1、dex文件格式android
dex的文件格式一般有7个主要部分和数据區组成,格式以下:程序员
header部分记录了主要的信息其余的部分只是索引,索引的内容存在data区域。数组
Header部分结构以下:安全
字段名称微信 |
偏移值app |
长度微信公众平台 |
描述函数 |
magic |
0x0 |
8 |
'Magic'值,即魔数字段,格式如”dex/n035/0”,其中的035表示结构的版本。 |
checksum |
0x8 |
4 |
校验码。 |
signature |
0xC |
20 |
SHA-1签名。 |
file_size |
0x20 |
4 |
Dex文件的总长度。 |
header_size |
0x24 |
4 |
文件头长度,009版本=0x5C,035版本=0x70。 |
endian_tag |
0x28 |
4 |
标识字节顺序的常量,根据这个常量能够判断文件是否交换了字节顺序,缺省状况下=0x78563412。 |
link_size |
0x2C |
4 |
链接段的大小,若是为0就表示是静态链接。 |
link_off |
0x30 |
4 |
链接段的开始位置,从本文件头开始算起。若是链接段的大小为0,这里也是0。 |
map_off |
0x34 |
4 |
map数据基地址。 |
string_ids_size |
0x38 |
4 |
字符串列表的字符串个数。 |
string_ids_off |
0x3C |
4 |
字符串列表表基地址。 |
type_ids_size |
0x40 |
4 |
类型列表里类型个数。 |
type_ids_off |
0x44 |
4 |
类型列表基地址。 |
proto_ids_size |
0x48 |
4 |
原型列表里原型个数。 |
proto_ids_off |
0x4C |
4 |
原型列表基地址。 |
field_ids_size |
0x50 |
4 |
字段列表里字段个数。 |
field_ids_off |
0x54 |
4 |
字段列表基地址。 |
method_ids_size |
0x58 |
4 |
方法列表里方法个数。 |
method_ids_off |
0x5C |
4 |
方法列表基地址。 |
class_defs_size |
0x60 |
4 |
类定义类表中类的个数。 |
class_defs_off |
0x64 |
4 |
类定义列表基地址。 |
data_size |
0x68 |
4 |
数据段的大小,必须以4字节对齐。 |
data_off |
0x6C |
4 |
数据段基地址 |
dex与class文件相比的一个优点,就是将全部的常量字符串集统一管理起来了,这样就能够减小冗余,最终的dex文件size也能变小一些。详细的dex文件介绍就不说了,有兴趣的能够查看android 源码dalvik/docs目录下的dex-format.html文件有详细介绍。不过我记得在android4.0版本后就没有了这个文件。
根据上面的dex文件的格式结构,dalvik虚拟机运行dex文件执行的字节码就存在method_ids区域里面。咱们查看dalvik虚拟机源码会有一个
struct DexCode {
u2 registersSize;
u2 insSize;
u2 outsSize;
u2 triesSize;
u4 debugInfoOff; /* file offset to debug info stream */
u4 insnsSize; /* size of the insns array, in u2 units */
u2 insns[1];
/* followed by optional u2 padding */
/* followed by try_item[triesSize] */
/* followed by uleb128 handlersSize */
/* followed by catch_handler_item[handlersSize] */
};
这样一个结构,这里的insns数组存放的就是dalvik的字节码。咱们只要定位到相关类方法的DexCode数据段,便可经过修改insns数组,从而实现咱们的目的。
2、odex文件格式
apk安装或启动时,会经过dexopt来将dex生成优化的odex文件。过程是将apk中的classes.dex解压后,用dexopt处理并保存为/data/dalvik-cache/data@app@<package-name>-X.apk@classes.dex文件。
odex文件结构以下:
从上图中咱们发现dex文件做为优化后的odex的一部分,咱们只须要从odex中找出dex的部分便可以了。
3、方法实现
要实现修改字节码,就须要先定位到想要修改得代码的位置,这就须要先解析dex文件。dex文件的解析在dalvik源码的dexDump.cpp给出了咱们具体的实现,根据它的实现咱们能够查找咱们须要的类及方法。具体实现步骤以下:
(1) 找到咱们apk生成的odex文件,得到odex文件在内存中的映射地址和大小。实现代码以下:
void *base = NULL; int module_size = 0; char filename[512]; // simple test code here! for(int i=0; i<2; i++){ sprintf(filename,"/data/dalvik-cache/data@app@%s-%d.apk@classes.dex", "com.android.dex", i+1); base = get_module_base(-1, filename);//得到odex文件在内存中的映射地址 if(base != NULL){ break; } } module_size = get_module_size(-1, filename); //得到odex文件大小
(2) 知道dex文件在odex中的偏移,以便解析dex文件。代码以下:
// search dex from odex void *dexBase = searchDexStart(base); if(checkDexMagic(dexBase) == false){ ALOGE("Error! invalid dex format at: %p", dexBase); return; }
(3) 找到dex偏移之后就能够解析dex文件,从而查找咱们要进行替换的方法所在的类,而后在该类中找到该方法并返回该方法对应的DexCode结构体。函数实现以下:
static const DexCode *dexFindClassMethod(DexFile *dexFile, const char *clazz, const char *method) { DexClassData* classData = dexFindClassData(dexFile, clazz); if(classData == NULL) return NULL; const DexCode* code = dexFindMethodInsns(dexFile, classData, method); if(code != NULL) { dumpDexCode(code); } return code; }
(4) 找到DexCode后就能够进行指令替换了。实现以下:
const DexCode *code = dexFindClassMethod(&gDexFile, "Lcom/android/dex/myclass;", "setflagHidden"); const DexCode*code2 = dexFindClassMethod(&gDexFile, "Lcom/android/dex/myclass;", "setflag"); // remap!!!! if(mprotect(base, module_size, PROT_READ | PROT_WRITE | PROT_EXEC) == 0){ DexCode *pCode = (DexCode *)code2; // Modify! pCode->registersSize = code->registersSize; for(u4 k=0; k<code->insnsSize; k++){ pCode->insns[k] = code->insns[k]; } mprotect(base, module_size, PROT_READ | PROT_EXEC); }
注意:因为是在运行时修改的dalvik指令,这是进程的内存映射为只读的,因此须要调用mprotect函数将只读改成读写才能进行指令的修改。
根据上面的讲述相信你们对运行时修改字节码的技术有了必定的了解,下一篇咱们将讲解另外一种android apk防止反编译技术,期待你们的捧场。若是对这篇讲的技术有任何疑问及想要得到这篇文章讲的技术的工程源码
欢迎关注我的微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或搜索微信号coder_online便可关注,咱们能够在线交流。