DEX文件解析 - proto_ids解析
在上一篇中介绍了type_ids的解析,那么接下来就要学习proto_ids
的解析。
1. proto_ids结构
在android
的aosp
源码中,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
的结构可以看出以下几点:
dex::StringIndex shorty_idx_
: 无符号int
类型,占4
个字节。字符串索引列表的索引/下标
,表示的是一个简短描述符
dex::TypeIndex return_type_idx_
:无符号int
类型,占2
个字节。类型索引列表的索引/下标
,表示的是返回值类型
uint16_t pad_
:无符号int
类型,占2
个字节。用于填充,无实际意义,默认为0uint32_t parameters_off_
:无符号int
类型,占4
个字节。参数列表在dex文件中偏移量。指向一个参数列表。
分析方法参数列表的文件偏移量(uint32_t parameters_off_)
对应的方法参数列表结构
- 通过
GetProtoParameters
方法获取到方法的参数列表
const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
return DataPointer<TypeList>(proto_id.parameters_off_);
}
- 在
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