Adding WSLHook

This commit is contained in:
Jonny_Surface
2026-03-16 11:06:00 -04:00
parent d24a7d95f4
commit 54dba7ae2a
4 changed files with 659 additions and 0 deletions
+1
View File
@@ -15,3 +15,4 @@ This repository holds POCs I have created for projects, blogs, etc.
| SuspendThreadDriver | Client for the vulnerable driver - gmer64.sys. Suspends threads given a ThreadId |
| LDAPPatch | Patches EtwEventWrite so that LDAP calls don't get logged |
| TokenActions | Provides various ways to impersonate tokens and to attempt to change token session ids |
| WSLHook | Leverages RTTI to hook ILxssUserSession::CreateLxProcess method |
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35222.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WSLHook", "WSLHook\WSLHook.vcxproj", "{B3D7E8F9-1A2C-4D5E-9F80-A1B2C3D4E5F6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
Release|ARM64 = Release|ARM64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B3D7E8F9-1A2C-4D5E-9F80-A1B2C3D4E5F6}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B3D7E8F9-1A2C-4D5E-9F80-A1B2C3D4E5F6}.Debug|ARM64.Build.0 = Debug|ARM64
{B3D7E8F9-1A2C-4D5E-9F80-A1B2C3D4E5F6}.Release|ARM64.ActiveCfg = Release|ARM64
{B3D7E8F9-1A2C-4D5E-9F80-A1B2C3D4E5F6}.Release|ARM64.Build.0 = Release|ARM64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+80
View File
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{B3D7E8F9-1A2C-4D5E-9F80-A1B2C3D4E5F6}</ProjectGuid>
<RootNamespace>WSLHook</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
+556
View File
@@ -0,0 +1,556 @@
#include <Windows.h>
#include <objbase.h>
#include <rpcdce.h>
#include <sddl.h>
#include <cstdio>
#include <cstring>
#include <cstdarg>
#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "rpcrt4.lib")
// Types from wslservice.idl
// https://github.com/microsoft/WSL/blob/master/src/windows/wslservice.idl
typedef enum _LxssHandleType
{
LxssHandleConsole = 0,
LxssHandleInput = 1,
LxssHandleOutput = 2,
} LxssHandleType;
typedef struct _LXSS_HANDLE
{
ULONG Handle;
LxssHandleType HandleType;
} LXSS_HANDLE;
typedef struct _LXSS_STD_HANDLES
{
LXSS_HANDLE StdIn;
LXSS_HANDLE StdOut;
LXSS_HANDLE StdErr;
} LXSS_STD_HANDLES, *PLXSS_STD_HANDLES;
typedef struct _LXSS_ERROR_INFO
{
ULONG Flags;
ULONGLONG Context;
LPWSTR Message;
LPWSTR Warnings;
ULONG WarningsPipe;
} LXSS_ERROR_INFO, *PLXSS_ERROR_INFO;
// CreateLxProcess function pointer type (matches vtable slot 16)
typedef HRESULT (STDMETHODCALLTYPE *CreateLxProcess_t)(
IUnknown* This,
const GUID* DistroGuid,
LPCSTR Filename,
ULONG CommandLineCount,
LPCSTR* CommandLine,
LPCWSTR CurrentWorkingDirectory,
LPCWSTR NtPath,
WCHAR* NtEnvironment,
ULONG NtEnvironmentLength,
LPCWSTR Username,
SHORT Columns,
SHORT Rows,
ULONG ConsoleHandle,
PLXSS_STD_HANDLES StdHandles,
ULONG Flags,
GUID* DistributionId,
GUID* InstanceId,
HANDLE* ProcessHandle,
HANDLE* ServerHandle,
HANDLE* StandardIn,
HANDLE* StandardOut,
HANDLE* StandardErr,
HANDLE* CommunicationChannel,
HANDLE* InteropSocket,
LXSS_ERROR_INFO* Error);
// RTTI CompleteObjectLocator — 64-bit/ARM64 layout
struct RttiCompleteObjectLocator
{
DWORD signature; // 1 on x64/ARM64
DWORD offset; // 0 = primary vtable, >0 = secondary (multiple inheritance)
DWORD cdOffset;
INT32 pTypeDescriptor; // RVA to TypeDescriptor
INT32 pClassDescriptor; // RVA to ClassHierarchyDescriptor
INT32 pSelf; // RVA to this COL (for validation)
};
static void Log(const wchar_t* fmt, ...)
{
wchar_t buf[2048];
va_list va;
va_start(va, fmt);
vswprintf_s(buf, fmt, va);
va_end(va);
OutputDebugStringW(buf);
}
//
// LogCaller identifies the COM client via RPC call attributes and token impersonation
//
static void LogCaller()
{
// Get client PID from the RPC runtime
RPC_CALL_ATTRIBUTES_V2_W callAttrs = {};
callAttrs.Version = 2;
callAttrs.Flags = RPC_QUERY_CLIENT_PID;
RPC_STATUS rpcStatus = RpcServerInqCallAttributesW(nullptr, &callAttrs);
if (rpcStatus == RPC_S_OK && callAttrs.ClientPID != 0)
{
DWORD clientPid = HandleToULong(callAttrs.ClientPID);
Log(L"[*] %-18s: %u\n", L"ClientPID", clientPid);
HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, clientPid);
if (hProc)
{
wchar_t imagePath[MAX_PATH] = {};
DWORD sz = MAX_PATH;
if (QueryFullProcessImageNameW(hProc, 0, imagePath, &sz))
{
const wchar_t* name = wcsrchr(imagePath, L'\\');
Log(L"[*] %-18s: %s\n", L"ClientProcess", name ? name + 1 : imagePath);
Log(L"[*] %-18s: %s\n", L"ClientImagePath", imagePath);
}
CloseHandle(hProc);
}
}
// Get caller SID and session via COM impersonation
if (SUCCEEDED(CoImpersonateClient()))
{
HANDLE hToken = nullptr;
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
{
BYTE tokenUserBuf[256] = {};
DWORD needed = 0;
if (GetTokenInformation(hToken, TokenUser, tokenUserBuf, sizeof(tokenUserBuf), &needed))
{
TOKEN_USER* tokenUser = reinterpret_cast<TOKEN_USER*>(tokenUserBuf);
LPWSTR sidStr = nullptr;
if (ConvertSidToStringSidW(tokenUser->User.Sid, &sidStr))
{
Log(L"[*] %-18s: %s\n", L"ClientSID", sidStr);
LocalFree(sidStr);
}
wchar_t userName[256] = {}, domainName[256] = {};
DWORD userLen = 256, domainLen = 256;
SID_NAME_USE sidType;
if (LookupAccountSidW(nullptr, tokenUser->User.Sid, userName, &userLen, domainName, &domainLen, &sidType))
{
Log(L"[*] %-18s: %s\\%s\n", L"ClientUser", domainName, userName);
}
}
DWORD sessionId = 0, needed2 = 0;
if (GetTokenInformation(hToken, TokenSessionId, &sessionId, sizeof(sessionId), &needed2)) {
Log(L"[*] %-18s: %u\n", L"ClientSessionId", sessionId);
}
CloseHandle(hToken);
}
CoRevertToSelf();
}
}
static void** g_pVtableSlot = nullptr;
static CreateLxProcess_t g_pfnOriginal = nullptr;
//
// Logging function
//
static void LogGuid(const wchar_t* label, const GUID* g)
{
if (!g) {
Log(L"[*] %-18s: (null)\n", label);
return;
}
Log(L"[*] %-18s: {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
label, g->Data1, g->Data2, g->Data3,
g->Data4[0], g->Data4[1], g->Data4[2], g->Data4[3],
g->Data4[4], g->Data4[5], g->Data4[6], g->Data4[7]);
}
//
// HookCreateLxProcess is the detour function called instead of CreateLxProcess.
// It logs information and then calls the original CreateLxProcess function
//
HRESULT STDMETHODCALLTYPE HookCreateLxProcess(
IUnknown* This,
const GUID* DistroGuid,
LPCSTR Filename,
ULONG CommandLineCount,
LPCSTR* CommandLine,
LPCWSTR CurrentWorkingDirectory,
LPCWSTR NtPath,
WCHAR* NtEnvironment,
ULONG NtEnvironmentLength,
LPCWSTR Username,
SHORT Columns,
SHORT Rows,
ULONG ConsoleHandle,
PLXSS_STD_HANDLES StdHandles,
ULONG Flags,
GUID* DistributionId,
GUID* InstanceId,
HANDLE* ProcessHandle,
HANDLE* ServerHandle,
HANDLE* StandardIn,
HANDLE* StandardOut,
HANDLE* StandardErr,
HANDLE* CommunicationChannel,
HANDLE* InteropSocket,
LXSS_ERROR_INFO* Error)
{
Log(L"\n[*] ========== CreateLxProcess ==========\n");
LogCaller();
// Log [in] parameters
LogGuid(L"DistroGuid", DistroGuid);
Log(L"[*] %-18s: %S\n", L"Filename", Filename ? Filename : "(null)");
Log(L"[*] %-18s: %u\n", L"CommandLineCount", CommandLineCount);
for (ULONG i = 0; i < CommandLineCount; i++) {
Log(L"[*] Arg[%u] : %S\n",
i, (CommandLine && CommandLine[i]) ? CommandLine[i] : "(null)");
}
Log(L"[*] %-18s: %s\n", L"CWD", CurrentWorkingDirectory ? CurrentWorkingDirectory : L"(null)");
Log(L"[*] %-18s: %s\n", L"NtPath", NtPath ? NtPath : L"(null)");
Log(L"[*] %-18s: %s\n", L"Username", Username ? Username : L"(null)");
Log(L"[*] %-18s: %hdx%hd\n", L"Console Size", Columns, Rows);
Log(L"[*] %-18s: 0x%X\n", L"ConsoleHandle", ConsoleHandle);
Log(L"[*] %-18s: 0x%X\n", L"Flags", Flags);
Log(L"[*] %-18s: %u\n", L"NtEnvLength", NtEnvironmentLength);
if (StdHandles)
{
Log(L"[*] %-18s: Handle=%u Type=%d\n", L"StdIn", StdHandles->StdIn.Handle, StdHandles->StdIn.HandleType);
Log(L"[*] %-18s: Handle=%u Type=%d\n", L"StdOut", StdHandles->StdOut.Handle, StdHandles->StdOut.HandleType);
Log(L"[*] %-18s: Handle=%u Type=%d\n", L"StdErr", StdHandles->StdErr.Handle, StdHandles->StdErr.HandleType);
}
// Call the real implementation
HRESULT hr = g_pfnOriginal(
This, DistroGuid, Filename, CommandLineCount, CommandLine,
CurrentWorkingDirectory, NtPath, NtEnvironment, NtEnvironmentLength,
Username, Columns, Rows, ConsoleHandle, StdHandles, Flags,
DistributionId, InstanceId, ProcessHandle, ServerHandle,
StandardIn, StandardOut, StandardErr, CommunicationChannel,
InteropSocket, Error);
// Log [out] parameters
Log(L"[*] --- out params (hr=0x%08X) ---\n", hr);
LogGuid(L"DistributionId", DistributionId);
LogGuid(L"InstanceId", InstanceId);
Log(L"[*] %-18s: %p\n", L"ProcessHandle", ProcessHandle ? *ProcessHandle : nullptr);
Log(L"[*] %-18s: %p\n", L"ServerHandle", ServerHandle ? *ServerHandle : nullptr);
Log(L"[*] %-18s: %p\n", L"StandardIn", StandardIn ? *StandardIn : nullptr);
Log(L"[*] %-18s: %p\n", L"StandardOut", StandardOut ? *StandardOut : nullptr);
Log(L"[*] %-18s: %p\n", L"StandardErr", StandardErr ? *StandardErr : nullptr);
Log(L"[*] %-18s: %p\n", L"CommunicationChan", CommunicationChannel ? *CommunicationChannel : nullptr);
Log(L"[*] %-18s: %p\n", L"InteropSocket", InteropSocket ? *InteropSocket : nullptr);
if (Error && Error->Message) {
Log(L"[*] %-18s: %s\n", L"Error.Message", Error->Message);
}
if (Error && Error->Warnings) {
Log(L"[*] %-18s: %s\n", L"Error.Warnings", Error->Warnings);
}
Log(L"[*] ==========================================\n\n");
return hr;
}
// ---------------------------------------------------------------------------
// RTTI vtable scanner
//
// Three-level scan: TypeDescriptor name -> CompleteObjectLocator -> vtable pointer
// Looks for COL.offset == 0 (primary vtable = the ILxssUserSession interface).
// ---------------------------------------------------------------------------
struct ScanRegion {
uint8_t* start;
SIZE_T size;
};
//
// GetReadableSections collects non-executable, readable PE sections (.rdata, .data, etc.)
//
static int GetReadableSections(HMODULE hMod, ScanRegion* out, int maxOut)
{
auto base = reinterpret_cast<uint8_t*>(hMod);
auto dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
auto nt = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
auto sec = IMAGE_FIRST_SECTION(nt);
int n = 0;
for (WORD i = 0; i < nt->FileHeader.NumberOfSections && n < maxOut; i++, sec++)
{
DWORD ch = sec->Characteristics;
if (!(ch & IMAGE_SCN_MEM_READ)) {
continue;
}
if (ch & IMAGE_SCN_MEM_EXECUTE) {
continue;
}
// Add to array of readable memory regions
out[n++] = { base + sec->VirtualAddress, sec->Misc.VirtualSize };
}
return n;
}
//
// FindVtableByRTTI finds the vtable address by searching for the TypeDescriptor name -> obtain COL -> finding vtable
//
static void** FindVtableByRTTI(HMODULE hMod, const char* classSubstr)
{
auto base = reinterpret_cast<uint8_t*>(hMod);
auto dos = reinterpret_cast<IMAGE_DOS_HEADER*>(base);
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
return nullptr;
}
auto nt = reinterpret_cast<IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
if (nt->Signature != IMAGE_NT_SIGNATURE) {
return nullptr;
}
ScanRegion regions[32];
// Only care if matches come from a readable section
int nRegions = GetReadableSections(hMod, regions, 32);
if (nRegions == 0) {
return nullptr;
}
// TypeDescriptor: [+0x00] pVFTable, [+0x08] spare, [+0x10] name
static constexpr SIZE_T kNameOffset = 2 * sizeof(void*);
void** primaryHit = nullptr;
// 1. Finding TypeDescriptor by name
for (int r = 0; r < nRegions && !primaryHit; r++)
{
uint8_t* data = regions[r].start;
SIZE_T sz = regions[r].size;
for (SIZE_T i = kNameOffset; i < sz && !primaryHit; i++)
{
const char* p = reinterpret_cast<const char*>(data + i);
if (p[0] != '.' || p[1] != '?' || p[2] != 'A') {
continue;
}
if (!strstr(p, classSubstr)) {
continue;
}
auto tdAddr = data + i - kNameOffset;
auto tdOff = static_cast<uintptr_t>(tdAddr - base);
if (tdOff > UINT32_MAX) {
continue;
}
auto tdOff32 = static_cast<INT32>(tdOff);
// TypeDescriptor found
Log(L"[+] TypeDescriptor \"%S\" @ %p (rva 0x%X)\n", p, tdAddr, tdOff32);
// 2. Finding COL referencing this TypeDescriptor
for (int r2 = 0; r2 < nRegions; r2++)
{
uint8_t* d2 = regions[r2].start;
SIZE_T s2 = regions[r2].size;
for (SIZE_T j = 0; j + sizeof(RttiCompleteObjectLocator) <= s2; j += 4)
{
auto col = reinterpret_cast<RttiCompleteObjectLocator*>(d2 + j);
if (col->signature != 1) {
continue;
}
if (col->pTypeDescriptor != tdOff32) {
continue;
}
auto colOff = static_cast<uintptr_t>((d2 + j) - base);
if (colOff > UINT32_MAX) {
continue;
}
if (col->pSelf != static_cast<INT32>(colOff)) {
continue;
}
// COL address found
void* colAddr = d2 + j;
Log(L"[+] COL @ %p offset=%u\n", colAddr, col->offset);
// 3. Finding pointer to COL (vtable[-1])
for (int r3 = 0; r3 < nRegions; r3++)
{
uint8_t* d3 = regions[r3].start;
SIZE_T s3 = regions[r3].size;
for (SIZE_T k = sizeof(void*); k + sizeof(void*) <= s3; k += sizeof(void*))
{
void** slot = reinterpret_cast<void**>(d3 + k);
if (*slot != colAddr) {
continue;
}
// Found vtable
void** vtable = slot + 1;
Log(L"[+] Vtable @ %p (COL.offset=%u)\n", vtable, col->offset);
// Only care about primary vtable
if (col->offset == 0) {
primaryHit = vtable;
break;
}
}
if (primaryHit) {
break;
}
}
}
}
}
}
if (!primaryHit) {
Log(L"[-] No vtable found for \"%S\"\n", classSubstr);
}
return primaryHit;
}
// ---------------------------------------------------------------------------
// Vtable patching
// ---------------------------------------------------------------------------
//
// PatchVtableEntry hooks CreateLxProcess
//
static bool PatchVtableEntry(void** vtable)
{
HMODULE hMod = nullptr;
GetModuleHandleExW(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCWSTR>(vtable), &hMod);
ULONGLONG base = reinterpret_cast<ULONGLONG>(hMod);
ULONGLONG rva = reinterpret_cast<ULONGLONG>(vtable) - base;
Log(L"[*] Vtable @ 0x%016llX (base=0x%016llX rva=0x%llX)\n",
reinterpret_cast<ULONGLONG>(vtable), base, rva);
// CreateLxProcess is the 16th (0-indexed) slot
g_pVtableSlot = &vtable[16];
g_pfnOriginal = reinterpret_cast<CreateLxProcess_t>(vtable[16]);
// Updating memory protection before setting hook
DWORD oldProtect = 0;
if (!VirtualProtect(g_pVtableSlot, sizeof(void*), PAGE_READWRITE, &oldProtect))
{
Log(L"[-] VirtualProtect failed: %u\n", GetLastError());
return false;
}
*g_pVtableSlot = reinterpret_cast<void*>(&HookCreateLxProcess);
// Restoring memory protection after setting hook
VirtualProtect(g_pVtableSlot, sizeof(void*), oldProtect, &oldProtect);
Log(L"[+] Patched slot %d: original=%p detour=%p\n",
16, g_pfnOriginal, &HookCreateLxProcess);
return true;
}
//
// UnpatchVtableEntry unhooks CreateLxProcess
//
static void UnpatchVtableEntry()
{
if (!g_pVtableSlot || !g_pfnOriginal) {
return;
}
DWORD old = 0;
// Updating memory protection before removing hook
VirtualProtect(g_pVtableSlot, sizeof(void*), PAGE_READWRITE, &old);
*g_pVtableSlot = reinterpret_cast<void*>(g_pfnOriginal);
// Updating memory protection after removing hook
VirtualProtect(g_pVtableSlot, sizeof(void*), old, &old);
Log(L"[*] Vtable restored\n");
}
//
// HookThreadProc is the function hook worker thread that finds the vtable and hooks function
//
static DWORD WINAPI HookThreadProc(LPVOID)
{
Log(L"[*] Hook thread started (PID %u)\n", GetCurrentProcessId());
HMODULE hWsl = GetModuleHandleW(L"wslservice.exe");
if (!hWsl)
{
Log(L"[-] GetModuleHandleW(\"wslservice.exe\") failed.\n");
return 1;
}
Log(L"[*] Scanning RTTI for \"LxssUserSession\"...\n");
void** vtable = FindVtableByRTTI(hWsl, "LxssUserSession");
if (!vtable)
{
Log(L"[-] RTTI scan failed — vtable not found.\n");
return 1;
}
// Hooking Function
if (!PatchVtableEntry(vtable))
{
return 1;
}
Log(L"[*] Function Hook Ready\n");
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hInst, DWORD reason, LPVOID lpvReserved)
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
// Don't care about thread attach/detach
DisableThreadLibraryCalls(hInst);
Log(L"---DLL_PROCESS_ATTACH---\n");
Log(L"[*] DLL loaded into PID %u\n", GetCurrentProcessId());
// Creating hook thread
CloseHandle(
CreateThread(nullptr, 0, HookThreadProc, nullptr, 0, nullptr)
);
break;
case DLL_PROCESS_DETACH:
if (lpvReserved == nullptr)
{
// Unhooking function
UnpatchVtableEntry();
}
break;
}
return TRUE;
}