授人与鱼不如授人与渔,作为初学者,最重要的是学会查看官方文档,自主学习。
首先放上google官方文档对dex解释的链接,里面相当详细的介绍了dex的格式的组成。
https://source.android.google.cn/devices/tech/dalvik/dex-format#header-item
上一篇文章解析了map_list,今天来解析dex中的字符串,老规矩,先来看官方文档的介绍。
1、string_ids_size记录了字符串的数量
2、string_ids_off记录了字符串从文件开头到字符串标识的偏移量
3、该偏移量(如果为非零值)应该是到 string_ids 区段开头的偏移量。
那么string_ids是什么呢?
从dex完整格式中可以看到有这么一个结构
string_ids |
string_id_item[] |
字符串标识符列表。这些是此文件使用的所有字符串的标识符,用于内部命名(例如类型描述符)或用作代码引用的常量对象。此列表必须使用 UTF-16 代码点值按字符串内容进行排序(不采用语言区域敏感方式),且不得包含任何重复条目。 |
|
|
|
看到这里就很明显了,string_ids是一个string_id_item格式的数组,string_ids_size记录了此数组的数量,string_ids_off记录了从文件开头到此数组的偏移量。
string_id_item格式
string_data_off //uint 从文件开头到此项的字符串数据的偏移量。该偏移量应该是到 data 区段中某个位置的偏移量,且其中的数据应采用下文中“string_data_item”指定的格式。 没有偏移量对齐要求。
string_id_item只是一个四字节的偏移量,没必要定义一个结构体。
接下来看看string_data_item格式:
utf16_size uleb128// 此字符串的大小;以 UTF-16 代码单元(在许多系统中为“字符串长度”)为单位。也就是说,这是该字符串的解码长度(编码长度隐含在 0 字节的位置)。
data ubyte[] //一系列 MUTF-8 代码单元(又称八位字节),后跟一个值为 0 的字节。请参阅上文中的“MUTF-8(修改后的 UTF-8)编码”,了解有关该数据格式的详情和讨论。
浅析
utf16_size记录了字符串的大小,格式是uleb128,uleb128是一个不固定的长度,c语言中并没有能表示这种变化长度的类型,(不知道uleb128是什么的,去官方文档或者我的第一篇文章看一下),再来看第二个data的格式是 ubyte[],并且后跟一个值为 0 的字节,而char*正好是以0来结尾的,而char*通过每个字节转换正好可以解析出utf16_size的值和占用的字节。
转换uleb128函数
//返回长度
WORD getLEB128Lenght(char* str)
{
//初始化长度为1
WORD index = 1;
//如果值为0,返回长度
if (*str)
{
// 如果值不为0,查看最高位是否为1
while (*str++ & 0x80)
{
//长度++
++index;
}
}
return index;
}
//返回值,此处比之前改进了一下,因为我发现进位时会因为宽度问题导致数值错误
WORD LEB128toInt(char* str)
{
DWORD value = 0;
WORD index = 0;
if (*str)
{
do {
//*str & 0X7F是吧最高位设置为0
DWORD m = (*str & 0X7F) << (index * 7);
value += m;
++index;
} while (*str++ & 0x80);
}
return value;
}
解析字符串
//此处我选择写一个函数,因为后面用到的地方太多了。
//第一个参数是dex_header结构体 之前有说怎么获取,这里不提
//第二个参数是指向string_id_item地址的指针 文件开始的地址加上string_ids_off中的偏移
//第三个参数是找的是第几个string_data_item 取值范围1到string_ids_size直接
char* getString(PIMAGE_DEX_HEADER IpParameter , LPDWORD virtualAddress_string,DWORD index)
{
//通过取值得到地址的偏移 *(virtualAddress_string + index),再加上文件开始的地址,
char* str = (char*)((DWORD)IpParameter + *(virtualAddress_string + index));
//计算字符串长度所占的字节数
DWORD n = getLEB128Lenght(str);
//返回正确的字符串首地址
return str + n;
}
c语言解析0结尾的字符串用%s,puts之类的都可以。
|