自定义类型 | 原类型 | 含义 |
---|---|---|
s1 | int8_t | 8 位有符号整型 |
u1 | uint8_t | 8 位无符号整型 |
s2 | int16_t | 16 位有符号整型,小端字节序 |
u2 | uint16_t | 16 位无符号整型,小端字节序 |
s4 | int32_t | 32 位有符号整型,小端字节序 |
u4 | uint32_t | 32 位无符号整型,小端字节序 |
s8 | int64_t | 64 位有符号整型,小端字节序 |
u8 | uint64_t | 64 位无符号整型,小端字节序 |
sleb128 | 无 | 有符号 LEB128,可变长度 |
uleb128 | 无 | 无符号 LEB128,可变长度 |
uleb128p1 | 无 | 无符号 LEB128 加 1,可变长度 |
DEX_INLINE int readUnsignedLeb128(const u1** pStream) {
const u1* ptr = *pStream;
int result = *(ptr++);
// 大于 0x7f,表示第一个字节最高位为 1
if (result > 0x7f) {
// 第二个字节
int cur = *(ptr++);
// 前两个字节的组合
result = (result & 0x7f) | ((cur & 0x7f) << 7);
// 大于 0x7f,表示第二个字节最高位仍为 1
if (cur > 0x7f) {
// 第三个字节
cur = *(ptr++);
// 前三个字节的组合
result |= (cur & 0x7f) << 14;
if (cur > 0x7f) {
// 第四个字节
cur = *(ptr++);
// 前四个字节的组合
result |= (cur & 0x7f) << 21;
if (cur > 0x7f) {
// 第五个字节
cur = *(ptr++);
// 前五个字节的组合
result |= cur << 28;
}
}
}
}
*pStream = ptr;
return result;
}
DEX_INLINE int readSignedLeb128(const u1** pStream) {
const u1* = *pStream;
int result = *(ptr++);
// 小于 0x7f,表示第一个字节的最高位不为 1
if (result <= 0x7f) {
// 对第一个字节的最高有效位进行符号扩展
result = (result << 25) >> 25;
}
else {
// 第二个字节
int cur = *(ptr++);
// 前两个字节的组合
result = (result & 0x7f) | ((cur & 0x7f) << 7);
if (cur <= 0x7f) {
// 对结果进行符号位扩展
result = (result << 18) >> 18;
}
else {
// 第三个字节
cur = *(ptr++);
// 前三个字节的组合
result |= (cur & 0x7f) << 14;
if (cur <= 0x7f) {
// 对结果进行符号位扩展
result = (result << 11) >> 11;
}
else {
// 第四个字节
cur = *(ptr++);
// 前四个字节的组合
result |= (cur & 0x7f) << 21;
if (cur <= 0x7f) {
// 对结果进行符号位扩展
result = (result << 4) >> 4;
}
else {
// 第五个字节
cur = *(ptr++);
// 前五个字节的组合
result |= cur << 28;
}
}
}
}
*pStream = ptr;
return result;
}
dex header |
---|
string_ids |
type_ids |
proto_ids |
field_ids |
method_ids |
class_def |
data |
link_data |
struct DexFile {
// directly-mapped "opt" header
const DexOptHeader* pOptHeader;
// pointers to directly-mapped structs and arrays in base DEX
const DexHeader* pHeader;
const DexStringId* pStringIds;
const DexTypeId* pTypeIds;
const DexFileId* pFileIds;
const DexMethodId* pMethodIds;
const DexProtoId* pProtoIds;
const DexClassDef* pClassDefs;
const DexLink* pLinkData;
// These are mapped out of the "auxillary" section,
// and may not be included in the file
const DexClassLookup* pClassLookup;
const void* pRegisterMapPool; // RegisterMapClassPool
// points to start of DEX file data
const u1* baseAddr;
// track memory overhead for auxillary structures
int overhead;
// additional app-specific data structures associated with the DEX
//void* auxData;
};
struct DexHeader {
u1 magic[8]; // DEX 版本标识
u4 checksum; // adler32 检验
u1 signature[kSHA1DigestLen]; // SHA-1 散列值
u4 fileSize; // 整个文件的大小
u4 headerSize; // DexHeader 结构的大小
u4 endianTag; // 字节序标记
u4 linkSize; // 链接段的大小
u4 linkOff; // 链接段的偏移量
u4 mapOff; // DexMapList 的文件偏移
u4 stringIdsSzie; // DexStringId 的个数
u4 stringIdsOff; // DexStringId 的文件偏移
u4 typeIdsSize; // DexTypeId 的个数
u4 typeIdsOff; // DexTypeId 的文件偏移
u4 protoIdsSize; // DexProtoId 的个数
u4 protoIdsOff; // DexProtoId 的文件偏移
u4 fieldIdsSize; // DexFieldId 的个数
u4 fieldIdsOff; // DexFieldId 的文件偏移
u4 methodIdsSize; // DexMethodId 的个数
u4 methodIdsOff; // DexMethodId 的文件偏移
u4 classDefsSize; // DexClassDef 的个数
u4 classDefsOff; // DexClassDef 的文件偏移
u4 dataSize; // 数据段的大小
u4 dataOff; // 数据段的文件偏移
};
struct DexMapList {
u4 size; // DexMapItem 结构的个数
DexMapItem list[1]; // DexMapItem 结构
};
struct DexMapItem {
u2 type; // kDexType 开头的类型
u2 unused; // 未使用,用于字节对齐
u4 size; // 指定类型的个数
u4 offset; // 指定类型数据的文件偏移
};
enum {
kDexTypeHeaderItem = 0x0000,
kDexTypeStringIdItem = 0x0001,
kDexTypeTypeIdItem = 0x0002,
kDexTypeProtoIdItem = 0x0003,
kDexTypeFieldIdItem = 0x0004,
kDexTypeMethodIdItem = 0x0005,
kDexTypeClassDefItem = 0x0006,
kDexTypeMapList = 0x1000,
kDexTypeTypeList = 0x1001,
kDexTypeAnnotationSetRefList = 0x1002,
kDexTypeAnnotationSetItem = 0x1003,
kDexTypeClassDataItem = 0x2000,
kDexTypeCodeItem = 0x2001,
kDexTypeStringDataItem = 0x2002,
kDexTypeDebugInfoItem = 0x2003,
kDexTypeAnnotationItem = 0x2004,
kDexTypeEncodeedArrayItem = 0x2005,
kDexTypeAnnotationsDirectoryItem = 0x2006,
};
类型 | 个数 | 偏移量 |
---|---|---|
kDexTypeHeaderItem | 0x1 | 0x0 |
kDexTypeStringIdItem | 0x10 | 0x70 |
kDexTypeTypeIdItem | 0x7 | 0xb0 |
kDexTypeProtoIdItem | 0x4 | 0xcc |
kDexTypeFieldIdItem | 0x1 | 0xfc |
kDexTypeMethodIdItem | 0x5 | 0x104 |
kDexTypeClassDefItem | 0x1 | 0x12c |
kDexTypeCodeItem | 0x3 | 0x14c |
kDexTypeTypeList | 0x3 | 0x1b4 |
kDexTypeStringDataItem | 0x10 | 0x1ca |
kDexTypeDebugInfoItem | 0x3 | 0x267 |
kDexTypeClassDataItem | 0x1 | 0x27b |
kDexTypeMapList | 0x1 | 0x290 |
对比文件头 DexHeader 部分,如下图,kDexTypeHeaderItem 描述了整个 DexHeader 结构,占用了文件的前 0x70 字节空间,而接下来的 kDexTypeStringIdItem ~ kDexTypeClassDefItem 与 DexHeader 中对应的类型及类型个数字段的值相同
如,kDexTypeStringIdItem 对应 DexHeader 的 stringIdsSize 和 stringIdsOff 字段,表示 0x70 偏移处有连续 0x10 个 DexStringId 对象。DexStringId 结构的声明:
struct DexStringId {
u4 stringDataOff; // 字符串数据偏移
};
DEX_INLINE const char* dexGetStringData(const DexFile* pDexFile,
const DexStringId* pStringId) {
// 指向 MUTF-8 字符串的指针
const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
// Skip the uleb128 length
while (*(ptr++) > 0x7f) /* empty */ ;
return (const char*)ptr;
}
在 MUTF-8 字符串的头部存放的是由 uleb128 编码的字符的个数。如,字符序列“02 e4 bd a0 e5 a5 bd 00”头部的“02”表示字符串中有两个字符,“e4 bd a0”是 UTF-8 编码字符“你”,“e5 a5 bd”是 UTF-8 编码字符“好”,最后的空字符“00”是字符串的结尾(计算字符个数时好像不包含它)
接下来是 kDexTypeTypeIdItem,它对应 DexHeader 中的 typeIdsSize 和 typeIdsOff 字段,指向的结构体位 DexTypeId,声明:
struct DexTypeId {
u4 descriptorIdx; // 指向 DexStringId 列表的索引
};
descriptorIdx 为指向 DexStringId 列表的索引,其所对应的字符串代表具体类的类型。从 0xb0 处开始,有 7 个 DexTypeId 结构,即有 7 个字符串的索引:
然后是 kDexTypeProtoIdItem,其对应 DexHeader 中的 protoIdsSize 和 protoIdsOff 字段,指向的结构体为 DexProtoId,声明:
struct DexProtoId {
u4 shortyIdx; // 指向 DexStringId 列表的索引
u4 returnTypeIdx; // 指向 DexTypeId 列表的索引
u4 parametersOff; // 指向 DexTypeList 的偏移量
};
struct DexTypeList {
u4 size; // 接下来 DexTypeItem 结构的个数
DexTypeItem list[1]; // DexTypeItem 结构
};
struct DexTypeItem {
u2 typeIdx; // 指向 DexTypeId 列表的索引
};
DexTypeItem 中的 typeIdx 最终也指向一个字符串。从 0xcc 开始,有 4 个 DexProtoId 结构:
由上图可发现,方法声明由返回类型与参数列表组成,且返回类型在参数列表前面。如:第一个 DexProtoId 结构,方法声明为“III”,返回类型为“I”,参数列表为“I, I”
接下来是 kDexTypeFieldIdItem,对应 DexHeader 中的 fieldIdsSize 和fieldIdsOff 字段,指向的结构体为 DexFieldId,声明:
struct DexFieldId {
u2 classIdx; // 类的类型,指向 DexTypeId 列表的索引
u2 typeIdx; // 字段类型,指向 DexTypeId 列表的索引
u4 nameIdx; // 字段名,指向 DexStringId 列表的索引
};
DexFieldId 结构中的数据全是索引值,指明了字段所在的类、字段类型、字段名。从 0xfc 开始,有 1 个 DexFieldId 结构:
接下来是 kDexTypeMethodIdItem,对应 DexHeader 中的 methodIdsSize 和 methodIdsOff 字段,指向的结构体是 DexMethodId,声明:
struct DexMethodId {
u2 classIdx; // 类的类型,指向 DexTypeId 列表的索引
u2 protoIdx; // 声明类型,指向 DexProtoId 列表的索引
u4 nameIdx; // 方法名,指向 DexStringId 列表的索引
};
DexMethodId 结构中的数据也都是索引值,指明了方法所在的类、方法的声明、方法名。从 0x104 处开始,有 5 个 DexMethodId 结构:
接下来是 kDexTypeClassDefItem,对应 DexHeader 中的 classDefsSize 和 classDefsOff 字段,指向的结构体是 DexClassDef,声明:
struct DexClassDef {
u4 classIdx; // 类的类型,指向 DexTypeId 列表的索引
u4 accessFlags; // 访问标志
u4 superclassIdx; // 父类的类型,指向 DexTypeId 列表的索引
u4 interfacesOff; // 接口,指向 DexTypeList 的偏移量
u4 sourceFileIdx; // 源文件名,指向 DexStringId 列表的索引
u4 annotationsOff; // 注解,指向 DexAnnotationsDirectoryItem 结构
u4 classDataOff; // 指向 DexClassData 结构的偏移量
u4 staticValuesOff; // 指向 DexEncodeedArray 结构的偏移量
};
struct DexClassData {
DexClassDataHeader header; // 指定字段与方法的个数
DexField* staticFields; // 静态字段,DexField 结构
DexField* instanceFields; // 实例字段,DexField 结构
DexMethod* directMethods; // 直接方法,DexMethod 结构
DexMethod* virtualMethods; // 虚方法,DexMethod 结构
};
struct DexClassDataHeader {
u4 staticFieldsSize; // 静态字段的个数
u4 instanceFieldsSize; // 实例字段的个数
u4 directMethodsSize; // 直接方法的个数
u4 virtualMethodsSize; // 虚方法的个数
};
struct DexField {
u4 fieldIdx; // 指向 DexFieldId 的索引
u4 accessFlags; // 访问标志
};
struct DexMethod {
u4 methodIdx; // 指向 DexMethodId 的索引
u4 accessFlags; // 访问标志
u4 codeOff; // 指向 DexCode 结构的偏移量
};
struct DexCode {
u2 registersSize; // 使用的寄存器个数
u2 insSize; // 参数个数
u2 outsSize; // 调用其他方法时使用的寄存器个数
u2 triesSize; // try/catch 语句个数
u4 debugInfoOff; // 指向调试信息的偏移量
u4 insnsSize; // 指令集个数,以 2 字节为单位
u2 insns[1]; // 指令集
// 2 字节空间用于结构对齐
// try_item[triesSize],DexTry 结构
// try/catch 语句中 handler 个数
// catch_handler_item[handlersSize],DexCatchHandler 结构
};
方法。第二个字段“81 80 04”为 0x10001,访问标志(可在这个网站查看:https://source.android.com/devices/tech/dalvik/dex-format )为 ACC_PUBLIC | ACC_CONSTRUCTOR
。第三个字段“cc 02”为 0x14c,指向 DexCode 结构invoke-direct
,格式为 0x35c。0x35c 的指令格式(可在这个网站查看:https://source.android.com/devices/tech/dalvik/instruction-formats )为“A|G|op BBBB F|E|D|C”,有八种表示方式,如下图三:[A=1] op {vC}, kind@BBBB
的方式,其中“vC”为 v0 寄存器,指令后面的“BBBB”和“F|E|D|C”都是 16 位的,“7010”后的两个 16 位都为 0,因此 BBBB=4 且 F=E=D=C=0,BBBB 为 kind@ 类型(指向 DexMethodId 列表的索引值),通过查找得到方法名
。指令“0e00”直接查表(网址:https://source.android.com/devices/tech/dalvik/dalvik-bytecode )(结果见下图)可知为“return-void”7010 0400 0000 invoke-direct {v0}, Ljava/lang/Object;.:()V
0e00 return-void
main
方法。第二个字段“09”为 0x9,访问标志为 ACC_PUBLIC | ACC_STATIC
。第三个字段“e4 02”为 0x164,指向 DexCode 结构new-instance vAA, type@BBBB
,格式为 0x21c。0x21c 的指令格式为 AA|op BBBB
,有五种表示方式(见下图三):op vAA, type@BBBB
的方式,其中的“vAA”为 v0 寄存器,BBBB=1,BBBB 为 type@ 类型(指向 DexTypeId 列表的索引值),通过查找得到类类型“LHello;”。至此,第一条指令分析完毕:2200 0100 new-instance v0, LHello;
。至此,第二条指令分析完毕:0710 0000 0000 invoke-direct {v0},LHello;.<init>:()V
sget-object
,格式为 0x21c。0x21c 上面也分析过。“01”表示 AA=0,即采用 op vAA, field@BBBB
的方式;寄存器为 v1。BBBB=0,表示查 DexFieldId 列表索引值为 0 的那一项。至此,第三条指令分析完毕:6201 0000 sget-object v1, Ljava/lang/system;.out:Ljava/io/PrintStream;
const/4 vA, #+B
,格式为 0x11n。0x11n 的指令格式为 B|A|op
,因为指令为“1252”,所以 B=5 且 A=2,即使用 v2 寄存器,常量为 int 类型的 5。至此,第四条指令分析完毕:1252 const/4 v2, 5
1233 const/4 v3, 3
invoke-virtual
,格式为 0x35c。又是分析过的,可知此处要分析“6e30 0100 2003”。A=3,G=0,表示采用 [A=3] op {vC, vD, vE}, kind@BBBB
。“2003”的小端字节序为“0320”,即F=0,E=3,D=2,C=0,表示使用 v0、v2、v3 寄存器。foo
。至此,第六条指令分析完毕:6e20 0100 2003 invoke-virtual {v0, v2, v3}, LHello;.foo:(II)I
move-result vAA
,格式为 0x11x,指令格式为 AA|op
,可知此处要分析“0a00”,AA=0,使用 v0 寄存器。至此,第七条指令分析完毕:0a00 move-result v0
[A=2] op {vC, vD}, kind@BBBB
。“0100”的小端字节序为“0001”,即F=0,E=0,D=0,C=1,表示使用 v1、v0 寄存器。println
。至此,第八条指令分析完毕:6e20 0300 0100 invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(I)V
2200 0100 new-instance v0, LHello;
0710 0000 0000 invoke-direct {v0},LHello;.:()V
6201 0000 sget-object v1, Ljava/lang/system;.out:Ljava/io/PrintStream;
1252 const/4 v2, 5
1233 const/4 v3, 3
6e20 0100 2003 invoke-virtual {v0, v2, v3}, LHello;.foo:(II)I
0a00 move-result v0
6e20 0300 0100 invoke-virtual {v1, v0}, Ljava/io/PrintStream;.println:(I)V
0e00 return-void
add-int
,格式为 0x23x,指令格式为 AA|op CC|BB
,可知此条指令要分析“9000 0304”。AA=0,CC=4,BB=3,即使用的寄存器为 v0、v3、v4。至此,第一条指令分析完毕:9000 0304 add-int v0, v3, v4
9101 0304 sub-int v1, v3, v4
mul-int/2addr
,格式为 0x12x,指令格式为 B|A|op
,可知此处要分析“b210”。B=1,A=0,使用的寄存器为 v0、v1。至此,第三条指令分析完毕:b210 mul-int/2addr v0, v1
0f00 return v0
9000 0304 add-int v0, v3, v4
9101 0304 sub-int v1, v3, v4
b210 mul-int/2addr v0, v1
0f00 return v0
至此,全部分析完毕
再回头看看之前列出的结构(如下表):kDexTypeCodeItem 与上面分析的 DexCode 结构对应;kDexTypeTypeList 与上面分析的 DexTypeList 结构对应;kDexTypeStringDataItem 指向 DexStringId 字符串列表的首地址;kDexTypeDebugInfoItem 指向调试信息偏移量,与 DexCode 的 debugInfoOff 字段指向的内容相同;kDexTypeClassDataItem 指向 DexClassData 结构;kDexTypeMapList 指向 DexMapItem 结构自身
类型 | 个数 | 偏移量 |
---|---|---|
kDexTypeHeaderItem | 0x1 | 0x0 |
kDexTypeStringIdItem | 0x10 | 0x70 |
kDexTypeTypeIdItem | 0x7 | 0xb0 |
kDexTypeProtoIdItem | 0x4 | 0xcc |
kDexTypeFieldIdItem | 0x1 | 0xfc |
kDexTypeMethodIdItem | 0x5 | 0x104 |
kDexTypeClassDefItem | 0x1 | 0x12c |
kDexTypeCodeItem | 0x3 | 0x14c |
kDexTypeTypeList | 0x3 | 0x1b4 |
kDexTypeStringDataItem | 0x10 | 0x1ca |
kDexTypeDebugInfoItem | 0x3 | 0x267 |
kDexTypeClassDataItem | 0x1 | 0x27b |
kDexTypeMapList | 0x1 | 0x290 |
if (flags & kDexParseVerifyChecksum) {
u4 adler = dexComputeChecksum(pHeader);
if (adler != pHeader->checksum) {
ALOGE("ERROR: bad checksum (%08x vs %08x)",
adler, pHeader->checksum);
if (!(flags & kDexParseContinueOnError)) {
goto bail;
}
}
else {
ALOGV("+++ adler32 checksum (%08x) verified", adler);
}
const DexOptHeader* oOptHeader = pDexFile->oOptHeader;
if (pOptHeader != NULL) {
adler = dexComputeOptChecksum(pOptHeader);
if (adler != pOptHeader->checksum) {
ALOGE("ERROR: bad opt checksum (%08x vs %08x)",
adler, pOptHeader->checksum);
if (!(flags & kDexParseContinueOnError)) {
goto bail;
}
}
else {
ALOGV("+++ adler32 opt checksum (%08x) verified", adler);
}
}
}
u4 dexComputeChecksum(const DexHeader* pHeader) {
const u1* start = (const u1*)pHeader;
uLong adler = adler32(0L, Z_NULL, 0);
const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
return (u4)adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
}
checksum 实际是调用 adler32() 完成计算的,整个步骤:跳过 DexHeader 的 magic 和 checksum 字段,将第三个字段到文件结尾作为计算的总数据长度,调用 Adler32 标准算法计算数据的 adler 值
dexComputeOptChecksum() 的代码:
u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader) {
cosnt u1* start = (const u1*)pOptHeader + pOptHeader->depsOffset;
const u1* end = (cosnt u1*)pOptHeader + pOptHeader->optOffset +
pOptHeader->optLength;
uLong adler = adler32(0L, Z_NULL, 0);
return (u4)adler32(adler, start, end - start);
}
ODEX 的 checksum 计算方法与 DEX 的一样,只是其取值范围是从 ODEX 文件头到最后的依赖库与辅助数据两个数据块
下一步是验证 signature,代码:
if (kVerifySignature) {
unsigned char sha1Digest[kSHA1Digest];
const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) + kSHA1DigestLen;
dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
char tmpBuf1[kSHA1DigestOutputLen];
char tmpBuf2[kSHA1DigestOutputLen];
ALOGE("ERROR: bad SHA1 digest (%s vs %s)",
dexSHA1DigestToStr(sha1Digest, tmpBuf1),
dexSHA1DigestToStr(pHeader-signature, tmpBuf2));
if (!(flags & kDexParseContinueOnError)) {
goto bail;
}
}
else {
ALOGV("+++ sha1 digest verified");
}
}
验证算法是:跳过 magic、checksum、signature 字段,调用 dexComputeSHA1Digest() 对 DEX 头部后面的数据进行 SHA-1 计算,然后将计算结果与 signature 字段中保存的值比较,若相等就通过,不相等则验证失败
dexComputeSHA1Digest() 的代码:
static void dexComputeSHA1Digest(const unsigned char* data,
size_t length, unsigned char digest[]) {
SHA1_CTX context;
SHA1Init(&context);
SHA1Update(&context, data, length);
SHA1Final(digest, &context);
}
这是 OpenSSL 库中计算 SHA-1 散列值的接口
回到分析流程。验证成功后,dvmDexFileOpenPartial() 调用 allocateAuxStructures(),设置与 DexFile 结构辅助数据相关的字段,执行后返回 rewriteDex()。接着,rewriteDex() 调用loadAllClasses(),加载 DEX 中所有的类(若这一步失败,程序等不到后面的优化与验证就推出了;若执行这一步没发生错误,程序会调用 verifyAndOptimizeClasses() 完成真正的验证工作)。loadAllClasses() 会调用 verifyAndOptimizeClass() 优化和验证具体的类,而且 verifyAndOptimizeClasses() 会细分这些工作,先调用 dvmVerifyClass() 进行验证,再调用 dvmOptimizeClass() 进行优化
dvmVerifyClass() 的实现代码位于 Android 系统源码文件 dalvik/vm/analysis/DexVerify.cpp 中。这个函数调用 verifyMethod() 对类的所有直接方法和虚方法进行验证。verifyMethod() 的具体工作:先调用 verifyInstructions() 验证方法中的指令及其数目的正确性,再调用 dvmVerifyCodeFlow() 验证代码流的正确性
dvmOptimizeClass() 的实现代码位于 Android 系统源码文件 dalvik/vm/analysis/Optimize.cpp 中。这个函数调用 optimizeMethod() 对类的所有直接方法和虚方法进行优化,优化的主要工作是进行指令替换,替换的优先级:volatile 替换最高,正确性替换其次,高性能替换最低。如,iget-wide 指令会根据优先级被替换成 volatile 形式的 iget-wide-volatile,而不是高性能的 iget-wide-quick
rewriteDex() 返回后,会再次调用 dvmDexFileOpenPartial() 验证 ODEX,并接着调用 dvmGenerateRegisterMaps() 填充辅助数据区的结构。完成对结构的填充后,会调用 updateChecksum() 重写 DEX 的 shecksum 值。接下来,调用 writeDependencies() 和 writeOptData()
dexopt 的整个验证和优化过程就是这样
if-nez
的 Opcode,只要将其改成 if-eqz
指令的 Opcode 即可,查看 Dalvik 指令集列表(如下),可知 if-eqz
指令的 Opcode 为“38”int endian = ReadInt(0x28); // endian_flag
if (endian == 0x12345678) {
LittleEndian();
}
else {
BigEndian();
}
uchar sha1[20];
ReadBytes(sha1, 0xc, 20);
Printf("src sha1: ");
uint i = 0;
for (i = 0; i < 20; i++) {
Printf("%02x", sha1[i]);
}
Printf("\n");
uchar checksum[20];
ChecksumAlgBytes(CHECKSUM_SHA1, checksum, 0x20);
Printf("calced sha1: ");
for (i = 0; i < 20; i++) {
Printf("%02x", checksum[i]);
}
Printf("\n");
int adler32 = ReadInt(0x8);
if (Memcmp(checksum, sha1, 20) != 0) {
WriteBytes(checksum, 0xc, 20);
}
else {
Printf("same sha1\n");
}
int adler32_ = Checksum(CHECKSUM_ADLER32, 0xc);
Printf("src adler32: %x\n", adler32);
Printf("calced adler32:%x\n", adler32_);
if (adler32_ != adler32) {
WriteInt(0x8, adler32_);
}
else {
Printf("same adler32\n");
}
Printf("Done.\n");
...
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(context);
Multidex.install(this);
}
}
apktoll d app-release.apk -o outdir