吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3163|回复: 7
收起左侧

[Android 原创] dex文件格式终极篇------class_ids解析

  [复制链接]
紫诺不离 发表于 2020-6-24 22:27
本帖最后由 紫诺不离 于 2020-6-25 10:08 编辑

授人与鱼不如授人与渔,作为初学者,最重要的是学会查看官方文档,自主学习。

首先放上google官方文档对dex解释的链接,里面相当详细的介绍了dex的格式的组成。

https://source.android.google.cn/devices/tech/dalvik/dex-format#header-item

首先声明一点,因为class_ids的内部定义很复杂,层层嵌套,这里我只研究了一部分,剩下的就要靠你们自己去研究了。

class_def_item结构

class_idx        uint        此类的 type_ids 列表中的索引。此项必须是“类”类型,而不能是“数组”或“基元”类型。
access_flags        uint        类的访问标记(public、final 等)。如需了解详情,请参阅“access_flags 定义”。
superclass_idx        uint        父类的 type_ids 列表中的索引。如果此类没有父类(即它是根类,例如 Object),则该值为常量值 NO_INDEX。如果此类存在父类,则此项必须是“类”类型,而不能是“数组”或“基元”类型。
interfaces_off        uint        从文件开头到接口列表的偏移量;如果没有接口,则该值为 0。该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“type_list”指定的格式。该列表的每个元素都必须是“类”类型(而不能是“数组”或“基元”类型),并且不得包含任何重复项。
source_file_idx        uint        文件(包含这个类(至少大部分)的原始来源)名称的 string_ids 列表中的索引;或者该值为特殊值 NO_INDEX,以表示缺少这种信息。任何指定方法的 debug_info_item 都可以替换此源文件,但预期情况是大多数类只来自一个源文件。
annotations_off        uint        从文件开头到此类的注释结构的偏移量;如果此类没有注释,则该值为 0。此偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“annotations_directory_item”指定的格式,同时所有项将此类作为定义符进行引用。
class_data_off        uint        从文件开头到此项的关联类数据的偏移量;如果此类没有类数据,则该值为 0(这种情况有可能出现,例如,如果此类是标记接口)。该偏移量(如果为非零值)应该位于 data 区段,且其中的数据应采用下文中“class_data_item”指定的格式,同时所有项将此类作为定义符进行引用。
static_values_off        uint        从文件开头到 static 字段初始值列表的偏移量;如果没有该列表(并且所有 static 字段都将使用 0 或 null 进行初始化),则该值为 0。此偏移量应位于 data 区段,且其中的数据应采用下文中“encoded_array_item”指定的格式。该数组的大小不得超出此类所声明的 static 字段的数量,且 static 字段所对应的元素应采用相对应的 field_list 中所声明的相同顺序。每个数组元素的类型均必须与其相应字段的声明类型相匹配。 如果该数组中的元素比 static 字段中的少,则剩余字段将使用适当类型的 0 或null 进行初

定义结构体

typedef struct _IMAGE_CLASS_DEF_ITEM
{
        DWORD   class_idx;
        DWORD        access_flags;        
        DWORD superclass_idx;        
        DWORD interfaces_off;        
        DWORD source_file_idx;        
        DWORD        annotations_off;        
        DWORD class_data_off;
        DWORD static_values_off;        

}IMAGE_CLASS_DEF_ITEM, * PIMAGE_CLASS_DEF_ITEM;

关于class里面的解析我只挑重点的说

一:access_flags

access_flags定义

ACC_PUBLIC 0x1 public:全部可见 public:全部可见 public:全部可见
ACC_PRIVATE 0x2 *private:仅对定义类可见 private:仅对定义类可见 private:仅对定义类可见
ACC_PROTECTED 0x4 *protected:对软件包和子类可见 protected:对软件包和子类可见 protected:对软件包和子类可见
ACC_STATIC 0x8 *static:无法通过外部 this 引用构造 static:对定义类全局可见 static:不采用 this 参数
ACC_FINAL 0x10 final:不可子类化 final:构建后不可变 final:不可替换
ACC_SYNCHRONIZED 0x20 synchronized:调用此方法时自动获得关联锁定。注意:这一项仅在同时设置 ACC_NATIVE 的情况下才有效。
ACC_VOLATILE 0x40 volatile:有助于确保线程安全的特殊访问规则
ACC_BRIDGE 0x40 桥接方法,由编译器自动添加为类型安全桥
ACC_TRANSIENT 0x80 transient:不会通过默认序列化保存
ACC_VARARGS 0x80 最后一个参数应被编译器解译为“rest”参数
ACC_NATIVE 0x100 native:在原生代码中实现
ACC_INTERFACE 0x200 interface:可多倍实现的抽象类
ACC_ABSTRACT 0x400 abstract:不可直接实例化 abstract:不通过此类实现
ACC_STRICT 0x800 strictfp:严格的浮点运算规则
ACC_SYNTHETIC 0x1000 不在源代码中直接定义 不在源代码中直接定义 不在源代码中直接定义
ACC_ANNOTATION 0x2000 声明为注释类
ACC_ENUM 0x4000 声明为枚举类型 声明为枚举值
(未使用) 0x8000
ACC_CONSTRUCTOR 0x10000 构造函数方法(类或实例初始化块)
ACCDECLARED SYNCHRONIZED 0x20000 声明了 synchronized

