Skip to content

Instantly share code, notes, and snippets.

@typcn
Created October 6, 2018 07:05
Show Gist options
  • Save typcn/6f433f344a277bb885a7dfa520f45449 to your computer and use it in GitHub Desktop.
Save typcn/6f433f344a277bb885a7dfa520f45449 to your computer and use it in GitHub Desktop.
A sample code to hide process window with HyperPlatform. https://github.com/tandasat/HyperPlatform
// Copyright (c) 2015-2016, tandasat. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
/// @file
/// Implements DdiMon functions.
#include "ddi_mon.h"
#include <ntimage.h>
#define NTSTRSAFE_NO_CB_FUNCTIONS
#include <ntstrsafe.h>
#include "../HyperPlatform/HyperPlatform/common.h"
#include "../HyperPlatform/HyperPlatform/log.h"
#include "../HyperPlatform/HyperPlatform/util.h"
#include "../HyperPlatform/HyperPlatform/ept.h"
#undef _HAS_EXCEPTIONS
#define _HAS_EXCEPTIONS 0
#include <array>
#include "shadow_hook.h"
#include <windef.h>
#include <intrin.h>
////////////////////////////////////////////////////////////////////////////////
//
// macro utilities
//
////////////////////////////////////////////////////////////////////////////////
//
// constants and macros
//
////////////////////////////////////////////////////////////////////////////////
//
// types
//
// A helper type for parsing a PoolTag value
union PoolTag {
ULONG value;
UCHAR chars[4];
};
// A callback type for EnumExportedSymbols()
using EnumExportedSymbolsCallbackType = bool (*)(
ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory,
ULONG_PTR directory_base, ULONG_PTR directory_end, void* context);
// For SystemProcessInformation
enum SystemInformationClass {
kSystemProcessInformation = 5,
};
// For NtQuerySystemInformation
struct SystemProcessInformation {
ULONG next_entry_offset;
ULONG number_of_threads;
LARGE_INTEGER working_set_private_size;
ULONG hard_fault_count;
ULONG number_of_threads_high_watermark;
ULONG64 cycle_time;
LARGE_INTEGER create_time;
LARGE_INTEGER user_time;
LARGE_INTEGER kernel_time;
UNICODE_STRING image_name;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
};
////////////////////////////////////////////////////////////////////////////////
//
// prototypes
//
_IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C
static void DdimonpFreeAllocatedTrampolineRegions();
_IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C static NTSTATUS
DdimonpEnumExportedSymbols(_In_ ULONG_PTR base_address,
_In_ EnumExportedSymbolsCallbackType callback,
_In_opt_ void* context);
_IRQL_requires_max_(PASSIVE_LEVEL) EXTERN_C
static bool DdimonpEnumExportedSymbolsCallback(
_In_ ULONG index, _In_ ULONG_PTR base_address,
_In_ PIMAGE_EXPORT_DIRECTORY directory, _In_ ULONG_PTR directory_base,
_In_ ULONG_PTR directory_end, _In_opt_ void* context);
static std::array<char, 5> DdimonpTagToString(_In_ ULONG tag_value);
template <typename T>
static T DdimonpFindOrignal(_In_ T handler);
static NTSTATUS DdimonpHandleNtQuerySystemInformation(
_In_ SystemInformationClass SystemInformationClass,
_Inout_ PVOID SystemInformation, _In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength);
static NTSTATUS NTAPI DdimonpHandleNtUserBuildHwndList(
IN HDESK hdesk,
IN HWND hwndNext,
IN ULONG fEnumChildren,
IN DWORD idThread,
IN UINT cHwndMax,
IN UINT WTF,
OUT HWND *phwndFirst,
OUT ULONG *pcHwndNeeded);
static HWND APIENTRY DdimonpHandleNtUserFindWindowEx(HWND hwndParent, HWND hwndChildAfter, PUNICODE_STRING ucClassName, PUNICODE_STRING ucWindowName, DWORD dwUnknown);
static ULONG64 DdimonpHandleNtUserQueryWindow(IN ULONG64 WindowHandle, IN ULONG64 TypeInformation);
static HWND APIENTRY DdimonpHandleNtUserGetForegroundWindow(VOID);
static BOOLEAN APIENTRY DdimonpHandleMmIsAddressValid(IN ULONG64 VirtualAddress);
#if defined(ALLOC_PRAGMA)
#pragma alloc_text(INIT, DdimonInitialization)
#pragma alloc_text(INIT, DdimonpEnumExportedSymbols)
#pragma alloc_text(INIT, DdimonpEnumExportedSymbolsCallback)
#pragma alloc_text(PAGE, DdimonTermination)
#pragma alloc_text(PAGE, DdimonpFreeAllocatedTrampolineRegions)
#endif
////////////////////////////////////////////////////////////////////////////////
//
// variables
//
// Defines where to install shadow hooks and their handlers
//
// Because of simplified implementation of DdiMon, DdiMon is unable to handle any
// of following exports properly:
// - already unmapped exports (eg, ones on the INIT section) because it no
// longer exists on memory
// - exported data because setting 0xcc does not make any sense in this case
// - functions does not comply x64 calling conventions, for example Zw*
// functions. Because contents of stack do not hold expected values leading
// handlers to failure of parameter analysis that may result in bug check.
//
// Also the following care should be taken:
// - Function parameters may be an user-address space pointer and not
// trusted. Even a kernel-address space pointer should not be trusted for
// production level security. Verity and capture all contents from user
// supplied address to VMM, then use them.
static ShadowHookTarget g_ddimonp_hook_targets[] = {
{
RTL_CONSTANT_STRING(L"NTUSERQUERYWINDOW"),
DdimonpHandleNtUserQueryWindow, nullptr,
},
{
RTL_CONSTANT_STRING(L"NTUSERBUILDHWNDLIST"),
DdimonpHandleNtUserBuildHwndList, nullptr,
},
{
RTL_CONSTANT_STRING(L"NTUSERGETFOREGROUNDWINDOW"),
DdimonpHandleNtUserGetForegroundWindow, nullptr,
},
{
RTL_CONSTANT_STRING(L"NTUSERFINDWINDOWEX"),
DdimonpHandleNtUserFindWindowEx, nullptr,
},
{
RTL_CONSTANT_STRING(L"NTQUERYSYSTEMINFORMATION"),
DdimonpHandleNtQuerySystemInformation, nullptr,
},
{
RTL_CONSTANT_STRING(L"MMISADDRESSVALID"),
DdimonpHandleMmIsAddressValid, nullptr,
}
};
////////////////////////////////////////////////////////////////////////////////
//
// implementations
//
ULONG64 MiIsAddrValid;
extern "C" NTKERNELAPI ULONG NTAPI PsGetProcessSessionId(PEPROCESS Process);
// Initializes DdiMon
_Use_decl_annotations_ EXTERN_C NTSTATUS
DdimonInitialization(SharedShadowHookData* shared_sh_data) {
// Get a base address of ntoskrnl
auto nt_base = UtilPcToFileHeader(KdDebuggerEnabled);
if (!nt_base) {
return STATUS_UNSUCCESSFUL;
}
ULONG64 MmisAddrValid = (ULONG64)UtilGetSystemProcAddress(L"MmIsAddressValid");
// E9 XX XX XX XX
INT JmpAddr = *(INT *)(MmisAddrValid+1);
MiIsAddrValid = MmisAddrValid + JmpAddr + 5;
HYPERPLATFORM_LOG_INFO("MiIsAddressValid: Base %llx, Jmp: %d , Target: %llx",MmisAddrValid,JmpAddr,MiIsAddrValid);
// Install hooks by enumerating exports of ntoskrnl, but not activate them yet
auto status = DdimonpEnumExportedSymbols(reinterpret_cast<ULONG_PTR>(nt_base),
DdimonpEnumExportedSymbolsCallback,
shared_sh_data);
PVOID win32kbase = UtilGetWin32KAddress();
HYPERPLATFORM_LOG_INFO("Win32KBase: %llx",win32kbase);
const auto current_cr3 = __readcr3();
HYPERPLATFORM_LOG_INFO("KCR3: %llx", current_cr3);
ULONG64 pid = 5;
PEPROCESS p;
while (true)
{
NTSTATUS s = PsLookupProcessByProcessId((HANDLE)pid, &p);
if (s != STATUS_SUCCESS) {
pid++;
continue;
}
BOOL bProcessInSession = PsGetProcessSessionId(p);
if (bProcessInSession)
{
break;
}
else {
ObfDereferenceObject(p);
pid++;
}
}
KeAttachProcess(p);
const auto current_cr23 = __readcr3();
HYPERPLATFORM_LOG_INFO("UCR3: %llx", current_cr23);
status = DdimonpEnumExportedSymbols(reinterpret_cast<ULONG_PTR>(win32kbase),
DdimonpEnumExportedSymbolsCallback,
shared_sh_data);
KeDetachProcess();
HYPERPLATFORM_LOG_INFO("Got All functions");
if (!NT_SUCCESS(status)) {
return status;
}
// Activate installed hooks
HYPERPLATFORM_LOG_INFO("Ready to enable hooks now");
status = ShEnableHooks();
if (!NT_SUCCESS(status)) {
DdimonpFreeAllocatedTrampolineRegions();
return status;
}
HYPERPLATFORM_LOG_INFO("VtHook has been initialized.");
return status;
}
// Terminates DdiMon
_Use_decl_annotations_ EXTERN_C void DdimonTermination() {
PAGED_CODE();
ShDisableHooks();
UtilSleep(1000);
DdimonpFreeAllocatedTrampolineRegions();
HYPERPLATFORM_LOG_INFO("VtHook has been terminated.");
}
// Frees trampoline code allocated and stored in g_ddimonp_hook_targets by
// DdimonpEnumExportedSymbolsCallback()
_Use_decl_annotations_ EXTERN_C static void
DdimonpFreeAllocatedTrampolineRegions() {
PAGED_CODE();
for (auto& target : g_ddimonp_hook_targets) {
if (target.original_call) {
ExFreePoolWithTag(target.original_call, kHyperPlatformCommonPoolTag);
target.original_call = nullptr;
}
}
}
// Enumerates all exports in a module specified by base_address.
_Use_decl_annotations_ EXTERN_C static NTSTATUS DdimonpEnumExportedSymbols(
ULONG_PTR base_address, EnumExportedSymbolsCallbackType callback,
void* context) {
PAGED_CODE();
auto dos = reinterpret_cast<PIMAGE_DOS_HEADER>(base_address);
auto nt = reinterpret_cast<PIMAGE_NT_HEADERS>(base_address + dos->e_lfanew);
auto dir = reinterpret_cast<PIMAGE_DATA_DIRECTORY>(
&nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
if (!dir->Size || !dir->VirtualAddress) {
return STATUS_SUCCESS;
}
auto dir_base = base_address + dir->VirtualAddress;
auto dir_end = base_address + dir->VirtualAddress + dir->Size - 1;
auto exp_dir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(base_address +
dir->VirtualAddress);
for (auto i = 0ul; i < exp_dir->NumberOfNames; i++) {
if (!callback(i, base_address, exp_dir, dir_base, dir_end, context)) {
return STATUS_SUCCESS;
}
}
return STATUS_SUCCESS;
}
// Checks if the export is listed as a hook target, and if so install a hook.
_Use_decl_annotations_ EXTERN_C static bool DdimonpEnumExportedSymbolsCallback(
ULONG index, ULONG_PTR base_address, PIMAGE_EXPORT_DIRECTORY directory,
ULONG_PTR directory_base, ULONG_PTR directory_end, void* context) {
PAGED_CODE();
if (!context) {
return false;
}
auto functions =
reinterpret_cast<ULONG*>(base_address + directory->AddressOfFunctions);
auto ordinals = reinterpret_cast<USHORT*>(base_address +
directory->AddressOfNameOrdinals);
auto names =
reinterpret_cast<ULONG*>(base_address + directory->AddressOfNames);
auto ord = ordinals[index];
auto export_address = base_address + functions[ord];
auto export_name = reinterpret_cast<const char*>(base_address + names[index]);
// Check if an export is forwarded one? If so, ignore it.
if (UtilIsInBounds(export_address, directory_base, directory_end)) {
return true;
}
// convert the name to UNICODE_STRING
wchar_t name[100];
auto status =
RtlStringCchPrintfW(name, RTL_NUMBER_OF(name), L"%S", export_name);
if (!NT_SUCCESS(status)) {
return true;
}
UNICODE_STRING name_u = {};
RtlInitUnicodeString(&name_u, name);
for (auto& target : g_ddimonp_hook_targets) {
// Is this export listed as a target
if (!FsRtlIsNameInExpression(&target.target_name, &name_u, TRUE, nullptr)) {
continue;
}
// Yes, install a hook to the export
if (!ShInstallHook(reinterpret_cast<SharedShadowHookData*>(context),
reinterpret_cast<void*>(export_address), &target)) {
// This is an error which should not happen
DdimonpFreeAllocatedTrampolineRegions();
return false;
}
HYPERPLATFORM_LOG_INFO("Hook has been installed at %p %s.", export_address,
export_name);
}
return true;
}
// Finds a handler to call an original function
template <typename T>
static T DdimonpFindOrignal(T handler) {
for (const auto& target : g_ddimonp_hook_targets) {
if (target.handler == handler) {
NT_ASSERT(target.original_call);
return reinterpret_cast<T>(target.original_call);
}
}
NT_ASSERT(false);
return nullptr;
}
HANDLE gProtectedProcessId = 0;
ULONG64 DdimonpHandleNtUserQueryWindow(IN ULONG64 WindowHandle, IN ULONG64 TypeInformation)
{
const auto original = DdimonpFindOrignal(DdimonpHandleNtUserQueryWindow);
if (gProtectedProcessId && (PsGetCurrentProcessId() != gProtectedProcessId))
{
if (original(WindowHandle, 0) == (ULONG64)gProtectedProcessId)
return 0;
}
return original(WindowHandle, TypeInformation);
}
HWND gLastForegroundWindow = NULL;
HWND APIENTRY DdimonpHandleNtUserGetForegroundWindow(VOID)
{
const auto original = DdimonpFindOrignal(DdimonpHandleNtUserGetForegroundWindow);
const auto OldNtUserQueryWindow = DdimonpFindOrignal(DdimonpHandleNtUserQueryWindow);
HWND result = original();
if ((PsGetCurrentProcessId() != gProtectedProcessId))
{
ULONG64 ProcessID;
ProcessID = OldNtUserQueryWindow((ULONG64)result, 0);
if (ProcessID == (ULONG64)gProtectedProcessId)
result = gLastForegroundWindow;
else
gLastForegroundWindow = result;
}
return result;
}
extern "C" PDRIVER_OBJECT pDrvObj;
typedef BOOLEAN(*tMiIsAddressValid)(PVOID);
PEPROCESS gProtectedPEP;
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
ULONG CheckSum;
ULONG TimeDateStamp;
PVOID LoadedImports;
struct _ACTIVATION_CONTEXT * EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY ForwarderLinks;
LIST_ENTRY ServiceTagLinks;
LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
BOOLEAN APIENTRY DdimonpHandleMmIsAddressValid(IN ULONG64 VirtualAddress) {
if (pDrvObj) {
ULONG64 StartAddress = (ULONG64)pDrvObj;
ULONG64 EndAddress = StartAddress + sizeof(DRIVER_OBJECT);
if (VirtualAddress >= StartAddress && VirtualAddress <= EndAddress) {
return FALSE;
}
StartAddress = (ULONG64)pDrvObj->DriverSection;
EndAddress = StartAddress + sizeof(LDR_DATA_TABLE_ENTRY);
if (VirtualAddress >= StartAddress && VirtualAddress <= EndAddress) {
return FALSE;
}
StartAddress = (ULONG64)pDrvObj->DriverStart;
EndAddress = StartAddress + pDrvObj->DriverSize;
if (VirtualAddress >= StartAddress && VirtualAddress <= EndAddress) {
return FALSE;
}
if (gProtectedPEP) {
StartAddress = (ULONG64)gProtectedPEP;
EndAddress = StartAddress + 40;
if (VirtualAddress >= StartAddress && VirtualAddress <= EndAddress) {
return FALSE;
}
}
}
tMiIsAddressValid func = (tMiIsAddressValid)MiIsAddrValid;
return func((PVOID)VirtualAddress);
}
HWND APIENTRY DdimonpHandleNtUserFindWindowEx(HWND hwndParent, HWND hwndChildAfter, PUNICODE_STRING ucClassName, PUNICODE_STRING ucWindowName, DWORD dwUnknown)
{
const auto original = DdimonpFindOrignal(DdimonpHandleNtUserFindWindowEx);
const auto OldNtUserQueryWindow = DdimonpFindOrignal(DdimonpHandleNtUserQueryWindow);
HWND result;
result = original(hwndParent, hwndChildAfter, ucClassName, ucWindowName, dwUnknown);
if ((PsGetCurrentProcessId() != gProtectedProcessId))
{
ULONG64 ProcessID;
ProcessID = OldNtUserQueryWindow((ULONG64)result, 0);
if (ProcessID == (ULONG64)gProtectedProcessId)
return (HWND)0;
}
return result;
}
NTSTATUS NTAPI DdimonpHandleNtUserBuildHwndList(
IN HDESK hdesk,
IN HWND hwndNext,
IN ULONG fEnumChildren,
IN DWORD idThread,
IN UINT cHwndMax,
IN UINT WTF,
OUT HWND *phwndFirst,
OUT ULONG *pcHwndNeeded) {
const auto original = DdimonpFindOrignal(DdimonpHandleNtUserBuildHwndList);
const auto OldNtUserQueryWindow = DdimonpFindOrignal(DdimonpHandleNtUserQueryWindow);
ULONG64 ProcessID;
if (fEnumChildren == 1 && gProtectedProcessId && PsGetCurrentProcessId() != gProtectedProcessId)
{
ProcessID = OldNtUserQueryWindow((ULONG64)hwndNext, 0);
if (ProcessID == (ULONG64)gProtectedProcessId)
return STATUS_UNSUCCESSFUL;
}
NTSTATUS result;
result = original(hdesk, hwndNext, fEnumChildren, idThread, cHwndMax, WTF , phwndFirst, pcHwndNeeded);
if (!NT_SUCCESS(result) || !gProtectedProcessId || PsGetCurrentProcessId() == gProtectedProcessId) {
return result;
}
ULONG64 i = 0;
ULONG64 j;
while (i<*pcHwndNeeded)
{
ProcessID = OldNtUserQueryWindow((ULONG64)phwndFirst[i], 0);
if (ProcessID == (ULONG64)gProtectedProcessId)
{
for (j = i; j<(*pcHwndNeeded) - 1; j++)
phwndFirst[j] = phwndFirst[j + 1]; //shift all handles after this one place
phwndFirst[*pcHwndNeeded - 1] = 0; //just make it empty
(*pcHwndNeeded)--; //return less
continue; //continue the loop and check the current i
}
i++;
}
return result;
}
_Use_decl_annotations_ static NTSTATUS DdimonpHandleNtQuerySystemInformation(
SystemInformationClass system_information_class, PVOID system_information,
ULONG system_information_length, PULONG return_length) {
const auto original =
DdimonpFindOrignal(DdimonpHandleNtQuerySystemInformation);
const auto result = original(system_information_class, system_information,
system_information_length, return_length);
if (!NT_SUCCESS(result)) {
return result;
}
if (system_information_class != kSystemProcessInformation) {
return result;
}
auto next = reinterpret_cast<SystemProcessInformation*>(system_information);
while (next->next_entry_offset) {
auto curr = next;
next = reinterpret_cast<SystemProcessInformation*>(
reinterpret_cast<UCHAR*>(curr) + curr->next_entry_offset);
if (_wcsnicmp(next->image_name.Buffer, L"notepad.exe", 7) == 0) {
if (!gProtectedProcessId) {
gProtectedProcessId = next->UniqueProcessId;
PsLookupProcessByProcessId(gProtectedProcessId, &gProtectedPEP);
}
if (next->next_entry_offset) {
curr->next_entry_offset += next->next_entry_offset;
} else {
curr->next_entry_offset = 0;
}
next = curr;
}
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment