1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-05-12 19:04:32 +02:00
OJ f7b50df020
Update peinjectory to avoid delay loading
Also added the GetExtensionName function.
2020-04-22 13:06:47 +10:00

623 lines
21 KiB
C
Executable File

/*
* \file libpefile.c
* \brief Manages disassembly and reassembly of the PE COFF File Format
*/
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include "libpefile.h"
#include "common.h"
#include "common_metapi.h"
/* Min/Max Macros */
#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
/* Calculate raw size of image an validates positions and sizes. Returns 0 on error */
static size_t __pefile_calculate_raw_size(PEFILE *in, PEFILE_WRITE_OPTIONS *options) {
size_t i = 0;
size_t ii = 0;
size_t additional_end, section_end;
/* Raw size of Headers and padding */
size_t size = in->dos_header.e_lfanew + sizeof(PE_HEADER) + in->pe_header.SizeOfOptionalHeader
+ in->pe_header.NumberOfSections * sizeof(SECTION_HEADER) + in->header_padding.memsize;
/* Calculate header size only */
if (options && options->header_only) {
/* If additional data is forced we need to add this size too. In this case, only 1 additional data
* segment is allowed, and only if it's directly after the raw header (happens when parsing a
* truncated pe file with header_only option) */
if (options->force_additional) {
if (in->additional_count > 1 || ((in->additional_count > 0) && (in->additional_data == NULL))) {
return 0;
} else if (in->additional_count > 0) {
if (size == in->additional_data[0].position) {
size += in->additional_data[0].memsize;
} else {
return 0;
}
}
}
return size;
}
/* Validate & add all Section sizes */
if ((in->pe_header.NumberOfSections > 0) && (in->section_header == NULL)) {
return 0;
}
for (i = 0; i < in->pe_header.NumberOfSections; ++i) {
/* Nested sections */
for (ii = 0; ii < in->pe_header.NumberOfSections; ++ii) {
if ((i != ii) && (in->section_header[ii].PointerToRawData <= in->section_header[i].PointerToRawData)
&& ((in->section_header[ii].PointerToRawData + in->section_header[ii].SizeOfRawData)
>= (in->section_header[i].PointerToRawData + in->section_header[i].SizeOfRawData))) {
size -= in->section_header[i].SizeOfRawData;
}
}
size += in->section_header[i].SizeOfRawData;
}
/* Validate & add all additional data */
if ((in->additional_count > 0) && (in->additional_data == NULL)) {
return 0;
}
for (i = 0; i < in->additional_count; ++i) {
size += in->additional_data[i].memsize;
}
/* Validate additional data positions */
for (i = 0; i < in->additional_count; ++i) {
if ((in->additional_data[i].position + in->additional_data[i].memsize) > size) {
return 0;
} else {
/* If additional data starts or ends inside section something is wrong */
additional_end = in->additional_data[i].position + in->additional_data[i].memsize;
for (ii = 0; ii < in->pe_header.NumberOfSections; ++ii) {
section_end = in->section_header[ii].PointerToRawData + in->section_header[ii].SizeOfRawData;
if (((in->additional_data[i].position >= in->section_header[ii].PointerToRawData)
&& in->additional_data[i].position < section_end)
|| ((additional_end > in->section_header[ii].PointerToRawData) && additional_end < section_end)) {
return 0;
}
}
}
}
/* Validate Section positions */
for (i = 0; i < in->pe_header.NumberOfSections; ++i) {
if ((in->section_header[i].PointerToRawData + in->section_header[i].SizeOfRawData) > size) {
return 0;
}
}
return size;
}
/* Adds additional data to an pe file structure. Return true on success, false otherwise*/
static bool __pefile_add_additional_data(unsigned char *mem, size_t position, size_t size, PEFILE *out) {
DATA_BLOB_EX *newAdditional = NULL;
out->additional_count++;
newAdditional = realloc(out->additional_data, out->additional_count * sizeof(DATA_BLOB_EX));
/* Couldn't allocate memory */
if (newAdditional == NULL) {
return false;
}
out->additional_data = newAdditional;
out->additional_data[out->additional_count - 1].position = position;
out->additional_data[out->additional_count - 1].memsize = size;
out->additional_data[out->additional_count - 1].mem = malloc(out->additional_data[out->additional_count - 1].memsize);
/* Couldn't allocate memory */
if (out->additional_data[out->additional_count - 1].mem == NULL) {
return false;
}
memcpy(out->additional_data[out->additional_count - 1].mem,
mem + out->additional_data[out->additional_count - 1].position,
out->additional_data[out->additional_count - 1].memsize);
return true;
}
bool pefile_read_mem(unsigned char *mem, size_t memsize, PEFILE_READ_OPTIONS *options, PEFILE *out) {
DOS_HEADER *dHead = NULL;
PE_HEADER *pHead = NULL;
OPTIONAL_HEADER_32 *oHead32 = NULL;
OPTIONAL_HEADER_64 *oHead64 = NULL;
SECTION_HEADER *firstsHead = NULL;
unsigned char *dos_stub = NULL;
size_t dos_stubsize = 0;
size_t header_raw_end = 0;
size_t header_padding_end = 0;
size_t additional_end = 0;
size_t additional_start = 0;
size_t gap_start = 0;
size_t gap_size = 0;
size_t i = 0;
size_t ii = 0;
/* Zero first */
memset(out, 0, sizeof(PEFILE));
/* DOS Header Magic OK & Size OK */
dHead = (DOS_HEADER*) mem;
if ((memsize < sizeof(DOS_HEADER)) || (dHead->e_magic != MZ_MAGIC)) {
return false;
}
/* PE Header Magic OK & Size OK) */
pHead = (PE_HEADER *) (mem + dHead->e_lfanew);
if ((memsize < dHead->e_lfanew + sizeof(PE_HEADER)) || (pHead->Signature != NT_MAGIC)) {
return false;
}
/* Save DOS Stub */
/* Sometimes there is no DOS Stub, the PE Header can be inside the DOS Header */
if (dHead->e_lfanew >= sizeof(DOS_HEADER) && (dHead->e_lfanew - sizeof(DOS_HEADER) > 0)) {
dos_stub = mem + sizeof(DOS_HEADER);
dos_stubsize = dHead->e_lfanew - sizeof(DOS_HEADER);
}
/* Optional Header Magic OK & Size OK */
oHead32 = (OPTIONAL_HEADER_32 *) (mem + dHead->e_lfanew + sizeof(PE_HEADER));
oHead64 = (OPTIONAL_HEADER_64 *) oHead32;
if (pHead->SizeOfOptionalHeader > 0) {
if ((memsize < ((size_t) oHead32 - (size_t) mem) + pHead->SizeOfOptionalHeader)
|| ((oHead32->Magic != NT_OPTIONAL_32_MAGIC) && (oHead64->Magic != NT_OPTIONAL_64_MAGIC))) {
return false;
}
}
/* Section Table. Size Ok? */
if (memsize
< (dHead->e_lfanew + sizeof(PE_HEADER) + pHead->SizeOfOptionalHeader
+ pHead->NumberOfSections * sizeof(SECTION_HEADER))) {
return false;
} else {
firstsHead = (SECTION_HEADER *) (mem + dHead->e_lfanew + sizeof(PE_HEADER) + pHead->SizeOfOptionalHeader);
}
/* Find real end of Header padding (Packers doing some shit with that ...) */
/* Raw end and padding end defined in Optional Header*/
header_raw_end = dHead->e_lfanew + sizeof(PE_HEADER) + pHead->SizeOfOptionalHeader
+ pHead->NumberOfSections * sizeof(SECTION_HEADER);
if (oHead32->Magic == NT_OPTIONAL_32_MAGIC) {
header_padding_end = oHead32->SizeOfHeaders;
} else {
header_padding_end = oHead64->SizeOfHeaders;
}
/* Section starting inside padding */
for (i = 0; i < pHead->NumberOfSections; ++i) {
if ((firstsHead[i].SizeOfRawData > 0) && firstsHead[i].PointerToRawData < header_padding_end) {
/* Section inside Header */
if (firstsHead[i].PointerToRawData < header_raw_end) {
return false;
}
header_padding_end = firstsHead[i].PointerToRawData;
}
}
/* Header Padding in virtual space */
if (header_padding_end > memsize) {
header_padding_end = memsize;
/* But must go till Section 0 */
} else if ((pHead->NumberOfSections > 0) && header_padding_end < firstsHead[0].PointerToRawData) {
header_padding_end = firstsHead[0].PointerToRawData;
}
/* Copy DOS & PE Header*/
memcpy(&out->dos_header, dHead, sizeof(DOS_HEADER));
memcpy(&out->pe_header, pHead, sizeof(PE_HEADER));
/* Copy DOS Stub */
if (dos_stub != NULL) {
out->dos_stub.mem = malloc(dos_stubsize);
out->dos_stub.memsize = dos_stubsize;
memcpy(out->dos_stub.mem, dos_stub, dos_stubsize);
}
/* Copy Optional Header */
if (pHead->SizeOfOptionalHeader > 0) {
if (oHead32->Magic == NT_OPTIONAL_32_MAGIC) {
memcpy(&out->optional_header_32, oHead32, MIN(pHead->SizeOfOptionalHeader, sizeof(OPTIONAL_HEADER_32)));
/* Rare: Gap between Optional Header an Section Table */
if (pHead->SizeOfOptionalHeader > sizeof(OPTIONAL_HEADER_32)) {
gap_start = dHead->e_lfanew + sizeof(PE_HEADER) + sizeof(OPTIONAL_HEADER_32);
gap_size = pHead->SizeOfOptionalHeader - sizeof(OPTIONAL_HEADER_32);
}
} else {
memcpy(&out->optional_header_64, oHead64, MIN(pHead->SizeOfOptionalHeader, sizeof(OPTIONAL_HEADER_64)));
/* Rare: Gap between Optional Header an Section Table */
if (pHead->SizeOfOptionalHeader > sizeof(OPTIONAL_HEADER_64)) {
gap_start = dHead->e_lfanew + sizeof(PE_HEADER) + sizeof(OPTIONAL_HEADER_64);
gap_size = pHead->SizeOfOptionalHeader - sizeof(OPTIONAL_HEADER_64);
}
}
/* Copy gap data if needed */
if (gap_start > 0) {
out->optional_header_gap.memsize = gap_size;
out->optional_header_gap.mem = malloc(gap_size);
/* Couldn't allocate memory */
if (out->optional_header_gap.mem == NULL) {
pefile_free(out);
return false;
}
memcpy(out->optional_header_gap.mem, mem + gap_start, gap_size);
}
}
/* Copy Section Headers & Sections */
if (firstsHead != NULL && pHead->NumberOfSections > 0) {
out->section_header = malloc(pHead->NumberOfSections * sizeof(SECTION_HEADER));
/* Couldn't allocate memory */
if (out->section_header == NULL) {
pefile_free(out);
return false;
}
memcpy(out->section_header, firstsHead, pHead->NumberOfSections * sizeof(SECTION_HEADER));
/* Only if full analysis */
if ((options == NULL) || ((options != NULL) && !options->header_only)) {
/* Copy Sections & Additional Data */
out->section_data = malloc(pHead->NumberOfSections * sizeof(DATA_BLOBC));
for (i = 0; i < pHead->NumberOfSections; ++i) {
if (firstsHead[i].SizeOfRawData) {
out->section_data[i].memsize = firstsHead[i].SizeOfRawData;
out->section_data[i].mem = malloc(out->section_data[i].memsize);
/* Couldn't allocate memory */
if (out->section_data[i].mem == NULL) {
pefile_free(out);
return false;
}
memcpy(out->section_data[i].mem, mem + firstsHead[i].PointerToRawData, out->section_data[i].memsize);
/* Find additional Data between sections */
additional_start = firstsHead[i].PointerToRawData + firstsHead[i].SizeOfRawData;
additional_end = memsize;
for (ii = 0; ii < pHead->NumberOfSections; ++ii) {
if (i == ii) {
continue;
}
if (firstsHead[ii].PointerToRawData == additional_start) {
additional_end = additional_start;
break;
} else if ((firstsHead[ii].PointerToRawData > additional_start)
&& (firstsHead[ii].PointerToRawData < additional_end)) {
additional_end = firstsHead[ii].PointerToRawData;
/* nested sections */
} else if ((firstsHead[ii].PointerToRawData <= firstsHead[i].PointerToRawData)
&& ((firstsHead[ii].PointerToRawData + firstsHead[ii].SizeOfRawData) >= additional_start)) {
additional_end = additional_start;
break;
}
}
/* Additional data found */
if ((additional_start != additional_end)
&& !__pefile_add_additional_data(mem, additional_start, additional_end - additional_start, out)) {
/* Something went wrong, terminate */
pefile_free(out);
return false;
}
}
}
/* Force add data (everything after header, useful for truncated pe files
* which shall be reassembled later */
} else if (options->force_additional && (memsize - header_padding_end > 0)
&& !__pefile_add_additional_data(mem, header_padding_end, memsize - header_padding_end, out)) {
/* Something went wrong, terminate */
pefile_free(out);
return false;
}
} else if ((options != NULL) && !(options->header_only && !options->force_additional)) {
/* No Sections: Everything else is additional */
if ((memsize - header_padding_end) > 0
&& !__pefile_add_additional_data(mem, header_padding_end, memsize - header_padding_end, out)) {
/* Something went wrong, terminate */
pefile_free(out);
return false;
}
}
/* Copy real Header Padding */
out->header_padding.position = header_raw_end; /* Won't be used for building, just a shortcut to real header size */
if (header_padding_end - header_raw_end > 0) {
out->header_padding.memsize = header_padding_end - header_raw_end;
out->header_padding.mem = malloc(out->header_padding.memsize);
memcpy(out->header_padding.mem, mem + header_raw_end, out->header_padding.memsize);
}
return true;
}
void pefile_init(PEFILE *in) {
memset(in, 0, sizeof(PEFILE));
}
bool pefile_read_file(char *file, PEFILE_READ_OPTIONS *options, PEFILE *out) {
bool returnVar = false;
unsigned char *file_mem;
FILE *fh;
/* Open file */
wchar_t *file_w = met_api->string.utf8_to_wchar(file);
if (_wfopen_s(&fh, file_w, L"rb") == 0) {
/* Get file size and allocate buffer */
fseek(fh, 0L, SEEK_END);
size_t size = ftell(fh);
size_t read_size = 0;
rewind(fh);
file_mem = malloc(size);
if (file_mem != NULL) {
/* Load file into buffer */
read_size = fread(file_mem, size, 1, fh);
fclose(fh);
fh = NULL;
/* Process PE file in memory */
if (read_size == 1) {
returnVar = pefile_read_mem(file_mem, size, options, out);
}
/* free buffer after use */
free(file_mem);
}
/* Close file (if memory allocation has failed) */
if (fh != NULL) {
fclose(fh);
}
}
free(file_w);
return returnVar;
}
bool pefile_write_mem(PEFILE *in, PEFILE_WRITE_OPTIONS *options, unsigned char **mem, size_t *memsize) {
size_t position = 0;
size_t i = 0;
uint64_t checksum = 0;
uint64_t top = 0xffffffffLL + 0x01;
size_t gap_start = 0;
size_t gap_size = 0;
/* Allocate needed memory */
*memsize = __pefile_calculate_raw_size(in, options);
/* Try allocate memory */
if (*memsize > 0) {
*mem = malloc(*memsize);
/* Couldn't allocate memory */
if (mem == NULL) {
return false;
}
} else {
return false;
}
/* Write DOS Header*/
memcpy(*mem, &in->dos_header, sizeof(DOS_HEADER));
/* Write PE Header */
position = in->dos_header.e_lfanew;
memcpy(*mem + position, &in->pe_header, sizeof(PE_HEADER));
/* Write DOS Stub if needed */
if (in->dos_stub.memsize) {
position = sizeof(DOS_HEADER);
memcpy(*mem + position, in->dos_stub.mem, in->dos_stub.memsize);
}
/* Write Optional Header if needed */
if (in->pe_header.SizeOfOptionalHeader > 0) {
position = in->dos_header.e_lfanew + sizeof(PE_HEADER);
if (in->optional_header_32.Magic == NT_OPTIONAL_32_MAGIC) {
memcpy(*mem + position, &in->optional_header_32,
MIN(in->pe_header.SizeOfOptionalHeader, sizeof(OPTIONAL_HEADER_32)));
/* Rare: Gap between Optional Header an Section Table */
if (in->pe_header.SizeOfOptionalHeader > sizeof(OPTIONAL_HEADER_32)) {
gap_start = in->dos_header.e_lfanew + sizeof(PE_HEADER) + sizeof(OPTIONAL_HEADER_32);
gap_size = in->pe_header.SizeOfOptionalHeader - sizeof(OPTIONAL_HEADER_32);
}
} else if (in->optional_header_64.Magic == NT_OPTIONAL_64_MAGIC) {
memcpy(*mem + position, &in->optional_header_64,
MIN(in->pe_header.SizeOfOptionalHeader, sizeof(OPTIONAL_HEADER_64)));
/* Rare: Gap between Optional Header an Section Table */
if (in->pe_header.SizeOfOptionalHeader > sizeof(OPTIONAL_HEADER_64)) {
gap_start = in->dos_header.e_lfanew + sizeof(PE_HEADER) + sizeof(OPTIONAL_HEADER_64);
gap_size = in->pe_header.SizeOfOptionalHeader - sizeof(OPTIONAL_HEADER_64);
}
}
/* Fill gap if there is saved data */
if (gap_start && (in->optional_header_gap.mem != NULL)) {
memcpy(*mem + gap_start, in->optional_header_gap.mem, MIN(gap_size, in->optional_header_gap.memsize));
}
}
/* Writes Section Table if needed */
if (in->pe_header.NumberOfSections > 0) {
position = in->dos_header.e_lfanew + sizeof(PE_HEADER) + in->pe_header.SizeOfOptionalHeader;
memcpy(*mem + position, in->section_header, in->pe_header.NumberOfSections * sizeof(SECTION_HEADER));
}
/* Writes Header Padding if needed */
if ((in->header_padding.mem != NULL) && (in->header_padding.position > 0)) {
position = in->dos_header.e_lfanew + sizeof(PE_HEADER) + in->pe_header.SizeOfOptionalHeader
+ in->pe_header.NumberOfSections * sizeof(SECTION_HEADER);
memcpy(*mem + position, in->header_padding.mem, in->header_padding.memsize);
}
/* Header Only */
/* Exception: force_additional override */
if (options != NULL && options->header_only && !options->force_additional) {
return true;
}
/* Write additional Data */
if (in->additional_count > 0) {
for (i = 0; i < in->additional_count; ++i) {
if (in->additional_data[i].mem != NULL) {
memcpy(*mem + in->additional_data[i].position, in->additional_data[i].mem, in->additional_data[i].memsize);
}
}
}
/* Header Only */
if (options != NULL && options->header_only) {
return true;
}
/* Write Section Data */
if (in->section_data != NULL) {
for (i = 0; i < in->pe_header.NumberOfSections; ++i) {
if (in->section_data[i].mem != NULL) {
memcpy(*mem + in->section_header[i].PointerToRawData, in->section_data[i].mem,
MIN(in->section_data[i].memsize, in->section_header[i].SizeOfRawData));
}
}
}
/* Recalculate checksum if forced & OptionalHeader is large enough*/
if ((options != NULL)
&& (in->pe_header.SizeOfOptionalHeader > offsetof(OPTIONAL_HEADER_32, CheckSum) + sizeof(uint32_t))
&& options->recaluclate_checksum) {
/* Position of CheckSum (32/64 Bit position is equal) */
position = in->dos_header.e_lfanew + sizeof(PE_HEADER) + offsetof(OPTIONAL_HEADER_32, CheckSum);
/* Calculate checksum for Image */
for (i = 0; i < *memsize; i += 4) {
if (i == position) {
continue;
}
checksum = (checksum & 0xffffffff) + *(uint32_t *) (*mem + i) + (checksum >> 32);
if (checksum > top) {
checksum = (checksum & 0xffffffff) + (checksum >> 32);
}
}
checksum = (checksum & 0xffff) + (checksum >> 16);
checksum = (checksum) + (checksum >> 16);
checksum = checksum & 0xffff;
checksum += *memsize;
/* Write checksum*/
*(uint32_t *) (*mem + position) = (uint32_t)checksum;
}
return true;
}
bool pefile_write_file(PEFILE *in, PEFILE_WRITE_OPTIONS *options, char* file) {
bool returnVar = false;
unsigned char *mem = NULL;
unsigned char **mem_ref = (unsigned char **) &mem;
size_t memsize = 0;
/* Open file */
FILE *fh;
wchar_t *file_w = met_api->string.utf8_to_wchar(file);
if (_wfopen_s(&fh, file_w, L"wb") == 0) {
/* Generate PE File memory */
if (pefile_write_mem(in, options, mem_ref, &memsize)) {
/* Write to file and verify size */
returnVar = (fwrite(mem, memsize, 1, fh) == 1) ? true : false;
/* Free memory */
free(mem);
}
/* Close file */
fclose(fh);
}
free(file_w);
return returnVar;
}
void pefile_free(PEFILE *in) {
size_t i = 0;
/* Free DOS stub */
if (in->dos_stub.mem != NULL) {
free(in->dos_stub.mem);
}
/* Free Optional Header Gap */
if (in->optional_header_gap.mem != NULL) {
free(in->optional_header_gap.mem);
}
/* Free Section Header */
if (in->section_header != NULL) {
free(in->section_header);
}
/* Free Header padding */
if (in->header_padding.mem != NULL) {
free(in->header_padding.mem);
}
/* Free Section data */
if (in->section_data != NULL) {
for (i = 0; i < in->pe_header.NumberOfSections; ++i) {
if (in->section_data[i].mem != NULL) {
free(in->section_data[i].mem);
}
}
free(in->section_data);
}
/* Free additional data*/
if (in->additional_count && (in->additional_data != NULL)) {
for (i = 0; i < in->additional_count; ++i) {
if (in->additional_data[i].mem != NULL) {
free(in->additional_data[i].mem);
}
}
free(in->additional_data);
}
/* Zero structure */
memset(in, 0, sizeof(PEFILE));
}
uint16_t get_file_architecture(char * target_executable_path) {
PEFILE mype;
pefile_init(&mype);
PEFILE_READ_OPTIONS read_options;
read_options.header_only = true;
if (pefile_read_file(target_executable_path, &read_options, &mype))
return mype.pe_header.Machine;
else
return 0;
}