引言

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 软件安全与逆向分析》