引言
dex 文件中用到的数据类型有:
类型 |
编码格式 |
byte |
8位有符号整数 |
ubyte |
8位无符号整数 |
short |
16位有符号整数,小端存储 |
ushort |
16位无符号整数,小端存储 |
int |
32位有符号整数,小端存储 |
uint |
32位无符号整数,小端存储 |
long |
64位有符号整数,小端存储 |
ulong |
64位无符号整数,小端存储 |
sleb128 |
有符号 LEB128,可变长度 |
uleb128 |
无符号 LEB128,可变长度 |
uleb128p1 |
无符号 LEB128 加 1,可变长度 |
[注] 将一个数编码为 uleb128p1 时需要将 uleb128 加 1,而将一个已经编码好的 uleb128p1 解码为正常数字时需要将 uleb128 减 1。
LEB128?
LEB128,全程 Little-Endian Base 128,借鉴自 DWARF3 (一种调试文件格式,广泛用于 Unix、Linux等操作系统,可扩展性强) 。在 .dex 文件中,LEB128 仅对 32 位数据编码。
每个 LEB128 编码的值由 1-5 个字节组成,合在一起表示一个 32 位的值。每个字节的有效位只有7位,最高位作为标记:最后一个字节的最高位设为0,其余设为1 。如果第 5 个字节的最高位为 1,说明这个 dex 文件是无效的。
sleb128 对最后一个字节的最高位进行了符号扩展(高位全补1,uleb128是高位全补0),即作为符号位。
uleb128p1 + 1 就是 uleb128 的值,也就是说在无符号 LEB128 类型中,只有非负数和一个负数(-1 or 0xffffffff)。
下面是 2 个字节的 LEB128 值 - 图示:
First byte |
1 |
bit6 |
bit5 |
bit4 |
bit3 |
bit2 |
bit1 |
bit0 |
Second byte |
0 |
bit13 |
bit12 |
bit12 |
bit11 |
bit10 |
bit9 |
bit8 |
bit7 |
官方栗子:
Encoded Sequence |
As sleb128 |
As uleb128 |
As uleb128p1 |
00 |
0 |
0 |
-1 |
01 |
1 |
1 |
0 |
7f |
-1 |
127 |
126 |
807f |
-128 |
16256 |
16255 |
解析:
7f |
二进制 |
0111,1111 |
有效值 |
111,1111 |
sleb128 |
1111,1111 = - 0000,0000,0000,0001 = -1 |
uleb128 |
0111,1111 = 127 |
uleb128p1 |
127-1=126 |
80 7f |
二进制 |
1000,0000,0111,1111 |
有效值 |
111,1111,000,0000 |
sleb128 |
1111,1111,1000,0000 = - 0000,0000,1000,0000 = -128 |
uleb128 |
0011,1111,1000,0000 = 16256 |
uleb128p1 |
16256-1=16255 |
相关源码
位于 /dalvik/libdex/Leb128.h
文件中。
读取无符号 LEB128 的源码
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 28 29 30 31
| /* * Reads an unsigned LEB128 value, updating the given pointer to point * just past the end of the read value. This function tolerates * non-zero high-order bits in the fifth encoded byte. */ DEX_INLINE int readUnsignedLeb128(const u1** pStream) { const u1* ptr = *pStream; int result = *(ptr++); //取第一个字节 if (result > 0x7f) { //如果大于 0x7f 表示第 1 字节最高位为 1 int cur = *(ptr++); //取第二个字节 result = (result & 0x7f) | ((cur & 0x7f) << 7); // 两个字节组合,第二个字节为高位 if (cur > 0x7f) { //如果大于 0x7f 表示第 2 字节最高位为 1 cur = *(ptr++); //取第三个字节 result |= (cur & 0x7f) << 14; //前三个字节组合,第三个字节为高位 if (cur > 0x7f) { //如果大于 0x7f 表示第 3 字节最高位为 1 cur = *(ptr++); //取第四个字节 result |= (cur & 0x7f) << 21; //前四个字节组合,第四个字节为高位 if (cur > 0x7f) { //如果大于 0x7f 表示第 4 字节最高位为 1 /* * 这里不检查 ptr 指针是否越界 * 也不检查第五个字节的最高位是否为0 */ cur = *(ptr++); //取第五个字节 result |= cur << 28; //前五个字节组合,第五个字节为高位 } } } } *pStream = ptr; return result; }
|
读取有符号 LEB128 的源码
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 28 29 30 31 32 33 34 35
| /* * Reads a signed LEB128 value, updating the given pointer to point * just past the end of the read value. This function tolerates * non-zero high-order bits in the fifth encoded byte. */ DEX_INLINE int readSignedLeb128(const u1** pStream) { const u1* ptr = *pStream; int result = *(ptr++); if (result <= 0x7f) { //第2个字节最高位为0 result = (result << 25) >> 25; //最高位进行符号扩展 } else { int cur = *(ptr++); result = (result & 0x7f) | ((cur & 0x7f) << 7); //前两个字节组合 if (cur <= 0x7f) { //第2个字节最高位为0 result = (result << 18) >> 18; //最高位进行符号扩展 } else { cur = *(ptr++); result |= (cur & 0x7f) << 14; //前3个字节组合 if (cur <= 0x7f) { //第3个字节最高位为0 result = (result << 11) >> 11; //最高位进行符号扩展 } else { cur = *(ptr++); result |= (cur & 0x7f) << 21; //前4个字节组合 if (cur <= 0x7f) { //第4个字节最高位为0 result = (result << 4) >> 4; //最高位进行符号扩展 } else { cur = *(ptr++); result |= cur << 28; //前5个字节组合 } } } } *pStream = ptr; return result; }
|
Reference
Dalvik Executable format
《Android 软件安全与逆向分析》