Introduction
Have you ever dreamed about Windows kernel hacking? Want to do something restricted by OS? Or go to the deep internals of the Windows kernel and do something directly with hardware, but thought that it is rocket science? Well, let's see, how deep the rabbit hole is!
The Red Pill and Welcome to the Matrix, Neo!
GitHub: https://github.com/HoShiMin/Kernel-Bridge
So, What Is It?
I'm glad to present you the kernel-mode development framework, API and ready-to-use kernel driver and usermode library written in C++17/C++20 to use it for your kernel researching and building your own kernel components.
It supports work with user- and kernel-memory, memory of another processes, IO, MSRs, processes and threads, physical memory, remote code execution, filtering of the file system, loading unsigned modules and drivers and much more.
Well, How to Use It in My Project?
Files hierarchy of the Kernel-Bridge framework:
- /User-Bridge/API - All usermode wrappers, headers and APIs for your usermode project
- /Kernel-Bridge/API - All kernelmode APIs (with no external dependencies) you can use in your own driver
- /SharedTypes/ - Both usermode and kernelmode shared headers
Ok, first of all, you should load this driver. You can do it in 2 ways: load as common driver or load as minifilter. Loading as minifilter allows you to use extended features like file system filtering or usermode Ob***- and Ps***-callbacks.
#include <Windows.h>
#include "WdkTypes.h"
#include "CtlTypes.h"
#include "User-Bridge.h"
int main()
{
using namespace KbLoader;
KbUnload();
BOOL Status = KbLoadAsFilter(
L"X:\\Folder\\Path\\To\\Kernel-Bridge.sys",
L"260000" );
if (!Status)
return 0;
...
KbUnload();
return 0;
}
Ok, What's Next? I Wanna Hack!
Patience, my dear!
Well, let's start with the most common question in the hacking community: how to read/write a process memory!
using namespace Processes::MemoryManagement;
constexpr int Size = 64;
BYTE Buffer[Size] = {};
BOOL Status = KbReadProcessMemory( ProcessId,
0x7FFF0000, &Buffer,
Size
);
Now, one level deeper - reading of the kernel memory:
using namespace VirtualMemory;
constexpr int Size = 64;
BYTE Buffer[Size];
BOOL Status = KbCopyMoveMemory(
reinterpret_cast<WdkTypes::PVOID>(Buffer),
0xFFFFF80000C00000, Size,
FALSE );
What about hardware-specific operations, like an I/O? Yes, we can do it! And more! We can do it in usermode by usermode forwarding using the IOPL bits in the EFlags register that allows us to use in/out/cli/sti instructions in usermode.
#include <intrin.h>
using namespace IO::Iopl;
KbRaiseIopl();
ULONG Frequency = 1000; ULONG Divider = 1193182 / Frequency;
__outbyte(0x43, 0xB6);
__outbyte(0x42, static_cast<unsigned char>(Divider));
__outbyte(0x42, static_cast<unsigned char>(Divider >> 8));
__outbyte(0x61, __inbyte(0x61) | 3);
for (int i = 0; i < 5000; i++);
__outbyte(0x61, __inbyte(0x61) & 252);
KbResetIopl();
Well, I see you amazed! What about Ring0
shells execution in usermode?
using namespace KernelShells;
ULONG Result = 1337;
KbExecuteShellCode(
[](
_GetKernelProcAddress GetKernelProcAddress,
PVOID Argument
) -> ULONG {
using _KeStallExecutionProcessor = VOID(WINAPI*)(ULONG Microseconds);
auto Stall = reinterpret_cast<_KeStallExecutionProcessor>(
GetKernelProcAddress(L"KeStallExecutionProcessor")
);
Stall(1000 * 1000);
ULONG Value = *static_cast<PULONG>(Argument);
return Value == 1337 ? 0x1EE7C0DE : 0;
},
&Result, &Result );
Real Talk! I'm Amazed, but What About More Serious Features?
Hmmm, well... Let's think... about filtering subsystem!
Using the filtering features of the Kernel-Bridge project, you can easily filter the most common part of the file system IO and Ps*** and Ob*** events (by ObRegisterCallbacks
and PsSet***NotifyRoutine
).
It works on the simple scheme:
- The Kernel-Bridge driver registers as filter or sets up an Ob***/Ps*** callbacks
- Usermode app connects to the communication port opened by driver
- At the filtering routine, driver broadcasts events to the connected usermode clients
- Usermode clients doing some filtration (blocks access to the file, descends handle's access rights, etc.)
- Returning back to the kernel with the processed data that applies to the filter request
Let's subscribe to the Ob***- and Ps***-filter:
#include <Windows.h>
#include <fltUser.h>
#include "CommPort.h"
#include "WdkTypes.h"
#include "FltTypes.h"
#include "Flt-Bridge.h"
...
CommPortListener<KB_FLT_OB_CALLBACK_INFO, KbObCallbacks> ObCallbacks;
Status = ObCallbacks.Subscribe([](
CommPort& Port,
MessagePacket<KB_FLT_OB_CALLBACK_INFO>& Message
) -> VOID {
auto Data = static_cast<PKB_FLT_OB_CALLBACK_INFO>(Message.GetData());
if (Data->Target.ProcessId == GetCurrentProcessId()) {
Data->CreateResultAccess &= ~PROCESS_VM_READ;
Data->DuplicateResultAccess &= ~PROCESS_VM_READ;
}
ReplyPacket<KB_FLT_OB_CALLBACK_INFO> Reply(Message, ERROR_SUCCESS, *Data);
Port.Reply(Reply); });
CommPortListener<KB_FLT_PS_IMAGE_INFO, KbPsImage> ProcessCallbacks;
Status = ProcessCallbacks.Subscribe([](
CommPort& Port,
MessagePacket<KB_FLT_PS_IMAGE_INFO>& Message
) -> VOID {
auto Data = static_cast<PKB_FLT_PS_IMAGE_INFO>(Message.GetData());
printf(
"[PID: %i | Base: 0x%I64X | Size: %ull]: %ws\r\n",
(int)Data->ProcessId, Data->BaseAddress, (int)Data->ImageSize, Data->FullImageName
);
});
Good Stuff! but What About Kernel Development?
Yes, it is a big part of the Kernel-Bridge framework.
It has a big set of API for work with a wide-range parts of the Windows kernel such as convenient containers (like string
s or linked lists written on C++17). You can use them in your own kernel projects without any external dependencies.
All of Kernel-Bridge kernel API are locates in /Kernel-Bridge/API/ folder.
Just for example, using the kernel StringsAPI
:
#include <wdm.h>
#include <ntstrsafe.h>
#include <stdarg.h>
#include "StringsAPI.h"
WideString wString = L"Some string";
AnsiString aString = wString.GetAnsi().GetLowerCase() + " and another string!";
if (aString.Matches("*another*"))
DbgPrint("%s\r\n", aString.GetData());
But I Want to Implement My Own Stuff!..
If you want to add your own IOCTL handler:
- Write the handler into the /Kernel-Bridge/Kernel-Bridge/IOCTLHandlers.cpp file
- Add it at the end of Handlers array in the DispatchIOCTL function
- Add the IOCTL number to the Ctls::KbCtlIndices
enum
at the CtlTypes.h to the same position as in the Handlers
array in the IOCTLHandlers.cpp - Call it from usermode in User-Bridge.cpp using the KbSendRequest function
- Enjoy the
Ring0
!
Awesome Work! and Afterwords...
Thank you for reading! This project has a much more features that I describe here and I have big plans for it.
But I need your help. I need contributors, testers and helpers for the deep kernel stuff.
If you are interested in it and Ring0-skilled enough to write a good, self-described and SDV-passing code, you're very welcome to become part of this project.
What is in the plans:
- GUI platform for the runtime kernel researching and scripting engine
- Code injectors
- Instrumentation callbacks support
- PTE-based direct memory manipulation and usermode exposing of the kernel memory
ObRegisterCallbacks
bypass - Wiki-page with documentation
- EV and Microsoft Hardware Center signing and publishing it to everyone
- HLK tests passing, WHQL certification (it would be great!)
So, you're welcome to become a contributor! If you are interested, please leave a note in the comments section below.