在逆向時(shí)并巍,struct結(jié)構(gòu)對(duì)象不期而遇的蹦入眼簾。在頭文件中它是那么的平凡而不值一提蜕企,但到了匯編代碼中,它完完全全的變了個(gè)模樣冠句,根本無法第一時(shí)間的辨別出來轻掩。必須要消耗比較多的能量從那大片的偏移地址中一一定位出struct的各個(gè)成員變量。這里給出我自己的分析思路以供參考:
首先給出classdump得到的結(jié)構(gòu)體定義:
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中有對(duì)齊這一概念:為了訪問速度與效率懦底,struct成員在內(nèi)存布局時(shí)會(huì)自動(dòng)的增添使用的比特位使成員變量使用的地址位正好為4(32位)或者8(64位)的倍數(shù)唇牧。因此SGKeySeqNoPair中_field1所占用的位數(shù)是8字節(jié)而不是2字節(jié)。最后可以用下面的代碼來驗(yàn)證關(guān)于成員布局位數(shù)的猜測(cè)聚唐。
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跟蹤并逆向代碼了丐重,下圖給出了使用這兩個(gè)struct的匯編代碼截圖,其中x20中保存的是SGKeyInfoStruct數(shù)組的地址杆查,x19為跳出循環(huán)條件值扮惦,ldrh成w9后值為1024。
可以看到這里對(duì)struct成員變量的取值再也沒有熟悉的OC屬性使用的objc_msgSend命令了根灯,全部變成了那一個(gè)個(gè)的相對(duì)于寄存器x20/x24的位置偏移径缅。如何根據(jù)這些偏移確定代碼中使用的具體成員變量呢?
add x24, x20, #0x20表示將x24指向SGKeyInfoStruct數(shù)組偏移32比特烙肺,一個(gè)SGKeyInfoStruct大小為40比特纳猪,那么x24就是指向SGKeyInfoStruct對(duì)象field3成員的_field2。
ldurh w8, [x24, #-0x18]將x24向后偏移24比特的半字賦值給w8桃笙,field3->field2偏移24應(yīng)該指向的是field2->field1氏堤,也就是SGKeySeqNoPair對(duì)象中的field1[2]數(shù)組。
同樣可以分析出ldurh w8, [x24, #-0x8]載入的是field3->field1[2]數(shù)組,ldurh x0, [x24, #-0x10]載入的是field2->field2對(duì)象鼠锈,而add x24, x24, #0x28將x24指向了數(shù)組的下一個(gè)序號(hào)值闪檬。
綜合以上,可以大致的逆向出該段代碼如下:
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將內(nèi)存地址上的數(shù)值打印出來輔助分析购笆,具體命令為memory read --size 8 --format x 0xxxxxxxxx:
總結(jié)
對(duì)struct進(jìn)行逆向最重要的一點(diǎn)是獲得struct的頭文件定義粗悯,這是通過位置偏移分析代碼的基礎(chǔ)。有了定義同欠,就可以分析struct成員變量的對(duì)齊情況样傍,如果定義比較復(fù)雜,可以將根據(jù)定義建立測(cè)試工程輔助計(jì)算成員的內(nèi)存布局铺遂。
其次是耐心與細(xì)心衫哥,十六進(jìn)制數(shù)據(jù)的加減法最好不要用心算,好在MAC上提供了計(jì)算器這個(gè)工具襟锐,交給它吧撤逢,保證不會(huì)出問題。