添加项目文件。

This commit is contained in:
琴心
2022-04-26 15:31:46 +08:00
parent 4f1d4343fe
commit a1b66995e4
134 changed files with 18302 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
#include "peconv/buffer_util.h"
#include <iostream>
//
// validate pointer:
//
bool peconv::validate_ptr(IN const void* buffer_bgn, IN SIZE_T buffer_size, IN const void* field_bgn, IN SIZE_T field_size)
{
if (buffer_bgn == nullptr || field_bgn == nullptr) {
return false;
}
BYTE* _start = (BYTE*)buffer_bgn;
BYTE* _end = _start + buffer_size;
BYTE* _field_start = (BYTE*)field_bgn;
BYTE* _field_end = (BYTE*)field_bgn + field_size;
if (_field_start < _start) {
return false;
}
if (_field_end > _end) {
return false;
}
return true;
}
//-----------------------------------------------------------------------------------
//
// alloc/free unaligned buffers:
//
//allocates a buffer that does not have to start from the beginning of the section
peconv::UNALIGNED_BUF peconv::alloc_unaligned(size_t buf_size)
{
if (!buf_size) return NULL;
UNALIGNED_BUF buf = (UNALIGNED_BUF) calloc(buf_size, sizeof(BYTE));
return buf;
}
void peconv::free_unaligned(peconv::UNALIGNED_BUF section_buffer)
{
free(section_buffer);
}
//
// alloc/free aligned buffers:
//
peconv::ALIGNED_BUF peconv::alloc_aligned(size_t buffer_size, DWORD protect, ULONGLONG desired_base)
{
if (!buffer_size) return NULL;
ALIGNED_BUF buf = (ALIGNED_BUF) VirtualAlloc((LPVOID) desired_base, buffer_size, MEM_COMMIT | MEM_RESERVE, protect);
return buf;
}
bool peconv::free_aligned(peconv::ALIGNED_BUF buffer, size_t buffer_size)
{
if (buffer == nullptr) return true;
if (!VirtualFree(buffer, 0, MEM_RELEASE)) {
#ifdef _DEBUG
std::cerr << "Releasing failed" << std::endl;
#endif
return false;
}
return true;
}
//-----------------------------------------------------------------------------------
//
// wrappers using appropriate buffer type according to the purpose:
//
// allocate a buffer for PE module:
peconv::ALIGNED_BUF peconv::alloc_pe_buffer(size_t buffer_size, DWORD protect, ULONGLONG desired_base)
{
return alloc_aligned(buffer_size, protect, desired_base);
}
// Free loaded PE module
bool peconv::free_pe_buffer(peconv::ALIGNED_BUF buffer, size_t buffer_size)
{
return peconv::free_aligned(buffer, buffer_size);
}

View File

@@ -0,0 +1,139 @@
#include "peconv/caves.h"
#include "peconv/pe_hdrs_helper.h"
#include "peconv/util.h"
using namespace peconv;
#ifdef _DEBUG
#include <iostream>
#endif
PBYTE peconv::find_ending_cave(BYTE*modulePtr, size_t moduleSize, const DWORD minimal_size, const DWORD req_charact)
{
size_t sec_count = peconv::get_sections_count(modulePtr, moduleSize);
if (sec_count == 0) return nullptr;
size_t last_sec = sec_count - 1;
PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(modulePtr, moduleSize, last_sec);
if (section_hdr == nullptr) return nullptr;
if (!(section_hdr->Characteristics & req_charact)) return nullptr;
DWORD raw_size = section_hdr->SizeOfRawData;
DWORD virtual_size = (DWORD)moduleSize - section_hdr->VirtualAddress;
if (raw_size >= virtual_size) {
#ifdef _DEBUG
std::cout << "Last section's raw_size: " << std::hex << raw_size << " >= virtual_size: " << virtual_size << std::endl;
#endif
return nullptr;
}
DWORD cave_size = virtual_size - raw_size;
if (cave_size < minimal_size) {
#ifdef _DEBUG
std::cout << "Cave is too small" << std::endl;
#endif
return nullptr;
}
PBYTE cave_ptr = modulePtr + section_hdr->VirtualAddress + section_hdr->SizeOfRawData;
if (!validate_ptr(modulePtr, moduleSize, cave_ptr, minimal_size)) {
#ifdef _DEBUG
std::cout << "Invalid cave pointer" << std::endl;
#endif
return nullptr;
}
section_hdr->SizeOfRawData += minimal_size; //book this cave
return cave_ptr;
}
PBYTE peconv::find_alignment_cave(BYTE* modulePtr, size_t moduleSize, const DWORD minimal_size, const DWORD req_charact)
{
DWORD alignment = peconv::get_sec_alignment(modulePtr, true);
if (alignment == 0) return nullptr;
size_t sec_count = peconv::get_sections_count(modulePtr, moduleSize);
for (size_t i = 0; i < sec_count; i++) {
PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(modulePtr, moduleSize, i);
if (section_hdr == nullptr) continue;
if (!(section_hdr->Characteristics & req_charact)) continue;
DWORD rem = section_hdr->SizeOfRawData % alignment;
if (rem == 0) continue;
DWORD div = (section_hdr->SizeOfRawData / alignment) + 1;
DWORD new_size = div * alignment;
DWORD cave_size = new_size - section_hdr->SizeOfRawData;
if (cave_size < minimal_size) {
#ifdef __DEBUG
std::cout << "Cave is too small" << std::endl;
#endif
continue;
}
DWORD sec_start = section_hdr->PointerToRawData;
if (sec_start == 0) continue;
DWORD sec_end = sec_start + section_hdr->SizeOfRawData;
#ifdef _DEBUG
std::cout << "section: " << std::hex << sec_start << " : " << sec_end << std::endl;
#endif
PBYTE cave_ptr = modulePtr + sec_end;
if (!validate_ptr(modulePtr, moduleSize, cave_ptr, minimal_size)) {
#ifdef _DEBUG
std::cout << "Invalid cave pointer" << std::endl;
#endif
continue;
}
section_hdr->SizeOfRawData += minimal_size; //book this cave
return cave_ptr;
}
#ifdef _DEBUG
std::cout << "Cave not found" << std::endl;
#endif
return nullptr;
}
PBYTE peconv::find_padding_cave(BYTE* modulePtr, size_t moduleSize, const size_t minimal_size, const DWORD req_charact)
{
size_t sec_count = peconv::get_sections_count(modulePtr, moduleSize);
for (size_t i = 0; i < sec_count; i++) {
PIMAGE_SECTION_HEADER section_hdr = peconv::get_section_hdr(modulePtr, moduleSize, i);
if (section_hdr == nullptr) continue;
if (!(section_hdr->Characteristics & req_charact)) continue;
if (section_hdr->SizeOfRawData < minimal_size) continue;
// we will be searching in the loaded, virtual image:
DWORD sec_start = section_hdr->VirtualAddress;
if (sec_start == 0) continue;
DWORD sec_end = sec_start + section_hdr->SizeOfRawData;
#ifdef _DEBUG
std::cout << "section: " << std::hex << sec_start << " : " << sec_end << std::endl;
#endif
//offset from the end of the section:
size_t cave_offset = section_hdr->SizeOfRawData - minimal_size;
PBYTE cave_ptr = modulePtr + sec_start + cave_offset;
if (!validate_ptr(modulePtr, moduleSize, cave_ptr, minimal_size)) {
#ifdef _DEBUG
std::cout << "Invalid cave pointer" << std::endl;
#endif
continue;
}
bool found = false;
if (is_padding(cave_ptr, minimal_size, 0)) {
found = true;
}
//if the section is code, check also code padding:
if (section_hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
if (is_padding(cave_ptr, minimal_size, 0xCC)) {
found = true;
}
}
if (found) {
return cave_ptr;
}
}
#ifdef _DEBUG
std::cout << "Cave not found" << std::endl;
#endif
return nullptr;
}

View File

@@ -0,0 +1,158 @@
#include "peconv/delayed_imports_loader.h"
#include "peconv/imports_loader.h"
#include <iostream>
IMAGE_DELAYLOAD_DESCRIPTOR* peconv::get_delayed_imps(IN const BYTE* modulePtr, IN const size_t moduleSize, OUT size_t &dir_size)
{
dir_size = 0;
IMAGE_DATA_DIRECTORY *d_imps_dir = peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
if (!d_imps_dir) {
return nullptr;
}
BYTE* dimps_table = (BYTE*)((ULONGLONG) modulePtr + d_imps_dir->VirtualAddress);
const size_t min_size = sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
if (d_imps_dir->Size < min_size) {
return nullptr;
}
if (!peconv::validate_ptr((LPVOID)modulePtr, moduleSize, dimps_table, min_size)) {
return nullptr;
}
dir_size = d_imps_dir->Size;
return reinterpret_cast<IMAGE_DELAYLOAD_DESCRIPTOR*> (dimps_table);
}
template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
bool parse_delayed_desc(BYTE* modulePtr, const size_t moduleSize,
const ULONGLONG img_base,
LPSTR lib_name,
const T_FIELD ordinal_flag,
IMAGE_DELAYLOAD_DESCRIPTOR *desc,
peconv::t_function_resolver* func_resolver
)
{
ULONGLONG iat_addr = desc->ImportAddressTableRVA;
if (iat_addr > img_base) iat_addr -= img_base; // it may be either RVA or VA
ULONGLONG thunk_addr = desc->ImportNameTableRVA;
if (thunk_addr > img_base) thunk_addr -= img_base; // it may be either RVA or VA
T_FIELD* record_va = (T_FIELD*)((ULONGLONG)modulePtr + iat_addr);
T_IMAGE_THUNK_DATA* thunk_va = (T_IMAGE_THUNK_DATA*)((ULONGLONG)modulePtr + thunk_addr);
for (; *record_va != NULL && thunk_va != NULL; record_va++, thunk_va++) {
if (!peconv::validate_ptr(modulePtr, moduleSize, record_va, sizeof(T_FIELD))) {
return false;
}
if (!peconv::validate_ptr(modulePtr, moduleSize, thunk_va, sizeof(T_FIELD))) {
return false;
}
T_FIELD iat_va = *record_va;
ULONGLONG iat_rva = (ULONGLONG)iat_va;
if (iat_va > img_base) iat_rva -= img_base; // it may be either RVA or VA
#ifdef _DEBUG
std::cout << std::hex << iat_rva << " : ";
#endif
T_FIELD* iat_record_ptr = (T_FIELD*)((ULONGLONG)modulePtr + iat_rva);
if (!peconv::validate_ptr(modulePtr, moduleSize, iat_record_ptr, sizeof(T_FIELD))) {
return false;
}
FARPROC hProc = nullptr;
if (thunk_va->u1.Ordinal & ordinal_flag) {
T_FIELD raw_ordinal = thunk_va->u1.Ordinal & (~ordinal_flag);
#ifdef _DEBUG
std::cout << std::hex << "ord: " << raw_ordinal << " ";
#endif
hProc = func_resolver->resolve_func(lib_name, MAKEINTRESOURCEA(raw_ordinal));
}
else {
ULONGLONG name_rva = thunk_va->u1.AddressOfData;
if (name_rva > img_base) {
name_rva -= img_base;
}
PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + name_rva);
LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
if (!peconv::is_valid_import_name(modulePtr, moduleSize, func_name)) {
continue;
}
#ifdef _DEBUG
std::cout << func_name << " ";
#endif
hProc = func_resolver->resolve_func(lib_name, func_name);
}
if (hProc) {
//rather than loading it via proxy function, we just overwrite the thunk like normal IAT:
*record_va = (T_FIELD) hProc;
#ifdef _DEBUG
std::cout << "[OK]\n";
#endif
}
else {
#ifdef _DEBUG
std::cout << "[NOPE]\n";
#endif
}
}
return true;
}
bool peconv::load_delayed_imports(BYTE* modulePtr, ULONGLONG moduleBase, t_function_resolver* func_resolver)
{
const bool is_64bit = peconv::is64bit(modulePtr);
bool is_loader64 = false;
#ifdef _WIN64
is_loader64 = true;
#endif
if (is_64bit != is_loader64) {
std::cerr << "[ERROR] Loader/Payload bitness mismatch.\n";
return false;
}
const size_t module_size = peconv::get_image_size(modulePtr);
default_func_resolver default_res;
if (!func_resolver) {
func_resolver = (t_function_resolver*)&default_res;
}
size_t table_size = 0;
IMAGE_DELAYLOAD_DESCRIPTOR *first_desc = get_delayed_imps(modulePtr, module_size, table_size);
if (!first_desc) {
return false;
}
#ifdef _DEBUG
std::cout << "OK, table_size = " << table_size << std::endl;
#endif
size_t max_count = table_size / sizeof(IMAGE_DELAYLOAD_DESCRIPTOR);
for (size_t i = 0; i < max_count; i++) {
IMAGE_DELAYLOAD_DESCRIPTOR *desc = &first_desc[i];
if (!validate_ptr(modulePtr, module_size, desc, sizeof(IMAGE_DELAYLOAD_DESCRIPTOR))) break;
if (desc->DllNameRVA == NULL) {
break;
}
ULONGLONG dll_name_rva = desc->DllNameRVA;
if (dll_name_rva > moduleBase) {
dll_name_rva -= moduleBase;
}
char* dll_name = (char*)((ULONGLONG) modulePtr + dll_name_rva);
if (!validate_ptr(modulePtr, module_size, dll_name, sizeof(char))) continue;
#ifdef _DEBUG
std::cout << dll_name << std::endl;
#endif
if (is_64bit) {
#ifdef _WIN64
parse_delayed_desc<ULONGLONG,IMAGE_THUNK_DATA64>(modulePtr, module_size, moduleBase, dll_name, IMAGE_ORDINAL_FLAG64, desc, func_resolver);
#else
return false;
#endif
}
else {
#ifndef _WIN64
parse_delayed_desc<DWORD, IMAGE_THUNK_DATA32>(modulePtr, module_size, moduleBase, dll_name, IMAGE_ORDINAL_FLAG32, desc, func_resolver);
#else
return false;
#endif
}
}
return true;
}

View File

@@ -0,0 +1,176 @@
#include "peconv/exported_func.h"
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <iostream>
using namespace peconv;
std::string peconv::get_dll_shortname(const std::string& str)
{
std::size_t len = str.length();
std::size_t found = str.find_last_of("/\\");
std::size_t ext = str.find_last_of('.');
if (ext >= len) return "";
std::string name = str.substr(found+1, ext - (found+1));
std::transform(name.begin(), name.end(), name.begin(), tolower);
return name;
}
size_t peconv::forwarder_name_len(BYTE* fPtr)
{
// names can be also mangled, i.e. MSVCRT.??0__non_rtti_object@std@@QAE@ABV01@@Z
bool has_dot = false;
size_t len = 0;
while ((*fPtr >= 'a' && *fPtr <= 'z')
|| (*fPtr >= 'A' && *fPtr <= 'Z')
|| (*fPtr >= '0' && *fPtr <= '9')
|| (*fPtr == '.')
|| (*fPtr == '_')
|| (*fPtr == '#')
|| (*fPtr == '@')
|| (*fPtr == '?')
|| (*fPtr == '-'))
{
if (*fPtr == '.') has_dot = true;
len++;
fPtr++;
}
if (*fPtr == '\0') {
if (!has_dot) {
return 0; //this is not a valid forwarder
}
return len;
}
return 0;
}
std::string peconv::get_func_name(const std::string& str)
{
std::size_t len = str.length();
std::size_t ext = str.find_last_of(".");
if (ext >= len) return "";
std::string name = str.substr(ext+1, len - (ext+1));
return name;
}
std::string peconv::ordinal_to_string(DWORD func_ordinal)
{
std::stringstream stream;
stream << "#";
stream << std::dec << func_ordinal;
return stream.str();
}
bool peconv::is_ordinal_string(const std::string& func_name_str)
{
if (func_name_str.length() < 2) return false;
return (func_name_str[0] == '#');
}
DWORD peconv::ordinal_string_to_val(const std::string& func_name_str)
{
if (!is_ordinal_string(func_name_str)) return 0;
const char* func_name = func_name_str.c_str();
return atoi(func_name + 1);
}
std::string peconv::format_dll_func(const std::string& str)
{
std::string dllName = get_dll_shortname(str);
std::string funcName = get_func_name(str);
if (dllName.length() == 0 || funcName.length() == 0) {
return "";
}
std::transform(dllName.begin(), dllName.end(), dllName.begin(), tolower);
return dllName + "." + funcName;
}
ExportedFunc::ExportedFunc(std::string libName, std::string funcName, DWORD funcOrdinal)
{
this->libName = ExportedFunc::formatName(libName);
this->funcName = funcName;
this->funcOrdinal = funcOrdinal;
this->isByOrdinal = false;
}
ExportedFunc::ExportedFunc(std::string libName, DWORD funcOrdinal)
{
this->libName = ExportedFunc::formatName(libName);
this->funcOrdinal = funcOrdinal;
this->isByOrdinal = true;
}
ExportedFunc::ExportedFunc(const ExportedFunc& other)
{
this->libName = other.libName;
this->funcName = other.funcName;
this->funcOrdinal = other.funcOrdinal;
this->isByOrdinal = other.isByOrdinal;
}
ExportedFunc::ExportedFunc(const std::string &forwarderName)
{
this->libName = get_dll_shortname(forwarderName);
std::string func_name_str = get_func_name(forwarderName);
if (func_name_str.length() < 2) {
this->funcOrdinal = -1;
this->funcName = "";
this->isByOrdinal = false;
#ifdef _DEBUG
std::cerr << "Invalid function data" << std::endl;
#endif
return;
}
if (is_ordinal_string(func_name_str)) {
// it is an ordinal in a string form, i.e.: "COMBASE.#110"
this->funcOrdinal = peconv::ordinal_string_to_val(func_name_str);
this->isByOrdinal = true;
this->funcName = "";
//std::cout << "[O] Adding forwarded func: " << forwarderName << " parsed: " << this->toString() << std::endl;
} else {
this->funcName = func_name_str;
this->isByOrdinal = false;
this->funcOrdinal = 0;
//std::cout << "[N] Adding forwarded func:" << this->toString() << std::endl;
}
}
std::string ExportedFunc::formatName(std::string name)
{
if (name.length() == 0 || name.length() == 0) {
return "";
}
std::transform(name.begin(), name.end(), name.begin(), tolower);
return name;
}
std::string ExportedFunc::toString() const
{
if (!isValid()) {
return "[Invalid func]";
}
std::stringstream stream;
stream << this->libName;
stream << ".";
if (!this->isByOrdinal) {
stream << this->funcName;
stream << " ";
}
stream << ordinal_to_string(this->funcOrdinal);
return stream.str();
}
std::string ExportedFunc::nameToString() const
{
if (!isValid()) {
return "";
}
if (this->isByOrdinal) {
return ordinal_to_string(this->funcOrdinal);
}
return this->funcName;
}

View File

@@ -0,0 +1,194 @@
#include "peconv/exports_lookup.h"
#include "peconv/util.h"
#include <iostream>
/*
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
*/
#ifndef TO_LOWERCASE
#define TO_LOWERCASE(c1) c1 = (c1 <= 'Z' && c1 >= 'A') ? c1 = (c1 - 'A') + 'a': c1;
#endif
bool is_wanted_func(LPSTR curr_name, LPSTR wanted_name)
{
if (curr_name == NULL || wanted_name == NULL) return false;
size_t wanted_name_len = strlen(wanted_name);
size_t curr_name_len = strlen(curr_name);
if (curr_name_len != wanted_name_len) return false;
for (size_t i = 0; i < wanted_name_len; i++) {
char c1 = curr_name[i];
char c2 = wanted_name[i];
TO_LOWERCASE(c1);
TO_LOWERCASE(c2);
if (c1 != c2) return false;
}
return true;
}
bool is_ordinal(IMAGE_EXPORT_DIRECTORY *exp, LPSTR func_name)
{
ULONGLONG base = exp->Base;
ULONGLONG max_ord = base + exp->NumberOfFunctions;
ULONGLONG name_ptr_val = (ULONGLONG)func_name;
if (name_ptr_val >= base && name_ptr_val < max_ord) {
return true;
}
return false;
}
FARPROC get_export_by_ord(PVOID modulePtr, IMAGE_EXPORT_DIRECTORY* exp, DWORD wanted_ordinal)
{
SIZE_T functCount = exp->NumberOfFunctions;
DWORD funcsListRVA = exp->AddressOfFunctions;
DWORD ordBase = exp->Base;
//go through names:
for (DWORD i = 0; i < functCount; i++) {
DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
BYTE* fPtr = (BYTE*) modulePtr + (*funcRVA); //pointer to the function
DWORD ordinal = ordBase + i;
if (ordinal == wanted_ordinal) {
if (peconv::forwarder_name_len(fPtr) > 1) {
std::cerr << "[!] Forwarded function: ["<< wanted_ordinal << " -> "<< fPtr << "] cannot be resolved!" << std::endl;
return NULL; // this function is forwarded, cannot be resolved
}
return (FARPROC) fPtr; //return the pointer to the found function
}
}
return NULL;
}
size_t peconv::get_exported_names(PVOID modulePtr, std::vector<std::string> &names_list)
{
IMAGE_EXPORT_DIRECTORY* exp = peconv::get_export_directory((HMODULE) modulePtr);
if (exp == 0) return 0;
SIZE_T namesCount = exp->NumberOfNames;
DWORD funcNamesListRVA = exp->AddressOfNames;
//go through names:
SIZE_T i = 0;
for (i = 0; i < namesCount; i++) {
DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
LPSTR name = (LPSTR)(*nameRVA + (BYTE*) modulePtr);
if (peconv::is_bad_read_ptr(name, 1)) break; // this shoudld not happen. maybe the PE file is corrupt?
names_list.push_back(name);
}
return i;
}
//WARNING: doesn't work for the forwarded functions.
FARPROC peconv::get_exported_func(PVOID modulePtr, LPSTR wanted_name)
{
IMAGE_EXPORT_DIRECTORY* exp = peconv::get_export_directory((HMODULE) modulePtr);
if (exp == NULL) return NULL;
SIZE_T namesCount = exp->NumberOfNames;
DWORD funcsListRVA = exp->AddressOfFunctions;
DWORD funcNamesListRVA = exp->AddressOfNames;
DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
if (is_ordinal(exp, wanted_name)) {
#ifdef _DEBUG
std::cerr << "[*] Getting function by ordinal" << std::endl;
#endif
const DWORD ordinal = MASK_TO_DWORD((ULONG_PTR)wanted_name);
return get_export_by_ord(modulePtr, exp, ordinal);
}
if (peconv::is_bad_read_ptr(wanted_name, 1)) {
std::cerr << "[-] Invalid pointer to the name" << std::endl;
return NULL;
}
//go through names:
for (SIZE_T i = 0; i < namesCount; i++) {
DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*) modulePtr + i * sizeof(WORD));
DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + (*nameIndex) * sizeof(DWORD));
LPSTR name = (LPSTR)(*nameRVA + (BYTE*) modulePtr);
BYTE* fPtr = (BYTE*) modulePtr + (*funcRVA); //pointer to the function
if (!is_wanted_func(name, wanted_name)) {
continue; //this is not the function we are looking for
}
if (forwarder_name_len(fPtr) > 1) {
std::cerr << "[!] Forwarded function: ["<< name << " -> "<< fPtr << "] cannot be resolved!" << std::endl;
return NULL; // this function is forwarded, cannot be resolved
}
return (FARPROC) fPtr; //return the pointer to the found function
}
//function not found
std::cerr << "Function not found!" << std::endl;
return NULL;
}
FARPROC peconv::export_based_resolver::resolve_func(LPSTR lib_name, LPSTR func_name)
{
HMODULE libBasePtr = LoadLibraryA(lib_name);
if (libBasePtr == NULL) {
std::cerr << "Could not load the library!" << std::endl;
return NULL;
}
FARPROC hProc = get_exported_func(libBasePtr, func_name);
if (hProc == NULL) {
#ifdef _DEBUG
if (!peconv::is_bad_read_ptr(func_name, 1)) {
std::cerr << "[!] Cound not get the function: "<< func_name <<" from exports!" << std::endl;
} else {
std::cerr << "[!] Cound not get the function: "<< MASK_TO_DWORD((ULONG_PTR)func_name) <<" from exports!" << std::endl;
}
std::cerr << "[!] Falling back to the default resolver..." <<std::endl;
#endif
hProc = default_func_resolver::resolve_func(lib_name, func_name);
if (hProc == NULL) {
std::cerr << "[-] Loading function from " << lib_name << " failed!" << std::endl;
}
}
#ifdef _DEBUG
FARPROC defaultProc = default_func_resolver::resolve_func(lib_name, func_name);
if (hProc != defaultProc) {
std::cerr << "[-] Loaded proc is not matching the default one!" << std::endl;
}
#endif
return hProc;
}
LPSTR peconv::read_dll_name(HMODULE modulePtr)
{
IMAGE_EXPORT_DIRECTORY* exp = get_export_directory(modulePtr);
if (exp == NULL) {
return NULL;
}
LPSTR module_name = (char*)((ULONGLONG)modulePtr + exp->Name);
if (peconv::is_bad_read_ptr(module_name, 1)) {
return NULL;
}
size_t len = peconv::forwarder_name_len((BYTE*) module_name);
if (len > 1) {
return module_name;
}
return NULL;
}

