约束

.dex 文件是 Dalvik 字节码的传输格式。文件要成为有效的 .dex 文件,必须满足某些语法和语义约束,并且运行时环境必须仅支持有效的 .dex 文件。

常规 .dex 完整性约束

常规完整性约束与 .dex 文件的较大结构有关,详见 .dex 格式

标识符 说明
G1 .dex 文件的 magic 数字对于版本 35 必须为 dex\n035\0,对于更高版本则类似。
G2 校验和必须是整个文件内容(magicchecksum 字段除外)的 Adler-32 校验和。
G3 签名必须是整个文件内容(magicchecksumsignature 除外)的 SHA-1 哈希值。
G4

file_size 必须与实际文件大小(以字节为单位)匹配。(v40 或更早版本)

file_size 必须指向容器中的下一个标头或物理文件(容器)的末尾。如果它指向下一个标头,则文件大小必须是 4 字节对齐的。所有 file_size 字段的总和必须等于 container_size(v41 或更高版本)

G5

header_size 必须具有值:0x70 (v40 或更早版本)

header_size 必须具有值:0x78 (v41 或更高版本)

G6 endian_tag 必须具有值:ENDIAN_CONSTANTREVERSE_ENDIAN_CONSTANT
G7

对于 linkstring_idstype_idsproto_idsfield_idsmethod_idsclass_defsdata 部分中的每一个,offsetsize 字段必须均为零或均为非零。在后一种情况下,偏移量必须是四字节对齐的。

offsetsize 字段必须位于容器内,并且指向定义它们的标头之后的数据。(v41 或更高版本)

G8 标头中的所有偏移量字段(map_off 除外)都必须是四字节对齐的。
G9 map_off 字段必须为零或指向数据部分。在后一种情况下,data 部分必须存在。
G10 linkstring_idstype_idsproto_idsfield_idsmethod_idsclass_defsdata 部分都不得相互重叠或与标头重叠。
G11 如果存在映射,则每个映射条目都必须具有有效的类型。每种类型最多可以出现一次。
G12 如果存在映射,则每个映射条目都必须具有非零的偏移量和大小。偏移量必须指向文件中相应的节(即,string_id_item 必须指向 string_ids 节),并且条目的显式或隐式大小必须与节的实际内容和大小匹配。
G13 如果存在映射,则映射条目 n+1 的偏移量必须大于或等于映射条目 n 的偏移量加上映射条目 n 的大小。这意味着条目不重叠且从低到高排序。
G14 以下类型的条目必须具有四字节对齐的偏移量:string_id_itemtype_id_itemproto_id_itemfield_id_itemmethod_id_itemclass_def_itemtype_listcode_itemannotations_directory_item
G15

对于每个 string_id_itemstring_data_off 字段必须包含对 data 部分的有效引用。(v40 或更早版本)

对于每个 string_id_itemstring_data_off 字段必须是容器内且在使用它的任何标头之后的偏移量。(v41 或更高版本)

对于引用的 string_data_itemdata 字段必须包含有效的 MUTF-8 字符串,并且 utf16_size 必须与字符串的解码长度匹配。

G16 对于每个 type_id_itemdescriptor_idx 字段必须包含对 string_ids 列表的有效引用。引用的字符串必须是有效的类型描述符。
G17 对于每个 proto_id_itemshorty_idx 字段必须包含对 string_ids 列表的有效引用。引用的字符串必须是有效的 shorty 描述符。此外,return_type_idx 字段必须是 type_ids 部分的有效索引,并且 parameters_off 字段必须为零或指向 data 部分的有效偏移量。如果为非零,则参数列表不得包含任何 void 条目。
G18 对于每个 field_id_itemclass_idxtype_idx 字段都必须是 type_ids 列表的有效索引。由 class_idx 引用的条目必须是非数组引用类型。此外,name_idx 字段必须是对 string_ids 部分的有效引用,并且引用条目的内容必须符合 MemberName 规范。
G19 对于每个 method_id_itemclass_idx 字段必须是 type_ids 部分的有效索引,并且引用条目必须是非数组引用类型。proto_id 字段必须是对 proto_ids 列表的有效引用。name_idx 字段必须是对 string_ids 部分的有效引用,并且引用条目的内容必须符合 MemberName 规范。
G20 对于每个 field_id_itemclass_idx 字段必须是 type_ids 列表的有效索引。引用条目必须是非数组引用类型。

静态字节码约束

静态约束是关于字节码各个元素的约束。它们通常可以在不使用控制流或数据流分析技术的情况下进行检查。

