PE练习

// ReadFile.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>

/*
* PE学习,参考《PE权威指南》
* 先理解PE的结构,才能更好的理解代码,如果反过来,是很难有效理解PE结构的
* 先练习32位的PE结构,64位系统可以使用C:\Windows\SysWOW64这个目录下的文件练习
* 64位需使用PIMAGE_NT_HEADERS64
*/

/*
* RVA转FOA
* 思路:先根据输入的地址找出它所在的节,然后计算其在文件中的偏移值
*/
DWORD RVAToFOA(PIMAGE_SECTION_HEADER pSectionHeader, DWORD addr)
{
    PIMAGE_SECTION_HEADER pTemp = pSectionHeader;
    for (; (pTemp->VirtualAddress <= addr && pTemp->VirtualAddress != 0); pTemp++)
    {
        if (pTemp->VirtualAddress == 0)
        {
            break;
        }

        if (pTemp->VirtualAddress == addr)
        {
            pTemp++;
            break;
        }
    }

    pTemp--;

    DWORD foa = addr - pTemp->VirtualAddress + pTemp->PointerToRawData;

    //DWORD endRVA = pTemp->VirtualAddress + pTemp->Misc.VirtualSize;
    //DWORD endFOA = pTemp->PointerToRawData + pTemp->SizeOfRawData;
    //printf("\nVirtualAddress %08X + %08X = %08X, ", pTemp->VirtualAddress, pTemp->Misc.VirtualSize, endRVA);
    //printf("PointerToRawData %08X + %08X = %08X\n", pTemp->PointerToRawData, pTemp->SizeOfRawData, endFOA);
    //printf("RVA: %08X --> FOA: %08X\n\n", addr, foa);

    return foa;
}

/*
* 获取文件路径
*/
void GetFile(TCHAR* szPath)
{
#if 1
    if (!GetModuleFileName(NULL, szPath, MAX_PATH))
    {
        printf("Cannot install service (%d)\n", GetLastError());
        return;
    }
    char* pPathEnd = (char*)szPath + strlen(szPath);
    while (pPathEnd--)
    {
        if (*pPathEnd == '\\')
        {
            *pPathEnd = '\0';
            break;
        }
    }
    // user32test.dll ntoskrnltest.exe
    //strcat_s(szPath, "\\ntdlltest.dll");

    // special? ntoskrnltest.exe
    strcat_s(szPath, MAX_PATH, "\\SysWOW64\\kernel32.dll");
#else
    strcpy_s(szPath, "D:\\test.dll");
#endif
}

/*
* 读取文件到内存
*/
bool ReadFile(const char* name, char** p)
{
    bool bRet = false;

    FILE* fp = NULL;
    errno_t error = fopen_s(&fp, name, "rb");
    if (error == 0)
    {
        fseek(fp, 0, SEEK_END);
        DWORD bytes = ftell(fp);
        // do not forget to free memory later
        *p = (char*)malloc(bytes);

        fseek(fp, 0, SEEK_SET);
        fread_s(*p, bytes, sizeof(char), bytes, fp);

        fclose(fp);
        bRet = true;
    }
    else
    {
        printf("ReadFile %s error %d\n", name, error);
    }

    return bRet;
}

void PrintFileHeader(PIMAGE_NT_HEADERS32 pNTHeaders)
{
    printf("==============File Header Size: %08X ==============\n", sizeof(IMAGE_FILE_HEADER));
    printf("Machine: %04X\n", pNTHeaders->FileHeader.Machine);
    printf("NumberOfSections: %04X\n", pNTHeaders->FileHeader.NumberOfSections);
    printf("TimeDateStamp: %08X\n", pNTHeaders->FileHeader.TimeDateStamp);
    printf("PointerToSymbolTable: %08X\n", pNTHeaders->FileHeader.PointerToSymbolTable);
    printf("NumberOfSymbols: %08X\n", pNTHeaders->FileHeader.NumberOfSymbols);
    printf("SizeOfOptionalHeader: %04X\n", pNTHeaders->FileHeader.SizeOfOptionalHeader);
    printf("Characteristics: %04X\n", pNTHeaders->FileHeader.Characteristics);
}

const char* directoryNameArray[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] = {
    "Export Directory", 
    "Import Directory", 
    "Resource Directory", 
    "Exception Directory",
    "Security Directory", 
    "Base Relocation Directory", 
    "Debug Directory", 
    "Description Directory",
    "Special Directory", 
    "TLS,Thread Storage Directory", 
    "Load Configuration Directory", 
    "Bound Import Directory",
    "IAT,Import Address Table Directory", 
    "Delay Import Directory", 
    "COM_DESCRIPTOR,COR20 Header Directory", 
    "Reserved Directory"
};

void PrintOptionalHeaders(PIMAGE_NT_HEADERS32 pNTHeaders)
{
    printf("==============Optional Headers Size: %08X ==============\n", sizeof(IMAGE_OPTIONAL_HEADER32));
    //
    // Standard fields.
    //
    printf("Magic: %04X\n", pNTHeaders->OptionalHeader.Magic);
    printf("MajorLinkerVersion: %02X\n", pNTHeaders->OptionalHeader.MajorLinkerVersion);
    printf("MinorLinkerVersion: %02X\n", pNTHeaders->OptionalHeader.MinorLinkerVersion);
    printf("SizeOfCode: %08X\n", pNTHeaders->OptionalHeader.SizeOfCode);
    printf("SizeOfInitializedData: %08X\n", pNTHeaders->OptionalHeader.SizeOfInitializedData);
    printf("SizeOfUninitializedData: %08X\n", pNTHeaders->OptionalHeader.SizeOfUninitializedData);
    printf("AddressOfEntryPoint: %08X\n", pNTHeaders->OptionalHeader.AddressOfEntryPoint);
    printf("BaseOfCode: %08X\n", pNTHeaders->OptionalHeader.BaseOfCode);
    printf("BaseOfData: %08X\n", pNTHeaders->OptionalHeader.BaseOfData);

    //
    // NT additional fields.
    //

    printf("ImageBase: %08X\n", pNTHeaders->OptionalHeader.ImageBase);
    printf("SectionAlignment: %08X\n", pNTHeaders->OptionalHeader.SectionAlignment);
    printf("FileAlignment: %08X\n", pNTHeaders->OptionalHeader.FileAlignment);
    printf("MajorOperatingSystemVersion: %04X\n", pNTHeaders->OptionalHeader.MajorOperatingSystemVersion);
    printf("MinorOperatingSystemVersion: %04X\n", pNTHeaders->OptionalHeader.MinorOperatingSystemVersion);
    printf("MajorImageVersion: %04X\n", pNTHeaders->OptionalHeader.MajorImageVersion);
    printf("MinorImageVersion: %04X\n", pNTHeaders->OptionalHeader.MinorImageVersion);
    printf("MajorSubsystemVersion: %04X\n", pNTHeaders->OptionalHeader.MajorSubsystemVersion);
    printf("MinorSubsystemVersion: %04X\n", pNTHeaders->OptionalHeader.MinorSubsystemVersion);
    printf("Win32VersionValue: %08X\n", pNTHeaders->OptionalHeader.Win32VersionValue);
    printf("SizeOfImage: %08X\n", pNTHeaders->OptionalHeader.SizeOfImage);
    printf("SizeOfHeaders: %08X\n", pNTHeaders->OptionalHeader.SizeOfHeaders);
    printf("CheckSum: %08X\n", pNTHeaders->OptionalHeader.CheckSum);
    printf("Subsystem: %04X\n", pNTHeaders->OptionalHeader.Subsystem);
    printf("DllCharacteristics: %04X\n", pNTHeaders->OptionalHeader.DllCharacteristics);
    printf("SizeOfStackReserve: %08X\n", pNTHeaders->OptionalHeader.SizeOfStackReserve);
    printf("SizeOfStackCommit: %08X\n", pNTHeaders->OptionalHeader.SizeOfStackCommit);
    printf("SizeOfHeapReserve: %08X\n", pNTHeaders->OptionalHeader.SizeOfHeapReserve);
    printf("SizeOfHeapCommit: %08X\n", pNTHeaders->OptionalHeader.SizeOfHeapCommit);
    printf("LoaderFlags: %08X\n", pNTHeaders->OptionalHeader.LoaderFlags);
    printf("NumberOfRvaAndSizes: %08X\n", pNTHeaders->OptionalHeader.NumberOfRvaAndSizes);
    for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
    {
        printf("DataDirectory[%02d] VirtualAddress: %08X", i,
            pNTHeaders->OptionalHeader.DataDirectory[i].VirtualAddress);
        printf(", Size: %08X %s\n", pNTHeaders->OptionalHeader.DataDirectory[i].Size, directoryNameArray[i]);
    }
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
}

/*
* 打印节表
* 说明:文件头 + DOS Stub + DOS头 + NT头 + 节表,
* 这些内容在文件中和加载到内存中是完全一样的,做拉伸练习时要注意
*/
void PrintSectionHeader(PIMAGE_NT_HEADERS32 pNTHeaders)
{
    printf("==============SECTION HEADER: Size: %08X ==============\n", sizeof(IMAGE_SECTION_HEADER));
    PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((char*)pNTHeaders + sizeof(IMAGE_NT_HEADERS32));

    char* Name[IMAGE_SIZEOF_SHORT_NAME + 1] = { 0 };
    int sectionNum = pNTHeaders->FileHeader.NumberOfSections;
    for (int i = 0; i < sectionNum; i++)
    {
        memcpy_s(Name, IMAGE_SIZEOF_SHORT_NAME, pSectionHeader->Name, IMAGE_SIZEOF_SHORT_NAME);
        printf("%d %s\nVirtualAddress: %08X, VirtualSize: %08X ==> %08X\nPointerToRawData: %08X, SizeOfRawData: %08X "
            "==> %08X\n",
            i, Name, pSectionHeader->VirtualAddress, pSectionHeader->Misc.VirtualSize,
            pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize, pSectionHeader->PointerToRawData,
            pSectionHeader->SizeOfRawData, pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData);

        pSectionHeader++;
    }
}

/*
* 打印导出表信息
* 导出表信息常见于DLL,但EXE也可以存在导出表
*/
void PrintExportTable(char* fileBuffer, PIMAGE_NT_HEADERS32 pNTHeaders, PIMAGE_SECTION_HEADER pFirstSectionHeader)
{
    printf("==============Export Table Size: %08X ==============\n", sizeof(IMAGE_EXPORT_DIRECTORY));
    IMAGE_DATA_DIRECTORY exportDirectory = pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    printf("EXPORT %08X+ %08X\n", exportDirectory.VirtualAddress, exportDirectory.Size);

    // 如果存在导出表
    if (exportDirectory.VirtualAddress > 0)
    {
        // 将导出表相对虚拟地址RVA转为文件偏移FOA
        DWORD exportFOA = RVAToFOA(pFirstSectionHeader, exportDirectory.VirtualAddress);
        printf("EXPORT RVA: %08X -- FOA: %08X\n", exportDirectory.VirtualAddress, exportFOA);

        // 将内存中的字节信息转为导出表结构体
        PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(fileBuffer + exportFOA);

        printf("Characteristics: %08X\n", imageExportDirectory->Characteristics);
        printf("TimeDateStamp: %08X\n", imageExportDirectory->TimeDateStamp);
        printf("MajorVersion: %08X\n", imageExportDirectory->MajorVersion);
        printf("MinorVersion: %08X\n", imageExportDirectory->MinorVersion);
        printf("Name: %08X\n", imageExportDirectory->Name);
        printf("Base: %08X\n", imageExportDirectory->Base);
        printf("NumberOfFunctions: %08X\n", imageExportDirectory->NumberOfFunctions);
        printf("NumberOfNames: %08X\n", imageExportDirectory->NumberOfNames);

        // 函数名称地址表RVA转FOA
        DWORD AddressOfNamesFOA = RVAToFOA(pFirstSectionHeader, imageExportDirectory->AddressOfNames);
        // 函数序号地址表RVA转FOA
        DWORD AddressOfNameOrdinalsFOA = RVAToFOA(pFirstSectionHeader, imageExportDirectory->AddressOfNameOrdinals);
        // 导出函数地址表RVA转FOA
        DWORD AddressOfFunctionsFOA = RVAToFOA(pFirstSectionHeader, imageExportDirectory->AddressOfFunctions);

        // 函数名称地址表在内存的起始地址
        int* pAddressOfNames = (int*)(fileBuffer + AddressOfNamesFOA);
        // 函数序号地址表在内存的起始地址
        WORD* pAddressOfNameOrdinals = (WORD*)(fileBuffer + AddressOfNameOrdinalsFOA);
        // 导出函数地址表在内存的起始地址
        int* pAddressOfFunctions = (int*)(fileBuffer + AddressOfFunctionsFOA);

        printf("AddressOfNames RVA: %08X (FOA: %08X)\n", imageExportDirectory->AddressOfNames, AddressOfNamesFOA);
        printf("AddressOfNameOrdinals RVA: %08X (FOA: %08X)\n", imageExportDirectory->AddressOfNameOrdinals,
            AddressOfNameOrdinalsFOA);
        printf("AddressOfFunctions RVA: %08X (FOA: %08X)\n", imageExportDirectory->AddressOfFunctions,
            AddressOfFunctionsFOA);

        // 缓存数组,Ordinals-- > 函数名及序号信息
        char arryOfOrdinalsInfo[2048][256] = { 0 };
        for (int i = 0; i < imageExportDirectory->NumberOfNames; i++)
        {
            //DWORD nameAddr = RVAToFOA(pFirstSectionHeader, *pAddressOfNames);
            //int* pName = (int*)(fileBuffer + nameAddr);
            //printf("%d FOA:%08X=%08X %s\n", i, nameAddr, *pAddressOfNames, (char*)pName);

            // 函数名称地址表地址单元存储的内容RVA转FOA
            DWORD nameAddr = RVAToFOA(pFirstSectionHeader, *pAddressOfNames);
            // 获取FOA对应的函数名称
            char* pName = (char*)(fileBuffer + nameAddr);

            // 地址单元的内容是函数序号,Base+函数序号,是按序号方式调用的值
            // 这个序号表是给名称表查询使用的,它必须和名称表一一对应
            sprintf_s(arryOfOrdinalsInfo[*pAddressOfNameOrdinals],
                "Hint Hex: %04X (%05d) Name RVA: %08X(FOA: %08X) [Ordinals]: %04d (+Base: %04d) %s", i, i,
                *pAddressOfNames, nameAddr, *pAddressOfNameOrdinals,
                (*pAddressOfNameOrdinals + imageExportDirectory->Base), (char*)pName);

            // WORD*型偏移2个地址,读取下一个序号
            pAddressOfNameOrdinals++;

            // int*型偏移4个地址,读取下一个地址
            pAddressOfNames++;
        }

        for (int i = 0; i < imageExportDirectory->NumberOfFunctions; i++)
        {
            if (*pAddressOfFunctions)
            {
                // 导出函数地址RVA转FOA,可以根据FOA直接在文件中查看该函数的硬编码
                DWORD AddressOfFunctionsFOA = RVAToFOA(pFirstSectionHeader, *pAddressOfFunctions);

                if (strlen(arryOfOrdinalsInfo[i]) > 0)
                {
                    printf("%s\n", arryOfOrdinalsInfo[i]);
                }
                else
                {
                    printf(" no function name info\n");
                }
                printf(" %04d [Function Entry Point] RVA: %08X FOA: %08X\n", i, *pAddressOfFunctions,
                    AddressOfFunctionsFOA);

                // out put code
                printf("  Code: ");
                unsigned char* pAddr = (unsigned char*)(fileBuffer + AddressOfFunctionsFOA);
                for (int i = 0; i < 10; i++)
                {
                    printf("%02X ", *pAddr);
                    pAddr++;
                }
                printf("\n\n");
            }

            pAddressOfFunctions++;
        }
    }
}

