一、dex文件中的数据结构
类型有三个:u1 u2 u4 u8 sleb128 uleb128 uleb128p1 七个类型 如图:
u1-u8好理解,就是1到8个字节的无符号整数,而后面的sleb128 uleb128 uleb128p1是dex文件中特有的LEB128数据类型,leb128表示可变长度1-5个字节所有字节组合在一起为32位数据,每个字节的有效位数只有七位,留一个最高位做标识,表示是否有下一位(为1),没有下一位(为0),leb128最多有5个字节,如果读到最后一个字节的标识位仍为1.则会返回dex文件无效,Dalvik虚拟机验证dex时会失败返回,LEB128数据类型如下图:
在android系统源码有leb128的实现,读取无符号leb128(uleb128)代码如下:
ptr是一个指针,指向数据的第一个字节,当第一个字节大于0x7f(b1111111)就代表有后一个字节,将第一个字节(result)和第二个字节(cur=ptr++)组合result&0x7f | ((cur&0x7f)<<7) 意思是:取第二个字节的后七位,然后左移7位再和第一个字节或,就将两个字节组合在一起,然后判断第二个字节的最高位(标识)为1则继续判断下一字节,组合三个字节,以此类推。
有符号的LEB128(sleb128)和无符号大体一样,只是多了对无符号LEB128最后一个字节最高有效位进行了符号扩展,代码如下:
对于每个字节最高位为0的(字节大小小于0x7f)的说明没有下一字节,直接进行符号位扩展,如果最高位为1,则进行字节组合之后,到最后一个字节然后进行符号扩展,关于符号扩展,补充一下(扫盲)
符号位拓展就是: 将数据的最高位(这里是指32位的最高位)设置成数据的符号位,这里符号位拓展用了(result<<25)>>25,25可变,由于数据有效位7位,第一个字节拓展就要向左移25(32-7)位,将最高符号位设为数据的最高位,然后再右移相同位数数据复原,但是32位最高位已被拓展成数据的符号位了,这里不影响是因为数据有效位7位,32位的最高位也是标识位,所以再向右移复原的时候标识位不会随着复原,即完成了最高位符号位的拓展。
示例:
- 无符号扩展:直接将扩展后的数据的高(32-n)位置为0。
- 符号扩展:将扩展后的数据的高(32-n)位置为立即数的最高位。
16位立即数 | 0x8000 | 0x1000 |
---|---|---|
符号扩展 | 0xFFFF8000 | 0x00001000 |
无符号扩展 | 0x00008000 | 0x00001000 |
两个例子:
二、dex文件整体结构
dex真题结构比较简单有多个结构体组成,一个dex文件由7个部分组成,如图:
其中DexHeader指定了dex文件的一些属性,并记录了其他六个部分数据结构在dex文件中的物理偏移,string_ids到class_def可理解为索引结构区,真实的数据存放在data数据区,link_data为静态链接数据区。
DexHeader结构体为:
对比结构体看16禁止文件,一一对应:
中间未标注出来的是string_ids到class_def的个数和文件偏移,其中endianTag意思是:指定了dex运行环境的cpu字节序,与设值ENDIAN_CONSTANT等于0x12345678,表示默认采用Little-Endian字节序。
三、dex文件结构分析
dex文件头有一个mapOff指明了DexMapList在dex文件中的偏移,声明如下:
size表示有多少个DexMapItem,其结构如下:
type字段是一个枚举常量,size表示特定类型的个数,offset为文件起始偏移以Hello.java->Hello.class->Hello.dex为例:
由上面可知mapOff的位置在0x024c位置:
整理出的DexMapItem表如下:
类型 | 个数 | 偏移 |
kDexTypeHeaderItem | 0x1 | 0x0 |
kDexTypeStringIdItem | 0xe | 0x70 |
kDexTypeTypeIdItem | 0x7 | 0xa8 |
kDexTypeProtoIdItem | 0x3 | 0xc4 |
kDexTypeFieldIdItem | 0x1 | 0xe8 |
kDexTypeMethodIdItem | 0x4 | 0xf0 |
. | . | . |
. | . | . |
. | . | . |
. | . | . |
. | . | . |
. | . | . |
.省略 | .省略 | .省略 |
上图和表格一一对应
上图是stringIdItem在0x70偏移处 的0xe个DexStringId对象,红线圈的是第一个字符串结构,蓝线的是第二个、黄线的是第三个以此类推,下面是kDexTypeTypeIdItem了,它对应DexHeader中的typeIdsSize与typeIdsOff字段,指向的结构体为:
descriptorIdx指向DexStringId列表的索引(就是上一个stringiditem),对应的字符串代表了具体类的类型,根据DexMapItem表,知typeTypeIdItem偏移0xa8处有0x7个DexTypeId结构,也就是说有7个字符串的索引,分别对应13个字符串的第0x2、0x3、0x4、0x5、0x6、0x7、0x9个,类型索引从大到小分别为0x0、1、2、3、4、5、6。
其他的一样以此类推,有时间补充
四、odex文件格式
1、如何生成odex文件
pass
2、odex文件整体结构
pass
3、odex文件结构分析
pass
占个位先。。。
五、android应用另类破解方法
通常我们破解简单的app都是通过反编译apk之后修改smail代码,然后重新打包,过程非常慢,还有一种另类的方法,我们都知道程序代码都是存储在dex文件中的,修改dex文件,重新签名就可以达到破解程序的目的,
例子程序:firstapp
步骤:1.用打开zip软件,打开apk文件,将classes.dex文件拉出来,用ida打开找到关键要修改的地方,记下地址
2.用16进制编辑器打开dex文件,找到对应地址,将 if-eqz改为if-nez对应的指令38改为39,保存
只改dex还不行因为安装时dexopt优化会对dexheader中的checksum标识进行合法性检查,不通过会导致安装失败,所以我们要修复dex的checksum值,为了方便可以用工具DexFixer,吾爱工具包里有,也可以在网上下载,图就不放了,直接将修改过的dex文件拖进去就行了。
3.最后重新签名,签名工具很多,同样这里用的是吾爱工具包里的APK上上签工具
4.安装测试
暂时告一段落。。。。。