mirror of
https://github.com/jonny-jhnson/RandomPOCs
synced 2026-06-21 13:54:51 +00:00
Adding WSLHook
This commit is contained in:
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user