前言:自己每天记录的学习笔记,可能不完善,初学逆向,请多多指教
上一篇文章的注册机简单的实现如下:
这里测试的被md5加密的值为:admin
#include
int main(){
// 输入的注册表首先进行md5加密
char szCode[] = "21232f297a57a5a743894a0e4a801fc3";
//
for (int i = 0; i < strlen(szCode); i += 2){
//printf("%c", szCode[i] & 0xFF);
szCode[i] & 0xFF;
}
// 222275aa4840481c
return 0;
}
--------------------------------------
class文件和dex文件之间的关系:
当java程序编译成class后,还需要使用dx工具将所有的class文件整合到一个dex文件,目的是其中各个类能够共享数据,在一定程度上降低了冗余,同时也是文件结构更加经凑,实验表明,dex文件是传统jar文件大小的50%左右
class文件生成dex文件,命令如下:
dx --dex --output TestMain.dex TestMain.class
在手机(这里测试的是dalvik虚拟机)中进行测试运行:
adb push TestMain.dex /storage/emulated/0
dalvikvm -cp /sdcard/TestMain.dex TestMain
接下来开始解析Dex文件格式:
1、DEX Header文件头
如下图所示
作用:记录了一些当前文件的信息以及其他数据结构在文件中的偏移量,比如可以在里面看到一些入 字符串列表 类型列表 原型列表 方法列表 类定义列表等的数据结构
用010editor编辑工具进行观察学习,显示如下,总共占0x70大小
magic(魔数):
1、 一般是常量,用来标记 DEX 文件
2、字符串格式为 dex\n035\0,十六进制为 0x6465780A30333500,感觉跟PE的MZ标志0X5A4D一样,用来作为标识
checksum (校验码):
1、checksum是对去除 magic(DEX标识头) 、 checksum 以外的文件部分作 alder32 算法得到的校验值,用于判断 DEX 文件是否被篡改
2、 signature (签名)是对除去 magic 、 checksum 、 signature 以外的文件部分作 sha1算法 得到的文件哈希值。
endianTag(存储方式):
1、endianTag用于标记 DEX 文件是大端表示还是小端表示(在intel 8086CPU x86 也是用的小端存储,其实也就是低位字节在低地址 ),由于 DEX 文件是运行在 Android 系统中的,所以一般都是小端表示,这个值也是恒定值 0x12345678
其余部分分别标记了 DEX 文件中其他各个数据结构的个数和其在数据区的偏移量(跟PE寻找相应的表的地址的时候其实很相似)
用C来实现dex文件头的解析很简单如下代码实现:
2、String_ids
接着就是字符串列表的数据结构解析,也就是dex文件头中的stringIdsSize和stringIdsOff这两个数据
注意:需要先知道的就是dex文件头中的这些数据一般都是偏移量,所以并不是真正的地址,还需要加上基址才是真正的对应的数据结构的位置
String_ids的数据结构:
struct DexStringId {
u4 stringDataOff;
};
这个结构体占4个字节,结构体中只有一个成员,并且这个成员表示的是每个字符串在data区的偏移量。
根据偏移量能拿到对应字符串,如下可以看到0x70开始的前4个字节为 0x000001AA,那么偏移量为0x01AA,跟过去可以看到(右图)所示,但是可以看到红色标记的一共是9个字节啊,其实第一个字节代表的字符串的长度,这里是8,那么就往后8个字节,也就是
C实现的代码如下:
3、Type_ids
接着就是类型列表的数据结构解析,也就是dex文件头中的typeIdsSize和typeIdsOff这两个数据
struct DexTypeId {
u4 descriptorIdx;
};
这个的数据结构如上,所以大家应该会猜到可能就是啥的索引值index,的确它这个就是一个索引值,并且是String_ids中的索引值,因为类型的名称也是存储在String_ids中的,因为大家可以看到上面的图中确实存储着类型的值,比如Ljava/lang/String等等的
那么思路其实也就是拿到这个索引值,然后再套回String_ids中进行查找,实现代码如下:
4、Proto_ids
接着就是方法列表的数据结构解析,也就是dex文件头中的protoIdsSize和protoIdsOff这两个数据,这个数据结构描述了dex文件中所有的方法声明!
struct DexProtoId {
u4 shortyIdx; /* 指向 string_ids ,表示方法声明的字符串*/
u4 returnTypeIdx; /* 指向 type_ids ,表示方法的返回类型
u4 parametersOff; /* 指向方法参数列表的偏移量,DexTypeList结构体
}
这个数据结构如上,简单的说下其实前两个的都是索引值,代表的是string_ids中的索引值(和上面的解析一样),第二个就是type_ids的索引值(和上面的解析一样),唯一不同的也就是第三个,第三个是一个偏移量,这个Dex文件的基址加上parametersOff偏移量 则是指向一个结构体DexTypeList,然后还会发现这个结构体中还有一个结构体也就是DexTypeItem,这是一个变长数组,根据size决定,也就是参数的个数
struct DexTypeList {
u4 size; //DexTypeItem的个数
DexTypeItem list[1]; //DexTypeItem变长数组
};
struct DexTypeItem {
u2 typeIdx; //DexTypeId中的索引下标
};
解析思路就是 基址加上parametersOff的偏移量,然后拿到DexTypeList,然后根据size循环,把DexTypeList加上基址进行获取对应的参数,实现代码如下:
今天下午学习的时候就解析了这四个,每天继续肝完就可以了,谢谢大家看到这里了!