/*
* 打印导入表信息
*/
void PrintImportTable(char* fileBuffer, PIMAGE_NT_HEADERS32 pNTHeaders, PIMAGE_SECTION_HEADER pFirstSectionHeader)
{
    printf("==============IMPORT Table Size: %08X ==============\n", sizeof(IMAGE_IMPORT_DESCRIPTOR));
    IMAGE_DATA_DIRECTORY importDirectory = pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    printf("IMPORT %08X + %08X\n", importDirectory.VirtualAddress, importDirectory.Size);

    // 导入表地址RVA转FOA
    DWORD importFOA = RVAToFOA(pFirstSectionHeader, importDirectory.VirtualAddress);
    printf("IMPORT RVA: %08X -- FOA: %08X\n", importDirectory.VirtualAddress, importFOA);

    // 获取导入表描述符结构体
    PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(fileBuffer + importFOA);

    // 遍历
    int indexOfDLL = 0;
    while (pImportDescriptor->OriginalFirstThunk != 0 && pImportDescriptor->FirstThunk != 0)
    {
        // 获取导入DLL的名称,根据这个FOA可以直接在文件查看到DLL名称
        DWORD dllNameFOA = RVAToFOA(pFirstSectionHeader, pImportDescriptor->Name);
        // 内存中的DLL名称,用于打印
        unsigned char* pAddr = (unsigned char*)(fileBuffer + dllNameFOA);

        // RVA转FOA
        DWORD foaOfOriginalFirstThunk = RVAToFOA(pFirstSectionHeader, pImportDescriptor->OriginalFirstThunk);
        DWORD foaOfFirstThunk = RVAToFOA(pFirstSectionHeader, pImportDescriptor->FirstThunk);
        printf("\nIndexOfDLL: %02d Name RVA: %08X(FOA: %08X) NAME: %s TimeDateStamp: %08X\nOriginalFirstThunk: "
            "%08X(FOA: %08X)\nFirstThunk: %08X(FOA: %08X)\n",
            indexOfDLL++, pImportDescriptor->Name, dllNameFOA, pAddr, pImportDescriptor->TimeDateStamp,
            pImportDescriptor->OriginalFirstThunk, foaOfOriginalFirstThunk, pImportDescriptor->FirstThunk,
            foaOfFirstThunk);

        // 内存地址
        int* pOriginalFirstThunk = (int*)(fileBuffer + foaOfOriginalFirstThunk);
        int* pFirstThunk = (int*)(fileBuffer + foaOfFirstThunk);

        printf("================INT===============\n");
        //遍历INT(Import Name Table)
        while (*pOriginalFirstThunk)
        {
            if (*pOriginalFirstThunk & IMAGE_ORDINAL_FLAG32)
            {
                // 函数以序号的方式导入
                printf("    %08X ==> %08X\n", *pOriginalFirstThunk, *pOriginalFirstThunk & 0x7fffffff);
            }
            else
            {
                // 函数以名称的方式导入
                PIMAGE_IMPORT_BY_NAME pImportByName
                    = (PIMAGE_IMPORT_BY_NAME)(fileBuffer + RVAToFOA(pFirstSectionHeader, *pOriginalFirstThunk));

                //printf("    %08d %08X %08X\n", i, *pIATAddr, RVAToFOA(pFirstSectionHeader, *pIATAddr));
                printf("    RVA: %08X FOA: %08X Hint Hex: %04X (%05d) Name: %s\n", *pOriginalFirstThunk,
                    RVAToFOA(pFirstSectionHeader, *pOriginalFirstThunk), pImportByName->Hint, pImportByName->Hint,
                    (char*)(pImportByName->Name));
            }

            pOriginalFirstThunk++;
        }

        printf("================IAT===============\n");
        // 遍历IAT(Import Address Table)
        while (pFirstThunk != 0 && *pFirstThunk)
        {
            if (*pFirstThunk & IMAGE_ORDINAL_FLAG32)
            {
                // 函数以序号的方式导入
                printf("    %08X ==> %08X\n", *pFirstThunk, *pFirstThunk & 0x7fffffff);
            }
            else
            {
                if (pImportDescriptor->TimeDateStamp == 0xffffffff)
                {
                    // 导入地址需查询绑定导入表
                    printf("    %08X\n", *pFirstThunk);
                }
                else
                {
                    // 函数以名称的方式导入
                    PIMAGE_IMPORT_BY_NAME pImportByName
                        = (PIMAGE_IMPORT_BY_NAME)(fileBuffer + RVAToFOA(pFirstSectionHeader, *pFirstThunk));

                    //printf("    %08d %08X %08X\n", i, *pIATAddr, RVAToFOA(pFirstSectionHeader, *pIATAddr));
                    printf("    RVA: %08X FOA: %08X Hint Hex: %04X (%05d) Name: %s\n", *pFirstThunk,
                        RVAToFOA(pFirstSectionHeader, *pFirstThunk), pImportByName->Hint, pImportByName->Hint,
                        (char*)(pImportByName->Name));
                }
            }

            pFirstThunk++;
        }

        pImportDescriptor++;
    }
}