标识符 说明
A1 insns 数组不能为空。
A2 insns 数组中的第一个操作码的索引必须为零。
A3 insns 数组必须仅包含有效的 Dalvik 操作码。
A4 指令 n+1 的索引必须等于指令 n 的索引加上指令 n 的长度(考虑到可能的运算数)。
A5 insns 数组中的最后一条指令必须在索引 insns_size-1 处结束。
A6 所有 gotoif-<kind> 目标都必须是同一方法内的操作码。
A7 packed-switch 指令的所有目标都必须是同一方法内的操作码。大小和目标列表必须一致。
A8 sparse-switch 指令的所有目标都必须是同一方法内的操作码。相应的表必须一致且按从低到高的顺序排序。
A9 const-stringconst-string/jumbo 指令的 B 运算数必须是字符串常量池的有效索引。
A10 iget<kind>iput<kind> 指令的 C 运算数必须是字段常量池的有效索引。引用的条目必须表示实例字段。
A11 sget<kind>sput<kind> 指令的 C 运算数必须是字段常量池的有效索引。引用的条目必须表示静态字段。
A12 invoke-virtualinvoke-superinvoke-directinvoke-static 指令的 C 运算数必须是方法常量池的有效索引。
A13 invoke-virtual/rangeinvoke-super/rangeinvoke-direct/rangeinvoke-static/range 指令的 B 运算数必须是方法常量池的有效索引。
A14 名称以“<”开头的方法只能由虚拟机隐式调用,而不能由源自 .dex 文件的代码调用。唯一的例外是实例初始化程序,它可以由 invoke-direct 调用。
A15 invoke-interface 指令的 C 运算数必须是方法常量池的有效索引。引用的 method_id 必须属于接口(而不是类)。
A16 invoke-interface/range 指令的 B 运算数必须是方法常量池的有效索引。引用的 method_id 必须属于接口(而不是类)。
A17 const-classcheck-castnew-instancefilled-new-array/range 指令的 B 运算数必须是类型常量池的有效索引。
A18 instance-ofnew-arrayfilled-new-array 指令的 C 运算数必须是类型常量池的有效索引。
A19 new-array 指令创建的数组的维度必须小于 256
A20 new 指令不得引用数组类、接口或抽象类。
A21 new-array 指令引用的类型必须是有效的非引用类型。
A22 以单宽度(非对)方式由指令引用的所有寄存器对于当前方法都必须有效。也就是说,它们的索引必须为非负数且小于 registers_size
A23 以双宽度(对)方式由指令引用的所有寄存器对于当前方法都必须有效。也就是说,它们的索引必须为非负数且小于 registers_size-1
A24 invoke-virtualinvoke-direct 指令的 method_id 运算数必须属于类(而不是接口)。在版本 037 之前的 Dex 文件中,invoke-superinvoke-static 指令也必须如此。
A25 invoke-virtual/rangeinvoke-direct/range 指令的 method_id 运算数必须属于类(而不是接口)。在版本 037 之前的 Dex 文件中,invoke-super/rangeinvoke-static/range 指令也必须如此。

结构字节码约束

结构约束是关于字节码的多个元素之间关系的约束。它们通常无法在不使用控制流或数据流分析技术的情况下进行检查。

标识符 说明
B1 参数(寄存器和立即数值)的数量和类型必须始终与指令匹配。
B2 寄存器对绝不能被拆分。
B3 寄存器(或对)必须先赋值才能读取。
B4 invoke-direct 指令必须仅在当前类或其超类之一中调用实例初始化程序或方法。
B5 实例初始化程序必须仅在未初始化的实例上调用。
B6 实例方法可能仅在已初始化的实例上调用,并且实例字段可能仅在已初始化的实例上访问。
B7 如果再次执行相同的 new-instance 指令,则在实例初始化之前,不得使用保存 new-instance 指令结果的寄存器。
B8 实例初始化程序必须在访问任何实例成员之前调用另一个实例初始化程序(同一类或超类)。例外情况是在调用另一个初始化程序之前可以赋值的非继承实例字段,以及一般的 Object 类。
B9 所有实际方法参数都必须与其各自的形式参数分配兼容。
B10 对于每个实例方法调用,实际实例必须与指令中指定的类或接口分配兼容。
B11 return<kind> 指令必须与其方法的返回类型匹配。
B12 访问超类的受保护成员时,被访问实例的实际类型必须是当前类或其子类之一。
B13 存储到静态字段中的值的类型必须与字段的类型分配兼容或可转换为字段的类型。
B14 存储到字段中的值的类型必须与字段的类型分配兼容或可转换为字段的类型。
B15 存储到数组中的每个值的类型都必须与数组的组件类型分配兼容。
B16 throw 指令的 A 运算数必须与 java.lang.Throwable 分配兼容。
B17 方法中最后一条可访问的指令必须是向后的 goto 或分支、returnthrow 指令。不得有可能在底部离开 insns 数组。
B18 在通过其他一些指令重新赋值之前,前一个寄存器对的未赋值一半可能无法读取(被视为无效)。
B19 move-result<kind> 指令之前必须紧跟(在 insns 数组中)invoke-<kind> 指令。唯一的例外是 move-result-object 指令,该指令也可以在 filled-new-array 指令之前。
B20 move-result<kind> 指令必须紧跟在(实际控制流中)匹配的 return-<kind> 指令之后(不得跳转到)。唯一的例外是 move-result-object 指令,该指令也可以在 filled-new-array 指令之前。
B21 move-exception 指令必须仅作为异常处理程序中的第一条指令出现。
B22 packed-switch-datasparse-switch-datafill-array-data 伪指令不得通过控制流访问。