PE節表詳細分析
0x00 前言
上一篇文章我們學習了PE結構中的PE頭,我之前說過PE文件結構是PE頭->節表->每個節,所以我們掌握了節表后也就知道怎么去獲取每個節了。(當然后面還有輸入表,輸出表這些比較重要的東西。這些知識在后面的文章詳細介紹。)
0x01 PE節表分析
節表結構
PE節表位於PE頭的下面,PE節表中記錄了各個節表的起始位置、大小,以及在內存中偏移位置和屬性。
其結構體如下:
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//*[1]:節名
union {
DWORD PhysicalAddress;
DWORD VirtualSize; //*[2]:對其前節大小,內存中節大小
} Misc;//聯合體
DWORD VirtualAddress; //*[3]:內存中偏移
DWORD SizeOfRawData; //*[4]:文件對其后節大小
DWORD PointerToRawData; //*[5]:文件中節位置
DWORD PointerToRelocations;//
DWORD PointerToLinenumbers;//
WORD NumberOfRelocations; //
WORD NumberOfLinenumbers; //
DWORD Characteristics; //*[6]:節屬性的標志
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
節表數量
節表數量不在節表中,而是在上一篇文章中已經介紹過,在標准PE頭中。標准PE頭中有一個成員是NumberOfSections
他是節表的數量。
節表名字
節表名字是各個節表的第一個成員,首先我們根據PE頭大小來確定節表所在位置即DOS頭+垃圾數據+NT頭
,下面在010Editor演示具體數據。
節表中第一個數據是節名
,節名一共占用的數據大小是8個字節
, 演示中第一個節名是.text
。
節表大小
節表中真實的大小在VirtualAddress
中,也就是節名后的4個字節。他所代表的意思是對其前的大小也是在內存中的大小。
而在他后4四節之后的4字節數據,代表的是對其后的大小也是文件中的大小SizeOfRawData
。
節位置
節位置都是在節大小之后,占用大小也是為4字節。VirualAddress
后的4字節代表的是內存中節偏移
,SizeOfRawData
后的4字節代表的是文件中節位置
。
在內存中鎖定具體的節位置需要內存地址
+節偏移
得到的地址才是真實的內存節所在的位置,而在文件中的節位置則可以根據地址直接索引到,不需要+文件地址因為文件地址開始就是0。
內存地址在可選PE頭的ImageBase
中,所以我們需要先獲取到ImageBase的值這里是0x00400000
,所以加上偏移位置就是0x00401000
。
一般程序在沒有被修改的情況下,默認.text
段就是程序用來放代碼的地方。所以我們也可以用OD直接載入程序,然后跳到.text段的位置可以看到程序的匯編代碼。
(小知識:OD載入程序就是模擬了程序拉伸的過程,程序在內存中的樣子。)
節表屬性
節表中的最后一個數據也是最重要的,他代表了這個節是否可讀
、可寫
、可執行
或者是否包含可執行代碼、初始化、未初始化
的數據。所以一般我們判斷一個段是否是代碼段就是根據這個屬性的值來判斷的,因為節表名是可以改的,比如我把隨便一個節表名改成.text、.code。那你就覺得他是代碼段了嗎?
代碼段的屬性一般是0x60000020
,其中這4字節的數據,他每一位都對應下面表格中的數據。下面我們把0x60000020
來拆分一下,首先最后2位對應下面的包含可執行代碼
的數據,然后最高位的6
對應下面的(該塊可執行)+(該塊可讀)
的值。所以代碼段的屬性就是:(包括可執行代碼)、(可執行)、(可讀)
。
--> 標志(屬性塊) 常用特征值對照表:<--
[值:00000020h](*包含可執行代碼)
[值:00000040h](*該塊包含已初始化的數據)
[值:00000080h](*該塊包含未初始化的數據)
[值:00000200h][Section contains comments or some other type of information.]
[值:00000800h][Section contents will not become part of image.]
[值:00001000h][Section contents comdat.]
[值:00004000h][Reset speculative exceptions handling bits in the TLB entries for this section.]
[值:00008000h][Section content can be accessed relative to GP.]
[值:00500000h][Default alignment if no others are specified.]
[值:01000000h][Section contains extended relocations.]
[值:02000000h][Section can be discarded.]
[值:04000000h][Section is not cachable.]
[值:08000000h][Section is not pageable.]
[值:10000000h](*該塊為共享塊).
[值:20000000h](*該塊可執行)
[值:40000000h](*該塊可讀)
[值:80000000h](*該塊可寫)
0x02 代碼編寫
在最后我們還是用代碼來寫個工具,之前寫過解析PE頭的數據了,所以繼續解析就是節表的數據了。
節表解析整體思路是:
- (1)、先得到節數量 NumberOfSections
- (2)、循環次數=節數量
- (3)、依據節的結構體來解析每個節的數據
- (4)、輸出相應的數據顯示到控制台中
vector<IMAGE_SECTION_HEADER_2> vsection_header;
//解析節表
for (size_t i = 0; i < IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections; i++)
{
IMAGE_SECTION_HEADER_2 aa;
fread(&aa, sizeof(aa), 1, fp);
vsection_header.push_back(aa);
}
//打印節中的數據
printf("---------------節表數據----------------\n");
cout << "節數量:" << IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections << endl;
for (size_t i = 0; i < IMAGE_NT_HREADERS_2.FileHeader.NumberOfSections; i++)
{
printf("--> %s段信息 <--\n", vsection_header[i].Name);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[內存中段大小]:0x%x\n", vsection_header[i].Misc.VirtualSize);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_BLUE);
printf("*[內存中偏移]:0x%x\n", vsection_header[i].VirtualAddress);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[文件中段大小]:0x%x\n", vsection_header[i].SizeOfRawData);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_BLUE);
printf("*[文件中偏移]:0x%x\n", vsection_header[i].PointerToRawData);
SetConsoleTextAttribute(handle, 0x07);
printf("[OBJ重定位偏移]:0x%x\n", vsection_header[i].PointerToRelocations);
printf("[OBJ重定位項數目]:0x%x\n", vsection_header[i].NumberOfRelocations);
printf("[行號表偏移]:0x%x\n", vsection_header[i].PointerToLinenumbers);
printf("[行號表中的數目]:0x%x\n", vsection_header[i].NumberOfLinenumbers);
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_RED);
printf("*[標志|屬性]:0x%x ", vsection_header[i].Characteristics);
//區段的屬性
DWORD l_Charctieristics = (BYTE)((DWORD)(vsection_header[i].Characteristics) & 0xFF);
DWORD h_Charctieristics = (BYTE)(((DWORD)(vsection_header[i].Characteristics) >> 24) & 0xFF);
vector<byte> l_flag;
vector<byte> h_flag;
//低位
l_flag.push_back((l_Charctieristics >> 7) ? 3 : 0);
l_flag.push_back((l_Charctieristics >> 6) & 1 ? 2 : 0);
l_flag.push_back((l_Charctieristics >> 5) & 1 ? 1 : 0);
//高位
h_flag.push_back((h_Charctieristics >> 7) ? 7 : 0);
h_flag.push_back((h_Charctieristics >> 6) & 1 ? 6 : 0);
h_flag.push_back((h_Charctieristics >> 5) & 1 ? 5 : 0);
h_flag.push_back((h_Charctieristics >> 4) & 1 ? 4 : 0);
//包含數據情況
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
for (vector<byte>::iterator iter = l_flag.begin(); iter != l_flag.end(); iter++)
{
switch (*iter)
{
case 1:
cout << "(包含可執行代碼),";
break;
case 2:
cout << "(包含已初始化數據),";
break;
case 3:
cout << "(包含未初始化數據),";
break;
default:
break;
}
}
//可讀寫執行情況
for (vector<byte>::iterator iter = h_flag.begin(); iter != h_flag.end(); iter++)
{
switch (*iter)
{
case 4:
cout << "(共享),";
break;
case 5:
cout << "(可執行),";
break;
case 6:
cout << "(可讀),";
break;
case 7:
cout << "(可寫),";
break;
default:
break;
}
}
cout << endl << endl;;
SetConsoleTextAttribute(handle, 0x07);
}
SetConsoleTextAttribute(handle, FOREGROUND_INTENSITY | FOREGROUND_GREEN);
printf("--> 標志(屬性塊) 常用特征值對照表:<--\n");
printf("[值:00000020h](*包含可執行代碼)\n");//IMAGE_SCN_CNT_CODE
printf("[值:00000040h](*該塊包含已初始化的數據)\n");//IMAGE_SCN_CNT_INITIALIZED_DATA
printf("[值:00000080h](*該塊包含未初始化的數據)\n");//IMAGE_SCN_CNT_UNINITIALIZED_DATA
printf("[值:00000200h][Section contains comments or some other type of information.]\n");//IMAGE_SCN_LNK_INFO
printf("[值:00000800h][Section contents will not become part of image.]\n");//IMAGE_SCN_LNK_REMOVE
printf("[值:00001000h][Section contents comdat.]\n");//IMAGE_SCN_LNK_COMDAT
printf("[值:00004000h][Reset speculative exceptions handling bits in the TLB entries for this section.]\n");//IMAGE_SCN_NO_DEFER_SPEC_EXC
printf("[值:00008000h][Section content can be accessed relative to GP.]\n");// IMAGE_SCN_GPREL
printf("[值:00500000h][Default alignment if no others are specified.]\n");//IMAGE_SCN_ALIGN_16BYTES
printf("[值:01000000h][Section contains extended relocations.]\n");//IMAGE_SCN_LNK_NRELOC_OVFL
printf("[值:02000000h][Section can be discarded.]\n");//IMAGE_SCN_MEM_DISCARDABLE
printf("[值:04000000h][Section is not cachable.]\n");//IMAGE_SCN_MEM_NOT_CACHED
printf("[值:08000000h][Section is not pageable.]\n");//IMAGE_SCN_MEM_NOT_PAGED
printf("[值:10000000h](*該塊為共享塊).\n");//IMAGE_SCN_MEM_SHARED
printf("[值:20000000h](*該塊可執行)\n");//IMAGE_SCN_MEM_EXECUTE
printf("[值:40000000h](*該塊可讀)\n");//IMAGE_SCN_MEM_READ
printf("[值:80000000h](*該塊可寫)\n\n");// IMAGE_SCN_MEM_WRITE
SetConsoleTextAttribute(handle, 0x07);//IMAGE_SCN_MEM_WRITE
運行結果:
最后歡迎加群: