逆向过程中遇到struct时的分析思路
在逆向时,struct结构对象不期而遇的蹦入眼帘。在头文件中它是那么的平凡而不值一提,但到了汇编代码中,它完完全全的变了个模样,根本无法第一时间的辨别出来。必须要消耗比较多的能量从那大片的偏移地址中一一定位出struct的各个成员变量。这里给出我自己的分析思路以供参考:
首先给出classdump得到的结构体定义:
struct SGKeyInfoStruct {
unsigned long long _field1; //8
struct SGKeySeqNoPair _field2; //16
struct SGKeySeqNoPair _field3; //16
};
struct SGKeySeqNoPair {
unsigned char _field1[2]; //8
id _field2; //8
};
必须要知道的是struct中有对齐这一概念:为了访问速度与效率,struct成员在内存布局时会自动的增添使用的比特位使成员变量使用的地址位正好为4(32位)或者8(64位)的倍数。因此SGKeySeqNoPair中_field1所占用的位数是8字节而不是2字节。最后可以用下面的代码来验证关于成员布局位数的猜测。
int main(int argc, const char * argv[]) {
@autoreleasepool {
printf("int =%u,char=%u,double=%u id=%u\n",sizeof(int),sizeof(char),sizeof(double), sizeof(id));
printf("SGKeySeqNoPair =%u SGKeyInfoStruct =%u\n",sizeof(struct SGKeySeqNoPair),sizeof(struct SGKeyInfoStruct));
}
return 0;
}
程序输出结果
接下来就是使用lldb跟踪并逆向代码了,下图给出了使用这两个struct的汇编代码截图,其中x20中保存的是SGKeyInfoStruct数组的地址,x19为跳出循环条件值,ldrh成w9后值为1024。
汇编中的struct
可以看到这里对struct成员变量的取值再也没有熟悉的OC属性使用的objc_msgSend命令了,全部变成了那一个个的相对于寄存器x20/x24的位置偏移。如何根据这些偏移确定代码中使用的具体成员变量呢?
add x24, x20, #0x20表示将x24指向SGKeyInfoStruct数组偏移32比特,一个SGKeyInfoStruct大小为40比特,那么x24就是指向SGKeyInfoStruct对象field3成员的_field2。
ldurh w8, [x24, #-0x18]将x24向后偏移24比特的半字赋值给w8,field3->field2偏移24应该指向的是field2->field1,也就是SGKeySeqNoPair对象中的field1[2]数组。
同样可以分析出ldurh w8, [x24, #-0x8]载入的是field3->field1[2]数组,ldurh x0, [x24, #-0x10]载入的是field2->field2对象,而add x24, x24, #0x28将x24指向了数组的下一个序号值。
综合以上,可以大致的逆向出该段代码如下:
SGKeyInfoStruct sgkey[] = xxx;
for(int i = 0; i < count; i++){
SGKeySeqNoPair* pair2 = sgkey[i]->field2;
SGKeySeqNoPair* pair3 = sgkey[i]->field3;
if(pair2->field1 == 1024){
key = pair2->field2;
break;
}else if(pair3->field1 == 1024){
key = pair3->field2;
break;
}
}
逆向过程中可以使用debugserver-lldb将内存地址上的数值打印出来辅助分析,具体命令为memory read --size 8 --format x 0xxxxxxxxx:
struct对象数组
总结
对struct进行逆向最重要的一点是获得struct的头文件定义,这是通过位置偏移分析代码的基础。有了定义,就可以分析struct成员变量的对齐情况,如果定义比较复杂,可以将根据定义建立测试工程辅助计算成员的内存布局。
其次是耐心与细心,十六进制数据的加减法最好不要用心算,好在MAC上提供了计算器这个工具,交给它吧,保证不会出问题。