添加项目文件。
This commit is contained in:
83
Etw Syscall/libpeconv-master/libpeconv/CMakeLists.txt
Normal file
83
Etw Syscall/libpeconv-master/libpeconv/CMakeLists.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
cmake_minimum_required ( VERSION 2.8...3.21 )
|
||||
project ( libpeconv )
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
|
||||
|
||||
include_directories (
|
||||
include
|
||||
)
|
||||
|
||||
set (srcs
|
||||
src/pe_hdrs_helper.cpp
|
||||
src/pe_mode_detector.cpp
|
||||
src/pe_raw_to_virtual.cpp
|
||||
src/pe_virtual_to_raw.cpp
|
||||
src/relocate.cpp
|
||||
src/buffer_util.cpp
|
||||
src/remote_pe_reader.cpp
|
||||
src/imports_loader.cpp
|
||||
src/delayed_imports_loader.cpp
|
||||
src/fix_imports.cpp
|
||||
src/pe_loader.cpp
|
||||
src/pe_dumper.cpp
|
||||
src/exports_lookup.cpp
|
||||
src/function_resolver.cpp
|
||||
src/hooks.cpp
|
||||
src/exported_func.cpp
|
||||
src/exports_mapper.cpp
|
||||
src/resource_parser.cpp
|
||||
src/file_util.cpp
|
||||
src/resource_util.cpp
|
||||
src/imports_uneraser.cpp
|
||||
src/load_config_util.cpp
|
||||
src/caves.cpp
|
||||
src/util.cpp
|
||||
src/fix_dot_net_ep.cpp
|
||||
src/find_base.cpp
|
||||
src/peb_lookup.cpp
|
||||
)
|
||||
|
||||
set (hdrs
|
||||
include/peconv.h
|
||||
include/peconv/pe_hdrs_helper.h
|
||||
include/peconv/pe_mode_detector.h
|
||||
include/peconv/pe_raw_to_virtual.h
|
||||
include/peconv/pe_virtual_to_raw.h
|
||||
include/peconv/relocate.h
|
||||
include/peconv/util.h
|
||||
include/peconv/buffer_util.h
|
||||
include/peconv/remote_pe_reader.h
|
||||
include/peconv/imports_loader.h
|
||||
include/peconv/delayed_imports_loader.h
|
||||
include/peconv/fix_imports.h
|
||||
include/peconv/pe_loader.h
|
||||
include/peconv/pe_dumper.h
|
||||
include/peconv/exports_lookup.h
|
||||
include/peconv/function_resolver.h
|
||||
include/peconv/hooks.h
|
||||
include/peconv/exported_func.h
|
||||
include/peconv/exports_mapper.h
|
||||
include/peconv/resource_parser.h
|
||||
include/peconv/file_util.h
|
||||
include/peconv/resource_util.h
|
||||
include/peconv/imports_uneraser.h
|
||||
include/peconv/load_config_util.h
|
||||
include/peconv/load_config_defs.h
|
||||
include/peconv/caves.h
|
||||
include/peconv/find_base.h
|
||||
include/peconv/peb_lookup.h
|
||||
src/fix_dot_net_ep.h #not in API
|
||||
)
|
||||
|
||||
add_library ( ${PROJECT_NAME} STATIC ${hdrs} ${srcs} )
|
||||
|
||||
if(PECONV_LIB_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
install(TARGETS ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
install(DIRECTORY "include/" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
endif()
|
||||
30
Etw Syscall/libpeconv-master/libpeconv/include/peconv.h
Normal file
30
Etw Syscall/libpeconv-master/libpeconv/include/peconv.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Master include file, including everything else.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "peconv/buffer_util.h"
|
||||
#include "peconv/util.h"
|
||||
#include "peconv/pe_hdrs_helper.h"
|
||||
#include "peconv/pe_mode_detector.h"
|
||||
#include "peconv/pe_raw_to_virtual.h"
|
||||
#include "peconv/pe_virtual_to_raw.h"
|
||||
#include "peconv/relocate.h"
|
||||
#include "peconv/remote_pe_reader.h"
|
||||
#include "peconv/imports_loader.h"
|
||||
#include "peconv/pe_loader.h"
|
||||
#include "peconv/pe_dumper.h"
|
||||
#include "peconv/exports_lookup.h"
|
||||
#include "peconv/function_resolver.h"
|
||||
#include "peconv/hooks.h"
|
||||
#include "peconv/exports_mapper.h"
|
||||
#include "peconv/caves.h"
|
||||
#include "peconv/fix_imports.h"
|
||||
#include "peconv/delayed_imports_loader.h"
|
||||
#include "peconv/resource_parser.h"
|
||||
#include "peconv/load_config_util.h"
|
||||
#include "peconv/peb_lookup.h"
|
||||
#include "peconv/find_base.h"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Definitions of the used buffer types. Functions for their allocation and deallocation.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Validates pointers, checks if the particular field is inside the given buffer. Sizes must be given in bytes.
|
||||
\param buffer_bgn : the start address of the buffer
|
||||
\param buffer_size : the size of the buffer
|
||||
\param field_bgn : the start address of the field
|
||||
\param field_size : the size of the field
|
||||
\return true if the field (defined by its start address: field_bgn, and size: field_size) is contained within the given buffer
|
||||
(defined by its start address: buffer_bgn, and size: buffer_size).
|
||||
false otherwise
|
||||
*/
|
||||
bool validate_ptr(
|
||||
IN const void* buffer_bgn,
|
||||
IN SIZE_T buffer_size,
|
||||
IN const void* field_bgn,
|
||||
IN SIZE_T field_size
|
||||
);
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
//
|
||||
// supported buffers:
|
||||
//
|
||||
/**
|
||||
A buffer allocated on the heap of a process, not aligned to the beginning of a memory page.
|
||||
*/
|
||||
typedef PBYTE UNALIGNED_BUF;
|
||||
|
||||
/**
|
||||
A buffer allocated in a virtual space of a process, aligned to the beginning of a memory page.
|
||||
*/
|
||||
typedef PBYTE ALIGNED_BUF;
|
||||
|
||||
//
|
||||
// alloc/free unaligned buffers:
|
||||
//
|
||||
/**
|
||||
Allocates a buffer on the heap. Can be used in the cases when the buffer does not have to start at the beginning of a page.
|
||||
*/
|
||||
UNALIGNED_BUF alloc_unaligned(size_t buf_size);
|
||||
|
||||
//
|
||||
/**
|
||||
Frees buffer allocated by alloc_unaligned.
|
||||
*/
|
||||
void free_unaligned(UNALIGNED_BUF section_buffer);
|
||||
|
||||
//
|
||||
// alloc/free aligned buffers:
|
||||
//
|
||||
|
||||
/**
|
||||
Allocates a buffer of a virtual memory (using VirtualAlloc). Can be used in the cases when the buffer have to be aligned to the beginning of a page.
|
||||
*/
|
||||
ALIGNED_BUF alloc_aligned(size_t buffer_size, DWORD protect, ULONGLONG desired_base=NULL);
|
||||
|
||||
/**
|
||||
Frees buffer allocated by alloc_aligned.
|
||||
*/
|
||||
bool free_aligned(ALIGNED_BUF buffer, size_t buffer_size=0);
|
||||
|
||||
//PE buffers (wrappers)
|
||||
|
||||
/**
|
||||
Allocates an aligned buffer for a PE file.
|
||||
*/
|
||||
ALIGNED_BUF alloc_pe_buffer(size_t buffer_size, DWORD protect, ULONGLONG desired_base=NULL);
|
||||
|
||||
/**
|
||||
Free the memory allocated with alloc_pe_buffer.
|
||||
*/
|
||||
bool free_pe_buffer(ALIGNED_BUF buffer, size_t buffer_size=0);
|
||||
|
||||
}; //namespace peconv
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions related to finding caves in the loaded PE file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Finds cave at the end of the image (extend last section's raw size without extending the full image size)
|
||||
*/
|
||||
PBYTE find_ending_cave(BYTE* module_ptr, size_t module_size, const DWORD cave_size, const DWORD cave_charact=IMAGE_SCN_MEM_READ);
|
||||
|
||||
/**
|
||||
Finds cave in the difference between the original raw size, and the raw size rounded to the aligmnent
|
||||
*/
|
||||
PBYTE find_alignment_cave(BYTE* modulePtr, size_t moduleSize, const DWORD cave_size, const DWORD req_charact = IMAGE_SCN_MEM_READ);
|
||||
|
||||
/**
|
||||
Finds cave at the end of the section, that comes from a NULL padding or INT3 padding
|
||||
*/
|
||||
PBYTE find_padding_cave(BYTE* modulePtr, size_t moduleSize, const size_t minimal_size, const DWORD req_charact = IMAGE_SCN_MEM_READ);
|
||||
|
||||
};//namespace peconv
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Parsing and filling the Delayload Import Table.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
#include "function_resolver.h"
|
||||
|
||||
#if (defined(_WIN32_WINNT) && _WIN32_WINNT > 0x0601) || __MINGW32__ //Windows SDK version 6.1 (Windows 7)
|
||||
#define DELAYLOAD_IMPORTS_DEFINED
|
||||
#endif
|
||||
|
||||
#ifndef DELAYLOAD_IMPORTS_DEFINED
|
||||
#include "pshpack4.h"
|
||||
|
||||
typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {
|
||||
union {
|
||||
DWORD AllAttributes;
|
||||
struct {
|
||||
DWORD RvaBased : 1; // Delay load version 2
|
||||
DWORD ReservedAttributes : 31;
|
||||
} DUMMYSTRUCTNAME;
|
||||
} Attributes;
|
||||
|
||||
DWORD DllNameRVA; // RVA to the name of the target library (NULL-terminate ASCII string)
|
||||
DWORD ModuleHandleRVA; // RVA to the HMODULE caching location (PHMODULE)
|
||||
DWORD ImportAddressTableRVA; // RVA to the start of the IAT (PIMAGE_THUNK_DATA)
|
||||
DWORD ImportNameTableRVA; // RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData)
|
||||
DWORD BoundImportAddressTableRVA; // RVA to an optional bound IAT
|
||||
DWORD UnloadInformationTableRVA; // RVA to an optional unload info table
|
||||
DWORD TimeDateStamp; // 0 if not bound,
|
||||
// Otherwise, date/time of the target DLL
|
||||
|
||||
} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;
|
||||
|
||||
typedef const IMAGE_DELAYLOAD_DESCRIPTOR *PCIMAGE_DELAYLOAD_DESCRIPTOR;
|
||||
|
||||
#include "poppack.h"
|
||||
#endif
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Get the Delayload Imports directory. Returns the pointer to the first descriptor. The size of the directory is passed via variable dir_size.
|
||||
*/
|
||||
IMAGE_DELAYLOAD_DESCRIPTOR* get_delayed_imps(IN const BYTE* modulePtr, IN const size_t moduleSize, OUT size_t &dir_size);
|
||||
|
||||
/**
|
||||
Fill the Delayload Imports in the given module.
|
||||
\param modulePtr : the pointer to the module where the imports needs to be filled.
|
||||
\param moduleBase : the base to which the module was relocated, it may (or not) be the same as modulePtr
|
||||
\param func_resolver : the resolver that will be used for loading the imports
|
||||
\return : true if resolving all succeeded, false otherwise
|
||||
*/
|
||||
bool load_delayed_imports(BYTE* modulePtr, const ULONGLONG moduleBase, t_function_resolver* func_resolver = nullptr);
|
||||
|
||||
}; // namespace peconv
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief A definition of ExportedFunc class - used for storing the details of the exported function. Helper functions related to the export parsing.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Check if the pointer redirects to a forwarder - if so, return the length, otherwise return 0.
|
||||
*/
|
||||
size_t forwarder_name_len(BYTE* fPtr);
|
||||
|
||||
/**
|
||||
get the DLL name without the extension
|
||||
*/
|
||||
std::string get_dll_shortname(const std::string& str);
|
||||
|
||||
/**
|
||||
Get the function name from the string in a format: DLL_name.function_name
|
||||
*/
|
||||
std::string get_func_name(const std::string& str);
|
||||
|
||||
/**
|
||||
Convert ordinal value to the ordinal string (in a format #[ordinal])
|
||||
*/
|
||||
std::string ordinal_to_string(DWORD func_ordinal);
|
||||
|
||||
/**
|
||||
Check if the given string is in a format typical for storing ordinals (#[ordinal])
|
||||
*/
|
||||
bool is_ordinal_string(const std::string& str);
|
||||
|
||||
/**
|
||||
Get the ordinal value from the ordinal string (in a format #[ordinal])
|
||||
*/
|
||||
DWORD ordinal_string_to_val(const std::string& str);
|
||||
|
||||
/**
|
||||
Convert the function in a format: DLL_name.function_name into a normalized form (DLL name in lowercase).
|
||||
*/
|
||||
std::string format_dll_func(const std::string& str);
|
||||
|
||||
/**
|
||||
A class storing the information about the exported function.
|
||||
*/
|
||||
class ExportedFunc
|
||||
{
|
||||
public:
|
||||
/**
|
||||
Converts the name to the normalized format.
|
||||
*/
|
||||
static std::string formatName(std::string name);
|
||||
|
||||
std::string libName;
|
||||
std::string funcName;
|
||||
DWORD funcOrdinal;
|
||||
bool isByOrdinal;
|
||||
|
||||
//default constructor:
|
||||
ExportedFunc() : funcOrdinal(0), isByOrdinal(false) {}
|
||||
|
||||
ExportedFunc(const ExportedFunc& other);
|
||||
ExportedFunc(std::string libName, std::string funcName, DWORD funcOrdinal);
|
||||
ExportedFunc(std::string libName, DWORD funcOrdinal);
|
||||
ExportedFunc(const std::string &forwarderName);
|
||||
|
||||
/**
|
||||
Compare two functions with each other.
|
||||
Gives the priority to the named functions: if one of the compared functions is unnamed, the named one is treated as smaller.
|
||||
If both functions are unnamed, the function with the smaller ordinal is treated as smaller.
|
||||
Otherwise, the function with the shorter name is treated as smaller.
|
||||
*/
|
||||
bool operator < (const ExportedFunc& other) const
|
||||
{
|
||||
//if only one function is named, give the preference to the named one:
|
||||
const size_t thisNameLen = this->funcName.length();
|
||||
const size_t otherNameLen = other.funcName.length();
|
||||
if (thisNameLen == 0 && otherNameLen > 0) {
|
||||
return false;
|
||||
}
|
||||
if (thisNameLen > 0 && otherNameLen == 0) {
|
||||
return true;
|
||||
}
|
||||
//select by shorter lib name:
|
||||
int cmp = libName.compare(other.libName);
|
||||
if (cmp != 0) {
|
||||
return cmp < 0;
|
||||
}
|
||||
if (thisNameLen == 0 || otherNameLen == 0) {
|
||||
return this->funcOrdinal < other.funcOrdinal;
|
||||
}
|
||||
if (thisNameLen != otherNameLen) {
|
||||
return thisNameLen < otherNameLen;
|
||||
}
|
||||
cmp = funcName.compare(other.funcName);
|
||||
return cmp < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
Gets a string representation of the variable. Full info about the function: library, name, ordinal.
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
Gets a string representation of the variable. Short info about the function: only function name or ordinal (if the name is missing).
|
||||
*/
|
||||
std::string nameToString() const;
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return (funcName != "" || funcOrdinal != -1);
|
||||
}
|
||||
};
|
||||
|
||||
}; //namespace peconv
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Searching specific functions in PE's Exports Table.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
#include "function_resolver.h"
|
||||
#include "exports_mapper.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Gets the function address by the name. Uses Export Table lookup.
|
||||
WARNING: doesn't work for the forwarded functions.
|
||||
*/
|
||||
FARPROC get_exported_func(PVOID modulePtr, LPSTR wanted_name);
|
||||
|
||||
/**
|
||||
Gets list of all the functions from a given module that are exported by names.
|
||||
*/
|
||||
size_t get_exported_names(PVOID modulePtr, std::vector<std::string> &names_list);
|
||||
|
||||
/**
|
||||
Function resolver using Export Table lookup.
|
||||
*/
|
||||
class export_based_resolver : default_func_resolver {
|
||||
public:
|
||||
/**
|
||||
Get the address (VA) of the function with the given name, from the given DLL.
|
||||
Uses Export Table lookup as a primary method of finding the import. On failure it falls back to the default Functions Resolver.
|
||||
\param func_name : the name of the function
|
||||
\param lib_name : the name of the DLL
|
||||
\return Virtual Address of the exported function
|
||||
*/
|
||||
virtual FARPROC resolve_func(LPSTR lib_name, LPSTR func_name);
|
||||
};
|
||||
|
||||
/**
|
||||
Read the DLL name from the Export Table.
|
||||
*/
|
||||
LPSTR read_dll_name(HMODULE modulePtr);
|
||||
|
||||
}; //namespace peconv
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief A definition of ExportsMapper class. Creates a lookup of all the exported functions from the supplied DLLs. Allows to associate an address with a corresponding function.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
#include "pe_raw_to_virtual.h"
|
||||
#include "peconv/exported_func.h"
|
||||
#include "peconv/file_util.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
class ExportsMapper {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Appends the given DLL to the lookup table of exported functions. Returns the number of functions exported from this DLL (not forwarded).
|
||||
\param moduleName : name of the DLL
|
||||
\param modulePtr : buffer containing the DLL in a Virtual format
|
||||
\param moduleBase : a base address to which the given DLL was relocated
|
||||
*/
|
||||
size_t add_to_lookup(std::string moduleName, HMODULE modulePtr, ULONGLONG moduleBase);
|
||||
|
||||
/**
|
||||
Appends the given DLL to the lookup table of exported functions. Returns the number of functions exported from this DLL (not forwarded).
|
||||
Assumes that the module was relocated to the same address as is the address of the given buffer (modulePtr).
|
||||
(A wrapper for the case if we are adding a DLL that was loaded within the current process.)
|
||||
\param moduleName : name of the DLL
|
||||
\param modulePtr : buffer containing the DLL in a Virtual format.
|
||||
*/
|
||||
size_t add_to_lookup(std::string moduleName, HMODULE modulePtr)
|
||||
{
|
||||
return add_to_lookup(moduleName, modulePtr, reinterpret_cast<ULONGLONG>(modulePtr));
|
||||
}
|
||||
|
||||
/**
|
||||
Find the set of Exported Functions that can be mapped to the given VA. Includes forwarders, and function aliases.
|
||||
*/
|
||||
const std::set<ExportedFunc>* find_exports_by_va(ULONGLONG va) const
|
||||
{
|
||||
std::map<ULONGLONG, std::set<ExportedFunc>>::const_iterator itr = va_to_func.find(va);
|
||||
if (itr != va_to_func.end()) {
|
||||
const std::set<ExportedFunc> &fSet = itr->second;
|
||||
return &fSet;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
Retrieve the full path of the DLL with the given short name.
|
||||
*/
|
||||
std::string get_dll_path(std::string short_name) const
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator found = this->dll_shortname_to_path.find(short_name);
|
||||
if (found == dll_shortname_to_path.end()) {
|
||||
return "";
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
|
||||
/**
|
||||
Retrieve the full name of the DLL (including the extension) using its short name (without the extension).
|
||||
*/
|
||||
std::string get_dll_fullname(std::string short_name) const
|
||||
{
|
||||
std::string dll_path = get_dll_path(short_name);
|
||||
if (dll_path.length() == 0) return "";
|
||||
|
||||
return get_file_name(dll_path);
|
||||
}
|
||||
|
||||
/**
|
||||
Find an Exported Function that can be mapped to the given VA,
|
||||
*/
|
||||
const ExportedFunc* find_export_by_va(ULONGLONG va) const
|
||||
{
|
||||
const std::set<ExportedFunc>* exp_set = find_exports_by_va(va);
|
||||
if (exp_set == NULL) return NULL;
|
||||
|
||||
std::set<ExportedFunc>::iterator fItr = exp_set->begin();
|
||||
const ExportedFunc* func = &(*fItr);
|
||||
return func;
|
||||
}
|
||||
|
||||
void print_va_to_func(std::stringstream &stream) const;
|
||||
void print_func_to_va(std::stringstream &stream) const;
|
||||
|
||||
|
||||
private:
|
||||
enum ADD_FUNC_RES { RES_INVALID = 0, RES_MAPPED = 1, RES_FORWARDED = 2 };
|
||||
ADD_FUNC_RES add_function_to_lookup(HMODULE modulePtr, ULONGLONG moduleBase, size_t moduleSize, ExportedFunc &currFunc, DWORD callRVA);
|
||||
|
||||
bool add_forwarded(ExportedFunc &currFunc, DWORD callRVA, PBYTE modulePtr, size_t moduleSize);
|
||||
bool add_to_maps(ULONGLONG va, ExportedFunc &currFunc);
|
||||
|
||||
size_t resolve_forwarders(const ULONGLONG va, ExportedFunc &currFunc);
|
||||
size_t make_ord_lookup_tables(PVOID modulePtr, size_t moduleSize, std::map<PDWORD, DWORD> &va_to_ord);
|
||||
|
||||
protected:
|
||||
/**
|
||||
Add a function and a VA into a mutual mapping.
|
||||
*/
|
||||
void associateVaAndFunc(ULONGLONG va, const ExportedFunc& func)
|
||||
{
|
||||
va_to_func[va].insert(func);
|
||||
func_to_va[func] = va;
|
||||
}
|
||||
|
||||
/**
|
||||
A map associating VA of the function with the related exports.
|
||||
*/
|
||||
std::map<ULONGLONG, std::set<ExportedFunc>> va_to_func;
|
||||
|
||||
/**
|
||||
A map associating an exported functions with its forwarders.
|
||||
*/
|
||||
std::map<ExportedFunc, std::set<ExportedFunc>> forwarders_lookup;
|
||||
|
||||
/**
|
||||
A map associating an exported functions with its VA.
|
||||
*/
|
||||
std::map<ExportedFunc, ULONGLONG> func_to_va;
|
||||
|
||||
/**
|
||||
A map associating DLL shortname with the full path to the DLL.
|
||||
*/
|
||||
std::map<std::string, std::string> dll_shortname_to_path;
|
||||
};
|
||||
|
||||
}; //namespace peconv
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions related to operations on files. Wrappers for read/write.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "buffer_util.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Maps a file with the given path and copies its raw content into the output buffer.
|
||||
If read_size is not zero, it reads maximum read_size of bytes. If read_size is zero, it reads the full file.
|
||||
The actual read size is returned back in read_size.
|
||||
Automatically allocates a buffer of the required size.
|
||||
*/
|
||||
peconv::ALIGNED_BUF load_file(IN const char *filename, OUT size_t &r_size);
|
||||
|
||||
/**
|
||||
Reads a raw content of the file with the given path.
|
||||
If read_size is not zero, it reads maximum read_size of bytes. If read_size is zero, it reads the full file.
|
||||
The actual read size is returned back in read_size.
|
||||
Automatically allocates a buffer of the required size.
|
||||
*/
|
||||
peconv::ALIGNED_BUF read_from_file(IN const char *path, IN OUT size_t &read_size);
|
||||
|
||||
/**
|
||||
Writes a buffer of bytes into a file of given path.
|
||||
\param path : the path to the output file
|
||||
\param dump_data : the buffer to be dumped
|
||||
\param dump_size : the size of data to be dumped (in bytes)
|
||||
\return true if succeeded, false if failed
|
||||
*/
|
||||
bool dump_to_file(IN const char *path, IN PBYTE dump_data, IN size_t dump_size);
|
||||
|
||||
/**
|
||||
Free the buffer allocated by load_file/read_from_file
|
||||
*/
|
||||
void free_file(IN peconv::ALIGNED_BUF buffer);
|
||||
|
||||
/**
|
||||
Get the file name from the given path.
|
||||
*/
|
||||
std::string get_file_name(IN const std::string full_path);
|
||||
|
||||
/**
|
||||
Get the directory name from the given path. It assumes that a directory name always ends with a separator ("/" or "\")
|
||||
*/
|
||||
std::string get_directory_name(IN const std::string full_path);
|
||||
|
||||
}; //namespace peconv
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions related to finding a base to which the module was relocated.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Try to find a base to which the PE file was relocated, basing on the filled relocations.
|
||||
WARNING: the found base is an estimate, and sometimes may not be fully accurate.
|
||||
\param module_ptr : the module which's base is being searched
|
||||
\param module_size : the size of the given module
|
||||
\return the base to which the module was relocated
|
||||
*/
|
||||
ULONGLONG find_base_candidate(IN BYTE *module_ptr, IN size_t module_size);
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions and classes responsible for fixing Import Table. A definition of ImportedDllCoverage class.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
#include "exports_lookup.h"
|
||||
#include "exports_mapper.h"
|
||||
|
||||
#define MIN_DLL_LEN 5
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
a helper class that allows to store information about functions that could not be covered by the given mapping
|
||||
*/
|
||||
class ImpsNotCovered
|
||||
{
|
||||
public:
|
||||
ImpsNotCovered() {}
|
||||
~ImpsNotCovered() {}
|
||||
|
||||
/*
|
||||
Number of stored records
|
||||
*/
|
||||
size_t count() { return thunkToAddr.size(); }
|
||||
|
||||
void insert(ULONGLONG thunk, ULONGLONG searchedAddr);
|
||||
|
||||
std::map<ULONGLONG, ULONGLONG> thunkToAddr; //addresses of not recovered functions with their thunks (call_via)
|
||||
};
|
||||
|
||||
/**
|
||||
fix imports in the given module, using the given map of all available exports
|
||||
*/
|
||||
bool fix_imports(IN OUT PVOID modulePtr, IN size_t moduleSize, IN const peconv::ExportsMapper& exportsMap, OUT OPTIONAL peconv::ImpsNotCovered* notCovered);
|
||||
|
||||
/**
|
||||
a helper class that allows to find out where the functions are imported from
|
||||
*/
|
||||
class ImportedDllCoverage
|
||||
{
|
||||
public:
|
||||
/**
|
||||
A constructor of an object of ImportedDllCoverage class.
|
||||
\param _addresses : the list of filled imports (VAs): the addresses to be covered
|
||||
\param _exportsMap : the map of the exports of all the loaded DLLs (the space in which we will be searching)
|
||||
*/
|
||||
ImportedDllCoverage(std::set<ULONGLONG>& _addresses, const peconv::ExportsMapper& _exportsMap)
|
||||
: addresses(_addresses), exportsMap(_exportsMap)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if all the addresses can be covered by one DLL. If yes, this dll will be saved into: dllName.
|
||||
\return true if the covering DLL for the addresses was found. false otherwise.
|
||||
*/
|
||||
bool findCoveringDll();
|
||||
|
||||
/**
|
||||
Maps the addresses from the set to functions from the given DLL.
|
||||
Results are saved into: addrToFunc.
|
||||
Addresses that could not be covered by the given DLL are saved into notFound.
|
||||
Before each execution, the content of involved variables is erased.
|
||||
\param _mappedDllName : the name of the DLL that we will be used to mapping. This DLL is saved into mappedDllName.
|
||||
\return a number of covered functions
|
||||
*/
|
||||
size_t mapAddressesToFunctions(const std::string &_mappedDllName);
|
||||
|
||||
/**
|
||||
Check if the functions mapping is complete.
|
||||
\return the status: true if all the addresses are mapped to specific exports, false if not
|
||||
*/
|
||||
bool isMappingComplete() { return (addresses.size() == addrToFunc.size()) ? true : false; }
|
||||
|
||||
/**
|
||||
A mapping associating each of the covered function addresses with the set of exports (from mapped DLL) that cover this address
|
||||
*/
|
||||
std::map<ULONGLONG, std::set<ExportedFunc>> addrToFunc;
|
||||
|
||||
/**
|
||||
Addresses of the functions not found in the mapped DLL
|
||||
*/
|
||||
std::set<ULONGLONG> notFound;
|
||||
|
||||
/**
|
||||
Name of the covering DLL
|
||||
*/
|
||||
std::string dllName;
|
||||
|
||||
protected:
|
||||
/**
|
||||
A name of the DLL that was used for mapping. In a typical scenario it will be the same as covering DLL, but may be set different.
|
||||
*/
|
||||
std::string mappedDllName;
|
||||
|
||||
/**
|
||||
A supplied set of the addresses of imported functions.
|
||||
Those addressed will be covered (associated with the corresponding exports from available DLLs, defined by exportsMap).
|
||||
*/
|
||||
std::set<ULONGLONG> &addresses;
|
||||
|
||||
/**
|
||||
A supplied exportsMap. Only used as a lookup, no changes applied.
|
||||
*/
|
||||
const peconv::ExportsMapper& exportsMap;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Definitions of basic Imports Resolver classes. They can be used for filling imports when the PE is loaded.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
/**
|
||||
A base class for functions resolver.
|
||||
*/
|
||||
class t_function_resolver {
|
||||
public:
|
||||
/**
|
||||
Get the address (VA) of the function with the given name, from the given DLL.
|
||||
\param func_name : the name of the function
|
||||
\param lib_name : the name of the DLL
|
||||
\return Virtual Address of the exported function
|
||||
*/
|
||||
virtual FARPROC resolve_func(LPSTR lib_name, LPSTR func_name) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
A default functions resolver, using LoadLibraryA and GetProcAddress.
|
||||
*/
|
||||
class default_func_resolver : t_function_resolver {
|
||||
public:
|
||||
/**
|
||||
Get the address (VA) of the function with the given name, from the given DLL, using LoadLibraryA and GetProcAddress.
|
||||
\param func_name : the name of the function
|
||||
\param lib_name : the name of the DLL
|
||||
\return Virtual Address of the exported function
|
||||
*/
|
||||
virtual FARPROC resolve_func(LPSTR lib_name, LPSTR func_name);
|
||||
};
|
||||
|
||||
}; //namespace peconv
|
||||
135
Etw Syscall/libpeconv-master/libpeconv/include/peconv/hooks.h
Normal file
135
Etw Syscall/libpeconv-master/libpeconv/include/peconv/hooks.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions related to hooking the loaded PE. Reditecting/replacing a functions with another.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "function_resolver.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "peconv/buffer_util.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
A buffer storing a binary patch, that can be applied on a module. Used as a restorable backup in case of function patching.
|
||||
*/
|
||||
class PatchBackup {
|
||||
public:
|
||||
/**
|
||||
Creates an empty backup.
|
||||
*/
|
||||
PatchBackup()
|
||||
: buffer(nullptr), bufferSize(0), sourcePtr(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~PatchBackup() {
|
||||
deleteBackup();
|
||||
}
|
||||
|
||||
/**
|
||||
Destroys the backup and resets internal fields.
|
||||
*/
|
||||
void deleteBackup()
|
||||
{
|
||||
if (buffer) {
|
||||
delete[] buffer;
|
||||
bufferSize = 0;
|
||||
sourcePtr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Reads bytes from the binary to the backup. The source buffer must be within the current process.
|
||||
*/
|
||||
bool makeBackup(BYTE *patch_ptr, size_t patch_size);
|
||||
|
||||
/**
|
||||
Applies the backup back to the pointer from which it was read.
|
||||
*/
|
||||
bool applyBackup();
|
||||
|
||||
/**
|
||||
Checks if the buffer was filled.
|
||||
*/
|
||||
bool isBackup()
|
||||
{
|
||||
return buffer != nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
BYTE *buffer;
|
||||
size_t bufferSize;
|
||||
|
||||
BYTE *sourcePtr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
A functions resolver that can be used for hooking IAT. Allows for defining functions that are supposed to be replaced.
|
||||
*/
|
||||
class hooking_func_resolver : peconv::default_func_resolver {
|
||||
public:
|
||||
/**
|
||||
Define a function that will be replaced.
|
||||
\param name : a name of the function that will be replaced
|
||||
\param function : an address of the replacement function
|
||||
*/
|
||||
void add_hook(std::string name, FARPROC function)
|
||||
{
|
||||
hooks_map[name] = function;
|
||||
}
|
||||
|
||||
/**
|
||||
Get the address (VA) of the function with the given name, from the given DLL. If the function was hooked, it retrieves the address of the replacement function instead.
|
||||
\param func_name : the name of the function
|
||||
\param lib_name : the name of the DLL
|
||||
\return Virtual Address of the exported function, or the address of the replacement function.
|
||||
*/
|
||||
virtual FARPROC resolve_func(LPSTR lib_name, LPSTR func_name);
|
||||
|
||||
private:
|
||||
std::map<std::string, FARPROC> hooks_map;
|
||||
};
|
||||
|
||||
/**
|
||||
Installs inline hook at the given ptr. Returns the number of bytes overwriten.
|
||||
64 bit version.
|
||||
\param ptr : pointer to the function to be replaced
|
||||
\param new_offset : VA of the new function
|
||||
\param backup : (optional) backup that can be used to reverse the changes
|
||||
\return size of the applied patch
|
||||
*/
|
||||
size_t redirect_to_local64(void *ptr, ULONGLONG new_offset, PatchBackup* backup = nullptr);
|
||||
|
||||
/**
|
||||
Installs inline hook at the given ptr. Returns the number of bytes overwriten.
|
||||
32 bit version.
|
||||
\param ptr : pointer to the function to be replaced
|
||||
\param new_offset : VA of the new function
|
||||
\param backup : (optional) backup that can be used to reverse the changes
|
||||
\return size of the applied patch
|
||||
*/
|
||||
size_t redirect_to_local32(void *ptr, DWORD new_offset, PatchBackup* backup = nullptr);
|
||||
|
||||
/**
|
||||
Installs inline hook at the given ptr. Returns the number of bytes overwriten.
|
||||
Uses bitness of the current applications for the bitness of the intalled hook.
|
||||
\param ptr : pointer to the function to be replaced
|
||||
\param new_function_ptr : pointer to the new function
|
||||
\param backup : (optional) backup that can be used to reverse the changes
|
||||
\return size of the applied patch
|
||||
*/
|
||||
size_t redirect_to_local(void *ptr, void* new_function_ptr, PatchBackup* backup = nullptr);
|
||||
|
||||
/**
|
||||
Replaces a target address of JMP [DWORD] or CALL [DWORD]
|
||||
*/
|
||||
bool replace_target(BYTE *ptr, ULONGLONG dest_addr);
|
||||
|
||||
};//namespace peconv
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Parsing and filling the Import Table.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
#include "function_resolver.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
A class defining a callback that will be executed when the next imported function was found
|
||||
*/
|
||||
class ImportThunksCallback
|
||||
{
|
||||
public:
|
||||
ImportThunksCallback(BYTE* _modulePtr, size_t _moduleSize)
|
||||
: modulePtr(_modulePtr), moduleSize(_moduleSize)
|
||||
{
|
||||
this->is64b = is64bit((BYTE*)modulePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
A callback that will be executed by process_import_table when the next imported function was found
|
||||
\param libName : the pointer to the DLL name
|
||||
\param origFirstThunkPtr : the pointer to the Original First Thunk
|
||||
\param firstThunkPtr : the pointer to the First Thunk
|
||||
\return : true if processing succeeded, false otherwise
|
||||
*/
|
||||
virtual bool processThunks(LPSTR libName, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr) = 0;
|
||||
|
||||
protected:
|
||||
BYTE* modulePtr;
|
||||
size_t moduleSize;
|
||||
bool is64b;
|
||||
};
|
||||
|
||||
/**
|
||||
Process the given PE's import table and execute the callback each time when the new imported function was found
|
||||
\param modulePtr : a pointer to the loded PE (in virtual format)
|
||||
\param moduleSize : a size of the supplied PE
|
||||
\param callback : a callback that will be executed to process each imported function
|
||||
\return : true if processing succeeded, false otherwise
|
||||
*/
|
||||
bool process_import_table(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ImportThunksCallback *callback);
|
||||
|
||||
/**
|
||||
Fills imports of the given PE with the help of the defined functions resolver.
|
||||
\param modulePtr : a pointer to the loded PE (in virtual format)
|
||||
\param func_resolver : a resolver that will be used to fill the thunk of the import
|
||||
\return : true if loading all functions succeeded, false otherwise
|
||||
*/
|
||||
bool load_imports(BYTE* modulePtr, t_function_resolver* func_resolver=nullptr);
|
||||
|
||||
/**
|
||||
Checks if the given PE has a valid import table.
|
||||
*/
|
||||
bool has_valid_import_table(const PBYTE modulePtr, size_t moduleSize);
|
||||
|
||||
/**
|
||||
Checks if the given lib_name is a valid DLL name.
|
||||
A valid name must contain printable characters. Empty name is also acceptable (may have been erased).
|
||||
*/
|
||||
bool is_valid_import_name(const PBYTE modulePtr, const size_t moduleSize, LPSTR lib_name);
|
||||
|
||||
}; // namespace peconv
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief A definition of ImportsUneraser class - for recovery of a partialy erased Import Table.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <iterator>
|
||||
#include "fix_imports.h"
|
||||
#include "caves.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
A class responsible for recovering the partially erased Import Table from the PE.
|
||||
*/
|
||||
class ImportsUneraser
|
||||
{
|
||||
public:
|
||||
ImportsUneraser(PVOID _modulePtr, size_t _moduleSize)
|
||||
: modulePtr((PBYTE)_modulePtr), moduleSize(_moduleSize)
|
||||
{
|
||||
is64 = peconv::is64bit((BYTE*)modulePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
Fill the imported functions' names in the given Import Descriptor, using the given coverage.
|
||||
Collect addressees of functions that couldn't be filled with the given mapping.
|
||||
\param lib_desc : the IMAGE_IMPORT_DESCRIPTOR where the functions' names should be set
|
||||
\param dllCoverage : a mapping associating addresses with the corresponding exports from available DLLs
|
||||
\param not_covered : a set of addresses that could not be found in the supplied mapping
|
||||
\return true if succeeded
|
||||
*/
|
||||
bool uneraseDllImports(IN OUT IMAGE_IMPORT_DESCRIPTOR* lib_desc, IN ImportedDllCoverage &dllCoverage, OUT OPTIONAL ImpsNotCovered* not_covered);
|
||||
|
||||
/**
|
||||
Recover the imported DLL name in the given Import Descriptor, filling it with the given dll_name.
|
||||
*/
|
||||
bool uneraseDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &dll_name);
|
||||
|
||||
protected:
|
||||
/**
|
||||
Copy the given DLL name into the given IMAGE_IMPORT_DESCRIPTOR. Validates the data correctness before writing.
|
||||
\param lib_desc : the IMAGE_IMPORT_DESCRIPTOR where the DLL name should be set
|
||||
\param dll_name : the DLL name that needs to be written into the lib_desc
|
||||
\return true if succeeded
|
||||
*/
|
||||
bool writeFoundDllName(IMAGE_IMPORT_DESCRIPTOR* lib_desc, const std::string &dll_name);
|
||||
|
||||
/**
|
||||
Fill the names of imported functions with names of the prepared mapping.
|
||||
Collect addressees of functions that couldn't be filled with the given mapping.
|
||||
\param lib_desc : the IMAGE_IMPORT_DESCRIPTOR where the functions' names should be set
|
||||
\param ordinal_flag : the flag that is used to recognize import by ordinal (32 or 64 bit)
|
||||
\param addr_to_func : a mapping assigning functions' addresses to their definitions (names etc.)
|
||||
\param not_covered : a set of addresses that could not be found in the supplied mapping
|
||||
\return true if succeeded
|
||||
*/
|
||||
template <typename FIELD_T, typename IMAGE_THUNK_DATA_T>
|
||||
bool 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* not_covered
|
||||
);
|
||||
|
||||
template <typename FIELD_T>
|
||||
bool 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
|
||||
);
|
||||
|
||||
/**
|
||||
Fill the function data into the given IMAGE_THUNK_DATA.
|
||||
\param desc : the poiner to IMAGE_THUNK_DATA that will be filled
|
||||
\param ordinal_flag : an ordinal flag: 32 or 64 bit
|
||||
\param foundFunc : the ExportedFunc that will be used for filling the desc
|
||||
*/
|
||||
template <typename FIELD_T, typename IMAGE_THUNK_DATA_T>
|
||||
bool writeFoundFunction(IMAGE_THUNK_DATA_T* desc, const FIELD_T ordinal_flag, const ExportedFunc &foundFunc);
|
||||
|
||||
PBYTE modulePtr;
|
||||
size_t moduleSize;
|
||||
bool is64;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Definitions of various versions of Load Config Directory (new fields added with new versions for Windows).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <pshpack4.h>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_CODE_INTEGRITY: a structure used by IMAGE_LOAD_CONFIG_DIR - the Windows 10 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10 {
|
||||
WORD Flags; // Flags to indicate if CI information is available, etc.
|
||||
WORD Catalog; // 0xFFFF means not available
|
||||
DWORD CatalogOffset;
|
||||
DWORD Reserved; // Additional bitmask to be defined later
|
||||
} IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10;
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_DIR32: the Windows 10 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_DIR32_W10 {
|
||||
DWORD Size;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD GlobalFlagsClear;
|
||||
DWORD GlobalFlagsSet;
|
||||
DWORD CriticalSectionDefaultTimeout;
|
||||
DWORD DeCommitFreeBlockThreshold;
|
||||
DWORD DeCommitTotalFreeThreshold;
|
||||
DWORD LockPrefixTable; // VA
|
||||
DWORD MaximumAllocationSize;
|
||||
DWORD VirtualMemoryThreshold;
|
||||
DWORD ProcessHeapFlags;
|
||||
DWORD ProcessAffinityMask;
|
||||
WORD CSDVersion;
|
||||
WORD DependentLoadFlags;
|
||||
DWORD EditList; // VA
|
||||
DWORD SecurityCookie; // VA
|
||||
DWORD SEHandlerTable; // VA
|
||||
DWORD SEHandlerCount;
|
||||
DWORD GuardCFCheckFunctionPointer; // VA
|
||||
DWORD GuardCFDispatchFunctionPointer; // VA
|
||||
DWORD GuardCFFunctionTable; // VA
|
||||
DWORD GuardCFFunctionCount;
|
||||
DWORD GuardFlags;
|
||||
IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10 CodeIntegrity;
|
||||
DWORD GuardAddressTakenIatEntryTable; // VA
|
||||
DWORD GuardAddressTakenIatEntryCount;
|
||||
DWORD GuardLongJumpTargetTable; // VA
|
||||
DWORD GuardLongJumpTargetCount;
|
||||
DWORD DynamicValueRelocTable; // VA
|
||||
DWORD CHPEMetadataPointer;
|
||||
DWORD GuardRFFailureRoutine; // VA
|
||||
DWORD GuardRFFailureRoutineFunctionPointer; // VA
|
||||
DWORD DynamicValueRelocTableOffset;
|
||||
WORD DynamicValueRelocTableSection;
|
||||
WORD Reserved2;
|
||||
DWORD GuardRFVerifyStackPointerFunctionPointer; // VA
|
||||
DWORD HotPatchTableOffset;
|
||||
DWORD Reserved3;
|
||||
DWORD EnclaveConfigurationPointer; // VA
|
||||
} IMAGE_LOAD_CONFIG_DIR32_W10;
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_DIR64: the Windows 10 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_DIR64_W10 {
|
||||
DWORD Size;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD GlobalFlagsClear;
|
||||
DWORD GlobalFlagsSet;
|
||||
DWORD CriticalSectionDefaultTimeout;
|
||||
ULONGLONG DeCommitFreeBlockThreshold;
|
||||
ULONGLONG DeCommitTotalFreeThreshold;
|
||||
ULONGLONG LockPrefixTable; // VA
|
||||
ULONGLONG MaximumAllocationSize;
|
||||
ULONGLONG VirtualMemoryThreshold;
|
||||
ULONGLONG ProcessAffinityMask;
|
||||
DWORD ProcessHeapFlags;
|
||||
WORD CSDVersion;
|
||||
WORD DependentLoadFlags;
|
||||
ULONGLONG EditList; // VA
|
||||
ULONGLONG SecurityCookie; // VA
|
||||
ULONGLONG SEHandlerTable; // VA
|
||||
ULONGLONG SEHandlerCount;
|
||||
ULONGLONG GuardCFCheckFunctionPointer; // VA
|
||||
ULONGLONG GuardCFDispatchFunctionPointer; // VA
|
||||
ULONGLONG GuardCFFunctionTable; // VA
|
||||
ULONGLONG GuardCFFunctionCount;
|
||||
DWORD GuardFlags;
|
||||
IMAGE_LOAD_CONFIG_CODE_INTEGRITY_W10 CodeIntegrity;
|
||||
ULONGLONG GuardAddressTakenIatEntryTable; // VA
|
||||
ULONGLONG GuardAddressTakenIatEntryCount;
|
||||
ULONGLONG GuardLongJumpTargetTable; // VA
|
||||
ULONGLONG GuardLongJumpTargetCount;
|
||||
ULONGLONG DynamicValueRelocTable; // VA
|
||||
ULONGLONG CHPEMetadataPointer; // VA
|
||||
ULONGLONG GuardRFFailureRoutine; // VA
|
||||
ULONGLONG GuardRFFailureRoutineFunctionPointer; // VA
|
||||
DWORD DynamicValueRelocTableOffset;
|
||||
WORD DynamicValueRelocTableSection;
|
||||
WORD Reserved2;
|
||||
ULONGLONG GuardRFVerifyStackPointerFunctionPointer; // VA
|
||||
DWORD HotPatchTableOffset;
|
||||
DWORD Reserved3;
|
||||
ULONGLONG EnclaveConfigurationPointer; // VA
|
||||
} IMAGE_LOAD_CONFIG_DIR64_W10;
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_DIR32: the Windows 8 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_DIR32_W8 {
|
||||
DWORD Size;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD GlobalFlagsClear;
|
||||
DWORD GlobalFlagsSet;
|
||||
DWORD CriticalSectionDefaultTimeout;
|
||||
DWORD DeCommitFreeBlockThreshold;
|
||||
DWORD DeCommitTotalFreeThreshold;
|
||||
DWORD LockPrefixTable; // VA
|
||||
DWORD MaximumAllocationSize;
|
||||
DWORD VirtualMemoryThreshold;
|
||||
DWORD ProcessHeapFlags;
|
||||
DWORD ProcessAffinityMask;
|
||||
WORD CSDVersion;
|
||||
WORD DependentLoadFlags;
|
||||
DWORD EditList; // VA
|
||||
DWORD SecurityCookie; // VA
|
||||
DWORD SEHandlerTable; // VA
|
||||
DWORD SEHandlerCount;
|
||||
DWORD GuardCFCheckFunctionPointer; // VA
|
||||
DWORD GuardCFDispatchFunctionPointer; // VA
|
||||
DWORD GuardCFFunctionTable; // VA
|
||||
DWORD GuardCFFunctionCount;
|
||||
DWORD GuardFlags;
|
||||
} IMAGE_LOAD_CONFIG_DIR32_W8;
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_DIR64: the Windows 8 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_DIR64_W8 {
|
||||
DWORD Size;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD GlobalFlagsClear;
|
||||
DWORD GlobalFlagsSet;
|
||||
DWORD CriticalSectionDefaultTimeout;
|
||||
ULONGLONG DeCommitFreeBlockThreshold;
|
||||
ULONGLONG DeCommitTotalFreeThreshold;
|
||||
ULONGLONG LockPrefixTable; // VA
|
||||
ULONGLONG MaximumAllocationSize;
|
||||
ULONGLONG VirtualMemoryThreshold;
|
||||
ULONGLONG ProcessAffinityMask;
|
||||
DWORD ProcessHeapFlags;
|
||||
WORD CSDVersion;
|
||||
WORD DependentLoadFlags;
|
||||
ULONGLONG EditList; // VA
|
||||
ULONGLONG SecurityCookie; // VA
|
||||
ULONGLONG SEHandlerTable; // VA
|
||||
ULONGLONG SEHandlerCount;
|
||||
ULONGLONG GuardCFCheckFunctionPointer; // VA
|
||||
ULONGLONG GuardCFDispatchFunctionPointer; // VA
|
||||
ULONGLONG GuardCFFunctionTable; // VA
|
||||
ULONGLONG GuardCFFunctionCount;
|
||||
DWORD GuardFlags;
|
||||
} IMAGE_LOAD_CONFIG_DIR64_W8;
|
||||
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_DIR32: the Windows 7 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_DIR32_W7 {
|
||||
DWORD Size;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD GlobalFlagsClear;
|
||||
DWORD GlobalFlagsSet;
|
||||
DWORD CriticalSectionDefaultTimeout;
|
||||
DWORD DeCommitFreeBlockThreshold;
|
||||
DWORD DeCommitTotalFreeThreshold;
|
||||
DWORD LockPrefixTable; // VA
|
||||
DWORD MaximumAllocationSize;
|
||||
DWORD VirtualMemoryThreshold;
|
||||
DWORD ProcessHeapFlags;
|
||||
DWORD ProcessAffinityMask;
|
||||
WORD CSDVersion;
|
||||
WORD DependentLoadFlags;
|
||||
DWORD EditList; // VA
|
||||
DWORD SecurityCookie; // VA
|
||||
DWORD SEHandlerTable; // VA
|
||||
DWORD SEHandlerCount;
|
||||
} IMAGE_LOAD_CONFIG_DIR32_W7;
|
||||
|
||||
/**
|
||||
IMAGE_LOAD_CONFIG_DIR64: the Windows 7 version.
|
||||
*/
|
||||
typedef struct _IMAGE_LOAD_CONFIG_DIR64_W7 {
|
||||
DWORD Size;
|
||||
DWORD TimeDateStamp;
|
||||
WORD MajorVersion;
|
||||
WORD MinorVersion;
|
||||
DWORD GlobalFlagsClear;
|
||||
DWORD GlobalFlagsSet;
|
||||
DWORD CriticalSectionDefaultTimeout;
|
||||
ULONGLONG DeCommitFreeBlockThreshold;
|
||||
ULONGLONG DeCommitTotalFreeThreshold;
|
||||
ULONGLONG LockPrefixTable; // VA
|
||||
ULONGLONG MaximumAllocationSize;
|
||||
ULONGLONG VirtualMemoryThreshold;
|
||||
ULONGLONG ProcessAffinityMask;
|
||||
DWORD ProcessHeapFlags;
|
||||
WORD CSDVersion;
|
||||
WORD DependentLoadFlags;
|
||||
ULONGLONG EditList; // VA
|
||||
ULONGLONG SecurityCookie; // VA
|
||||
ULONGLONG SEHandlerTable; // VA
|
||||
ULONGLONG SEHandlerCount;
|
||||
} IMAGE_LOAD_CONFIG_DIR64_W7;
|
||||
}; //namespace peconv
|
||||
|
||||
#include <poppack.h>
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Fetching Load Config Directory and recognizing its version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#include "buffer_util.h"
|
||||
#include "load_config_defs.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
A version of Load Config Directory.
|
||||
*/
|
||||
typedef enum {
|
||||
LOAD_CONFIG_NONE = 0, /**< Load Config Directory not found */
|
||||
LOAD_CONFIG_W7_VER = 7, /**< Load Config Directory in the Windows 7 version */
|
||||
LOAD_CONFIG_W8_VER = 8, /**< Load Config Directory in the Windows 8 version */
|
||||
LOAD_CONFIG_W10_VER = 10, /**< Load Config Directory in the Windows 10 version */
|
||||
LOAD_CONFIG_UNK_VER = -1 /**< Load Config Directory in an unknown version */
|
||||
} t_load_config_ver;
|
||||
|
||||
/**
|
||||
Get a pointer to the Load Config Directory within the given PE.
|
||||
\param buffer : a buffer containing the PE file in a Virtual format
|
||||
\param buf_size : size of the buffer
|
||||
\return a pointer to the Load Config Directory, NULL if the given PE does not have this directory
|
||||
*/
|
||||
BYTE* get_load_config_ptr(BYTE* buffer, size_t buf_size);
|
||||
|
||||
/**
|
||||
Detect which version of Load Config Directory was used in the given PE.
|
||||
\param buffer : a buffer containing the PE file in a Virtual format
|
||||
\param buf_size : size of the buffer
|
||||
\param ld_config_ptr : pointer to the Load Config Directory within the given PE
|
||||
\return detected version of Load Config Directory
|
||||
*/
|
||||
t_load_config_ver get_load_config_version(BYTE* buffer, size_t buf_size, BYTE* ld_config_ptr);
|
||||
|
||||
}; // namespace peconv
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Dumping PE from the memory buffer into a file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "exports_mapper.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
A mode in which the PE fille be dumped.
|
||||
*/
|
||||
typedef enum {
|
||||
PE_DUMP_AUTO = 0, /**< autodetect which dump mode is the most suitable for the given input */
|
||||
PE_DUMP_VIRTUAL,/**< dump as it is in the memory (virtual) */
|
||||
PE_DUMP_UNMAP, /**< convert to the raw format: using raw sections' headers */
|
||||
PE_DUMP_REALIGN, /**< convert to the raw format: by realigning raw sections' headers to be the same as virtual (useful if the PE was unpacked in memory) */
|
||||
PE_DUMP_MODES_COUNT /**< total number of the dump modes */
|
||||
} t_pe_dump_mode;
|
||||
|
||||
/**
|
||||
Detect dump mode that is the most suitable for the given input.
|
||||
\param buffer : the buffer containing the PE to be dumped.
|
||||
\param buffer_size : the size of the given buffer
|
||||
*/
|
||||
t_pe_dump_mode detect_dump_mode(IN const BYTE* buffer, IN size_t buffer_size);
|
||||
|
||||
/**
|
||||
Dumps PE from the fiven buffer into a file. It expects the module base and size to be given.
|
||||
\param outputFilePath : name of the file where the dump should be saved
|
||||
\param buffer : the buffer containing the PE to be dumped. WARNING: the buffer may be preprocessed before dumping.
|
||||
\param buffer_size : the size of the given buffer
|
||||
\param module_base : the base to which the PE buffer was relocated
|
||||
\param dump_mode : specifies in which format the PE should be dumped. If the mode was set to PE_DUMP_AUTO, it autodetects mode and returns the detected one.
|
||||
\param exportsMap : optional. If exportsMap is supplied, it will try to recover destroyed import table of the PE, basing on the supplied map of exported functions.
|
||||
*/
|
||||
bool dump_pe(IN const char *outputFilePath,
|
||||
IN OUT BYTE* buffer,
|
||||
IN size_t buffer_size,
|
||||
IN const ULONGLONG module_base,
|
||||
IN OUT t_pe_dump_mode &dump_mode,
|
||||
IN OPTIONAL const peconv::ExportsMapper* exportsMap = nullptr
|
||||
);
|
||||
|
||||
};// namespace peconv
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Wrappers over various fields in the PE header. Read, write, parse PE headers.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "buffer_util.h"
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 0x1000
|
||||
#endif
|
||||
|
||||
#define MASK_TO_DWORD(val) (val & 0xffffffff)
|
||||
#define MASK_TO_WORD(val) (val & 0xffff)
|
||||
|
||||
namespace peconv {
|
||||
/**
|
||||
Maximal size of the PE header.
|
||||
*/
|
||||
const ULONGLONG MAX_HEADER_SIZE = PAGE_SIZE;
|
||||
|
||||
/**
|
||||
Fetch image size from headers.
|
||||
*/
|
||||
DWORD get_image_size(IN const BYTE *payload);
|
||||
|
||||
/**
|
||||
Change the Image Size in Optional Header to the given one.
|
||||
*/
|
||||
bool update_image_size(IN OUT BYTE* payload, IN DWORD new_img_size);
|
||||
|
||||
/**
|
||||
Fetch architecture from the NT headers. Checks for bad pointers.
|
||||
*/
|
||||
WORD get_nt_hdr_architecture(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Wrapper for get_nt_hdr_architecture. Returns true if the PE file is 64 bit.
|
||||
*/
|
||||
bool is64bit(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Fetch pointer to the NT headers of the PE file.
|
||||
Checks for bad pointers. If buffer_size is set, validates pointers against the buffer size.
|
||||
*/
|
||||
BYTE* get_nt_hdrs(
|
||||
IN const BYTE *pe_buffer,
|
||||
IN OPTIONAL size_t buffer_size=0 //if buffer_size=0 means size unknown
|
||||
);
|
||||
|
||||
/**
|
||||
Wrapper for get_nt_headers. Automatically detects if the PE is 32 bit - if not, returns null pointer.
|
||||
*/
|
||||
IMAGE_NT_HEADERS32* get_nt_hdrs32(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Wrapper for get_nt_headers. Automatically detects if the PE is 64 bit - if not, returns null pointer.
|
||||
*/
|
||||
IMAGE_NT_HEADERS64* get_nt_hdrs64(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Fetches optional header of the PE. Validates pointers against buffer size.
|
||||
*/
|
||||
LPVOID get_optional_hdr(IN const BYTE* payload, IN const size_t buffer_size);
|
||||
|
||||
/**
|
||||
Fetches file header of the PE. Validates pointers against buffer size.
|
||||
*/
|
||||
const IMAGE_FILE_HEADER* get_file_hdr(
|
||||
IN const BYTE* payload,
|
||||
IN const size_t buffer_size
|
||||
);
|
||||
|
||||
/**
|
||||
Fetch the size of headers (from Optional Header).
|
||||
*/
|
||||
DWORD get_hdrs_size(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
get Data Directory entry of the given number. If the entry is not filled and allow_empty is not set, it returns null pointer.
|
||||
*/
|
||||
IMAGE_DATA_DIRECTORY* get_directory_entry(IN const BYTE* pe_buffer, IN DWORD dir_id, IN bool allow_empty = false);
|
||||
|
||||
/**
|
||||
Get pointer to the Data Directory content of the given number. Automatically cast to the chosen type.
|
||||
*/
|
||||
template <typename IMAGE_TYPE_DIRECTORY>
|
||||
IMAGE_TYPE_DIRECTORY* get_type_directory(IN HMODULE modulePtr, IN DWORD dir_id)
|
||||
{
|
||||
IMAGE_DATA_DIRECTORY *my_dir = peconv::get_directory_entry((const BYTE*)modulePtr, dir_id);
|
||||
if (!my_dir) return nullptr;
|
||||
|
||||
DWORD dir_addr = my_dir->VirtualAddress;
|
||||
if (dir_addr == 0) return nullptr;
|
||||
|
||||
return (IMAGE_TYPE_DIRECTORY*)(dir_addr + (ULONG_PTR)modulePtr);
|
||||
}
|
||||
|
||||
/**
|
||||
Get pointer to the Export Directory.
|
||||
*/
|
||||
IMAGE_EXPORT_DIRECTORY* get_export_directory(IN HMODULE modulePtr);
|
||||
|
||||
// Fetch Image Base from Optional Header.
|
||||
ULONGLONG get_image_base(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Change the Image Base in Optional Header to the given one.
|
||||
*/
|
||||
bool update_image_base(IN OUT BYTE* payload, IN ULONGLONG destImageBase);
|
||||
|
||||
/**
|
||||
Get RVA of the Entry Point from the Optional Header.
|
||||
*/
|
||||
DWORD get_entry_point_rva(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Change the Entry Point RVA in the Optional Header to the given one.
|
||||
*/
|
||||
bool update_entry_point_rva(IN OUT BYTE *pe_buffer, IN DWORD ep);
|
||||
|
||||
/**
|
||||
Get number of sections from the File Header. It does not validate if this the actual number.
|
||||
*/
|
||||
size_t get_sections_count(
|
||||
IN const BYTE* buffer,
|
||||
IN const size_t buffer_size
|
||||
);
|
||||
|
||||
/**
|
||||
Checks if the section headers are reachable. It does not validate sections alignment.
|
||||
*/
|
||||
bool is_valid_sections_hdr_offset(IN const BYTE* buffer, IN const size_t buffer_size);
|
||||
|
||||
/**
|
||||
Gets pointer to the section header of the given number.
|
||||
*/
|
||||
PIMAGE_SECTION_HEADER get_section_hdr(
|
||||
IN const BYTE* pe_buffer,
|
||||
IN const size_t buffer_size,
|
||||
IN size_t section_num
|
||||
);
|
||||
|
||||
/**
|
||||
Fetch the PE Characteristics from the File Header.
|
||||
*/
|
||||
WORD get_file_characteristics(IN const BYTE* payload);
|
||||
|
||||
/**
|
||||
Check if the module is a DLL (basing on the Characteristcs in the header).
|
||||
*/
|
||||
bool is_module_dll(IN const BYTE* payload);
|
||||
|
||||
/**
|
||||
Check if the module is a .NET executable
|
||||
*/
|
||||
bool is_dot_net(BYTE *pe_buffer, size_t pe_buffer_size);
|
||||
|
||||
/**
|
||||
Fetch the DLL Characteristics from the Optional Header.
|
||||
*/
|
||||
WORD get_dll_characteristics(IN const BYTE* payload);
|
||||
|
||||
/**
|
||||
Set the PE subsystem in the header.
|
||||
*/
|
||||
bool set_subsystem(IN OUT BYTE* payload, IN WORD subsystem);
|
||||
|
||||
/**
|
||||
Get the PE subsystem from the header.
|
||||
*/
|
||||
WORD get_subsystem(IN const BYTE* payload);
|
||||
|
||||
/**
|
||||
Check if the PE has relocations Data Directory.
|
||||
*/
|
||||
bool has_relocations(IN const BYTE *pe_buffer);
|
||||
|
||||
/**
|
||||
Fetch the pointer to the .NET header (if exist).
|
||||
*/
|
||||
IMAGE_COR20_HEADER* get_dotnet_hdr(
|
||||
IN const BYTE* pe_buffer,
|
||||
IN size_t const buffer_size,
|
||||
IN const IMAGE_DATA_DIRECTORY* dotNetDir
|
||||
);
|
||||
|
||||
/**
|
||||
Fetch section aligmenent from headers. Depending on the flag, it fetches either Raw Alignment or Virtual Alignment.
|
||||
*/
|
||||
DWORD get_sec_alignment(IN const BYTE* modulePtr, IN bool is_raw);
|
||||
|
||||
/**
|
||||
Change section aligmenent in headers. Depending on the flag, it sets either Raw Alignment or Virtual Alignment.
|
||||
*/
|
||||
bool set_sec_alignment(IN OUT BYTE* pe_buffer, IN bool is_raw, IN DWORD new_alignment);
|
||||
|
||||
/**
|
||||
Get size of virtual section from the headers (optionaly rounds it up to the Virtual Alignment)
|
||||
*/
|
||||
DWORD get_virtual_sec_size(
|
||||
IN const BYTE* pe_hdr,
|
||||
IN const PIMAGE_SECTION_HEADER sec_hdr,
|
||||
IN bool rounded //if set, it rounds it up to the Virtual Alignment
|
||||
);
|
||||
|
||||
/**
|
||||
Get the last section (in a raw or virtual alignment)
|
||||
\param pe_buffer : buffer with a PE
|
||||
\param pe_size : size of the given PE
|
||||
\param is_raw : If true, give the section with the highest Raw offset. If false, give the section with the highest Virtual offset.
|
||||
*/
|
||||
PIMAGE_SECTION_HEADER get_last_section(IN const PBYTE pe_buffer, IN size_t pe_size, IN bool is_raw);
|
||||
|
||||
/**
|
||||
Calculate full PE size (raw or virtual) using information from sections' headers. WARNING: it drops an overlay.
|
||||
\param pe_buffer : a buffer containing a PE
|
||||
\param pe_size : the size of the given buffer
|
||||
\param is_raw : If true, the Raw alignment is used. If false, the Virtual alignment is used.
|
||||
*/
|
||||
DWORD calc_pe_size(
|
||||
IN const PBYTE pe_buffer,
|
||||
IN size_t pe_size,
|
||||
IN bool is_raw
|
||||
);
|
||||
|
||||
/**
|
||||
Walk through sections headers checking if the sections beginnings and sizes are fitting the alignment (Virtual or Raw)
|
||||
\param buffer : a buffer containing a PE
|
||||
\param buffer_size : the size of the given buffer
|
||||
\param is_raw : If true, the Raw alignment is checked. If false, the Virtual alignment is checked.
|
||||
*/
|
||||
bool is_valid_sectons_alignment(IN const BYTE* buffer, IN const SIZE_T buffer_size, IN bool is_raw);
|
||||
|
||||
}; // namespace peconv
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Loading PE from a file with the help of the custom loader.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pe_raw_to_virtual.h"
|
||||
#include "function_resolver.h"
|
||||
|
||||
namespace peconv {
|
||||
/**
|
||||
Reads PE from the given buffer into memory and maps it into vitual format.
|
||||
(Automatic raw to virtual conversion).
|
||||
If the executable flag is true, the PE file is loaded into executable memory.
|
||||
If the relocate flag is true, applies relocations. Does not load imports.
|
||||
Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_buffer.
|
||||
*/
|
||||
BYTE* load_pe_module(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, bool executable, bool relocate);
|
||||
|
||||
/**
|
||||
Reads PE from the given file into memory and maps it into vitual format.
|
||||
(Automatic raw to virtual conversion).
|
||||
If the executable flag is true, the PE file is loaded into executable memory.
|
||||
If the relocate flag is true, applies relocations. Does not load imports.
|
||||
Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_buffer.
|
||||
*/
|
||||
BYTE* load_pe_module(const char *filename, OUT size_t &v_size, bool executable, bool relocate);
|
||||
|
||||
/**
|
||||
Loads full PE from the raw buffer in a way in which it can be directly executed: remaps to virual format, applies relocations, loads imports.
|
||||
Allows for supplying custom function resolver.
|
||||
*/
|
||||
BYTE* load_pe_executable(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, t_function_resolver* import_resolver=NULL);
|
||||
|
||||
/**
|
||||
Loads full PE from file in a way in which it can be directly executed: remaps to virual format, applies relocations, loads imports.
|
||||
Allows for supplying custom function resolver.
|
||||
*/
|
||||
BYTE* load_pe_executable(const char *filename, OUT size_t &v_size, t_function_resolver* import_resolver=NULL);
|
||||
|
||||
};// namespace peconv
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Detecting in which mode is the PE in the supplied buffer (i.e. raw, virtual). Analyzes PE features typical for particular modes.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
check if the PE in the memory is in raw format
|
||||
*/
|
||||
bool is_pe_raw(
|
||||
IN const BYTE* pe_buffer,
|
||||
IN size_t pe_size
|
||||
);
|
||||
|
||||
/**
|
||||
check if Virtual section addresses are identical to Raw addresses (i.e. if the PE was realigned)
|
||||
*/
|
||||
bool is_pe_raw_eq_virtual(
|
||||
IN const BYTE* pe_buffer,
|
||||
IN size_t pe_size
|
||||
);
|
||||
|
||||
/**
|
||||
checks if the PE has sections that were unpacked/expanded in the memory
|
||||
*/
|
||||
bool is_pe_expanded(
|
||||
IN const BYTE* pe_buffer,
|
||||
IN size_t pe_size
|
||||
);
|
||||
|
||||
/**
|
||||
checks if the given section was unpacked in the memory
|
||||
*/
|
||||
bool is_section_expanded(IN const BYTE* pe_buffer,
|
||||
IN size_t pe_size,
|
||||
IN const PIMAGE_SECTION_HEADER sec
|
||||
);
|
||||
|
||||
};// namespace peconv
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Converting PE from raw to virtual format.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "buffer_util.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Converts a raw PE supplied in a buffer to a virtual format.
|
||||
If the executable flag is true (default), the PE file is loaded into executable memory.
|
||||
Does not apply relocations. Does not load imports.
|
||||
Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_module.
|
||||
If the desired_base is defined (0 by default), it enforces allocation at the particular base.
|
||||
*/
|
||||
BYTE* pe_raw_to_virtual(
|
||||
IN const BYTE* rawPeBuffer,
|
||||
IN size_t rawPeSize,
|
||||
OUT size_t &outputSize,
|
||||
IN OPTIONAL bool executable = true,
|
||||
IN OPTIONAL ULONGLONG desired_base = 0
|
||||
);
|
||||
|
||||
}; // namespace peconv
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Converting PE from virtual to raw format.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "buffer_util.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Maps virtual image of PE to into raw. Automaticaly applies relocations.
|
||||
Automatically allocates buffer of the needed size (the size is returned in outputSize).
|
||||
\param payload : the PE in the Virtual format that needs to be converted into the Raw format
|
||||
\param in_size : size of the input buffer (the PE in the Virtual format)
|
||||
\param loadBase : the base to which the given PE was relocated
|
||||
\param outputSize : the size of the output buffer (the PE in the Raw format)
|
||||
\param rebuffer : if set (default), the input buffer is rebuffered and the original buffer is not modified.
|
||||
\return a buffer of the outputSize, containing the Raw PE. The buffer can be freed by the function free_pe_module.
|
||||
*/
|
||||
BYTE* pe_virtual_to_raw(
|
||||
IN BYTE* payload,
|
||||
IN size_t in_size,
|
||||
IN ULONGLONG loadBase,
|
||||
OUT size_t &outputSize,
|
||||
IN OPTIONAL bool rebuffer=true
|
||||
);
|
||||
|
||||
/*
|
||||
Modifies raw alignment of the PE to be the same as virtual alignment.
|
||||
\param payload : the PE in the Virtual format that needs to be realigned
|
||||
\param in_size : size of the input buffer
|
||||
\param loadBase : the base to which the given PE was relocated
|
||||
\param outputSize : the size of the output buffer (the PE in the Raw format)
|
||||
\return a buffer of the outputSize, containing the realigned PE. The buffer can be freed by the function free_pe_module.
|
||||
*/
|
||||
BYTE* pe_realign_raw_to_virtual(
|
||||
IN const BYTE* payload,
|
||||
IN size_t in_size,
|
||||
IN ULONGLONG loadBase,
|
||||
OUT size_t &outputSize
|
||||
);
|
||||
|
||||
};//namespace peconv
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions for retrieving process information from PEB.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
/**
|
||||
Gets handle to the given module via PEB. A low-level equivalent of `GetModuleHandleW`.
|
||||
\param module_name : (optional) the name of the DLL loaded within the current process. If not set, the main module of the current process is used.
|
||||
\return the handle of the DLL with given name, or, if the name was not given, the handle of the main module of the current process.
|
||||
*/
|
||||
HMODULE get_module_via_peb(IN OPTIONAL LPWSTR module_name = nullptr);
|
||||
|
||||
|
||||
/**
|
||||
Gets size of the given module via PEB.
|
||||
\param hModule : (optional) the base of the module which's size we want to retrieve. If not set, the main module of the current process is used.
|
||||
\return the size of the given module.
|
||||
*/
|
||||
size_t get_module_size_via_peb(IN OPTIONAL HMODULE hModule = nullptr);
|
||||
|
||||
/**
|
||||
Sets the given module as the main module in the current PEB.
|
||||
\param hModule : the module to be connected to the current PEB.
|
||||
\return true if succeeded, false if failed
|
||||
*/
|
||||
bool set_main_module_in_peb(HMODULE hModule);
|
||||
|
||||
/**
|
||||
Gets the main module from the current PEB.
|
||||
\return the main module connected to the current PEB.
|
||||
*/
|
||||
HMODULE get_main_module_via_peb();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Operating on PE file's relocations table.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
|
||||
typedef struct _BASE_RELOCATION_ENTRY {
|
||||
WORD Offset : 12;
|
||||
WORD Type : 4;
|
||||
} BASE_RELOCATION_ENTRY;
|
||||
|
||||
class RelocBlockCallback
|
||||
{
|
||||
public:
|
||||
RelocBlockCallback(bool _is64bit)
|
||||
: is64bit(_is64bit)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool processRelocField(ULONG_PTR relocField) = 0;
|
||||
|
||||
protected:
|
||||
bool is64bit;
|
||||
};
|
||||
|
||||
// Processs the relocation table and make your own callback on each relocation field
|
||||
bool process_relocation_table(IN PVOID modulePtr, IN SIZE_T moduleSize, IN RelocBlockCallback *callback);
|
||||
|
||||
/**
|
||||
Applies relocations on the PE in virtual format. Relocates it from the old base given to the new base given.
|
||||
If 0 was supplied as the old base, it assumes that the old base is the ImageBase given in the header.
|
||||
\param modulePtr : a buffer containing the PE to be relocated
|
||||
\param moduleSize : the size of the given PE buffer
|
||||
\param newBase : a base to which the PE should be relocated
|
||||
\param oldBase : a base to which the PE is currently relocated (if not set, the imageBase from the header will be used)
|
||||
*/
|
||||
bool relocate_module(IN BYTE* modulePtr, IN SIZE_T moduleSize, IN ULONGLONG newBase, IN ULONGLONG oldBase = 0);
|
||||
|
||||
/**
|
||||
Checks if the given PE has a valid relocations table.
|
||||
\param modulePtr : a buffer containing the PE to be checked
|
||||
\param moduleSize : the size of the given PE buffer
|
||||
*/
|
||||
bool has_valid_relocation_table(IN const PBYTE modulePtr, IN const size_t moduleSize);
|
||||
|
||||
};//namespace peconv
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Reading from a PE module that is loaded within a remote process.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "pe_hdrs_helper.h"
|
||||
#include "pe_virtual_to_raw.h"
|
||||
#include "exports_mapper.h"
|
||||
#include "pe_dumper.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
bool fetch_region_info(HANDLE processHandle, LPVOID start_addr, MEMORY_BASIC_INFORMATION &page_info);
|
||||
|
||||
/**
|
||||
Fetch size of the memory region starting from the given address.
|
||||
*/
|
||||
size_t fetch_region_size(HANDLE processHandle, LPVOID start_addr);
|
||||
|
||||
/**
|
||||
Fetch the allocation base of the memory region with the supplied start address.
|
||||
\param processHandle : handle of the process where the region of interest belongs
|
||||
\param start_addr : the address inside the region of interest
|
||||
\return the allocation base address of the memory region, or 0 if not found
|
||||
*/
|
||||
ULONGLONG fetch_alloc_base(HANDLE processHandle, LPVOID start_addr);
|
||||
|
||||
/**
|
||||
Wrapper over ReadProcessMemory. Requires a handle with privilege PROCESS_VM_READ.
|
||||
If reading of the full buffer_size was not possible, it will keep trying to read a smaller chunk, decreasing requested size on each attempt,
|
||||
till the minimal_size is reached (it is a workaround for errors such as FAULTY_HARDWARE_CORRUPTED_PAGE).
|
||||
Returns how many bytes were successfuly read.
|
||||
\param processHandle : handle of the process where the memory of interest belongs
|
||||
\param start_addr : the address within the remote process to start reading from
|
||||
\param buffer : the buffer where the read data will be stored
|
||||
\param buffer_size : the size of the buffer, and the size that will be attempted to read
|
||||
\param minimal_size : the minimal size that has to be read in order to consider the read successful
|
||||
\return the number of bytes successfuly read
|
||||
*/
|
||||
size_t read_remote_memory(HANDLE processHandle, LPVOID start_addr, OUT BYTE* buffer, const size_t buffer_size, const SIZE_T minimal_size = 0x100);
|
||||
|
||||
/**
|
||||
Reads a single memory region (continuous, with the same access rights) within a given process, starting at the start_addr.
|
||||
In case if it is inaccessible, if the flag force_access was set, it tries to force the access by temporarly changing the permissions.
|
||||
Requires a handle with privilege PROCESS_QUERY_INFORMATION. In order for force_access to work, PROCESS_VM_OPERATION is additionally required.
|
||||
step_size is passed to the underlying read_remote_memory.
|
||||
\param processHandle : handle of the process where the memory of interest belongs
|
||||
\param start_addr : the address within the remote process to start reading from
|
||||
\param buffer : the buffer where the read data will be stored
|
||||
\param buffer_size : the size of the buffer
|
||||
\param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading.
|
||||
WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability.
|
||||
\param minimal_size : the minimal size that has to be read in order to consider the read successful (passed to read_remote_memory)
|
||||
\return the number of bytes successfuly read
|
||||
*/
|
||||
size_t 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 = 0x100);
|
||||
|
||||
/**
|
||||
Reads a full memory area within a given process, starting at the start_addr, till the buffer_size is exceeded.
|
||||
The memory area can consist of multiple regions with various access rights.
|
||||
In case if the region is inaccessible, if the flag force_access was set, it tries to force the access by temporarly changing the permissions.
|
||||
On read failure the region is skipped, and the read is moving to the next one, leaving in the output buffer an empty space of the region size.
|
||||
Requires a handle with privilege PROCESS_QUERY_INFORMATION. In order for force_access to work, PROCESS_VM_OPERATION is additionally required.
|
||||
step_size is passed to the underlying read_remote_memory.
|
||||
\param processHandle : handle of the process where the memory of interest belongs
|
||||
\param start_addr : the address within the remote process to start reading from
|
||||
\param buffer : the buffer where the read data will be stored
|
||||
\param buffer_size : the size of the buffer
|
||||
\param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading.
|
||||
WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability.
|
||||
\param minimal_size : the minimal size that has to be read in order to consider the read successful (passed to read_remote_memory)
|
||||
\return the number of bytes successfuly read
|
||||
*/
|
||||
size_t 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 = 0x100);
|
||||
|
||||
/**
|
||||
Reads a PE header of the remote module within the given process. Requires a valid output buffer to be supplied (buffer).
|
||||
\param processHandle : handle of the process where the memory of interest belongs
|
||||
\param moduleBase : the base address of the module within the remote process
|
||||
\param buffer : the buffer where the read data will be stored
|
||||
\param buffer_size : the size of the buffer
|
||||
\param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading.
|
||||
WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability.
|
||||
*/
|
||||
bool read_remote_pe_header(HANDLE processHandle, LPVOID moduleBase, OUT BYTE* buffer, const size_t bufferSize, bool force_access = false);
|
||||
|
||||
/**
|
||||
Reads a PE section with a given number (sectionNum) from the remote module within the given process.
|
||||
The buffer of appropriate size is automatically allocated. After use, it should be freed by the function free_unaligned.
|
||||
The size of the buffer is writen into sectionSize.
|
||||
\param processHandle : the handle to the remote process
|
||||
\param moduleBase : the base address of the module
|
||||
\param sectionNum : number of the section to be read
|
||||
\param sectionSize : the size of the read section (output)
|
||||
\param roundup : if set, the section size is roundup to the alignment unit
|
||||
\param force_access : if this flag is set, in case if the region is inaccassible (PAGE_NOACCESS) it will try to force the the read by changing the permissions, and applying the old ones back after reading.
|
||||
WARNING: force_access should be used only on a suspended process, or a process relection, otherwise it may cause instability.
|
||||
\return a buffer containing a copy of the section.
|
||||
*/
|
||||
peconv::UNALIGNED_BUF get_remote_pe_section(HANDLE processHandle, LPVOID moduleBase, const size_t sectionNum, OUT size_t §ionSize, bool roundup, bool force_access = false);
|
||||
|
||||
/**
|
||||
Reads PE file from the remote process into the supplied buffer. It expects the module base and size to be given.
|
||||
*/
|
||||
size_t read_remote_pe(const HANDLE processHandle, LPVOID moduleBase, const size_t moduleSize, OUT BYTE* buffer, const size_t bufferSize);
|
||||
|
||||
/**
|
||||
Dumps PE from the remote process into a file. It expects the module base and size to be given.
|
||||
\param outputFilePath : the path where the dump will be saved
|
||||
\param processHandle : the handle to the remote process
|
||||
\param moduleBase : the base address of the module that needs to be dumped
|
||||
\param dump_mode : specifies in which format the PE should be dumped. If the mode was set to PE_DUMP_AUTO, it autodetects mode and returns the detected one.
|
||||
\param exportsMap : optional. If exportsMap is supplied, it will try to recover destroyed import table of the PE, basing on the supplied map of exported functions.
|
||||
*/
|
||||
bool dump_remote_pe(
|
||||
IN const char *outputFilePath,
|
||||
IN const HANDLE processHandle,
|
||||
IN LPVOID moduleBase,
|
||||
IN OUT t_pe_dump_mode &dump_mode,
|
||||
IN OPTIONAL peconv::ExportsMapper* exportsMap = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
Retrieve the Image Size saved in the header of the remote PE.
|
||||
\param processHandle : process from where we are reading
|
||||
\param start_addr : a base address of the PE within the given process
|
||||
*/
|
||||
DWORD get_remote_image_size(IN const HANDLE processHandle, IN LPVOID start_addr);
|
||||
|
||||
}; //namespace peconv
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Parsing PE's resource directory.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
namespace peconv {
|
||||
/**
|
||||
A callback that will be executed by the function parse_resources when the Resource Entry was found.
|
||||
*/
|
||||
typedef bool(*t_on_res_entry_found) (
|
||||
BYTE* modulePtr,
|
||||
IMAGE_RESOURCE_DIRECTORY_ENTRY *root_dir,
|
||||
IMAGE_RESOURCE_DATA_ENTRY *curr_entry
|
||||
);
|
||||
|
||||
/**
|
||||
A function walking through the Resource Tree of the given PE. On each Resource Entry found, the callback is executed.
|
||||
\param modulePtr : pointer to the buffer with the PE in a Virtual format
|
||||
\param on_entry : a callback function executed on each Resource Entry
|
||||
*/
|
||||
bool parse_resources(BYTE* modulePtr, t_on_res_entry_found on_entry);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Functions related to manual retrieving of PE resources.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include "buffer_util.h"
|
||||
|
||||
namespace peconv {
|
||||
|
||||
const LPSTR RT_RCDATA_A = MAKEINTRESOURCEA(10);
|
||||
|
||||
/**
|
||||
Maps a resource with the given id + type and copies its raw content into the output buffer.
|
||||
If out_size is not zero, it reads maximum out_size of bytes. If out_size is zero, it reads the full resource.
|
||||
The actual read size is returned back in out_size.
|
||||
Automatically allocates a buffer of the required size.
|
||||
If hInstance is NULL, it search the resource in the current module. Otherwise, it search in the given module.
|
||||
*/
|
||||
peconv::ALIGNED_BUF load_resource_data(OUT size_t &out_size, const int res_id, const LPSTR res_type = RT_RCDATA_A, HMODULE hInstance = nullptr);
|
||||
|
||||
/**
|
||||
Free the buffer with PE Resources, mapped by the function load_resource_data.
|
||||
*/
|
||||
void free_resource_data(peconv::ALIGNED_BUF buffer);
|
||||
|
||||
/**
|
||||
a helper function to get the module handle of the current DLL
|
||||
*/
|
||||
HMODULE get_current_module_handle();
|
||||
|
||||
}; //namespace peconv
|
||||
41
Etw Syscall/libpeconv-master/libpeconv/include/peconv/util.h
Normal file
41
Etw Syscall/libpeconv-master/libpeconv/include/peconv/util.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Miscellaneous utility functions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "file_util.h"
|
||||
#include "resource_util.h"
|
||||
|
||||
namespace peconv {
|
||||
/**
|
||||
Checks if the given buffer is fully filled with the specified character.
|
||||
\param cave_ptr : pointer to the buffer to be checked
|
||||
\param cave_size : size of the buffer to be checked
|
||||
\param padding_char : the required character
|
||||
*/
|
||||
bool is_padding(const BYTE *cave_ptr, size_t cave_size, const BYTE padding_char);
|
||||
|
||||
/**
|
||||
Wrapper for GetProcessId - for a backward compatibility with old versions of Windows
|
||||
*/
|
||||
DWORD get_process_id(HANDLE hProcess);
|
||||
|
||||
/**
|
||||
Verifies if the calling process has a defined access to the specified continuous range of memory, defined by areaStart and areaSize.
|
||||
If the area includes pages that are not commited, or pages with access rights PAGE_GUARD | PAGE_NOACCESS, it is treated as inaccessible.
|
||||
\param areaStart : A pointer to the first byte of the memory block
|
||||
\param areaSize : The size of the memory block, in bytes. If this parameter is zero, the return value is false.
|
||||
\param accessRights : The access rights to be checked
|
||||
*/
|
||||
bool is_mem_accessible(LPCVOID areaStart, SIZE_T areaSize, DWORD accessRights);
|
||||
|
||||
/**
|
||||
Verifies that the calling process has read access to the specified range of memory.
|
||||
\param areaStart : A pointer to the first byte of the memory block
|
||||
\param areaSize : The size of the memory block, in bytes. If this parameter is zero, the return value is true (bad pointer).
|
||||
*/
|
||||
bool is_bad_read_ptr(LPCVOID areaStart, SIZE_T areaSize);
|
||||
};
|
||||
|
||||
88
Etw Syscall/libpeconv-master/libpeconv/src/buffer_util.cpp
Normal file
88
Etw Syscall/libpeconv-master/libpeconv/src/buffer_util.cpp
Normal 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);
|
||||
}
|
||||
|
||||
139
Etw Syscall/libpeconv-master/libpeconv/src/caves.cpp
Normal file
139
Etw Syscall/libpeconv-master/libpeconv/src/caves.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
176
Etw Syscall/libpeconv-master/libpeconv/src/exported_func.cpp
Normal file
176
Etw Syscall/libpeconv-master/libpeconv/src/exported_func.cpp
Normal 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;
|
||||
}
|
||||
194
Etw Syscall/libpeconv-master/libpeconv/src/exports_lookup.cpp
Normal file
194
Etw Syscall/libpeconv-master/libpeconv/src/exports_lookup.cpp
Normal 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;
|
||||
}
|
||||
265
Etw Syscall/libpeconv-master/libpeconv/src/exports_mapper.cpp
Normal file
265
Etw Syscall/libpeconv-master/libpeconv/src/exports_mapper.cpp
Normal 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;
|
||||
}
|
||||
148
Etw Syscall/libpeconv-master/libpeconv/src/file_util.cpp
Normal file
148
Etw Syscall/libpeconv-master/libpeconv/src/file_util.cpp
Normal 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);
|
||||
}
|
||||
125
Etw Syscall/libpeconv-master/libpeconv/src/find_base.cpp
Normal file
125
Etw Syscall/libpeconv-master/libpeconv/src/find_base.cpp
Normal 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;
|
||||
}
|
||||
137
Etw Syscall/libpeconv-master/libpeconv/src/fix_dot_net_ep.cpp
Normal file
137
Etw Syscall/libpeconv-master/libpeconv/src/fix_dot_net_ep.cpp
Normal 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
306
Etw Syscall/libpeconv-master/libpeconv/src/fix_imports.cpp
Normal file
306
Etw Syscall/libpeconv-master/libpeconv/src/fix_imports.cpp
Normal 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> ¬_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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
224
Etw Syscall/libpeconv-master/libpeconv/src/hooks.cpp
Normal file
224
Etw Syscall/libpeconv-master/libpeconv/src/hooks.cpp
Normal 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;
|
||||
}
|
||||
271
Etw Syscall/libpeconv-master/libpeconv/src/imports_loader.cpp
Normal file
271
Etw Syscall/libpeconv-master/libpeconv/src/imports_loader.cpp
Normal 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);
|
||||
}
|
||||
243
Etw Syscall/libpeconv-master/libpeconv/src/imports_uneraser.cpp
Normal file
243
Etw Syscall/libpeconv-master/libpeconv/src/imports_uneraser.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
4300
Etw Syscall/libpeconv-master/libpeconv/src/ntddk.h
Normal file
4300
Etw Syscall/libpeconv-master/libpeconv/src/ntddk.h
Normal file
File diff suppressed because it is too large
Load Diff
73
Etw Syscall/libpeconv-master/libpeconv/src/pe_dumper.cpp
Normal file
73
Etw Syscall/libpeconv-master/libpeconv/src/pe_dumper.cpp
Normal 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;
|
||||
}
|
||||
652
Etw Syscall/libpeconv-master/libpeconv/src/pe_hdrs_helper.cpp
Normal file
652
Etw Syscall/libpeconv-master/libpeconv/src/pe_hdrs_helper.cpp
Normal 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;
|
||||
}
|
||||
122
Etw Syscall/libpeconv-master/libpeconv/src/pe_loader.cpp
Normal file
122
Etw Syscall/libpeconv-master/libpeconv/src/pe_loader.cpp
Normal 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;
|
||||
}
|
||||
196
Etw Syscall/libpeconv-master/libpeconv/src/pe_mode_detector.cpp
Normal file
196
Etw Syscall/libpeconv-master/libpeconv/src/pe_mode_detector.cpp
Normal 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;
|
||||
}
|
||||
140
Etw Syscall/libpeconv-master/libpeconv/src/pe_raw_to_virtual.cpp
Normal file
140
Etw Syscall/libpeconv-master/libpeconv/src/pe_raw_to_virtual.cpp
Normal 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;
|
||||
}
|
||||
213
Etw Syscall/libpeconv-master/libpeconv/src/pe_virtual_to_raw.cpp
Normal file
213
Etw Syscall/libpeconv-master/libpeconv/src/pe_virtual_to_raw.cpp
Normal 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;
|
||||
}
|
||||
178
Etw Syscall/libpeconv-master/libpeconv/src/peb_lookup.cpp
Normal file
178
Etw Syscall/libpeconv-master/libpeconv/src/peb_lookup.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include "ntddk.h"
|
||||
|
||||
#include <peconv/peb_lookup.h>
|
||||
|
||||
class SectionLocker {
|
||||
public:
|
||||
SectionLocker(RTL_CRITICAL_SECTION &_section)
|
||||
: section(_section)
|
||||
{
|
||||
RtlEnterCriticalSection(§ion);
|
||||
}
|
||||
|
||||
~SectionLocker()
|
||||
{
|
||||
RtlLeaveCriticalSection(§ion);
|
||||
}
|
||||
|
||||
protected:
|
||||
RTL_CRITICAL_SECTION §ion;
|
||||
};
|
||||
|
||||
//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;
|
||||
}
|
||||
193
Etw Syscall/libpeconv-master/libpeconv/src/relocate.cpp
Normal file
193
Etw Syscall/libpeconv-master/libpeconv/src/relocate.cpp
Normal 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);
|
||||
}
|
||||
|
||||
421
Etw Syscall/libpeconv-master/libpeconv/src/remote_pe_reader.cpp
Normal file
421
Etw Syscall/libpeconv-master/libpeconv/src/remote_pe_reader.cpp
Normal 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 §ion_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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
56
Etw Syscall/libpeconv-master/libpeconv/src/resource_util.cpp
Normal file
56
Etw Syscall/libpeconv-master/libpeconv/src/resource_util.cpp
Normal 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);
|
||||
}
|
||||
163
Etw Syscall/libpeconv-master/libpeconv/src/util.cpp
Normal file
163
Etw Syscall/libpeconv-master/libpeconv/src/util.cpp
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user