zoukankan      html  css  js  c++  java
  • 32位PE文件结构

    MZ标记 : 0x5A4D   WORD

    PE标记:0x00004550  DWORD

    1、DOC头:

    WORD e_magic * "MZ标记" 用于判断是否为可执行文件.
    DWORD e_lfanew; * PE头相对于文件的偏移,用于定位PE文件

    2、标准PE头:

    WORD Machine; * 程序运行的CPU型号:0x0 任何处理器/0x14C 386及后续处理器
    WORD NumberOfSections; * 文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值.
    DWORD TimeDateStamp; * 时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的.
    DWORD PointerToSymbolTable;
    DWORD NumberOfSymbols;
    WORD SizeOfOptionalHeader; * 可选PE头的大小,32位PE文件默认E0h 64位PE文件默认为F0h 大小可以自定义.
    WORD Characteristics; * 每个位有不同的含义,可执行文件值为10F 即0 1 2 3 8位置1

    3、可选PE头:

    WORD Magic; * 说明文件类型:10B 32位下的PE文件 20B 64位下的PE文件
    BYTE MajorLinkerVersion;
    BYTE MinorLinkerVersion;
    DWORD SizeOfCode;* 所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用
    DWORD SizeOfInitializedData;* 已初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用
    DWORD SizeOfUninitializedData;* 未初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用
    DWORD AddressOfEntryPoint;* 程序入口
    DWORD BaseOfCode;* 代码开始的基址,编译器填的 没用
    DWORD BaseOfData;* 数据开始的基址,编译器填的 没用
    DWORD ImageBase;* 内存镜像基址
    DWORD SectionAlignment;* 内存对齐
    DWORD FileAlignment;* 文件对齐
    WORD MajorOperatingSystemVersion;
    WORD MinorOperatingSystemVersion;
    WORD MajorImageVersion;
    WORD MinorImageVersion;
    WORD MajorSubsystemVersion;
    WORD MinorSubsystemVersion;
    DWORD Win32VersionValue;
    DWORD SizeOfImage;* 内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍
    DWORD SizeOfHeaders;* 所有头+节表按照文件对齐后的大小,否则加载会出错
    DWORD CheckSum;* 校验和,一些系统文件有要求.用来判断文件是否被修改.
    WORD Subsystem;
    WORD DllCharacteristics;
    DWORD SizeOfStackReserve;* 初始化时保留的堆栈大小
    DWORD SizeOfStackCommit;* 初始化时实际提交的大小
    DWORD SizeOfHeapReserve;* 初始化时保留的堆大小
    DWORD SizeOfHeapCommit;* 初始化时实践提交的大小
    DWORD LoaderFlags;
    DWORD NumberOfRvaAndSizes;* 目录项数目

     4、 节表:

     节的属性标志对照表:

    [值:00000020h] [IMAGE_SCN_CNT_CODE // Section contains code.(包含可执行代码)]
    [值:00000040h] [IMAGE_SCN_CNT_INITIALIZED_DATA // Section contains initialized data.(该块包含已初始化的数据)]
    [值:00000080h] [IMAGE_SCN_CNT_UNINITIALIZED_DATA // Section contains uninitialized data.(该块包含未初始化的数据)]
    [值:00000200h] [IMAGE_SCN_LNK_INFO // Section contains comments or some other type of information.]
    [值:00000800h] [IMAGE_SCN_LNK_REMOVE // Section contents will not become part of image.]
    [值:00001000h] [IMAGE_SCN_LNK_COMDAT // Section contents comdat.]
    [值:00004000h] [IMAGE_SCN_NO_DEFER_SPEC_EXC // Reset speculative exceptions handling bits in the TLB entries for this section.]
    [值:00008000h] [IMAGE_SCN_GPREL // Section content can be accessed relative to GP.]
    [值:00500000h] [IMAGE_SCN_ALIGN_16BYTES // Default alignment if no others are specified.]
    [值:01000000h] [IMAGE_SCN_LNK_NRELOC_OVFL // Section contains extended relocations.]
    [值:02000000h] [IMAGE_SCN_MEM_DISCARDABLE // Section can be discarded.]
    [值:04000000h] [IMAGE_SCN_MEM_NOT_CACHED // Section is not cachable.]
    [值:08000000h] [IMAGE_SCN_MEM_NOT_PAGED // Section is not pageable.]
    [值:10000000h] [IMAGE_SCN_MEM_SHARED // Section is shareable(该块为共享块).]
    [值:20000000h] [IMAGE_SCN_MEM_EXECUTE // Section is executable.(该块可执行)]
    [值:40000000h] [IMAGE_SCN_MEM_READ // Section is readable.(该块可读)]
    [值:80000000h] [IMAGE_SCN_MEM_WRITE // Section is writeable.(该块可写)]

     5、RVA转FOA计算公式

    1)变量所在地址减去分配的内存基址

    2)减完的结果判断变量在哪个节中,大于哪个节的VirtualAddress并且小于这个节的VirtualAddress+VirtualSize

    3)使用减完的结果减去所在节的VirtualAddress,得到偏移量

    4)使用得到的偏移量+所在节的PointerToRawData,得到文件对应的偏移地址

    6、FileBuffer转ImageBuffer

    1)根绝SizeOfImage分配内存空间

    2)初始化内存空间把内容全部改成0

    3)把SizeOfHEADERS对应的内容全部拷贝到新分配的内存空间

    4)循环拷贝使用每个节的virtualAddress分割每个节的大小,把FileBuffer的每个节的SizeOfRawData对应的数据拷贝到每个VirtualAddress对应的节中

    7、节空白区添加代码(手动)

    1)E8 硬编码对应汇编指令 CALL

    2)E9硬编码对应汇编指令 JMP

    3)X = 真正要跳转的地址 - E8这条指令的下一行地址

    4)修改OEP指向添加代码开头的地址

    5)修改E9跳转的地址指向原来程序的OEP

    6)修改时需要注意文件对齐和内存对齐问题

    MESSAGEBOX硬编码 6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00 

    8、编程方式实现任意代码区添加代码

    1)按SizeOfImage申请内存空间

    2)memset初始化新申请的内存空间全部初始化为0

    3)FileBuffer和ImageBuffer的SizeOfHeaders是相同的没有任何变化可以直接把FileBuffer的SizeOfHeaders使用memcpy拷贝过来

    4)按照每个节的VirtualAddress为起点拷贝大小为每个节的SizeOfRawData全部拷贝结束后拉伸文件完成返回ImageBuffer

    5)设置一个全局的宏类型为BYTE类型里面放E8E9的硬编码

    6)E8硬编码的值为本机(DWORD)MASSAGEBOXAADDR - (DWORD)(ImageOptionalHeader32->ImageBase + (ImageSectionHeader->VirtualAddress + ImageSectionHeader->Misc.VirtualSize + 0xD))

    7)E9硬编码的值为(DWORD)(ImageOptionalHeader32->ImageBase + ImageOptionalHeader32->AddressOfEntryPoint) - (DWORD)(ImageOptionalHeader32->ImageBase + (ImageSectionHeader->VirtualAddress + ImageSectionHeader->Misc.VirtualSize + 0x12))

    8)使用memcpy在ImageSectionHeader->VirtualAddress + ImageSectionHeader->Misc.VirtualSize后面添加信息;

    需要判断SizeOfRawData-VirtualSize的值是否可以容纳下shellCode的大小,节的数量是否大于ImageFileHeader->NumberOfSections的数量,VirtualSize的大小是否超出了SizeOfRawData的大小

    9)根据最后一个ImageSectionHeader->PointerToRawData + SizeOfRawData申请一个NewBuffer的空间把ImageBuffer通过每个节的PointerToRawData + SizeOfRawData的大小还原回去

    10)新打开一个文件使用fwrite把NewBuffer的信息写入到新文件完成。

    8、增加一个节(代码实现)

    1)先判断SizeOfHeaders-(dos头+(dos头和标准PE头直接的垃圾数据大小)+ PE标志(4字节NT头) + 标准PE头 + 可选PE头 + 节表的大小)剩余的空间是否够80个字节(前40个字节存放新增的节表信息后40个字节全部为0{微软要求节表最后必须有40个字符全部为0的区域})

    2)如果够80个字节在原有节表最后添加一个新的40字节的节表信息,后40个字节全部置0(如果不够80字节可以把NT头向后一直到节表结束区的数据全部提升到DOS头后面覆盖掉DOS头和NT头直接的垃圾数据区,也可以扩展最后一个节在最后一个节的扩展区域内添加代码)

    3)修改SizeOfImage大小,修改VirtualAddress(注意文件对齐和内存对齐问题),修改VirtualSize为新增节的大小,修改SizeOfRawData,修改PointerToRawData,修改程序入口偏移,修改NumberOfSections完成

    1)创建NewImageBuffer 分配空间大小为 SizeOfImage + 0x1000

    2)判断最后一个节的SizeOfRawData和VirtualSize哪个大,使用内存对齐后大的那个值加上VirtualAddress 计算出添加硬编码的地址

    3)添加硬编码然后修改程序入口偏移

    4)修改最后一个节的SizeOfRawData、VirtualSize

    5)修改SizeOfImage、Characteristics(完成)

     

    提升除DOS头之外的所有头信息

    1、拉伸文件到内存

    2、提升的位置为ImageBuffer + 0x40

    3、ImageBuffer + e_lfnew开始复制数据到ImageBuffer + 0x40复制的大小为

    (4 + IMAGE_SIZEOF_FILE_HEADER + SIZEOF_OPTIONAL_HEADERS + (NUMBERSECTION * IMAGE_SIZEOF_SECTION_HEADER)

    4、修改e_lfnew = 0x40

    5、新增一个节

    9、数据目录

    1、我们所了解的PE分为头和节,在每个节中,都包含了我们写的一些代码和数据,但还有一些非常重要
    的信息是编译器替我们加到PE文件中的,这些信息可能存在在任何可以利用的地方。

    2、这些信息之所以重要,是因为这些信息包含了诸如:

    PE程序的图标在哪里?

    用到了哪些系统提供的函数?

    为其他的程序提供哪些函数?

    3、编译器添加了这么多信息,那程序是如何找到这些信息的呢?

    答案就是:数据目录

    4、数据目录定位:

    可选PE头最后一个成员,就是数据目录.一共有16个:

    typedef struct _IMAGE_DATA_DIRECTORY {    
    DWORD VirtualAddress;    //内存偏移
    DWORD Size;    //大小
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;    
    
    #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16    

    分别是:

    导出表、 #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory

    导入表、 #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory

    资源表、 #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory

    异常信息表、 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory

    安全证书表、 #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory

    重定位表、 #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table

    调试信息表、 #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory

    版权所以表、 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data

    全局指针表 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP

    TLS表、 #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory

    加载配置表、 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory

    绑定导入表、 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers

    IAT表、 #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table

    延迟导入表、 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors

    COM信息表 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

    最后一个保留未使用。

    和程序运行时息息相关的表有:

    导出表

    导入表

    重定位表

    IAT表

    10、导出表

    1、如何定位导出表:

    数据目录项的第一个结构,就是导出表.

    typedef struct _IMAGE_DATA_DIRECTORY {    
    DWORD VirtualAddress;    
    DWORD Size;    
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;    

    VirtualAddress 导出表的RVA

    Size 导出表大小

    2、导出表结构

    上面的结构,只是说明导出表在哪里,有多大,并不是真正的导出表.

    如何在FileBuffer中找到这个结构呢?在VirtualAddress中存储的是RVA,如果想在FileBuffer中定位

    必须要先将该RVA转换成FOA.

    真正的导出表结构如下:

    3、AddressOfFunctions说明:

    该表中元素宽度为4个字节

    该表中存储所有导出函数的地址

    该表中个数由NumberOfFunctions决定

    该表项中的值是RVA, 加上ImageBase才是函数真正的地址

    定位:

    IMAGE_EXPORT_DIRECTORY->AddressOfFunctions 中存储的是该表的RVA 需要先转换成FOA


    4、AddressOfNames说明:

    该表中元素宽度为4个字节

    该表中存储所有以名字导出函数的名字的RVA

    该表项中的值是RVA, 指向函数真正的名称

     

    总结:

    为什么要分成3张表?

    1、函数导出的个数与函数名的个数未必一样.所以要将函数地址表和函数名称表分开.

    2、函数地址表是不是一定大于函数名称表?

    未必,一个相同的函数地址,可能有多个不同的名字.

    3、如何根据函数的名字获取一个函数的地址?

     11、重定位表

    特别说明:

    1、一般情况下,EXE都是可以按照ImageBase的地址进行加载的.因为Exe拥有自己独立的4GB 的虚拟内存空间
    但DLL 不是 DLL是有EXE使用它,才加载到相关EXE的进程空间的.

    2、为了提高搜索的速度,模块间地址也是要对齐的 模块地址对齐为10000H 也就是64K

     打开一个程序,观察一下全局变量的反汇编

    1、也就是说,如果程序能够按照预定的ImageBase来加载的话,那么就不需要重定位表
    这也是为什么exe很少有重定位表,而DLL大多都有重定位表的原因

    2、一旦某个模块没有按照ImageBase进行加载,那么所有类似上面中的地址就都需要修正,否则,引用的地址就是无效的.

    3、一个EXE中,需要修正的地方会很多,那我们如何来记录都有哪些地方需要修正呢?

    答案就是重定位表

    重定位表的定位:


    数据目录项的第6个结构,就是重定位表.

    typedef struct _IMAGE_DATA_DIRECTORY {    
    DWORD VirtualAddress;    
    DWORD Size;    
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

    RVA转FOA

    typedef struct _IMAGE_BASE_RELOCATION {    
    DWORD VirtualAddress;    
    DWORD SizeOfBlock;    
    } IMAGE_BASE_RELOCATION;    
    typedef IMAGE_BASE_RELOCATION ,* PIMAGE_BASE_RELOCATION;

     12 移动导出表

    第一步:在DLL中新增一个节,并返回新增后的FOA  新增节注意VirtualAddress的内存对齐不要使用文件对齐

    第二步:复制AddressOfFunctions

    长度:4*NumberOfFunctions

    第三步:复制AddressOfNameOrdinals

    长度:NumberOfNames*2

    第四步:复制AddressOfNames

    长度:NumberOfNames*4

    第五步:复制所有的函数名

    长度不确定,复制时直接修复AddressOfNames最绕的地方是这里

    使用*AddressOfNames获取到函数名的地址后还需要RVA转FOA一次获取到函数名的地址,复制的时候修改AddressOfNames指向的地址。 


    第六步:复制IMAGE_EXPORT_DIRECTORY结构

    第七步:修复IMAGE_EXPORT_DIRECTORY结构中的

    AddressOfFunctions

    AddressOfNameOrdinals

    AddressOfNames

    第八步:修复目录项中的值,指向新的IMAGE_EXPORT_DIRECTORY

    //移动导出表
    LPVOID MoveExportTable(LPVOID NewFileBuffer)
    {
        PIMAGE_DOS_HEADER ImageDosHeader = NULL;
        PIMAGE_NT_HEADERS ImageNTheader = NULL;
        PIMAGE_FILE_HEADER ImageFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER32 ImageOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;
        PIMAGE_SECTION_HEADER LastImageSectionHeader = NULL;
        PIMAGE_SECTION_HEADER NewImageSectionHeader = NULL;
        PIMAGE_DATA_DIRECTORY ImageDataDirectory = NULL;
        PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL;
        PIMAGE_EXPORT_DIRECTORY NewImageExportDirectory = NULL;
    
        if (!NewFileBuffer)
        {
            printf("空间分配失败
    ");
            return NULL;
        }
        if (*((PWORD)(DWORD)NewFileBuffer) != IMAGE_DOS_SIGNATURE)
        {
            printf("不是有效的MZ标志
    ");
            free(NewFileBuffer);
            return NULL;
        }
        ImageDosHeader = (PIMAGE_DOS_HEADER)NewFileBuffer;
        if (*((PDWORD)((DWORD)NewFileBuffer + ImageDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
        {
            printf("不是有效的PE标志");
            free(NewFileBuffer);
            return NULL;
        }
        ImageNTheader = (PIMAGE_NT_HEADERS)((DWORD)NewFileBuffer + ImageDosHeader->e_lfanew);
        ImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ImageNTheader + 4);
        ImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)ImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        ImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)ImageOptionalHeader + ImageFileHeader->SizeOfOptionalHeader);
        LastImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)ImageSectionHeader + ((ImageFileHeader->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER));
        ImageDataDirectory = (PIMAGE_DATA_DIRECTORY)&ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
        ImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageDataDirectory->VirtualAddress));
        //复制AddressOfFunctions
        memcpy((PNZCH)((DWORD)NewFileBuffer + LastImageSectionHeader->PointerToRawData), (PNZCH)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageExportDirectory->AddressOfFunctions)),
            (ImageExportDirectory->NumberOfFunctions * 4));
        //复制AddressOfNameOrdinals
        memcpy((PNZCH)((DWORD)NewFileBuffer + (LastImageSectionHeader->PointerToRawData + (ImageExportDirectory->NumberOfFunctions * 4))), (PNZCH)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageExportDirectory->AddressOfNameOrdinals)),
            (ImageExportDirectory->NumberOfNames * 2));
        //复制AddressOfNames
        memcpy((PNZCH)((DWORD)NewFileBuffer + (LastImageSectionHeader->PointerToRawData + ((ImageExportDirectory->NumberOfFunctions * 4) + (ImageExportDirectory->NumberOfNames * 2)))), 
            (PNZCH)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageExportDirectory->AddressOfNames)),
            (ImageExportDirectory->NumberOfNames * 4));
        //复制函数名在新加节中的起始位置
        DWORD FunctionNameStartAddress = ((DWORD)LastImageSectionHeader->PointerToRawData + ((ImageExportDirectory->NumberOfFunctions * 4) + (ImageExportDirectory->NumberOfNames * 2)
            + (ImageExportDirectory->NumberOfNames * 4)));
        //FileBuffer中的原函数名的起始位置
        PDWORD FunctionNameAddress = (PDWORD)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageExportDirectory->AddressOfNames));
        PDWORD NewFunctionNameAddress = (PDWORD)((DWORD)NewFileBuffer + (LastImageSectionHeader->PointerToRawData + (ImageExportDirectory->NumberOfFunctions*4) + (ImageExportDirectory->NumberOfNames*2)));
        //计算FunctionNameAddress偏移
        DWORD Offset = FunctionNameStartAddress - LastImageSectionHeader->PointerToRawData;
        for (size_t i = 0; i < ImageExportDirectory->NumberOfNames; i++)
        {
            //取出原函数名的起始位置
            
            PNZCH FunctionName = (PNZCH)(((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, *FunctionNameAddress)));
            memcpy((PNZCH)((DWORD)NewFileBuffer + FunctionNameStartAddress), FunctionName,strlen(FunctionName));
            memset((PNZCH)((DWORD)NewFileBuffer + (FunctionNameStartAddress + strlen(FunctionName))), 0, 1);
    
            *NewFunctionNameAddress = (DWORD)LastImageSectionHeader->VirtualAddress + Offset;
            Offset = Offset + strlen(FunctionName) + 1;
            FunctionNameStartAddress = FunctionNameStartAddress + strlen(FunctionName) + 1;
            FunctionNameAddress = FunctionNameAddress++;
            NewFunctionNameAddress = NewFunctionNameAddress++;
            
        }
        //修复新的导出表数据
        NewImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)NewFileBuffer + (LastImageSectionHeader->PointerToRawData + Offset));
        memcpy(NewImageExportDirectory, ImageExportDirectory, sizeof(IMAGE_EXPORT_DIRECTORY));
        memcpy((PNZCH)((DWORD)NewFileBuffer + (LastImageSectionHeader->PointerToRawData + Offset) + sizeof(IMAGE_EXPORT_DIRECTORY)), NewExportFileName, sizeof(NewExportFileName));
        NewImageExportDirectory->Name = (LastImageSectionHeader->VirtualAddress + (Offset + sizeof(IMAGE_EXPORT_DIRECTORY)));
        NewImageExportDirectory->AddressOfFunctions = LastImageSectionHeader->VirtualAddress;
        NewImageExportDirectory->AddressOfNameOrdinals = (LastImageSectionHeader->VirtualAddress + (NewImageExportDirectory->NumberOfFunctions * 4));
        NewImageExportDirectory->AddressOfNames = (LastImageSectionHeader->VirtualAddress + (NewImageExportDirectory->NumberOfFunctions * 4) + (NewImageExportDirectory->NumberOfNames * 2));
        ImageDataDirectory->VirtualAddress = ((DWORD)LastImageSectionHeader->VirtualAddress + Offset);
        return NewFileBuffer;
    
    }

    13、移动重定位表

    循环复制DataDirectory的VirtualAddress指向的FOA地址到新加节中,每次复制的大小为ImageBaseRelocation->SizeOfBlock

    复制结束后修改DataDirectory的VirtualAddress指向新加节的开头位置

    //移动重定位表
    LPVOID MoveRelocateTable(LPVOID NewFileBuffer)
    {
        PIMAGE_DOS_HEADER ImageDosHeader = NULL;
        PIMAGE_NT_HEADERS ImageNTheader = NULL;
        PIMAGE_FILE_HEADER ImageFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER32 ImageOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;
        PIMAGE_SECTION_HEADER LastImageSectionHeader = NULL;
        PIMAGE_SECTION_HEADER NewImageSectionHeader = NULL;
        PIMAGE_DATA_DIRECTORY ImageDataDirectory = NULL;
        PIMAGE_BASE_RELOCATION ImageBaseRelocation = NULL;
        PIMAGE_BASE_RELOCATION NewImageBaseRelocationStartAddress = NULL;
    
        if (!NewFileBuffer)
        {
            printf("空间分配失败
    ");
            return NULL;
        }
        if (*((PWORD)(DWORD)NewFileBuffer) != IMAGE_DOS_SIGNATURE)
        {
            printf("不是有效的MZ标志
    ");
            free(NewFileBuffer);
            return NULL;
        }
        ImageDosHeader = (PIMAGE_DOS_HEADER)NewFileBuffer;
        if (*((PDWORD)((DWORD)NewFileBuffer + ImageDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
        {
            printf("不是有效的PE标志");
            free(NewFileBuffer);
            return NULL;
        }
        ImageNTheader = (PIMAGE_NT_HEADERS)((DWORD)NewFileBuffer + ImageDosHeader->e_lfanew);
        ImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ImageNTheader + 4);
        ImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)ImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        ImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)ImageOptionalHeader + ImageFileHeader->SizeOfOptionalHeader);
        LastImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)ImageSectionHeader + ((ImageFileHeader->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER));
        ImageDataDirectory = (PIMAGE_DATA_DIRECTORY)(&ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
        ImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageDataDirectory->VirtualAddress));
        //新的重定位表起始地址
        NewImageBaseRelocationStartAddress = (PIMAGE_BASE_RELOCATION)((DWORD)NewFileBuffer + LastImageSectionHeader->PointerToRawData);
        while (ImageBaseRelocation->VirtualAddress!=0 && ImageBaseRelocation->SizeOfBlock !=0)
        {
    
            memcpy((PNZCH)NewImageBaseRelocationStartAddress, (PNZCH)ImageBaseRelocation, ImageBaseRelocation->SizeOfBlock);
            NewImageBaseRelocationStartAddress = (PIMAGE_BASE_RELOCATION)((DWORD)NewImageBaseRelocationStartAddress + ImageBaseRelocation->SizeOfBlock);
            ImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)ImageBaseRelocation + ImageBaseRelocation->SizeOfBlock);
    
        }
        ImageDataDirectory->VirtualAddress = LastImageSectionHeader->VirtualAddress;
        return NewFileBuffer;
    }

    14、新加节并修改ImageBase

    使用while (ImageBaseRelocation->VirtualAddress!=0&&ImageBaseRelocation->SizeOfBlock!=0)来遍历什么时候到重定位表的结尾

    从ImageBaseRelocation + 8位置开始循环循环((ImageBaseRelocation->SizeOfBlock-8)/2)次因为ImageBaseRelocation + 8开始的数据都是WORD宽度的

    把数据拆分成高4位和低12位,高4位用来判断地址是否需要修正,如果高4位的值为3表示数据需要修正

    WORD FourHigh = ((*StartDataAddress & 0xF000)>>12);
    WORD LowBit = (*StartDataAddress & 0x0FFF);

    低12位用来表示偏移具体偏移位置修改FOA指向的位置的数据

    if (FourHigh == 3)
    {
    *((PDWORD)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ((DWORD)ImageBaseRelocation->VirtualAddress + LowBit)))) = *((PDWORD)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ((DWORD)ImageBaseRelocation->VirtualAddress + LowBit)))) + 0x10000000;
    }

    修改高4位为3的所有数据使用原数据 + 增加的ImageBase的大小,修改完成

    //移动重定位表并修改ImageBase
    LPVOID MoveRelocateTableAndAlterImageBase(LPVOID NewFileBuffer)
    {
        PIMAGE_DOS_HEADER ImageDosHeader = NULL;
        PIMAGE_NT_HEADERS ImageNTheader = NULL;
        PIMAGE_FILE_HEADER ImageFileHeader = NULL;
        PIMAGE_OPTIONAL_HEADER32 ImageOptionalHeader = NULL;
        PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;
        PIMAGE_SECTION_HEADER LastImageSectionHeader = NULL;
        PIMAGE_SECTION_HEADER NewImageSectionHeader = NULL;
        PIMAGE_DATA_DIRECTORY ImageDataDirectory = NULL;
        PIMAGE_BASE_RELOCATION ImageBaseRelocation = NULL;
    
        if (!NewFileBuffer)
        {
            printf("空间分配失败
    ");
            return NULL;
        }
        if (*((PWORD)(DWORD)NewFileBuffer) != IMAGE_DOS_SIGNATURE)
        {
            printf("不是有效的MZ标志
    ");
            free(NewFileBuffer);
            return NULL;
        }
        ImageDosHeader = (PIMAGE_DOS_HEADER)NewFileBuffer;
        if (*((PDWORD)((DWORD)NewFileBuffer + ImageDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
        {
            printf("不是有效的PE标志");
            free(NewFileBuffer);
            return NULL;
        }
        ImageNTheader = (PIMAGE_NT_HEADERS)((DWORD)NewFileBuffer + ImageDosHeader->e_lfanew);
        ImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ImageNTheader + 4);
        ImageOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)ImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        ImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)ImageOptionalHeader + ImageFileHeader->SizeOfOptionalHeader);
        LastImageSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)ImageSectionHeader + ((ImageFileHeader->NumberOfSections - 1) * IMAGE_SIZEOF_SECTION_HEADER));
        ImageDataDirectory = (PIMAGE_DATA_DIRECTORY)(&ImageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]);
        ImageOptionalHeader->ImageBase = 0x20000000;
        ImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ImageDataDirectory->VirtualAddress));
        while (ImageBaseRelocation->VirtualAddress!=0&&ImageBaseRelocation->SizeOfBlock!=0)
        {
            PWORD StartDataAddress = PWORD((DWORD)ImageBaseRelocation + 8);
            for (size_t i = 0; i < ((ImageBaseRelocation->SizeOfBlock-8)/2); i++)
            {
                WORD FourHigh = ((*StartDataAddress & 0xF000)>>12);
                WORD LowBit = (*StartDataAddress & 0x0FFF);
                if (FourHigh == 3)
                {
                    *((PDWORD)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ((DWORD)ImageBaseRelocation->VirtualAddress + LowBit)))) = *((PDWORD)((DWORD)NewFileBuffer + RVAToFOA(NewFileBuffer, ((DWORD)ImageBaseRelocation->VirtualAddress + LowBit)))) + 0x10000000;
                }
                StartDataAddress++;
            }
            ImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)ImageBaseRelocation + ImageBaseRelocation->SizeOfBlock);
        }
        
        return NewFileBuffer;
    }

    15、IAT表

    IAT表寻址有2种方式:

    1)使用IMAGE_DATA_DIRECTORY寻址 #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table

    2)使用导入表寻址FirstThunk

    IAT表的状态分为运行前和运行时2种状态

    运行前IAT中存的是函数的序号或者是函数名的RVA

    运行时IAT中存的值会被操作系统直接替换成Dll或exe中函数的基址

    16、导入表

    实现:

    1、使用OD打开一个发布版的exe程序,定位到某个DLL的API

    2、在没有加载的EXE中找到这个位置,观察加载前后的区别

    导入表结构:

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
    DWORD Characteristics;
    DWORD OriginalFirstThunk; //RVA 指向IMAGE_THUNK_DATA结构数组
    };
    DWORD TimeDateStamp; //时间戳
    DWORD ForwarderChain;
    DWORD Name; //RVA,指向dll名字,该名字已0结尾
    DWORD FirstThunk; //RVA,指向IMAGE_THUNK_DATA结构数组
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

    PE文件加载前:

    PE文件加载后:

    typedef struct _IMAGE_THUNK_DATA32 { 
    union {    
    PBYTE ForwarderString;    
    PDWORD Function;    
    DWORD Ordinal;    //序号    
    PIMAGE_IMPORT_BY_NAME AddressOfData;    //指向IMAGE_IMPORT_BY_NAME    
    } u1;    
    } IMAGE_THUNK_DATA32;    
    typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;    
    
    
    typedef struct _IMAGE_IMPORT_BY_NAME {    
    WORD Hint;    //可能为空,编译器决定 如果不为空 是函数在导出表中的索引    
    BYTE Name[1];    //函数名称,以0结尾    
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    //打印导入表
    VOID PrintImportTable(LPVOID FileBuffer)
    {
    
        PIMAGE_FILE_HEADER ImageFileHeader = NULL;
        PIMAGE_DOS_HEADER ImageDosHeader = NULL;
        PIMAGE_NT_HEADERS ImageNTheader = NULL;
        PIMAGE_OPTIONAL_HEADER32 ImageOptionalHeader32 = NULL;
        PIMAGE_DATA_DIRECTORY ImageDataDirectory = NULL;
        PIMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor = NULL;
    
        if (*((PWORD)FileBuffer) != IMAGE_DOS_SIGNATURE)
        {
            printf("不是有效的MZ标记
    ");
            free(FileBuffer);
            return ;
        }
    
        ImageDosHeader = (PIMAGE_DOS_HEADER)FileBuffer;
        if (*((PDWORD)((DWORD)FileBuffer + ImageDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
        {
            printf("不是有效的PE标志
    ");
            free(FileBuffer);
            return ;
        }
    
        ImageNTheader = (PIMAGE_NT_HEADERS)((DWORD)FileBuffer + ImageDosHeader->e_lfanew);
        ImageFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ImageNTheader + 4);
        ImageOptionalHeader32 = (PIMAGE_OPTIONAL_HEADER32)((DWORD)ImageFileHeader + IMAGE_SIZEOF_FILE_HEADER);
        ImageDataDirectory = (PIMAGE_DATA_DIRECTORY)&ImageOptionalHeader32->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
        ImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)FileBuffer + RVAToFOA(FileBuffer, ImageDataDirectory->VirtualAddress));
        while (ImageImportDescriptor->Characteristics != 0)
        {
            PNZCH DllName = (PNZCH)((DWORD)FileBuffer + RVAToFOA(FileBuffer, ImageImportDescriptor->Name));
            printf("Dll名称->%s
    ", DllName);
            printf("-------------------------------------------
    ");
            printf("OriginalFirstThunk函数名或序号为
    ");
            PDWORD OriginalFirstThunk = (PDWORD)((DWORD)FileBuffer + RVAToFOA(FileBuffer, ImageImportDescriptor->OriginalFirstThunk));
            PDWORD FirstThunk = (PDWORD)((DWORD)FileBuffer + RVAToFOA(FileBuffer, ImageImportDescriptor->FirstThunk));
            while (*OriginalFirstThunk != 0)
            {
                
                DWORD OriginalFirstThunkBestHighBit = (*OriginalFirstThunk & 0x80000000) >> 31;
                if (OriginalFirstThunkBestHighBit == 1)
                {
                    DWORD OriginalFirstThunkSerialNumber = (*OriginalFirstThunk & 0x6FFFFFFF);
                    printf("序号为->%x
    ", OriginalFirstThunkSerialNumber);
                }
                else
                {
                    PIMAGE_IMPORT_BY_NAME OriginalFirstThunkFunctionName = (PIMAGE_IMPORT_BY_NAME)((DWORD)FileBuffer + RVAToFOA(FileBuffer, *OriginalFirstThunk));
                    printf("HIT为->%x
    ", OriginalFirstThunkFunctionName->Hint);
                    printf("函数名为->%s
    ", OriginalFirstThunkFunctionName->Name);
                }
                OriginalFirstThunk++;
            }
            printf("FirstThunk函数名或序号为
    ");
            while (*FirstThunk != 0)
            {
    
                DWORD FirstThunkBestHighBit = (*FirstThunk & 0x80000000) >> 31;
                if (FirstThunkBestHighBit == 1)
                {
                    DWORD FirstThunkSerialNumber = (*FirstThunk & 0x6FFFFFFF);
                    printf("序号为->%x
    ", FirstThunkSerialNumber);
                }
                else
                {
                    PIMAGE_IMPORT_BY_NAME FirstThunkFunctionName = (PIMAGE_IMPORT_BY_NAME)((DWORD)FileBuffer + RVAToFOA(FileBuffer, *FirstThunk));
                    printf("HIT为->%x
    ", FirstThunkFunctionName->Hint);
                    printf("函数名为->%s
    ", FirstThunkFunctionName->Name);
                }
                FirstThunk++;
            }
            ImageImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)ImageImportDescriptor + sizeof(IMAGE_IMPORT_DESCRIPTOR));
        }
        free(FileBuffer);
    }

     17、绑定导入表


    当IMAGE_BOUND_IMPORT_DESCRIPTOR结构中的TimeDateStamp与DLL文件标准PE头中的TimeDateStamp值不相符
    时,或者DLL需要重新定位的时候,就会重新计算IAT中的值.

    绑定导入表结构:

    PE加载EXE相关的DLL时,首先会根据IMAGE_IMPORT_DESCRIPTOR结构中的TimeDateStamp来判断是否要重新
    计算IAT表中的地址。

    TimeDateStamp == 0 未绑定

    TimeDateStamp == -1 已绑定 真正的绑定时间为IMAGE_BOUND_IMPORT_DESCRIPTOR的TimeDateStamp

    绑定导入表的定位:

    绑定导入表位于数据目录的第12项

    typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {                            
        DWORD   TimeDateStamp;                            
        WORD    OffsetModuleName;                            
        WORD    NumberOfModuleForwarderRefs;                            
    // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows                            
    } IMAGE_BOUND_IMPORT_DESCRIPTOR,  *PIMAGE_BOUND_IMPORT_DESCRIPTOR;                            
                                
    typedef struct _IMAGE_BOUND_FORWARDER_REF {                            
        DWORD   TimeDateStamp;                            
        WORD    OffsetModuleName;                            
        WORD    Reserved;                            
    } IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;    

    绑定导入表的2个难点

    第一:数据目录中存储的直接是FOA地址不需要RVA转FOA

    第二:所有DLL名字的地址是第一个IMAGE_BOUND_IMPORT_DESCRIPTOR的地址加上每个IMAGE_BOUND_IMPORT_DESCRIPTOR或IMAGE_BOUND_FORWARDER_REF的OffsetModuleName

     

     18、导入表注入

    注入的种类:

    1、注册表注入 5、无DLL注入

    2、导入表注入 6、Apc 注入

    3、特洛伊注入 7、Windows挂钩注入DLL

    4、远程线程注入 8、输入法注入


    导入表注入原理:

    当Exe被加载时,系统会根据Exe导入表信息来加载需要用到的DLL,导入表注入的原理就是修改exe导入表,将自己的DLL
    添加到exe的导入表中,这样exe运行时可以将自己的DLL加载到exe的进程空间.


    导入表注入的实现步骤:


    第一步:

    根据目录项(第二个就是导入表)得到导入表信息:

    typedef struct _IMAGE_DATA_DIRECTORY {                    
        DWORD   VirtualAddress;                    
        DWORD   Size;                    
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;                    
                        
    VirtualAddress :指向导入表结构                    
    Size:导入表的总大小                    

    这两个值都需要

    第二步:

                                        
    typedef struct _IMAGE_IMPORT_DESCRIPTOR {                                    
        union {                                    
            DWORD   Characteristics;                                               
            DWORD   OriginalFirstThunk;                                             
        };                                    
        DWORD   TimeDateStamp;                                                   
        DWORD   ForwarderChain;                                                  
        DWORD   Name;                                    
        DWORD   FirstThunk;                                                     
    } IMAGE_IMPORT_DESCRIPTOR;                                    
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;                                    

    判断哪一个节的空白区 > Size(原导入表的大小) + 20 + A + B + C + D

    如果空间不够:可以将C/D 存储在其他的空白区

    也就是,只要空白区 > Size + 0x20就可以了

    如果仍然不够,就需要扩大最后一个节,或者新增节来解决.

    第三步:

    将原导入表全部Copy到空白区

    第四步:

    在新的导入表后面,追加一个导入表.

    第五步:

    追加8个字节的INT表 8个字节的IAT表

    第六步:

    追加一个IMAGE_IMPORT_BY_NAME 结构,前2个字节是0 后面是函数名称字符串

    第七步:

    将IMAGE_IMPORT_BY_NAME结构的RVA赋值给INT和IAT表中的第一项

    第八步:

    分配空间存储DLL名称字符串 并将该字符串的RVA赋值给Name属性

    第九步:

    修正IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size

  • 相关阅读:
    关于Visual Studio中的TraceDebugging文件夹
    没有App打得开发证书, 收不到推送
    转:ios应用崩溃日志揭秘
    转 iOS:NSAttributedString
    [UIDevice currentDevice].model
    转: Your build settings specify a provisioning profile with the UUID, no provisioning profile was found
    NSTimer 增加引用计数, 导致内存泄露,
    matplotlib基础(2)
    matplotlib基础
    《python自然语言处理》(1)
  • 原文地址:https://www.cnblogs.com/louzi/p/11039761.html
Copyright © 2011-2022 走看看