View File

@@ -0,0 +1,265 @@
#include "peconv/exports_mapper.h"
#include <algorithm>
#include <iostream>
using namespace peconv;
void ExportsMapper::print_va_to_func(std::stringstream &stream) const
{
std::map<ULONGLONG, std::set<ExportedFunc>>::const_iterator itr;
for (itr = va_to_func.begin(); itr != va_to_func.end(); ++itr) {
stream << std::hex << itr->first << " :\n";
std::set<ExportedFunc>::const_iterator itr2;
const std::set<ExportedFunc> &funcs = itr->second;
for (itr2 = funcs.begin(); itr2 != funcs.end(); ++itr2) {
stream << "\t" << itr2->toString() << "\n";
}
}
}
void ExportsMapper::print_func_to_va(std::stringstream &stream) const
{
std::map<ExportedFunc, ULONGLONG>::const_iterator itr;
for (itr = func_to_va.begin(); itr != func_to_va.end(); ++itr) {
stream << itr->first.toString() << " : "
<< std::hex << itr->second << "\n";
}
}
ULONGLONG rebase_va(ULONGLONG va, ULONGLONG currBase, ULONGLONG targetBase)
{
if (currBase == targetBase) {
return va;
}
ULONGLONG rva = va - (ULONGLONG) currBase;
return rva + targetBase;
}
size_t ExportsMapper::make_ord_lookup_tables(
PVOID modulePtr,
size_t moduleSize,
std::map<PDWORD, DWORD> &va_to_ord
)
{
IMAGE_EXPORT_DIRECTORY* exp = peconv::get_export_directory((HMODULE) modulePtr);
if (exp == NULL) return 0;
SIZE_T functCount = exp->NumberOfFunctions;
DWORD funcsListRVA = exp->AddressOfFunctions;
DWORD ordBase = exp->Base;
//go through names:
for (DWORD i = 0; i < functCount; i++) {
DWORD* recordRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
if (*recordRVA == 0) {
#ifdef _DEBUG
std::cout << ">>> Skipping 0 function address at RVA:" << std::hex << (BYTE*)recordRVA - (BYTE*)modulePtr<< "(ord)\n";
#endif
//skip if the function RVA is 0 (empty export)
continue;
}
if (!peconv::validate_ptr(modulePtr, moduleSize, recordRVA, sizeof(DWORD))) {
break;
}
DWORD ordinal = ordBase + i;
va_to_ord[recordRVA] = ordinal;
}
return functCount;
}
size_t ExportsMapper::resolve_forwarders(const ULONGLONG va, ExportedFunc &currFunc)
{
size_t resolved = 0;
//resolve forwarders of this function (if any):
std::map<ExportedFunc, std::set<ExportedFunc>>::iterator fItr = forwarders_lookup.find(currFunc);
if (fItr != forwarders_lookup.end()) {
//printf("[+] Forwarders (%d):\n", fItr->second.size());
std::set<ExportedFunc>::iterator sItr;
for (sItr = fItr->second.begin(); sItr != fItr->second.end(); ++sItr) {
//printf("-> %s\n", sItr->c_str());
associateVaAndFunc(va, *sItr);
resolved++;
}
}
return resolved;
}
bool ExportsMapper::add_forwarded(ExportedFunc &currFunc, DWORD callRVA, PBYTE modulePtr, size_t moduleSize)
{
PBYTE fPtr = modulePtr + callRVA;
if (!peconv::validate_ptr(modulePtr, moduleSize, fPtr, 1)) {
return false;
}
if (peconv::forwarder_name_len(fPtr) < 1) {
return false; //not forwarded
}
std::string forwardedFunc = format_dll_func((char*)fPtr);
if (forwardedFunc.length() == 0) {
return false; //not forwarded
}
ExportedFunc forwarder(forwardedFunc);
if (!forwarder.isValid()) {
#ifdef _DEBUG
std::cerr << "Skipped invalid forwarder" << std::endl;
#endif
return false;
}
forwarders_lookup[forwarder].insert(currFunc);
if (func_to_va[forwarder] != 0) {
ULONGLONG va = func_to_va[forwarder];
associateVaAndFunc(va, currFunc);
}
return true;
}
DWORD get_ordinal(PDWORD recordPtr, std::map<PDWORD, DWORD> &va_to_ord)
{
std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.find(recordPtr);
if (ord_itr == va_to_ord.end()) {
//ordinal not found
return -1;
}
DWORD ordinal = ord_itr->second;
va_to_ord.erase(ord_itr);
return ordinal;
}
bool ExportsMapper::add_to_maps(ULONGLONG va, ExportedFunc &currFunc)
{
associateVaAndFunc(va, currFunc);
resolve_forwarders(va, currFunc);
return true;
}
bool is_valid_export_table(IMAGE_EXPORT_DIRECTORY* exp, HMODULE modulePtr, const size_t module_size)
{
if (exp == nullptr) return false;
const SIZE_T namesCount = exp->NumberOfNames;
const SIZE_T funcCount = exp->NumberOfFunctions;
const DWORD funcsListRVA = exp->AddressOfFunctions;
const DWORD funcNamesListRVA = exp->AddressOfNames;
const DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
for (DWORD i = 0; i < funcCount; i++) {
DWORD* recordRVA = (DWORD*)(funcsListRVA + (BYTE*)modulePtr + i * sizeof(DWORD));
if (!peconv::validate_ptr(modulePtr, module_size, recordRVA, sizeof(DWORD))) {
return false;
}
}
for (SIZE_T i = 0; i < namesCount; i++) {
DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*)modulePtr + i * sizeof(DWORD));
WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*)modulePtr + i * sizeof(WORD));
if ((!peconv::validate_ptr(modulePtr, module_size, nameRVA, sizeof(DWORD)))
|| (!peconv::validate_ptr(modulePtr, module_size, nameIndex, sizeof(WORD))))
{
return false;
}
DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*)modulePtr + (*nameIndex) * sizeof(DWORD));
if (!peconv::validate_ptr(modulePtr, module_size, funcRVA, sizeof(DWORD)))
{
return false;
}
}
return true;
}
ExportsMapper::ADD_FUNC_RES ExportsMapper::add_function_to_lookup(HMODULE modulePtr, ULONGLONG moduleBase, size_t moduleSize, ExportedFunc &currFunc, DWORD callRVA)
{
if (add_forwarded(currFunc, callRVA, (BYTE*)modulePtr, moduleSize)) {
#ifdef _DEBUG
char* fPtr = (char*)modulePtr + callRVA;
std::cout << "FWD " << currFunc.toString() << " -> " << fPtr << "\n";
#endif
return ExportsMapper::RES_FORWARDED;
}
ULONGLONG callVa = callRVA + moduleBase;
if (!peconv::validate_ptr((BYTE*)moduleBase, moduleSize, (BYTE*)callVa, sizeof(ULONGLONG))) {
// this may happen when the function was forwarded and it is already filled
#ifdef _DEBUG
std::cout << "Validation failed: " << currFunc.toString() << "\n";
#endif
return ExportsMapper::RES_INVALID;
}
//not forwarded, simple case:
add_to_maps(callVa, currFunc);
return ExportsMapper::RES_MAPPED;
}
size_t ExportsMapper::add_to_lookup(std::string moduleName, HMODULE modulePtr, ULONGLONG moduleBase)
{
IMAGE_EXPORT_DIRECTORY* exp = get_export_directory(modulePtr);
if (exp == NULL) {
return 0;
}
size_t module_size = peconv::get_image_size(reinterpret_cast<const PBYTE>(modulePtr));
if (!is_valid_export_table(exp, modulePtr, module_size)) {
return 0;
}
std::string dllName = get_dll_shortname(moduleName);
this->dll_shortname_to_path[dllName] = moduleName;
std::map<PDWORD, DWORD> va_to_ord;
size_t functCount = make_ord_lookup_tables(modulePtr, module_size, va_to_ord);
//go through names:
size_t forwarded_ctr = 0;
SIZE_T namesCount = exp->NumberOfNames;
DWORD funcsListRVA = exp->AddressOfFunctions;
DWORD funcNamesListRVA = exp->AddressOfNames;
DWORD namesOrdsListRVA = exp->AddressOfNameOrdinals;
size_t mapped_ctr = 0;
for (SIZE_T i = 0; i < namesCount; i++) {
DWORD* nameRVA = (DWORD*)(funcNamesListRVA + (BYTE*) modulePtr + i * sizeof(DWORD));
WORD* nameIndex = (WORD*)(namesOrdsListRVA + (BYTE*) modulePtr + i * sizeof(WORD));
DWORD* funcRVA = (DWORD*)(funcsListRVA + (BYTE*) modulePtr + (*nameIndex) * sizeof(DWORD));
if (*funcRVA == 0) {
#ifdef _DEBUG
std::cout << ">>> Skipping 0 function address at RVA:" << std::hex << (BYTE*)funcRVA - (BYTE*)modulePtr << "(name)\n";
#endif
//skip if the function RVA is 0 (empty export)
continue;
}
LPSTR name = (LPSTR)(*nameRVA + (BYTE*) modulePtr);
if (!peconv::validate_ptr(modulePtr, module_size, name, sizeof(char))) break;
DWORD funcOrd = get_ordinal(funcRVA, va_to_ord);
DWORD callRVA = *funcRVA;
ExportedFunc currFunc(dllName, name, funcOrd);
int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
}
//go through unnamed functions exported by ordinals:
std::map<PDWORD, DWORD>::iterator ord_itr = va_to_ord.begin();
for (;ord_itr != va_to_ord.end(); ++ord_itr) {
DWORD* funcRVA = ord_itr->first;
DWORD callRVA = *funcRVA;
ExportedFunc currFunc(dllName, ord_itr->second);
int res = add_function_to_lookup(modulePtr, moduleBase, module_size, currFunc, callRVA);
if (res == ExportsMapper::RES_FORWARDED) forwarded_ctr++;
if (res == ExportsMapper::RES_MAPPED) mapped_ctr++;
}
#ifdef _DEBUG
std::cout << "Finished exports parsing, mapped: "<< mapped_ctr << " forwarded: " << forwarded_ctr << std::endl;
#endif
return mapped_ctr;
}

View File

@@ -0,0 +1,148 @@
#include "peconv/file_util.h"
#include "peconv/buffer_util.h"
#include "peconv/util.h"
#include <fstream>
#ifdef _DEBUG
#include <iostream>
#endif
//load file content using MapViewOfFile
peconv::ALIGNED_BUF peconv::load_file(IN const char *filename, OUT size_t &read_size)
{
HANDLE file = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(file == INVALID_HANDLE_VALUE) {
#ifdef _DEBUG
std::cerr << "Could not open file!" << std::endl;
#endif
return nullptr;
}
HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0);
if (!mapping) {
#ifdef _DEBUG
std::cerr << "Could not create mapping!" << std::endl;
#endif
CloseHandle(file);
return nullptr;
}
BYTE *dllRawData = (BYTE*) MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
if (!dllRawData) {
#ifdef _DEBUG
std::cerr << "Could not map view of file" << std::endl;
#endif
CloseHandle(mapping);
CloseHandle(file);
return nullptr;
}
size_t r_size = GetFileSize(file, 0);
if (read_size != 0 && read_size <= r_size) {
r_size = read_size;
}
if (peconv::is_bad_read_ptr(dllRawData, r_size)) {
std::cerr << "[-] Mapping of " << filename << " is invalid!" << std::endl;
UnmapViewOfFile(dllRawData);
CloseHandle(mapping);
CloseHandle(file);
return nullptr;
}
peconv::ALIGNED_BUF localCopyAddress = peconv::alloc_aligned(r_size, PAGE_READWRITE);
if (localCopyAddress != nullptr) {
memcpy(localCopyAddress, dllRawData, r_size);
read_size = r_size;
} else {
read_size = 0;
#ifdef _DEBUG
std::cerr << "Could not allocate memory in the current process" << std::endl;
#endif
}
UnmapViewOfFile(dllRawData);
CloseHandle(mapping);
CloseHandle(file);
return localCopyAddress;
}
//load file content using ReadFile
peconv::ALIGNED_BUF peconv::read_from_file(IN const char *in_path, IN OUT size_t &read_size)
{
HANDLE file = CreateFileA(in_path, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE) {
#ifdef _DEBUG
std::cerr << "Cannot open the file for reading!" << std::endl;
#endif
return nullptr;
}
size_t r_size = static_cast<size_t>(GetFileSize(file, 0));
if (read_size != 0 && read_size <= r_size) {
r_size = read_size;
}
PBYTE buffer = peconv::alloc_pe_buffer(r_size, PAGE_READWRITE);
if (buffer == nullptr) {
#ifdef _DEBUG
std::cerr << "Allocation has failed!" << std::endl;
#endif
return nullptr;
}
DWORD out_size = 0;
if (!ReadFile(file, buffer, r_size, &out_size, nullptr)) {
#ifdef _DEBUG
std::cerr << "Reading failed!" << std::endl;
#endif
peconv::free_pe_buffer(buffer, r_size);
buffer = nullptr;
read_size = 0;
} else {
read_size = r_size;
}
CloseHandle(file);
return buffer;
}
//save the given buffer into a file
bool peconv::dump_to_file(IN const char *out_path, IN PBYTE dump_data, IN size_t dump_size)
{
if (!out_path || !dump_data || !dump_size) return false;
HANDLE file = CreateFileA(out_path, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE) {
#ifdef _DEBUG
std::cerr << "Cannot open the file for writing!" << std::endl;
#endif
return false;
}
DWORD written_size = 0;
bool is_dumped = false;
if (WriteFile(file, dump_data, (DWORD) dump_size, &written_size, nullptr)) {
is_dumped = true;
}
#ifdef _DEBUG
else {
std::cerr << "Failed to write to the file : " << out_path << std::endl;
}
#endif
CloseHandle(file);
return is_dumped;
}
//free the buffer allocated by load_file/read_from_file
void peconv::free_file(IN peconv::ALIGNED_BUF buffer)
{
peconv::free_aligned(buffer);
}
std::string peconv::get_file_name(IN const std::string str)
{
size_t found = str.find_last_of("/\\");
if (found == std::string::npos) {
return str;
}
return str.substr(found + 1);
}
std::string peconv::get_directory_name(IN const std::string str)
{
size_t found = str.find_last_of("/\\");
if (found == std::string::npos) {
return "";
}
return str.substr(0, found);
}

View File

@@ -0,0 +1,125 @@
#include <peconv/find_base.h>
#include <peconv/pe_hdrs_helper.h>
#include <peconv/relocate.h>
#include <set>
#include <map>
#include <iostream>
namespace peconv {
class CollectCodeRelocs : public RelocBlockCallback
{
public:
CollectCodeRelocs(BYTE *pe_buffer, size_t buffer_size, IN bool _is64bit, OUT std::set<ULONGLONG> &_relocs)
: RelocBlockCallback(_is64bit), relocs(_relocs),
peBuffer(pe_buffer), bufferSize(buffer_size)
{
codeSec = getCodeSection(peBuffer, bufferSize);
}
virtual bool processRelocField(ULONG_PTR relocField)
{
if (!codeSec) return false;
ULONGLONG reloc_addr = (relocField - (ULONGLONG)peBuffer);
const bool is_in_code = (reloc_addr >= codeSec->VirtualAddress) && (reloc_addr < codeSec->Misc.VirtualSize);
if (!is64bit && !is_in_code) {
// in case of 32 bit PEs process only the relocations form the code section
return true;
}
ULONGLONG rva = 0;
if (is64bit) {
ULONGLONG* relocateAddr = (ULONGLONG*)((ULONG_PTR)relocField);
rva = (*relocateAddr);
//std::cout << std::hex << (relocField - (ULONGLONG)peBuffer) << " : " << rva << std::endl;
}
else {
DWORD* relocateAddr = (DWORD*)((ULONG_PTR)relocField);
rva = ULONGLONG(*relocateAddr);
//std::cout << std::hex << (relocField - (ULONGLONG)peBuffer) << " : " << rva << std::endl;
}
relocs.insert(rva);
return true;
}
static PIMAGE_SECTION_HEADER getCodeSection(BYTE *peBuffer, size_t bufferSize)
{
size_t sec_count = peconv::get_sections_count(peBuffer, bufferSize);
for (size_t i = 0; i < sec_count; i++) {
PIMAGE_SECTION_HEADER hdr = peconv::get_section_hdr(peBuffer, bufferSize, i);
if (!hdr) break;
if (hdr->VirtualAddress == 0 || hdr->SizeOfRawData == 0) {
continue;
}
if (hdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
return hdr;
}
}
return nullptr;
}
protected:
std::set<ULONGLONG> &relocs;
PIMAGE_SECTION_HEADER codeSec;
BYTE *peBuffer;
size_t bufferSize;
};
}
ULONGLONG peconv::find_base_candidate(IN BYTE* modulePtr, IN size_t moduleSize)
{
if (moduleSize == 0) {
moduleSize = peconv::get_image_size((const BYTE*)modulePtr);
}
if (moduleSize == 0) return 0;
bool is64 = peconv::is64bit(modulePtr);
std::set<ULONGLONG> relocs;
peconv::CollectCodeRelocs callback(modulePtr, moduleSize, is64, relocs);
if (!peconv::process_relocation_table(modulePtr, moduleSize, &callback)) {
return 0;
}
if (relocs.size() == 0) {
return 0;
}
PIMAGE_SECTION_HEADER hdr = peconv::CollectCodeRelocs::getCodeSection(modulePtr, moduleSize);
if (!hdr) {
return 0;
}
const ULONGLONG mask = ~ULONGLONG(0xFFFF);
std::map<ULONGLONG, size_t>base_candidates;
std::set<ULONGLONG>::iterator itr = relocs.begin();
for (itr = relocs.begin(); itr != relocs.end(); ++itr) {
const ULONGLONG guessed_base = (*itr) & mask;
std::map<ULONGLONG, size_t>::iterator found = base_candidates.find(guessed_base);
if (found == base_candidates.end()) {
base_candidates[guessed_base] = 0;
}
base_candidates[guessed_base]++;
}
ULONGLONG most_freqent = 0;
size_t max_freq = 0;
std::map<ULONGLONG, size_t>::iterator mapItr;
for (mapItr = base_candidates.begin(); mapItr != base_candidates.end(); ++mapItr) {
if (mapItr->second >= max_freq) {
most_freqent = mapItr->first;
max_freq = mapItr->second;
}
}
for (itr = relocs.begin(); itr != relocs.end(); ++itr) {
ULONGLONG first = *itr;
ULONGLONG first_base = first & mask;
if (first_base > most_freqent) {
break;
}
ULONGLONG delta = most_freqent - first_base;
if (delta < moduleSize) {
return first_base;
}
}
return 0;
}

View File

@@ -0,0 +1,137 @@
#include "fix_dot_net_ep.h"
#include <peconv.h>
#include <string>
#include <map>
class ListImportNames : public peconv::ImportThunksCallback
{
public:
ListImportNames(BYTE* _modulePtr, size_t _moduleSize, std::map<std::string, DWORD> &name_to_addr)
: ImportThunksCallback(_modulePtr, _moduleSize), nameToAddr(name_to_addr)
{
}
virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
{
if (this->is64b) {
IMAGE_THUNK_DATA64* desc = reinterpret_cast<IMAGE_THUNK_DATA64*>(origFirstThunkPtr);
ULONGLONG* call_via = reinterpret_cast<ULONGLONG*>(firstThunkPtr);
return processThunks_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64);
}
IMAGE_THUNK_DATA32* desc = reinterpret_cast<IMAGE_THUNK_DATA32*>(origFirstThunkPtr);
DWORD* call_via = reinterpret_cast<DWORD*>(firstThunkPtr);
return processThunks_tpl<DWORD, IMAGE_THUNK_DATA32>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32);
}
protected:
template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag)
{
DWORD call_via_rva = static_cast<DWORD>((ULONG_PTR)call_via - (ULONG_PTR)this->modulePtr);
#ifdef _DEBUG
std::cout << "via RVA: " << std::hex << call_via_rva << " : ";
#endif
bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
if (!is_by_ord) {
PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
#ifdef _DEBUG
std::cout << "name: " << func_name << std::endl;
#endif
nameToAddr[func_name] = call_via_rva;
}
return true;
}
std::map<std::string, DWORD> &nameToAddr;
};
DWORD find_corexemain(BYTE *buf, size_t buf_size)
{
std::map<std::string, DWORD> name_to_addr;
ListImportNames callback(buf, buf_size, name_to_addr);
if (!peconv::process_import_table(buf, buf_size, &callback)) return 0;
std::map<std::string, DWORD>::iterator found = name_to_addr.find("_CorExeMain");
if (found != name_to_addr.end()) return found->second;
found = name_to_addr.find("_CorDllMain");
if (found != name_to_addr.end()) return found->second;
return 0;
}
BYTE* search_jump(BYTE *buf, size_t buf_size, const DWORD cor_exe_main_thunk, const ULONGLONG img_base)
{
// search the jump pattern, i.e.:
//JMP DWORD NEAR [0X402000] : FF 25 00204000
const size_t jmp_size = 2;
const BYTE jmp_pattern[jmp_size] = { 0xFF, 0x25 };
const size_t arg_size = sizeof(DWORD);
if ((jmp_size + arg_size) > buf_size) {
return nullptr;
}
const size_t end_offset = buf_size - (jmp_size + arg_size);
for (size_t i = end_offset; // search backwards
(i + 1) != 0; // this is unsigned comparison, so we cannot do: i >= 0
i--) // go back by one BYTE
{
if (buf[i] == jmp_pattern[0] && buf[i + 1] == jmp_pattern[1]) { // JMP
DWORD* addr = (DWORD*)(&buf[i + jmp_size]);
DWORD rva = static_cast<DWORD>((*addr) - img_base);
if (rva == cor_exe_main_thunk) {
#ifdef _DEBUG
std::cout << "Found call to _CorExeMain\n";
#endif
return buf + i;
}
else {
std::cerr << "[!] Mismatch: " << std::hex << rva << " vs _CorExeMain: " << cor_exe_main_thunk << std::endl;
}
}
}
return nullptr;
}
bool fix_dot_net_ep(BYTE *pe_buffer, size_t pe_buffer_size)
{
if (!pe_buffer) return false;
if (peconv::is64bit(pe_buffer)) {
//64bit .NET files have EP=0
peconv::update_entry_point_rva(pe_buffer, 0);
return true;
}
DWORD ep_rva = peconv::get_entry_point_rva(pe_buffer);
#ifdef _DEBUG
std::cout << "[*] This is a .NET payload and may require Enty Point correction. Current EP: " << std::hex << ep_rva << "\n";
#endif
PIMAGE_SECTION_HEADER sec_hdr = peconv::get_section_hdr(pe_buffer, pe_buffer_size, 0);
if (!sec_hdr) {
return false;
}
BYTE* sec_ptr = (BYTE*)((ULONG_PTR)pe_buffer + sec_hdr->VirtualAddress);
if (!peconv::validate_ptr(pe_buffer, pe_buffer_size, sec_ptr, sec_hdr->SizeOfRawData)) {
return false;
}
ULONGLONG img_base = peconv::get_image_base(pe_buffer);
DWORD cor_exe_main_thunk = find_corexemain(pe_buffer, pe_buffer_size);
if (!cor_exe_main_thunk) {
return false;
}
BYTE* jump_ptr = search_jump(sec_ptr, sec_hdr->SizeOfRawData, cor_exe_main_thunk, img_base);
if (!jump_ptr) {
return false;
}
size_t offset = (ULONG_PTR)jump_ptr - (ULONG_PTR)pe_buffer;
peconv::update_entry_point_rva(pe_buffer, static_cast<DWORD>(offset));
#ifdef _DEBUG
std::cout << "[*] Found possible Entry Point: " << std::hex << offset << std::endl;
#endif
return true;
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include <windows.h>
bool fix_dot_net_ep(BYTE *pe_buffer, size_t pe_buffer_size);
BYTE* search_jump(BYTE *buf, size_t buf_size, const DWORD cor_exe_main_thunk, const ULONGLONG img_base);

View File

@@ -0,0 +1,306 @@
#include "peconv/fix_imports.h"
#include "peconv/imports_uneraser.h"
#include "peconv/file_util.h"
#include <iostream>
#include <algorithm>
using namespace peconv;
template <typename FIELD_T>
size_t find_addresses_to_fill(FIELD_T call_via, FIELD_T thunk_addr, LPVOID modulePtr, size_t moduleSize, IN const peconv::ExportsMapper& exportsMap, OUT std::set<ULONGLONG> &addresses)
{
size_t addrCounter = 0;
do {
LPVOID call_via_ptr = (LPVOID)((ULONGLONG)modulePtr + call_via);
if (call_via_ptr == nullptr) break;
LPVOID thunk_ptr = (LPVOID)((ULONGLONG)modulePtr + thunk_addr);
if (thunk_ptr == nullptr) break;
if (!validate_ptr(modulePtr, moduleSize, thunk_ptr, sizeof(FIELD_T))) {
break;
}
if (!validate_ptr(modulePtr, moduleSize, call_via_ptr, sizeof(FIELD_T))) {
break;
}
FIELD_T *thunk_val = reinterpret_cast<FIELD_T*>(thunk_ptr);
FIELD_T *call_via_val = reinterpret_cast<FIELD_T*>(call_via_ptr);
if (*call_via_val == 0) {
//nothing to fill, probably the last record
break;
}
ULONGLONG searchedAddr = ULONGLONG(*call_via_val);
if (exportsMap.find_export_by_va(searchedAddr) != nullptr) {
addresses.insert(searchedAddr);
addrCounter++;
}
//---
call_via += sizeof(FIELD_T);
thunk_addr += sizeof(FIELD_T);
} while (true);
return addrCounter;
}
std::set<std::string> get_all_dlls_exporting_function(ULONGLONG func_addr, const peconv::ExportsMapper& exportsMap)
{
std::set<std::string> currDllNames;
//1. Get all the functions from all accessible DLLs that correspond to this address:
const std::set<ExportedFunc>* exports_for_va = exportsMap.find_exports_by_va(func_addr);
if (!exports_for_va) {
std::cerr << "Cannot find any DLL exporting: " << std::hex << func_addr << std::endl;
return currDllNames; //empty
}
//2. Iterate through their DLL names and add them to a set:
for (std::set<ExportedFunc>::iterator strItr = exports_for_va->begin();
strItr != exports_for_va->end();
++strItr)
{
currDllNames.insert(strItr->libName);
}
return currDllNames;
}
std::set<std::string> get_dlls_intersection(const std::set<std::string> &dllNames, const std::set<std::string> &currDllNames)
{
std::set<std::string> resultSet;
std::set_intersection(dllNames.begin(), dllNames.end(),
currDllNames.begin(), currDllNames.end(),
std::inserter(resultSet, resultSet.begin())
);
return resultSet;
}
//find the name of the DLL that can cover all the addresses of imported functions
std::string find_covering_dll(std::set<ULONGLONG> &addresses, const peconv::ExportsMapper& exportsMap)
{
std::set<std::string> mainDllsSet;
std::set<std::string> reserveDllSet;
bool isFresh = true;
// the earliest addresses are more significant for the final decision on what DLL to choose
// so, they should be processed at the end
std::set<ULONGLONG>::iterator addrItr;
for (addrItr = addresses.begin(); addrItr != addresses.end(); ++addrItr) {
ULONGLONG searchedAddr = *addrItr;
//---
// 1. Find all the DLLs exporting this particular function (can be forwarded etc)
std::set<std::string> currDllNames = get_all_dlls_exporting_function(searchedAddr, exportsMap);
//2. Which of those DLLs covers also previous functions from this series?
if (isFresh) {
//if no other function was processed before, set the current DLL set as the total set
mainDllsSet = currDllNames;
isFresh = false;
continue;
}
// find the intersection between the total set and the current set
std::set<std::string> resultSet = get_dlls_intersection(mainDllsSet, currDllNames);
if (resultSet.size() > 0) {
//found intersection, overwrite the main set
mainDllsSet = resultSet;
continue;
}
// if no intersection found in the main set, check if there is any in the reserved set:
resultSet = get_dlls_intersection(reserveDllSet, currDllNames);
if (resultSet.size() > 0) {
//found intersection, overwrite the main set
reserveDllSet = mainDllsSet; // move the current to the reserve
mainDllsSet = resultSet;
continue;
}
// no intersection found with any of the sets:
reserveDllSet = currDllNames; //set is as a reserved DLL: to be used if it will reoccur
}
if (mainDllsSet.size() > 0) {
const std::string main_dll = *(mainDllsSet.begin());
return main_dll;
}
return "";
}
bool ImportedDllCoverage::findCoveringDll()
{
std::string found_name = find_covering_dll(this->addresses, this->exportsMap);
if (found_name.length() == 0) {
#ifdef _DEBUG
std::cerr << "Cannot find a covering DLL" << std::endl;
#endif
return false;
}
this->dllName = found_name;
#ifdef _DEBUG
std::cout << "[+] Found DLL name: " << found_name << std::endl;
#endif
return true;
}
size_t map_addresses_to_functions(std::set<ULONGLONG> &addresses,
IN const std::string &chosenDll,
IN const peconv::ExportsMapper& exportsMap,
OUT std::map<ULONGLONG, std::set<ExportedFunc>> &addr_to_func,
OUT std::set<ULONGLONG> &not_found
)
{
std::set<ULONGLONG> coveredAddresses;
std::set<ULONGLONG>::iterator addrItr;
for (addrItr = addresses.begin(); addrItr != addresses.end(); ++addrItr) {
ULONGLONG searchedAddr = *addrItr;
const std::set<ExportedFunc>* exports_for_va = exportsMap.find_exports_by_va(searchedAddr);
if (exports_for_va == nullptr) {
not_found.insert(searchedAddr);
#ifdef _DEBUG
std::cerr << "Cannot find any DLL exporting: " << std::hex << searchedAddr << std::endl;
#endif
continue;
}
for (std::set<ExportedFunc>::iterator strItr = exports_for_va->begin();
strItr != exports_for_va->end();
++strItr)
{
std::string dll_name = strItr->libName;
if (dll_name != chosenDll) {
continue;
}
ExportedFunc func = *strItr;
addr_to_func[searchedAddr].insert(func);
coveredAddresses.insert(searchedAddr);
}
if (addr_to_func.find(searchedAddr) == addr_to_func.end()) {
const ExportedFunc* func = exportsMap.find_export_by_va(searchedAddr);
not_found.insert(searchedAddr);
#ifdef _DEBUG
std::cerr << "[WARNING] A function: " << func->toString() << " not found in the covering DLL: " << chosenDll << std::endl;
#endif
}
}
return coveredAddresses.size();
}
size_t ImportedDllCoverage::mapAddressesToFunctions(const std::string &dll)
{
//reset all stored info:
this->mappedDllName = dll;
if (this->addrToFunc.size() > 0) {
this->addrToFunc.clear();
}
this->notFound.clear();
const size_t coveredCount = map_addresses_to_functions(this->addresses, dll, this->exportsMap, this->addrToFunc, this->notFound);
#ifdef _DEBUG
if (notFound.size()) {
std::cout << "[-] Not all addresses are covered! Not found: " << std::dec << notFound.size() << std::endl;
} else {
std::cout << "All covered!" << std::endl;
}
#endif
return coveredCount;
}
void ImpsNotCovered::insert(ULONGLONG thunk, ULONGLONG searchedAddr)
{
#ifdef _DEBUG
std::cerr << "[-] Function not recovered: [" << std::hex << searchedAddr << "] " << std::endl;
#endif
thunkToAddr[thunk] = searchedAddr;
}
bool peconv::fix_imports(IN OUT PVOID modulePtr, IN size_t moduleSize, IN const peconv::ExportsMapper& exportsMap, OUT OPTIONAL peconv::ImpsNotCovered* notCovered)
{
bool skip_bound = false; // skip boud imports?
IMAGE_DATA_DIRECTORY *importsDir = peconv::get_directory_entry((const BYTE*) modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (importsDir == NULL) {
return true; // done! no imports -> nothing to fix
}
bool is64 = peconv::is64bit((BYTE*)modulePtr);
DWORD maxSize = importsDir->Size;
DWORD impAddr = importsDir->VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL;
DWORD parsedSize = 0;
#ifdef _DEBUG
printf("---IMP---\n");
#endif
while (parsedSize < maxSize) {
lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR) modulePtr);
if (!validate_ptr(modulePtr, moduleSize, lib_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
printf("[-] Invalid descriptor pointer!\n");
return false;
}
parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL) {
break;
}
const bool is_bound = (lib_desc->TimeDateStamp == (-1));
if (is_bound && skip_bound) {
continue;
}
#ifdef _DEBUG
printf("Imported Lib: %x : %x : %x\n", lib_desc->FirstThunk, lib_desc->OriginalFirstThunk, lib_desc->Name);
#endif
std::string lib_name = "";
if (lib_desc->Name != 0) {
LPSTR name_ptr = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
if (validate_ptr(modulePtr, moduleSize, name_ptr, sizeof(char) * MIN_DLL_LEN)) {
lib_name = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
}
}
DWORD call_via = lib_desc->FirstThunk;
DWORD thunk_addr = lib_desc->OriginalFirstThunk; // warning: it can be NULL!
std::set<ULONGLONG> addresses;
if (!is64) {
find_addresses_to_fill<DWORD>(call_via, thunk_addr, modulePtr, moduleSize, exportsMap, addresses);
} else {
find_addresses_to_fill<ULONGLONG>(call_via, thunk_addr, modulePtr, moduleSize, exportsMap, addresses);
}
ImportedDllCoverage dllCoverage(addresses, exportsMap);
bool is_all_covered = dllCoverage.findCoveringDll();
bool is_lib_erased = false;
lib_name = get_dll_shortname(lib_name); //without extension
if (lib_name.length() == 0) {
is_lib_erased = true;
if (is_all_covered) {
// set a name of the covering DLL:
lib_name = dllCoverage.dllName;
}
}
if (lib_name.length() == 0) {
//could not find a relevant DLL
continue;
}
#ifdef _DEBUG
std::cout << lib_name << std::endl;
#endif
if (!dllCoverage.mapAddressesToFunctions(lib_name)) {
// cannot find any functions imported from this DLL
continue;
}
//everything mapped, now recover it:
ImportsUneraser impUneraser(modulePtr, moduleSize);
if (!impUneraser.uneraseDllImports(lib_desc, dllCoverage, notCovered)) {
return false;
}
if (is_lib_erased) {
const std::string dll_with_ext = exportsMap.get_dll_fullname(dllCoverage.dllName);
impUneraser.uneraseDllName(lib_desc, dll_with_ext);
}
}
#ifdef _DEBUG
std::cout << "---------" << std::endl;
#endif
return true;
}

View File

@@ -0,0 +1,18 @@
#include "peconv/function_resolver.h"
#include <iostream>
FARPROC peconv::default_func_resolver::resolve_func(LPSTR lib_name, LPSTR func_name)
{
HMODULE libBasePtr = LoadLibraryA(lib_name);
if (libBasePtr == NULL) {
std::cerr << "Could not load the library!" << std::endl;
return NULL;
}
FARPROC hProc = GetProcAddress(libBasePtr, func_name);
if (hProc == NULL) {
std::cerr << "Could not load the function!" << std::endl;
return NULL;
}
return hProc;
}

View File

