PE節表詳細分析


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;

image-20211106161212671

節表數量

節表數量不在節表中,而是在上一篇文章中已經介紹過,在標准PE頭中。標准PE頭中有一個成員是NumberOfSections他是節表的數量。

image-20211106161056869

節表名字

節表名字是各個節表的第一個成員,首先我們根據PE頭大小來確定節表所在位置即DOS頭+垃圾數據+NT頭,下面在010Editor演示具體數據。

節表中第一個數據是節名,節名一共占用的數據大小是8個字節, 演示中第一個節名是.text

image-20211106162142952

節表大小

節表中真實的大小在VirtualAddress中,也就是節名后的4個字節。他所代表的意思是對其前的大小也是在內存中的大小。

而在他后4四節之后的4字節數據,代表的是對其后的大小也是文件中的大小SizeOfRawData

image-20211106183020009

節位置

節位置都是在節大小之后,占用大小也是為4字節。VirualAddress后的4字節代表的是內存中節偏移SizeOfRawData后的4字節代表的是文件中節位置

image-20211106183953593

在內存中鎖定具體的節位置需要內存地址+節偏移得到的地址才是真實的內存節所在的位置,而在文件中的節位置則可以根據地址直接索引到,不需要+文件地址因為文件地址開始就是0。

image-20211106185117155

內存地址在可選PE頭的ImageBase中,所以我們需要先獲取到ImageBase的值這里是0x00400000,所以加上偏移位置就是0x00401000

image-20211106184450035

image-20211106184804699

一般程序在沒有被修改的情況下,默認.text段就是程序用來放代碼的地方。所以我們也可以用OD直接載入程序,然后跳到.text段的位置可以看到程序的匯編代碼。

(小知識:OD載入程序就是模擬了程序拉伸的過程,程序在內存中的樣子。)

image-20211106185730814

節表屬性

節表中的最后一個數據也是最重要的,他代表了這個節是否可讀可寫可執行或者是否包含可執行代碼、初始化、未初始化的數據。所以一般我們判斷一個段是否是代碼段就是根據這個屬性的值來判斷的,因為節表名是可以改的,比如我把隨便一個節表名改成.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

運行結果:

image-20211106193525571

最后歡迎加群:

Pwn菜雞學習小分隊群聊二維碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM