1
mirror of https://github.com/rapid7/metasploit-payloads synced 2025-05-06 16:09:38 +02:00
2018-08-31 17:01:31 -05:00

400 lines
15 KiB
C
Executable File

/*
* \file libpetool.c
* \brief Applies complex operations on a given PE FILE
*/
#include <string.h>
#include <stddef.h>
#include "libpetool.h"
/* Min/Max Macros */
#define MIN(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define MAX(_a, _b) ((_a) > (_b) ? (_a) : (_b))
/* Align value */
static uint32_t __petool_align(uint32_t value, uint32_t alignment) {
return (value % alignment > 0) ? value + alignment - (value % alignment) : value;
}
/* Adjusting values in the optional header */
static void __petool_adjust_optional_header(PEFILE *out) {
size_t i = 0;
uint32_t size_of_code = 0;
uint32_t size_of_initialized_data = 0;
uint32_t size_of_uninitialized_data = 0;
bool is_32_bit = false;
uint32_t sizeofimage;
/* Default values (PE COFF Specification) */
size_t section_alignment = NT_SECTION_ALIGNMENT;
/* Check if 32/64 bit header */
if (out->optional_header_32.Magic == NT_OPTIONAL_32_MAGIC) {
is_32_bit = true;
section_alignment = out->optional_header_32.SectionAlignment;
} else if (out->optional_header_64.Magic == NT_OPTIONAL_64_MAGIC) {
is_32_bit = false;
section_alignment = out->optional_header_64.SectionAlignment;
} else {
/* Nothing to adjust */
return;
}
/* Sum up over sections */
for (i = 0; i < out->pe_header.NumberOfSections; ++i) {
if (out->section_header[i].Characteristics & IMAGE_SCN_CNT_CODE) {
size_of_code += out->section_header[i].Misc.VirtualSize;
}
if (out->section_header[i].Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
size_of_initialized_data += out->section_header[i].Misc.VirtualSize;
}
if (out->section_header[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
size_of_uninitialized_data += out->section_header[i].Misc.VirtualSize;
}
}
/* Align SizeOfImage to Section Alignment */
sizeofimage = __petool_align(
out->section_header[out->pe_header.NumberOfSections - 1].VirtualAddress
+ out->section_header[out->pe_header.NumberOfSections - 1].Misc.VirtualSize, (uint32_t)section_alignment);
/* Write new values into required fields */
if (is_32_bit) {
out->optional_header_32.SizeOfImage = sizeofimage;
out->optional_header_32.SizeOfCode = size_of_code;
out->optional_header_32.SizeOfInitializedData = size_of_initialized_data;
out->optional_header_32.SizeOfUninitializedData = size_of_uninitialized_data;
} else {
out->optional_header_64.SizeOfImage = sizeofimage;
out->optional_header_64.SizeOfCode = size_of_code;
out->optional_header_64.SizeOfInitializedData = size_of_initialized_data;
out->optional_header_64.SizeOfUninitializedData = size_of_uninitialized_data;
}
}
/* Increases the header padding with the given size */
static bool __petool_increase_header_padding(size_t size, PEFILE *out) {
size_t i = 0;
unsigned char *new_header_padding = NULL;
size_t header_raw_end = out->dos_header.e_lfanew + sizeof(PE_HEADER) + out->pe_header.SizeOfOptionalHeader
+ out->pe_header.NumberOfSections * sizeof(SECTION_HEADER);
new_header_padding = realloc(out->header_padding.mem, out->header_padding.memsize + size);
/* Couldn't reallocate memory */
if (new_header_padding == NULL) {
return false;
}
/* No way to resize without damaging code execution */
/* Sorry for this cruel if .. */
if ((out->pe_header.NumberOfSections > 0) && (out->section_header != NULL)
&& ((out->optional_header_32.Magic == NT_OPTIONAL_32_MAGIC)
|| (out->optional_header_64.Magic == NT_OPTIONAL_64_MAGIC))
&& (header_raw_end + out->header_padding.memsize + size) > out->section_header[0].VirtualAddress) {
/* Reset header padding */
out->header_padding.mem = realloc(new_header_padding, out->header_padding.memsize);
return false;
}
/* Increase at head */
memmove(new_header_padding + size, new_header_padding, out->header_padding.memsize);
memset(new_header_padding, 0, size);
out->header_padding.mem = new_header_padding;
out->header_padding.memsize += size;
/* Fix SizeOfHeaders */
if (out->optional_header_32.Magic == NT_OPTIONAL_32_MAGIC) {
if (out->optional_header_32.SizeOfHeaders < (header_raw_end + out->header_padding.memsize)) {
out->optional_header_32.SizeOfHeaders = (uint32_t)(header_raw_end + out->header_padding.memsize);
}
} else if (out->optional_header_64.Magic == NT_OPTIONAL_64_MAGIC) {
if (out->optional_header_64.SizeOfHeaders < (header_raw_end + out->header_padding.memsize)) {
out->optional_header_64.SizeOfHeaders = (uint32_t)(header_raw_end + out->header_padding.memsize);
}
}
/* Fix section positions */
if ((out->pe_header.NumberOfSections > 0) && (out->section_header != NULL)) {
for (i = 0; i < out->pe_header.NumberOfSections; ++i) {
out->section_header[i].PointerToRawData += (uint32_t)size;
}
}
/* Fix additional data positions */
if ((out->additional_count > 0) && (out->additional_data != NULL)) {
for (i = 0; i < out->additional_count; ++i) {
out->additional_data[i].position += size;
}
}
return true;
}
bool petool_resize_section(size_t section_index, size_t new_raw_size, size_t new_virtual_size, bool header_only,
PEFILE *out) {
uint32_t old_raw_size = 0;
uint32_t diff = 0;
bool shrink = false;
bool raw_change = false;
unsigned char *newmem = NULL;
uint32_t i = 0;
/* Default values (PE COFF Specification) */
size_t file_alignment = NT_FILE_ALIGNMENT;
/* No Section Headers*/
if (out->section_header == NULL) {
return false;
}
/* Section Index out of bounds */
if (out->pe_header.NumberOfSections <= section_index) {
return false;
}
/* Virtual Size won't fit */
if ((out->pe_header.NumberOfSections < (section_index + 1))
&& ((out->section_header[section_index].VirtualAddress + new_virtual_size)
> out->section_header[section_index + 1].VirtualAddress)) {
return false;
}
/* Nothing to do */
if (!(raw_change = (new_raw_size != out->section_header[section_index].SizeOfRawData))
&& out->section_header[section_index].Misc.VirtualSize == new_virtual_size) {
return true;
}
/* Try to change raw size of section nested in another section, this won't work ... */
if (raw_change) {
for (i = 0; i < out->pe_header.NumberOfSections; ++i) {
if ((section_index != i)
&& (out->section_header[i].PointerToRawData <= out->section_header[section_index].PointerToRawData)
&& ((out->section_header[i].PointerToRawData + out->section_header[i].SizeOfRawData)
>= (out->section_header[section_index].PointerToRawData + out->section_header[section_index].SizeOfRawData))) {
return false;
}
}
}
/* New Virtual Size */
out->section_header[section_index].Misc.VirtualSize = (uint32_t)new_virtual_size;
/* Only needed if RawSize was changed */
if (raw_change) {
/* Save old raw size*/
old_raw_size = out->section_header[section_index].SizeOfRawData;
/* Try to load specific alignments */
/* x86 */
if ((out->optional_header_32.Magic == NT_OPTIONAL_32_MAGIC)
&& (out->pe_header.SizeOfOptionalHeader >= offsetof(OPTIONAL_HEADER_32, MajorOperatingSystemVersion))) {
file_alignment = out->optional_header_32.FileAlignment;
/* x64 */
} else if (out->optional_header_64.Magic == NT_OPTIONAL_64_MAGIC
&& (out->pe_header.SizeOfOptionalHeader >= offsetof(OPTIONAL_HEADER_64, MajorOperatingSystemVersion))) {
file_alignment = out->optional_header_64.FileAlignment;
}
/* Align new raw size */
new_raw_size = __petool_align((uint32_t)new_raw_size, (uint32_t)file_alignment);
/* Set new RawSize */
out->section_header[section_index].SizeOfRawData = (uint32_t)new_raw_size;
/* Get Size difference */
if (new_raw_size > old_raw_size) {
diff = (uint32_t)(new_raw_size - old_raw_size);
shrink = false;
} else {
diff = (uint32_t)(old_raw_size - new_raw_size);
shrink = true;
}
/* Fix section offsets*/
for (i = 0; i < out->pe_header.NumberOfSections; ++i) {
if (out->section_header[i].PointerToRawData
>= (out->section_header[section_index].PointerToRawData + old_raw_size)) {
shrink ? (out->section_header[i].PointerToRawData -= diff) : (out->section_header[i].PointerToRawData += diff);
}
}
/* Resize section memory */
if (!header_only) {
if (out->section_data == NULL) {
return false;
}
/* Try resize */
newmem = realloc(out->section_data[section_index].mem, new_raw_size);
if (newmem == NULL) {
return false;
}
/* Set new Memory */
out->section_data[section_index].mem = newmem;
out->section_data[section_index].memsize = new_raw_size;
/* Fix additional data positions */
if ((out->additional_count > 0) && (out->additional_data != NULL)) {
for (i = 0; i < out->additional_count; ++i) {
if (out->additional_data[i].position
>= (out->section_header[section_index].PointerToRawData + old_raw_size)) {
shrink ? (out->additional_data[i].position -= diff) : (out->additional_data[i].position += diff);
}
}
}
}
}
/* Adjust Optional Header*/
__petool_adjust_optional_header(out);
return true;
}
bool petool_add_section(char *name, size_t namesize, size_t characteristics, unsigned char *mem, size_t memsize,
bool header_only, PEFILE *out) {
SECTION_HEADER *new_section_header = NULL;
DATA_BLOBC *new_section_data = NULL;
size_t i;
size_t last_section_rva = 0;
size_t last_section_virtualsize = 0;
size_t header_raw_end = out->dos_header.e_lfanew + sizeof(PE_HEADER) + out->pe_header.SizeOfOptionalHeader
+ out->pe_header.NumberOfSections * sizeof(SECTION_HEADER);
/* Default values (PE COFF Specification) */
size_t section_alignment = NT_SECTION_ALIGNMENT;
size_t file_alignment = NT_FILE_ALIGNMENT;
unsigned char *section_mem = NULL;
size_t section_memsize = 0;
size_t section_raw_pointer = 0;
size_t section_rva;
/* Try to load specific alignments */
/* x86 */
if ((out->optional_header_32.Magic == NT_OPTIONAL_32_MAGIC)
&& (out->pe_header.SizeOfOptionalHeader >= offsetof(OPTIONAL_HEADER_32, MajorOperatingSystemVersion))) {
section_alignment = out->optional_header_32.SectionAlignment;
file_alignment = out->optional_header_32.FileAlignment;
last_section_rva = out->optional_header_32.BaseOfCode;
/* x64 */
} else if (out->optional_header_64.Magic == NT_OPTIONAL_64_MAGIC
&& (out->pe_header.SizeOfOptionalHeader >= offsetof(OPTIONAL_HEADER_64, MajorOperatingSystemVersion))) {
section_alignment = out->optional_header_64.SectionAlignment;
file_alignment = out->optional_header_64.FileAlignment;
last_section_rva = out->optional_header_64.BaseOfCode;
}
/* Increase header padding till the new header fits */
while (out->header_padding.memsize < sizeof(SECTION_HEADER)) {
if (!__petool_increase_header_padding(file_alignment, out)) {
/* Something went wrong*/
return false;
}
}
/* Move padding */
memmove(out->header_padding.mem, out->header_padding.mem + sizeof(SECTION_HEADER),
out->header_padding.memsize - sizeof(SECTION_HEADER));
out->header_padding.memsize -= sizeof(SECTION_HEADER);
/* Try find position of last section */
if ((out->pe_header.NumberOfSections > 0) && (out->section_header != NULL)) {
last_section_rva = out->section_header[out->pe_header.NumberOfSections - 1].VirtualAddress;
last_section_virtualsize = out->section_header[out->pe_header.NumberOfSections - 1].Misc.VirtualSize;
/* Sections can be nested inside other sections */
for (i = 0; i < out->pe_header.NumberOfSections; ++i) {
section_raw_pointer = MAX(section_raw_pointer,
out->section_header[i].PointerToRawData + out->section_header[i].SizeOfRawData);
}
} else {
section_raw_pointer = header_raw_end + out->header_padding.memsize;
}
/* Resize holder structures */
out->pe_header.NumberOfSections++;
new_section_header = realloc(out->section_header, out->pe_header.NumberOfSections * sizeof(SECTION_HEADER));
/* Couldn't allocate memory */
if (new_section_header == NULL) {
return false;
}
out->section_header = new_section_header;
if (!header_only) {
new_section_data = realloc(out->section_data, out->pe_header.NumberOfSections * sizeof(DATA_BLOBC));
/* Couldn't allocate memory */
if (new_section_data == NULL) {
return false;
}
out->section_data = new_section_data;
}
/* Calculate padded raw size & relative virtual address */
section_memsize = __petool_align((uint32_t)memsize, (uint32_t)file_alignment);
section_rva = __petool_align((uint32_t)(last_section_rva + last_section_virtualsize), (uint32_t)section_alignment);
/* Header only, don't modify data */
if (!header_only) {
/* Allocate new section memory */
section_mem = malloc(section_memsize);
/* Couldn't allocate memory */
if (section_mem == NULL) {
return false;
}
/* Copy memory to section memory and clear rest */
if (mem != NULL) {
memcpy(section_mem, mem, memsize);
memset(section_mem + memsize, 0, section_memsize - memsize);
} else {
memset(section_mem, 0, section_memsize);
}
/* Store section data */
out->section_data[out->pe_header.NumberOfSections - 1].mem = section_mem;
out->section_data[out->pe_header.NumberOfSections - 1].memsize = section_memsize;
}
/* New Section Header */
memset(&out->section_header[out->pe_header.NumberOfSections - 1], 0, sizeof(SECTION_HEADER));
out->section_header[out->pe_header.NumberOfSections - 1].PointerToRawData = (uint32_t)section_raw_pointer;
out->section_header[out->pe_header.NumberOfSections - 1].SizeOfRawData = (uint32_t)section_memsize;
out->section_header[out->pe_header.NumberOfSections - 1].VirtualAddress = (uint32_t)section_rva;
out->section_header[out->pe_header.NumberOfSections - 1].Misc.VirtualSize = (uint32_t)memsize;
out->section_header[out->pe_header.NumberOfSections - 1].Characteristics = (uint32_t)characteristics;
if (name != NULL) {
memcpy(out->section_header[out->pe_header.NumberOfSections - 1].Name, name, MIN(NT_SHORT_NAME_LEN, namesize));
}
/* Header only, don't modify data */
if (!header_only) {
/* Fix additional data positions */
if ((out->additional_count > 0) && (out->additional_data != NULL)) {
for (i = 0; i < out->additional_count; ++i) {
if (out->additional_data[i].position
== out->section_header[out->pe_header.NumberOfSections - 1].PointerToRawData) {
out->additional_data[i].position += out->section_header[out->pe_header.NumberOfSections - 1].SizeOfRawData;
break;
}
}
}
}
/* Adjust Optional Header */
__petool_adjust_optional_header(out);
return true;
}