How to manipulate section address table in a PE binary with C++?

Viewed 117

What I want to achieve is to add a new section to a second PE file (target PE) and be able to access it on runtime inside the target, so I'm looking for guidance on how to overwrite the section address table after inserting the new section.

I'm loading and parsing the PE binary from an unsigned char value with a library named libpeconv, and adding the section at the EOF, but I want to know how to overwrite the section alignment and set the characteristics to read-only because there won't be any execution inside, I'm not able to do this using the library apparently, so I will need to write the bytes directly.

Also, I'm not able to add a new section in the linking step, I need to strictly parse the target PE binary in its raw format.

1 Answers

Someone posted an answer, but later was deleted before I could make it the official answer, anyways, we just need to grab the DOS headers, get the e_lfanew value from DOS headers, use it to calculate start address from NT headers, sum the NT headers and DOS headers address to get the section table, add a new IMAGE_SECTION_HEADER structure calculate SizeOfHeaders and SizeOfImage alignment from FileAlignment and SectionAlignment. This is the code plus some editions I made:

#include <Windows.h>
#include <stdio.h>

PIMAGE_DOS_HEADER lpDosHeader;
PIMAGE_NT_HEADERS lpNtHeaders;
PIMAGE_FILE_HEADER lpFileHeader;
PIMAGE_OPTIONAL_HEADER lpOptionalHeader;
PIMAGE_SECTION_HEADER lpImageSections;
IMAGE_SECTION_HEADER ourSection;
FILE* fileStream = NULL;

char* peFileBuffer = NULL;
char* fileName = NULL;
char* outputFileName = NULL;
char* binaryFileName = NULL;
char* sectionName = NULL;
char* sectionData = NULL;
int sizeofNewSectionData = 0;
int nextArg = 1;

int align(int value, int alignment);

int main(int argc, char* argv[])
{
    PIMAGE_SECTION_HEADER lpLastSection = NULL;
    int newFileSize = 0;
    int fileAlignmentOfPEImage = 0;
    int sectionAlignmentOfPEImage = 0;
    if (argc != 5)
    {
        return -1;
    }

    fileName = argv[nextArg++];
    outputFileName = argv[nextArg++];
    sectionName = argv[nextArg++];
    if (strlen(sectionName) + 1 > 8)
    {
        return -1;
    }

    binaryFileName = argv[nextArg++];
    FILE* pre_SectionData = fopen(binaryFileName, "rb"); // Read beacon binary
    fseek(pre_SectionData, 0, SEEK_END); // Seek to SEEK_END
    size_t sizeofNewSectionData = ftell(pre_SectionData); // Get size of new section
    fseek(pre_SectionData, 0, SEEK_SET); // Seek to SEEK_SET
    sectionData = (char*)malloc(sizeofNewSectionData); // Allocate enough heap memory 
    fread(sectionData, 1, sizeofNewSectionData, pre_SectionData);
    sizeofNewSectionData = (int)sizeofNewSectionData;

    fileStream = fopen(fileName, "rb"); // Read stub binary
    fseek(fileStream, 0, SEEK_END);
    size_t fileSize = ftell(fileStream);
    fseek(fileStream, 0, SEEK_SET);
    peFileBuffer = (char*)malloc(fileSize);
    if (!peFileBuffer)
        return NULL;
    fread(peFileBuffer, 1, fileSize, fileStream);
    fclose(fileStream);

    lpDosHeader = (PIMAGE_DOS_HEADER)peFileBuffer;
    if (lpDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        return -2;
    }
    lpNtHeaders = (PIMAGE_NT_HEADERS)(peFileBuffer + lpDosHeader->e_lfanew);
    if (lpNtHeaders->Signature != IMAGE_NT_SIGNATURE)
    {
        return -3;
    }
    lpFileHeader = (PIMAGE_FILE_HEADER) & (lpNtHeaders->FileHeader);
    lpOptionalHeader = (PIMAGE_OPTIONAL_HEADER) & (lpNtHeaders->OptionalHeader);
    lpImageSections = (PIMAGE_SECTION_HEADER)((char*)lpOptionalHeader + lpFileHeader->SizeOfOptionalHeader);

    fileAlignmentOfPEImage = lpOptionalHeader->FileAlignment;
    sectionAlignmentOfPEImage = lpOptionalHeader->SectionAlignment;

    lpLastSection = lpImageSections + lpFileHeader->NumberOfSections - 1;

    fileStream = fopen(outputFileName, "wb");

    lpFileHeader->NumberOfSections++;

    lpOptionalHeader->SizeOfInitializedData += sizeofNewSectionData;
    // Edit: Size Of Headers is the sum of the NT Header and the Section Headers.
    lpOptionalHeader->SizeOfHeaders = align(lpOptionalHeader->SizeOfHeaders + sizeof(IMAGE_SECTION_HEADER), fileAlignmentOfPEImage);
    lpOptionalHeader->SizeOfImage = align(lpOptionalHeader->SizeOfImage + sizeofNewSectionData, sectionAlignmentOfPEImage);
    fwrite(peFileBuffer, 1, lpDosHeader->e_lfanew +
        sizeof(IMAGE_NT_HEADERS) +
        (lpFileHeader->NumberOfSections - 1) * sizeof(IMAGE_SECTION_HEADER), fileStream);

    memset(&ourSection, 0, sizeof(IMAGE_SECTION_HEADER));
    strcpy((char*)&ourSection.Name[0], sectionName);
    ourSection.Characteristics = IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
    ourSection.SizeOfRawData = ourSection.Misc.VirtualSize = sizeofNewSectionData;
    ourSection.PointerToRawData = align(lpLastSection->PointerToRawData + lpLastSection->SizeOfRawData, fileAlignmentOfPEImage);
    ourSection.VirtualAddress = align(lpLastSection->VirtualAddress + lpLastSection->Misc.VirtualSize, sectionAlignmentOfPEImage);
    fwrite(&ourSection, 1, sizeof(IMAGE_SECTION_HEADER), fileStream);

    int fileOffset = ftell(fileStream);

    fileOffset = align(fileOffset, fileAlignmentOfPEImage);

    for (int i = 0; i < lpFileHeader->NumberOfSections - 1; i++)
    {
        PIMAGE_SECTION_HEADER lpSection = lpImageSections + i;
        fseek(fileStream, fileOffset, SEEK_SET);
        fwrite(peFileBuffer + lpSection->PointerToRawData, 1, lpSection->SizeOfRawData, fileStream);
        fileOffset += align(lpSection->SizeOfRawData, fileAlignmentOfPEImage);
    }

    fseek(fileStream, fileOffset, SEEK_SET);
    fwrite(sectionData, 1, ourSection.SizeOfRawData, fileStream);
    fclose(fileStream);
    return 0;
}

int align(int value, int alignment)
{
    int remainder = value % alignment;
    if (remainder != 0)
    {
        int returnvalue = value + alignment - (value % alignment);
        return value + alignment - remainder;
    }
    else
    {
        int returnvalue = value;
        return value;
    }
}
Related