DEX file parsing - string_ids parsing
In the previous article, the analysis of the header was introduced , so the next step is to learn string_ids
the analysis.
1. string_ids structure
In android
the aosp
source code, string_ids
the structure is as follows:
aosp
Source code location: art/libdexfile/dex/dex_file.h
// Raw string_id_item.
struct StringId {
uint32_t string_data_off_; // offset in bytes from the base address
private:
DISALLOW_COPY_AND_ASSIGN(StringId);
};
StringId
It is a type in the code 结构体
, where the internal attribute string_data_off_
is an unsigned 4个字节
data, which represents the offset of the string data
In DexFile
the class StringId
is a pointer variable, which actually uses pointers to represent arrays
class DexFile {
// Points to the base of the string identifier list.
const StringId* const string_ids_;
}
2. Analysis of 010Editor
As shown in the picture, observe carefully
3. string_ids parsing
java
language analysisstring_ids
/**
* 解析StringIds
* @param raf
* @return 字符串索引列表
*/
private static List<StringId> toPaeseDexStringIds(RandomAccessFile raf) {
try {
//字符串索引列表 stringIdsList
List<StringId> stringIdsList = new ArrayList<>();
//获取字符串索引列表的文件偏移量(string_ids_off),偏移到指定位置,解析字符串列表
raf.seek(mDexFile.getString_ids_off());
//获取字符串索引列表的大小,循环读取字符串池
for (int i=0;i<mDexFile.getString_ids_size();i++) {
//获取字符串数据的偏移量
int string_data_off = NumConversion.byteToInt(readData(raf, 4),false);
//记录当前偏移值
long curOff = raf.getFilePointer();
//偏移到当前字符串数据所在位置
raf.seek(string_data_off);
//当前字符串所在位置,需要读取的结构大致如下
// ------------------------
// | | |
// |size|string_data[size]|
// | | |
// ------------------------
//其中 size 表示的是字符串的字节数,size 采用 uleb128类型进行存储
//string_data[size] 表示的是具体的数据
//读取字符串的大小
byte uleb128Byte = raf.readByte();
ByteBuffer byteBuffer = ByteBuffer.allocate(5);
byteBuffer.put(uleb128Byte);
while ((uleb128Byte & 0xff) > 0x7f) {
//uleb 判断是否需要下一个字节,最高位为1时表示需要 0x7f: 01111 1111
uleb128Byte = raf.readByte();
byteBuffer.put(uleb128Byte);
}
byte[] uLeb128Bytes = byteBuffer.array();
//将uleb128类型的数据进行解码转换成 int
int decodeuleb128=NumConversion.byteToIntformUleb128(uLeb128Bytes);
byte[] stringDataByte = null;
if(decodeuleb128 > 0) {
//根据解码的字符串大小,读取字符串数据
stringDataByte = readData(raf, decodeuleb128);
System.out.println(i+" uleb未解码结果:"+Arrays.toString(uLeb128Bytes) +" 解码uleb128: "+decodeuleb128+" 字符串:"+new String(stringDataByte));
}
//创建StringId类,保存数据
StringId stringId = new StringId();
stringId.setString_data_off(string_data_off);
stringId.setString_data_size(decodeuleb128);
stringId.setData(stringDataByte);
stringIdsList.add(stringIds);
//恢复偏移
raf.seek(curOff);
}
return stringIdsList;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static byte[] readData(RandomAccessFile raf,int limit) {
byte[] buff = new byte[limit];
try {
raf.read(buff);
} catch (IOException e) {
e.printStackTrace();
}
return buff;
}
工具类:NumConversion
/**
* uleb128转int
* ULEB128 需要的字节大约在: 1~5字节
* 转换规则:每一个字节只有7位为有效位,第一个字节若最高位为 1 ,表示ULEB128需要使用第二个字节,
* 如果第二个字节最高位为 1 ,则表示需要使用第三个字节,以此类推,知道最后一个字节最
* 高位为0为止。
*在转换过程中采用了小端字节序
* @return
*/
public static int byteToIntformUleb128(byte[] bytes) {
if ((bytes[0]&0xff) > 0x7f) {
// 0x7f -> 0111 1111 = 127
//最高位为 1 ,表示需要第二个字节
int result = ((bytes[0]&0xff) & 0x7f) | ((bytes[1]&0xff) & 0x7f) << 7 ;
if((bytes[1]&0xff) > 0x7f) {
//最高位为 1 ,表示需要第三个字节
result = result | ((bytes[2]&0xff) & 0x7f) << 14;
if((bytes[2]&0xff) > 0x7f) {
//最高位为 1 ,表示需要第四个字节
result = result | (bytes[3] & 0x7f) << 21;
if((bytes[3]&0xff) > 0x7f) {
//最高位为 1 ,表示需要第五个字节
result = result | ((bytes[4]&0xff) & 0x7f) << 28;
}
}
}
return result;
}
return bytes[0] ; //说明 若第一个字节的大小在 0~127范围内,没有超过java中byte的最大值,没有溢出
}
实体类:StringId
public class StringId {
private int string_data_off; //字符串数据偏移
private int string_data_size; //字符串数据大小(uleb128类型解码的大小)
private byte[] data; //字符串数据
public int getString_data_off() {
return string_data_off;
}
public void setString_data_off(int string_data_off) {
this.string_data_off = string_data_off;
}
public int getString_data_size() {
return string_data_size;
}
public void setString_data_size(int string_data_size) {
this.string_data_size = string_data_size;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
}
asjhan for Android reverse