[原创]分享一个快速加载dex文件的方法

2015-11-7 13:12

23842

[原创]分享一个快速加载dex文件的方法

2015-11-7 13:12

23842

在Android系统下有一个DexClassLoader类,可以动态加载dex文件,但是,这个类有一个缺陷,就是第一次启动并加载一个dex文件时,会执行一次dex2oat或 dexopt操作,正常情况下不会感觉到它的不足,但是如果将它用于加固,就会出现第一次启动时间特别长的问题,为此可以使用下面的方法去提升第一次启动的速度:

首先是art模式下,art模式下支持解释执行dex文件,不需要编译,也不需要oat文件,所以直接hook execv函数,让调用dex2oat的进程直接退出就好,

hook回调函数代码如下:

int hook_execv(const char *name, char **argv) {

int ret = 0;

char tmp[512];

if (!IsDex2oat(name)) {

return ori_execv(name, argv);

}

exit(0);

}

hook的方式是got hook,代码如下:

void* GotHook(soinfo *si, char *Target_Name, void* Hook_Callback) {

Elf32_Rel *rel = (Elf32_Rel*)si->plt_rela;

int count = si->plt_rela_count;

void *Target_Addr = 0;

int idx = 0;

for (idx = 0; idx

unsigned type = ELF32_R_TYPE(rel->r_info);

unsigned sym = ELF32_R_SYM(rel->r_info);

Elf32_Addr reloc = (Elf32_Addr)(rel->r_offset + (u4)si->base);

Elf32_Addr sym_addr = 0;

const char* sym_name = 0;

if (type == 0) {

continue;

}

Elf32_Sym *s = 0;

struct soinfo_mine *lsi = 0;

if (sym != 0) {

sym_name = get_string(((Elf32_Sym*)si->symtab)[sym].st_name, si);

printf("%d sym_name=%s\n", idx, sym_name);

if (strcmp(sym_name, Target_Name))

continue;

printf("%d sym_name=%s\n", idx, sym_name);

Target_Addr = *(void**)reloc;

if (Hook_Callback != 0) {

Elf32_Addr seg_page_start = PAGE_START(reloc);

Elf32_Addr seg_page_end = PAGE_END(reloc + 4);

errno=0;

int ret = mprotect((void*)seg_page_start, seg_page_end-seg_page_start,

PROT_READ | PROT_WRITE);

*(void**)reloc = Hook_Callback;

}

}

}

return Target_Addr;

}

int JNI_onLoad() {

libart = (soinfo*)dlopen("libart.so", RTLD_NOW);

ori_execv = (fnexecv)GotHook((soinfo*)libart, "execv", (void*)hook_execv);

}

其中使用到的结构体,可以从android的linker的源码里拿取。

在dalvik虚拟机下,正常的情况下需要执行一个dexopt的过程,但同时,dalvik虚拟机里,也支持一种从内存里直接加载dex文件的字节码的办法:

http://androidxref.com/4.4.2_r2/xref/dalvik/vm/native/dalvik_system_DexFile.cpp#249,使用这个函数可以秒加载内存中的dex文件。

但是可悲的是这个函数并没有导出,我们无法直接去调用。

为此有两种解决办法:

1、        使用函数dvmLookupInternalNativeMethod去查找Dalvik_dalvik_system_DexFile_openDexFile_bytearray的地址。

2、        用Dalvik_dalvik_system_DexFile_openDexFile_bytearray内部使用的已导出的函数去重写一个自己的my_Dalvik_dalvik_system_DexFile_openDexFile_bytearray函数。

为了使这种快速加载技术支持2.x的版本,所以最好使用的是第2种方法。

