parent
58dc53850b
commit
7b1a343014
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include "game_module.hpp"
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
namespace arxan
|
namespace arxan
|
||||||
{
|
{
|
||||||
@ -18,6 +20,11 @@ namespace arxan
|
|||||||
#define ProcessDebugObjectHandle 30 // WinXP source says 31?
|
#define ProcessDebugObjectHandle 30 // WinXP source says 31?
|
||||||
#define ProcessDebugFlags 31 // WinXP source says 32?
|
#define ProcessDebugFlags 31 // WinXP source says 32?
|
||||||
|
|
||||||
|
HANDLE process_id_to_handle(const DWORD pid)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<HANDLE>(static_cast<DWORD64>(pid));
|
||||||
|
}
|
||||||
|
|
||||||
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
|
NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class,
|
||||||
const PVOID info,
|
const PVOID info,
|
||||||
const ULONG info_length, const PULONG ret_length)
|
const ULONG info_length, const PULONG ret_length)
|
||||||
@ -71,6 +78,13 @@ namespace arxan
|
|||||||
return STATUS_INVALID_HANDLE;
|
return STATUS_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hide_being_debugged()
|
||||||
|
{
|
||||||
|
auto* const peb = PPEB(__readgsqword(0x60));
|
||||||
|
peb->BeingDebugged = false;
|
||||||
|
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70;
|
||||||
|
}
|
||||||
|
|
||||||
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
|
LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info)
|
||||||
{
|
{
|
||||||
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
|
if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE)
|
||||||
@ -78,19 +92,185 @@ namespace arxan
|
|||||||
return EXCEPTION_CONTINUE_EXECUTION;
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION)
|
|
||||||
{
|
|
||||||
//MessageBoxA(0, 0, "AV", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hide_being_debugged()
|
struct integrity_handler_context
|
||||||
{
|
{
|
||||||
auto* const peb = PPEB(__readgsqword(0x60));
|
uint32_t* computed_checksum;
|
||||||
peb->BeingDebugged = false;
|
uint32_t* original_checksum;
|
||||||
*reinterpret_cast<PDWORD>(LPSTR(peb) + 0xBC) &= ~0x70;
|
};
|
||||||
|
|
||||||
|
// Pretty trashy, but working, heuristic to search integrity the handler context
|
||||||
|
bool is_handler_context(uint8_t* stack_frame, const uint32_t computed_checksum, const uint32_t frame_offset)
|
||||||
|
{
|
||||||
|
auto* potential_address = *reinterpret_cast<uint32_t**>(stack_frame + frame_offset);
|
||||||
|
|
||||||
|
int64_t diff = reinterpret_cast<uint64_t>(stack_frame) - reinterpret_cast<uint64_t>(potential_address);
|
||||||
|
diff = std::abs(diff);
|
||||||
|
|
||||||
|
return diff < 0x1000 && *potential_address == computed_checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
integrity_handler_context* search_handler_context(uint8_t* stack_frame, const uint32_t computed_checksum)
|
||||||
|
{
|
||||||
|
for (uint32_t frame_offset = 24; frame_offset < 64; frame_offset += 8)
|
||||||
|
{
|
||||||
|
if (is_handler_context(stack_frame, computed_checksum, frame_offset))
|
||||||
|
{
|
||||||
|
return reinterpret_cast<integrity_handler_context*>(stack_frame + frame_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t adjust_integrity_checksum(const uint64_t return_address, uint8_t* stack_frame,
|
||||||
|
const uint32_t current_checksum)
|
||||||
|
{
|
||||||
|
const auto handler_address = return_address - 5;
|
||||||
|
const auto* context = search_handler_context(stack_frame, current_checksum);
|
||||||
|
|
||||||
|
if (!context)
|
||||||
|
{
|
||||||
|
OutputDebugStringA(utils::string::va("Unable to find frame offset for: %llX", return_address));
|
||||||
|
return current_checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto correct_checksum = *context->original_checksum;
|
||||||
|
*context->computed_checksum = correct_checksum;
|
||||||
|
|
||||||
|
if (current_checksum != correct_checksum)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
OutputDebugStringA(utils::string::va("Adjusting checksum (%llX): %X -> %X", handler_address,
|
||||||
|
current_checksum, correct_checksum));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return correct_checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_intact_basic_block_integrity_check(void* address)
|
||||||
|
{
|
||||||
|
const auto game_address = reinterpret_cast<uint64_t>(address);
|
||||||
|
constexpr auto inst_len = 3;
|
||||||
|
|
||||||
|
const auto next_inst_addr = game_address + inst_len;
|
||||||
|
const auto next_inst = *reinterpret_cast<uint32_t*>(next_inst_addr);
|
||||||
|
|
||||||
|
if ((next_inst & 0xFF00FFFF) != 0xFF004583)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(utils::string::va("Unable to patch intact basic block: %llX", game_address));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto other_frame_offset = static_cast<uint8_t>(next_inst >> 16);
|
||||||
|
static const auto stub = utils::hook::assemble([](utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
a.push(rax);
|
||||||
|
|
||||||
|
a.mov(rax, qword_ptr(rsp, 8));
|
||||||
|
a.sub(rax, 2); // Skip the push we inserted
|
||||||
|
|
||||||
|
a.push(rax);
|
||||||
|
a.pushad64();
|
||||||
|
|
||||||
|
a.mov(r8, qword_ptr(rsp, 0x88));
|
||||||
|
a.mov(rcx, rax);
|
||||||
|
a.mov(rdx, rbp);
|
||||||
|
a.call_aligned(adjust_integrity_checksum);
|
||||||
|
|
||||||
|
a.mov(qword_ptr(rsp, 0x80), rax);
|
||||||
|
|
||||||
|
a.popad64();
|
||||||
|
a.pop(rax);
|
||||||
|
|
||||||
|
a.add(rsp, 8);
|
||||||
|
|
||||||
|
a.mov(dword_ptr(rdx, rcx, 4), eax);
|
||||||
|
|
||||||
|
a.pop(rax); // return addr
|
||||||
|
a.xchg(rax, qword_ptr(rsp)); // switch with push
|
||||||
|
|
||||||
|
a.add(dword_ptr(rbp, rax), 0xFFFFFFFF);
|
||||||
|
|
||||||
|
a.mov(rax, dword_ptr(rdx, rcx, 4)); // restore rax
|
||||||
|
|
||||||
|
a.ret();
|
||||||
|
});
|
||||||
|
|
||||||
|
// push other_frame_offset
|
||||||
|
utils::hook::set<uint16_t>(game_address, static_cast<uint16_t>(0x6A | (other_frame_offset << 8)));
|
||||||
|
utils::hook::call(game_address + 2, stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_split_basic_block_integrity_check(void* address)
|
||||||
|
{
|
||||||
|
const auto game_address = reinterpret_cast<uint64_t>(address);
|
||||||
|
constexpr auto inst_len = 3;
|
||||||
|
|
||||||
|
const auto next_inst_addr = game_address + inst_len;
|
||||||
|
|
||||||
|
if (*reinterpret_cast<uint8_t*>(next_inst_addr) != 0xE9)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(utils::string::va("Unable to patch split basic block: %llX", game_address));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto jump_target = utils::hook::extract<void*>(reinterpret_cast<void*>(next_inst_addr + 1));
|
||||||
|
const auto stub = utils::hook::assemble([jump_target](utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
a.push(rax);
|
||||||
|
|
||||||
|
a.mov(rax, qword_ptr(rsp, 8));
|
||||||
|
a.push(rax);
|
||||||
|
|
||||||
|
a.pushad64();
|
||||||
|
|
||||||
|
a.mov(r8, qword_ptr(rsp, 0x88));
|
||||||
|
a.mov(rcx, rax);
|
||||||
|
a.mov(rdx, rbp);
|
||||||
|
a.call_aligned(adjust_integrity_checksum);
|
||||||
|
|
||||||
|
a.mov(qword_ptr(rsp, 0x80), rax);
|
||||||
|
|
||||||
|
a.popad64();
|
||||||
|
a.pop(rax);
|
||||||
|
|
||||||
|
a.add(rsp, 8);
|
||||||
|
|
||||||
|
a.mov(dword_ptr(rdx, rcx, 4), eax);
|
||||||
|
|
||||||
|
a.add(rsp, 8);
|
||||||
|
|
||||||
|
a.jmp(jump_target);
|
||||||
|
});
|
||||||
|
|
||||||
|
utils::hook::call(game_address, stub);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_and_patch_integrity_checks()
|
||||||
|
{
|
||||||
|
// There seem to be 670 results.
|
||||||
|
// Searching them is quite slow.
|
||||||
|
// Maybe precomputing that might be better?
|
||||||
|
const auto intact_results = "89 04 8A 83 45 ? FF"_sig;
|
||||||
|
const auto split_results = "89 04 8A E9"_sig;
|
||||||
|
|
||||||
|
int results = 0;
|
||||||
|
|
||||||
|
for (auto* i : intact_results)
|
||||||
|
{
|
||||||
|
patch_intact_basic_block_integrity_check(i);
|
||||||
|
results++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* i : split_results)
|
||||||
|
{
|
||||||
|
patch_split_basic_block_integrity_check(i);
|
||||||
|
results++;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputDebugStringA(utils::string::va("integrity check amount: %d\n", results));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,12 +283,10 @@ namespace arxan
|
|||||||
scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
|
scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
|
||||||
|
|
||||||
const utils::nt::library ntdll("ntdll.dll");
|
const utils::nt::library ntdll("ntdll.dll");
|
||||||
|
|
||||||
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);
|
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);
|
||||||
|
|
||||||
const auto nt_query_information_process = ntdll.get_proc<void*>("NtQueryInformationProcess");
|
const auto nt_query_information_process = ntdll.get_proc<void*>("NtQueryInformationProcess");
|
||||||
nt_query_information_process_hook.create(nt_query_information_process,
|
nt_query_information_process_hook.create(nt_query_information_process, nt_query_information_process_stub);
|
||||||
nt_query_information_process_stub);
|
|
||||||
nt_query_information_process_hook.move();
|
nt_query_information_process_hook.move();
|
||||||
|
|
||||||
AddVectoredExceptionHandler(1, exception_filter);
|
AddVectoredExceptionHandler(1, exception_filter);
|
||||||
@ -116,7 +294,12 @@ namespace arxan
|
|||||||
|
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
|
search_and_patch_integrity_checks();
|
||||||
|
}
|
||||||
|
|
||||||
|
int priority() override
|
||||||
|
{
|
||||||
|
return COMPONENT_MAX_PRIORITY;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,171 @@
|
|||||||
#include "hook.hpp"
|
#include "hook.hpp"
|
||||||
#include "string.hpp"
|
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <MinHook.h>
|
#include <MinHook.h>
|
||||||
|
|
||||||
|
#include "concurrency.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "nt.hpp"
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace utils::hook
|
namespace utils::hook
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
uint8_t* allocate_somewhere_near(const void* base_address, const size_t size)
|
||||||
|
{
|
||||||
|
size_t offset = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
offset += size;
|
||||||
|
auto* target_address = static_cast<const uint8_t*>(base_address) - offset;
|
||||||
|
if (is_relatively_far(base_address, target_address))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto res = VirtualAlloc(const_cast<uint8_t*>(target_address), size, MEM_RESERVE | MEM_COMMIT,
|
||||||
|
PAGE_EXECUTE_READWRITE);
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
if (is_relatively_far(base_address, target_address))
|
||||||
|
{
|
||||||
|
VirtualFree(res, 0, MEM_RELEASE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<uint8_t*>(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class memory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
memory() = default;
|
||||||
|
|
||||||
|
memory(const void* ptr)
|
||||||
|
: memory()
|
||||||
|
{
|
||||||
|
this->length_ = 0x1000;
|
||||||
|
this->buffer_ = allocate_somewhere_near(ptr, this->length_);
|
||||||
|
if (!this->buffer_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to allocate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~memory()
|
||||||
|
{
|
||||||
|
if (this->buffer_)
|
||||||
|
{
|
||||||
|
VirtualFree(this->buffer_, 0, MEM_RELEASE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memory(memory&& obj) noexcept
|
||||||
|
: memory()
|
||||||
|
{
|
||||||
|
this->operator=(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
memory& operator=(memory&& obj) noexcept
|
||||||
|
{
|
||||||
|
if (this != &obj)
|
||||||
|
{
|
||||||
|
this->~memory();
|
||||||
|
this->buffer_ = obj.buffer_;
|
||||||
|
this->length_ = obj.length_;
|
||||||
|
this->offset_ = obj.offset_;
|
||||||
|
|
||||||
|
obj.buffer_ = nullptr;
|
||||||
|
obj.length_ = 0;
|
||||||
|
obj.offset_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* allocate(const size_t length)
|
||||||
|
{
|
||||||
|
if (!this->buffer_)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->offset_ + length > this->length_)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ptr = this->get_ptr();
|
||||||
|
this->offset_ += length;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* get_ptr() const
|
||||||
|
{
|
||||||
|
return this->buffer_ + this->offset_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t* buffer_{};
|
||||||
|
size_t length_{};
|
||||||
|
size_t offset_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
void* get_memory_near(const void* address, const size_t size)
|
||||||
|
{
|
||||||
|
static concurrency::container<std::vector<memory>> memory_container{};
|
||||||
|
|
||||||
|
return memory_container.access<void*>([&](std::vector<memory>& memories)
|
||||||
|
{
|
||||||
|
for (auto& memory : memories)
|
||||||
|
{
|
||||||
|
if (!is_relatively_far(address, memory.get_ptr()))
|
||||||
|
{
|
||||||
|
const auto buffer = memory.allocate(size);
|
||||||
|
if (buffer)
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memories.emplace_back(address);
|
||||||
|
return memories.back().allocate(size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
concurrency::container<std::map<const void*, uint8_t>>& get_original_data_map()
|
||||||
|
{
|
||||||
|
static concurrency::container<std::map<const void*, uint8_t>> og_data{};
|
||||||
|
return og_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void store_original_data(const void* /*data*/, size_t /*length*/)
|
||||||
|
{
|
||||||
|
/*get_original_data_map().access([data, length](std::map<const void*, uint8_t>& og_map)
|
||||||
|
{
|
||||||
|
const auto data_ptr = static_cast<const uint8_t*>(data);
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
const auto pos = data_ptr + i;
|
||||||
|
if (!og_map.contains(pos))
|
||||||
|
{
|
||||||
|
og_map[pos] = *pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
void* initialize_min_hook()
|
void* initialize_min_hook()
|
||||||
{
|
{
|
||||||
static class min_hook_init
|
static class min_hook_init
|
||||||
@ -140,6 +299,7 @@ namespace utils::hook
|
|||||||
{
|
{
|
||||||
this->clear();
|
this->clear();
|
||||||
this->place_ = place;
|
this->place_ = place;
|
||||||
|
store_original_data(place, 14);
|
||||||
|
|
||||||
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
|
if (MH_CreateHook(this->place_, target, &this->original_) != MH_OK)
|
||||||
{
|
{
|
||||||
@ -192,6 +352,8 @@ namespace utils::hook
|
|||||||
auto* const ptr = library.get_iat_entry(target_library, process);
|
auto* const ptr = library.get_iat_entry(target_library, process);
|
||||||
if (!ptr) return false;
|
if (!ptr) return false;
|
||||||
|
|
||||||
|
store_original_data(ptr, sizeof(*ptr));
|
||||||
|
|
||||||
DWORD protect;
|
DWORD protect;
|
||||||
VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect);
|
VirtualProtect(ptr, sizeof(*ptr), PAGE_EXECUTE_READWRITE, &protect);
|
||||||
|
|
||||||
@ -203,6 +365,8 @@ namespace utils::hook
|
|||||||
|
|
||||||
void nop(void* place, const size_t length)
|
void nop(void* place, const size_t length)
|
||||||
{
|
{
|
||||||
|
store_original_data(place, length);
|
||||||
|
|
||||||
DWORD old_protect{};
|
DWORD old_protect{};
|
||||||
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||||
|
|
||||||
@ -219,6 +383,8 @@ namespace utils::hook
|
|||||||
|
|
||||||
void copy(void* place, const void* data, const size_t length)
|
void copy(void* place, const void* data, const size_t length)
|
||||||
{
|
{
|
||||||
|
store_original_data(place, length);
|
||||||
|
|
||||||
DWORD old_protect{};
|
DWORD old_protect{};
|
||||||
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &old_protect);
|
||||||
|
|
||||||
@ -233,6 +399,16 @@ namespace utils::hook
|
|||||||
copy(reinterpret_cast<void*>(place), data, length);
|
copy(reinterpret_cast<void*>(place), data, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void copy_string(void* place, const char* str)
|
||||||
|
{
|
||||||
|
copy(reinterpret_cast<void*>(place), str, strlen(str) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_string(const size_t place, const char* str)
|
||||||
|
{
|
||||||
|
copy_string(reinterpret_cast<void*>(place), str);
|
||||||
|
}
|
||||||
|
|
||||||
bool is_relatively_far(const void* pointer, const void* data, const int offset)
|
bool is_relatively_far(const void* pointer, const void* data, const int offset)
|
||||||
{
|
{
|
||||||
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
|
const int64_t diff = size_t(data) - (size_t(pointer) + offset);
|
||||||
@ -244,12 +420,23 @@ namespace utils::hook
|
|||||||
{
|
{
|
||||||
if (is_relatively_far(pointer, data))
|
if (is_relatively_far(pointer, data))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Too far away to create 32bit relative branch");
|
auto* trampoline = get_memory_near(pointer, 14);
|
||||||
|
if (!trampoline)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Too far away to create 32bit relative branch");
|
||||||
|
}
|
||||||
|
|
||||||
|
call(pointer, trampoline);
|
||||||
|
jump(trampoline, data, true, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t copy_data[5];
|
||||||
|
copy_data[0] = 0xE8;
|
||||||
|
*reinterpret_cast<int32_t*>(©_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5));
|
||||||
|
|
||||||
auto* patch_pointer = PBYTE(pointer);
|
auto* patch_pointer = PBYTE(pointer);
|
||||||
set<uint8_t>(patch_pointer, 0xE8);
|
copy(patch_pointer, copy_data, sizeof(copy_data));
|
||||||
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call(const size_t pointer, void* data)
|
void call(const size_t pointer, void* data)
|
||||||
@ -274,7 +461,14 @@ namespace utils::hook
|
|||||||
|
|
||||||
if (!use_far && is_relatively_far(pointer, data))
|
if (!use_far && is_relatively_far(pointer, data))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Too far away to create 32bit relative branch");
|
auto* trampoline = get_memory_near(pointer, 14);
|
||||||
|
if (!trampoline)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Too far away to create 32bit relative branch");
|
||||||
|
}
|
||||||
|
jump(pointer, trampoline, false, false);
|
||||||
|
jump(trampoline, data, true, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* patch_pointer = PBYTE(pointer);
|
auto* patch_pointer = PBYTE(pointer);
|
||||||
@ -283,19 +477,28 @@ namespace utils::hook
|
|||||||
{
|
{
|
||||||
if (use_safe)
|
if (use_safe)
|
||||||
{
|
{
|
||||||
copy(patch_pointer, jump_data_safe, sizeof(jump_data_safe));
|
uint8_t copy_data[sizeof(jump_data_safe) + sizeof(data)];
|
||||||
copy(patch_pointer + sizeof(jump_data_safe), &data, sizeof(data));
|
memcpy(copy_data, jump_data_safe, sizeof(jump_data_safe));
|
||||||
|
memcpy(copy_data + sizeof(jump_data_safe), &data, sizeof(data));
|
||||||
|
|
||||||
|
copy(patch_pointer, copy_data, sizeof(copy_data));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
copy(patch_pointer, jump_data, sizeof(jump_data));
|
uint8_t copy_data[sizeof(jump_data)];
|
||||||
copy(patch_pointer + 2, &data, sizeof(data));
|
memcpy(copy_data, jump_data, sizeof(jump_data));
|
||||||
|
memcpy(copy_data + 2, &data, sizeof(data));
|
||||||
|
|
||||||
|
copy(patch_pointer, copy_data, sizeof(copy_data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
set<uint8_t>(patch_pointer, 0xE9);
|
uint8_t copy_data[5];
|
||||||
set<int32_t>(patch_pointer + 1, int32_t(size_t(data) - (size_t(pointer) + 5)));
|
copy_data[0] = 0xE9;
|
||||||
|
*reinterpret_cast<int32_t*>(©_data[1]) = int32_t(size_t(data) - (size_t(pointer) + 5));
|
||||||
|
|
||||||
|
copy(patch_pointer, copy_data, sizeof(copy_data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,4 +589,26 @@ namespace utils::hook
|
|||||||
|
|
||||||
return extract<void*>(data + 1);
|
return extract<void*>(data + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> query_original_data(const void* data, const size_t length)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> og_data{};
|
||||||
|
og_data.resize(length);
|
||||||
|
memcpy(og_data.data(), data, length);
|
||||||
|
|
||||||
|
get_original_data_map().access([data, length, &og_data](const std::map<const void*, uint8_t>& og_map)
|
||||||
|
{
|
||||||
|
auto* ptr = static_cast<const uint8_t*>(data);
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
auto entry = og_map.find(ptr + i);
|
||||||
|
if (entry != og_map.end())
|
||||||
|
{
|
||||||
|
og_data[i] = entry->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return og_data;
|
||||||
|
}
|
||||||
}
|
}
|
@ -22,9 +22,9 @@ namespace utils::hook
|
|||||||
{
|
{
|
||||||
auto functions = get_iota_functions<Entries - 1>();
|
auto functions = get_iota_functions<Entries - 1>();
|
||||||
functions.emplace_back([]()
|
functions.emplace_back([]()
|
||||||
{
|
{
|
||||||
return Entries - 1;
|
return Entries - 1;
|
||||||
});
|
});
|
||||||
return functions;
|
return functions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,6 +155,9 @@ namespace utils::hook
|
|||||||
void copy(void* place, const void* data, size_t length);
|
void copy(void* place, const void* data, size_t length);
|
||||||
void copy(size_t place, const void* data, size_t length);
|
void copy(size_t place, const void* data, size_t length);
|
||||||
|
|
||||||
|
void copy_string(void* place, const char* str);
|
||||||
|
void copy_string(size_t place, const char* str);
|
||||||
|
|
||||||
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
bool is_relatively_far(const void* pointer, const void* data, int offset = 5);
|
||||||
|
|
||||||
void call(void* pointer, void* data);
|
void call(void* pointer, void* data);
|
||||||
@ -184,19 +187,13 @@ namespace utils::hook
|
|||||||
void* follow_branch(void* address);
|
void* follow_branch(void* address);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void set(void* place, T value)
|
static void set(void* place, T value = false)
|
||||||
{
|
{
|
||||||
DWORD old_protect;
|
copy(place, &value, sizeof(value));
|
||||||
VirtualProtect(place, sizeof(T), PAGE_EXECUTE_READWRITE, &old_protect);
|
|
||||||
|
|
||||||
*static_cast<T*>(place) = value;
|
|
||||||
|
|
||||||
VirtualProtect(place, sizeof(T), old_protect, &old_protect);
|
|
||||||
FlushInstructionCache(GetCurrentProcess(), place, sizeof(T));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static void set(const size_t place, T value)
|
static void set(const size_t place, T value = false)
|
||||||
{
|
{
|
||||||
return set<T>(reinterpret_cast<void*>(place), value);
|
return set<T>(reinterpret_cast<void*>(place), value);
|
||||||
}
|
}
|
||||||
@ -212,4 +209,6 @@ namespace utils::hook
|
|||||||
{
|
{
|
||||||
return static_cast<T(*)(Args ...)>(func)(args...);
|
return static_cast<T(*)(Args ...)>(func)(args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> query_original_data(const void* data, size_t length);
|
||||||
}
|
}
|
@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef min
|
||||||
|
#undef min
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace utils::hook
|
namespace utils::hook
|
||||||
{
|
{
|
||||||
void signature::load_pattern(const std::string& pattern)
|
void signature::load_pattern(const std::string& pattern)
|
||||||
@ -29,7 +37,7 @@ namespace utils::hook
|
|||||||
throw std::runtime_error("Invalid pattern");
|
throw std::runtime_error("Invalid pattern");
|
||||||
}
|
}
|
||||||
|
|
||||||
char str[] = {val, 0};
|
char str[] = { val, 0 };
|
||||||
const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16));
|
const auto current_nibble = static_cast<uint8_t>(strtol(str, nullptr, 16));
|
||||||
|
|
||||||
if (!has_nibble)
|
if (!has_nibble)
|
||||||
@ -68,15 +76,15 @@ namespace utils::hook
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<size_t> signature::process_range(uint8_t* start, const size_t length) const
|
signature::signature_result signature::process_range(uint8_t* start, const size_t length) const
|
||||||
{
|
{
|
||||||
if (this->has_sse_support()) return this->process_range_vectorized(start, length);
|
if (this->has_sse_support()) return this->process_range_vectorized(start, length);
|
||||||
return this->process_range_linear(start, length);
|
return this->process_range_linear(start, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<size_t> signature::process_range_linear(uint8_t* start, const size_t length) const
|
signature::signature_result signature::process_range_linear(uint8_t* start, const size_t length) const
|
||||||
{
|
{
|
||||||
std::vector<size_t> result;
|
std::vector<uint8_t*> result;
|
||||||
|
|
||||||
for (size_t i = 0; i < length; ++i)
|
for (size_t i = 0; i < length; ++i)
|
||||||
{
|
{
|
||||||
@ -93,17 +101,17 @@ namespace utils::hook
|
|||||||
|
|
||||||
if (j == this->mask_.size())
|
if (j == this->mask_.size())
|
||||||
{
|
{
|
||||||
result.push_back(size_t(address));
|
result.push_back(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<size_t> signature::process_range_vectorized(uint8_t* start, const size_t length) const
|
signature::signature_result signature::process_range_vectorized(uint8_t* start, const size_t length) const
|
||||||
{
|
{
|
||||||
std::vector<size_t> result;
|
std::vector<uint8_t*> result;
|
||||||
__declspec(align(16)) char desired_mask[16] = {0};
|
__declspec(align(16)) char desired_mask[16] = { 0 };
|
||||||
|
|
||||||
for (size_t i = 0; i < this->mask_.size(); i++)
|
for (size_t i = 0; i < this->mask_.size(); i++)
|
||||||
{
|
{
|
||||||
@ -118,14 +126,14 @@ namespace utils::hook
|
|||||||
const auto address = start + i;
|
const auto address = start + i;
|
||||||
const auto value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(address));
|
const auto value = _mm_loadu_si128(reinterpret_cast<const __m128i*>(address));
|
||||||
const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()),
|
const auto comparison = _mm_cmpestrm(value, 16, comparand, static_cast<int>(this->mask_.size()),
|
||||||
_SIDD_CMP_EQUAL_EACH);
|
_SIDD_CMP_EQUAL_EACH);
|
||||||
|
|
||||||
const auto matches = _mm_and_si128(mask, comparison);
|
const auto matches = _mm_and_si128(mask, comparison);
|
||||||
const auto equivalence = _mm_xor_si128(mask, matches);
|
const auto equivalence = _mm_xor_si128(mask, matches);
|
||||||
|
|
||||||
if (_mm_test_all_zeros(equivalence, equivalence))
|
if (_mm_test_all_zeros(equivalence, equivalence))
|
||||||
{
|
{
|
||||||
result.push_back(size_t(address));
|
result.push_back(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +152,7 @@ namespace utils::hook
|
|||||||
signature::signature_result signature::process_serial() const
|
signature::signature_result signature::process_serial() const
|
||||||
{
|
{
|
||||||
const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
|
const auto sub = this->has_sse_support() ? 16 : this->mask_.size();
|
||||||
return {this->process_range(this->start_, this->length_ - sub)};
|
return { this->process_range(this->start_, this->length_ - sub) };
|
||||||
}
|
}
|
||||||
|
|
||||||
signature::signature_result signature::process_parallel() const
|
signature::signature_result signature::process_parallel() const
|
||||||
@ -156,7 +164,7 @@ namespace utils::hook
|
|||||||
const auto grid = range / cores;
|
const auto grid = range / cores;
|
||||||
|
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::vector<size_t> result;
|
std::vector<uint8_t*> result;
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
for (auto i = 0u; i < cores; ++i)
|
for (auto i = 0u; i < cores; ++i)
|
||||||
@ -165,7 +173,7 @@ namespace utils::hook
|
|||||||
const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid;
|
const auto length = (i + 1 == cores) ? (this->start_ + this->length_ - sub) - start : grid;
|
||||||
threads.emplace_back([&, start, length]()
|
threads.emplace_back([&, start, length]()
|
||||||
{
|
{
|
||||||
auto local_result = this->process_range(start, length);
|
const auto local_result = this->process_range(start, length);
|
||||||
if (local_result.empty()) return;
|
if (local_result.empty()) return;
|
||||||
|
|
||||||
std::lock_guard _(mutex);
|
std::lock_guard _(mutex);
|
||||||
@ -185,7 +193,7 @@ namespace utils::hook
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::sort(result.begin(), result.end());
|
std::sort(result.begin(), result.end());
|
||||||
return {std::move(result)};
|
return { std::move(result) };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool signature::has_sse_support() const
|
bool signature::has_sse_support() const
|
||||||
|
@ -7,33 +7,9 @@ namespace utils::hook
|
|||||||
class signature final
|
class signature final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class signature_result
|
using signature_result = std::vector<uint8_t*>;
|
||||||
{
|
|
||||||
public:
|
|
||||||
signature_result(std::vector<size_t>&& matches) : matches_(std::move(matches))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] uint8_t* get(const size_t index) const
|
explicit signature(const std::string& pattern, const nt::library& library = {})
|
||||||
{
|
|
||||||
if (index >= this->count())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid index");
|
|
||||||
}
|
|
||||||
|
|
||||||
return reinterpret_cast<uint8_t*>(this->matches_[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] size_t count() const
|
|
||||||
{
|
|
||||||
return this->matches_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<size_t> matches_;
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit signature(const std::string& pattern, const nt::library library = {})
|
|
||||||
: signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage)
|
: signature(pattern, library.get_ptr(), library.get_optional_header()->SizeOfImage)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -62,9 +38,9 @@ namespace utils::hook
|
|||||||
|
|
||||||
signature_result process_parallel() const;
|
signature_result process_parallel() const;
|
||||||
signature_result process_serial() const;
|
signature_result process_serial() const;
|
||||||
std::vector<size_t> process_range(uint8_t* start, size_t length) const;
|
signature_result process_range(uint8_t* start, size_t length) const;
|
||||||
std::vector<size_t> process_range_linear(uint8_t* start, size_t length) const;
|
signature_result process_range_linear(uint8_t* start, size_t length) const;
|
||||||
std::vector<size_t> process_range_vectorized(uint8_t* start, size_t length) const;
|
signature_result process_range_vectorized(uint8_t* start, size_t length) const;
|
||||||
|
|
||||||
bool has_sse_support() const;
|
bool has_sse_support() const;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user