Add binary loader
This commit is contained in:
parent
22bd1aad3f
commit
dfc1037bd2
18
premake5.lua
18
premake5.lua
@ -17,6 +17,15 @@ workspace "open-mw3"
|
|||||||
staticruntime "On"
|
staticruntime "On"
|
||||||
warnings "Extra"
|
warnings "Extra"
|
||||||
|
|
||||||
|
flags {
|
||||||
|
"StaticRuntime",
|
||||||
|
"NoIncrementalLink",
|
||||||
|
"NoEditAndContinue",
|
||||||
|
"NoMinimalRebuild",
|
||||||
|
"MultiProcessorCompile",
|
||||||
|
"No64BitChecks"
|
||||||
|
}
|
||||||
|
|
||||||
configuration "windows"
|
configuration "windows"
|
||||||
defines {
|
defines {
|
||||||
"_WINDOWS",
|
"_WINDOWS",
|
||||||
@ -31,7 +40,6 @@ workspace "open-mw3"
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags {
|
flags {
|
||||||
"MultiProcessorCompile",
|
|
||||||
"LinkTimeOptimization",
|
"LinkTimeOptimization",
|
||||||
"FatalCompileWarnings",
|
"FatalCompileWarnings",
|
||||||
}
|
}
|
||||||
@ -44,10 +52,6 @@ workspace "open-mw3"
|
|||||||
"_DEBUG",
|
"_DEBUG",
|
||||||
}
|
}
|
||||||
|
|
||||||
flags {
|
|
||||||
"MultiProcessorCompile",
|
|
||||||
}
|
|
||||||
|
|
||||||
configuration {}
|
configuration {}
|
||||||
|
|
||||||
project "open-mw3"
|
project "open-mw3"
|
||||||
@ -57,6 +61,10 @@ workspace "open-mw3"
|
|||||||
pchheader "std_include.hpp"
|
pchheader "std_include.hpp"
|
||||||
pchsource "src/std_include.cpp"
|
pchsource "src/std_include.cpp"
|
||||||
|
|
||||||
|
linkoptions "/IGNORE:4254 /DYNAMICBASE:NO /SAFESEH:NO /LARGEADDRESSAWARE"
|
||||||
|
linkoptions "/LAST:.zdata"
|
||||||
|
|
||||||
|
|
||||||
files {
|
files {
|
||||||
"./src/**.rc",
|
"./src/**.rc",
|
||||||
"./src/**.hpp",
|
"./src/**.hpp",
|
||||||
|
@ -111,7 +111,7 @@ void launcher::paint() const
|
|||||||
EndPaint(this->window_, &ps);
|
EndPaint(this->window_, &ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void launcher::mouse_move(LPARAM l_param)
|
void launcher::mouse_move(const LPARAM l_param)
|
||||||
{
|
{
|
||||||
this->mouse_.x = GET_X_LPARAM(l_param);
|
this->mouse_.x = GET_X_LPARAM(l_param);
|
||||||
this->mouse_.y = GET_Y_LPARAM(l_param);
|
this->mouse_.y = GET_Y_LPARAM(l_param);
|
||||||
|
@ -17,7 +17,7 @@ window::window(const std::string& title, const int width, const int height)
|
|||||||
this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
this->wc_.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
this->wc_.hIcon = LoadIcon(handle, MAKEINTRESOURCE(102));
|
this->wc_.hIcon = LoadIcon(handle, MAKEINTRESOURCE(102));
|
||||||
this->wc_.hIconSm = this->wc_.hIcon;
|
this->wc_.hIconSm = this->wc_.hIcon;
|
||||||
this->wc_.hbrBackground = CreateSolidBrush(RGB(35, 35, 35)); //HBRUSH(COLOR_WINDOW);
|
this->wc_.hbrBackground = CreateSolidBrush(RGB(35, 35, 35));
|
||||||
this->wc_.lpszClassName = L"omw3_window";
|
this->wc_.lpszClassName = L"omw3_window";
|
||||||
RegisterClassEx(&this->wc_);
|
RegisterClassEx(&this->wc_);
|
||||||
|
|
||||||
|
187
src/loader/loader.cpp
Normal file
187
src/loader/loader.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader.hpp"
|
||||||
|
|
||||||
|
loader::loader(const launcher::mode mode) : mode_(mode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void loader::patch()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FARPROC loader::load(const utils::nt::module& module) const
|
||||||
|
{
|
||||||
|
const auto buffer = this->load_binary();
|
||||||
|
if (buffer.empty()) return nullptr;
|
||||||
|
|
||||||
|
utils::nt::module source(HMODULE(buffer.data()));
|
||||||
|
if (!source) return nullptr;
|
||||||
|
|
||||||
|
this->load_sections(module, source);
|
||||||
|
this->load_imports(module, source);
|
||||||
|
|
||||||
|
if (source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size)
|
||||||
|
{
|
||||||
|
const IMAGE_TLS_DIRECTORY* target_tls = reinterpret_cast<PIMAGE_TLS_DIRECTORY>(module.get_ptr() + module
|
||||||
|
.get_optional_header()
|
||||||
|
->DataDirectory
|
||||||
|
[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);
|
||||||
|
const IMAGE_TLS_DIRECTORY* source_tls = reinterpret_cast<PIMAGE_TLS_DIRECTORY>(module.get_ptr() + source
|
||||||
|
.get_optional_header()
|
||||||
|
->DataDirectory
|
||||||
|
[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);
|
||||||
|
|
||||||
|
*reinterpret_cast<DWORD*>(source_tls->AddressOfIndex) = 0;
|
||||||
|
|
||||||
|
DWORD old_protect;
|
||||||
|
VirtualProtect(PVOID(target_tls->StartAddressOfRawData),
|
||||||
|
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData, PAGE_READWRITE, &old_protect);
|
||||||
|
|
||||||
|
const LPVOID tls_base = *reinterpret_cast<LPVOID*>(__readfsdword(0x2C));
|
||||||
|
std::memmove(tls_base, PVOID(source_tls->StartAddressOfRawData),
|
||||||
|
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData);
|
||||||
|
std::memmove(PVOID(target_tls->StartAddressOfRawData), PVOID(source_tls->StartAddressOfRawData),
|
||||||
|
source_tls->EndAddressOfRawData - source_tls->StartAddressOfRawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD oldProtect;
|
||||||
|
VirtualProtect(module.get_nt_headers(), 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||||
|
|
||||||
|
module.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] = source
|
||||||
|
.get_optional_header()->DataDirectory[
|
||||||
|
IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||||
|
std::memmove(module.get_nt_headers(), source.get_nt_headers(),
|
||||||
|
sizeof(IMAGE_NT_HEADERS) + (source.get_nt_headers()->FileHeader.NumberOfSections * (sizeof(
|
||||||
|
IMAGE_SECTION_HEADER))));
|
||||||
|
|
||||||
|
return FARPROC(module.get_ptr() + source.get_relative_entry_point());
|
||||||
|
}
|
||||||
|
|
||||||
|
void loader::set_import_resolver(const std::function<FARPROC(const std::string&, const std::string&)>& resolver)
|
||||||
|
{
|
||||||
|
this->import_resolver_ = resolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string loader::load_binary() const
|
||||||
|
{
|
||||||
|
if (this->mode_ == launcher::mode::SINGLEPLAYER)
|
||||||
|
{
|
||||||
|
return loader::load_resource(BINARY_SP);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->mode_ == launcher::mode::MULTIPLAYER)
|
||||||
|
{
|
||||||
|
return loader::load_resource(BINARY_MP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string loader::load_resource(const int id)
|
||||||
|
{
|
||||||
|
const auto res = FindResource(::utils::nt::module(), MAKEINTRESOURCE(id), RT_RCDATA);
|
||||||
|
if (!res) return {};
|
||||||
|
|
||||||
|
const auto handle = LoadResource(nullptr, res);
|
||||||
|
if (!handle) return {};
|
||||||
|
|
||||||
|
return std::string(LPSTR(LockResource(handle)), SizeofResource(nullptr, res));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loader::load_section(const utils::nt::module& target, const utils::nt::module& source,
|
||||||
|
IMAGE_SECTION_HEADER* section)
|
||||||
|
{
|
||||||
|
void* target_ptr = target.get_ptr() + section->VirtualAddress;
|
||||||
|
const void* source_ptr = source.get_ptr() + section->PointerToRawData;
|
||||||
|
|
||||||
|
if (PBYTE(target_ptr) >= (target.get_ptr() + BINARY_PAYLOAD_SIZE))
|
||||||
|
{
|
||||||
|
MessageBoxA(nullptr, "Section exceeds the binary payload size, please increase it!", nullptr, MB_ICONERROR);
|
||||||
|
TerminateProcess(GetCurrentProcess(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->SizeOfRawData > 0)
|
||||||
|
{
|
||||||
|
const auto size_of_data = min(section->SizeOfRawData, section->Misc.VirtualSize);
|
||||||
|
std::memmove(target_ptr, source_ptr, size_of_data);
|
||||||
|
|
||||||
|
DWORD old_protect;
|
||||||
|
VirtualProtect(target_ptr, size_of_data, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loader::load_sections(const utils::nt::module& target, const utils::nt::module& source) const
|
||||||
|
{
|
||||||
|
for (auto& section : source.get_section_headers())
|
||||||
|
{
|
||||||
|
this->load_section(target, source, section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loader::load_imports(const utils::nt::module& target, const utils::nt::module& source) const
|
||||||
|
{
|
||||||
|
IMAGE_DATA_DIRECTORY* import_directory = &source.get_optional_header()->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||||
|
|
||||||
|
auto descriptor = PIMAGE_IMPORT_DESCRIPTOR(target.get_ptr() + import_directory->VirtualAddress);
|
||||||
|
|
||||||
|
while (descriptor->Name)
|
||||||
|
{
|
||||||
|
std::string name = LPSTR(target.get_ptr() + descriptor->Name);
|
||||||
|
|
||||||
|
auto name_table_entry = reinterpret_cast<uintptr_t*>(target.get_ptr() + descriptor->OriginalFirstThunk);
|
||||||
|
auto address_table_entry = reinterpret_cast<uintptr_t*>(target.get_ptr() + descriptor->FirstThunk);
|
||||||
|
|
||||||
|
if (!descriptor->OriginalFirstThunk)
|
||||||
|
{
|
||||||
|
name_table_entry = reinterpret_cast<uintptr_t*>(target.get_ptr() + descriptor->FirstThunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*name_table_entry)
|
||||||
|
{
|
||||||
|
FARPROC function = nullptr;
|
||||||
|
std::string function_name;
|
||||||
|
|
||||||
|
// is this an ordinal-only import?
|
||||||
|
if (IMAGE_SNAP_BY_ORDINAL(*name_table_entry))
|
||||||
|
{
|
||||||
|
auto module = utils::nt::module::load(name);
|
||||||
|
if (module)
|
||||||
|
{
|
||||||
|
function = GetProcAddress(module, MAKEINTRESOURCEA(IMAGE_ORDINAL(*name_table_entry)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function_name = "#" + std::to_string(IMAGE_ORDINAL(*name_table_entry));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto import = PIMAGE_IMPORT_BY_NAME(target.get_ptr() + *name_table_entry);
|
||||||
|
function_name = import->Name;
|
||||||
|
|
||||||
|
if (this->import_resolver_) function = this->import_resolver_(name, function_name);
|
||||||
|
if (!function)
|
||||||
|
{
|
||||||
|
auto module = utils::nt::module::load(name);
|
||||||
|
if (module)
|
||||||
|
{
|
||||||
|
function = GetProcAddress(module, function_name.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function)
|
||||||
|
{
|
||||||
|
auto error = "Unable to load import '"s + function_name + "' from module '"s + name + "'"s;
|
||||||
|
|
||||||
|
MessageBoxA(nullptr, error.data(), nullptr, MB_ICONERROR);
|
||||||
|
TerminateProcess(GetCurrentProcess(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
*address_table_entry = reinterpret_cast<uintptr_t>(function);
|
||||||
|
|
||||||
|
name_table_entry++;
|
||||||
|
address_table_entry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor++;
|
||||||
|
}
|
||||||
|
}
|
26
src/loader/loader.hpp
Normal file
26
src/loader/loader.hpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "utils/nt.hpp"
|
||||||
|
#include "launcher/launcher.hpp"
|
||||||
|
|
||||||
|
class loader final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit loader(launcher::mode mode);
|
||||||
|
|
||||||
|
void patch();
|
||||||
|
FARPROC load(const utils::nt::module& module) const;
|
||||||
|
|
||||||
|
void set_import_resolver(const std::function<FARPROC(const std::string&, const std::string&)>& resolver);
|
||||||
|
|
||||||
|
private:
|
||||||
|
launcher::mode mode_;
|
||||||
|
std::function<FARPROC(const std::string&, const std::string&)> import_resolver_;
|
||||||
|
|
||||||
|
std::string load_binary() const;
|
||||||
|
static std::string load_resource(const int id);
|
||||||
|
|
||||||
|
static void load_section(const utils::nt::module& target, const utils::nt::module& source, IMAGE_SECTION_HEADER* section);
|
||||||
|
void load_sections(const utils::nt::module& target, const utils::nt::module& source) const;
|
||||||
|
void load_imports(const utils::nt::module& target, const utils::nt::module& source) const;
|
||||||
|
|
||||||
|
};
|
10
src/loader/module.hpp
Normal file
10
src/loader/module.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~module() {};
|
||||||
|
virtual void pre_load() {}
|
||||||
|
virtual void post_load() {}
|
||||||
|
virtual void pre_destroy() {}
|
||||||
|
};
|
40
src/main.cpp
40
src/main.cpp
@ -1,23 +1,37 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "launcher/launcher.hpp"
|
#include "launcher/launcher.hpp"
|
||||||
|
#include "loader/loader.hpp"
|
||||||
|
|
||||||
int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
|
int CALLBACK WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
|
||||||
{
|
{
|
||||||
launcher launcher;
|
FARPROC entry_point = nullptr;
|
||||||
const auto mode = launcher.run();
|
|
||||||
|
|
||||||
if(mode == launcher::mode::NONE)
|
|
||||||
{
|
{
|
||||||
return 0;
|
launcher launcher;
|
||||||
}
|
const auto mode = launcher.run();
|
||||||
else if(mode == launcher::mode::SINGLEPLAYER)
|
|
||||||
{
|
if (mode == launcher::mode::NONE) return 0;
|
||||||
OutputDebugStringA("\n\nSINGLEPLAYER\n\n");
|
|
||||||
}
|
loader loader(mode);
|
||||||
else if(mode == launcher::mode::MULTIPLAYER)
|
loader.set_import_resolver([](const std::string& module, const std::string& function) -> FARPROC
|
||||||
{
|
{
|
||||||
MessageBoxA(nullptr, "Multiplayer not supported yet!", "ERROR", MB_ICONEXCLAMATION);
|
if (module == "steam_api.dll")
|
||||||
|
{
|
||||||
|
return utils::nt::module().get_proc<FARPROC>(function);
|
||||||
|
}
|
||||||
|
else if (function == "ExitProcess")
|
||||||
|
{
|
||||||
|
return FARPROC(exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
entry_point = loader.load({});
|
||||||
|
if (!entry_point) return 1;
|
||||||
|
|
||||||
|
loader.patch();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return entry_point();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define IMAGE_SP 300
|
#define IMAGE_SP 300
|
||||||
#define IMAGE_MP 301
|
#define IMAGE_MP 301
|
||||||
|
#define BINARY_SP 302
|
||||||
|
#define BINARY_MP 303
|
||||||
|
@ -86,9 +86,11 @@ END
|
|||||||
// Binary Data
|
// Binary Data
|
||||||
//
|
//
|
||||||
|
|
||||||
102 ICON "resources/icon.ico"
|
102 ICON "resources/icon.ico"
|
||||||
IMAGE_SP BITMAP "resources/singleplayer.bmp"
|
IMAGE_SP BITMAP "resources/singleplayer.bmp"
|
||||||
IMAGE_MP BITMAP "resources/multiplayer.bmp"
|
IMAGE_MP BITMAP "resources/multiplayer.bmp"
|
||||||
|
BINARY_SP RCDATA "resources/iw5sp.exe"
|
||||||
|
BINARY_MP RCDATA "resources/iw5mp.exe"
|
||||||
|
|
||||||
|
|
||||||
#endif // English (United States) resources
|
#endif // English (United States) resources
|
||||||
|
@ -1 +1,18 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
|
|
||||||
|
#pragma comment(linker, "/merge:.data=.cld")
|
||||||
|
#pragma comment(linker, "/merge:.rdata=.clr")
|
||||||
|
#pragma comment(linker, "/merge:.cl=.zdata")
|
||||||
|
#pragma comment(linker, "/merge:.text=.zdata")
|
||||||
|
#pragma comment(linker, "/section:.zdata,re")
|
||||||
|
#pragma comment(linker, "/base:0x400000")
|
||||||
|
|
||||||
|
__declspec(thread) char tls_data[0x2000];
|
||||||
|
|
||||||
|
#pragma bss_seg(".cdummy")
|
||||||
|
char dummy_seg[BINARY_PAYLOAD_SIZE];
|
||||||
|
|
||||||
|
char stub_seg[0x100000];
|
||||||
|
|
||||||
|
#pragma data_seg(".zdata")
|
||||||
|
char zdata[200] = { 1 };
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define BINARY_PAYLOAD_SIZE 0x0A000000
|
||||||
|
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4458)
|
#pragma warning(disable: 4458)
|
||||||
|
|
||||||
@ -14,6 +16,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
202
src/utils/nt.cpp
Normal file
202
src/utils/nt.cpp
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "nt.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace nt
|
||||||
|
{
|
||||||
|
module module::load(const std::string& name)
|
||||||
|
{
|
||||||
|
return module(LoadLibraryA(name.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
module module::get_by_address(void* address)
|
||||||
|
{
|
||||||
|
HMODULE handle = nullptr;
|
||||||
|
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast<LPCSTR>(address), &handle);
|
||||||
|
return module(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
module::module()
|
||||||
|
{
|
||||||
|
this->module_ = GetModuleHandleA(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
module::module(const std::string& name)
|
||||||
|
{
|
||||||
|
this->module_ = GetModuleHandleA(name.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
module::module(const HMODULE handle)
|
||||||
|
{
|
||||||
|
this->module_ = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool module::operator==(const module &obj) const
|
||||||
|
{
|
||||||
|
return this->module_ == obj.module_;
|
||||||
|
}
|
||||||
|
|
||||||
|
module::operator bool() const
|
||||||
|
{
|
||||||
|
return this->is_valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
module::operator HMODULE() const
|
||||||
|
{
|
||||||
|
return this->get_handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_NT_HEADERS module::get_nt_headers() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return nullptr;
|
||||||
|
return reinterpret_cast<PIMAGE_NT_HEADERS>(this->get_ptr() + this->get_dos_header()->e_lfanew);
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_DOS_HEADER module::get_dos_header() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<PIMAGE_DOS_HEADER>(this->get_ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_OPTIONAL_HEADER module::get_optional_header() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return nullptr;
|
||||||
|
return &this->get_nt_headers()->OptionalHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PIMAGE_SECTION_HEADER> module::get_section_headers() const
|
||||||
|
{
|
||||||
|
std::vector<PIMAGE_SECTION_HEADER> headers;
|
||||||
|
|
||||||
|
auto nt_headers = this->get_nt_headers();
|
||||||
|
auto section = IMAGE_FIRST_SECTION(nt_headers);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section)
|
||||||
|
{
|
||||||
|
if (section) headers.push_back(section);
|
||||||
|
else OutputDebugStringA("There was an invalid section :O");
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint8_t* module::get_ptr() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<std::uint8_t*>(this->module_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void module::unprotect() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return;
|
||||||
|
|
||||||
|
DWORD protection;
|
||||||
|
VirtualProtect(this->get_ptr(), this->get_optional_header()->SizeOfImage, PAGE_EXECUTE_READWRITE, &protection);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t module::get_relative_entry_point() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return 0;
|
||||||
|
return this->get_nt_headers()->OptionalHeader.AddressOfEntryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* module::get_entry_point() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return nullptr;
|
||||||
|
return this->get_ptr() + this->get_relative_entry_point();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool module::is_valid() const
|
||||||
|
{
|
||||||
|
return this->module_ != nullptr && this->get_dos_header()->e_magic == IMAGE_DOS_SIGNATURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string module::get_name() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return "";
|
||||||
|
|
||||||
|
auto path = this->get_path();
|
||||||
|
const auto pos = path.find_last_of("/\\");
|
||||||
|
if (pos == std::string::npos) return path;
|
||||||
|
|
||||||
|
return path.substr(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string module::get_path() const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return "";
|
||||||
|
|
||||||
|
char name[MAX_PATH] = { 0 };
|
||||||
|
GetModuleFileNameA(this->module_, name, sizeof name);
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void module::free()
|
||||||
|
{
|
||||||
|
if (this->is_valid())
|
||||||
|
{
|
||||||
|
FreeLibrary(this->module_);
|
||||||
|
this->module_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE module::get_handle() const
|
||||||
|
{
|
||||||
|
return this->module_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void** module::get_iat_entry(const std::string& module_name, const std::string& proc_name) const
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) return nullptr;
|
||||||
|
|
||||||
|
module other_module(module_name);
|
||||||
|
if (!other_module.is_valid()) return nullptr;
|
||||||
|
|
||||||
|
const auto target_function = other_module.get_proc<void*>(proc_name);
|
||||||
|
if (!target_function) return nullptr;
|
||||||
|
|
||||||
|
auto* header = this->get_optional_header();
|
||||||
|
if (!header) return nullptr;
|
||||||
|
|
||||||
|
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(this->get_ptr() + header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
|
||||||
|
|
||||||
|
while (import_descriptor->Name)
|
||||||
|
{
|
||||||
|
if (!_stricmp(reinterpret_cast<char*>(this->get_ptr() + import_descriptor->Name), module_name.data()))
|
||||||
|
{
|
||||||
|
auto* original_thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->OriginalFirstThunk + this->get_ptr());
|
||||||
|
auto* thunk_data = reinterpret_cast<PIMAGE_THUNK_DATA>(import_descriptor->FirstThunk + this->get_ptr());
|
||||||
|
|
||||||
|
while(original_thunk_data->u1.AddressOfData)
|
||||||
|
{
|
||||||
|
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
|
||||||
|
|
||||||
|
if (ordinal_number > 0xFFFF) continue;
|
||||||
|
|
||||||
|
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) == target_function)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<void**>(&thunk_data->u1.Function);
|
||||||
|
}
|
||||||
|
|
||||||
|
++original_thunk_data;
|
||||||
|
++thunk_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++import_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void raise_hard_exception()
|
||||||
|
{
|
||||||
|
int data = false;
|
||||||
|
utils::nt::module ntdll("ntdll.dll");
|
||||||
|
ntdll.invoke_pascal<void>("RtlAdjustPrivilege", 19, true, false, &data);
|
||||||
|
ntdll.invoke_pascal<void>("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
89
src/utils/nt.hpp
Normal file
89
src/utils/nt.hpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace nt
|
||||||
|
{
|
||||||
|
class module final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static module load(const std::string& name);
|
||||||
|
static module get_by_address(void* address);
|
||||||
|
|
||||||
|
module();
|
||||||
|
explicit module(const std::string& name);
|
||||||
|
explicit module(HMODULE handle);
|
||||||
|
|
||||||
|
module(const module& a) : module_(a.module_) {}
|
||||||
|
|
||||||
|
bool operator!=(const module &obj) const { return !(*this == obj); };
|
||||||
|
bool operator==(const module &obj) const;
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
operator HMODULE() const;
|
||||||
|
|
||||||
|
void unprotect() const;
|
||||||
|
void* get_entry_point() const;
|
||||||
|
size_t get_relative_entry_point() const;
|
||||||
|
|
||||||
|
bool is_valid() const;
|
||||||
|
std::string get_name() const;
|
||||||
|
std::string get_path() const;
|
||||||
|
std::uint8_t* get_ptr() const;
|
||||||
|
void free();
|
||||||
|
|
||||||
|
HMODULE get_handle() const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T get_proc(const std::string& process)
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) T{};
|
||||||
|
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::function<T> get(const std::string& process)
|
||||||
|
{
|
||||||
|
if (!this->is_valid()) std::function<T>();
|
||||||
|
return reinterpret_cast<T*>(this->get_proc<void*>(process));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T invoke(const std::string& process, Args... args)
|
||||||
|
{
|
||||||
|
auto method = this->get<T(__cdecl)(Args...)>(process);
|
||||||
|
if (method) return method(args...);
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T invoke_pascal(const std::string& process, Args... args)
|
||||||
|
{
|
||||||
|
auto method = this->get<T(__stdcall)(Args...)>(process);
|
||||||
|
if (method) return method(args...);
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T invoke_this(const std::string& process, void* thisPtr, Args... args)
|
||||||
|
{
|
||||||
|
auto method = this->get<T(__thiscall)(void*,Args...)>(thisPtr, process);
|
||||||
|
if (method) return method(args...);
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PIMAGE_SECTION_HEADER> get_section_headers() const;
|
||||||
|
|
||||||
|
PIMAGE_NT_HEADERS get_nt_headers() const;
|
||||||
|
PIMAGE_DOS_HEADER get_dos_header() const;
|
||||||
|
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
|
||||||
|
|
||||||
|
void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
HMODULE module_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void raise_hard_exception();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user