├── hook.c ├── hook.h ├── AsmCode.c ├── AsmCode.h ├── LICENSE ├── logging.h └── README.MD /hook.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallzhong/KernelHook/HEAD/hook.c -------------------------------------------------------------------------------- /hook.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallzhong/KernelHook/HEAD/hook.h -------------------------------------------------------------------------------- /AsmCode.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallzhong/KernelHook/HEAD/AsmCode.c -------------------------------------------------------------------------------- /AsmCode.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallzhong/KernelHook/HEAD/AsmCode.h -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 smallzhong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /logging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | //#ifdef _DEBUG 5 | //#define LOG(fmt, ...) DbgPrintEx(77, 0, \ 6 | // "[smallzhong][%s():%u] " fmt "\n", __FUNCTION__, __LINE__, ## __VA_ARGS__) 7 | //#else 8 | //#define LOG(...) 9 | //#pragma warning(disable: 4101 4189) // unreferenced local variable 10 | //#endif 11 | 12 | 13 | #define LOG_LEVEL_FATAL 1 14 | #define LOG_LEVEL_ERROR 2 15 | #define LOG_LEVEL_WARNING 3 16 | #define LOG_LEVEL_INFO 4 17 | #define LOG_LEVEL_DEBUG 5 18 | #define LOG_LEVEL_TRACE 6 19 | 20 | #define CURRENT_LOG_LEVEL LOG_LEVEL_INFO 21 | 22 | #if CURRENT_LOG_LEVEL >= LOG_LEVEL_FATAL 23 | #define LOG_FATAL(fmt, ...) DbgPrintEx(77, 0, "[smallzhong][%s():%u] " fmt , __FUNCTION__, __LINE__, ## __VA_ARGS__) 24 | #define LOG_FATAL_NOPREFIX(fmt, ...) DbgPrintEx(77, 0, fmt, ## __VA_ARGS__) 25 | #else 26 | #define LOG_FATAL(fmt, ...) 27 | #define LOG_FATAL_NOPREFIX(fmt, ...) 28 | #endif 29 | 30 | #if CURRENT_LOG_LEVEL >= LOG_LEVEL_ERROR 31 | #define LOG_ERROR(fmt, ...) DbgPrintEx(77, 0, "[smallzhong][%s():%u] " fmt , __FUNCTION__, __LINE__, ## __VA_ARGS__) 32 | #define LOG_ERROR_NOPREFIX(fmt, ...) DbgPrintEx(77, 0, fmt, ## __VA_ARGS__) 33 | #else 34 | #define LOG_ERROR(fmt, ...) 35 | #define LOG_ERROR_NOPREFIX(fmt, ...) 36 | #endif 37 | 38 | #if CURRENT_LOG_LEVEL >= LOG_LEVEL_WARNING 39 | #define LOG_WARN(fmt, ...) DbgPrintEx(77, 0, "[smallzhong][%s():%u] " fmt , __FUNCTION__, __LINE__, ## __VA_ARGS__) 40 | #define LOG_WARN_NOPREFIX(fmt, ...) DbgPrintEx(77, 0, fmt, ## __VA_ARGS__) 41 | #else 42 | #define LOG_WARN(fmt, ...) 43 | #define LOG_WARN_NOPREFIX(fmt, ...) 44 | #endif 45 | 46 | #if CURRENT_LOG_LEVEL >= LOG_LEVEL_INFO 47 | #define LOG_INFO(fmt, ...) DbgPrintEx(77, 0, "[smallzhong][%s():%u] " fmt , __FUNCTION__, __LINE__, ## __VA_ARGS__) 48 | #define LOG_INFO_NOPREFIX(fmt, ...) DbgPrintEx(77, 0, fmt, ## __VA_ARGS__) 49 | #else 50 | #define LOG_INFO(fmt, ...) 51 | #define LOG_INFO_NOPREFIX(fmt, ...) 52 | #endif 53 | 54 | #if CURRENT_LOG_LEVEL >= LOG_LEVEL_DEBUG 55 | #define LOG_DEBUG(fmt, ...) DbgPrintEx(77, 0, "[smallzhong][%s():%u] " fmt , __FUNCTION__, __LINE__, ## __VA_ARGS__) 56 | #define LOG_DEBUG_NOPREFIX(fmt, ...) DbgPrintEx(77, 0, fmt, ## __VA_ARGS__) 57 | #else 58 | #define LOG_DEBUG(fmt, ...) 59 | #define LOG_DEBUG_NOPREFIX(fmt, ...) 60 | #endif 61 | 62 | #if CURRENT_LOG_LEVEL >= LOG_LEVEL_TRACE 63 | #define LOG_TRACE(fmt, ...) DbgPrintEx(77, 0, "[smallzhong][%s():%u] " fmt , __FUNCTION__, __LINE__, ## __VA_ARGS__) 64 | #define LOG_TRACE_NOPREFIX(fmt, ...) DbgPrintEx(77, 0, fmt, ## __VA_ARGS__) 65 | #else 66 | #define LOG_TRACE(fmt, ...) 67 | #define LOG_TRACE_NOPREFIX(fmt, ...) 68 | #endif 69 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # KernelHook 2 | 3 | + 这是一个用来在windows内核使用的inlinehook框架。本框架代码还在开发中,并不太完善,请谨慎使用。. 4 | + 本仓库为基础hook框架。如果需要开箱即用的对系统所有导出函数进行hook分析的实战代码,请移步 [https://github.com/smallzhong/kernel_monitor](https://github.com/smallzhong/kernel_monitor) 。其使用本框架实现了挂钩所有导出函数的功能,可以大大提高对驱动的分析效率。 5 | 6 | ## 使用方法 7 | 8 | + 将 `zydis.h` `zydis.c` `asmcode.h` `asmcode.c` `hook.h` `hook.c` `logging.h` 包含到你的项目中,然后在需要使用到本框架的地方 `#include "hook.h"` 即可使用本框架。 9 | 10 | ## 大致hook流程 11 | 12 | + ![image-20221206161301914](https://cdn.jsdelivr.net/gh/smallzhong/new_new_new_picgo_picbed@main/image-20221206161301914.png) 13 | 14 | 如图,假设未被hook的代码如黄色图块显示。代码顺序为ABCDE,假设ABC三条指令加起来长度大于14字节,可以放下 `ff 25 00 00 00 00 00 00 00 00 00 00 00 00` 这个跳转。本框架会自动识别这三条代码的长度,然后将其替换为一个 `ff25` jmp。其跳到自己申请的一块空间。跳转完成之后首先进行环境的保存,将所有寄存器保存到栈中。然后call一个C语言写的callback函数。可以在这个函数中进行相应的操作。如果这个函数的返回值是 `FALSE` ,则跳转回原函数处进行执行。如果为 `TRUE` ,则直接return,不再执行原函数。如果需要执行原函数,则重新POP所有之前保存的寄存器,然后执行 `A B C` 三条语句,最后通过一个 `ff25` jmp跳到原函数中的下一行处执行(在此示例中是D处)。 15 | 16 | ## set_fast_prehandler使用 17 | 18 | + 在对某些调用非常频繁的函数(如pagefault处理函数)进行hook时,如果使用上面的流程可能会导致非常卡。因为上面的流程在每次调用中都需要PUSH和POP所有的寄存器,并跳转到C语言编写的函数处进行是否处理的判断。因此,本框架提供了设置快速判断的功能。需要自行编写汇编代码进行判断。用法如下 19 | 20 | ```c 21 | UCHAR buf[] = { 22 | 0x48, 0x83, 0xF9, 0x01, // 00007FF806EA094F | | cmp rcx,1 | 23 | 0x74, 0x0E, // 00007FF806EA0953 | | je ntdll.7FF806EA0963 | 24 | 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, //00007FF806EA0955 | | jmp qword ptr ds : [7FF806EA095B] | 25 | 0x00, 0x00, // 00007FF806EA095B | | add byte ptr ds : [rax] ,al | 26 | 0x00, 0x00, // 00007FF806EA095D | | add byte ptr ds : [rax] ,al | 27 | 0x00, 0x00, // 00007FF806EA095F | | add byte ptr ds : [rax] ,al | 28 | 0x00, 0x00, // 00007FF806EA0961 | | add byte ptr ds : [rax] ,al | 29 | 0x90, // 00007FF806EA0963 | | nop 30 | }; 31 | 32 | set_fast_prehandler(number, buf, sizeof buf, 12); 33 | ``` 34 | 35 | 第一个参数是需要设置fast_prehandler的hook的编号。第二个参数是自行编写的prehandler的buffer地址,第三个参数是buffer的大小,第四个参数是buffer中 `ff25` jmp的地址的偏移。自行编写的代码的格式如下 36 | 37 | ```c 38 | // prehandler格式类似如下 39 | // cmp XXX 40 | // jnz 重新运行原来的code,运行原始逻辑,然后跳回到原来位置 ; 对一些参数进行判断 41 | // jmp [eip] ; 一个ff25 jmp,offset填0 42 | // 00 00 43 | // 00 00 44 | // 00 00 45 | // 00 00 46 | // @重新运行原来的code,运行原始逻辑,然后跳回到原来位置 47 | // ; 这后面的原始逻辑由后面的代码自动填入,不用手动写。 48 | ``` 49 | 50 | 如果前面cmp判断不需要处理,那么就跳到 `@重新运行原来的code,运行原始逻辑,然后跳回到原来位置` 。否则通过 `ff25` jmp重新跳到原来的hook函数的地址,重新执行原来的hook_handler。 51 | 52 | ## 对使用了相对地址的汇编代码的处理方法 53 | 54 | ### 7X XX && E0 xx && E1 xx && E2 xx && E3 xx && EB xx一字节短跳 55 | 56 | + ![image-20221206174018926](https://cdn.jsdelivr.net/gh/smallzhong/new_new_new_picgo_picbed@main/image-20221206174018926.png) 57 | 58 | 思路如下。将这个短跳的跳转地址改为 `jmp 到原函数里面的jx目标地址` 代码的地址。最后在执行完 `A B C` 之后通过一个 `EB` 短跳跳到原来的 `JMP D` 处。 59 | 60 | 在开发过程中发现如果给短跳加上REX prefix或者四种legacy prefix中除了F0的其他前缀,也能正常执行,虽然白皮书中说明了不要这样使用,有部分为UB行为。不过本着严谨一点的原则就给这个特殊情况处理了一下。 61 | 62 | ### 0F 8X XX XX XX XX四字节相对跳转 63 | 64 | + 思路同上。 65 | 66 | ### E8 XX XX XX XX 四字节相对call,E9 XX XX XX XX四字节相对跳转 67 | 68 | + 思路同上 69 | 70 | ### 使用了4字节disp的代码 71 | 72 | + 如 73 | 74 | ```asm 75 | 0005 00000000 add byte ptr ds:[7FFA50DA0957],al 76 | ``` 77 | 78 | 这种代码的偏移必须在32位,也就是2GB内。之前的申请内存的方式一定会在2GB外,导致无法跳转。因此如果需要适配这种指令,必须要在当前模块内找一个能用的空白地址来执行这条语句。 79 | 80 | + 这里用了比较复杂的实现。。如果一开始就把代码都放在模块内,那就不需要搞这么麻烦了。之后可以再封装一个函数把所有的hook相关的代码都放在模块里面,不用跳来跳去。 81 | 82 | + 假设原来的代码如下 83 | 84 | ![image-20221215094648776](https://raw.githubusercontent.com/smallzhong/new_new_new_picgo_picbed/main/image-20221215094648776.png) 85 | 86 | 将其改为如下代码 87 | 88 | ![image-20221216154539461](https://cdn.jsdelivr.net/gh/smallzhong/new_new_new_picgo_picbed@main/image-20221216154539461.png) 89 | 90 | 修改之后将disp中的偏移进行相应的调整即可。 91 | 92 | + 在使用了四字节disp的代码中,有一种比较特殊的情况 93 | 94 | ```asm 95 | 67:0005 00000000 add byte ptr ds:[eip],al 96 | ``` 97 | 98 | 带一个67前缀,可以用eip寻址。。。我感觉这种情况应该不会发生吧,不过还是给特判了一下,如果出现这种情况就返回失败。 99 | 100 | ## 实例代码 101 | 102 | + handler函数的形式如下 103 | 104 | ```c 105 | BOOLEAN NtOpenProcess_callback(PGuestContext pcontext) 106 | ``` 107 | 108 | 其中返回值为FALSE表示执行完本函数后继续执行原来的函数。如果为true则不再执行原始的函数,直接返回。 `pcontext` 是一个指向之前保存的寄存器的指针。其结构如下 109 | 110 | ```c 111 | typedef struct _GuestContext 112 | { 113 | ULONG64 mRflags; 114 | ULONG64 mRax; 115 | ULONG64 mRcx; 116 | ULONG64 mRdx; 117 | ULONG64 mRbx; 118 | ULONG64 mRbp; 119 | ULONG64 mRsi; 120 | ULONG64 mRdi; 121 | ULONG64 mR8; 122 | ULONG64 mR9; 123 | ULONG64 mR10; 124 | ULONG64 mR11; 125 | ULONG64 mR12; 126 | ULONG64 mR13; 127 | ULONG64 mR14; 128 | ULONG64 mR15; 129 | ULONG64 mRsp; 130 | }GuestContext, * PGuestContext; 131 | ``` 132 | 133 | 在handler函数中可以通过读取这些寄存器来获取调用的信息,**也可以通过修改这些寄存器达到修改调用方调用原函数时的调用参数的目的。** 134 | 135 | + 对于 `NtOpenProcess` 进行hook的示例代码如下 136 | 137 | ```c 138 | #include 139 | #include 140 | #include 141 | #include "hook.h" 142 | 143 | ULONG64 num = 0; 144 | 145 | VOID DRIVERUNLOAD(_In_ struct _DRIVER_OBJECT* DriverObject) 146 | { 147 | KdPrintEx((77, 0, "unload\r\n")); 148 | reset_hook(num); 149 | // 重置hook之后不要马上卸载驱动,防止有些handler还在跑的时候被卸载了导致蓝屏。 150 | LARGE_INTEGER inTime; 151 | inTime.QuadPart = 1000 * -10000; 152 | KeDelayExecutionThread(KernelMode, FALSE, &inTime); 153 | } 154 | 155 | BOOLEAN NtOpenProcess_callback(PGuestContext pcontext) 156 | { 157 | KdPrintEx((77, 0, "参数为 %llx %llx %llx %llx\r\n", pcontext->mRcx, pcontext->mRdx, pcontext->mR8, pcontext->mR9)); 158 | return FALSE; // RETURN FALSE表示执行完本函数后继续执行原始的ntopenprocess函数。如果return true则不再执行原始的openprocess函数,直接返回。 159 | } 160 | 161 | 162 | VOID hook_NtOpenProcess() 163 | { 164 | UNICODE_STRING unName = { 0 }; 165 | RtlInitUnicodeString(&unName, L"NtOpenProcess"); 166 | PUCHAR funcAddr = MmGetSystemRoutineAddress(&unName); 167 | 168 | hook_by_addr(funcAddr, NtOpenProcess_callback, &num); 169 | } 170 | 171 | 172 | NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) 173 | { 174 | KdPrintEx((77, 0, "entry\r\n")); 175 | hook_NtOpenProcess(); 176 | pDriver->DriverUnload = DRIVERUNLOAD; 177 | return STATUS_SUCCESS; 178 | } 179 | ``` 180 | 181 | 运行结果如下 182 | 183 | ![image-20221206170555861](https://cdn.jsdelivr.net/gh/smallzhong/new_new_new_picgo_picbed@main/image-20221206170555861.png) 184 | 185 | 设置fast_prehandler的代码如下 186 | 187 | ```c 188 | VOID hook_NtOpenProcess() 189 | { 190 | UNICODE_STRING unName = { 0 }; 191 | RtlInitUnicodeString(&unName, L"NtOpenProcess"); 192 | PUCHAR funcAddr = MmGetSystemRoutineAddress(&unName); 193 | 194 | hook_by_addr(funcAddr, NtOpenProcess_callback, &num); 195 | 196 | UCHAR buf[] = { 197 | //0x48, 0x81, 0xFA ,0x00 ,0x10 ,0x00, 0x00, 198 | 0x48, 0x83, 0xF9, 0x01, // 00007FF806EA094F | | cmp rcx,1 | 199 | 0x75, 0x0E, // 00007FF806EA0953 | | jne ntdll.7FF806EA0963 | 200 | 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, //00007FF806EA0955 | | jmp qword ptr ds : [7FF806EA095B] | 201 | 0x00, 0x00, // 00007FF806EA095B | | add byte ptr ds : [rax] ,al | 202 | 0x00, 0x00, // 00007FF806EA095D | | add byte ptr ds : [rax] ,al | 203 | 0x00, 0x00, // 00007FF806EA095F | | add byte ptr ds : [rax] ,al | 204 | 0x00, 0x00, // 00007FF806EA0961 | | add byte ptr ds : [rax] ,al | 205 | 0x90, // 00007FF806EA0963 | | nop 206 | }; 207 | //DbgBreakPoint(); 208 | set_fast_prehandler(num, buf, sizeof buf, 12); 209 | } 210 | ``` 211 | 212 | 213 | 214 | 运行结果如下 215 | 216 | ![image-20221206171227711](https://cdn.jsdelivr.net/gh/smallzhong/new_new_new_picgo_picbed@main/image-20221206171227711.png) 217 | 218 | 这里在fast_prehandler中判断了 `rcx` 是否等于1,如果不等于1就直接走原流程,因此没有打印出任何信息。我们把他改为 `cmp rdx, 0x1000` 再试一下 219 | 220 | ```c 221 | UCHAR buf[] = { 222 | 0x48, 0x81, 0xFA ,0x00 ,0x10 ,0x00, 0x00, // cmp rdx, 0x1000 223 | //0x48, 0x83, 0xF9, 0x01, // 00007FF806EA094F | | cmp rcx,1 | 224 | 0x75, 0x0E, // 00007FF806EA0953 | | jne ntdll.7FF806EA0963 | 225 | 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, //00007FF806EA0955 | | jmp qword ptr ds : [7FF806EA095B] | 226 | 0x00, 0x00, // 00007FF806EA095B | | add byte ptr ds : [rax] ,al | 227 | 0x00, 0x00, // 00007FF806EA095D | | add byte ptr ds : [rax] ,al | 228 | 0x00, 0x00, // 00007FF806EA095F | | add byte ptr ds : [rax] ,al | 229 | 0x00, 0x00, // 00007FF806EA0961 | | add byte ptr ds : [rax] ,al | 230 | 0x90, // 00007FF806EA0963 | | nop 231 | }; 232 | ``` 233 | 234 | 235 | 236 | 可以看到只有 `rdx` 为1000的时候才会进入到C语言编写的handler中。否则快速跳回到原流程中执行。 237 | 238 | ![image-20221206171426253](https://cdn.jsdelivr.net/gh/smallzhong/new_new_new_picgo_picbed@main/image-20221206171426253.png) 239 | 240 | ## TODO 241 | 242 | + 本框架还在开发中,还有很多不完善的地方。由于汇编代码中有很多**使用相对地址的跳转指令**,如果在别的地方运行势必会导致出错。需要对这些指令逐个进行相应的处理。 243 | 244 | - [x] 处理硬编码为 `7X XX 或 E1 xx 或 E2 xx 或 E3 xx 或 EB xx` 的一字节相对短跳 245 | 246 | - [x] 处理硬编码为 `0f 8x xx xx xx xx` 的四字节相对跳转 247 | 248 | - [x] 处理编码为 `e8(e9) xx xx xx xx` 的相对跳转指令 249 | 250 | - [x] 当指令出现了无效的REX前缀时可以进行跳过,防止因无效的REX前缀导致hook失败(这种情况应该非常罕见,编译器不会随意给加上无效的REX前缀) 251 | 252 | - [ ] 使用SEH对函数大小进行判断,如果函数没有足够的空间,则返回失败 253 | 254 | - [ ] 如果函数已经被挂钩,要进行判断,防止运行了无法运行的代码导致抛出UD 255 | 256 | - [ ] 想办法对XMM寄存器也进行相应的保存。 257 | 258 | - [x] 保存环境的时候shellcode忘记加pushfq了,要加上。 259 | 260 | - [x] 有可能出现别的地方跳到被hook的地方中间的情况,比如hook的时候修改了ABC三条指令,后面有代码跳转到了B这条指令的地址。由于已经inlinehook,这个地址上的内容已经不再是原来的代码,会引发不可预测的结果。 261 | 262 | - [x] 释放内存的时候应该sleep一段时间再做。因为有可能还有callback没跑完。或者在callback开头和结尾都要求增减一个count,只有在count为0的时候再释放内存。 263 | 264 | - [x] 想个办法把修改14字节填入ff25的操作做成原子操作。 265 | 266 | - [x] 要想适配disp32的add test这种指令,估计只能是在PE节间隙里面找地方放东西了。可以直接在allocatememory函数里面改。这样的话就能直接改disp32的偏移达到目的了。如果是2GB以上的偏移,好像没有这种类似的指令能做到。如果用另外一个寄存器来达到目的的话又怕改变了寄存器的值导致无法预测的bug。 267 | 268 | - [x] 寻找到节之间的空白区域之后进行记录,下一次快速从上一次的下一个位置放东西。 269 | 270 | - [x] 在清除hook之后对内存进行释放,还要不能蓝屏。 271 | 272 | - [ ] 在清除hook后对占用的空白区域重新写0。 273 | 274 | - [ ] 引入自己的内存分配库,不要每一次都 `ExallocatePool` 申请内存,提高内存利用率 275 | 276 | - [x] 处理其他使用了相对寻址但并不是相对跳转的指令,如 `test,lea,mov` 等。下面是一例 277 | 278 | ``` 279 | .text:0000000140041834 F6 05 CB 2B 53 00 01 test byte ptr cs:PerfGlobalGroupMask+6, 1 280 | ``` 281 | 282 | - [x] 引入DPC和事务,确保Hook和Unhook操作均为”原子操作“。不会出现修改一半其他核心跳过来或者被中断打断的情况。 283 | 284 | - [ ] 这里有一个内存泄漏,加载一次驱动泄漏一个页,问题不是特别大,但是可以修改一下。 285 | 286 | ```cpp 287 | if (!g_hook_record_head) 288 | { 289 | // TODO:这里有一个内存泄漏。但是不严重,一次就4096字节。 290 | g_hook_record_head = allocateMemory(PAGE_SIZE); 291 | InitializeListHead(&g_hook_record_head->entry); 292 | } 293 | ``` 294 | 295 | - [ ] 把链表记录相关代码全部加上锁。 296 | 297 | ## credits 298 | 299 | + 本项目使用了 `https://github.com/oblique/insn_len` 和 `https://github.com/zyantific/zydis` 两个项目作为依赖。 300 | + MDL写功能借鉴了 `https://github.com/HoShiMin/HookLib` 项目中的相关代码。 301 | + 查找空白内存和 DPC call 功能参考了米松哥的 `https://github.com/MiroKaku/DetoursX` 项目。感谢米松哥在我实现本框架过程中给予的指点。 302 | 303 | ## 如何贡献 304 | 305 | + 非常欢迎你的加入![提一个 Issue](https://github.com/smallzhong/kernelhook/issues/new) 或者提交一个 Pull Request。 306 | --------------------------------------------------------------------------------