{"id":21088081,"url":"https://github.com/programmingclone/kernelhooking","last_synced_at":"2026-02-26T03:11:08.802Z","repository":{"id":262792218,"uuid":"888327017","full_name":"ProgrammingClone/KernelHooking","owner":"ProgrammingClone","description":"Explains with working examples how to implement a trampoline hook for kernel functions inside of Windows.","archived":false,"fork":false,"pushed_at":"2024-11-14T09:24:53.000Z","size":19,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-30T23:43:02.165Z","etag":null,"topics":["detour-hook","detours-example","function-hooking","function-hooks","hooking","hooking-example","kernel","kernel-driver","trampoline-hooking","windows"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ProgrammingClone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-14T07:56:14.000Z","updated_at":"2025-02-03T11:13:31.000Z","dependencies_parsed_at":"2024-11-14T18:15:15.111Z","dependency_job_id":null,"html_url":"https://github.com/ProgrammingClone/KernelHooking","commit_stats":null,"previous_names":["programmingclone/kernelhooking"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammingClone%2FKernelHooking","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammingClone%2FKernelHooking/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammingClone%2FKernelHooking/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ProgrammingClone%2FKernelHooking/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ProgrammingClone","download_url":"https://codeload.github.com/ProgrammingClone/KernelHooking/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251801001,"owners_count":21645968,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["detour-hook","detours-example","function-hooking","function-hooks","hooking","hooking-example","kernel","kernel-driver","trampoline-hooking","windows"],"created_at":"2024-11-19T21:14:30.995Z","updated_at":"2026-02-26T03:11:08.764Z","avatar_url":"https://github.com/ProgrammingClone.png","language":"C","readme":"# Kernel-Function Detour Trampoline Hooking Example\nThis repo was designed to show the basics of how to hook kernel functions from a driver. This specific example uses a trampoline, but I could have done without it but maybe I can showcase that next.\nAnyways this driver is fairly basic all it contains is an IOCTL queue so I can trigger it from a userland application and the actual hooking code that’s it. Once my userland application sends an\nIOCTL request it also includes the processor count we want to spoof aka the core count. Of course, this does not fully spoof the core count since there’s a lot more things that need to happen. Also\nplease note if you run this driver, it will cause a BSOD after a while if you have PatchGuard running. This is because PatchGuard will detect us hooking `NtQuerySystemInformation` so if you want to\nmess around with this driver make sure you use a VM and disable PatchGuard unless you are fine with crashing every 10ish min.\n\n## Overview\nThe general concept is we first find the target function in this case its `NtQuerySystemInformation`. Next, we write an absolute Jump statement to the start of the function that will jump to our own drivers \nfunction which I called `HookedNtQuerySystemInformation`. Next, this `HookedNtQuerySystemInformation` function will then point to an exectuable block of memory `myTrampoline` which contains the first few bytes\nwe overwritten from `NtQuerySystemInformation` followed up by an absolute jump statement back to the original `NtQuerySystemInformation` function. Then once `NtQuerySystemInformation` returns its status our\nhooked function can then modify the output before returning its status. This status then gets reported back to the original caller of `NtQuerySystemInformation`.\n\n### Creating the trampoline\nThe first thing to note here is that in the kernel we dont have to worry about our function having a different address space like you do in userland when you are working with VirtualMemory. However, this also\nmeans the address space is very vast so relative jumps using the `0xE9` assembly instruction will not work becuase that only takes in a 4 byte relative address and our jumps can be larger than that in x64. \nFurthermore, the general idea is to allocate non-pagable executable memory that is page aligned. I also aligned this memory to a multiple of 16 for cache alignement purposes. \n\nAnyways, I first zerod the memory then copied 12 bytes from the start of `NtQuerySystemInformation` into my trampoline. Now please be very carful when doing this because you MUST NOT copy bytes half way through \nan instruction otherwise you will ruin things. That being said I got lucky in this instance and everything lined up which you can see from my debug of `NtQuerySystemInformation` bellow.\n```\n0x40 0x53                     ; push rbx\n0x48 0x83 0xEC 0x30           ; sub rsp, 0x30\n0x45 0x33 0xD2                ; xor r10d, r10d\n0x45 0x8B 0xD8                ; mov r11d, r8d\n0x66 0x44 0x89 0x54 0x24 0x40 ; mov [rsp+0x40], r10w\n```\nOne more thing to note here is that instead of using assembly instructions I had to type the instruction in machine code cause trying to line everything up using assembly would also be annoying. Having said that no\none actually memorizes the hex code for every assembly operation  :rofl:  so I just looked up the conversions to make it easier on myself.\n```\n        ULONGLONG offsetBackToOriginal = (ULONGLONG)((uintptr_t)(PUCHAR)OriginalNtQuerySystemInformation + bytesRemoved);\n\n        RtlZeroMemory(myTrampoline, TRAMPOLINE_SIZE); \n        RtlCopyMemory(myTrampoline, (PVOID)OriginalNtQuerySystemInformation, bytesRemoved); // Copy the first 12 bytes from NtQuerySystemInformation into trampoline\n\n         // MOV REX, offsetBackToOriginal -\u003e \u003cOriginalNtQuerySystemInformation\u003e\n        *((PUCHAR)myTrampoline + bytesRemoved) = 0x48;      \n        *((PUCHAR)myTrampoline + bytesRemoved + 1) = 0xB8;  \n        *((ULONGLONG*)((PUCHAR)myTrampoline + bytesRemoved + 2)) = offsetBackToOriginal; // mov (8 bytes)\n\n        // Indrect JMP to RAX aka `offsetBackToOriginal` \n        *((PUCHAR)myTrampoline + bytesRemoved + 10) = 0xFF;\n        *((PUCHAR)myTrampoline + bytesRemoved + 11) = 0xE0;\n```\nNow that is all done our trampoline will contain the missing `NtQuerySystemInformation` instructions followed by an absolute jump back to `NtQuerySystemInformation`.\n\n### Hooking our target function\nNext up we need to actually hook our target function aka `NtQuerySystemInformation`. To do this we have created a local 12-byte buffer which shows how many bytes it takes to write an absolute jump. This part is fairly simple but \nwe do have to remember that this function will be read only so we need to write to it in a different way.\n```\n        UCHAR jump[12] = { 0 };\n\n        // Step 1: MOV RAX, \u003cHookedNtQuerySystemInformation\u003e\n        jump[0] = 0x48;   \n        jump[1] = 0xB8;   \n        *((ULONGLONG*)(jump + 2)) = (ULONGLONG)HookedNtQuerySystemInformation; \n\n        // Step 2: JMP RAX (indirect jump)\n        jump[10] = 0xFF;  \n        jump[11] = 0xE0;  \n\n        WriteToReadOnlyMemory((PVOID)OriginalNtQuerySystemInformation, jump, sizeof(jump));\n```\nOnce that is done `HookedNtQuerySystemInformation` which is a function we created inside the driver will call `status = ((NtQuerySystemInformation_t)myTrampoline)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);`\nwhich is what makes all of this work. One more thing I would like to add is that these absolute JMP calls will cause code execution to not return unlike normal function CALL statements. This is why we dont get a gross loop of code execution.\n\n## Memory examples\nWhen running this driver on my VM I used WinDbg to network debug the kernel of my VM which is how I got the following memory dumpes of our hooks so you can see how it works from a low level.\n\n### Memory dump of our trampoline\nBellow you can observe that the first 12-bytes match the original 12 bytes of `NtQuerySystemInformation`. From there you an observe the jump clause back to `NtQuerySystemInformation`\n```\nkd\u003e u FFFFB7010DB5B000\nffffb701`0db5b000 4053            push    rbx\nffffb701`0db5b002 4883ec30        sub     rsp,30h\nffffb701`0db5b006 4533d2          xor     r10d,r10d\nffffb701`0db5b009 458bd8          mov     r11d,r8d\nffffb701`0db5b00c 48b8dc98e13804f8ffff mov rax,offset nt!NtQuerySystemInformation+0xc (fffff804`38e198dc)\nffffb701`0db5b016 ffe0            jmp     rax\nffffb701`0db5b018 0000            add     byte ptr [rax],al\nffffb701`0db5b01a 0000            add     byte ptr [rax],al\n```\n\n### Memory dump of NtQuerySystemInformation\nBelow you can observe the `NtQuerySystemInformation` function and you can see the jump statement to our `HookedNtQuerySystemInformation` function at the very start.\n```\nkd\u003e u FFFFF80438E198D0 L20\nnt!NtQuerySystemInformation:\nfffff804`38e198d0 48b8001054380af8ffff mov rax,offset KernelTrampolineHooking!HookedNtQuerySystemInformation (fffff80a`38541000)\nfffff804`38e198da ffe0            jmp     rax\nfffff804`38e198dc 664489542440    mov     word ptr [rsp+40h],r10w\nfffff804`38e198e2 488bda          mov     rbx,rdx\nfffff804`38e198e5 83f94a          cmp     ecx,4Ah\nfffff804`38e198e8 7c24            jl      nt!NtQuerySystemInformation+0x3e (fffff804`38e1990e)\nfffff804`38e198ea 83f953          cmp     ecx,53h\nfffff804`38e198ed 7d1f            jge     nt!NtQuerySystemInformation+0x3e (fffff804`38e1990e)\n```\n\n\n### Memory dump of HookedNtQuerySystemInformation\nLastly, below you can see the memory dump of our custom `HookedNtQuerySystemInformation` function! Now this one has a lot more instructions at the start since I needed to ensure its thread safe but at the very bottom\nyou can observe it does a conditional JNE jump to `myTrampoline`. \n```\nkd\u003e u FFFFF80A38541000 L30\nKernelTrampolineHooking!HookedNtQuerySystemInformation [\\source\\repos\\KernelHooking\\KernelTrampolineHooking\\Hook.c @ 10]:\nfffff80a`38541000 488bc4          mov     rax,rsp\nfffff80a`38541003 48895808        mov     qword ptr [rax+8],rbx\nfffff80a`38541007 48897010        mov     qword ptr [rax+10h],rsi\nfffff80a`3854100b 48897818        mov     qword ptr [rax+18h],rdi\nfffff80a`3854100f 4156            push    r14\nfffff80a`38541011 4883ec40        sub     rsp,40h\nfffff80a`38541015 498bd9          mov     rbx,r9\nfffff80a`38541018 458bf0          mov     r14d,r8d\nfffff80a`3854101b 488bfa          mov     rdi,rdx\nfffff80a`3854101e 8bf1            mov     esi,ecx\nfffff80a`38541020 8360e800        and     dword ptr [rax-18h],0\nfffff80a`38541024 488360d800      and     qword ptr [rax-28h],0\nfffff80a`38541029 4533c9          xor     r9d,r9d\nfffff80a`3854102c 4533c0          xor     r8d,r8d\nfffff80a`3854102f 33d2            xor     edx,edx\nfffff80a`38541031 488d0d08330000  lea     rcx,[KernelTrampolineHooking!HookConfiguredEvent (fffff80a`38544340)]\nfffff80a`38541038 ff15ea1f0000    call    qword ptr [KernelTrampolineHooking!_imp_KeWaitForSingleObject (fffff80a`38543028)]\nfffff80a`3854103e 488b05d3320000  mov     rax,qword ptr [KernelTrampolineHooking!myTrampoline (fffff80a`38544318)]\nfffff80a`38541045 4885c0          test    rax,rax\nfffff80a`3854109c 750c            jne     KernelTrampolineHooking!HookedNtQuerySystemInformation+0xaa (fffff80a`385410aa)\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprogrammingclone%2Fkernelhooking","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprogrammingclone%2Fkernelhooking","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprogrammingclone%2Fkernelhooking/lists"}