了解java的应该都知道,类和方法的关键字并不是只有一个,而是有多个,这里就需要我们把数字转换成对应的关键字。

从上表中可以发现,任何关键字对应的值相加都不会等于一个关键字对应的值。

解析思路:

定义两个数组:

extern LPSTR mClassType[]{"public ","private ","protected ","static ","final ","synchronized ","volatile bridge ","transient varargs ","native ","interface ","abstract ","strict ","synthetic ","annotation ","enum ","constructor ","declared_synchronized "};
extern DWORD mClassValue[]{ 0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,
0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x10000,0x20000 };

比较数值的大小,有三种情况:

1、从第一个开始比较,直到与这个数相等  -----------直接返回对应的字符串,跳出循环

2、从第一个开始比较,直到大于这个数 ---------------返回前一个索引的字符串,并把数值减去前一个索引的值,进入下一个循环

3、比较到了结尾 -------------返回当前对应的字符串,减去当前数值,进入下一个循环

此时如果直接拼接字符串,那么顺序是乱的,此处我写了一个动态数组(需要一点的数据结构基础,非必须,可用一个int数组来替代)来保存索引值,此处要理解,指针的值不一定都是地址值。

代码:

void getClassType(char** str, DWORD index)
{//这是动态数组的初始化
        void* arr = Init_DynamicArray(1);
        //开辟的空间 
        char* m_str = (char*)malloc(100);
        int n = 0;
        memset(m_str, 0, 100);
        //循环,只要这个值大于0
        while (index)
        {
//循环判断 IMAGE_SIZEOF_CLASS_TYPE是我定义的一个宏,值是上面数组的长度
                for (size_t i = 0; i < IMAGE_SIZEOF_CLASS_TYPE; i++)
                {
                        if (index == mClassValue[i])
                        {//如果相等 给动态数组插入值 index等于0跳出循环
                                Insert_DynamicArray(arr, n++, (void*)i);
                                index = 0;
                                break;
                        }
                        if (index < mClassValue[i])
                        {//如果小于 给动态数组插入前一个的索引 index减去前一个索引的数值
                                Insert_DynamicArray(arr, n++, (void*)(i - 1));
                                index -= mClassValue[i - 1];
                                break;
                        }
                        if(i==IMAGE_SIZEOF_CLASS_TYPE-1)
                        {//到最后一个了
                        Insert_DynamicArray(arr, n++, (void*)i);
                                index -=mClassValue[i];
                                break;
                        }
                }

        }

        int num = Size_DynamicArray(arr);
        //倒序拼接
        for (int i = num-1; i >= 0; i--)
        {
                int m = (int)getByPos_DynamicArray(arr, i);
                strcat(m_str, mClassType[m]);
        }
        Destroy_DynamicArray(arr);
        *str = m_str;
}

二:class_data_off

class_data_item格式:

static_fields_size uleb128 此项中定义的静态字段的数量
instance_fields_size uleb128 此项中定义的实例字段的数量
direct_methods_size uleb128 此项中定义的直接方法的数量
virtual_methods_size uleb128 此项中定义的虚拟方法的数量
static_fields encoded_field[static_fields_size] 定义的静态字段;以一系列编码元素的形式表示。这些字段必须按 field_idx 以升序进行排序。
instance_fields encoded_field[instance_fields_size] 定义的实例字段;以一系列编码元素的形式表示。这些字段必须按 field_idx 以升序进行排序。
direct_methods encoded_method[direct_methods_size] 定义的直接(staticprivate 或构造函数的任何一个)方法;以一系列编码元素的形式表示。这些方法必须按 method_idx 以升序进行排序。
virtual_methods encoded_method[virtual_methods_size] 定义的虚拟(非 staticprivate 或构造函数)方法;以一系列编码元素的形式表示。此列表不得包括继承方法,除非被此项所表示的类覆盖。这些方法必须按 method_idx 以升序进行排序。 虚拟方法的 method_idx 不得与任何直接方法相同。

此处只简单的说一下,需要分别计算出前四个记录数量的字节数和值,然后才能得到后边四个数组的位置。

三:static_values_off

encoded_array 格式

名称 格式 说明
size uleb128 数组中的元素数量
values encoded_value[size] 采用本部分所指定格式的一系列 size encoded_value 字节序列;依序连接。
encoded_value 编码
(value_arg << 5) \ value_type ubyte 一种字节,用于表示紧跟后面的 value 及高 3 位中可选澄清参数的类型。请参阅下文,了解各种 value 定义。在大多数情况下,value_arg 会以字节为单位将紧跟后面的 value 的长度编码为 (size - 1);例如,0 表示该值需要 1 个字节;7 表示该值需要 8 个字节;不过,也存在下述例外情况。
value ubyte[] 用于表示值的字节,不同 value_type 字节的长度不同且采用不同的解译方式;不过一律采用小端字节序。如需了解详情,请参阅下文中的各种值定义。

第一个字节的高三位记录字节的个数,低五位记录value_type

int values = *strtic_value & 0x1f; //计算出类型
int lenght = *strtic_value >> 5; //计算出长度

value_type

VALUE_BYTE 0x00 (无;必须为 0 ubyte[1] 有符号的单字节整数值
VALUE_SHORT 0x02 size - 1 (0…1) ubyte[size] 有符号的双字节整数值,符号扩展
VALUE_CHAR 0x03 size - 1 (0…1) ubyte[size] 无符号的双字节整数值,零扩展
VALUE_INT 0x04 size - 1 (0…3) ubyte[size] 有符号的四字节整数值,符号扩展
VALUE_LONG 0x06 size - 1 (0…7) ubyte[size] 有符号的八字节整数值,符号扩展
VALUE_FLOAT 0x10 size - 1 (0…3) ubyte[size] 四字节位模式,向右零扩展,系统会将其解译为 IEEE754 32 位浮点值
VALUE_DOUBLE 0x11 size - 1 (0…7) ubyte[size] 八字节位模式,向右零扩展,系统会将其解译为 IEEE754 64 位浮点值
VALUE_METHOD_TYPE 0x15 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 proto_ids 区段的索引;表示方法类型值
VALUE_METHOD_HANDLE 0x16 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 method_handles 区段的索引;表示方法句柄值
VALUE_STRING 0x17 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 string_ids 区段的索引;表示字符串值
VALUE_TYPE 0x18 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 type_ids 区段的索引;表示反射类型/类值
VALUE_FIELD 0x19 size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 field_ids 区段的索引;表示反射字段值
VALUE_METHOD 0x1a size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 method_ids 区段的索引;表示反射方法值
VALUE_ENUM 0x1b size - 1 (0…3) ubyte[size] 无符号(零扩展)四字节整数值,会被解译为要编入 field_ids 区段的索引;表示枚举类型常量的值
VALUE_ARRAY 0x1c (无;必须为 0 encoded_array 值的数组,采用下文“encoded_array 格式”所指定的格式。value 的大小隐含在编码中。
VALUE_ANNOTATION 0x1d (无;必须为 0 encoded_annotation 子注释,采用下文“encoded_annotation 格式”所指定的格式。value 的大小隐含在编码中。
VALUE_NULL 0x1e (无;必须为 0 (无) null 引用值
VALUE_BOOLEAN 0x1f 布尔值 (0…1) (无) 一位值;0 表示 false1 表示 true。该位在 value_arg 中表示。

看到上面那么多的类型,长度还都是不固定的有没有头皮发麻?其实没有那么复杂,其实计算机并不知道里面的数据是有符号还是无符号的数值,你只需要小端模式拼接一下,转换成相应的类型即可。

    对于变动长度,这个转换是通用的,你只需要注意num的类型,如果是八字节的就用long long类型,因为c\c++在vs中无论是64位还是32位中都是4字节。最后再强转成需要的类型即可。
    int n=0;
    int m=0;
    int num;
    lenght是计算出来的长度,因为数组的个数是长度减1,所以此处要大于-1
    while (lenght-- > -1)
                                            {
                                                    m = *strtic_value;
                                                    m = m << (8 * n);
                                                    n++;
                                                    num += m;
                                                    strtic_value2++;
                                            }

encoded_array是一个循环类型转换

最后附上我写的半成品mfc应用和源码,很多的细节都没有处理,点击按钮之后会在桌面上新建一个文件夹,并生成相应的文件,class我只解析了类名,变量,变量的值(array和方法句柄没有写),和方法名,字节码没有进行解析。

软件界面很简陋,很单调


生成的class_item文件

软件我上传了两个,一个动态编译的需要vc的运行库,一个静态编译不需要运行库,根据自己需要下载任意一个即可,代码我是复制出来的,需要的自己研究。
DexApplication.zip (100.64 KB, 下载次数: 11)
DexApplication.7z (2.2 MB, 下载次数: 20)
核心代码.7z (79.17 KB, 下载次数: 5)

软件界面

软件界面

生成的类文件

生成的类文件

免费评分

参与人数 4威望 +1 吾爱币 +24 热心值 +4 收起 理由
qtfreet00 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
无忌哥哥 + 2 + 1 虽然我完全不懂,连小白都不是,但是我认为这种分享值得赞扬。
CraftDeadMRC + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
lunoah + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wang666xuan 发表于 2020-6-24 23:17
感谢楼主 我得好好研究一下
moranyuyan 发表于 2020-6-25 08:45
一人之下123456 发表于 2020-6-25 09:55
coolsnake 发表于 2020-6-25 12:56
好牛皮的工作
 楼主| 紫诺不离 发表于 2020-6-25 14:22

这是造轮子啊&#128559;,理解底层原理
blindcat 发表于 2020-6-25 15:20
楼主厉害了
 楼主| 紫诺不离 发表于 2020-6-26 20:02

其实我也是小白…,我上学的专业是会计
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 21:58

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表