DEX file parsing - string_ids parsing

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_idsthe analysis.

1. string_ids structure

In androidthe aospsource code, string_idsthe structure is as follows:
aospSource 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);
  };

StringIdIt 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 DexFilethe class StringIdis 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
insert image description here
insert image description here

3. string_ids parsing

javalanguage 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

Guess you like

Origin blog.csdn.net/qq_35993502/article/details/123265193