reactos/sdk/tools/pefixup.c
Mark Jansen 0b948581fc
[SDK][HOST-TOOLS] Add load_config workaround for gcc builds
Since binutils can not add this, we re-introduce pefixup.
It searches for the exported symbol '_load_config_used',
and uses that to fill out the LOAD_CONFIG directory in the PE header
2020-04-17 17:47:24 +02:00

197 lines
5.6 KiB
C

/*
* PE Fixup Utility
* Copyright (C) 2005 Filip Navara
* Copyright (C) 2020 Mark Jansen
*
* The purpose of this utility is fix PE binaries generated by binutils and
* to manipulate flags that can't be set by binutils.
*
* Currently one features is implemented:
*
* - Updating the PE header to use a LOAD_CONFIG,
* when the struct is exported with the name '_load_config_used'
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// host_includes
#include <typedefs.h>
#include <pecoff.h>
#include "../../dll/win32/dbghelp/compat.h"
void *rva_to_ptr(unsigned char *buffer, PIMAGE_NT_HEADERS nt_header, DWORD rva)
{
unsigned int i;
PIMAGE_SECTION_HEADER section_header = IMAGE_FIRST_SECTION(nt_header);
for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section_header++)
{
if (rva >= section_header->VirtualAddress &&
rva < section_header->VirtualAddress + section_header->Misc.VirtualSize)
{
return buffer + rva - section_header->VirtualAddress + section_header->PointerToRawData;
}
}
return NULL;
}
static void fix_checksum(unsigned char *buffer, long len, PIMAGE_NT_HEADERS nt_header)
{
unsigned int checksum = 0;
long n;
nt_header->OptionalHeader.CheckSum = 0;
for (n = 0; n < len; n += 2)
{
checksum += *(unsigned short *)(buffer + n);
checksum = (checksum + (checksum >> 16)) & 0xffff;
}
checksum += len;
nt_header->OptionalHeader.CheckSum = checksum;
}
static int add_loadconfig(unsigned char *buffer, PIMAGE_NT_HEADERS nt_header)
{
PIMAGE_DATA_DIRECTORY export_dir;
PIMAGE_EXPORT_DIRECTORY export_directory;
export_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_dir->Size != 0)
{
export_directory = rva_to_ptr(buffer, nt_header, export_dir->VirtualAddress);
if (export_directory != NULL)
{
DWORD *name_ptr, *function_ptr, n;
WORD *ordinal_ptr;
name_ptr = rva_to_ptr(buffer, nt_header, export_directory->AddressOfNames);
ordinal_ptr = rva_to_ptr(buffer, nt_header, export_directory->AddressOfNameOrdinals);
function_ptr = rva_to_ptr(buffer, nt_header, export_directory->AddressOfFunctions);
for (n = 0; n < export_directory->NumberOfNames; n++)
{
const char* name = rva_to_ptr(buffer, nt_header, name_ptr[n]);
if (!strcmp(name, "_load_config_used"))
{
PIMAGE_DATA_DIRECTORY load_config_dir;
DWORD load_config_rva = function_ptr[ordinal_ptr[n]];
DWORD* load_config_ptr = rva_to_ptr(buffer, nt_header, load_config_rva);
/* Update the DataDirectory pointer / size
The first entry of the LOAD_CONFIG struct is the size, use that as DataDirectory.Size */
load_config_dir = &nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
load_config_dir->VirtualAddress = load_config_rva;
load_config_dir->Size = *load_config_ptr;
return 0;
}
}
fprintf(stderr, "Export '_load_config_used' not found\n");
}
else
{
fprintf(stderr, "Invalid rva for export directory\n");
}
}
else
{
fprintf(stderr, "No export directory\n");
}
return 1;
}
int main(int argc, char **argv)
{
FILE* file;
long len;
unsigned char *buffer;
PIMAGE_DOS_HEADER dos_header;
int result = 1;
if (argc < 2)
{
printf("Usage: %s <filename>\n", argv[0]);
return 1;
}
/* Read the whole file to memory. */
file = fopen(argv[1], "r+b");
if (!file)
{
fprintf(stderr, "Can't open '%s'.\n", argv[1]);
return 1;
}
fseek(file, 0, SEEK_END);
len = ftell(file);
if (len < sizeof(IMAGE_DOS_HEADER))
{
fclose(file);
fprintf(stderr, "'%s' isn't a PE image (too short)\n", argv[1]);
return 1;
}
/* Add one byte extra for the case where the input file size is odd.
We rely on this in our crc calculation */
buffer = calloc(len + 1, 1);
if (buffer == NULL)
{
fclose(file);
fprintf(stderr, "Not enough memory available.\n");
return 1;
}
/* Read the whole input file into a buffer */
fseek(file, 0, SEEK_SET);
fread(buffer, 1, len, file);
/* Check the headers and save pointers to them. */
dos_header = (PIMAGE_DOS_HEADER)buffer;
if (dos_header->e_magic == IMAGE_DOS_SIGNATURE)
{
PIMAGE_NT_HEADERS nt_header;
nt_header = (PIMAGE_NT_HEADERS)(buffer + dos_header->e_lfanew);
if (nt_header->Signature == IMAGE_NT_SIGNATURE)
{
if (!add_loadconfig(buffer, nt_header))
{
fix_checksum(buffer, len, nt_header);
/* We could 'optimize by only writing the changed parts, but keep it simple for now */
fseek(file, 0, SEEK_SET);
fwrite(buffer, 1, len, file);
/* Success */
result = 0;
}
else
{
/* Error already printed inside add_loadconfig */
}
}
else
{
fprintf(stderr, "'%s' Invalid PE signature: %x\n", argv[1], nt_header->Signature);
}
}
else
{
fprintf(stderr, "'%s' Invalid DOS signature: %x\n", argv[1], dos_header->e_magic);
}
free(buffer);
fclose(file);
return result;
}