void* LoadByte(JNIEnv *env, u1 *byDexFile, int len) {

jbyte *bytes = (jbyte*)byDexFile;

u1 *pnc = (u1*)bytes;

RawDexFile *pRawDexFile = (RawDexFile*)malloc(sizeof(RawDexFile));

DvmDex *pDvmDex = NULL;

void* pClassLookup = NULL;

LOGI2("dvmDexFileOpenPartial_ptr=%p %p\n", dvmDexFileOpenPartial_ptr, dexCreateClassLookup_ptr);

dvmDexFileOpenPartial_ptr(pnc, len, &pDvmDex);

pClassLookup = (void*)dexCreateClassLookup_ptr(pDvmDex->pDexFile);

LOGI2("pDvmDex=%p pDvmDex->pDexFile=%p\n", pDvmDex, pDvmDex->pDexFile);

pDvmDex->pDexFile->pClassLookup = pClassLookup;

pRawDexFile->pDvmDex = pDvmDex;

DexOrJar *pd = (DexOrJar*)malloc(sizeof(DexOrJar));

LOGI2("pnc=%p\n", pnc);

pd->isDex = true;

pd->pRawDexFile = pRawDexFile;

pd->pDexMemory = pnc;

pd->filename = strdup("");

void *libdvm = dlopen("libdvm.so", RTLD_NOW);

unsigned addrOfgDvm = (unsigned)dlsym(libdvm, "gDvm") + 0x330;

fndvmHashTableLookup addrOfdvmHashTableLookup = (fndvmHashTableLookup)dlsym(libdvm, "_Z18dvmHashTableLookupP9HashTablejPvPFiPKvS3_Eb");

//int result = dvmHashTableLookup_ptr(addrOfgDvm, (int)pd, (int)pd, hashcmpDexOrJar, true);

LOGI2("result=%p\n", pd);

if (old_DvmDex == NULL)

old_DvmDex = pDvmDex;

return pd;

}

成功调用上面的两种内存加载dex文件方法中的任意一种后,得到的返回值是一个DexOrJar结构体的指针。这个指针就是http://androidxref.com/4.4.2_r2/xref/libcore/dalvik/src/main/java/dalvik/system/DexFile.java#37这里的mCookie。

得到mCookie后,该怎么将它的值传给DexFile呢?

我使用的方法是hook openDexFileNative/openDexFile函数。

这两个函数没有导出,但它们是一个jni函数,hook的办法是替换这两个方法的Method结构体的里的回调函数。

这两个JNI方法与一般的JNI方法不同,它们是内部JNI,因此,它的回调函数保存在这两个位置之中的一个:

1、          ((u4**)Method->insns)[10];

2、        Method->nativeFunc(当Method->insns==0时)

Hook代码:

addr = (u4**)openDexFileNative_med->insns;

if (openDexFileNative_med->insns == 0) {

Dalvik_dalvik_system_DexFile_openDexFileNative_ptr = (Dalvik_dalvik_system_DexFile_openDexFileNative_func)openDexFileNative_med->nativeFunc;

openDexFileNative_med->nativeFunc = (u4)Dalvik_dalvik_system_DexFile_my_openDexFileNative;

}

else {

Dalvik_dalvik_system_DexFile_openDexFileNative_ptr = (Dalvik_dalvik_system_DexFile_openDexFileNative_func)addr[10];

addr[10] = (u4*)Dalvik_dalvik_system_DexFile_my_openDexFileNative;

}

至此,总结一下dalvik下加速启动的过程:

1、        调用DexClassLoader,加载一个payload.dex文件

2、        DexClassLoader调用openDexFileNative函数

3、        由于openDexFileNative被hook了,实际调用的是my_openDexFileNative

4、        my_openDexFileNative里调用my_Dalvik_dalvik_system_DexFile_openDexFile_bytearray加载内存中的dex文件,并将结果返回

5、        成功实现秒加载payload.dex文件。

不知道怎么插入图片,很抱歉,先写原理,过些时间,放出源码。

爱讨论的加个群456853837,补个群号282215163

