二进制入门学习笔记-4.节表

1. 节简介

PE文件中所有节的属性都被定义在节表中,节表由一系列的IMAGE_SECTION_HEADER结构排列而成,每个结构用来描述一个节,结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束,所以节表中IMAGE_SECTION_HEADER结构数量等于节的数量加一。节表总是被存放在紧接在PE文件头的地方。另外,节表中IMAGE_SECTION_HEADER结构的总数总是由PE文件头 IMAGE_NT_HEADERS结构中的FileHeader.NumberOfSections字段来指定的。

2.1 节表的结构

#define IMAGE_SIZEOF_SHORT_NAME              8              

typedef struct _IMAGE_SECTION_HEADER {              
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];              
    union {             
            DWORD   PhysicalAddress;                
            DWORD   VirtualSize;                
    } Misc;             
    DWORD   VirtualAddress;             
    DWORD   SizeOfRawData;              
    DWORD   PointerToRawData;               
    DWORD   PointerToRelocations;               
    DWORD   PointerToLinenumbers;               
    WORD    NumberOfRelocations;                
    WORD    NumberOfLinenumbers;                
    DWORD   Characteristics;                
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;             

2.2解表字段详解

1.Name  8个字节 一般情况下是以"\0"结尾的ASCII吗字符串来标识的名称,内容可以自定义.                                 

注意:该名称并不遵守必须以"\0"结尾的规律,如果不是以"\0"结尾,系统会截取8个字节的长度进行处理.                                        

2.Misc  双字 是该节在没有对齐前的真实尺寸,该值可以不准确。                                      

3.VirtualAddress 节区在内存中的偏移地址。加上ImageBase才是在内存中的真正地址.                                        

4.SizeOfRawData  节在文件中对齐后的尺寸.                                       

5.PointerToRawData 节区在文件中的偏移.                                       

6.PointerToRelocations obj文件中使用 exe无意义                                        

7.PointerToLinenumbers 行号表的位置 调试的时候使用                                       

8.NumberOfRelocations obj文件中使用 ,对exe无意义                                        

9.NumberOfLinenumbers 行号表中行号的数量 调试的时候使用                                     

10.Characteristics 节的属性

以上的字段决定了PE文件从硬盘中映射到内存中的映射关系。如下图所示(本图来自滴水逆向教程2015-3-13课堂讲义):

2.3 读取节表的内容c实现

// shellcode_inject.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include<Windows.h>
#include<stdlib.h>

void * readFile(char * filepath){
    //printf(filepath);
    int filesize=0;
    FILE *filep=NULL;
    char *buffer=NULL;
    filep = fopen(filepath,"rb");
    if (filep == NULL){
        printf("打开文件失败!");
        return NULL;
    }
    fseek(filep,0,SEEK_END);
    filesize = ftell(filep);
    fseek(filep,0,SEEK_SET);
    //printf("filesize is %d\n",filesize);
    buffer = (char *)malloc(sizeof(char)*filesize);
    if(buffer == NULL){
        printf("内存分配失败!");
        fclose(filep);
        return NULL;
    }
    memset(buffer,0,filesize);
    size_t n = fread(buffer,1,filesize,filep);
    if(!n){
        printf("读取数据失败!");
        fclose(filep);
        free(buffer);
        return NULL;
    }
    fclose(filep);
    return buffer;
}
void readPEHeader(char *filebuffer){
    //声明所要用到的变量
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNTHeader = NULL;

    IMAGE_FILE_HEADER PEHeader;
    PIMAGE_OPTIONAL_HEADER64 pOptionHeader;
    PIMAGE_SECTION_HEADER pSectionHeader = NULL;


    //获取DOS头
    printf("*********DOS头输出:**********\n");
    pDosHeader = (PIMAGE_DOS_HEADER)filebuffer;
    printf("MZ标志:%x\n",(*pDosHeader).e_magic);
    printf("NT头相对于文件初始位置的偏移:%x\n",(*pDosHeader).e_lfanew);
    printf("*********DOS头输出:**********\n");
    //获取NP头
    printf("*********NT头输出:**********\n");
    pNTHeader = PIMAGE_NT_HEADERS(filebuffer + (*pDosHeader).e_lfanew);
    printf("PE标记:%x\n",(*pNTHeader).Signature);
    printf("*********NT头输出:**********\n");

    //获取标准PE头
    printf("*********标准PE头输出:**********\n");
    PEHeader = (*pNTHeader).FileHeader;
    printf("Machine:%x\n",PEHeader.Machine);
    printf("节的数量为:0x%x\n",PEHeader.NumberOfSections);
    printf("可选PE头的大小:0x%x\n",PEHeader.SizeOfOptionalHeader);
    printf("*********标准PE头输出:**********\n");

    //获取可选PE头,我这里用的是64位的notepad.exe所以使用这个PIMAGE_OPTIONAL_HEADER64
    pOptionHeader = PIMAGE_OPTIONAL_HEADER64(filebuffer + (*pDosHeader).e_lfanew + sizeof((*pNTHeader).Signature)+sizeof((*pNTHeader).FileHeader));
    printf("*********可选头输出:**********\n");
    printf("Magic:0x%x\n",(*pOptionHeader).Magic);
    printf("*********可选PE头输出:**********\n");
    //free(filebuffer);//释放内存

    //获取节表内容
    int numOfSection = PEHeader.NumberOfSections;
    printf("节表的数量为:0x%x\n",numOfSection);
    pSectionHeader = (PIMAGE_SECTION_HEADER)(filebuffer+(*pDosHeader).e_lfanew+sizeof((*pNTHeader).Signature)+sizeof((*pNTHeader).FileHeader)+PEHeader.SizeOfOptionalHeader);
    printf("节表的偏移为:0x%x\n",(*pDosHeader).e_lfanew+sizeof((*pNTHeader).Signature)+sizeof((*pNTHeader).FileHeader)+PEHeader.SizeOfOptionalHeader);
    printf("第一个节表的名称为:%s\n",(*pSectionHeader).Name);

}
int main(int argc, char* argv[])
{
    char *filepath = "C:\\Users\\crls\\Desktop\\VC6.0green\\MyProjects\\PEReader\\Debug\\notepad.exe";
    char *filebuffer = (char *)readFile(filepath);
    readPEHeader(filebuffer);
    return 0;
}

2.在PE文件中新增一个节

为了在PE文件中新增一个节需要进行以下几步的操作:

  • 1.判断节表后方的剩余空间是否足够添加一个节表
  • 2.修改节表数量
  • 3.修改sizeofimage
  • 4.添加一个新的节表,节表中的内容依次修改