@@ -0,0 +1,224 @@
#include "peconv/hooks.h"
#include "peconv.h"
#include "peconv/peb_lookup.h"
using namespace peconv;
namespace peconv {
bool is_pointer_in_ntdll(LPVOID lpAddress)
{
HMODULE mod = peconv::get_module_via_peb(L"ntdll.dll");
size_t module_size = peconv::get_module_size_via_peb(mod);
if (peconv::validate_ptr(mod, module_size, lpAddress, sizeof(BYTE))) {
return true; //this address lies within NTDLL
}
return false;
}
BOOL nt_protect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
{
FARPROC proc = peconv::get_exported_func(
peconv::get_module_via_peb(L"ntdll.dll"),
"NtProtectVirtualMemory"
);
if (!proc) {
return FALSE;
}
NTSTATUS(NTAPI *_NtProtectVirtualMemory)(
IN HANDLE,
IN OUT PVOID*,
IN OUT PSIZE_T,
IN DWORD,
OUT PDWORD) =
(NTSTATUS(NTAPI *)(
IN HANDLE,
IN OUT PVOID*,
IN OUT PSIZE_T,
IN DWORD,
OUT PDWORD)) proc;
SIZE_T protect_size = dwSize;
NTSTATUS status = _NtProtectVirtualMemory(GetCurrentProcess(), &lpAddress, &protect_size, flNewProtect, lpflOldProtect);
if (status != S_OK) {
return FALSE;
}
return TRUE;
}
};
bool PatchBackup::makeBackup(BYTE *patch_ptr, size_t patch_size)
{
if (!patch_ptr) {
return false;
}
deleteBackup();
this->sourcePtr = patch_ptr;
this->buffer = new BYTE[patch_size];
this->bufferSize = patch_size;
memcpy(buffer, patch_ptr, patch_size);
return true;
}
bool PatchBackup::applyBackup()
{
if (!isBackup()) {
return false;
}
DWORD oldProtect = 0;
if (!nt_protect((LPVOID)sourcePtr, bufferSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
return false;
}
memcpy(sourcePtr, buffer, bufferSize);
nt_protect((LPVOID)sourcePtr, bufferSize, oldProtect, &oldProtect);
//flush cache:
FlushInstructionCache(GetCurrentProcess(), sourcePtr, bufferSize);
return true;
}
FARPROC peconv::hooking_func_resolver::resolve_func(LPSTR lib_name, LPSTR func_name)
{
//the name may be ordinal rather than string, so check if it is a valid pointer:
if (!peconv::is_bad_read_ptr(func_name, 1)) {
std::map<std::string, FARPROC>::iterator itr = hooks_map.find(func_name);
if (itr != hooks_map.end()) {
FARPROC hook = itr->second;
#ifdef _DEBUG
std::cout << ">>>>>>Replacing: " << func_name << " by: " << hook << std::endl;
#endif
return hook;
}
}
return peconv::default_func_resolver::resolve_func(lib_name, func_name);
}
size_t peconv::redirect_to_local64(void *ptr, ULONGLONG new_offset, PatchBackup* backup)
{
if (!ptr) return 0;
BYTE hook_64[] = {
0x48, 0xB8, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xEE, 0xFF, //movabs rax,FFEE998877665544
0xFF, 0xE0 //jmp rax
};
const size_t hook64_size = sizeof(hook_64);
if (is_pointer_in_ntdll(ptr)) {
std::cout << "[WARNING] Patching NTDLL is not allowed because of possible stability issues!\n";
return 0;
}
DWORD oldProtect = 0;
if (!nt_protect((LPVOID)ptr,
hook64_size,
PAGE_EXECUTE_READWRITE, //this must be executable if we are hooking kernel32.dll, because we are using VirtualProtect from kernel32 at the same time
&oldProtect))
{
return 0;
}
if (backup != nullptr) {
backup->makeBackup((BYTE*)ptr, hook64_size);
}
memcpy(hook_64 + 2, &new_offset, sizeof(ULONGLONG));
memcpy(ptr, hook_64, hook64_size);
nt_protect((LPVOID)ptr, hook64_size, oldProtect, &oldProtect);
//flush cache:
FlushInstructionCache(GetCurrentProcess(), ptr, hook64_size);
return hook64_size;
}
size_t peconv::redirect_to_local32(void *ptr, DWORD new_offset, PatchBackup* backup)
{
if (!ptr) return 0;
BYTE hook_32[] = {
0xB8, 0xCC, 0xDD, 0xEE, 0xFF, // mov eax,FFEEDDCC
0xFF, 0xE0 //jmp eax
};
const size_t hook32_size = sizeof(hook_32);
if (is_pointer_in_ntdll(ptr)) {
std::cout << "[WARNING] Patching NTDLL is not allowed because of possible stability issues!\n";
return 0;
}
DWORD oldProtect = 0;
if (!nt_protect((LPVOID)ptr,
hook32_size,
PAGE_EXECUTE_READWRITE, //this must be executable if we are hooking kernel32.dll, because we are using VirtualProtect from kernel32 at the same time
&oldProtect))
{
return 0;
}
if (backup != nullptr) {
backup->makeBackup((BYTE*)ptr, hook32_size);
}
memcpy(hook_32 + 1, &new_offset, sizeof(DWORD));
memcpy(ptr, hook_32, hook32_size);
nt_protect((LPVOID)ptr, hook32_size, oldProtect, &oldProtect);
//flush cache:
FlushInstructionCache(GetCurrentProcess(), ptr, hook32_size);
return hook32_size;
}
size_t peconv::redirect_to_local(void *ptr, void* new_function_ptr, PatchBackup* backup)
{
#ifdef _WIN64
return peconv::redirect_to_local64(ptr, (ULONGLONG)new_function_ptr, backup);
#else
return peconv::redirect_to_local32(ptr, (DWORD)new_function_ptr, backup);
#endif
}
inline long long int get_jmp_delta(ULONGLONG currVA, int instrLen, ULONGLONG destVA)
{
long long int diff = destVA - (currVA + instrLen);
return diff;
}
inline bool is_valid_delta(long long int delta)
{
DWORD first_dw = delta >> sizeof(DWORD) * 8;
if (first_dw == 0) {
return true;
}
const DWORD max_dword = DWORD(-1);
if (first_dw != max_dword) {
return false;
}
DWORD delta_dw = DWORD(delta);
if (delta_dw & 0x80000000) {
return true;
}
//invalid, sign bit is missing
return false;
}
bool peconv::replace_target(BYTE *patch_ptr, ULONGLONG dest_addr)
{
typedef enum {
OP_JMP = 0xE9,
OP_CALL_DWORD = 0xE8
} t_opcode;
if (patch_ptr[0] == OP_JMP || patch_ptr[0] == OP_CALL_DWORD) {
ULONGLONG delta = get_jmp_delta(ULONGLONG(patch_ptr), 5, dest_addr);
if (!is_valid_delta(delta)) {
#ifdef _DEBUG
std::cout << "Cannot replace the target: too big delta: " << std::hex << delta << std::endl;
#endif
//too big delta, cannot be saved in a DWORD
return false;
}
DWORD delta_dw = DWORD(delta);
memcpy(patch_ptr + 1, &delta_dw, sizeof(DWORD));
//flush cache:
FlushInstructionCache(GetCurrentProcess(), patch_ptr + 1, sizeof(DWORD));
return true;
}
return false;
}

View File

@@ -0,0 +1,271 @@
#include "peconv/imports_loader.h"
#include <iostream>
using namespace peconv;
class FillImportThunks : public ImportThunksCallback
{
public:
FillImportThunks(BYTE* _modulePtr, size_t _moduleSize, t_function_resolver* func_resolver)
: ImportThunksCallback(_modulePtr, _moduleSize), funcResolver(func_resolver)
{
}
virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
{
if (this->is64b) {
#ifdef _WIN64 // loader is 64 bit, allow to load imports for 64-bit payload:
IMAGE_THUNK_DATA64* desc = reinterpret_cast<IMAGE_THUNK_DATA64*>(origFirstThunkPtr);
ULONGLONG* call_via = reinterpret_cast<ULONGLONG*>(firstThunkPtr);
return processThunks_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64);
#else
std::cerr << "[!] Cannot fill imports into 64 bit PE via 32 bit loader!\n";
return false;
#endif
}
else {
#ifndef _WIN64 // loader is 32 bit, allow to load imports for 32-bit payload:
IMAGE_THUNK_DATA32* desc = reinterpret_cast<IMAGE_THUNK_DATA32*>(origFirstThunkPtr);
DWORD* call_via = reinterpret_cast<DWORD*>(firstThunkPtr);
return processThunks_tpl<DWORD, IMAGE_THUNK_DATA32>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32);
#else
std::cerr << "[!] Cannot fill imports into 32 bit PE via 64 bit loader!\n";
return false;
#endif
}
}
protected:
template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag)
{
if (!this->funcResolver) {
return false;
}
bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
FARPROC hProc = nullptr;
if (is_by_ord) {
T_FIELD raw_ordinal = desc->u1.Ordinal & (~ordinal_flag);
#ifdef _DEBUG
std::cout << "raw ordinal: " << std::hex << raw_ordinal << std::endl;
#endif
hProc = funcResolver->resolve_func(lib_name, MAKEINTRESOURCEA(raw_ordinal));
}
else {
PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
#ifdef _DEBUG
std::cout << "name: " << func_name << std::endl;
#endif
hProc = this->funcResolver->resolve_func(lib_name, func_name);
}
if (!hProc) {
#ifdef _DEBUG
std::cerr << "Could not resolve the function!" << std::endl;
#endif
return false;
}
(*call_via) = reinterpret_cast<T_FIELD>(hProc);
return true;
}
//fields:
t_function_resolver* funcResolver;
};
template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
bool process_imp_functions_tpl(BYTE* modulePtr, size_t module_size, LPSTR lib_name, DWORD call_via, DWORD thunk_addr, IN ImportThunksCallback *callback)
{
bool is_ok = true;
T_FIELD *thunks = (T_FIELD*)((ULONGLONG)modulePtr + thunk_addr);
T_FIELD *callers = (T_FIELD*)((ULONGLONG)modulePtr + call_via);
for (size_t index = 0; true; index++) {
if (!validate_ptr(modulePtr, module_size, &callers[index], sizeof(T_FIELD))) {
break;
}
if (!validate_ptr(modulePtr, module_size, &thunks[index], sizeof(T_FIELD))) {
break;
}
if (callers[index] == 0) {
//nothing to fill, probably the last record
return true;
}
LPVOID thunk_ptr = &thunks[index];
T_IMAGE_THUNK_DATA* desc = reinterpret_cast<T_IMAGE_THUNK_DATA*>(thunk_ptr);
if (!validate_ptr(modulePtr, module_size, desc, sizeof(T_IMAGE_THUNK_DATA))) {
break;
}
if (desc->u1.Function == NULL) {
break;
}
T_FIELD ordinal_flag = (sizeof(T_FIELD) == sizeof(ULONGLONG)) ? IMAGE_ORDINAL_FLAG64 : IMAGE_ORDINAL_FLAG32;
bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
if (!is_by_ord) {
PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
if (!validate_ptr(modulePtr, module_size, by_name, sizeof(IMAGE_IMPORT_BY_NAME))) {
break;
}
}
//when the callback is called, all the pointers should be already verified
if (!callback->processThunks(lib_name, (ULONG_PTR)&thunks[index], (ULONG_PTR)&callers[index])) {
is_ok = false;
}
}
return is_ok;
}
//Walk through the table of imported DLLs (starting from the given descriptor) and execute the callback each time when the new record was found
bool process_dlls(BYTE* modulePtr, size_t module_size, IMAGE_IMPORT_DESCRIPTOR *first_desc, IN ImportThunksCallback *callback)
{
bool isAllFilled = true;
#ifdef _DEBUG
std::cout << "---IMP---" << std::endl;
#endif
const bool is64 = is64bit((BYTE*)modulePtr);
IMAGE_IMPORT_DESCRIPTOR* lib_desc = nullptr;
for (size_t i = 0; true; i++) {
lib_desc = &first_desc[i];
if (!validate_ptr(modulePtr, module_size, lib_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
break;
}
if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL) {
break;
}
LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);
if (!peconv::is_valid_import_name(modulePtr, module_size, lib_name)) {
//invalid name
return false;
}
DWORD call_via = lib_desc->FirstThunk;
DWORD thunk_addr = lib_desc->OriginalFirstThunk;
if (thunk_addr == NULL) {
thunk_addr = lib_desc->FirstThunk;
}
#ifdef _DEBUG
std::cout << "Imported Lib: " << std::hex << lib_desc->FirstThunk << " : " << std::hex << lib_desc->OriginalFirstThunk << " : " << lib_desc->Name << std::endl;
#endif
size_t all_solved = false;
if (is64) {
all_solved = process_imp_functions_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(modulePtr, module_size, lib_name, call_via, thunk_addr, callback);
}
else {
all_solved = process_imp_functions_tpl<DWORD, IMAGE_THUNK_DATA32>(modulePtr, module_size, lib_name, call_via, thunk_addr, callback);
}
if (!all_solved) {
isAllFilled = false;
}
}
#ifdef _DEBUG
printf("---------\n");
#endif
return isAllFilled;
}
bool peconv::process_import_table(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ImportThunksCallback *callback)
{
if (moduleSize == 0) { //if not given, try to fetch
moduleSize = peconv::get_image_size((const BYTE*)modulePtr);
}
if (moduleSize == 0) return false;
IMAGE_DATA_DIRECTORY *importsDir = get_directory_entry((BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (!importsDir) {
return true; //no import table
}
const DWORD impAddr = importsDir->VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR *first_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + (ULONG_PTR)modulePtr);
if (!peconv::validate_ptr(modulePtr, moduleSize, first_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
return false;
}
return process_dlls(modulePtr, moduleSize, first_desc, callback);
}
bool peconv::load_imports(BYTE* modulePtr, t_function_resolver* func_resolver)
{
size_t moduleSize = peconv::get_image_size((const BYTE*)modulePtr);
if (moduleSize == 0) return false;
bool is64 = is64bit((BYTE*)modulePtr);
bool is_loader64 = false;
#ifdef _WIN64
is_loader64 = true;
#endif
if (is64 != is_loader64) {
std::cerr << "[ERROR] Loader/Payload bitness mismatch.\n";
return false;
}
default_func_resolver default_res;
if (!func_resolver) {
func_resolver = (t_function_resolver*)&default_res;
}
FillImportThunks callback(modulePtr, moduleSize, func_resolver);
return peconv::process_import_table(modulePtr, moduleSize, &callback);
}
// A valid name must contain printable characters. Empty name is also acceptable (may have been erased)
bool peconv::is_valid_import_name(const PBYTE modulePtr, const size_t moduleSize, LPSTR lib_name)
{
while (true) {
if (!peconv::validate_ptr(modulePtr, moduleSize, lib_name, sizeof(char))) {
return false;
}
char next_char = *lib_name;
if (next_char == '\0') break;
if (next_char <= 0x20 || next_char >= 0x7E) {
return false;
}
lib_name++;
}
return true;
}
bool peconv::has_valid_import_table(const PBYTE modulePtr, size_t moduleSize)
{
IMAGE_DATA_DIRECTORY *importsDir = get_directory_entry((BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (importsDir == NULL) return false;
const DWORD impAddr = importsDir->VirtualAddress;
IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL;
DWORD parsedSize = 0;
size_t valid_records = 0;
while (true) { //size of the import table doesn't matter
lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(impAddr + parsedSize + (ULONG_PTR)modulePtr);
if (!peconv::validate_ptr(modulePtr, moduleSize, lib_desc, sizeof(IMAGE_IMPORT_DESCRIPTOR))) {
return false;
}
parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
if (lib_desc->OriginalFirstThunk == NULL && lib_desc->FirstThunk == NULL) {
break;
}
LPSTR lib_name = (LPSTR)((ULONGLONG)modulePtr + lib_desc->Name);
if (!is_valid_import_name(modulePtr, moduleSize, lib_name)) return false;
DWORD call_via = lib_desc->FirstThunk;
DWORD thunk_addr = lib_desc->OriginalFirstThunk;
if (thunk_addr == NULL) thunk_addr = lib_desc->FirstThunk;
DWORD *thunks = (DWORD*)((ULONGLONG)modulePtr + thunk_addr);
if (!peconv::validate_ptr(modulePtr, moduleSize, thunks, sizeof(DWORD))) return false;
DWORD *callers = (DWORD*)((ULONGLONG)modulePtr + call_via);
if (!peconv::validate_ptr(modulePtr, moduleSize, callers, sizeof(DWORD))) return false;
valid_records++;
}
return (valid_records > 0);
}

View File

@@ -0,0 +1,243 @@
#include "peconv/imports_uneraser.h"
#include <iostream>
using namespace peconv;
LPVOID search_name(std::string name, const char* modulePtr, size_t moduleSize)
{
const char* namec = name.c_str();
const size_t searched_len = name.length() + 1; // with terminating NULL
const char* found_ptr = std::search(modulePtr, modulePtr + moduleSize, namec, namec + searched_len);
if (found_ptr == NULL) {
return NULL;
}
size_t o = found_ptr - modulePtr;
if (o < moduleSize) {
return (LPVOID)(found_ptr);
}
return NULL;
}
bool ImportsUneraser::writeFoundDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &found_name)
{
#ifdef _DEBUG
std::cout << "Found name:" << found_name << std::endl;
#endif
LPSTR name_ptr = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
size_t full_name_len = found_name.length() + 1; // with terminating zero
if (!validate_ptr(modulePtr, moduleSize, name_ptr, full_name_len)) {
//corner case: allow to save the name at the very end of the buffer, without the terminating zero
full_name_len--;
if (!validate_ptr(modulePtr, moduleSize, name_ptr, full_name_len)) {
return false; //invalid pointer, cannot save
}
}
memcpy(name_ptr, found_name.c_str(), full_name_len);
return true;
}
bool ImportsUneraser::uneraseDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &dll_name)
{
LPSTR name_ptr = nullptr;
if (lib_desc->Name != 0) {
name_ptr = (LPSTR)((ULONGLONG) modulePtr + lib_desc->Name);
}
if (name_ptr == nullptr || !validate_ptr(modulePtr, moduleSize, name_ptr, sizeof(char) * MIN_DLL_LEN)) {
//try to get the cave:
DWORD cave_size = DWORD(dll_name.length() + 1 + 5); //ending null + padding
PBYTE ptr = find_ending_cave(modulePtr, moduleSize, cave_size);
if (ptr == nullptr) {
std::cerr << "Cannot save the DLL name: " << dll_name << std::endl;
return false;
}
DWORD cave_rva = static_cast<DWORD>(ptr - modulePtr);
lib_desc->Name = cave_rva;
}
if (writeFoundDllName(lib_desc, dll_name)) {
return true; // written the found name
}
return false;
}
template <typename FIELD_T>
bool ImportsUneraser::findNameInBinaryAndFill(IMAGE_IMPORT_DESCRIPTOR* lib_desc,
LPVOID call_via_ptr,
LPVOID thunk_ptr,
const FIELD_T ordinal_flag,
std::map<ULONGLONG, std::set<ExportedFunc>> &addr_to_func
)
{
if (call_via_ptr == NULL || modulePtr == NULL || lib_desc == NULL) {
return false; //malformed input
}
IMAGE_DATA_DIRECTORY *importsDir = get_directory_entry((BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT);
if (!importsDir) return false;
const DWORD impAddr = importsDir->VirtualAddress; //start of the import table
FIELD_T *call_via_val = (FIELD_T*)call_via_ptr;
if (*call_via_val == 0) {
//nothing to fill, probably the last record
return false;
}
ULONGLONG searchedAddr = ULONGLONG(*call_via_val);
bool is_name_saved = false;
FIELD_T lastOrdinal = 0; //store also ordinal of the matching function
std::set<ExportedFunc>::iterator funcname_itr = addr_to_func[searchedAddr].begin();
for (funcname_itr = addr_to_func[searchedAddr].begin();
funcname_itr != addr_to_func[searchedAddr].end();
++funcname_itr)
{
const ExportedFunc &found_func = *funcname_itr;
lastOrdinal = found_func.funcOrdinal;
const char* names_start = ((const char*) modulePtr + impAddr);
BYTE* found_ptr = (BYTE*) search_name(found_func.funcName, names_start, moduleSize - (names_start - (const char*)modulePtr));
if (!found_ptr) {
//name not found in the binary
//TODO: maybe it is imported by ordinal?
continue;
}
const ULONGLONG name_offset = (ULONGLONG)found_ptr - (ULONGLONG)modulePtr;
#ifdef _DEBUG
//if it is not the first name from the list, inform about it:
if (funcname_itr != addr_to_func[searchedAddr].begin()) {
std::cout << ">[*][" << std::hex << searchedAddr << "] " << found_func.toString() << std::endl;
}
std::cout <<"[+] Found the name at: " << std::hex << name_offset << std::endl;
#endif
PIMAGE_IMPORT_BY_NAME imp_field = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(name_offset - sizeof(WORD)); // substract the size of Hint
//TODO: validate more...
memcpy(thunk_ptr, &imp_field, sizeof(FIELD_T));
#ifdef _DEBUG
std::cout << "[+] Wrote found to offset: " << std::hex << call_via_ptr << std::endl;
#endif
is_name_saved = true;
break;
}
//name not found or could not be saved - fill the ordinal instead:
if (!is_name_saved && lastOrdinal != 0) {
#ifdef _DEBUG
std::cout << "[+] Filling ordinal: " << lastOrdinal << std::endl;
#endif
FIELD_T ord_thunk = lastOrdinal | ordinal_flag;
memcpy(thunk_ptr, &ord_thunk, sizeof(FIELD_T));
is_name_saved = true;
}
return is_name_saved;
}
template <typename FIELD_T, typename IMAGE_THUNK_DATA_T>
bool ImportsUneraser::writeFoundFunction(IMAGE_THUNK_DATA_T* desc, const FIELD_T ordinal_flag, const ExportedFunc &foundFunc)
{
if (foundFunc.isByOrdinal) {
FIELD_T ordinal = foundFunc.funcOrdinal | ordinal_flag;
FIELD_T* by_ord = (FIELD_T*) desc;
*by_ord = ordinal;
#ifdef _DEBUG
std::cout << "[+] Saved ordinal" << std::endl;
#endif
return true;
}
PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME) ((ULONGLONG) modulePtr + desc->u1.AddressOfData);
LPSTR func_name_ptr = reinterpret_cast<LPSTR>(by_name->Name);
std::string found_name = foundFunc.funcName;
bool is_nameptr_valid = validate_ptr(modulePtr, moduleSize, func_name_ptr, found_name.length());
// try to save the found name under the pointer:
if (is_nameptr_valid) {
by_name->Hint = MASK_TO_WORD(foundFunc.funcOrdinal);
memcpy(func_name_ptr, found_name.c_str(), found_name.length() + 1); // with the ending '\0'
#ifdef _DEBUG
std::cout << "[+] Saved name" << std::endl;
#endif
return true;
}
return false;
}
template <typename FIELD_T, typename IMAGE_THUNK_DATA_T>
bool ImportsUneraser::fillImportNames(
IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc,
IN const FIELD_T ordinal_flag,
IN std::map<ULONGLONG, std::set<ExportedFunc>> &addr_to_func,
OUT OPTIONAL ImpsNotCovered* notCovered
)
{
if (lib_desc == NULL) return false;
FIELD_T call_via = lib_desc->FirstThunk;
if (call_via == NULL) return false;
size_t processed_imps = 0;
size_t recovered_imps = 0;
FIELD_T thunk_addr = lib_desc->OriginalFirstThunk;
if (thunk_addr == NULL) {
thunk_addr = call_via;
}
BYTE* call_via_ptr = (BYTE*)((ULONGLONG)modulePtr + call_via);
BYTE* thunk_ptr = (BYTE*)((ULONGLONG)modulePtr + thunk_addr);
for (;
call_via_ptr != NULL && thunk_ptr != NULL;
call_via_ptr += sizeof(FIELD_T), thunk_ptr += sizeof(FIELD_T)
)
{
FIELD_T *thunk_val = (FIELD_T*)thunk_ptr;
FIELD_T *call_via_val = (FIELD_T*)call_via_ptr;
if (*call_via_val == 0) {
//nothing to fill, probably the last record
break;
}
IMAGE_THUNK_DATA_T* desc = (IMAGE_THUNK_DATA_T*)thunk_ptr;
if (desc->u1.Function == NULL) {
break;
}
ULONGLONG searchedAddr = ULONGLONG(*call_via_val);
std::map<ULONGLONG,std::set<ExportedFunc>>::const_iterator found_itr = addr_to_func.find(searchedAddr);
if (found_itr == addr_to_func.end() || found_itr->second.size() == 0) {
//not found, move on
if (notCovered) {
notCovered->insert((call_via_ptr - modulePtr), searchedAddr);
}
continue;
}
std::set<ExportedFunc>::const_iterator funcname_itr = found_itr->second.begin();
const peconv::ExportedFunc &foundFunc = *funcname_itr;
#ifdef _DEBUG
std::cout << "[*][" << std::hex << searchedAddr << "] " << funcname_itr->toString() << std::endl;
#endif
bool is_name_saved = writeFoundFunction<FIELD_T, IMAGE_THUNK_DATA_T>(desc, ordinal_flag, *funcname_itr);
if (!is_name_saved) {
is_name_saved = findNameInBinaryAndFill<FIELD_T>(lib_desc, call_via_ptr, thunk_ptr, ordinal_flag, addr_to_func);
}
processed_imps++;
if (is_name_saved) recovered_imps++;
}
return (recovered_imps == processed_imps);
}
bool ImportsUneraser::uneraseDllImports(IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc, IN ImportedDllCoverage &dllCoverage, OUT OPTIONAL ImpsNotCovered* notCovered)
{
//everything mapped, now recover it:
bool is_filled = false;
if (!is64) {
is_filled = fillImportNames<DWORD, IMAGE_THUNK_DATA32>(lib_desc, IMAGE_ORDINAL_FLAG32, dllCoverage.addrToFunc, notCovered);
} else {
is_filled = fillImportNames<ULONGLONG, IMAGE_THUNK_DATA64>(lib_desc, IMAGE_ORDINAL_FLAG64, dllCoverage.addrToFunc, notCovered);
}
if (!is_filled) {
std::cerr << "[-] Could not fill some import names!" << std::endl;
return false;
}
return is_filled;
}

View File

@@ -0,0 +1,57 @@
#include "peconv/load_config_util.h"
#include "peconv/pe_hdrs_helper.h"
BYTE* peconv::get_load_config_ptr(BYTE* buffer, size_t buf_size)
{
if (!buffer || !buf_size) return nullptr;
IMAGE_DATA_DIRECTORY* dir = peconv::get_directory_entry(buffer, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG);
if (!dir) {
return 0;
}
DWORD entry_rva = dir->VirtualAddress;
DWORD entry_size = dir->Size;
if (!peconv::validate_ptr(buffer, buf_size, buffer + entry_rva, entry_size)) {
return 0;
}
IMAGE_LOAD_CONFIG_DIRECTORY32* ldc = reinterpret_cast<IMAGE_LOAD_CONFIG_DIRECTORY32*>((ULONG_PTR)buffer + entry_rva);
return reinterpret_cast<BYTE*>(ldc);
}
peconv::t_load_config_ver peconv::get_load_config_version(BYTE* buffer, size_t buf_size, BYTE* ld_config_ptr)
{
if (!buffer || !buf_size || !ld_config_ptr) peconv::LOAD_CONFIG_NONE;
bool is64b = peconv::is64bit(buffer);
if (!peconv::validate_ptr(buffer, buf_size, ld_config_ptr, sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W7))) {
return peconv::LOAD_CONFIG_NONE;
}
peconv::IMAGE_LOAD_CONFIG_DIR32_W7* smallest = (peconv::IMAGE_LOAD_CONFIG_DIR32_W7*)ld_config_ptr;
const size_t curr_size = smallest->Size;
if (is64b) {
switch (curr_size) {
case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR64_W7) :
return peconv::LOAD_CONFIG_W7_VER;
case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR64_W8) :
return peconv::LOAD_CONFIG_W8_VER;
case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR64_W10) :
return peconv::LOAD_CONFIG_W10_VER;
default:
return LOAD_CONFIG_UNK_VER;
}
}
else {
switch (curr_size) {
case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W7) :
return peconv::LOAD_CONFIG_W7_VER;
case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W8) :
return peconv::LOAD_CONFIG_W8_VER;
case sizeof(peconv::IMAGE_LOAD_CONFIG_DIR32_W10) :
return peconv::LOAD_CONFIG_W10_VER;
default:
return LOAD_CONFIG_UNK_VER;
}
}
return LOAD_CONFIG_UNK_VER;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
#include "peconv/pe_dumper.h"
#include "peconv/pe_hdrs_helper.h"
#include "peconv/pe_virtual_to_raw.h"
#include "peconv/fix_imports.h"
#include "peconv/file_util.h"
#include "peconv/pe_mode_detector.h"
#include "fix_dot_net_ep.h"
#include <iostream>
using namespace peconv;
t_pe_dump_mode peconv::detect_dump_mode(IN const BYTE* buffer, IN size_t mod_size)
{
const t_pe_dump_mode default_mode = peconv::PE_DUMP_UNMAP;
if (peconv::is_pe_raw(buffer, mod_size)) {
return peconv::PE_DUMP_VIRTUAL;
}
if (peconv::is_pe_expanded(buffer, mod_size)) {
return peconv::PE_DUMP_REALIGN;
}
return default_mode;
}
bool peconv::dump_pe(IN const char *out_path,
IN OUT BYTE *buffer, IN size_t mod_size,
IN const ULONGLONG start_addr,
IN OUT t_pe_dump_mode &dump_mode,
IN OPTIONAL const peconv::ExportsMapper* exportsMap
)
{
// if the exportsMap is supplied, attempt to recover the (destroyed) import table:
if (exportsMap != nullptr) {
if (!peconv::fix_imports(buffer, mod_size, *exportsMap, NULL)) {
std::cerr << "[-] Unable to fix imports!" << std::endl;
}
}
if (dump_mode == PE_DUMP_AUTO || dump_mode >= PE_DUMP_MODES_COUNT) {
dump_mode = detect_dump_mode(buffer, mod_size);
}
BYTE* dump_data = buffer;
size_t dump_size = mod_size;
size_t out_size = 0;
BYTE* unmapped_module = nullptr;
if (dump_mode == peconv::PE_DUMP_UNMAP || dump_mode == peconv::PE_DUMP_REALIGN) {
//if the image base in headers is invalid, set the current base and prevent from relocating PE:
if (peconv::get_image_base(buffer) == 0) {
peconv::update_image_base(buffer, (ULONGLONG)start_addr);
}
if (is_dot_net(buffer, mod_size)) {
fix_dot_net_ep(buffer, mod_size);
}
if (dump_mode == peconv::PE_DUMP_UNMAP) {
unmapped_module = pe_virtual_to_raw(buffer, mod_size, (ULONGLONG)start_addr, out_size, false);
}
else if (dump_mode == peconv::PE_DUMP_REALIGN) {
unmapped_module = peconv::pe_realign_raw_to_virtual(buffer, mod_size, (ULONGLONG)start_addr, out_size);
}
// unmap the PE file (convert from the Virtual Format into Raw Format)
if (unmapped_module) {
dump_data = unmapped_module;
dump_size = out_size;
}
}
// save the read module into a file
const bool is_dumped = dump_to_file(out_path, dump_data, dump_size);
peconv::free_pe_buffer(unmapped_module, mod_size);
return is_dumped;
}

View File

@@ -0,0 +1,652 @@
#include "peconv/pe_hdrs_helper.h"
#include "peconv/util.h"
using namespace peconv;
#ifdef _DEBUG
#include <iostream>
#endif
BYTE* peconv::get_nt_hdrs(IN const BYTE *pe_buffer, IN OPTIONAL size_t buffer_size)
{
if (!pe_buffer) return nullptr;
IMAGE_DOS_HEADER *idh = (IMAGE_DOS_HEADER*)pe_buffer;
if (buffer_size != 0) {
if (!peconv::validate_ptr((LPVOID)pe_buffer, buffer_size, (LPVOID)idh, sizeof(IMAGE_DOS_HEADER))) {
return nullptr;
}
}
else {
if (peconv::is_bad_read_ptr(idh, sizeof(IMAGE_DOS_HEADER))) {
return nullptr;
}
}
if (idh->e_magic != IMAGE_DOS_SIGNATURE) {
return nullptr;
}
const LONG kMaxOffset = 1024;
LONG pe_offset = idh->e_lfanew;
if (pe_offset > kMaxOffset) return nullptr;
IMAGE_NT_HEADERS32 *inh = (IMAGE_NT_HEADERS32 *)(pe_buffer + pe_offset);
if (buffer_size != 0) {
if (!peconv::validate_ptr((LPVOID)pe_buffer, buffer_size, (LPVOID)inh, sizeof(IMAGE_NT_HEADERS32))) {
return nullptr;
}
}
else {
if (peconv::is_bad_read_ptr(inh, sizeof(IMAGE_NT_HEADERS32))) {
return nullptr;
}
}
if (inh->Signature != IMAGE_NT_SIGNATURE) {
return nullptr;
}
return (BYTE*)inh;
}
IMAGE_NT_HEADERS32* peconv::get_nt_hdrs32(IN const BYTE *payload)
{
if (!payload) return nullptr;
BYTE *ptr = get_nt_hdrs(payload);
if (!ptr) return nullptr;
if (!is64bit(payload)) {
return (IMAGE_NT_HEADERS32*)ptr;
}
return nullptr;
}
IMAGE_NT_HEADERS64* peconv::get_nt_hdrs64(IN const BYTE *payload)
{
if (payload == nullptr) return nullptr;
BYTE *ptr = get_nt_hdrs(payload);
if (!ptr) return nullptr;
if (is64bit(payload)) {
return (IMAGE_NT_HEADERS64*)ptr;
}
return nullptr;
}
DWORD peconv::get_image_size(IN const BYTE *payload)
{
if (!get_nt_hdrs(payload)) {
return 0;
}
DWORD image_size = 0;
if (is64bit(payload)) {
IMAGE_NT_HEADERS64* nt64 = get_nt_hdrs64(payload);
image_size = nt64->OptionalHeader.SizeOfImage;
} else {
IMAGE_NT_HEADERS32* nt32 = get_nt_hdrs32(payload);
image_size = nt32->OptionalHeader.SizeOfImage;
}
return image_size;
}
bool peconv::update_image_size(IN OUT BYTE* payload, IN DWORD image_size)
{
if (!get_nt_hdrs(payload)) {
return false;
}
if (is64bit(payload)) {
IMAGE_NT_HEADERS64* nt64 = get_nt_hdrs64(payload);
nt64->OptionalHeader.SizeOfImage = image_size;
}
else {
IMAGE_NT_HEADERS32* nt32 = get_nt_hdrs32(payload);
nt32->OptionalHeader.SizeOfImage = image_size;
}
return true;
}
WORD peconv::get_nt_hdr_architecture(IN const BYTE *pe_buffer)
{
void *ptr = get_nt_hdrs(pe_buffer);
if (!ptr) return 0;
IMAGE_NT_HEADERS32 *inh = static_cast<IMAGE_NT_HEADERS32*>(ptr);
if (peconv::is_bad_read_ptr(inh, sizeof(IMAGE_NT_HEADERS32))) {
return 0;
}
return inh->OptionalHeader.Magic;
}
bool peconv::is64bit(IN const BYTE *pe_buffer)
{
WORD arch = get_nt_hdr_architecture(pe_buffer);
if (arch == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
return true;
}
return false;
}
IMAGE_DATA_DIRECTORY* peconv::get_directory_entry(IN const BYTE *pe_buffer, IN DWORD dir_id, IN bool allow_empty)
{
if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return nullptr;
BYTE* nt_headers = get_nt_hdrs((BYTE*)pe_buffer);
if (!nt_headers) return nullptr;
IMAGE_DATA_DIRECTORY* peDir = nullptr;
if (is64bit(pe_buffer)) {
IMAGE_NT_HEADERS64* nt_headers64 = (IMAGE_NT_HEADERS64*)nt_headers;
peDir = &(nt_headers64->OptionalHeader.DataDirectory[dir_id]);
}
else {
IMAGE_NT_HEADERS32* nt_headers64 = (IMAGE_NT_HEADERS32*)nt_headers;
peDir = &(nt_headers64->OptionalHeader.DataDirectory[dir_id]);
}
if (!allow_empty && peDir->VirtualAddress == NULL) {
return nullptr;
}
return peDir;
}
ULONGLONG peconv::get_image_base(IN const BYTE *pe_buffer)
{
bool is64b = is64bit(pe_buffer);
//update image base in the written content:
BYTE* payload_nt_hdr = get_nt_hdrs(pe_buffer);
if (!payload_nt_hdr) {
return 0;
}
ULONGLONG img_base = 0;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
img_base = payload_nt_hdr64->OptionalHeader.ImageBase;
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
img_base = static_cast<ULONGLONG>(payload_nt_hdr32->OptionalHeader.ImageBase);
}
return img_base;
}
DWORD peconv::get_entry_point_rva(IN const BYTE *pe_buffer)
{
//update image base in the written content:
BYTE* payload_nt_hdr = get_nt_hdrs(pe_buffer);
if (!payload_nt_hdr) {
return 0;
}
const bool is64b = is64bit(pe_buffer);
DWORD value = 0;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
value = payload_nt_hdr64->OptionalHeader.AddressOfEntryPoint;
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
value = payload_nt_hdr32->OptionalHeader.AddressOfEntryPoint;
}
return value;
}
bool peconv::update_entry_point_rva(IN OUT BYTE *pe_buffer, IN DWORD value)
{
bool is64b = is64bit(pe_buffer);
//update image base in the written content:
BYTE* payload_nt_hdr = get_nt_hdrs(pe_buffer);
if (!payload_nt_hdr) {
return false;
}
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
payload_nt_hdr64->OptionalHeader.AddressOfEntryPoint = value;
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
payload_nt_hdr32->OptionalHeader.AddressOfEntryPoint = value;
}
return true;
}
DWORD peconv::get_hdrs_size(IN const BYTE *pe_buffer)
{
bool is64b = is64bit(pe_buffer);
BYTE* payload_nt_hdr = get_nt_hdrs(pe_buffer);
if (!payload_nt_hdr) {
return 0;
}
DWORD hdrs_size = 0;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
hdrs_size = payload_nt_hdr64->OptionalHeader.SizeOfHeaders;
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
hdrs_size = payload_nt_hdr32->OptionalHeader.SizeOfHeaders;
}
return hdrs_size;
}
bool peconv::update_image_base(IN OUT BYTE* payload, IN ULONGLONG destImageBase)
{
bool is64b = is64bit(payload);
BYTE* payload_nt_hdr = get_nt_hdrs(payload);
if (!payload_nt_hdr) {
return false;
}
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
payload_nt_hdr64->OptionalHeader.ImageBase = (ULONGLONG)destImageBase;
}
else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
payload_nt_hdr32->OptionalHeader.ImageBase = (DWORD)destImageBase;
}
return true;
}
template <typename IMAGE_NT_HEADERS_T>
inline const IMAGE_FILE_HEADER* fetch_file_hdr(IN const BYTE* payload, IN const size_t buffer_size, IN const IMAGE_NT_HEADERS_T *payload_nt_hdr)
{
if (!payload || !payload_nt_hdr) return nullptr;
const IMAGE_FILE_HEADER *fileHdr = &(payload_nt_hdr->FileHeader);
if (!validate_ptr((const LPVOID)payload, buffer_size, (const LPVOID)fileHdr, sizeof(IMAGE_FILE_HEADER))) {
return nullptr;
}
return fileHdr;
}
const IMAGE_FILE_HEADER* peconv::get_file_hdr(IN const BYTE* payload, IN const size_t buffer_size)
{
if (!payload) return nullptr;
BYTE* payload_nt_hdr = get_nt_hdrs(payload, buffer_size);
if (!payload_nt_hdr) {
return nullptr;
}
if (is64bit(payload)) {
return fetch_file_hdr(payload, buffer_size, (IMAGE_NT_HEADERS64*)payload_nt_hdr);
}
return fetch_file_hdr(payload, buffer_size, (IMAGE_NT_HEADERS32*)payload_nt_hdr);
}
template <typename IMAGE_NT_HEADERS_T>
inline const LPVOID fetch_opt_hdr(IN const BYTE* payload, IN const size_t buffer_size, IN const IMAGE_NT_HEADERS_T *payload_nt_hdr)
{
if (!payload) return nullptr;
const IMAGE_FILE_HEADER *fileHdr = fetch_file_hdr<IMAGE_NT_HEADERS_T>(payload, buffer_size, payload_nt_hdr);
if (!fileHdr) {
return nullptr;
}
const LPVOID opt_hdr = (const LPVOID) &(payload_nt_hdr->OptionalHeader);
const size_t opt_size = fileHdr->SizeOfOptionalHeader;
if (!validate_ptr((const LPVOID)payload, buffer_size, opt_hdr, opt_size)) {
return nullptr;
}
return opt_hdr;
}
LPVOID peconv::get_optional_hdr(IN const BYTE* payload, IN const size_t buffer_size)
{
if (!payload) return nullptr;
BYTE* payload_nt_hdr = get_nt_hdrs(payload, buffer_size);
const IMAGE_FILE_HEADER* fileHdr = get_file_hdr(payload, buffer_size);
if (!payload_nt_hdr || !fileHdr) {
return nullptr;
}
if (is64bit(payload)) {
return fetch_opt_hdr<IMAGE_NT_HEADERS64>(payload,buffer_size, (IMAGE_NT_HEADERS64*)payload_nt_hdr);
}
return fetch_opt_hdr<IMAGE_NT_HEADERS32>(payload, buffer_size, (IMAGE_NT_HEADERS32*)payload_nt_hdr);
}
template <typename IMAGE_NT_HEADERS_T>
inline LPVOID fetch_section_hdrs_ptr(IN const BYTE* payload, IN const size_t buffer_size, IN const IMAGE_NT_HEADERS_T *payload_nt_hdr)
{
const IMAGE_FILE_HEADER *fileHdr = fetch_file_hdr<IMAGE_NT_HEADERS_T>(payload, buffer_size, payload_nt_hdr);
if (!fileHdr) {
return nullptr;
}
const size_t opt_size = fileHdr->SizeOfOptionalHeader;
BYTE* opt_hdr = (BYTE*)fetch_opt_hdr(payload, buffer_size, payload_nt_hdr);
if (!validate_ptr((const LPVOID)payload, buffer_size, opt_hdr, opt_size)) {
return nullptr;
}
//sections headers starts right after the end of the optional header
return (LPVOID)(opt_hdr + opt_size);
}
size_t peconv::get_sections_count(IN const BYTE* payload, IN const size_t buffer_size)
{
const IMAGE_FILE_HEADER* fileHdr = get_file_hdr(payload, buffer_size);
if (!fileHdr) {
return 0;
}
return fileHdr->NumberOfSections;
}
bool peconv::is_valid_sections_hdr_offset(IN const BYTE* buffer, IN const size_t buffer_size)
{
size_t sec_count = peconv::get_sections_count(buffer, buffer_size);
if (sec_count == 0) {
//no sections found - a valid PE should have at least one section
return false;
}
PIMAGE_SECTION_HEADER last_hdr = get_section_hdr(buffer, buffer_size, sec_count - 1);
if (!last_hdr) {
//could not fetch the last section
return false;
}
return true;
}
PIMAGE_SECTION_HEADER peconv::get_section_hdr(IN const BYTE* payload, IN const size_t buffer_size, IN size_t section_num)
{
if (!payload) return nullptr;
const size_t sections_count = peconv::get_sections_count(payload, buffer_size);
if (section_num >= sections_count) {
return nullptr;
}
LPVOID nt_hdrs = peconv::get_nt_hdrs(payload, buffer_size);
if (!nt_hdrs) return nullptr; //this should never happened, because the get_sections_count did not fail
LPVOID secptr = nullptr;
//get the beginning of sections headers:
if (is64bit(payload)) {
secptr = fetch_section_hdrs_ptr<IMAGE_NT_HEADERS64>(payload, buffer_size, (IMAGE_NT_HEADERS64*)nt_hdrs);
}
else {
secptr = fetch_section_hdrs_ptr<IMAGE_NT_HEADERS32>(payload, buffer_size, (IMAGE_NT_HEADERS32*)nt_hdrs);
}
//get the section header of given number:
PIMAGE_SECTION_HEADER next_sec = (PIMAGE_SECTION_HEADER)(
(ULONGLONG)secptr + (IMAGE_SIZEOF_SECTION_HEADER * section_num)
);
//validate pointer:
if (!validate_ptr((const LPVOID) payload, buffer_size, (const LPVOID) next_sec, sizeof(IMAGE_SECTION_HEADER))) {
return nullptr;
}
return next_sec;
}
WORD peconv::get_file_characteristics(IN const BYTE* payload)
{
if (!payload) return 0;
bool is64b = is64bit(payload);
BYTE* payload_nt_hdr = get_nt_hdrs(payload);
if (!payload_nt_hdr) {
return 0;
}
IMAGE_FILE_HEADER *fileHdr = nullptr;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
fileHdr = &(payload_nt_hdr64->FileHeader);
}
else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
fileHdr = &(payload_nt_hdr32->FileHeader);
}
return fileHdr->Characteristics;
}
bool peconv::is_module_dll(IN const BYTE* payload)
{
if (!payload) return false;
WORD charact = get_file_characteristics(payload);
return ((charact & IMAGE_FILE_DLL) != 0);
}
bool peconv::is_dot_net(BYTE *pe_buffer, size_t pe_buffer_size)
{
if (!pe_buffer) return false;
IMAGE_DATA_DIRECTORY* dotnet_ptr = peconv::get_directory_entry(pe_buffer, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR, false);
if (!dotnet_ptr) return false;
if (peconv::get_dotnet_hdr(pe_buffer, pe_buffer_size, dotnet_ptr)) {
return true;
}
return false;
}
WORD peconv::get_dll_characteristics(IN const BYTE* payload)
{
if (!payload) return 0;
bool is64b = is64bit(payload);
BYTE* payload_nt_hdr = get_nt_hdrs(payload);
if (!payload_nt_hdr) {
return 0;
}
WORD charact = 0;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
charact = payload_nt_hdr64->OptionalHeader.DllCharacteristics;
}
else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
charact = payload_nt_hdr32->OptionalHeader.DllCharacteristics;
}
return charact;
}
bool peconv::set_subsystem(IN OUT BYTE* payload, IN WORD subsystem)
{
if (!payload) return false;
bool is64b = is64bit(payload);
BYTE* payload_nt_hdr = get_nt_hdrs(payload);
if (!payload_nt_hdr) {
return false;
}
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
payload_nt_hdr64->OptionalHeader.Subsystem = subsystem;
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
payload_nt_hdr32->OptionalHeader.Subsystem = subsystem;
}
return true;
}
WORD peconv::get_subsystem(IN const BYTE* payload)
{
if (!payload) return 0;
BYTE* payload_nt_hdr = get_nt_hdrs(payload);
if (payload_nt_hdr == NULL) {
return 0;
}
const bool is64b = is64bit(payload);
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
return payload_nt_hdr64->OptionalHeader.Subsystem;
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
return payload_nt_hdr32->OptionalHeader.Subsystem;
}
}
bool peconv::has_relocations(IN const BYTE *pe_buffer)
{
IMAGE_DATA_DIRECTORY* relocDir = get_directory_entry(pe_buffer, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if (!relocDir) {
return false;
}
return true;
}
IMAGE_EXPORT_DIRECTORY* peconv::get_export_directory(IN HMODULE modulePtr)
{
return get_type_directory<IMAGE_EXPORT_DIRECTORY>(modulePtr, IMAGE_DIRECTORY_ENTRY_EXPORT);
}
IMAGE_COR20_HEADER * peconv::get_dotnet_hdr(IN const BYTE* module, IN size_t const module_size, IN const IMAGE_DATA_DIRECTORY * dotNetDir)
{
DWORD rva = dotNetDir->VirtualAddress;
DWORD hdr_size = dotNetDir->Size;
if (!peconv::validate_ptr(module, module_size, module + rva, hdr_size)) {
return nullptr;
}
IMAGE_COR20_HEADER *dnet_hdr = (IMAGE_COR20_HEADER*)(module + rva);
if (!peconv::validate_ptr(module, module_size, module + dnet_hdr->MetaData.VirtualAddress, dnet_hdr->MetaData.Size)) {
return nullptr;
}
DWORD* signature_ptr = (DWORD*)(module + dnet_hdr->MetaData.VirtualAddress);
const DWORD dotNetSign = 0x424A5342;
if (*signature_ptr != dotNetSign) {
//invalid header
return nullptr;
}
return dnet_hdr;
}
template <typename IMAGE_NT_HEADERS_T>
DWORD* _get_sec_alignment_ptr(const BYTE* modulePtr, bool is_raw)
{
IMAGE_NT_HEADERS_T* hdrs = reinterpret_cast<IMAGE_NT_HEADERS_T*>(peconv::get_nt_hdrs(modulePtr));
if (!hdrs) return nullptr;
if (is_raw) {
return &hdrs->OptionalHeader.FileAlignment;
}
return &hdrs->OptionalHeader.SectionAlignment;
}
DWORD peconv::get_sec_alignment(IN const BYTE* modulePtr, IN bool is_raw)
{
DWORD* alignment = 0;
if (peconv::is64bit(modulePtr)) {
alignment = _get_sec_alignment_ptr<IMAGE_NT_HEADERS64>(modulePtr, is_raw);
} else {
alignment = _get_sec_alignment_ptr<IMAGE_NT_HEADERS32>(modulePtr, is_raw);
}
if (!alignment) return 0;
return *alignment;
}
bool peconv::set_sec_alignment(IN OUT BYTE* modulePtr, IN bool is_raw, IN DWORD new_alignment)
{
DWORD* alignment = 0;
if (peconv::is64bit(modulePtr)) {
alignment = _get_sec_alignment_ptr<IMAGE_NT_HEADERS64>(modulePtr, is_raw);
}
else {
alignment = _get_sec_alignment_ptr<IMAGE_NT_HEADERS32>(modulePtr, is_raw);
}
if (!alignment) return false;
*alignment = new_alignment;
return true;
}
DWORD peconv::get_virtual_sec_size(IN const BYTE* pe_hdr, IN const PIMAGE_SECTION_HEADER sec_hdr, IN bool rounded)
{
if (!pe_hdr || !sec_hdr) {
return 0;
}
if (!rounded) {
return sec_hdr->Misc.VirtualSize;;
}
//TODO: calculate real size, round up to Virtual Alignment
DWORD alignment = peconv::get_sec_alignment((const PBYTE)pe_hdr, false);
DWORD vsize = sec_hdr->Misc.VirtualSize;
DWORD units = vsize / alignment;
if ((vsize % alignment) > 0) units++;
vsize = units * alignment;
DWORD image_size = peconv::get_image_size(pe_hdr);
//if it is bigger than the image size, use the size from the headers
if ((sec_hdr->VirtualAddress + vsize) > image_size) {
vsize = sec_hdr->Misc.VirtualSize;
}
return vsize;
}
PIMAGE_SECTION_HEADER peconv::get_last_section(IN const PBYTE pe_buffer, IN size_t pe_size, IN bool is_raw)
{
SIZE_T module_end = peconv::get_hdrs_size(pe_buffer);
const size_t sections_count = peconv::get_sections_count(pe_buffer, pe_size);
if (sections_count == 0) {
return nullptr;
}
PIMAGE_SECTION_HEADER last_sec = nullptr;
//walk through sections
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER sec = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (!sec) break;
size_t new_end = is_raw ? (sec->PointerToRawData + sec->SizeOfRawData) : (sec->VirtualAddress + sec->Misc.VirtualSize);
if (new_end > module_end) {
module_end = new_end;
last_sec = sec;
}
}
return last_sec;
}
DWORD peconv::calc_pe_size(IN const PBYTE pe_buffer, IN size_t pe_size, IN bool is_raw)
{
DWORD module_end = peconv::get_hdrs_size(pe_buffer);
const size_t sections_count = peconv::get_sections_count(pe_buffer, pe_size);
if (sections_count == 0) {
return module_end;
}
//walk through sections
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER sec = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (!sec) break;
DWORD new_end = is_raw ? (sec->PointerToRawData + sec->SizeOfRawData) : (sec->VirtualAddress + sec->Misc.VirtualSize);
if (new_end > module_end) module_end = new_end;
}
return module_end;
}
bool peconv::is_valid_sectons_alignment(IN const BYTE* payload, IN const SIZE_T payload_size, IN bool is_raw)
{
if (payload == NULL) return false;
const DWORD my_align = peconv::get_sec_alignment(payload, is_raw);
if (my_align == 0) {
#ifdef _DEBUG
std::cout << "Section alignment cannot be 0\n";
#endif
return false;
}
const size_t sections_count = peconv::get_sections_count(payload, payload_size);
if (sections_count == 0) {
//no sections
return false;
}
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER next_sec = peconv::get_section_hdr(payload, payload_size, i);
if (!next_sec) return false; //the number of the sections in header is out of scope
const DWORD next_sec_addr = is_raw ? (next_sec->PointerToRawData) : (next_sec->VirtualAddress);
SIZE_T sec_size = is_raw ? next_sec->SizeOfRawData : next_sec->Misc.VirtualSize;
if (sec_size == 0) continue;
if (next_sec->Misc.VirtualSize == 0) {
continue; // if the VirtualSize == 0 the section will not be mapped anyways
}
if (next_sec_addr == 0) {
//if cannot be 0 if the size is not 0
return false;
}
//check only if raw_align is non-zero
if (my_align && next_sec_addr % my_align != 0) {
#ifdef _DEBUG
std::cout << "Section is misaligned\n";
#endif
return false; //misaligned
}
}
return true;
}

View File

@@ -0,0 +1,122 @@
#include "peconv/pe_loader.h"
#include "peconv/relocate.h"
#include "peconv/imports_loader.h"
#include "peconv/buffer_util.h"
#include "peconv/function_resolver.h"
#include "peconv/exports_lookup.h"
#include <iostream>
using namespace peconv;
namespace peconv {
BYTE* load_no_sec_pe(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, bool executable)
{
ULONGLONG desired_base = 0;
size_t out_size = (r_size < PAGE_SIZE) ? PAGE_SIZE : r_size;
if (executable) {
desired_base = get_image_base(dllRawData);
out_size = peconv::get_image_size(dllRawData);
}
DWORD protect = (executable) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
BYTE* mappedPE = peconv::alloc_pe_buffer(out_size, protect, desired_base);
if (!mappedPE) {
return NULL;
}
memcpy(mappedPE, dllRawData, r_size);
v_size = out_size;
return mappedPE;
}
};
BYTE* peconv::load_pe_module(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, bool executable, bool relocate)
{
if (!peconv::get_nt_hdrs(dllRawData, r_size)) {
return NULL;
}
if (peconv::get_sections_count(dllRawData, r_size) == 0) {
return load_no_sec_pe(dllRawData, r_size, v_size, executable);
}
// by default, allow to load the PE at any base:
ULONGLONG desired_base = NULL;
// if relocating is required, but the PE has no relocation table...
if (relocate && !has_relocations(dllRawData)) {
// ...enforce loading the PE image at its default base (so that it will need no relocations)
desired_base = get_image_base(dllRawData);
}
// load a virtual image of the PE file at the desired_base address (random if desired_base is NULL):
BYTE *mappedDLL = pe_raw_to_virtual(dllRawData, r_size, v_size, executable, desired_base);
if (mappedDLL) {
//if the image was loaded at its default base, relocate_module will return always true (because relocating is already done)
if (relocate && !relocate_module(mappedDLL, v_size, (ULONGLONG)mappedDLL)) {
// relocating was required, but it failed - thus, the full PE image is useless
printf("Could not relocate the module!");
free_pe_buffer(mappedDLL, v_size);
mappedDLL = NULL;
}
} else {
printf("Could not allocate memory at the desired base!\n");
}
return mappedDLL;
}
BYTE* peconv::load_pe_module(const char *filename, OUT size_t &v_size, bool executable, bool relocate)
{
size_t r_size = 0;
BYTE *dllRawData = load_file(filename, r_size);
if (!dllRawData) {
#ifdef _DEBUG
std::cerr << "Cannot load the file: " << filename << std::endl;
#endif
return NULL;
}
BYTE* mappedPE = load_pe_module(dllRawData, r_size, v_size, executable, relocate);
free_pe_buffer(dllRawData);
return mappedPE;
}
BYTE* peconv::load_pe_executable(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, t_function_resolver* import_resolver)
{
BYTE* loaded_pe = load_pe_module(dllRawData, r_size, v_size, true, true);
if (!loaded_pe) {
printf("[-] Loading failed!\n");
return NULL;
}
#if _DEBUG
printf("Loaded at: %p\n", loaded_pe);
#endif
if (has_valid_import_table(loaded_pe, v_size)) {
if (!load_imports(loaded_pe, import_resolver)) {
printf("[-] Loading imports failed!");
free_pe_buffer(loaded_pe, v_size);
return NULL;
}
}
else {
printf("[-] PE doesn't have a valid Import Table!\n");
}
return loaded_pe;
}
BYTE* peconv::load_pe_executable(const char *my_path, OUT size_t &v_size, t_function_resolver* import_resolver)
{
#if _DEBUG
printf("Module: %s\n", my_path);
#endif
BYTE* loaded_pe = load_pe_module(my_path, v_size, true, true);
if (!loaded_pe) {
printf("Loading failed!\n");
return NULL;
}
#if _DEBUG
printf("Loaded at: %p\n", loaded_pe);
#endif
if (!load_imports(loaded_pe, import_resolver)) {
printf("[-] Loading imports failed!");
free_pe_buffer(loaded_pe, v_size);
return NULL;
}
return loaded_pe;
}

View File

@@ -0,0 +1,196 @@
#include "peconv/pe_mode_detector.h"
#include "peconv/util.h"
#include "peconv/imports_loader.h"
#include "peconv/relocate.h"
#ifdef _DEBUG
#include <iostream>
#endif
// Check if gaps between sections are typical for Virtual Alignment.
// Returns true if confirmed, false if not confirmed. False result can also mean that data was invalid/insufficient to decide.
bool is_virtual_padding(const BYTE* pe_buffer, size_t pe_size)
{
const size_t r_align = peconv::get_sec_alignment((PBYTE)pe_buffer, true);
size_t sections_count = peconv::get_sections_count(pe_buffer, pe_size);
if (sections_count < 2) return false;
bool is_valid_padding = false;
for (size_t i = 1; i < sections_count; i += 2) {
PIMAGE_SECTION_HEADER sec1 = peconv::get_section_hdr(pe_buffer, pe_size, i-1);
PIMAGE_SECTION_HEADER sec2 = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (!sec1 || !sec2) continue; //skip if fetching any of the sections failed
if (sec1->SizeOfRawData == 0) continue; //skip empty sections
const DWORD sec1_end_offset = sec1->VirtualAddress + sec1->SizeOfRawData;
if (sec2->VirtualAddress == sec1_end_offset) continue;
if (sec2->VirtualAddress < sec1_end_offset) {
//std::cout << "Invalid size of the section: " << std::hex << sec2->VirtualAddress << " vs "<< sec1_end_offset << std::endl;
return false;
}
const size_t diff = sec2->VirtualAddress - sec1_end_offset;
if (diff < r_align) continue; //to small to determine
BYTE* sec1_end_ptr = (BYTE*)((ULONGLONG)pe_buffer + sec1_end_offset);
if (!peconv::validate_ptr((const LPVOID)pe_buffer, pe_size, sec1_end_ptr, diff)) {
//std::cout << "Invalid pointer to the section\n";
return false;
}
if (peconv::is_padding(sec1_end_ptr, diff, 0)) {
is_valid_padding = true;
}
else {
return false;
}
}
return is_valid_padding;
}
// Check if the gap between the end of headers and the first section is typical for Virtual Alignment.
// Returns true if confirmed, false if not confirmed. False result can also mean that data was invalid/insufficient to decide.
bool is_hdr_virtual_align(const BYTE* pe_buffer, size_t pe_size)
{
const size_t v_align = peconv::get_sec_alignment((PBYTE)pe_buffer, false);
if (peconv::get_hdrs_size(pe_buffer) >= v_align) {
//undetermined for such case
return false;
}
//walk through sections and check their sizes
size_t sections_count = peconv::get_sections_count(pe_buffer, pe_size);
if (sections_count == 0) return false;
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER sec = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (!sec || sec->PointerToRawData == 0 || sec->SizeOfRawData == 0) {
continue; // check next
}
if (sec->PointerToRawData >= v_align) continue;
size_t diff = v_align - sec->PointerToRawData;
BYTE* sec_raw_ptr = (BYTE*)((ULONGLONG)pe_buffer + sec->PointerToRawData);
if (!peconv::validate_ptr((const LPVOID)pe_buffer, pe_size, sec_raw_ptr, diff)) {
return false;
}
if (peconv::is_padding(sec_raw_ptr, diff, 0)) {
return true;
}
}
return false;
}
bool sec_hdrs_erased(IN const BYTE* pe_buffer, IN size_t pe_size, bool is_raw)
{
const size_t count = peconv::get_sections_count(pe_buffer, pe_size);
for (size_t i = 0; i < count; i++) {
const IMAGE_SECTION_HEADER* hdr = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (!hdr) continue;
if (is_raw) {
if (hdr->PointerToRawData != 0) return false;
}
else {
if (hdr->VirtualAddress != 0) return false;
}
}
return true;
}
bool peconv::is_pe_raw_eq_virtual(IN const BYTE* pe_buffer, IN size_t pe_size)
{
const size_t count = peconv::get_sections_count(pe_buffer, pe_size);
for (size_t i = 0; i < count; i++) {
const IMAGE_SECTION_HEADER* hdr = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (!hdr) continue;
if (hdr->VirtualAddress != hdr->PointerToRawData) {
return false;
}
}
return true;
}
bool is_pe_mapped(IN const BYTE* pe_buffer, IN size_t pe_size)
{
size_t v_score = 0;
if (peconv::has_valid_import_table((const PBYTE)pe_buffer, pe_size)) {
#ifdef _DEBUG
std::cout << "Valid Import Table found" << std::endl;
#endif
v_score++;
}
if (peconv::has_valid_relocation_table((const PBYTE)pe_buffer, pe_size)) {
#ifdef _DEBUG
std::cout << "Valid Relocations Table found" << std::endl;
#endif
v_score++;
}
if (is_hdr_virtual_align(pe_buffer, pe_size)) {
#ifdef _DEBUG
std::cout << "Header virtual align OK" << std::endl;
#endif
v_score++;
}
#ifdef _DEBUG
std::cout << "TOTAL v_score: " << std::dec << v_score << std::endl;
#endif
if (v_score > 0) {
return true;
}
return false;
}
bool peconv::is_pe_raw(IN const BYTE* pe_buffer, IN size_t pe_size)
{
if (peconv::get_sections_count(pe_buffer, pe_size) == 0) {
return true;
}
if (is_pe_mapped(pe_buffer, pe_size)) {
// it has artefacts typical for a PE in a virtual alignment
return false;
}
if (sec_hdrs_erased(pe_buffer, pe_size, true)) {
#ifdef _DEBUG
std::cout << "Raw alignment is erased\n";
#endif
// the raw alignment of the sections is erased
return false;
}
return true;
}
// checks if any sections has been expanded in the memory
bool peconv::is_pe_expanded(IN const BYTE* pe_buffer, IN size_t pe_size)
{
//walk through sections and check their sizes
size_t sections_count = peconv::get_sections_count(pe_buffer, pe_size);
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER sec = peconv::get_section_hdr(pe_buffer, pe_size, i);
if (is_section_expanded(pe_buffer, pe_size, sec)) {
return true;
}
}
return false;
}
// checks if the section's content in memory is bigger than in the raw format
bool peconv::is_section_expanded(IN const BYTE* pe_buffer, IN size_t pe_size, IN const PIMAGE_SECTION_HEADER sec)
{
if (!sec) return false;
size_t sec_vsize = peconv::get_virtual_sec_size(pe_buffer, sec, true);
size_t sec_rsize = sec->SizeOfRawData;
if (sec_rsize >= sec_vsize) return false;
size_t diff = sec_vsize - sec_rsize;
BYTE* sec_raw_end_ptr = (BYTE*)((ULONGLONG)pe_buffer + sec->VirtualAddress + sec_rsize);
if (!peconv::validate_ptr((const LPVOID)pe_buffer, pe_size, sec_raw_end_ptr, diff)) {
return false;
}
if (!is_padding(sec_raw_end_ptr, diff, 0)) {
//this is not padding: non-zero content detected
return true;
}
return false;
}

View File

@@ -0,0 +1,140 @@
#include "peconv/pe_raw_to_virtual.h"
#include "peconv/util.h"
#include "peconv/pe_hdrs_helper.h"
#include <iostream>
using namespace peconv;
// Map raw PE into virtual memory of local process:
bool sections_raw_to_virtual(IN const BYTE* payload, IN SIZE_T payloadSize, OUT BYTE* destBuffer, IN SIZE_T destBufferSize)
{
if (!payload || !destBuffer) return false;
BYTE* payload_nt_hdr = get_nt_hdrs(payload, payloadSize);
if (payload_nt_hdr == NULL) {
std::cerr << "Invalid payload: " << std::hex << (ULONGLONG) payload << std::endl;
return false;
}
const bool is64b = is64bit(payload);
IMAGE_FILE_HEADER *fileHdr = NULL;
DWORD hdrsSize = 0;
LPVOID secptr = NULL;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*)payload_nt_hdr;
fileHdr = &(payload_nt_hdr64->FileHeader);
hdrsSize = payload_nt_hdr64->OptionalHeader.SizeOfHeaders;
secptr = (LPVOID)((ULONGLONG)&(payload_nt_hdr64->OptionalHeader) + fileHdr->SizeOfOptionalHeader);
}
else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*)payload_nt_hdr;
fileHdr = &(payload_nt_hdr32->FileHeader);
hdrsSize = payload_nt_hdr32->OptionalHeader.SizeOfHeaders;
secptr = (LPVOID)((ULONGLONG)&(payload_nt_hdr32->OptionalHeader) + fileHdr->SizeOfOptionalHeader);
}
DWORD first_raw = 0;
//copy all the sections, one by one:
SIZE_T raw_end = 0;
for (WORD i = 0; i < fileHdr->NumberOfSections; i++) {
PIMAGE_SECTION_HEADER next_sec = (PIMAGE_SECTION_HEADER)((ULONGLONG)secptr + (IMAGE_SIZEOF_SECTION_HEADER * i));
if (!validate_ptr((const LPVOID)payload, destBufferSize, next_sec, IMAGE_SIZEOF_SECTION_HEADER)) {
return false;
}
if (next_sec->PointerToRawData == 0 || next_sec->SizeOfRawData == 0) {
continue; //skipping empty
}
LPVOID section_mapped = destBuffer + next_sec->VirtualAddress;
LPVOID section_raw_ptr = (BYTE*)payload + next_sec->PointerToRawData;
SIZE_T sec_size = next_sec->SizeOfRawData;
raw_end = next_sec->SizeOfRawData + next_sec->PointerToRawData;
if ((next_sec->VirtualAddress + sec_size) > destBufferSize) {
std::cerr << "[!] Virtual section size is out ouf bounds: " << std::hex << sec_size << std::endl;
sec_size = (destBufferSize > next_sec->VirtualAddress) ? SIZE_T(destBufferSize - next_sec->VirtualAddress) : 0;
std::cerr << "[!] Truncated to maximal size: " << std::hex << sec_size << ", buffer size:" << destBufferSize << std::endl;
}
if (next_sec->VirtualAddress >= destBufferSize && sec_size != 0) {
std::cerr << "[-] VirtualAddress of section is out ouf bounds: " << std::hex << next_sec->VirtualAddress << std::endl;
return false;
}
if (next_sec->PointerToRawData + sec_size > destBufferSize) {
std::cerr << "[-] Raw section size is out ouf bounds: " << std::hex << sec_size << std::endl;
return false;
}
// validate source:
if (!validate_ptr((const LPVOID)payload, payloadSize, section_raw_ptr, sec_size)) {
std::cerr << "[-] Section " << i << ": out ouf bounds, skipping... " << std::endl;
continue;
}
// validate destination:
if (!peconv::validate_ptr(destBuffer, destBufferSize, section_mapped, sec_size)) {
std::cerr << "[-] Section " << i << ": out ouf bounds, skipping... " << std::endl;
continue;
}
memcpy(section_mapped, section_raw_ptr, sec_size);
if (first_raw == 0 || (next_sec->PointerToRawData < first_raw)) {
first_raw = next_sec->PointerToRawData;
}
}
//copy payload's headers:
if (hdrsSize == 0) {
hdrsSize= first_raw;
#ifdef _DEBUG
std::cout << "hdrsSize not filled, using calculated size: " << std::hex << hdrsSize << "\n";
#endif
}
if (!validate_ptr((const LPVOID)payload, destBufferSize, (const LPVOID)payload, hdrsSize)) {
return false;
}
memcpy(destBuffer, payload, hdrsSize);
return true;
}
BYTE* peconv::pe_raw_to_virtual(
IN const BYTE* payload,
IN size_t in_size,
OUT size_t &out_size,
IN OPTIONAL bool executable,
IN OPTIONAL ULONGLONG desired_base
)
{
//check payload:
BYTE* nt_hdr = get_nt_hdrs(payload);
if (nt_hdr == NULL) {
std::cerr << "Invalid payload: " << std::hex << (ULONGLONG) payload << std::endl;
return nullptr;
}
DWORD payloadImageSize = 0;
bool is64 = is64bit(payload);
if (is64) {
IMAGE_NT_HEADERS64* payload_nt_hdr = (IMAGE_NT_HEADERS64*)nt_hdr;
payloadImageSize = payload_nt_hdr->OptionalHeader.SizeOfImage;
}
else {
IMAGE_NT_HEADERS32* payload_nt_hdr = (IMAGE_NT_HEADERS32*)nt_hdr;
payloadImageSize = payload_nt_hdr->OptionalHeader.SizeOfImage;
}
DWORD protect = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
//first we will prepare the payload image in the local memory, so that it will be easier to edit it, apply relocations etc.
//when it will be ready, we will copy it into the space reserved in the target process
BYTE* localCopyAddress = alloc_pe_buffer(payloadImageSize, protect, desired_base);
if (localCopyAddress == NULL) {
std::cerr << "Could not allocate memory in the current process" << std::endl;
return NULL;
}
//printf("Allocated local memory: %p size: %x\n", localCopyAddress, payloadImageSize);
if (!sections_raw_to_virtual(payload, in_size, (BYTE*)localCopyAddress, payloadImageSize)) {
std::cerr << "Could not copy PE file" << std::endl;
return NULL;
}
out_size = payloadImageSize;
return localCopyAddress;
}

View File

@@ -0,0 +1,213 @@
#include "peconv/pe_virtual_to_raw.h"
#include "peconv/util.h"
#include "peconv/pe_hdrs_helper.h"
#include "peconv/relocate.h"
#include <iostream>
using namespace peconv;
bool sections_virtual_to_raw(BYTE* payload, SIZE_T payload_size, OUT BYTE* destAddress, OUT SIZE_T *raw_size_ptr)
{
if (!payload || !destAddress) return false;
BYTE* payload_nt_hdr = get_nt_hdrs(payload, payload_size);
if (payload_nt_hdr == NULL) {
std::cerr << "Invalid payload: " << std::hex << (ULONGLONG) payload << std::endl;
return false;
}
const bool is64b = is64bit(payload);
IMAGE_FILE_HEADER *fileHdr = NULL;
DWORD hdrsSize = 0;
LPVOID secptr = NULL;
if (is64b) {
IMAGE_NT_HEADERS64* payload_nt_hdr64 = (IMAGE_NT_HEADERS64*) payload_nt_hdr;
fileHdr = &(payload_nt_hdr64->FileHeader);
hdrsSize = payload_nt_hdr64->OptionalHeader.SizeOfHeaders;
secptr = (LPVOID)((ULONGLONG)&(payload_nt_hdr64->OptionalHeader) + fileHdr->SizeOfOptionalHeader);
} else {
IMAGE_NT_HEADERS32* payload_nt_hdr32 = (IMAGE_NT_HEADERS32*) payload_nt_hdr;
fileHdr = &(payload_nt_hdr32->FileHeader);
hdrsSize = payload_nt_hdr32->OptionalHeader.SizeOfHeaders;
secptr = (LPVOID)((ULONGLONG)&(payload_nt_hdr32->OptionalHeader) + fileHdr->SizeOfOptionalHeader);
}
//copy all the sections, one by one:
#ifdef _DEBUG
std::cout << "Coping sections:" << std::endl;
#endif
DWORD first_raw = 0;
SIZE_T raw_end = hdrsSize;
for (WORD i = 0; i < fileHdr->NumberOfSections; i++) {
PIMAGE_SECTION_HEADER next_sec = (PIMAGE_SECTION_HEADER)((ULONGLONG)secptr + (IMAGE_SIZEOF_SECTION_HEADER * i));
if (!validate_ptr(payload, payload_size, next_sec, IMAGE_SIZEOF_SECTION_HEADER)) {
return false;
}
LPVOID section_mapped = (BYTE*) payload + next_sec->VirtualAddress;
LPVOID section_raw_ptr = destAddress + next_sec->PointerToRawData;
SIZE_T sec_size = next_sec->SizeOfRawData;
size_t new_end = sec_size + next_sec->PointerToRawData;
if (new_end > raw_end) raw_end = new_end;
if ((next_sec->VirtualAddress + sec_size) > payload_size) {
std::cerr << "[!] Virtual section size is out ouf bounds: " << std::hex << sec_size << std::endl;
sec_size = (payload_size > next_sec->VirtualAddress) ? SIZE_T(payload_size - next_sec->VirtualAddress) : 0;
std::cerr << "[!] Truncated to maximal size: " << std::hex << sec_size << ", buffer size: " << payload_size << std::endl;
}
if (next_sec->VirtualAddress > payload_size && sec_size != 0) {
std::cerr << "[-] VirtualAddress of section is out ouf bounds: " << std::hex << next_sec->VirtualAddress << std::endl;
return false;
}
if (next_sec->PointerToRawData + sec_size > payload_size) {
std::cerr << "[-] Raw section size is out ouf bounds: " << std::hex << sec_size << std::endl;
return false;
}
#ifdef _DEBUG
std::cout << "[+] " << next_sec->Name << " to: " << std::hex << section_raw_ptr << std::endl;
#endif
//validate source:
if (!peconv::validate_ptr(payload, payload_size, section_mapped, sec_size)) {
std::cerr << "[-] Section " << i << ": out ouf bounds, skipping... " << std::endl;
continue;
}
//validate destination:
if (!peconv::validate_ptr(destAddress, payload_size, section_raw_ptr, sec_size)) {
std::cerr << "[-] Section " << i << ": out ouf bounds, skipping... " << std::endl;
continue;
}
memcpy(section_raw_ptr, section_mapped, sec_size);
if (first_raw == 0 || (next_sec->PointerToRawData < first_raw)) {
first_raw = next_sec->PointerToRawData;
}
}
if (raw_end > payload_size) raw_end = payload_size;
if (raw_size_ptr != NULL) {
(*raw_size_ptr) = raw_end;
}
//copy payload's headers:
if (hdrsSize == 0) {
hdrsSize = first_raw;
#ifdef _DEBUG
std::cout << "hdrsSize not filled, using calculated size: " << std::hex << hdrsSize << "\n";
#endif
}
if (!validate_ptr(payload, payload_size, payload, hdrsSize)) {
return false;
}
memcpy(destAddress, payload, hdrsSize);
return true;
}
BYTE* peconv::pe_virtual_to_raw(
IN BYTE* payload,
IN size_t in_size,
IN ULONGLONG loadBase,
OUT size_t &out_size,
IN OPTIONAL bool rebuffer
)
{
BYTE* out_buf = (BYTE*)alloc_pe_buffer(in_size, PAGE_READWRITE);
if (out_buf == NULL) return NULL; //could not allocate output buffer
BYTE* in_buf = payload;
if (rebuffer) {
in_buf = (BYTE*) alloc_pe_buffer(in_size, PAGE_READWRITE);
if (in_buf == NULL) {
free_pe_buffer(out_buf, in_size);
return NULL;
}
memcpy(in_buf, payload, in_size);
}
ULONGLONG oldBase = get_image_base(in_buf);
bool isOk = true;
// from the loadBase go back to the original base
if (!relocate_module(in_buf, in_size, oldBase, loadBase)) {
//Failed relocating the module! Changing image base instead...
if (!update_image_base(in_buf, (ULONGLONG)loadBase)) {
std::cerr << "[-] Failed relocating the module!" << std::endl;
isOk = false;
} else {
#ifdef _DEBUG
std::cerr << "[!] WARNING: The module could not be relocated, so the ImageBase has been changed instead!" << std::endl;
#endif
}
}
SIZE_T raw_size = 0;
if (isOk) {
if (!sections_virtual_to_raw(in_buf, in_size, out_buf, &raw_size)) {
isOk = false;
}
}
if (rebuffer && in_buf != NULL) {
free_pe_buffer(in_buf, in_size);
in_buf = NULL;
}
if (!isOk) {
free_pe_buffer(out_buf, in_size);
out_buf = NULL;
raw_size = 0;
}
out_size = raw_size;
return out_buf;
}
BYTE* peconv::pe_realign_raw_to_virtual(
IN const BYTE* payload,
IN size_t in_size,
IN ULONGLONG loadBase,
OUT size_t &out_size
)
{
out_size = in_size;
BYTE* out_buf = (BYTE*)alloc_pe_buffer(out_size, PAGE_READWRITE);
if (!out_buf) {
out_size = 0;
return nullptr;
}
memcpy(out_buf, payload, in_size);
ULONGLONG oldBase = get_image_base(out_buf);
bool isOk = true;
// from the loadBase go back to the original base
if (!relocate_module(out_buf, out_size, oldBase, loadBase)) {
//Failed relocating the module! Changing image base instead...
if (!update_image_base(out_buf, (ULONGLONG)loadBase)) {
std::cerr << "[-] Failed relocating the module!" << std::endl;
isOk = false;
} else {
#ifdef _DEBUG
std::cerr << "[!] WARNING: The module could not be relocated, so the ImageBase has been changed instead!" << std::endl;
#endif
}
}
//---
//set raw alignment the same as virtual
DWORD v_alignment = peconv::get_sec_alignment((const PBYTE)payload, false);
if (!peconv::set_sec_alignment(out_buf, true, v_alignment)) {
isOk = false;
}
//set Raw pointers and sizes of the sections same as Virtual
size_t sections_count = peconv::get_sections_count(out_buf, out_size);
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER sec = peconv::get_section_hdr(out_buf, out_size, i);
if (!sec) break;
sec->Misc.VirtualSize = peconv::get_virtual_sec_size(out_buf, sec, true);
sec->SizeOfRawData = sec->Misc.VirtualSize;
sec->PointerToRawData = sec->VirtualAddress;
}
//!---
if (!isOk) {
free_pe_buffer(out_buf);
out_buf = nullptr;
out_size = 0;
}
return out_buf;
}

View File

@@ -0,0 +1,178 @@
#include "ntddk.h"
#include <peconv/peb_lookup.h>
class SectionLocker {
public:
SectionLocker(RTL_CRITICAL_SECTION &_section)
: section(_section)
{
RtlEnterCriticalSection(&section);
}
~SectionLocker()
{
RtlLeaveCriticalSection(&section);
}
protected:
RTL_CRITICAL_SECTION &section;
};
//here we don't want to use any functions imported form extenal modules
typedef struct _LDR_MODULE {
LIST_ENTRY InLoadOrderModuleList;// +0x00
LIST_ENTRY InMemoryOrderModuleList;// +0x08
LIST_ENTRY InInitializationOrderModuleList;// +0x10
void* BaseAddress; // +0x18
void* EntryPoint; // +0x1c
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
HANDLE SectionHandle;
ULONG CheckSum;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
inline PPEB get_peb()
{
#if defined(_WIN64)
return (PPEB)__readgsqword(0x60);
#else
return (PPEB)__readfsdword(0x30);
/*
//alternative way to fetch it:
LPVOID PEB = NULL;
__asm {
mov eax, fs:[30h]
mov PEB, eax
};
return (PPEB)PEB;
or:
LPVOID PEB = RtlGetCurrentPeb();
*/
#endif
}
inline WCHAR to_lowercase(WCHAR c1)
{
if (c1 <= L'Z' && c1 >= L'A') {
c1 = (c1 - L'A') + L'a';
}
return c1;
}
bool is_wanted_module(LPWSTR curr_name, LPWSTR wanted_name)
{
if (wanted_name == NULL || curr_name == NULL) return false;
WCHAR *curr_end_ptr = curr_name;
while (*curr_end_ptr != L'\0') {
curr_end_ptr++;
}
if (curr_end_ptr == curr_name) return false;
WCHAR *wanted_end_ptr = wanted_name;
while (*wanted_end_ptr != L'\0') {
wanted_end_ptr++;
}
if (wanted_end_ptr == wanted_name) return false;
while ((curr_end_ptr != curr_name) && (wanted_end_ptr != wanted_name)) {
if (to_lowercase(*wanted_end_ptr) != to_lowercase(*curr_end_ptr)) {
return false;
}
wanted_end_ptr--;
curr_end_ptr--;
}
return true;
}
HMODULE peconv::get_module_via_peb(IN OPTIONAL LPWSTR module_name)
{
PPEB peb = get_peb();
if (!peb) {
return NULL;
}
SectionLocker locker(*peb->LoaderLock);
LIST_ENTRY head = peb->Ldr->InLoadOrderModuleList;
const PLDR_MODULE first_module = *((PLDR_MODULE *)(&head));
PLDR_MODULE curr_module = first_module;
if (!module_name) {
return (HMODULE)(curr_module->BaseAddress);
}
// it is a cyclic list, so if the next record links to the initial one, it means we went throught the full loop
do {
// this should also work as a terminator, because the BaseAddress of the last module in the cycle is NULL
if (curr_module == NULL || curr_module->BaseAddress == NULL) {
break;
}
if (is_wanted_module(curr_module->BaseDllName.Buffer, module_name)) {
return (HMODULE)(curr_module->BaseAddress);
}
curr_module = (PLDR_MODULE)curr_module->InLoadOrderModuleList.Flink;
} while (curr_module != first_module);
return NULL;
}
size_t peconv::get_module_size_via_peb(IN OPTIONAL HMODULE hModule)
{
PPEB peb = get_peb();
if (!peb) {
return 0;
}
SectionLocker locker(*peb->LoaderLock);
LIST_ENTRY head = peb->Ldr->InLoadOrderModuleList;
const PLDR_MODULE first_module = *((PLDR_MODULE *)(&head));
PLDR_MODULE curr_module = first_module;
if (!hModule) {
return (size_t)(curr_module->SizeOfImage);
}
// it is a cyclic list, so if the next record links to the initial one, it means we went throught the full loop
do {
// this should also work as a terminator, because the BaseAddress of the last module in the cycle is NULL
if (curr_module == NULL || curr_module->BaseAddress == NULL) {
break;
}
if (hModule == (HMODULE)(curr_module->BaseAddress)) {
return (size_t)(curr_module->SizeOfImage);
}
curr_module = (PLDR_MODULE)curr_module->InLoadOrderModuleList.Flink;
} while (curr_module != first_module);
return 0;
}
bool peconv::set_main_module_in_peb(HMODULE module_ptr)
{
PPEB peb = get_peb();
if (peb == NULL) {
return false;
}
SectionLocker locker(*peb->FastPebLock);
peb->ImageBaseAddress = module_ptr;
return true;
}
HMODULE peconv::get_main_module_via_peb()
{
PPEB peb = get_peb();
if (peb == NULL) {
return NULL;
}
SectionLocker locker(*peb->FastPebLock);
return (HMODULE) peb->ImageBaseAddress;
}

View File

@@ -0,0 +1,193 @@
#include "peconv/relocate.h"
#include "peconv/pe_hdrs_helper.h"
#include <stdio.h>
#include <iostream>
using namespace peconv;
#define RELOC_32BIT_FIELD 3
#define RELOC_64BIT_FIELD 0xA
class ApplyRelocCallback : public RelocBlockCallback
{
public:
ApplyRelocCallback(bool _is64bit, ULONGLONG _oldBase, ULONGLONG _newBase)
: RelocBlockCallback(_is64bit), oldBase(_oldBase), newBase(_newBase)
{
}
virtual bool processRelocField(ULONG_PTR relocField)
{
if (is64bit) {
ULONGLONG* relocateAddr = (ULONGLONG*)((ULONG_PTR)relocField);
ULONGLONG rva = (*relocateAddr) - oldBase;
(*relocateAddr) = rva + newBase;
}
else {
DWORD* relocateAddr = (DWORD*)((ULONG_PTR)relocField);
ULONGLONG rva = ULONGLONG(*relocateAddr) - oldBase;
(*relocateAddr) = static_cast<DWORD>(rva + newBase);
}
return true;
}
protected:
ULONGLONG oldBase;
ULONGLONG newBase;
};
bool is_empty_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize)
{
if (entriesNum == 0) {
return true; // nothing to process
}
BASE_RELOCATION_ENTRY* entry = block;
for (SIZE_T i = 0; i < entriesNum; i++) {
if (!validate_ptr(modulePtr, moduleSize, entry, sizeof(BASE_RELOCATION_ENTRY))) {
return false;
}
DWORD type = entry->Type;
if (type != 0) {
//non empty block found
return false;
}
entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
}
return true;
}
bool process_reloc_block(BASE_RELOCATION_ENTRY *block, SIZE_T entriesNum, DWORD page, PVOID modulePtr, SIZE_T moduleSize, bool is64bit, RelocBlockCallback *callback)
{
if (entriesNum == 0) {
return true; // nothing to process
}
BASE_RELOCATION_ENTRY* entry = block;
SIZE_T i = 0;
for (i = 0; i < entriesNum; i++) {
if (!validate_ptr(modulePtr, moduleSize, entry, sizeof(BASE_RELOCATION_ENTRY))) {
break;
}
DWORD offset = entry->Offset;
DWORD type = entry->Type;
if (type == 0) {
break;
}
if (type != RELOC_32BIT_FIELD && type != RELOC_64BIT_FIELD) {
if (callback) { //print debug messages only if the callback function was set
printf("[-] Not supported relocations format at %d: %d\n", (int)i, (int)type);
}
return false;
}
DWORD reloc_field = page + offset;
if (reloc_field >= moduleSize) {
if (callback) { //print debug messages only if the callback function was set
printf("[-] Malformed field: %lx\n", reloc_field);
}
return false;
}
if (callback) {
bool isOk = callback->processRelocField(((ULONG_PTR)modulePtr + reloc_field));
if (!isOk) {
std::cout << "[-] Failed processing reloc field at: " << std::hex << reloc_field << "\n";
return false;
}
}
entry = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)entry + sizeof(WORD));
}
return (i != 0);
}
bool peconv::process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback)
{
IMAGE_DATA_DIRECTORY* relocDir = peconv::get_directory_entry((const BYTE*)modulePtr, IMAGE_DIRECTORY_ENTRY_BASERELOC);
if (relocDir == NULL) {
#ifdef _DEBUG
std::cout << "[!] WARNING: no relocation table found!\n";
#endif
return false;
}
if (!validate_ptr(modulePtr, moduleSize, relocDir, sizeof(IMAGE_DATA_DIRECTORY))) {
std::cerr << "[!] Invalid relocDir pointer\n";
return false;
}
DWORD maxSize = relocDir->Size;
DWORD relocAddr = relocDir->VirtualAddress;
bool is64b = is64bit((BYTE*)modulePtr);
IMAGE_BASE_RELOCATION* reloc = NULL;
DWORD parsedSize = 0;
DWORD validBlocks = 0;
while (parsedSize < maxSize) {
reloc = (IMAGE_BASE_RELOCATION*)(relocAddr + parsedSize + (ULONG_PTR)modulePtr);
if (!validate_ptr(modulePtr, moduleSize, reloc, sizeof(IMAGE_BASE_RELOCATION))) {
#ifdef _DEBUG
std::cerr << "[-] Invalid address of relocations\n";
#endif
return false;
}
if (reloc->SizeOfBlock == 0) {
break;
}
size_t entriesNum = (reloc->SizeOfBlock - 2 * sizeof(DWORD)) / sizeof(WORD);
DWORD page = reloc->VirtualAddress;
BASE_RELOCATION_ENTRY* block = (BASE_RELOCATION_ENTRY*)((ULONG_PTR)reloc + sizeof(DWORD) + sizeof(DWORD));
if (!validate_ptr(modulePtr, moduleSize, block, sizeof(BASE_RELOCATION_ENTRY))) {
std::cerr << "[-] Invalid address of relocations block\n";
return false;
}
if (!is_empty_reloc_block(block, entriesNum, page, modulePtr, moduleSize)) {
if (process_reloc_block(block, entriesNum, page, modulePtr, moduleSize, is64b, callback)) {
validBlocks++;
}
else {
// the block was malformed
return false;
}
}
parsedSize += reloc->SizeOfBlock;
}
return (validBlocks != 0);
}
bool apply_relocations(PVOID modulePtr, SIZE_T moduleSize, ULONGLONG newBase, ULONGLONG oldBase)
{
const bool is64b = is64bit((BYTE*)modulePtr);
ApplyRelocCallback callback(is64b, oldBase, newBase);
return process_relocation_table(modulePtr, moduleSize, &callback);
}
bool peconv::relocate_module(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ULONGLONG newBase, IN ULONGLONG oldBase)
{
if (modulePtr == NULL) {
return false;
}
if (oldBase == 0) {
oldBase = get_image_base(modulePtr);
}
#ifdef _DEBUG
printf("New Base: %llx\n", newBase);
printf("Old Base: %llx\n", oldBase);
#endif
if (newBase == oldBase) {
#ifdef _DEBUG
printf("Nothing to relocate! oldBase is the same as the newBase!\n");
#endif
return true; //nothing to relocate
}
if (apply_relocations(modulePtr, moduleSize, newBase, oldBase)) {
return true;
}
#ifdef _DEBUG
printf("Could not relocate the module!\n");
#endif
return false;
}
bool peconv::has_valid_relocation_table(IN const PBYTE modulePtr, IN const size_t moduleSize)
{
return process_relocation_table(modulePtr, moduleSize, nullptr);
}

View File

@@ -0,0 +1,421 @@
#include "peconv/remote_pe_reader.h"
#include <iostream>
#include "peconv/util.h"
#include "peconv/fix_imports.h"
using namespace peconv;
bool peconv::fetch_region_info(HANDLE processHandle, LPVOID moduleBase, MEMORY_BASIC_INFORMATION &page_info)
{
memset(&page_info, 0, sizeof(MEMORY_BASIC_INFORMATION));
SIZE_T out = VirtualQueryEx(processHandle, moduleBase, &page_info, sizeof(page_info));
if (out != sizeof(page_info)) {
return false;
}
return true;
}
size_t _fetch_region_size(MEMORY_BASIC_INFORMATION &page_info, LPVOID moduleBase)
{
if (page_info.Type == 0) {
return false; //invalid type, skip it
}
if ((BYTE*)page_info.BaseAddress > moduleBase) {
return 0; //should never happen
}
const size_t offset = (ULONG_PTR)moduleBase - (ULONG_PTR)page_info.BaseAddress;
const size_t area_size = page_info.RegionSize - offset;
return area_size;
}
size_t peconv::fetch_region_size(HANDLE processHandle, LPVOID moduleBase)
{
MEMORY_BASIC_INFORMATION page_info = { 0 };
if (!peconv::fetch_region_info(processHandle, moduleBase, page_info)) {
return 0;
}
const size_t area_size = _fetch_region_size(page_info, moduleBase);
return area_size;
}
ULONGLONG peconv::fetch_alloc_base(HANDLE processHandle, LPVOID moduleBase)
{
MEMORY_BASIC_INFORMATION page_info = { 0 };
if (!peconv::fetch_region_info(processHandle, moduleBase, page_info)) {
return 0;
}
if (page_info.Type == 0) {
return 0; //invalid type, skip it
}
return (ULONGLONG) page_info.AllocationBase;
}
namespace peconv {
/**
Performs a binary search along with ReadProcessMemory, trying to find the biggest size of memory (within the buffer_size) that can be read. The search stops when the minimal_size was reached.
The given minimal_size must be non-zero, and smaller than the buffer_size.
If the size matching the constraints was found, it reads that many bytes to the buffer.
*/
SIZE_T _search_readable_size(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const SIZE_T minimal_size)
{
if (!buffer || buffer_size == 0) {
return 0;
}
if ((buffer_size < minimal_size) || minimal_size == 0) {
return 0;
}
SIZE_T last_failed_size = buffer_size;
SIZE_T last_success_size = 0;
SIZE_T test_read_size = 0;
if (!ReadProcessMemory(processHandle, start_addr, buffer, minimal_size, &test_read_size)) {
//cannot read even the minimal size, quit trying
return test_read_size;
}
last_success_size = minimal_size;
SIZE_T read_size = 0;
SIZE_T to_read_size = buffer_size/2;
while (to_read_size > minimal_size && to_read_size < buffer_size)
{
read_size = 0;
if (ReadProcessMemory(processHandle, start_addr, buffer, to_read_size, &read_size)) {
last_success_size = to_read_size;
}
else {
last_failed_size = to_read_size;
}
const size_t delta = (last_failed_size - last_success_size) / 2;
if (delta == 0) break;
to_read_size = last_success_size + delta;
}
if (last_success_size) {
read_size = 0;
memset(buffer, 0, buffer_size);
ReadProcessMemory(processHandle, start_addr, buffer, last_success_size, &read_size);
return read_size;
}
return 0;
}
};
size_t peconv::read_remote_memory(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const SIZE_T minimal_size)
{
if (!buffer || buffer_size == 0) {
return 0;
}
memset(buffer, 0, buffer_size);
SIZE_T read_size = 0;
DWORD last_error = ERROR_SUCCESS;
while (buffer_size > 0)
{
if (ReadProcessMemory(processHandle, start_addr, buffer, buffer_size, &read_size)) {
break;
}
last_error = GetLastError();
if (last_error != ERROR_SUCCESS) {
if (read_size == 0 && (last_error != ERROR_PARTIAL_COPY)) {
break; // break
}
}
if (last_error == ERROR_PARTIAL_COPY) {
read_size = peconv::_search_readable_size(processHandle, start_addr, buffer, buffer_size, minimal_size);
#ifdef _DEBUG
std::cout << "peconv::search_readable_size res: " << std::hex << read_size << std::endl;
#endif
}
break;
}
#ifdef _DEBUG
if (read_size == 0) {
std::cerr << "[WARNING] Cannot read memory. Last Error : " << last_error << std::endl;
}
else if (read_size < buffer_size) {
std::cerr << "[WARNING] Read size: " << std::hex << read_size
<< " is smaller than the requested size: " << std::hex << buffer_size
<< ". Last Error: " << last_error << std::endl;
}
#endif
return static_cast<size_t>(read_size);
}
size_t peconv::read_remote_region(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const bool force_access, const SIZE_T minimal_size)
{
if (!buffer || buffer_size == 0) {
return 0;
}
MEMORY_BASIC_INFORMATION page_info = { 0 };
if (!peconv::fetch_region_info(processHandle, start_addr, page_info)) {
return 0;
}
if ((page_info.State & MEM_COMMIT) == 0) {
return 0;
}
size_t region_size = _fetch_region_size(page_info, start_addr);
if (region_size == 0) {
return 0;
}
const size_t size_to_read = (region_size > buffer_size) ? buffer_size : region_size;
const bool is_accessible = (page_info.Protect & PAGE_NOACCESS) == 0;
BOOL access_changed = FALSE;
DWORD oldProtect = 0;
// check the access right and eventually try to change it
if (force_access && !is_accessible) {
access_changed = VirtualProtectEx(processHandle, start_addr, region_size, PAGE_READONLY, &oldProtect);
#ifdef _DEBUG
if (!access_changed) {
DWORD err = GetLastError();
if (err != ERROR_ACCESS_DENIED) {
std::cerr << "[!] " << std::hex << start_addr << " : " << region_size << " inaccessible area, changing page access failed: " << std::dec << err << "\n";
}
}
#endif
}
size_t size_read = 0;
if (is_accessible || access_changed) {
size_read = peconv::read_remote_memory(processHandle, start_addr, buffer, size_to_read, minimal_size);
if ((size_read == 0) && (page_info.Protect & PAGE_GUARD)) {
#ifdef _DEBUG
std::cout << "Warning: guarded page, trying to read again..." << std::endl;
#endif
size_read = peconv::read_remote_memory(processHandle, start_addr, buffer, size_to_read, minimal_size);
}
}
// if the access rights were changed, change it back:
if (access_changed) {
VirtualProtectEx(processHandle, start_addr, region_size, oldProtect, &oldProtect);
}
return size_read;
}
size_t peconv::read_remote_area(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const bool force_access, const SIZE_T minimal_size)
{
if (!buffer || !start_addr || buffer_size == 0) {
return 0;
}
memset(buffer, 0, buffer_size);
size_t real_read = 0; // how many bytes has been realy read (not counting the skipped areas)
size_t last_valid = 0; // the last chunk that was really read (don't count the last skipped ones)
size_t buf_index = 0;
for (buf_index = 0; buf_index < buffer_size; ) {
LPVOID remote_chunk = LPVOID((ULONG_PTR)start_addr + buf_index);
MEMORY_BASIC_INFORMATION page_info = { 0 };
if (!peconv::fetch_region_info(processHandle, remote_chunk, page_info)) {
break;
}
const size_t region_size = _fetch_region_size(page_info, remote_chunk);
if (region_size == 0) {
break;
}
// read the memory:
const size_t read_chunk = read_remote_region(
processHandle,
remote_chunk,
(BYTE*)((ULONG_PTR)buffer + buf_index),
buffer_size - buf_index,
force_access,
minimal_size
);
if (read_chunk == 0) {
//skip the region that could not be read, and proceed to the next:
buf_index += region_size;
continue;
}
buf_index += read_chunk;
real_read += read_chunk; // total sum of the read content
last_valid = buf_index; // the last chunk that was really read
}
if (real_read == 0) {
return 0;
}
return last_valid;
}
bool peconv::read_remote_pe_header(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, bool force_access)
{
if (buffer == nullptr) {
return false;
}
SIZE_T read_size = read_remote_area(processHandle, start_addr, buffer, buffer_size, force_access);
if (read_size == 0) {
return false;
}
BYTE *nt_ptr = get_nt_hdrs(buffer, buffer_size);
if (nt_ptr == nullptr) {
return false;
}
const size_t nt_offset = nt_ptr - buffer;
const size_t nt_size = peconv::is64bit(buffer) ? sizeof(IMAGE_NT_HEADERS64) : sizeof(IMAGE_NT_HEADERS32);
const size_t min_size = nt_offset + nt_size;
if (read_size < min_size) {
std::cerr << "[-] [" << std::dec << get_process_id(processHandle)
<< " ][" << std::hex << (ULONGLONG) start_addr
<< "] Read size: " << std::hex << read_size
<< " is smaller that the minimal size:" << get_hdrs_size(buffer)
<< std::endl;
return false;
}
//reading succeeded and the header passed the checks:
return true;
}
namespace peconv {
inline size_t roundup_to_unit(size_t size, size_t unit)
{
if (unit == 0) {
return size;
}
size_t parts = size / unit;
if (size % unit) parts++;
return parts * unit;
}
};
peconv::UNALIGNED_BUF peconv::get_remote_pe_section(HANDLE processHandle, LPVOID start_addr, const size_t section_num, OUT size_t &section_size, bool roundup, bool force_access)
{
BYTE header_buffer[MAX_HEADER_SIZE] = { 0 };
if (!read_remote_pe_header(processHandle, start_addr, header_buffer, MAX_HEADER_SIZE)) {
return NULL;
}
PIMAGE_SECTION_HEADER section_hdr = get_section_hdr(header_buffer, MAX_HEADER_SIZE, section_num);
if (section_hdr == NULL || section_hdr->Misc.VirtualSize == 0) {
return NULL;
}
size_t buffer_size = section_hdr->Misc.VirtualSize;
if (roundup) {
DWORD va = peconv::get_sec_alignment(header_buffer, false);
if (va == 0) va = PAGE_SIZE;
buffer_size = roundup_to_unit(section_hdr->Misc.VirtualSize, va);
}
UNALIGNED_BUF module_code = peconv::alloc_unaligned(buffer_size);
if (module_code == NULL) {
return NULL;
}
size_t read_size = peconv::read_remote_memory(processHandle, LPVOID((ULONG_PTR)start_addr + section_hdr->VirtualAddress), module_code, buffer_size);
if (read_size == 0) {
// this function is slower, so use it only if the normal read has failed:
read_size = read_remote_area(processHandle, LPVOID((ULONG_PTR)start_addr + section_hdr->VirtualAddress), module_code, buffer_size, force_access);
}
if (read_size == 0) {
peconv::free_unaligned(module_code);
return NULL;
}
section_size = buffer_size;
return module_code;
}
size_t peconv::read_remote_pe(const HANDLE processHandle, LPVOID start_addr, const size_t mod_size, OUT BYTE* buffer, const size_t bufferSize)
{
if (buffer == nullptr) {
std::cerr << "[-] Invalid output buffer: NULL pointer" << std::endl;
return 0;
}
if (bufferSize < mod_size || bufferSize < MAX_HEADER_SIZE ) {
std::cerr << "[-] Invalid output buffer: too small size!" << std::endl;
return 0;
}
// read PE section by section
PBYTE hdr_buffer = buffer;
//try to read headers:
if (!read_remote_pe_header(processHandle, start_addr, hdr_buffer, MAX_HEADER_SIZE)) {
std::cerr << "[-] Failed to read the module header" << std::endl;
return 0;
}
if (!is_valid_sections_hdr_offset(hdr_buffer, MAX_HEADER_SIZE)) {
std::cerr << "[-] Sections headers are invalid or atypically aligned" << std::endl;
return 0;
}
size_t sections_count = get_sections_count(hdr_buffer, MAX_HEADER_SIZE);
#ifdef _DEBUG
std::cout << "Sections: " << sections_count << std::endl;
#endif
size_t read_size = MAX_HEADER_SIZE;
for (size_t i = 0; i < sections_count; i++) {
PIMAGE_SECTION_HEADER hdr = get_section_hdr(hdr_buffer, MAX_HEADER_SIZE, i);
if (!hdr) {
std::cerr << "[-] Failed to read the header of section: " << i << std::endl;
break;
}
const DWORD sec_va = hdr->VirtualAddress;
const DWORD sec_vsize = get_virtual_sec_size(hdr_buffer, hdr, true);
if (sec_va + sec_vsize > bufferSize) {
std::cerr << "[-] No more space in the buffer!" << std::endl;
break;
}
if (sec_vsize > 0 && !read_remote_memory(processHandle, LPVOID((ULONG_PTR)start_addr + sec_va), buffer + sec_va, sec_vsize)) {
std::cerr << "[-] Failed to read the module section " << i <<" : at: " << std::hex << (ULONG_PTR)start_addr + sec_va << std::endl;
}
// update the end of the read area:
size_t new_end = sec_va + sec_vsize;
if (new_end > read_size) read_size = new_end;
}
#ifdef _DEBUG
std::cout << "Total read size: " << read_size << std::endl;
#endif
return read_size;
}
DWORD peconv::get_remote_image_size(IN const HANDLE processHandle, IN LPVOID start_addr)
{
BYTE hdr_buffer[MAX_HEADER_SIZE] = { 0 };
if (!read_remote_pe_header(processHandle, start_addr, hdr_buffer, MAX_HEADER_SIZE)) {
return 0;
}
return peconv::get_image_size(hdr_buffer);
}
bool peconv::dump_remote_pe(IN const char *out_path,
IN const HANDLE processHandle,
IN LPVOID start_addr,
IN OUT t_pe_dump_mode &dump_mode,
IN OPTIONAL peconv::ExportsMapper* exportsMap)
{
DWORD mod_size = get_remote_image_size(processHandle, start_addr);
#ifdef _DEBUG
std::cout << "Module Size: " << mod_size << std::endl;
#endif
if (mod_size == 0) {
return false;
}
BYTE* buffer = peconv::alloc_pe_buffer(mod_size, PAGE_READWRITE);
if (buffer == nullptr) {
std::cerr << "[-] Failed allocating buffer. Error: " << GetLastError() << std::endl;
return false;
}
//read the module that it mapped in the remote process:
const size_t read_size = read_remote_pe(processHandle, start_addr, mod_size, buffer, mod_size);
if (read_size == 0) {
std::cerr << "[-] Failed reading module. Error: " << GetLastError() << std::endl;
peconv::free_pe_buffer(buffer, mod_size);
buffer = nullptr;
return false;
}
const bool is_dumped = peconv::dump_pe(out_path,
buffer, mod_size,
reinterpret_cast<ULONGLONG>(start_addr),
dump_mode, exportsMap);
peconv::free_pe_buffer(buffer, mod_size);
buffer = nullptr;
return is_dumped;
}

View File

@@ -0,0 +1,90 @@
#include "peconv/resource_parser.h"
#include "peconv/pe_hdrs_helper.h"
#ifdef _DEBUG
#include <iostream>
#endif
bool parse_resource_dir(BYTE* modulePtr, const size_t moduleSize,
IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir,
const IMAGE_RESOURCE_DIRECTORY *upper_dir,
IMAGE_RESOURCE_DIRECTORY* curr_dir,
peconv::t_on_res_entry_found on_entry);
bool parse_resource_entry(BYTE* modulePtr, const size_t moduleSize,
IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir,
const IMAGE_RESOURCE_DIRECTORY *upper_dir,
IMAGE_RESOURCE_DIRECTORY_ENTRY* entry,
peconv::t_on_res_entry_found on_entry)
{
if (!entry->DataIsDirectory) {
#ifdef _DEBUG
std::cout << "Entry is NOT a directory\n";
#endif
DWORD offset = entry->OffsetToData;
#ifdef _DEBUG
std::cout << "Offset: " << offset << std::endl;
#endif
IMAGE_RESOURCE_DATA_ENTRY *data_entry = (IMAGE_RESOURCE_DATA_ENTRY*)(offset + (ULONGLONG)upper_dir);
if (!peconv::validate_ptr(modulePtr, moduleSize, data_entry, sizeof(IMAGE_RESOURCE_DATA_ENTRY))) {
return false;
}
#ifdef _DEBUG
std::cout << "Data Offset: " << data_entry->OffsetToData << " : " << data_entry->Size << std::endl;
#endif
BYTE* data_ptr = (BYTE*)((ULONGLONG)modulePtr + data_entry->OffsetToData);
if (!peconv::validate_ptr(modulePtr, moduleSize, data_ptr, data_entry->Size)) {
return false;
}
on_entry(modulePtr, root_dir, data_entry);
return true;
}
#ifdef _DEBUG
std::cout << "Entry is a directory\n";
#endif
//else: it is a next level directory
DWORD offset = entry->OffsetToDirectory;
#ifdef _DEBUG
std::cout << "Offset: " << offset << std::endl;
#endif
IMAGE_RESOURCE_DIRECTORY *next_dir = (IMAGE_RESOURCE_DIRECTORY*)(offset + (ULONGLONG)upper_dir);
if (!peconv::validate_ptr(modulePtr, moduleSize, next_dir, sizeof(IMAGE_RESOURCE_DIRECTORY))) {
return false;
}
return parse_resource_dir(modulePtr, moduleSize, root_dir, upper_dir, next_dir, on_entry);
}
bool parse_resource_dir(BYTE* modulePtr, const size_t moduleSize,
IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir,
const IMAGE_RESOURCE_DIRECTORY *upper_dir,
IMAGE_RESOURCE_DIRECTORY* curr_dir,
peconv::t_on_res_entry_found on_entry)
{
size_t total_entries = curr_dir->NumberOfIdEntries + curr_dir->NumberOfNamedEntries;
IMAGE_RESOURCE_DIRECTORY_ENTRY* first_entry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((ULONGLONG)&curr_dir->NumberOfIdEntries + sizeof(WORD));
for (size_t i = 0; i < total_entries; i++) {
IMAGE_RESOURCE_DIRECTORY_ENTRY* entry = &first_entry[i];
#ifdef _DEBUG
std::cout << "Entry:" << std::hex << i << " ; " << "Id: " << entry->Id << " ; dataOffset:" << entry->OffsetToData << "\n";
#endif
if (root_dir == nullptr) {
root_dir = entry;
}
parse_resource_entry(modulePtr, moduleSize, root_dir, upper_dir, entry, on_entry);
}
return true;
}
bool peconv::parse_resources(BYTE* modulePtr, t_on_res_entry_found on_entry)
{
const size_t module_size = peconv::get_image_size(modulePtr);
IMAGE_DATA_DIRECTORY *dir = peconv::get_directory_entry(modulePtr, IMAGE_DIRECTORY_ENTRY_RESOURCE);
if (!dir || dir->VirtualAddress == 0 || dir->Size == 0) {
return false;
}
IMAGE_RESOURCE_DIRECTORY *res_dir = (IMAGE_RESOURCE_DIRECTORY*)(dir->VirtualAddress + (ULONGLONG)modulePtr);
if (!peconv::validate_ptr(modulePtr, module_size, res_dir, sizeof(IMAGE_DEBUG_DIRECTORY))) {
return false;
}
return parse_resource_dir(modulePtr, module_size, nullptr, res_dir, res_dir, on_entry);
}

View File

@@ -0,0 +1,56 @@
#include "peconv/resource_util.h"
#ifdef _DEBUG
#include <iostream>
#endif
HMODULE peconv::get_current_module_handle()
{
HMODULE hMod = NULL;
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(&peconv::get_current_module_handle),
&hMod);
return hMod;
}
peconv::ALIGNED_BUF peconv::load_resource_data(OUT size_t &out_size, int res_id, const LPSTR res_type, HMODULE hInstance)
{
if (hInstance == nullptr) {
hInstance = GetModuleHandleA(NULL);
}
HRSRC res = FindResourceA(hInstance, MAKEINTRESOURCEA(res_id), res_type);
if (!res) {
#ifdef _DEBUG
std::cerr << "Cannot find resource" << std::endl;
#endif
return nullptr;
}
HGLOBAL res_handle = LoadResource(hInstance, res);
if (res_handle == nullptr) {
#ifdef _DEBUG
std::cerr << "Cannot get resource handle" << std::endl;
#endif
return nullptr;
}
BYTE* res_data = (BYTE*) LockResource(res_handle);
size_t r_size = static_cast<size_t>(SizeofResource(hInstance, res));
if (out_size != 0 && out_size <= r_size) {
r_size = out_size;
}
peconv::ALIGNED_BUF out_buf = peconv::alloc_aligned(r_size, PAGE_READWRITE);
if (out_buf != nullptr) {
memcpy(out_buf, res_data, r_size);
out_size = r_size;
} else {
out_size = 0;
}
FreeResource(res_handle);
return out_buf;
}
void peconv::free_resource_data(peconv::ALIGNED_BUF buffer)
{
peconv::free_aligned(buffer);
}

View File

@@ -0,0 +1,163 @@
#include "peconv/util.h"
#include <iostream>
#define USE_OLD_BADPTR
namespace peconv {
HMODULE g_kernel32Hndl = nullptr;
HMODULE g_ntdllHndl = nullptr;
HMODULE get_kernel32_hndl()
{
if (g_kernel32Hndl == nullptr) {
g_kernel32Hndl = LoadLibraryA("kernel32.dll");
}
return g_kernel32Hndl;
}
HMODULE get_ntdll_hndl()
{
if (g_ntdllHndl == nullptr) {
g_ntdllHndl = LoadLibraryA("ntdll.dll");
}
return g_ntdllHndl;
}
};
DWORD ntdll_get_process_id(HANDLE hProcess)
{
#if !defined PROCESSINFOCLASS
typedef LONG PROCESSINFOCLASS;
#endif
NTSTATUS(WINAPI *_ZwQueryInformationProcess)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength
) = NULL;
HINSTANCE hNtDll = peconv::get_ntdll_hndl();
if (!hNtDll) {
return 0;
}
FARPROC procPtr = GetProcAddress(hNtDll, "ZwQueryInformationProcess");
if (!procPtr) {
return 0;
}
_ZwQueryInformationProcess = (NTSTATUS(WINAPI *)(
HANDLE,
PROCESSINFOCLASS,
PVOID,
ULONG,
PULONG)
) procPtr;
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
PROCESS_BASIC_INFORMATION pbi = { 0 };
if (_ZwQueryInformationProcess(hProcess, 0, &pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL) == S_OK) {
const DWORD pid = static_cast<DWORD>(pbi.UniqueProcessId);
return pid;
}
return 0;
}
DWORD peconv::get_process_id(HANDLE hProcess)
{
static DWORD(WINAPI *_GetProcessId)(IN HANDLE Process) = nullptr;
DWORD processID = 0;
if (!_GetProcessId) {
HMODULE kernelLib = peconv::get_kernel32_hndl();
if (kernelLib) {
FARPROC procPtr = GetProcAddress(kernelLib, "GetProcessId");
if (procPtr) {
_GetProcessId = (DWORD(WINAPI *) (IN HANDLE))procPtr;
}
}
}
if (_GetProcessId) {
processID = _GetProcessId(hProcess);
}
if (processID == 0) {
//could not retrieve Pid using GetProcessId, try using NTDLL:
processID = ntdll_get_process_id(hProcess);
}
return processID;
}
bool peconv::is_padding(const BYTE *cave_ptr, size_t cave_size, const BYTE padding)
{
for (size_t i = 0; i < cave_size; i++) {
if (cave_ptr[i] != padding) {
return false;
}
}
return true;
}
bool peconv::is_mem_accessible(LPCVOID areaStart, SIZE_T areaSize, DWORD dwAccessRights)
{
if (!areaSize) return false; // zero-sized areas are not allowed
const DWORD dwForbiddenArea = PAGE_GUARD | PAGE_NOACCESS;
MEMORY_BASIC_INFORMATION mbi = { 0 };
const size_t mbiSize = sizeof(MEMORY_BASIC_INFORMATION);
SIZE_T sizeToCheck = areaSize;
LPCVOID areaPtr = areaStart;
while (sizeToCheck > 0) {
//reset area
memset(&mbi, 0, mbiSize);
// query the next area
if (VirtualQuery(areaPtr, &mbi, mbiSize) != mbiSize) {
return false; // could not query the area, assume it is bad
}
// check the privileges
bool isOk = (mbi.State & MEM_COMMIT) // memory allocated and
&& !(mbi.Protect & dwForbiddenArea) // access to page allowed and
&& (mbi.Protect & dwAccessRights); // the required rights
if (!isOk) {
return false; //invalid access
}
SIZE_T offset = (ULONG_PTR)areaPtr - (ULONG_PTR)mbi.BaseAddress;
SIZE_T queriedSize = mbi.RegionSize - offset;
if (queriedSize >= sizeToCheck) {
return true; // it is fine
}
// move to the next region
sizeToCheck -= queriedSize;
areaPtr = LPCVOID((ULONG_PTR)areaPtr + queriedSize);
}
// by default assume it is inaccessible
return false;
}
bool peconv::is_bad_read_ptr(LPCVOID areaStart, SIZE_T areaSize)
{
#ifdef USE_OLD_BADPTR // classic IsBadReadPtr is much faster than the version using VirtualQuery
return IsBadReadPtr(areaStart, areaSize);
#else
const DWORD dwReadRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
bool isAccessible = peconv::is_mem_accessible(areaStart, areaSize, dwReadRights);
if (isAccessible) {
// the area has read access rights: not a bad read pointer
return false;
}
return true;
#endif
}