void PrintIATTable(char* fileBuffer, PIMAGE_NT_HEADERS32 pNTHeaders, PIMAGE_SECTION_HEADER pFirstSectionHeader)
{
    printf("==============IAT Table==============\n");
    IMAGE_DATA_DIRECTORY iatDirectory = pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT];
    printf("IAT %08X + %08X\n", iatDirectory.VirtualAddress, iatDirectory.Size);

    // RVA转FOA,可以对照文件和内存的值加深理解
    DWORD iatFOA = RVAToFOA(pFirstSectionHeader, iatDirectory.VirtualAddress);

    // 获取内存buffer中IAT的起始地址,注意int*,加一是偏移4个字节
    int* pIATAddr = (int*)(fileBuffer + iatFOA);
    printf("\nIAT RVA: %08X FOA: %08X\n", iatDirectory.VirtualAddress, iatFOA);
    // 4个字节为一组,所以除以4就是地址个数
    for (int i = 0; i < iatDirectory.Size / 4; i++)
    {
        if (*pIATAddr)
        {
            if (*pIATAddr < 0x80000000)
            {
                if (*pIATAddr > pNTHeaders->OptionalHeader.SizeOfImage)
                {
                    printf("    %08X\n", *pIATAddr);
                }
                else
                {
                    DWORD importByNameFOA = RVAToFOA(pFirstSectionHeader, *pIATAddr);
                    PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)(fileBuffer + importByNameFOA);

                    //printf("    %08d %08X %08X\n", i, *pIATAddr, RVAToFOA(pFirstSectionHeader, *pIATAddr));
                    printf("    %08d RVA: %08X FOA: %08X Hint Hex: %04X (%05d) Name: %s\n", i, *pIATAddr,
                        importByNameFOA, pImportByName->Hint, pImportByName->Hint, (char*)(pImportByName->Name));
                }
            }
            else
            {
                printf("    %08d %08X\n", i, *pIATAddr);
            }
        }
        else
        {
            printf("    %08d %08X\n", i, *pIATAddr);
        }
        pIATAddr++;
    }
}

/*
* 打印重定位表
*/
void PrintBaseRelocationTable(char* fileBuffer, PIMAGE_NT_HEADERS32 pNTHeaders,
    PIMAGE_SECTION_HEADER pFirstSectionHeader)
{
    printf("==============BASE RELOCATION Table Size: %08X ==============\n", sizeof(IMAGE_BASE_RELOCATION));
    IMAGE_DATA_DIRECTORY baseRelocationDirectory
        = pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];

    // RVA转FOA
    DWORD baseRelocationFOA = RVAToFOA(pFirstSectionHeader, baseRelocationDirectory.VirtualAddress);
    printf("Base Relocation RVA: %08X -- FOA: %08X\n", baseRelocationDirectory.VirtualAddress, baseRelocationFOA);

    // 获取内存buffer中重定位表起始地址
    char* pBaseRelocationAddr = (char*)(fileBuffer + baseRelocationFOA);
    // 指针转结构体
    PIMAGE_BASE_RELOCATION pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)(pBaseRelocationAddr);

    // 重定位表结构:前8个字节是代码起始页码的起始虚拟地址和块大小
    int headSize = sizeof(pImageBaseRelocation->VirtualAddress) + sizeof(pImageBaseRelocation->SizeOfBlock);

    // 注意先判断有没有重定位表
    if (baseRelocationDirectory.VirtualAddress != 0)
    {
        // 遍历
        while (pBaseRelocationAddr != 0 && pImageBaseRelocation->VirtualAddress != 0)
        {
            // ImageBase + 代码起始页面地址
            DWORD imageDataBase = pNTHeaders->OptionalHeader.ImageBase + pImageBaseRelocation->VirtualAddress;
            // 偏移8个字节才是第一个重定位项的值,注意每个重定位项内存宽度时两个字节
            WORD* pOffSetData = (WORD*)(pBaseRelocationAddr + headSize);

            printf("\nVirtualAddress: %08X SizeOfBlock: %08X\n ImageBase: %08X imageDataBase: %08X\n",
                pImageBaseRelocation->VirtualAddress, pImageBaseRelocation->SizeOfBlock,
                pNTHeaders->OptionalHeader.ImageBase, imageDataBase);

            // 遍历重定位项,一项占两个字节,所以除以2
            for (int i = 0; i < (pImageBaseRelocation->SizeOfBlock - headSize) / 2; i++)
            {
                if (pImageBaseRelocation->VirtualAddress)
                {
                    // 3c90 -> c90
                    //以下3 C90的含义是:
                    //3表示这个值对应的地址需修正,0表示不需修正
                    //低12位为修正地址
                    //实际VA=基地址+代码其实页面+低十二位虚拟地址
                    //=0x77610000+2000+c90

                    // 这里处理不够严谨,应先判断是否需修正
                    // ImageBase + 代码起始页地址 + 低十二位虚拟地址
                    DWORD functionVirtualAddr = imageDataBase + (*pOffSetData & 0x0fff);
                    printf("\n%04X(%08X) \n", *pOffSetData, functionVirtualAddr);

                    // (代码起始页地址 + 低十二位虚拟地址) 转FOA
                    DWORD offSetFOA
                        = RVAToFOA(pFirstSectionHeader, pImageBaseRelocation->VirtualAddress + (*pOffSetData & 0x0fff));
                    // 获取内存buffer的地址,可根据FOA直接查看偏移值的文件内容
                    DWORD* pAddr = (DWORD*)(fileBuffer + offSetFOA);
                    printf("offSetFOA %08X --> Value: %08X(functionVirtualAddr: %08X)) \n", offSetFOA, *pAddr,
                        functionVirtualAddr);

                    // test new base测试DLL重定位为新的地址
                    DWORD newBase = 0x77610000;
                    // 重定位后的内存地址
                    DWORD newAddr = newBase + pImageBaseRelocation->VirtualAddress + (*pOffSetData & 0x0fff);
                    // 重定位后内存地址存储的内容,这个就是文件中需修改的值
                    // *pAddr是编译时,根据DLL的ImageBase计算出的函数地址
                    DWORD newVaue = newBase - pNTHeaders->OptionalHeader.ImageBase + *pAddr;
                    printf("new functionVirtualAddr: %08X new Value: %08X\n", newAddr, newVaue);

                    if ((i > 0) && (i % 16 == 1))
                    {
                        printf("\n");
                    }
                }

                // 偏移两个字节,取下一个重定位项
                pOffSetData++;
            }
            printf("\n");

            // 下一个重定位表
            pBaseRelocationAddr += pImageBaseRelocation->SizeOfBlock;
            pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)(pBaseRelocationAddr);
        }
    }
    else
    {
        printf(" is null\n");
    }
}

int main(int argc, char** argv)
{
    TCHAR szPath[MAX_PATH];
    if (argc <= 1)
    {
        printf("Please use [ReadFile.exe] [file]\n");

        // use default test
        GetFile(szPath);
    }
    else
    {
        printf("%d %s\n", argc, argv[1]);
        strncpy_s(szPath, argv[1], strlen(argv[1]));
    }

    char* fileBuffer = NULL;
    bool bRet = ReadFile(szPath, &fileBuffer);
    if (bRet)
    {
        PIMAGE_DOS_HEADER pDosHeader;
        PIMAGE_NT_HEADERS32 pNTHeaders;
        PIMAGE_SECTION_HEADER pSectionHeader;
        PIMAGE_SECTION_HEADER pFirstSectionHeader;

        pDosHeader = (PIMAGE_DOS_HEADER)fileBuffer;
        pNTHeaders = (PIMAGE_NT_HEADERS32)(fileBuffer + pDosHeader->e_lfanew);

        pSectionHeader = (PIMAGE_SECTION_HEADER)((char*)pNTHeaders + sizeof(IMAGE_NT_HEADERS32));
        pFirstSectionHeader = pSectionHeader;

        PrintFileHeader(pNTHeaders);
        PrintOptionalHeaders(pNTHeaders);
        PrintSectionHeader(pNTHeaders);
        PrintExportTable(fileBuffer, pNTHeaders, pFirstSectionHeader);
        PrintImportTable(fileBuffer, pNTHeaders, pFirstSectionHeader);
        PrintIATTable(fileBuffer, pNTHeaders, pFirstSectionHeader);
        PrintBaseRelocationTable(fileBuffer, pNTHeaders, pFirstSectionHeader);

        if (fileBuffer)
        {
            delete fileBuffer;
            fileBuffer = NULL;
        }
    }

    //system("pause");
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值