DEX文件解析 - proto_ids解析

在上一篇中介绍了type_ids的解析,那么接下来就要学习proto_ids的解析。

1. proto_ids结构

androidaosp源码中,proto_ids的结构如下:
aosp源码位置:art/libdexfile/dex/dex_file.h

ProtoId

// Raw proto_id_item.
struct ProtoId {
  dex::StringIndex shorty_idx_;     // index into string_ids array for shorty descriptor
  dex::TypeIndex return_type_idx_;  // index into type_ids array for return type
  uint16_t pad_;                    // padding = 0
  uint32_t parameters_off_;         // file offset to type_list for parameter types

 private:
  DISALLOW_COPY_AND_ASSIGN(ProtoId);
};

StringIndex

class StringIndex {
 public:
  uint32_t index_;
  ....  
  ....
};

TypeIndex

class TypeIndex {
 public:
  uint16_t index_;
  ....  
  ....
};

protoId的结构可以看出以下几点:

  1. dex::StringIndex shorty_idx_: 无符号int类型,占4个字节。字符串索引列表的索引/下标,表示的是一个简短描述符
  2. dex::TypeIndex return_type_idx_:无符号int类型,占2个字节。类型索引列表的索引/下标,表示的是返回值类型
  3. uint16_t pad_:无符号int类型,占2个字节。用于填充,无实际意义,默认为0
  4. uint32_t parameters_off_:无符号int类型,占4个字节。参数列表在dex文件中偏移量。指向一个参数列表。

分析方法参数列表的文件偏移量(uint32_t parameters_off_)对应的方法参数列表结构

  1. 通过GetProtoParameters方法获取到方法的参数列表
const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
  return DataPointer<TypeList>(proto_id.parameters_off_);
}
  1. TypeList结构中,可以发现其结构为:
    uint32_t size_ :无符号int类型,占4个字节。表示的是参数列表的大小(参数的个数)
    TypeItem list_[1]:参数列表, TypeItem类型内部存储着TypeIndex,而TypeIndex有存储着uint16_t index_,所以TypeItem类型占2个字节。每2个字节表示1个参数
class TypeList {
  public:
  uint32_t Size() const {
    return size_;
  }

  const TypeItem& GetTypeItem(uint32_t idx) const {
    DCHECK_LT(idx, this->size_);
    return this->list_[idx];
  }
 ......
 ......
 private:
  uint32_t size_;  // size of the list, in entries
  TypeItem list_[1];  // elements of the list   
  DISALLOW_COPY_AND_ASSIGN(TypeList);
};

2. 010Editor解析

如图,仔细观察
在这里插入图片描述

在这里插入图片描述

3. proto_ids解析

java语言解析proto_ids

