You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vmhook-eac/drv_entry.cpp

237 lines
5.6 KiB

//
// Registers on image load callback then applies vmhook to EAC
//
//
#include "scn.hpp"
#include "shithook.hpp"
#include "sha1.hpp"
#include "md5.hpp"
//
// game cheat offset flash backs...
//
#define EAC_VM_HANDLE_OFFSET 0xE93D
#define EAC_SHA1_OFFSET 0x4C00
#define EAC_MD5_OFFSET 0x37378
#define EAC_CRC32_OFFSET 0x27C8C
#define EAC_IMAGE_BASE 0x140000000
#define DBG_PRINT(format, ...) \
DbgPrintEx( DPFLTR_SYSTEM_ID, DPFLTR_ERROR_LEVEL, \
"[vmhook-eac [core number = %d]]" format, KeGetCurrentProcessorNumber(), __VA_ARGS__)
//
// vm handler indexes for READQ...
//
u8 readq_idxs[] = { 247, 215, 169, 159, 71, 60, 55, 43, 23 };
//
// vm handler indexes for READDW
//
u8 readdw_idxs[] = { 218, 180, 179, 178, 163, 137, 92, 22, 12 };
//
// vm handler indexes for READB
//
u8 readb_idxs[] = { 249, 231, 184, 160, 88, 85, 48, 9, 2 };
vm::hook_t* g_vmhook = nullptr;
vm::handler::table_t* g_vm_table = nullptr;
u64 __image_base = 0u, __image_size = 0u, __image_clone = 0u;
inline_hook_t __crc32_hook, __sha1_hook, __md5_hook;
void*
operator new(
u64 size
)
{
//
// Could have also used ExAllocatePoolZero...
//
return RtlZeroMemory(ExAllocatePool(NonPagedPool, size), size);
}
void
operator delete
(
void* ptr,
u64 size
)
{
UNREFERENCED_PARAMETER(size);
ExFreePool(ptr);
}
__declspec(noinline)
void hook_sha1(
void *data, unsigned int len, void* result
)
{
sha1_ctx ctx;
sha1_init(&ctx);
if (scn::read_only(__image_base, (u64)data))
{
DBG_PRINT("sha1 hash data = 0x%p, len = 0x%x, result = 0x%p\n", data, len, result);
sha1_update(&ctx, (unsigned char*)__image_clone + (((u64)data) - __image_base), len);
sha1_final((unsigned char*)result, &ctx);
return;
}
sha1_update(&ctx, (unsigned char*) data, len);
sha1_final((unsigned char*)result, &ctx);
}
void
image_loaded(
PUNICODE_STRING image_name,
HANDLE pid,
PIMAGE_INFO image_info
)
{
//
// PID is zero when the module being loaded is going into the kernel...
//
if (!pid && wcsstr(image_name->Buffer, L"EasyAntiCheat.sys"))
{
if (g_vmhook && g_vm_table && __image_clone)
delete g_vmhook, delete g_vm_table, ExFreePool((void*)__image_clone);
//
// allocate memory for a g_vmhook, g_vm_table and then zero it...
//
// > 0x00007FF77A233736 mov rcx, [r12+rax*8]
// > 0x00007FF77A23373D ror rcx, 0x30 <--- decrypt vm handler entry...
// > 0x00007FF77A233747 add rcx, r13
// > 0x00007FF77A23374A jmp rcx
vm::decrypt_handler_t _decrypt_handler =
[](u64 val) -> u64
{
return _rotr64(val, 0x30);
};
// > 0x00007FF77A233736 mov rcx, [r12+rax*8]
// > 0x00007FF77A23373D ror rcx, 0x30 <--- inverse to encrypt vm handler entry...
// > 0x00007FF77A233747 add rcx, r13
// > 0x00007FF77A23374A jmp rcx
vm::encrypt_handler_t _encrypt_handler =
[](u64 val) -> u64
{
return _rotl64(val, 0x30);
};
vm::handler::edit_entry_t _edit_entry =
[](u64* entry_ptr, u64 val) -> void
{
//
// disable write protect bit in cr0...
//
{
auto cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);
_disable();
}
*entry_ptr = val;
//
// enable write protect bit in cr0...
//
{
auto cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
}
};
auto image_base = reinterpret_cast<u64>(image_info->ImageBase);
auto handler_table_ptr = reinterpret_cast<u64*>(image_base + EAC_VM_HANDLE_OFFSET);
__image_clone = reinterpret_cast<u64>(ExAllocatePool(NonPagedPool, image_info->ImageSize));
RtlCopyMemory((void*)__image_clone, (void*)image_base, image_info->ImageSize);
g_vm_table = new vm::handler::table_t(handler_table_ptr, _edit_entry);
g_vmhook = new vm::hook_t(image_base, EAC_IMAGE_BASE, _decrypt_handler, _encrypt_handler, g_vm_table);
__image_base = image_base, __image_size = image_info->ImageSize;
const auto callback = [](vm::registers* regs, u8 handler_idx)
{
const auto read_addr = reinterpret_cast<u64*>(regs->rbp)[0];
// shoot the tires right off the virtualized integrity checks in about 2 lines of code...
if (scn::read_only(__image_base, read_addr))
{
DBG_PRINT(" READ(Q/DW/B) EasyAntiCheat.sys+0x%x\n", (read_addr - __image_base));
reinterpret_cast<u64*>(regs->rbp)[0] = __image_clone + (read_addr - __image_base);
}
};
// install hooks on READQ virtual machine handlers...
for (auto idx = 0u; idx < sizeof readq_idxs; ++idx)
g_vm_table->set_callback(readq_idxs[idx], callback);
// install hooks on READDW virtual machine handlers...
for (auto idx = 0u; idx < sizeof readdw_idxs; ++idx)
g_vm_table->set_callback(readdw_idxs[idx], callback);
// install hooks on READB virtual machine handlers...
for (auto idx = 0u; idx < sizeof readb_idxs; ++idx)
g_vm_table->set_callback(readb_idxs[idx], callback);
//
// hooks all vm handlers and starts callbacks...
//
g_vmhook->start();
// hook on sha1...
make_inline_hook(&__sha1_hook,
reinterpret_cast<void*>(
image_base + EAC_SHA1_OFFSET), &hook_sha1, true);
}
}
/*++
Routine Description:
This is the entry routine for the vmhook-eac driver.
Arguments:
drv_object - Pointer to driver object created by the system.
reg_path - Receives the full registry path to the SERVICES
node of the current control set.
Return Value:
An NTSTATUS code.
--*/
extern "C"
NTSTATUS
DriverEntry( // entry called from ZwSwapCert...
PDRIVER_OBJECT drv_object,
PUNICODE_STRING reg_path
)
{
UNREFERENCED_PARAMETER(drv_object);
UNREFERENCED_PARAMETER(reg_path);
//
// This kernel driver cannot be unloaded so there is no unload routine...
// This is because ZwSwapCert will cause the system to crash...
//
DBG_PRINT("> Registering ImageLoad Callbacks...\n");
return PsSetLoadImageNotifyRoutine(&image_loaded);
}