android apk 防止反编译技术第二篇-运行时修改Dalvik指令
上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372。接下来我们将介绍另一种防止apk反编译的技术-运行时修改字节码。这种方法是在工作中在实现app wrapping时,看到国外的一篇关于android 安全的介绍实现的并且独创。下面我们来介绍一下这种方法。
我们知道apk生成后所有的java生成的class文件都被dx命令整合成了一个classes.dex文件,当apk运行时dalvik虚拟机加载classes.dex文件并且用dexopt命令进行进一步的优化成odex文件。我们的方法就是在这个过程中修改dalvik指令来达到我们的目的。
一、dex文件格式
dex的文件格式通常有7个主要部分和数据區组成,格式如下:

header部分记录了主要的信息其他的部分只是索引,索引的内容存在data区域。
Header部分结构如下:
|
字段名称 |
偏移值 |
长度 |
描述 |
|
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数组,从而实现我们的目的。
二、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的部分即可以了。
三、方法实现
要实现修改字节码,就需要先定位到想要修改得代码的位置,这就需要先解析dex文件。dex文件的解析在dalvik源码的dexDump.cpp给出了我们具体的实现,根据它的实现我们可以查找我们需要的类及方法。具体实现步骤如下:
(1) 找到我们apk生成的odex文件,获得odex文件在内存中的映射地址和大小。实现代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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文件。代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
|
// 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结构体。函数实现如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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后就可以进行指令替换了。实现如下:
|
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
26
27
|
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即可关注,我们可以在线交流。

android apk 防止反编译技术第二篇-运行时修改Dalvik指令的更多相关文章
- android apk 防止反编译技术第二篇-运行时修改字节码
上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...
- android apk 防止反编译技术第一篇-加壳技术
做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习.现在将最近学习成果做一下整理总结.学习的这些成 ...
- android apk 防止反编译技术第四篇-对抗JD-GUI
又到周末一个人侘在家里无事可干,这就是程序员的悲哀啊.好了我们利用周末的时间继续介绍android apk防止反编译技术的另一种方法.前三篇我们讲了加壳技术(http://my.oschina.net ...
- android apk 防止反编译技术第三篇-加密
上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...
- 转: android apk 防止反编译技术(1~5连载)
转: android apk 防止反编译技术 做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习. ...
- APK防反编译技术
APK防反编译技术 下载地址:地址 我们的APK实际上就是一个ZIP压缩文件,里面包括有一个classes.dex.我们编译后生成的程序代码就所有在那里了, 通过apktool等工具能够轻松地将它们反 ...
- Android Apk的反编译和加密
这几天在上海出差,忙里偷闲学习了一下Apk的反编译工具的基本使用.下面就简单介绍一下如何将我们从网上下载的Apk文件进行反编译得到我们想要获得的资源文件和源码. Android的应用程序APK文件说到 ...
- Android Apk的反编译与代码混淆
一.反编译 1.获取工具: 既然是反编译,肯定要用到一些相关的工具,工具可以到这里下载,里面包含三个文件夹,用于反编译,查看反编译之后的代码: 其实这两工具都是google官方出的,也可在google ...
- android apk 文件反编译
最近,自己坑逼的把一个android 项目修改版本的代码删除了.这个项目居然还没上传到源代码管理器.幸好还有apk文件,修改的代码也不多可以反编译一下. 1.下载 dex2jar 获取源码工具 地 ...
随机推荐
- java 操作clob
之前在学校的时候做的都是练习,小儿科,遇到的情况完全都在自己的设想范围内.最近老是遇到字段溢出的情况,但是varchar2好像最长也只有4000个字符.所以不得不另辟蹊径,就找上了clob字段. pa ...
- Apache服务器防范DoS
Apache服务器对拒绝服务攻击的防范主要通过软件Apache DoS Evasive Maneuvers Module 来实现.它是一款mod_access的替代软件,可以对抗DoS攻击.该软件可 ...
- Js配置资料下载
1.使用windows.loaction.href链接下载: 此种下载在本页打开,eg:windows.location.href = http://www.xxx.xx/aa.apk; 2.使用wi ...
- dubbo之主机绑定
主机绑定 查找顺序 缺省主机 IP 查找顺序: 通过 LocalHost.getLocalHost() 获取本机地址. 如果是 127.* 等 loopback 地址,则扫描各网卡,获取网卡 IP. ...
- Java程序员2016年终总结
回顾2016年, 很庆幸,自己能在2016年年尾找到一份满意的web后台开发工作.这也是我学习编程以来第一份开发工作,我很是珍惜. 还记得大三接触了Java的JFrame编写的坦克大战之后,就对编程产 ...
- HDU_1079_思维题
Calendar Game Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- 戴尔14G服务器用H740P配置阵列
公司采购了几台dell r740机器.做阵列的方式跟之前ctrl+r有很大改动. 戴尔14G机器已经面世一段时间了,14G的机器使用过后都能发现器性能比上一代机器提升了很多,今天给大家带来戴尔14代服 ...
- MATLAB仿真学习笔记(一)
一.Simulink概述 1.特点 simulink是对动态系统进行建模.仿真和综合分析的图形化软件,可以处理线性和非线性.离散.连续和混合系统,也可以处理单任务和多任务系统,并支持多种采样频率的系统 ...
- 1.Linux入门介绍
1.1 Linux概述 1.1.1 Linux简要介绍 Linux的由来: Linux的内核最初是由芬兰人李纳斯·托瓦茨在上大学的时候编写的一个内核,它是基于Unix操作系统编写的 大多服务器使用的是 ...
- 使用final关键字修饰一个引用类型变量时,是引用不能变,还是引用的对象不能变?
使用final关键字修饰一个引用类型变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的. 测试代码如下: package reviewTest; /** * @ClassName: ...