|
|
@ -1,31 +1,31 @@
|
|
|
|
#pragma once
|
|
|
|
#pragma once
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#pragma comment(lib, "ntdll.lib")
|
|
|
|
#pragma comment( lib, "ntdll.lib" )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <string>
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
#include <map>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <memory>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <string>
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <tlhelp32.h>
|
|
|
|
#include <ntstatus.h>
|
|
|
|
#include <psapi.h>
|
|
|
|
#include <psapi.h>
|
|
|
|
|
|
|
|
#include <tlhelp32.h>
|
|
|
|
#include <winternl.h>
|
|
|
|
#include <winternl.h>
|
|
|
|
#include <ntstatus.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define LOG_SIG "[xtils]"
|
|
|
|
#define LOG_SIG "[xtils]"
|
|
|
|
#define LOG(...) \
|
|
|
|
#define LOG( ... ) \
|
|
|
|
{ \
|
|
|
|
{ \
|
|
|
|
char buff[256]; \
|
|
|
|
char buff[ 256 ]; \
|
|
|
|
snprintf(buff, sizeof buff, LOG_SIG ## __VA_ARGS__); \
|
|
|
|
snprintf( buff, sizeof buff, LOG_SIG##__VA_ARGS__ ); \
|
|
|
|
OutputDebugStringA(buff); \
|
|
|
|
OutputDebugStringA( buff ); \
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define NT_HEADER(x) reinterpret_cast<PIMAGE_NT_HEADERS>( \
|
|
|
|
#define NT_HEADER( x ) \
|
|
|
|
uint64_t(x) + reinterpret_cast<PIMAGE_DOS_HEADER>(x)->e_lfanew )
|
|
|
|
reinterpret_cast< PIMAGE_NT_HEADERS >( uint64_t( x ) + reinterpret_cast< PIMAGE_DOS_HEADER >( x )->e_lfanew )
|
|
|
|
|
|
|
|
|
|
|
|
#define PAGE_4K 0x1000
|
|
|
|
#define PAGE_4K 0x1000
|
|
|
|
#define PAGE_2MB PAGE_4K * 512
|
|
|
|
#define PAGE_2MB PAGE_4K * 512
|
|
|
@ -42,175 +42,176 @@ typedef struct _RTL_PROCESS_MODULE_INFORMATION
|
|
|
|
USHORT InitOrderIndex;
|
|
|
|
USHORT InitOrderIndex;
|
|
|
|
USHORT LoadCount;
|
|
|
|
USHORT LoadCount;
|
|
|
|
USHORT OffsetToFileName;
|
|
|
|
USHORT OffsetToFileName;
|
|
|
|
UCHAR FullPathName[256];
|
|
|
|
UCHAR FullPathName[ 256 ];
|
|
|
|
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
|
|
|
|
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _RTL_PROCESS_MODULES
|
|
|
|
typedef struct _RTL_PROCESS_MODULES
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ULONG NumberOfModules;
|
|
|
|
ULONG NumberOfModules;
|
|
|
|
RTL_PROCESS_MODULE_INFORMATION Modules[1];
|
|
|
|
RTL_PROCESS_MODULE_INFORMATION Modules[ 1 ];
|
|
|
|
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
|
|
|
|
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace xtils
|
|
|
|
namespace xtils
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using uq_handle = std::unique_ptr<void, decltype(&CloseHandle)>;
|
|
|
|
using uq_handle = std::unique_ptr< void, decltype( &CloseHandle ) >;
|
|
|
|
|
|
|
|
|
|
|
|
class um_t
|
|
|
|
class um_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using module_callback_t = std::function<bool(std::wstring, std::uintptr_t)>;
|
|
|
|
using module_callback_t = std::function< bool( std::wstring, std::uintptr_t ) >;
|
|
|
|
using module_map_t = std::map<std::wstring, std::uintptr_t>;
|
|
|
|
using module_map_t = std::map< std::wstring, std::uintptr_t >;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
static auto get_instance() -> um_t* { static um_t obj; return &obj; }
|
|
|
|
static auto get_instance() -> um_t *
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
static um_t obj;
|
|
|
|
|
|
|
|
return &obj;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto image_base(const char* image_path) -> std::uintptr_t
|
|
|
|
auto image_base( const char *image_path ) -> std::uintptr_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
char image_header[PAGE_4K];
|
|
|
|
char image_header[ PAGE_4K ];
|
|
|
|
std::ifstream file(image_path, std::ios::binary);
|
|
|
|
std::ifstream file( image_path, std::ios::binary );
|
|
|
|
file.read(image_header, PAGE_4K);
|
|
|
|
file.read( image_header, PAGE_4K );
|
|
|
|
file.close();
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
|
|
return NT_HEADER(image_header)->OptionalHeader.ImageBase;
|
|
|
|
return NT_HEADER( image_header )->OptionalHeader.ImageBase;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto sigscan(void* base, std::uint32_t size, const char* pattern, const char* mask) -> void*
|
|
|
|
auto image_size(const char* image_path) -> std::uintptr_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
static const auto check_mask =
|
|
|
|
char image_header[ PAGE_4K ];
|
|
|
|
[&](const char* base, const char* pattern, const char* mask) -> bool
|
|
|
|
std::ifstream file( image_path, std::ios::binary );
|
|
|
|
|
|
|
|
file.read( image_header, PAGE_4K );
|
|
|
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return NT_HEADER( image_header )->OptionalHeader.SizeOfImage;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto sigscan( void *base, std::uint32_t size, const char *pattern, const char *mask ) -> void *
|
|
|
|
{
|
|
|
|
{
|
|
|
|
for (; *mask; ++base, ++pattern, ++mask)
|
|
|
|
static const auto check_mask = [ & ]( const char *base, const char *pattern, const char *mask ) -> bool {
|
|
|
|
if (*mask == 'x' && *base != *pattern)
|
|
|
|
for ( ; *mask; ++base, ++pattern, ++mask )
|
|
|
|
|
|
|
|
if ( *mask == 'x' && *base != *pattern )
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
size -= strlen(mask);
|
|
|
|
size -= strlen( mask );
|
|
|
|
for (auto i = 0; i <= size; ++i)
|
|
|
|
for ( auto i = 0; i <= size; ++i )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void* addr = (void*)&(((char*)base)[i]);
|
|
|
|
void *addr = ( void * )&( ( ( char * )base )[ i ] );
|
|
|
|
if (check_mask((char*)addr, pattern, mask))
|
|
|
|
if ( check_mask( ( char * )addr, pattern, mask ) )
|
|
|
|
return addr;
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto get_modules(std::uint32_t pid, module_map_t& module_map) -> bool
|
|
|
|
auto get_modules( std::uint32_t pid, module_map_t &module_map ) -> bool
|
|
|
|
{
|
|
|
|
{
|
|
|
|
uq_handle snapshot = { CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid), &CloseHandle };
|
|
|
|
uq_handle snapshot = { CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, pid ), &CloseHandle };
|
|
|
|
|
|
|
|
|
|
|
|
if (snapshot.get() == INVALID_HANDLE_VALUE)
|
|
|
|
if ( snapshot.get() == INVALID_HANDLE_VALUE )
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
MODULEENTRY32 module_info = { sizeof MODULEENTRY32 };
|
|
|
|
MODULEENTRY32 module_info = { sizeof MODULEENTRY32 };
|
|
|
|
Module32First(snapshot.get(), &module_info);
|
|
|
|
Module32First( snapshot.get(), &module_info );
|
|
|
|
|
|
|
|
|
|
|
|
// lowercase the module name...
|
|
|
|
// lowercase the module name...
|
|
|
|
std::for_each(module_info.szModule,
|
|
|
|
std::for_each( module_info.szModule, module_info.szModule + wcslen( module_info.szModule ) * 2,
|
|
|
|
module_info.szModule + wcslen(module_info.szModule) * 2,
|
|
|
|
[]( wchar_t &c ) { c = ::towlower( c ); } );
|
|
|
|
[](wchar_t& c) { c = ::towlower(c); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module_map[module_info.szModule] = reinterpret_cast<std::uintptr_t>(module_info.modBaseAddr);
|
|
|
|
module_map[ module_info.szModule ] = reinterpret_cast< std::uintptr_t >( module_info.modBaseAddr );
|
|
|
|
|
|
|
|
|
|
|
|
for (Module32First(snapshot.get(), &module_info); Module32Next(snapshot.get(), &module_info);)
|
|
|
|
for ( Module32First( snapshot.get(), &module_info ); Module32Next( snapshot.get(), &module_info ); )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// lowercase the module name...
|
|
|
|
// lowercase the module name...
|
|
|
|
std::for_each(module_info.szModule,
|
|
|
|
std::for_each( module_info.szModule, module_info.szModule + wcslen( module_info.szModule ) * 2,
|
|
|
|
module_info.szModule + wcslen(module_info.szModule) * 2,
|
|
|
|
[]( wchar_t &c ) { c = ::towlower( c ); } );
|
|
|
|
[](wchar_t& c) { c = ::towlower(c); });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module_map[module_info.szModule] =
|
|
|
|
module_map[ module_info.szModule ] = reinterpret_cast< std::uintptr_t >( module_info.modBaseAddr );
|
|
|
|
reinterpret_cast<std::uintptr_t>(module_info.modBaseAddr);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void each_module(std::uint32_t pid, module_callback_t callback)
|
|
|
|
void each_module( std::uint32_t pid, module_callback_t callback )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
module_map_t module_map;
|
|
|
|
module_map_t module_map;
|
|
|
|
if (!get_modules(pid, module_map))
|
|
|
|
if ( !get_modules( pid, module_map ) )
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& [module_name, module_base] : module_map)
|
|
|
|
for ( auto &[ module_name, module_base ] : module_map )
|
|
|
|
if (!callback(module_name, module_base))
|
|
|
|
if ( !callback( module_name, module_base ) )
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// https://github.com/PierreCiholas/GetBaseAddress/blob/master/main.cpp#L7
|
|
|
|
// https://github.com/PierreCiholas/GetBaseAddress/blob/master/main.cpp#L7
|
|
|
|
auto get_process_base(HANDLE proc_handle)->std::uintptr_t
|
|
|
|
auto get_process_base( HANDLE proc_handle ) -> std::uintptr_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
HMODULE lph_modules[1024];
|
|
|
|
HMODULE lph_modules[ 1024 ];
|
|
|
|
DWORD needed = 0u;
|
|
|
|
DWORD needed = 0u;
|
|
|
|
|
|
|
|
|
|
|
|
if (!EnumProcessModules(proc_handle, lph_modules, sizeof(lph_modules), &needed))
|
|
|
|
if ( !EnumProcessModules( proc_handle, lph_modules, sizeof( lph_modules ), &needed ) )
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
TCHAR mod_name[MAX_PATH];
|
|
|
|
TCHAR mod_name[ MAX_PATH ];
|
|
|
|
if (!GetModuleFileNameEx(proc_handle, lph_modules[0], mod_name, sizeof(mod_name) / sizeof(TCHAR)))
|
|
|
|
if ( !GetModuleFileNameEx( proc_handle, lph_modules[ 0 ], mod_name, sizeof( mod_name ) / sizeof( TCHAR ) ) )
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
return reinterpret_cast<std::uintptr_t>(lph_modules[0]);
|
|
|
|
return reinterpret_cast< std::uintptr_t >( lph_modules[ 0 ] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto get_pid(const wchar_t* proc_name) -> std::uint32_t
|
|
|
|
auto get_pid( const wchar_t *proc_name ) -> std::uint32_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
uq_handle snapshot = { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), &CloseHandle };
|
|
|
|
uq_handle snapshot = { CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, NULL ), &CloseHandle };
|
|
|
|
|
|
|
|
|
|
|
|
if (snapshot.get() == INVALID_HANDLE_VALUE)
|
|
|
|
if ( snapshot.get() == INVALID_HANDLE_VALUE )
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
PROCESSENTRY32W process_entry{ sizeof(PROCESSENTRY32W) };
|
|
|
|
PROCESSENTRY32W process_entry{ sizeof( PROCESSENTRY32W ) };
|
|
|
|
Process32FirstW(snapshot.get(), &process_entry);
|
|
|
|
Process32FirstW( snapshot.get(), &process_entry );
|
|
|
|
if (!std::wcscmp(proc_name, process_entry.szExeFile))
|
|
|
|
if ( !std::wcscmp( proc_name, process_entry.szExeFile ) )
|
|
|
|
return process_entry.th32ProcessID;
|
|
|
|
return process_entry.th32ProcessID;
|
|
|
|
|
|
|
|
|
|
|
|
for (Process32FirstW(snapshot.get(), &process_entry); Process32NextW(snapshot.get(), &process_entry); )
|
|
|
|
for ( Process32FirstW( snapshot.get(), &process_entry ); Process32NextW( snapshot.get(), &process_entry ); )
|
|
|
|
if (!std::wcscmp(proc_name, process_entry.szExeFile))
|
|
|
|
if ( !std::wcscmp( proc_name, process_entry.szExeFile ) )
|
|
|
|
return process_entry.th32ProcessID;
|
|
|
|
return process_entry.th32ProcessID;
|
|
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto get_handle(const wchar_t* proc_name, DWORD access = PROCESS_ALL_ACCESS) -> uq_handle
|
|
|
|
auto get_handle( const wchar_t *proc_name, DWORD access = PROCESS_ALL_ACCESS ) -> uq_handle
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::uint32_t pid = 0u;
|
|
|
|
std::uint32_t pid = 0u;
|
|
|
|
if (!(pid = get_pid(proc_name)))
|
|
|
|
if ( !( pid = get_pid( proc_name ) ) )
|
|
|
|
return { NULL, &CloseHandle };
|
|
|
|
return { NULL, &CloseHandle };
|
|
|
|
|
|
|
|
|
|
|
|
return { OpenProcess(access, FALSE, pid), &CloseHandle };
|
|
|
|
return { OpenProcess( access, FALSE, pid ), &CloseHandle };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto get_handle(std::uint32_t pid, DWORD access = PROCESS_ALL_ACCESS)->uq_handle
|
|
|
|
auto get_handle( std::uint32_t pid, DWORD access = PROCESS_ALL_ACCESS ) -> uq_handle
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!pid) return { NULL, &CloseHandle };
|
|
|
|
if ( !pid )
|
|
|
|
return { OpenProcess(access, FALSE, pid), &CloseHandle };
|
|
|
|
return { NULL, &CloseHandle };
|
|
|
|
|
|
|
|
return { OpenProcess( access, FALSE, pid ), &CloseHandle };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto load_lib(HANDLE proc_handle, const char* dll_path) -> std::uintptr_t
|
|
|
|
auto load_lib( HANDLE proc_handle, const char *dll_path ) -> std::uintptr_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto dll_path_page =
|
|
|
|
const auto dll_path_page =
|
|
|
|
VirtualAllocEx(
|
|
|
|
VirtualAllocEx( proc_handle, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
|
|
|
|
proc_handle,
|
|
|
|
|
|
|
|
nullptr,
|
|
|
|
if ( !dll_path_page )
|
|
|
|
0x1000,
|
|
|
|
|
|
|
|
MEM_COMMIT | MEM_RESERVE,
|
|
|
|
|
|
|
|
PAGE_READWRITE
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!dll_path_page)
|
|
|
|
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
SIZE_T handled_bytes;
|
|
|
|
SIZE_T handled_bytes;
|
|
|
|
if (!WriteProcessMemory(proc_handle, dll_path_page,
|
|
|
|
if ( !WriteProcessMemory( proc_handle, dll_path_page, dll_path, strlen( dll_path ), &handled_bytes ) )
|
|
|
|
dll_path, strlen(dll_path), &handled_bytes))
|
|
|
|
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
// +6 for string address
|
|
|
|
// +6 for string address
|
|
|
|
// +16 for LoadLibrary address...
|
|
|
|
// +16 for LoadLibrary address...
|
|
|
|
unsigned char jmp_code[] =
|
|
|
|
unsigned char jmp_code[] = {
|
|
|
|
{
|
|
|
|
|
|
|
|
0x48, 0x83, 0xEC, 0x28, // sub rsp, 0x28
|
|
|
|
0x48, 0x83, 0xEC, 0x28, // sub rsp, 0x28
|
|
|
|
0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // mov rcx, &dllpath
|
|
|
|
0x48, 0xB9, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // mov rcx, &dllpath
|
|
|
|
0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // mov rax, &LoadLibraryA
|
|
|
|
0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // mov rax, &LoadLibraryA
|
|
|
@ -220,342 +221,306 @@ namespace xtils
|
|
|
|
0xC3 // ret
|
|
|
|
0xC3 // ret
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
*reinterpret_cast<std::uintptr_t*>(&jmp_code[6]) =
|
|
|
|
*reinterpret_cast< std::uintptr_t * >( &jmp_code[ 6 ] ) =
|
|
|
|
reinterpret_cast<std::uintptr_t>(dll_path_page);
|
|
|
|
reinterpret_cast< std::uintptr_t >( dll_path_page );
|
|
|
|
|
|
|
|
|
|
|
|
*reinterpret_cast<std::uintptr_t*>(&jmp_code[16]) =
|
|
|
|
*reinterpret_cast< std::uintptr_t * >( &jmp_code[ 16 ] ) =
|
|
|
|
reinterpret_cast<std::uintptr_t>(&LoadLibraryA);
|
|
|
|
reinterpret_cast< std::uintptr_t >( &LoadLibraryA );
|
|
|
|
|
|
|
|
|
|
|
|
const auto jmp_code_page =
|
|
|
|
const auto jmp_code_page =
|
|
|
|
VirtualAllocEx(
|
|
|
|
VirtualAllocEx( proc_handle, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
|
|
|
|
proc_handle,
|
|
|
|
|
|
|
|
nullptr,
|
|
|
|
if ( !jmp_code_page )
|
|
|
|
0x1000,
|
|
|
|
|
|
|
|
MEM_COMMIT | MEM_RESERVE,
|
|
|
|
|
|
|
|
PAGE_EXECUTE_READWRITE
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!jmp_code_page)
|
|
|
|
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
if (!WriteProcessMemory(proc_handle,
|
|
|
|
if ( !WriteProcessMemory( proc_handle, jmp_code_page, jmp_code, sizeof jmp_code, &handled_bytes ) )
|
|
|
|
jmp_code_page, jmp_code, sizeof jmp_code, &handled_bytes))
|
|
|
|
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
DWORD tid = 0u;
|
|
|
|
DWORD tid = 0u;
|
|
|
|
auto thandle = CreateRemoteThread(proc_handle, nullptr,
|
|
|
|
auto thandle = CreateRemoteThread( proc_handle, nullptr, NULL, ( LPTHREAD_START_ROUTINE )jmp_code_page,
|
|
|
|
NULL, (LPTHREAD_START_ROUTINE)jmp_code_page, nullptr, NULL, &tid);
|
|
|
|
nullptr, NULL, &tid );
|
|
|
|
|
|
|
|
|
|
|
|
if (thandle == INVALID_HANDLE_VALUE)
|
|
|
|
if ( thandle == INVALID_HANDLE_VALUE )
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
WaitForSingleObject(thandle, INFINITE);
|
|
|
|
WaitForSingleObject( thandle, INFINITE );
|
|
|
|
|
|
|
|
|
|
|
|
// read the base address out of the shellcode...
|
|
|
|
// read the base address out of the shellcode...
|
|
|
|
std::uintptr_t module_base = 0u;
|
|
|
|
std::uintptr_t module_base = 0u;
|
|
|
|
if (!ReadProcessMemory(proc_handle, reinterpret_cast<void*>(
|
|
|
|
if ( !ReadProcessMemory( proc_handle,
|
|
|
|
reinterpret_cast<std::uintptr_t>(jmp_code_page) + sizeof jmp_code),
|
|
|
|
reinterpret_cast< void * >( reinterpret_cast< std::uintptr_t >( jmp_code_page ) +
|
|
|
|
&module_base, sizeof module_base, &handled_bytes))
|
|
|
|
sizeof jmp_code ),
|
|
|
|
|
|
|
|
&module_base, sizeof module_base, &handled_bytes ) )
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
|
|
|
|
|
|
|
|
return module_base;
|
|
|
|
return module_base;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
auto start_exec(const char* image_path, char* cmdline = nullptr,
|
|
|
|
auto start_exec( const char *image_path, char *cmdline = nullptr, bool suspend = false )
|
|
|
|
bool suspend = false) -> std::tuple<HANDLE, std::uint32_t, std::uintptr_t>
|
|
|
|
-> std::tuple< HANDLE, std::uint32_t, std::uintptr_t >
|
|
|
|
{
|
|
|
|
{
|
|
|
|
STARTUPINFOA info = { sizeof info };
|
|
|
|
STARTUPINFOA info = { sizeof info };
|
|
|
|
PROCESS_INFORMATION proc_info;
|
|
|
|
PROCESS_INFORMATION proc_info;
|
|
|
|
|
|
|
|
|
|
|
|
if (!CreateProcessA(image_path, cmdline, nullptr,
|
|
|
|
if ( !CreateProcessA( image_path, cmdline, nullptr, nullptr, false,
|
|
|
|
nullptr, false,
|
|
|
|
suspend ? CREATE_SUSPENDED | CREATE_NEW_CONSOLE : CREATE_NEW_CONSOLE, nullptr,
|
|
|
|
suspend ? CREATE_SUSPENDED | CREATE_NEW_CONSOLE : CREATE_NEW_CONSOLE,
|
|
|
|
nullptr, &info, &proc_info ) )
|
|
|
|
nullptr, nullptr, &info, &proc_info
|
|
|
|
|
|
|
|
))
|
|
|
|
|
|
|
|
return { {}, {}, {} };
|
|
|
|
return { {}, {}, {} };
|
|
|
|
|
|
|
|
|
|
|
|
Sleep(1); // sleep just for a tiny amount of time so that get_process_base works...
|
|
|
|
Sleep( 1 ); // sleep just for a tiny amount of time so that get_process_base works...
|
|
|
|
return { proc_info.hProcess, proc_info.dwProcessId, get_process_base(proc_info.hProcess) };
|
|
|
|
return { proc_info.hProcess, proc_info.dwProcessId, get_process_base( proc_info.hProcess ) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
|
|
|
|
std::uintptr_t scan( std::uintptr_t base, std::uint32_t size, const char *pattern, const char *mask )
|
|
|
|
{
|
|
|
|
|
|
|
|
static const auto check_mask =
|
|
|
|
|
|
|
|
[&](const char* base, const char* pattern, const char* mask) -> bool
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
for (; *mask; ++base, ++pattern, ++mask)
|
|
|
|
static const auto check_mask = [ & ]( const char *base, const char *pattern, const char *mask ) -> bool {
|
|
|
|
if (*mask == 'x' && *base != *pattern)
|
|
|
|
for ( ; *mask; ++base, ++pattern, ++mask )
|
|
|
|
|
|
|
|
if ( *mask == 'x' && *base != *pattern )
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
size -= strlen(mask);
|
|
|
|
size -= strlen( mask );
|
|
|
|
for (auto i = 0; i <= size; ++i)
|
|
|
|
for ( auto i = 0; i <= size; ++i )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void* addr = (void*)&(((char*)base)[i]);
|
|
|
|
void *addr = ( void * )&( ( ( char * )base )[ i ] );
|
|
|
|
if (check_mask((char*)addr, pattern, mask))
|
|
|
|
if ( check_mask( ( char * )addr, pattern, mask ) )
|
|
|
|
return reinterpret_cast<std::uintptr_t>(addr);
|
|
|
|
return reinterpret_cast< std::uintptr_t >( addr );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
explicit um_t() {}
|
|
|
|
explicit um_t()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class km_t
|
|
|
|
class km_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
|
|
|
|
using kmodule_callback_t = std::function< bool( PRTL_PROCESS_MODULE_INFORMATION, const char * ) >;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
static auto get_instance() -> km_t* { static km_t obj; return &obj; };
|
|
|
|
static auto get_instance() -> km_t *
|
|
|
|
auto get_base(const char* drv_name)->std::uintptr_t
|
|
|
|
{
|
|
|
|
|
|
|
|
static km_t obj;
|
|
|
|
|
|
|
|
return &obj;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
auto get_base( const char *drv_name ) -> std::uintptr_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void* buffer = nullptr;
|
|
|
|
void *buffer = nullptr;
|
|
|
|
DWORD buffer_size = NULL;
|
|
|
|
DWORD buffer_size = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
auto status = NtQuerySystemInformation(
|
|
|
|
auto status = NtQuerySystemInformation( static_cast< SYSTEM_INFORMATION_CLASS >( 0xB ), buffer, buffer_size,
|
|
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
|
|
&buffer_size );
|
|
|
|
buffer, buffer_size, &buffer_size);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
while ( status == STATUS_INFO_LENGTH_MISMATCH )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
|
|
buffer = VirtualAlloc( nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
|
|
|
|
status = NtQuerySystemInformation(
|
|
|
|
status = NtQuerySystemInformation( static_cast< SYSTEM_INFORMATION_CLASS >( 0xB ), buffer, buffer_size,
|
|
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
|
|
&buffer_size );
|
|
|
|
buffer, buffer_size, &buffer_size);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
if ( !NT_SUCCESS( status ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
|
|
|
const auto modules = static_cast< PRTL_PROCESS_MODULES >( buffer );
|
|
|
|
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
|
|
|
for ( auto idx = 0u; idx < modules->NumberOfModules; ++idx )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto current_module_name =
|
|
|
|
const auto current_module_name =
|
|
|
|
std::string(reinterpret_cast<char*>(
|
|
|
|
std::string( reinterpret_cast< char * >( modules->Modules[ idx ].FullPathName ) +
|
|
|
|
modules->Modules[idx].FullPathName) +
|
|
|
|
modules->Modules[ idx ].OffsetToFileName );
|
|
|
|
modules->Modules[idx].OffsetToFileName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!_stricmp(current_module_name.c_str(), drv_name))
|
|
|
|
if ( !_stricmp( current_module_name.c_str(), drv_name ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto result =
|
|
|
|
const auto result = reinterpret_cast< std::uint64_t >( modules->Modules[ idx ].ImageBase );
|
|
|
|
reinterpret_cast<std::uint64_t>(
|
|
|
|
|
|
|
|
modules->Modules[idx].ImageBase);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return NULL;
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void each_module(kmodule_callback_t callback)
|
|
|
|
void each_module( kmodule_callback_t callback )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void* buffer = nullptr;
|
|
|
|
void *buffer = nullptr;
|
|
|
|
DWORD buffer_size = NULL;
|
|
|
|
DWORD buffer_size = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
auto status = NtQuerySystemInformation(
|
|
|
|
auto status = NtQuerySystemInformation( static_cast< SYSTEM_INFORMATION_CLASS >( 0xB ), buffer, buffer_size,
|
|
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
|
|
&buffer_size );
|
|
|
|
buffer, buffer_size, &buffer_size);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
while ( status == STATUS_INFO_LENGTH_MISMATCH )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
|
|
buffer = VirtualAlloc( nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
|
|
|
|
status = NtQuerySystemInformation(
|
|
|
|
status = NtQuerySystemInformation( static_cast< SYSTEM_INFORMATION_CLASS >( 0xB ), buffer, buffer_size,
|
|
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
|
|
&buffer_size );
|
|
|
|
buffer, buffer_size, &buffer_size);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
if ( !NT_SUCCESS( status ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
|
|
|
const auto modules = static_cast< PRTL_PROCESS_MODULES >( buffer );
|
|
|
|
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
|
|
|
for ( auto idx = 0u; idx < modules->NumberOfModules; ++idx )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto full_path = std::string(
|
|
|
|
auto full_path = std::string( reinterpret_cast< char * >( modules->Modules[ idx ].FullPathName ) );
|
|
|
|
reinterpret_cast<char*>(
|
|
|
|
|
|
|
|
modules->Modules[idx].FullPathName));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
|
|
|
if ( full_path.find( "\\SystemRoot\\" ) != std::string::npos )
|
|
|
|
full_path.replace(full_path.find("\\SystemRoot\\"),
|
|
|
|
full_path.replace( full_path.find( "\\SystemRoot\\" ), sizeof( "\\SystemRoot\\" ) - 1,
|
|
|
|
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
|
|
|
std::string( getenv( "SYSTEMROOT" ) ).append( "\\" ) );
|
|
|
|
|
|
|
|
|
|
|
|
else if (full_path.find("\\??\\") != std::string::npos)
|
|
|
|
else if ( full_path.find( "\\??\\" ) != std::string::npos )
|
|
|
|
full_path.replace(full_path.find("\\??\\"),
|
|
|
|
full_path.replace( full_path.find( "\\??\\" ), sizeof( "\\??\\" ) - 1, "" );
|
|
|
|
sizeof("\\??\\") - 1, "");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!callback(&modules->Modules[idx], full_path.c_str()))
|
|
|
|
if ( !callback( &modules->Modules[ idx ], full_path.c_str() ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto get_export( const char *drv_name, const char *export_name ) -> std::uintptr_t
|
|
|
|
auto get_export(const char* drv_name, const char* export_name)->std::uintptr_t
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void* buffer = nullptr;
|
|
|
|
void *buffer = nullptr;
|
|
|
|
DWORD buffer_size = NULL;
|
|
|
|
DWORD buffer_size = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
NTSTATUS status = NtQuerySystemInformation(
|
|
|
|
NTSTATUS status = NtQuerySystemInformation( static_cast< SYSTEM_INFORMATION_CLASS >( 0xB ), buffer,
|
|
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
|
|
buffer_size, &buffer_size );
|
|
|
|
buffer,
|
|
|
|
|
|
|
|
buffer_size,
|
|
|
|
|
|
|
|
&buffer_size
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (status == STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
while ( status == STATUS_INFO_LENGTH_MISMATCH )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, 0, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, 0, MEM_RELEASE );
|
|
|
|
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
|
|
buffer = VirtualAlloc( nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
|
|
|
|
status = NtQuerySystemInformation(
|
|
|
|
status = NtQuerySystemInformation( static_cast< SYSTEM_INFORMATION_CLASS >( 0xB ), buffer, buffer_size,
|
|
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
|
|
&buffer_size );
|
|
|
|
buffer,
|
|
|
|
|
|
|
|
buffer_size,
|
|
|
|
|
|
|
|
&buffer_size
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!NT_SUCCESS(status))
|
|
|
|
if ( !NT_SUCCESS( status ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VirtualFree(buffer, 0, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, 0, MEM_RELEASE );
|
|
|
|
return NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
|
|
|
const auto modules = static_cast< PRTL_PROCESS_MODULES >( buffer );
|
|
|
|
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
|
|
|
for ( auto idx = 0u; idx < modules->NumberOfModules; ++idx )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// find module and then load library it
|
|
|
|
// find module and then load library it
|
|
|
|
const std::string current_module_name =
|
|
|
|
const std::string current_module_name =
|
|
|
|
std::string(reinterpret_cast<char*>(
|
|
|
|
std::string( reinterpret_cast< char * >( modules->Modules[ idx ].FullPathName ) +
|
|
|
|
modules->Modules[idx].FullPathName) +
|
|
|
|
modules->Modules[ idx ].OffsetToFileName );
|
|
|
|
modules->Modules[idx].OffsetToFileName
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!_stricmp(current_module_name.c_str(), drv_name))
|
|
|
|
if ( !_stricmp( current_module_name.c_str(), drv_name ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
auto full_path = std::string(
|
|
|
|
auto full_path = std::string( reinterpret_cast< char * >( modules->Modules[ idx ].FullPathName ) );
|
|
|
|
reinterpret_cast<char*>(
|
|
|
|
|
|
|
|
modules->Modules[idx].FullPathName));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
full_path.replace(full_path.find("\\SystemRoot\\"),
|
|
|
|
full_path.replace( full_path.find( "\\SystemRoot\\" ), sizeof( "\\SystemRoot\\" ) - 1,
|
|
|
|
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
|
|
|
std::string( getenv( "SYSTEMROOT" ) ).append( "\\" ) );
|
|
|
|
|
|
|
|
|
|
|
|
const auto module_base =
|
|
|
|
const auto module_base = LoadLibraryExA( full_path.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES );
|
|
|
|
LoadLibraryExA(
|
|
|
|
|
|
|
|
full_path.c_str(),
|
|
|
|
|
|
|
|
NULL,
|
|
|
|
|
|
|
|
DONT_RESOLVE_DLL_REFERENCES
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto image_base =
|
|
|
|
const auto image_base = reinterpret_cast< std::uintptr_t >( modules->Modules[ idx ].ImageBase );
|
|
|
|
reinterpret_cast<std::uintptr_t>(
|
|
|
|
|
|
|
|
modules->Modules[idx].ImageBase);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// free the RTL_PROCESS_MODULES buffer...
|
|
|
|
// free the RTL_PROCESS_MODULES buffer...
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
|
|
|
|
|
|
|
|
const auto rva =
|
|
|
|
const auto rva = reinterpret_cast< std::uintptr_t >( GetProcAddress( module_base, export_name ) ) -
|
|
|
|
reinterpret_cast<std::uintptr_t>(
|
|
|
|
reinterpret_cast< std::uintptr_t >( module_base );
|
|
|
|
GetProcAddress(module_base, export_name)) -
|
|
|
|
|
|
|
|
reinterpret_cast<std::uintptr_t>(module_base);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return image_base + rva;
|
|
|
|
return image_base + rva;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
VirtualFree( buffer, NULL, MEM_RELEASE );
|
|
|
|
return NULL;
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
explicit km_t() {}
|
|
|
|
explicit km_t()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class pe_t
|
|
|
|
class pe_t
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
|
|
|
|
using section_callback_t = std::function< bool( PIMAGE_SECTION_HEADER, std::uintptr_t ) >;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
public:
|
|
|
|
static auto get_instance() -> pe_t* { static pe_t obj; return &obj; }
|
|
|
|
static auto get_instance() -> pe_t *
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
static pe_t obj;
|
|
|
|
|
|
|
|
return &obj;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// returns an std::vector containing all of the bytes of the section
|
|
|
|
// returns an std::vector containing all of the bytes of the section
|
|
|
|
// and also the RVA from the image base to the beginning of the section...
|
|
|
|
// and also the RVA from the image base to the beginning of the section...
|
|
|
|
auto get_section(std::uintptr_t module_base,
|
|
|
|
auto get_section( std::uintptr_t module_base, const char *section_name )
|
|
|
|
const char* section_name) -> std::pair<std::vector<std::uint8_t>, std::uint32_t>
|
|
|
|
-> std::pair< std::vector< std::uint8_t >, std::uint32_t >
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
|
|
const auto nt_headers = reinterpret_cast< PIMAGE_NT_HEADERS >(
|
|
|
|
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
|
|
|
reinterpret_cast< PIMAGE_DOS_HEADER >( module_base )->e_lfanew + module_base );
|
|
|
|
|
|
|
|
|
|
|
|
const auto section_header =
|
|
|
|
const auto section_header = reinterpret_cast< PIMAGE_SECTION_HEADER >(
|
|
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
|
|
reinterpret_cast< std::uintptr_t >( nt_headers ) + sizeof( DWORD ) + sizeof( IMAGE_FILE_HEADER ) +
|
|
|
|
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
|
|
|
nt_headers->FileHeader.SizeOfOptionalHeader );
|
|
|
|
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
|
|
|
for ( auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto _section_name =
|
|
|
|
const auto _section_name = reinterpret_cast< char * >( section_header[ idx ].Name );
|
|
|
|
reinterpret_cast<char*>(
|
|
|
|
|
|
|
|
section_header[idx].Name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// sometimes section names are not null terminated...
|
|
|
|
// sometimes section names are not null terminated...
|
|
|
|
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
|
|
|
|
if ( !strncmp( _section_name, section_name, strlen( section_name ) - 1 ) )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto section_base =
|
|
|
|
const auto section_base =
|
|
|
|
reinterpret_cast<std::uint8_t*>(
|
|
|
|
reinterpret_cast< std::uint8_t * >( module_base + section_header[ idx ].VirtualAddress );
|
|
|
|
module_base + section_header[idx].VirtualAddress);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const auto section_end =
|
|
|
|
const auto section_end =
|
|
|
|
reinterpret_cast<std::uint8_t*>(
|
|
|
|
reinterpret_cast< std::uint8_t * >( section_base + section_header[ idx ].Misc.VirtualSize );
|
|
|
|
section_base + section_header[idx].Misc.VirtualSize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::uint8_t> section_bin(section_base, section_end);
|
|
|
|
std::vector< std::uint8_t > section_bin( section_base, section_end );
|
|
|
|
return { section_bin, section_header[idx].VirtualAddress };
|
|
|
|
return { section_bin, section_header[ idx ].VirtualAddress };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return { {}, {} };
|
|
|
|
return { {}, {} };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void each_section(section_callback_t callback, std::uintptr_t module_base)
|
|
|
|
void each_section( section_callback_t callback, std::uintptr_t module_base )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!module_base)
|
|
|
|
if ( !module_base )
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
|
|
const auto nt_headers = reinterpret_cast< PIMAGE_NT_HEADERS >(
|
|
|
|
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
|
|
|
reinterpret_cast< PIMAGE_DOS_HEADER >( module_base )->e_lfanew + module_base );
|
|
|
|
|
|
|
|
|
|
|
|
const auto section_header =
|
|
|
|
const auto section_header = reinterpret_cast< PIMAGE_SECTION_HEADER >(
|
|
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
|
|
reinterpret_cast< std::uintptr_t >( nt_headers ) + sizeof( DWORD ) + sizeof( IMAGE_FILE_HEADER ) +
|
|
|
|
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
|
|
|
nt_headers->FileHeader.SizeOfOptionalHeader );
|
|
|
|
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
|
|
|
for ( auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx )
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const auto _section_name =
|
|
|
|
const auto _section_name = reinterpret_cast< char * >( section_header[ idx ].Name );
|
|
|
|
reinterpret_cast<char*>(
|
|
|
|
|
|
|
|
section_header[idx].Name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// keep looping until the callback returns false...
|
|
|
|
// keep looping until the callback returns false...
|
|
|
|
if (!callback(§ion_header[idx], module_base))
|
|
|
|
if ( !callback( §ion_header[ idx ], module_base ) )
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
private:
|
|
|
|
explicit pe_t() {};
|
|
|
|
explicit pe_t(){};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} // namespace xtils
|