/**
* 解析ProtoIds
* @param raf
* @return
*/
private static List<ProtoId> toParseDexProtoIds(RandomAccessFile raf) {
    try {
        List<ProtoId> protoIdList = new ArrayList<>();
        //原型索引列表的文件偏移量
        int proto_ids_off = mDexHeader.getProto_ids_off();
        //原型索引列表的大小
        int proto_ids_size = mDexHeader.getProto_ids_size();
        //偏移到原型索引列表的开始位置
        raf.seek(proto_ids_off);
        for (int i=0;i<proto_ids_size;i++) {
        	//原型简短描述符,是字符串索引列表中的索引/下标
            int shorty_idx = NumConversion.byteToInt(readData(raf, 4),false);
            //返回值类型,是类型索引列表中的索引/下标。
            //return_idx 占2个字节,读取4个字节是由于剩下的2个字节是用于填充的
            int return_idx = NumConversion.byteToInt(readData(raf, 4),false);
            //方法参数列表在文件中的偏移量
            int parameter_off = NumConversion.byteToInt(readData(raf,4),false);
            if(parameter_off > 0) {
                //记录偏移位置
                long curOff = raf.getFilePointer();
                //偏移到参数列表位置
                raf.seek(parameter_off);
                //获取参数个数
                int parameter_size = NumConversion.byteToInt(readData(raf,4),false);
                //获取参数索引
                int[] parameterIds = new int[parameter_size];
                for (int j=0;j<parameterIds.length;j++) {
                    int parameter_id = NumConversion.byteToInt(readData(raf,2),false);
                    parameterIds[j] = parameter_id;
                }
                //获取数据 - 原型简短描述符
                StringId shortyStringId = mStringIds.get(shorty_idx);
                String shortyData = new String(shortyStringId.getData());
                //获取原型数据 - 返回值数据
                TypeId typeId = mTypeIds.get(return_idx);
                StringId stringId = mStringIds.get(typeId.getTypeDescriptorIdx());
                String returnData = new String(stringId.getData());
                //获取原型数据 - 参数列表数据
                StringBuilder sb = new StringBuilder("(");
                for (int j=0;j<parameterIds.length;j++) {
                    int parameter_idx = parameterIds[j];
                    TypeId typeId1 = mTypeIds.get(parameter_idx);
                    StringId paramterStringId = mStringIds.get(typeId1.getTypeDescriptorIdx());
                    String paramterData = new String(paramterStringId.getData());
                    sb.append(paramterData);
                    if (j == parameterIds.length-1) {
                        sb.append(")");
                    }else if(parameterIds.length != 1){
                        sb.append(", ");
                    }
                }
                //创建ProtoId实体类
                ProtoId protoId = new ProtoId();
                protoId.setShortyIdx(shorty_idx);
                protoId.setReturnIdx(return_idx);
                protoId.setParameterOff(parameter_off);
                protoId.setParameter_size(parameter_size);
                protoId.setParameterIdxs(parameterIds);
                protoIdList.add(protoId);
                //打印数据
                System.out.println("索引:"+i+" shorty:"+shortyData+" -> "+returnData+" "+sb.toString());
                raf.seek(curOff);
            }else {
                //获取数据 - 原型简短描述符
                StringId shortyStringId = mStringIds.get(shorty_idx);
                String shortyData = new String(shortyStringId.getData());
                //获取原型数据 - 返回值数据
                TypeId typeId = mTypeIds.get(return_idx);
                StringId stringId = mStringIds.get(typeId.getTypeDescriptorIdx());
                String returnData = new String(stringId.getData());
                //创建ProtoId实体类
                ProtoId protoId = new ProtoId();
                protoId.setShortyIdx(shorty_idx);
                protoId.setReturnIdx(return_idx);
                protoId.setParameterOff(parameter_off);
                protoIdList.add(protoId);
                //打印数据
                System.out.println("索引:"+i+" shorty:"+shortyData+" -> "+returnData+" ()");
            }

        }
        return protoIdList;
    }catch (Exception 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;
}

实体类ProtoId

/**
 * 原型索引列表(原型/方法签名)
 */
public class ProtoId {
    private int shortyIdx;;  //原型简写,字符串索引列表中的索引
    private int returnIdx;  // 返回值索引,在类型索引列表中的索引
    private int parameterOff; //参数列表偏移
    //拓展
    private int parameter_size; //参数的个数
    private int[] parameterIdxs; //参数索引,在类型索引列表中的所索引
}

工具类NumConversion

public class NumConversion {

    public static int byteToInt(byte[] bytes,boolean isBigEndian) {
        if (bytes.length <=0 || bytes.length > 4) return -1;
        int result = 0;
        for (int i=0;i<bytes.length;i++) {
            int b ;
            if(isBigEndian){
                b = (bytes[i] & 0xFF) << (8*(bytes.length-1-i));
            }else {
                b = (bytes[i] & 0xFF) << (8*i);
            }
            result = result | b;
        }
        return result;
    }
}

asjhan for Android reverse

posted @ 2022-03-20 22:56  asjhan  阅读(32)  评论(0编辑  收藏  举报  来源