社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

用python一步步解剖dex文件(五)--- odex格式

金正皓 • 7 年前 • 860 次点击  

odex文件是dalvik虚拟机(android 5.0以前的版本),为了优化dex的使用(如提高查找类的速度,gc等)而实现的一种格式。

它针对特定手机,在应用安装的时候进行优化,不同手机的odex文件可能是不同的;所以dex是一种通用虚拟机执行格式,而odex则不是。

获取的方式如下: (真机需要root,我是直接用的虚拟设备)

存放目录:   /data/dalvik-cache

获取命令: adb pull /data/dalvik-cache/data@app@com.hello.testmodifydex-2.apk@classes.dex ~/Desktop/classes.odex

本文对odex文件的分析代码:

github.com/callmejacob…


总格式

odex的格式分为四部分,依次是:

1. 头部结构

2. dex原文件(有部分更改)

3. 依赖目录结构

4. 优化数据部分


头部结构

头部结构中,记录的剩余三部分的偏移量和字节总大小; 根据这个信息可以解析出其余部分。

头部结构 头部解析dex原文件

dex原文件

使用之前的DexInfo解析类,把[dex_off, dex_off+dex_size]部分的字节数组解析了。

参考: 用python一步步解剖dex文件(三)

dex解析

这个部分的dex文件能够正常解析,但其实已经被修改过了。

我们提取这一部分的字节数组后直接保存为新的dex文件,对比优化前的dex文件,发现有两部分发生了改变,数据分别定位在class_def段和code段。

对比class_def段

dex对比(class_def_section)

上面的这一部分是class_def段,其中更改的内容是access_flags,原本为0x00000011(public final),更改后变为0x00030011,其中的0x00030000是不存在的,对照:

source.android.google.cn/devices/tec…

也就是说,这是dalvik虚拟机在优化dex时候加入的特有标志。

对比code段

dex对比(code_section)

解析后的code段信息对比如下:

dex指令对比

这里可以清楚的看到,code段的指令字节码部分被更改了,总大小不变。


依赖目录结构

这个部分记录了一些校验信息和一个依赖文件的列表。

依赖目录结构

这个列表中的每一项包含了依赖项的路径信息和对应文件的sha1值。

依赖目录子结构

解析如下:

依赖目录解码

优化数据部分

这一部分包含一个chunk列表,其中chunk只有三种类型,以end为结束,如下:

chunk块类型和描述

每个chunk段的前8个字节记录了[type, size]信息,紧接着就是chunk的数据信息。

chunk块总解析

chunk块(class_lookup)

类索引块的主要作用是能够通过它快速查找到类的信息。

它的前8个字节,记录了[total_size, item_size]信息,就是该chunk块占用的总字节数和子项列表的总个数。

chunk块(class_lookup)

这个子项列表中的每一项,就包含着对类信息的快速索引记录,如hash值,名称偏移量,类定义偏移量。

class_lookup子项

虚拟机查找类的时候,会通过类名形成一个hash数值,然后通过hash数值快速的碰撞检测,获取到助记信息(name_off, class_def_off),达到快速找到类的目的。

chunk块(register_map)

这个块的主要作用是记录了所有函数的指令数据中,相关寄存器是否引用对象的状态信息。

垃圾回收机制,会利用到以上的信息,快速的定位对象的被引用情况。

那么它的结构也是相应的,要包含所有类的所有函数的所有指令的寄存器映射状态。(其中指令需要gc安全点的)

这里就是三重结构: class列表  --->  method列表  -->  指令列表

register_map总结构:

前4个字节表示列表总个数item_size;

接着是一个偏移表,大小为item_size,每一项的数值代表类信息项的偏移;

最后就是类信息列表,大小也是item_size。

chunk块(寄存器映射)

类信息项

前4个字节是总个数item_size;

接着是一个函数信息项的列表,个数为item_size。

register_class_item

函数信息项

这里涉及的信息其实有两个,一个是指令字节数组的总个数,一个是寄存器的总个数。

根据上面的两个个数信息,决定寄存器映射表信息的记录方式,比如能用一个字节的就用一个字节表示,最多不超过2个字节。

这个结构中的第一个字节表示格式信息,这个格式就决定了记录指令索引时用一个字节还是两个;

第二个字节表示记录寄存器状态需要的字节数,因为每一位记录一个寄存器是否为对象应用的状态,所以8个以内就可以用一个字节表示,8个以上就必须用2个字节了。

第三和第四个字节,记录了总映射信息的个数,表示为item_size。

最后就是映射信息的列表。

register_method_item

其中上述的指令格式枚举如下:

format

最后这个结构,就是函数中每一个寄存器映射项的结构。

它的第一部分表示当前指令的索引号,根据之前的format格式决定是占用一个字节还是两个。

第二部分就是寄存器的状态,根据之前的reg_width决定是占用一个字节还是两个。

寄存器映射子结构

测试程序

测试程序

打印头部信息

odex-header

打印依赖目录信息

odex-deps

打印优化数据信息

chunk块(class_look_up)

odex-class-lookup

chunk块(end)

chunk(end)

参考文章:

www.aliyun.com/jiaocheng/5…

blog.csdn.net/roland_sun/…

blog.csdn.net/roland_sun/…

blog.csdn.net/roland_sun/…



请勿转载,谢谢! 

欢迎关注微信公众号:  一叶谷


今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/q9ywmXcqjZ
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/7305
 
860 次点击