在PE的导入表中添加一个条目

ass*_*fmo 3 windows dll automation portable-executable

我正在寻找一个命令行程序来向 PE 文件的导入表添加条目。我的目标是将一个新的导入函数从外部 DLL 添加到我的 EXE,然后使用 ollydbg 使用代码洞穴插入新代码。新代码将使用新导入的函数。

事实上我已经实现了我的目标,但是为了向导入表添加新条目,我使用了Stud_PE,它是一个 GUI 应用程序,我想自动化这部分过程。

我会考虑以编程方式解决方案,但我担心 PE 结构太复杂,无法在我的时间范围内学习和探索。此外,如果已经存在一个实现,那么不使用它将是一种耻辱。:-)

ass*_*fmo 5

找到了正在寻找的东西。

m-PEFile for c++: http://forum.exetools.com/showpost.php?s=17e7516356489bb9dd17e294e147ef96&p=60183&postcount=3

另请查看python 的pefile :http: //code.google.com/p/pefile/

以及用于 java 的PE/COFF 4J :http: //pecoff4j.sourceforge.net/

在我看来,PE/COFF 4J 的功能有限,但也许您会发现它很有帮助。

代码:PEFile.h

/*******************************************************************************
********************************   Team AT4RE   ********************************
********************************************************************************
*******************  PLEASE DON'T CHANGE/REMOVE THIS HEADER  *******************
********************************************************************************
**                                                                            **
**  Title:      PEFile class.                                                 **
**  Desc:       A handy class to manipulate pe files.                         **
**  Author:     MohammadHi [ in4matics at hotmail dot com ]                   **
**  WwW:        AT4RE      [ http://www.at4re.com ]                           **
**  Date:       2008-01-28                                                    **
**                                                                            **
********************************************************************************
*******************************************************************************/

/*
[  PE File Format   ]
---------------------
|    DOS Header     |
---------------------
|     DOS Stub      |
---------------------
|     PE Header     |
---------------------
|   Section Table   |
---------------------
|      Padding      |
---------------------
|     Section 1     |
---------------------
|     Section 2     |
---------------------
|        ...        |
---------------------
|     Section n     |
---------------------*/

//==============================================================================
#pragma once
#pragma pack(1)
//==============================================================================
#include <windows.h>
//==============================================================================
#define MAX_SECTION_COUNT       64
#define SECTION_IMPORT          "@.import"
#define SECTION_RESERV          "@.reserv"
//==============================================================================
struct PE_DOS_HEADER {
    WORD   Signature;
    WORD   LastPageBytes;
    WORD   NumberOfPages;
    WORD   Relocations;
    WORD   HeaderSize;
    WORD   MinMemory;
    WORD   MaxMemory;
    WORD   InitialSS;
    WORD   InitialSP;
    WORD   Checksum;
    WORD   InitialIP;
    WORD   InitialCS;
    WORD   RelocTableOffset;
    WORD   Overlay;
    WORD   Reserved1[4];
    WORD   OemId;
    WORD   OemInfo;
    WORD   Reserved2[10];
    LONG   PEHeaderOffset;
};
struct PE_DOS_STUB {
    char*   RawData;
    DWORD   Size;
};
struct PE_SECTION_DATA {
    DWORD   Offset;
    char*   RawData;
    DWORD   Size;
};
struct PE_IMPORT_FUNCTION {
    char*               FunctionName;
    int                 FunctionId;
    PE_IMPORT_FUNCTION* Next;
};
struct PE_IMPORT_DLL {
    char*               DllName;
    PE_IMPORT_FUNCTION* Functions;
    PE_IMPORT_DLL*  Next;
};
//==============================================================================
typedef IMAGE_NT_HEADERS PE_NT_HEADERS;
typedef IMAGE_SECTION_HEADER PE_SECTION_HEADER;
//==============================================================================
class PEFile {
public:
    PE_DOS_HEADER       dosHeader;
    PE_DOS_STUB         dosStub;
    PE_NT_HEADERS       peHeaders;
    PE_SECTION_HEADER   sectionTable[MAX_SECTION_COUNT];
    PE_SECTION_DATA     reservedData;
    PE_SECTION_DATA     sections[MAX_SECTION_COUNT];
    PE_IMPORT_DLL       importTable;
    PE_IMPORT_DLL       newImports;

    PEFile();
    PEFile(char* filePath);
    ~PEFile();
    bool                loadFromFile(char* filePath);
    bool                loadFromMemory(char* memoryAddress);
    bool                saveToFile(char* filePath);
    int                 addSection(char* name, DWORD size, bool isExecutable);
    void                addImport(char* dllName, char** functions, int functionCount);
    void                commit();

private:
    char*               peMemory;

    void                init();
    bool                readFileData(char* filePath);
    bool                checkValidity();
    bool                readHeaders();
    bool                readBody();
    bool                readImportTable();
    bool                writePadding(HANDLE fileHandle, long paddingSize);
    void                unloadFile();

    void                buildImportTable();
    char*               buildNewImports(DWORD baseRVA);
    DWORD               calcNewImportsSize(DWORD &sizeDlls, DWORD &sizeFunctions, DWORD &sizeStrings);

    DWORD               alignNumber(DWORD number, DWORD alignment);
    DWORD               rvaToOffset(DWORD rva);
    DWORD               offsetToRVA(DWORD offset);

    void                fixReservedData();
    void                fixHeaders();
    void                fixSectionTable();

};
//==============================================================================
Run Code Online (Sandbox Code Playgroud)

代码:PEFile.cpp

/*******************************************************************************
********************************   Team AT4RE   ********************************
********************************************************************************
*******************  PLEASE DON'T CHANGE/REMOVE THIS HEADER  *******************
********************************************************************************
**                                                                            **
**  Title:      PEFile class.                                                 **
**  Desc:       A handy class to manipulate pe files.                         **
**  Author:     MohammadHi [ in4matics at hotmail dot com ]                   **
**  WwW:        AT4RE      [ http://www.at4re.com ]                           **
**  Date:       2008-01-28                                                    **
**                                                                            **
********************************************************************************
*******************************************************************************/

#include "PEFile.h"
#include <math.h>
//==============================================================================
#define DEBUG_ENABLED true;
#ifdef DEBUG_ENABLED
    #define echo(x)         MessageBox(0, x, "DEBUG", MB_ICONERROR);
    #define echo2(x, y)     { char v[256]; strcpy_s(v, 256, x); strcat_s(v, 256, y); echo(v); }
    #define echo3(x, y, z)  { char w[256]; strcpy_s(w, 256, x); strcat_s(w, 256, y); echo2(w, z); }
#else
    #define echo(x) ;
    #define echo2(x, y) ;
    #define echo3(x, y, z) ;
