深入探索Windows内核:Kernel-Bridge框架

是否曾经认为直接与硬件交互是火箭科学,或者需要深入Windows内核的深层才能实现?现在,让一起来看看这个“兔子洞”到底有多深。

欢迎来到Kernel-Bridge的世界,这是一个内核模式开发框架,提供了API和现成的内核驱动程序以及用户模式库,全部使用C++17/C++20编写,以便用于内核研究和构建自己的内核组件。

Kernel-Bridge支持用户和内核内存操作、其他进程的内存、I/O、MSR、进程和线程、物理内存、远程代码执行、文件系统过滤、加载未签名的模块和驱动程序等等。

如何在项目中使用它?首先,需要加载这个驱动程序。可以通过两种方式加载:作为普通驱动程序加载或作为minifilter加载。作为minifilter加载可以让使用扩展功能,比如文件系统过滤或用户模式Ob***和Ps***回调。

#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" // minifilter的高度 ); if (!Status) return 0; // 无法加载驱动程序! // 成功加载! // 现在可以使用User-Bridge API了!... KbUnload(); return 0; }

接下来,让开始黑客社区最常见的问题:如何读写进程内存!

using namespace Processes::MemoryManagement; constexpr int Size = 64; BYTE Buffer[Size] = {}; BOOL Status = KbReadProcessMemory( // 或者使用KbWriteProcessMemory来写入 ProcessId, 0x7FFF0000, // ProcessId上下文中期望的地址 &Buffer, Size );

再深入一层——读取内核内存:

using namespace VirtualMemory; constexpr int Size = 64; BYTE Buffer[Size]; // 将内核内存读取到Buffer: BOOL Status = KbCopyMoveMemory( reinterpret_cast(Buffer), 0xFFFFF80000C00000, // 任何内核地址 Size, FALSE // 缓冲区不相交 );

关于硬件特定操作,比如I/O?是的,可以做到!而且更多!可以通过用户模式转发在用户模式下使用IOPL位来使用in/out/cli/sti指令。

#include <intrin.h> using namespace IO::Iopl; // 让从用户模式发出系统蜂鸣器的声音! // 所有的__in***/__out***指令都是Ring0的特权! KbRaiseIopl(); // 现在__in***/__out***(比如__cli/__sti)在用户模式下是允许的! ULONG Frequency = 1000; ULONG Divider = 1193182 / Frequency; __outbyte(0x43, 0xB6); // 设置蜂鸣器模式 __outbyte(0x42, static_cast(Divider)); __outbyte(0x42, static_cast(Divider >> 8)); __outbyte(0x61, __inbyte(0x61) | 3); // 启动蜂鸣器! for (int i = 0; i < 5000; i++); __outbyte(0x61, __inbyte(0x61) & 252); // 停止蜂鸣器! KbResetIopl();

好吧,看到很惊讶!那么在用户模式下执行Ring0 shell呢?

using namespace KernelShells; // 调用KeStallExecutionProcessor: 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); // 让CPU暂停1秒 ULONG Value = *static_cast(Argument); return Value == 1337 ? 0x1EE7C0DE : 0; }, &Result, // 参数 &Result // 结果 ); // 返回Result = 0x1EE7C0DE

真正的对话!很惊讶,但是更严肃的功能呢?

嗯,让考虑一下...文件系统过滤子系统!使用Kernel-Bridge项目的过滤功能,可以轻松过滤文件系统IO的最常见的部分和Ps***和Ob***事件(通过ObRegisterCallbacks和PsSet***NotifyRoutine)。

它在简单的方案上工作:Kernel-Bridge驱动程序注册为过滤器或设置Ob***/Ps***回调,用户模式应用程序连接到驱动程序打开的通信端口,在过滤例程中,驱动程序将事件广播给连接的用户模式客户端,用户模式客户端进行一些过滤(阻止访问文件,降低句柄的访问权限等),然后将处理过的数据返回给内核,以应用于过滤请求。

让订阅Ob***和Ps***过滤器:

#include <Windows.h> #include <fltUser.h> #include "CommPort.h" #include "WdkTypes.h" #include "FltTypes.h" #include "Flt-Bridge.h" ... // ObRegisterCallbacks通知器: CommPortListener ObCallbacks; // 防止以PROCESS_VM_READ权限打开进程: Status = ObCallbacks.Subscribe([]( CommPort& Port, MessagePacket& Message ) -> VOID { auto Data = static_cast(Message.GetData()); if (Data->Target.ProcessId == GetCurrentProcessId()) { Data->CreateResultAccess &= ~PROCESS_VM_READ; Data->DuplicateResultAccess &= ~PROCESS_VM_READ; } ReplyPacket Reply(Message, ERROR_SUCCESS, *Data); Port.Reply(Reply); // 回复信息到驱动程序 }); // Ps***回调: CommPortListener ProcessCallbacks; Status = ProcessCallbacks.Subscribe([]( CommPort& Port, MessagePacket& Message ) -> VOID { auto Data = static_cast(Message.GetData()); printf("\n[PID: %i | Base: 0x%I64X | Size: %ull]: %ws\r\n", (int)Data->ProcessId, Data->BaseAddress, (int)Data->ImageSize, Data->FullImageName); });

好东西!但是内核开发呢?

是的,这是Kernel-Bridge框架的一个重要部分。它有大量的API用于处理Windows内核的广泛部分,比如方便的容器(比如在C++17上写的字符串或链表)。可以在自己的内核项目中使用它们,而不需要任何外部依赖。

所有的Kernel-Bridge内核API都位于/Kernel-Bridge/API/文件夹中。例如,使用内核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());

但是想实现自己的东西!如果想添加自己的IOCTL处理程序:

将处理程序写入/Kernel-Bridge/Kernel-Bridge/IOCTLHandlers.cpp文件,添加到DispatchIOCTL函数中的Handlers数组的末尾,将IOCTL编号添加到CtlTypes.h中的Ctls::KbCtlIndices枚举中,与IOCTLHandlers.cpp中的Handlers数组中的相同位置,使用User-Bridge.cpp中的KbSendRequest函数从用户模式调用它。享受Ring0吧!

很棒的工作!之后...

感谢阅读!这个项目有更多的功能,在这里描述的只是冰山一角,对这个项目有很大的计划。但需要帮助。需要贡献者、测试者和内核方面的助手。

如果对这个项目感兴趣,并且有足够的Ring0技能来编写良好、自描述的并且通过SDV检查的代码,非常欢迎成为这个项目的一部分。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485