android加载dex方法,[原创]分享一个快速加载dex文件的方法相关推荐

  1. pythonweb毕业设计-[分享]我发现了一个快速完成物联网毕业设计的好方法!

    原标题:[分享]我发现了一个快速完成物联网毕业设计的好方法! 对于计算机相关专业的毕业生来说,毕业论文真的是一件特别令人头疼的事情,当然学霸除外-- 毕设.编程--每每想到这里!是不是很想原地爆炸?! ...

  2. 分享一个日文图片文字识别在线的方法

    对于一些从事贸易的朋友,尤其是一些从事国际贸易的朋友,往往需要整理很多外语资料.最近就有个小伙伴说他刚从事与一些日本企业合作,刚开始每天都需要整理大量的日文信息,但人工整理效率太低,但是如果有可以识别 ...

  3. 分享一个好用的照片格式转换器方法

    如今信息技术的发展非常迅速,我们可以通过使用计算机来完成大部分工作.以前我们要查看图片,一般是打印的形式,现在多是在屏幕上查看图片.但是电脑图片其实有很多种格式,每种格式都有自己的特点和应用场景.有时 ...

  4. Android NDK编译中在libs\armeabi中加入第三方so库文件的方法

    Android NDK编译中在libs\armeabi中加入第三方so库文件的方法 假设要加入库文件的名字为libffmpeg.so文件 1.要在project\jni目录下新建一目录prebuilt ...

  5. Java黑皮书课后题第6章:6.9(英尺和米之间的转换)编写一个类,包含如下两个方法:编写一个测试程序,调用这两个方法以显示下面的表格

    6.9(英尺和米之间的转换)编写一个类,包含如下两个方法:编写一个测试程序,调用这两个方法以显示下面的表格 题目 题目概述与运行示例 破题 代码(本题) 题目 题目概述与运行示例 6.9(英尺和米之间 ...

  6. cmd窗口快速定位到具体文件夹方法

    在用Python进行机器实战时,打开cmd窗口后,总是到定位到kNN.py所在文件夹才能Python(否则import kNN失败),每次都要输入地址非常麻烦 这里介绍一个cmd窗口快速定位到具体文件 ...

  7. glob php,php使用glob函数快速查询指定目录文件的方法

    本文实例讲述了php使用glob函数快速查询指定目录文件的方法.分享给大家供大家参考.具体如下: php搜索当前目录所有文件,代码如下: $array = glob('*.*'); print_r($ ...

  8. android 禁用dlsym_[原创] 分享一个最近新撸增强版的绕过Android/iOS的dlopen/dlsym限制的dlfunctions库...

    简介 byOpen是一个绕过移动端系统限制的dlopen库. 支持特性 Android 支持App中加载和使用Android系统库接口(即使maps中还没有被加载也支持). Android 7以上dl ...

  9. 在一个html加载多个echarts,Echarts一个页面加载多个图表及图表自适应

    Echarts一个页面加载多个图表及图表自适应 模块化加载 //入口 require.config({ paths: { echarts: 'http://echarts.baidu.com/buil ...

  10. 分享一个快速下载百度网盘资源的方法

    百度网盘非会员下载限制速度太厉害,网上有大牛总结了破解方法.这我也写一篇破解方法,就是用PanDownload下载来破解非会员限速.PanDownload的官网地址是http://pandownloa ...

最新文章

  1. java 重定向和转发 的区别
  2. pycharm Debug问题
  3. 建筑学跨专业计算机考研方向,不适合女生报考的考研专业你知道几个?
  4. 2008安装完了找不到_7206BEP.进口轴承_玉溪SKF轴承安装指南
  5. 第20章 TCP的成块数据流
  6. [题解] [AHOI2009] 跳棋
  7. 今天起改用mac的marsedit写博
  8. AEJoy —— 表达式之通过 sampleImage() 营造遮蔽效果【JS】
  9. js刻度尺插件_html5 canvas+js刻度尺代码
  10. 新评论接口——京东评论接口
  11. python 弱引用
  12. 牛客小bai月赛39 F 孤独(dp)
  13. 网页pdf打印——window.print()
  14. 使用DevExpress的PdfViewer控件加载http传输文件
  15. 4 Bootstrap4组件——徽章(Badges)
  16. 开通微信零钱通的方法微信免手续费提现
  17. 恒生电子软件测试岗实习生面试(一对一)
  18. 1.3 人工智能产业发展
  19. ecmall 连接mysql服务器失败_ecmall ECMall的MySQL数据库调用
  20. 暴风影音——去除广告的方法

热门文章

  1. 北航计算机学院博士开题,【北航毕设开题报告】北航博士开题报告格式.doc
  2. java mina框架实例_MINA框架简介和一个简单的例子
  3. 斐讯k2php环境,斐讯K2 刷华硕固件 实现 单线复用(网络、IPTV走一根网线)
  4. 斐讯K2P B1 博通TTL刷机方法
  5. 【MMD动作下载】随心所欲mercy(Kimagure Mercy)
  6. java中.rtf文件变成文本文件
  7. Java怎么学?分享6个学习窍门
  8. world模板生成ftl文件
  9. Eclipse JEE Mars 2 配置 WSO2 ESB 5.0.0 环境
  10. hysys动态模拟教程_学习记录-过程模拟实训-Aspen HYSYS教程