#endif
//==============================================================================
PEFile::PEFile() {
    init();
}
//==============================================================================
PEFile::PEFile(char* filePath) {
    init();
    loadFromFile(filePath);
}
//==============================================================================
PEFile::~PEFile() {
    unloadFile();
}
//==============================================================================
void PEFile::init() {
    peMemory = NULL;
    ZeroMemory(&newImports, sizeof(PE_IMPORT_DLL));
}
//==============================================================================
bool PEFile::readFileData(char* filePath) {
    // open the file for read
    HANDLE fileHandle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fileHandle == INVALID_HANDLE_VALUE) {
        echo3("Couldn't open file : [", filePath, "]");
        return false;
    }

    // get the file size
    DWORD fileSize = GetFileSize(fileHandle, 0);
    if (fileSize == 0) {
        CloseHandle(fileHandle);
        echo3("File size is ZeR0! : [", filePath, "]");
        return false;
    }

    // allocate memory to read the pe file (note that we used VirtualAlloc not GlobalAlloc!)
    peMemory = (char*)VirtualAlloc(NULL, fileSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (peMemory == NULL) {
        CloseHandle(fileHandle);
        echo("Couldn't allocate memory!");
        return false;
    }

    DWORD bytesRead;
    // read whole file data
    if (!ReadFile(fileHandle, peMemory, fileSize, &bytesRead, NULL) || bytesRead != fileSize) {
        CloseHandle(fileHandle);
        echo3("Couldn't read file! : [", filePath, "]");
        return false;
    }

    // close the file
    CloseHandle(fileHandle);

    return true;
}
//==============================================================================
bool PEFile::checkValidity() {
    // 'dosHeader.Signature' must be "MZ" && 'peHeaders.Signature' must be "PE\0\0"
    if (dosHeader.Signature != IMAGE_DOS_SIGNATURE || peHeaders.Signature != IMAGE_NT_SIGNATURE) {
        unloadFile();
        echo("Invalid PE file!");
        return false;
    }

    if (peHeaders.FileHeader.NumberOfSections > MAX_SECTION_COUNT) {
        unloadFile();
        echo("Number of sections > MAX_SECTION_COUNT !");
        return false;
    }

    return true;
}
//==============================================================================
bool PEFile::readHeaders() {
    // read dos/pe headers
    CopyMemory(&dosHeader, peMemory, sizeof(PE_DOS_HEADER));
    dosStub.RawData = peMemory + sizeof(PE_DOS_HEADER);
    dosStub.Size = dosHeader.PEHeaderOffset - sizeof(PE_DOS_HEADER);
    CopyMemory(&peHeaders, peMemory + dosHeader.PEHeaderOffset, sizeof(PE_NT_HEADERS));

    // check validity of the file to ensure that we loaded a "PE File" not another thing!
    if (!checkValidity()) {
        return false;
    }

    // read section table
    ZeroMemory(sectionTable, sizeof(sectionTable));
    CopyMemory(sectionTable, peMemory + dosHeader.PEHeaderOffset + sizeof(PE_NT_HEADERS), 
        peHeaders.FileHeader.NumberOfSections * sizeof(PE_SECTION_HEADER));

    return true;
}
//==============================================================================
bool PEFile::readBody() {
    // read reserved data
    DWORD reservedDataOffset = dosHeader.PEHeaderOffset + sizeof(PE_NT_HEADERS) + 
        peHeaders.FileHeader.NumberOfSections * sizeof(PE_SECTION_HEADER);

    reservedData.Offset = reservedDataOffset;
    reservedData.RawData = peMemory + reservedDataOffset;
    /*reservedData.Size = peHeaders.OptionalHeader.SizeOfHeaders - reservedDataOffset;*/
    if (sectionTable[0].PointerToRawData > 0) {
        reservedData.Size = sectionTable[0].PointerToRawData - reservedDataOffset;
    } else {
        reservedData.Size = sectionTable[0].VirtualAddress - reservedDataOffset;
    }

    // read sections
    for (int i = 0; i < peHeaders.FileHeader.NumberOfSections; i++) {
        sections[i].Offset = sectionTable[i].PointerToRawData;
        sections[i].RawData = peMemory + sectionTable[i].PointerToRawData;
        sections[i].Size = sectionTable[i].SizeOfRawData;
    }

    return true;
}
//==============================================================================
bool PEFile::readImportTable() {
    DWORD tableRVA = peHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    DWORD tableOffset = rvaToOffset(tableRVA);
    if (tableOffset == 0) {
        return false;
    }

    ZeroMemory(&importTable, sizeof(PE_IMPORT_DLL));

    IMAGE_IMPORT_DESCRIPTOR* importDesc = (IMAGE_IMPORT_DESCRIPTOR*)(peMemory + tableOffset);
    IMAGE_THUNK_DATA* importThunk;
    PE_IMPORT_DLL* importDll = &this->importTable;
    PE_IMPORT_FUNCTION* importFunction;

    while (true) {
        importDll->DllName = (char*)(peMemory + rvaToOffset(importDesc->Name));
        if (importDesc->OriginalFirstThunk > 0) {
            importThunk = (IMAGE_THUNK_DATA*)(peMemory + rvaToOffset(importDesc->OriginalFirstThunk));
        } else {
            importThunk = (IMAGE_THUNK_DATA*)(peMemory + rvaToOffset(importDesc->FirstThunk));
        }

        importDll->Functions = new PE_IMPORT_FUNCTION();
        ZeroMemory(importDll->Functions, sizeof(PE_IMPORT_FUNCTION));
        importFunction = importDll->Functions;
        while (true) {
            if ((importThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32) == IMAGE_ORDINAL_FLAG32) {
                importFunction->FunctionId = IMAGE_ORDINAL32(importThunk->u1.Ordinal);
            } else {
                DWORD nameOffset = rvaToOffset(importThunk->u1.AddressOfData);
                importFunction->FunctionName = (char*)(peMemory + nameOffset + 2);
            }

            importThunk = (IMAGE_THUNK_DATA*)((char*)importThunk + sizeof(IMAGE_THUNK_DATA));
            if (importThunk->u1.AddressOfData == 0) {
                break;
            }
            importFunction->Next = new PE_IMPORT_FUNCTION();
            ZeroMemory(importFunction->Next, sizeof(PE_IMPORT_FUNCTION));
            importFunction = importFunction->Next;
        }

        importDesc = (IMAGE_IMPORT_DESCRIPTOR*)((char*)importDesc + sizeof(IMAGE_IMPORT_DESCRIPTOR));
        if (importDesc->Name == 0) {
            break;
        }
        importDll->Next = new PE_IMPORT_DLL();
        ZeroMemory(importDll->Next, sizeof(PE_IMPORT_DLL));
        importDll = importDll->Next;
    }

    return true;
}
//==============================================================================
bool PEFile::loadFromFile(char* filePath) {
    unloadFile();

    return readFileData(filePath) &&
           readHeaders() &&
           readBody() &&
           readImportTable();
}
//==============================================================================
bool PEFile::loadFromMemory(char* memoryAddress) {
    unloadFile();

    peMemory = memoryAddress;

    return readHeaders()/* &&
           readBody() &&
           readImportTable()*/;
}
//==============================================================================
bool PEFile::saveToFile(char* filePath) {
    commit();
    buildImportTable();

    // create the output file
    HANDLE fileHandle = CreateFile(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fileHandle == INVALID_HANDLE_VALUE) {
        echo("Couldn't create file");
        return false;
    }

    DWORD bytesWritten;

    WriteFile(fileHandle, &dosHeader, sizeof(PE_DOS_HEADER), &bytesWritten, NULL);
    WriteFile(fileHandle, dosStub.RawData, dosStub.Size, &bytesWritten, NULL);
    writePadding(fileHandle, dosHeader.PEHeaderOffset - sizeof(PE_DOS_HEADER) - dosStub.Size);
    WriteFile(fileHandle, &peHeaders, sizeof(PE_NT_HEADERS), &bytesWritten, NULL);
    WriteFile(fileHandle, &sectionTable, peHeaders.FileHeader.NumberOfSections * sizeof(PE_SECTION_HEADER), &bytesWritten, NULL);
    WriteFile(fileHandle, reservedData.RawData, reservedData.Size, &bytesWritten, NULL);

    for (int i = 0; i < peHeaders.FileHeader.NumberOfSections; i++) {
        writePadding(fileHandle, sectionTable[i].PointerToRawData - GetFileSize(fileHandle, NULL));
        WriteFile(fileHandle, sections[i].RawData, sections[i].Size, &bytesWritten, NULL);
    }

    CloseHandle(fileHandle);

    return true;
}
//==============================================================================
bool PEFile::writePadding(HANDLE fileHandle, long paddingSize) {
    if (paddingSize <= 0)
        return false;

    DWORD bytesWritten;
    char* padding = new char[paddingSize];
    memset(padding, 0, paddingSize);
    WriteFile(fileHandle, padding, paddingSize, &bytesWritten, NULL);
    delete padding;

    return (bytesWritten == paddingSize);
}
//==============================================================================
void PEFile::unloadFile() {
    if (peMemory != NULL) {
        VirtualFree(peMemory, 0, MEM_RELEASE);
        peMemory = NULL;
    }
}
//==============================================================================
void PEFile::buildImportTable() {
    DWORD sizeDlls = 0;
    DWORD sizeFunctions = 0;
    DWORD sizeStrings = 0;
    DWORD newImportsSize = calcNewImportsSize(sizeDlls, sizeFunctions, sizeStrings);

    // we'll move the old dll list to the new import table, so we'll calc its size
    DWORD oldImportDllsSize = 0;
    PE_IMPORT_DLL* importDll = &this->importTable;
    while (importDll != NULL) {
        oldImportDllsSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
        importDll = importDll->Next;
    }

    // add a new section to handle the new import table
    int index = addSection(SECTION_IMPORT, oldImportDllsSize + newImportsSize, false);

    // copy old import dll list
    DWORD oldImportTableRVA = peHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    DWORD oldImportTableOffset = rvaToOffset(oldImportTableRVA);
    CopyMemory(sections[index].RawData, peMemory + oldImportTableOffset, oldImportDllsSize);

    // copy new imports
    char* newImportsData = buildNewImports(sectionTable[index].VirtualAddress + oldImportDllsSize);
    CopyMemory(sections[index].RawData + oldImportDllsSize, newImportsData, newImportsSize);

    peHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = sectionTable[index].VirtualAddress;
    peHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = sectionTable[index].SizeOfRawData;
    peHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0;
    peHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0;
}
//==============================================================================
char* PEFile::buildNewImports(DWORD baseRVA) {
    commit();

    IMAGE_IMPORT_DESCRIPTOR importDesc;
    IMAGE_THUNK_DATA importThunk;
    PE_IMPORT_DLL* importDll;
    PE_IMPORT_FUNCTION* importFunction;

    DWORD sizeDlls = 0;
    DWORD sizeFunctions = 0;
    DWORD sizeStrings = 0;
    DWORD newImportsSize = calcNewImportsSize(sizeDlls, sizeFunctions, sizeStrings);
    DWORD offsetDlls = 0;
    DWORD offsetFunctions = sizeDlls;
    DWORD offsetStrings = sizeDlls + 2 * sizeFunctions;

    char* buffer = new char[newImportsSize];
    ZeroMemory(buffer, newImportsSize);

    importDll = &newImports;
    while (importDll != NULL) {
        ZeroMemory(&importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR));
        importDesc.OriginalFirstThunk = baseRVA + offsetFunctions;
        importDesc.FirstThunk = baseRVA + offsetFunctions + sizeFunctions;
        importDesc.Name = baseRVA + offsetStrings;
        CopyMemory(buffer + offsetStrings, importDll->DllName, strlen(importDll->DllName));     
        offsetStrings += alignNumber((DWORD)strlen(importDll->DllName) + 1, 2);

        CopyMemory(buffer + offsetDlls, &importDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR));
        offsetDlls += sizeof(IMAGE_IMPORT_DESCRIPTOR);

        importFunction = importDll->Functions;
        while (importFunction != NULL) {
            ZeroMemory(&importThunk, sizeof(IMAGE_THUNK_DATA));
            if (importFunction->FunctionId != 0) {
                importThunk.u1.Ordinal = importFunction->FunctionId | IMAGE_ORDINAL_FLAG32;
            } else {