|
本帖最后由 陌路人 于 2015-4-5 19:33 编辑
首先感谢 Tesla.Angela 的这套教程 才让我学到这么多.
在写这篇帖子之前我也思考了很久,
因为我是新手,所以新的东西搞不出来, 旧的东西把也都是论坛有的
尤其是SSDT 坛主的这个教程已经讲的 明明白白了
但是不发个帖子把,感觉来论坛学了这么多东西 没有一点贡献 也过不去
所以没办法了硬着头皮 把自己学习时的心得 发出来吧 ! 如有不正确的请指教
大牛 勿喷啊...........................................
进入正题: 需要过PG或者 内核越狱 测试环境 W7 x64
下面的代码 是我看了坛主的教程后突发奇想. 实现了一点功能
1, 卸载时 一个函数 恢复所有SSDT表中的偏移HOOK.
2, 函数支持 一次性最多HOOK 10个SSDT中的函数, 只需要传递一个新的 函数地址 跟要HOOK的索引号;
其次 就是SSDT偏移正确计算 ,和64位 驱动加载内核,基地址重定位遇到的一点问题;
一键恢复SSDT的实现就是 先重载内核基地址重定位后 计算出SSDT表中的正确偏移然后存放在 ULONG的0x191最小的数组中
等卸载的时候 只要跟 原始内核中的SSDT 中的偏移 比较 如果 不对就换成我们计算好的 偏移, ( 判断是需要都转换成真实地址在判断)
x64 基地址重定位的时候 有个低 4位的判断 要换成 IMAGE_REL_BASED_DIR64
其次 就是修改写 变量的大小 基本就很32位的重载内核一样的了
还有些问题 都在代码中注释表明的
/*
SSDT的偏移非常有意思.内核初始化的时候ServiceTableBase数组中的值
都是每8个字节存放一个函数的真实地址,然后再经过一些算法
把ServiceTableBase数组中每4个字节存放一个函数偏移的值
(以ServiceTableBase * 8 * 0x191)\2 ==后的地址 可以用windbg查看
dq 后 内存中存放的是函数的绝对地址 函数的名称则是从
SSDT总序列号0x191\2 的函数开始,有兴趣的可以查看下;
然后说下算法,它是以ServiceTableBase地址为例,然后让每个
ULONG A=(绝对函数地址 - ServiceTableBase ==的偏移)+ServiceTableBase的低4字节
存放在一个4字节的变量里. 然后 按照下面的算法后才是正确的地址
(ServiceTableBase * i *4)= (A - ServiceTableBase的低4字节)<<4;
如果ULONG64 B > ULONG64 C , C - B =的偏移 L ;
如果 L 直接+B ==的值 将是一个错误的值.
因为B是 8个字节, 计算出的偏移是4个字节 低4节相加将会溢出到8字节的高4字节
所以地址就会错误.果断的让2个4字节的变量计算出正确的偏移后再写入内存
*/
- #pragma once //只编译一次
- #ifdef __cplusplus
- extern"C"
- {
- #endif
- #include "ssdt.h"
- #ifdef __cplusplus
- }
- #endif
- #define MyDbgPrint //DbgPrint //需要打印信息是 把前面的 {//} 去掉
- #define _max(a,b) a>b?a:b //计算ab值那个大用哪个
- ULONG gu_SSDT[0x300] = { NULL };
- ULONG64 gu64_Raw_SSDT[0x200] = { NULL };
- ULONG COUNT=0;
- BOOLEAN FLAG = FALSE;
- KIRQL WPOFFx64()
- {
- /*
- 提升IRQL等级到Dispatch_Level
- 关闭页面保护,CR0寄存器的值16位清0 0~16
- 硬件中断标志位清0 ,不响应可屏蔽中断
- 实现了,多线程安全性 _disable为硬件中断不响应
- Dispatch_Level为线程不可切换软中断
- */
- KIRQL irql = KeRaiseIrqlToDpcLevel();
- UINT64 cr0 = __readcr0();
- _disable();
- cr0 &= 0xfffffffffffeffff;
- __writecr0(cr0);
- return irql;
- }
- void WPONx64(KIRQL irql)
- {
- /*
- 恢复页面保护. CRO寄存器值16位值1 0~16
- IF中断标志至1 可以响应可屏蔽中断
- 降低软中断级别
- */
- UINT64 cr0 = __readcr0();
- cr0 |= 0x10000;
- __writecr0(cr0);
- _enable();
- KeLowerIrql(irql);
- }
- /*
- 直接 在 DriverEntry 文件中
- extern ULONG64 gu64_Raw_SSDT[];
- 然后声明下函数 后 就可以直接使用了
- HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
- 卸载时 UnhookSSDT();
- */
- /*
- 输入要替换的函数地址,已经SSDT中要HOOK的函数索引;
- 成功 返回真 最多支持 HOOK 10 个函数
- */
- BOOLEAN HookSSDT(ULONG64 NewFunction,ULONG index)
- {
-
- if (FLAG == FALSE)
- {
- BOOLEAN Boo;
- Boo = Relocation();
- if (!Boo)
- {
- MyDbgPrint("HookSSDT()->if (!Boo),");
- return FALSE;
- }
- FLAG = TRUE;
- }
- if (COUNT>=10)
- {
- MyDbgPrint("hookssdt上限,只支持10个");
- return FALSE;
- }
- if (!MmIsAddressValid((PVOID)NewFunction))
- {
- MyDbgPrint("HookSSDT()->输入的函数地址无效");
- return FALSE;
- }
- if (index>0x191)
- {
- MyDbgPrint("要输入的函数索引无效");
- return FALSE;
- }
- /*
- \x48\xB8\ == mov rax
- \xFF\xE0\ == jmp rax
- 0x90==nop
- */
- UCHAR jmp_code[] = "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
- KIRQL irql;
- ULONG64 add,address;
- add = ((ULONG64)KeBugCheckEx + COUNT * 15);
- address = NewFunction;
- RtlCopyMemory(jmp_code + 2, &address, 8);
- irql = WPOFFx64();
- memset((PVOID)add , 0x90, 15);
- RtlCopyMemory((PVOID)add, jmp_code, 12);
- WPONx64(irql);
- MyDbgPrint("add%llx\n", add);
- /*
- 然后计算出这个地址与SSDT中的偏移
- */
- ULONG offset=0;
- offset = FuncOffset(add);
- if (offset==0)
- {
- MyDbgPrint("HookSSDT()->获得函数偏移失败");
- return FALSE;
- }
- PSYSTEM_SERVICE_TABLE ssdt = NULL;
- ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
- if (ssdt == NULL)
- {
- MyDbgPrint("HookSSDT()->if (ssdt==NULL)");
- return 0;
- }
- irql = WPOFFx64();
- *(ULONG *)((ULONG64)ssdt->ServiceTableBase + index * 4) = offset;
- WPONx64(irql);
- COUNT++;
- return TRUE;
- }
- /*
- 根据传入的地址计算相对与SSDT的偏移
- */
- ULONG FuncOffset(ULONG64 FuncAdd)
- {
- PSYSTEM_SERVICE_TABLE ssdt = NULL;
- ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
- if (ssdt == NULL)
- {
- MyDbgPrint("FuncOffset()->if (ssdt==NULL)");
- return 0;
- }
- typedef struct ssdt_recover
- {
- ULONG64 add;
- }shuzu, *pshuzu;
- typedef union asdf
- {
- ULONG a[2];
- shuzu s;
- }myunion, *pmyunion;
- myunion add;
- add.s.add = (ULONG64)ssdt->ServiceTableBase;
- ULONG address;
- address = FuncAdd - add.s.add;
- address += add.a[0];
- address = (address - add.a[0]) << 4;
- return address;
- }
- /*
- 一键恢复SSDT的所有HOOK
- */
- BOOLEAN UnhookSSDT()
- {
- if (FLAG == FALSE)
- {
- BOOLEAN Boo;
- Boo = Relocation();
- if (!Boo)
- {
- MyDbgPrint("UnhookSSDT()->if (!Boo),");
- return FALSE;
- }
- FLAG = TRUE;
- }
- PSYSTEM_SERVICE_TABLE ssdt = NULL;
- ULONG i = 0;
- ULONG judge;//判断
- if (gu_SSDT[0]==NULL)
- {
- MyDbgPrint("UnhookSSDT()->全局数组变量无效");
- return FALSE;
- }
- ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
- if (ssdt==NULL)
- {
- MyDbgPrint("UnhookSSDT()->if (ssdt==NULL)");
- return FALSE;
- }
- KIRQL irql;
- for (i = 0; i < ssdt->NumberOfServices;i++)
- {
- judge = *(ULONG*)((ULONG64)ssdt->ServiceTableBase + i * 4) >> 4;
- if (judge!= gu_SSDT[i]>>4)
- {
- irql=WPOFFx64();
- *(ULONG*)((ULONG64)ssdt->ServiceTableBase + i * 4) = gu_SSDT[i];
- WPONx64(irql);
- MyDbgPrint("被HOOK的SSDT序列号=%d,正确的函数地址%llx:\n", i, GetSSDTFunctionAddress64_2(i));
- }
- }
-
- MyDbgPrint("UnhookSSDT()->SSDT恢复完毕");
- return TRUE;
- }
- /*
- 读取SSDTtable地址
- 返回0 表示失败;否则返回地址KeServiceDescriptorTable
- */
- ULONGLONG MyGetKeServiceDescriptorTable64() //我的方法
- {
- //获取 KiSystemCall64 的地址
- PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
- PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
- PUCHAR i = NULL;
- UCHAR b1 = 0, b2 = 0, b3 = 0;
- ULONG templong = 0;
- ULONGLONG addr = 0;
- for (i = StartSearchAddress; i < EndSearchAddress; i++)
- {
- if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
- {
- b1 = *i;
- b2 = *(i + 1);
- b3 = *(i + 2);
- if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15) //4c8d15 SSDT 的特征码
- {
- memcpy(&templong, i + 3, 4);
- addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
- return addr;
- }
- }
- }
- return 0;
- }
- /*
- 根据序列号返回函数地址;
- */
- ULONGLONG GetSSDTFunctionAddress64_2(ULONGLONG Index)
- {
- LONG dwTemp = 0;
- ULONGLONG qwTemp = 0, stb = 0, ret = 0;
- PSYSTEM_SERVICE_TABLE ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
- /*
- 先获取KeServiceDescriptorTable的地址
- 然后获取数组的地址KiServiceTable 基地址
- 这个数组里边方的是一个函数的偏移地址,
- 数组首地址+对应索引内的地址=实际的函数地址.
- (因为这里并没有执行任何指令所以不需要加上指令长度)
- 值得注意的是,这个偏移要转化成二进制后右移4位才是正确的偏移地址
- */
- stb = (ULONGLONG)(ssdt->ServiceTableBase);
- qwTemp = stb + 4 * Index;
- dwTemp = *(PLONG)qwTemp;
- dwTemp = dwTemp >> 4;
- ret = stb + (LONG64)dwTemp;
- return ret;
- }
- VOID PrintSSDT()
- {
-
- ULONG i;
- ULONG64 j;
- PSYSTEM_SERVICE_TABLE a;
- a = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
- for (i = 0; i < a->NumberOfServices;i++)
- {
- j = GetSSDTFunctionAddress64_2(i);
- DbgPrint("%d:地址=%llx\r\n", i, j);
- }
- return;
- }
- /*
- 获取SSDT正确偏移,成功返回真;
- */
- BOOLEAN Relocation()
- {
- ULONG a = 0;
- char module_path[260] = { NULL }; //内核路径
- ULONG_PTR base=NULL;//内核地址
- PULONG new_module_pointer; //新内核
- base=MyModuleQuery(module_path);
- if (base==NULL)
- {
- MyDbgPrint("Relocation()->获取模块地址失败\n");
- return FALSE;
- }
- new_module_pointer = MyPeLoad(module_path, NULL);
- if (new_module_pointer==0)
- {
- MyDbgPrint("Relocation()->new_module_pointer==0)\n");
- return FALSE;
- }
- MyDbgPrint("新地址%p\n", new_module_pointer);
-
- a = MyRelocationTable(new_module_pointer, (PVOID)base);
- if (a==0)
- {
- MyDbgPrint("Relocation()->基地址重定位数据失败\n");
- ExFreePool(new_module_pointer);
- return FALSE;
- }
- a= MySSDTRelocation((ULONG_PTR)new_module_pointer, base);
- if (a == 0)
- {
- MyDbgPrint("Relocation()->SSDT重定位失败\n");
- ExFreePool(new_module_pointer);
- return FALSE;
- }
- if (gu_SSDT[0]==NULL)
- {
- MyDbgPrint("Relocation()->获取SSDT偏移失败\n");
- ExFreePool(new_module_pointer);
- return FALSE;
- }
- if (gu64_Raw_SSDT[0] == NULL)
- {
- MyDbgPrint("Relocation()->获取SSDT真实地址失败\n");
- ExFreePool(new_module_pointer);
- return FALSE;
- }
- ExFreePool(new_module_pointer);
- return TRUE;
- }
- /*
- 获取内核模块 路径跟地址
- */
- ULONG_PTR MyModuleQuery(OUT char* ModulePath )
- {
- PSYSTEM_MODULE_INFORMATION module = NULL;
- ULONG_PTR Base;
- module = (PSYSTEM_MODULE_INFORMATION)MyZwQuerySystemInformation(11);
- if (module==NULL)
- {
- MyDbgPrint("MyModuleQuery(),获取模块错误\n\r");
- return 0;
- }
- MyDbgPrint("模块路径:%s\n", module->Module->ImageName);
- MyDbgPrint("%s:%p\n", module->Module->ImageName + module->Module->ModuleNameOffset, module->Module->Base);
- memcpy(ModulePath, module->Module->ImageName, strlen(module->Module->ImageName));
- Base = (ULONG_PTR)module->Module->Base;
- ExFreePool(module);
- return Base;
- }
- ULONG_PTR MyZwQuerySystemInformation(IN ULONG Number)
- {
- VOID * Buffer;
- NTSTATUS status;
- ULONG buffer1 = 20;
- Buffer = ExAllocatePool(NonPagedPool, buffer1);
- //或许实际需要的缓冲区大小传入buffer1中
- status = ZwQuerySystemInformation(Number, Buffer, buffer1, &buffer1);
- ExFreePool(Buffer);
- if (status == STATUS_INFO_LENGTH_MISMATCH)
- {
- //分配一个无分页缓冲区NonPagedPool
- Buffer = ExAllocatePool(NonPagedPool, buffer1);
- if (!MmIsAddressValid(Buffer))
- {
- MyDbgPrint("GetSystemIniformation(),if (!MmIsAddressValid(Buffer)),Error\r\n");
- return 0;
- }
- memset(Buffer, 0, sizeof(Buffer));
- status = ZwQuerySystemInformation(Number, Buffer, buffer1, &buffer1);
- if (!NT_SUCCESS(status))
- {
- MyDbgPrint("错误状态=%d\r\n", RtlNtStatusToDosError(status));
- //分配缓冲区后如果失败就必须把分配的内存释放掉
- ExFreePool(Buffer);
- MyDbgPrint("GetSystemIniformation(),if (!NT_SUCCESS(status)),Error\r\n");
- return 0;
- }
- return (ULONG_PTR)Buffer;
- }
- MyDbgPrint("GetSystemIniformation(),错误状态=%d\r\n", RtlNtStatusToDosError(status));
- return 0;
- }
- /*
- 输入导出函数名字 L""
- 获取函数地址
- */
- ULONG_PTR QueryFunction(PWCHAR name)
- {
- UNICODE_STRING na;
- ULONG_PTR add;
- RtlInitUnicodeString(&na, name);
- add = (ULONG_PTR)MmGetSystemRoutineAddress(&na);
- return add;
- }
- /*
- 加载PE到内存
- 传入ascii 字符格式文件路径,或者是unicode 字符格式文件路径
- 只能二选一,其中一个为0;
- */
- PULONG MyPeLoad(IN char* cStr, IN WCHAR *wcSrt)
- {
- /*
- 读取pe文件到内存的流程, (是先从硬盘读取文件,然后加载到内存)
- 1,先通过一个路径打开一个文件,
- 2,然后开始读取PE文件的各个信息
- 3,然后从获得的信息中获取文件需要的大小后 分配同样的大小的缓冲区
- 4,然后在依次按照规定,在把文件从DOS头 到模块依次读取到刚分配的缓冲区内存中
- */
- HANDLE hfile; //接受句柄
- NTSTATUS status; //状态
- PVOID sizeof_image=NULL; //载入内存后的地址指针
- IO_STATUS_BLOCK io_status_block; //接受状态结构
- OBJECT_ATTRIBUTES object_attributes; //句柄属性
- UNICODE_STRING path_name;
- ANSI_STRING ansi_path;
- ULONG flag = 0;//标志
-
- /*
- 判断传入的参数后初始化字符串
- */
- if (cStr != NULL&&wcSrt != NULL)
- {
- MyDbgPrint("请输入一个路径,MyPeLoad()");
- return 0;
- }
- if (cStr == NULL&&wcSrt != NULL)
- {
- RtlInitUnicodeString(&path_name, wcSrt);
- }
- else if (cStr != NULL&&wcSrt == NULL)
- {
- RtlInitAnsiString(&ansi_path, cStr);
- status = RtlAnsiStringToUnicodeString(&path_name, &ansi_path, TRUE);
- if (!NT_SUCCESS(status))
- {
- MyDbgPrint("MyLoadModule(),(cStr != NULL&&wcSrt == NULL)错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
- }
- flag = 1;
- }
- else
- {
- MyDbgPrint("输入参数无效,MyLoadModule,else\n");
- return 0;
- }
- /*
- 初始化对象属性
- ZwCreateFile函数呢不直接接受字符串只接受一个OBJECT_ATTRIBUTES的一个结构
- 所以我们要用InitializeObjectAttributes函数来初始化这个结构
- 然后在当成参数传给ZwCreateFile
- */
- InitializeObjectAttributes(&object_attributes, //对象属性变量 POBJECT_ATTRIBUTES OUT
- &path_name, //文件名 PUNICODE_STRING
- OBJ_CASE_INSENSITIVE, //表示不区分大小写
- NULL, //NULL
- NULL); //NULL
- MyDbgPrint("OBJECT_ATTRIBUTES后验证路径:%wZ\n", object_attributes.ObjectName);
- /*
- 打开文件后,开始读取到内存
- */
- status = ZwCreateFile(
- &hfile, //返回的句柄 OUT PHANDLE
- FILE_ALL_ACCESS, //访问权限->所有权限
- &object_attributes, //POBJECT_ATTRIBUTES 该结构包含要打开的文件名
- &io_status_block, //PIO_STATUS_BLOCK 返回结果状态 OUT
- 0, //初始分配大小,0是动态分配
- FILE_ATTRIBUTE_NORMAL, //文件属性 一般为<-或者0;
- FILE_SHARE_READ, //指定共享方式一般<- 或者0;
- FILE_OPEN, //这个参数指定要对文件干嘛
- FILE_NON_DIRECTORY_FILE, //指定控制打开操作和句柄使用的附加标志位
- NULL, //指向可选的扩展属性区
- 0); //扩展属性区的长度
- if (!NT_SUCCESS(status))
- {
- MyDbgPrint("MyLoadModule(),status = ZwCreateFile错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
- }
- /*
- InitializeObjectAttributes初始化对象属性,用的全局路径名字还是个指针
- 如果释放字符串缓冲区太早的话就会因路径参数为空而打开文件失败
- */
- if (flag == 1) //表示如果传入的是ansi码需要把转换时分配的缓冲区释放掉
- {
- RtlFreeUnicodeString(&path_name);
- }
- //读取DOS头******
- IMAGE_DOS_HEADER image_dos_header;//dos头结构
- LARGE_INTEGER large_integer;//记录偏移
- large_integer.QuadPart = 0;
- status = ZwReadFile(hfile, //ZwCreateFile成功后得到的句柄
- NULL, //一个事件 NULL
- NULL, //回调例程。NULL
- NULL, //NULL
- &io_status_block, //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
- &image_dos_header, //存放读取数据的缓冲区 OUT PVOID
- sizeof(IMAGE_DOS_HEADER), //试图读取文件的长度
- &large_integer, //要读取数据相对文件的偏移量PLARGE_INTEGER
- 0); //NULL
- if (!NT_SUCCESS(status))
- {
- ZwClose(hfile);
- MyDbgPrint("MyLoadModule(),status = ZwCreateFile错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
-
- }
- //读取NT头*******
- IMAGE_NT_HEADERS64 image_nt_header;//NT头
- large_integer.QuadPart = image_dos_header.e_lfanew; //PE头偏移
- status = ZwReadFile(hfile, //ZwCreateFile成功后得到的句柄
- NULL, //一个事件 NULL
- NULL, //回调例程。NULL
- NULL, //NULL
- &io_status_block, //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
- &image_nt_header, //存放读取数据的缓冲区 OUT PVOID
- sizeof(IMAGE_NT_HEADERS64), //试图读取文件的长度
- &large_integer, //要读取数据相对文件的偏移量PLARGE_INTEGER
- 0); //NULL
- if (!NT_SUCCESS(status))
- {
- ZwClose(hfile);
- MyDbgPrint("MyLoadModule(),status = ZwCreateFile错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
- }
- //读取区块*****
- IMAGE_SECTION_HEADER * p_image_section_header;//指向多个区块结构
- //分配所有模块总大小
- p_image_section_header = (IMAGE_SECTION_HEADER*)ExAllocatePool(NonPagedPool, //NonPagedPool 从非分页内存池中分配内存
- sizeof(IMAGE_SECTION_HEADER)*image_nt_header.FileHeader.NumberOfSections);
- memset(p_image_section_header, 0, sizeof(p_image_section_header));
- //读
- large_integer.QuadPart += sizeof(IMAGE_NT_HEADERS64); //区块偏移
- status = ZwReadFile(hfile, //ZwCreateFile成功后得到的句柄
- NULL, //一个事件 NULL
- NULL, //回调例程。NULL
- NULL, //NULL
- &io_status_block, //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
- p_image_section_header, //存放读取数据的缓冲区 OUT PVOID
- sizeof(IMAGE_SECTION_HEADER)*image_nt_header.FileHeader.NumberOfSections, //试图读取文件的长度
- &large_integer, //要读取数据相对文件的偏移量PLARGE_INTEGER
- 0); //NULL
- if (!NT_SUCCESS(status))
- {
- ExFreePool(p_image_section_header);
- ZwClose(hfile);
- MyDbgPrint("MyLoadModule(),large_integer.QuadPart +=错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
- }
- sizeof_image = ExAllocatePool(NonPagedPool, image_nt_header.OptionalHeader.SizeOfImage);//NonPagedPool 从非分页内存池中分配内存
- if (sizeof_image == 0)
- {
- ZwClose(hfile);
- KdPrint(("sizeof_image ExAllocatePool Failed!"));
- ExFreePool(p_image_section_header); //释放内存
- MyDbgPrint("MyLoadModule(),sizeof_image ExAllocatePool=错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
- }
- //初始化下内存
- memset(sizeof_image, 0, image_nt_header.OptionalHeader.SizeOfImage);
- RtlCopyMemory(sizeof_image, &image_dos_header, sizeof(IMAGE_DOS_HEADER)); //dos头
- RtlCopyMemory((PVOID)((ULONG_PTR)sizeof_image + image_dos_header.e_lfanew),
- &image_nt_header, sizeof(IMAGE_NT_HEADERS)); //nt头
- RtlCopyMemory((PVOID)((ULONG_PTR)sizeof_image + image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS)), //区块
- p_image_section_header, sizeof(IMAGE_SECTION_HEADER)*image_nt_header.FileHeader.NumberOfSections);//计算区块总大小
- //读取各个数据段的实际地址
- ULONG sizeof_raw_data;
- for (ULONG i = 0; i < image_nt_header.FileHeader.NumberOfSections; i++)
- { //磁盘占用的大小选择最大的
- sizeof_raw_data = _max(p_image_section_header[i].Misc.VirtualSize, p_image_section_header[i].SizeOfRawData);
- large_integer.QuadPart = p_image_section_header[i].PointerToRawData; //各个磁盘的偏移地址
- //读
- status = ZwReadFile(hfile, //ZwCreateFile成功后得到的句柄
- NULL, //一个事件 NULL
- NULL, //回调例程。NULL
- NULL, //NULL
- &io_status_block, //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
- (PVOID)((ULONG_PTR)sizeof_image + p_image_section_header[i].VirtualAddress), //区块装入内存后的相对文件头偏移量
- sizeof_raw_data, //试图读取文件的长度
- &large_integer, //要读取数据相对文件的偏移量PLARGE_INTEGER
- 0); //NULL
- if (!NT_SUCCESS(status))
- {
- MyDbgPrint("循环区块出错[%s]%x\n",
- p_image_section_header[i].Name,
- (ULONG_PTR)sizeof_image + p_image_section_header[i].VirtualAddress);
- ExFreePool(sizeof_image);
- ExFreePool(p_image_section_header); //释放内存
- ZwClose(hfile);
- MyDbgPrint("MyLoadModule(),错误=%d\n", RtlNtStatusToDosError(status));
- return 0;
- }
- }
- ExFreePool(p_image_section_header); //释放内存
- ZwClose(hfile);
- return (PULONG)sizeof_image;
- }
- /*
- 模块全局变量地址重定位表
- */
- ULONG MyRelocationTable(PVOID NewImage,PVOID RawImage)
- {
- //_IMAGE_OPTIONAL_HEADER64
- ULONG i; //for循环变量
- ULONG uRelocTableSize = 0; //存放数据块中的数据总个数
- ULONG Type; //16位数据高4位
- PVOID uRelocAddress = 0; //指向需要修改内容的地址
- PIMAGE_BASE_RELOCATION pImageBaseRelocation = 0;//重定位表
- ULONG_PTR size;
- ULONG_PTR offset;//偏移
- /*
- RtlImageNtHeader可以直接获取模块的NT头
- */
- PIMAGE_NT_HEADERS64 nt_header=NULL;
- nt_header = RtlImageNtHeader(RawImage);
- if (!nt_header)
- {
- MyDbgPrint("MyRelocationTable()->if(!nt_header)错误\n");
- return 0;
- }
- //MyDbgPrint("重载模块ImageBase地址%p", nt_header->OptionalHeader.ImageBase);
- MyDbgPrint("旧模块ImageBase地址%p\n", nt_header->OptionalHeader.ImageBase);
- /*
- 这里我们用的原始内核的偏移,目的就是定位到原始内核里的数据
- */
- offset = (ULONG_PTR)RawImage - nt_header->OptionalHeader.ImageBase;
- /*
- RtlImageDirectoryEntryToData函数
- 可以获取数据目录表中的任意结构
- 这里IMAGE_DIRECTORY_ENTRY_BASERELOC ==5 重定位表
- */
- pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData(NewImage, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, (PULONG)&size);
- if (pImageBaseRelocation == NULL)
- {
- MyDbgPrint("MyRelocationTable(),if (pImageBaseRelocation == NULL\n");
- return 0;
- }
- /*
- 开始修改数据 重新定位
- 等pImageBaseRelocation->VirtualAddress的值为空的时候就结束循环
- */
- while (pImageBaseRelocation->VirtualAddress)
- { //得到需要更改数据的个数
- uRelocTableSize = (pImageBaseRelocation->SizeOfBlock - 8) / 2;
- //循环遍历
- for (i = 0; i < uRelocTableSize; i++)
- {//判断高4位是否等于3
- Type = pImageBaseRelocation->TypeOffset[i] >> 12;
- if (Type == IMAGE_REL_BASED_DIR64)
- {
- //让指针指向要重定位的数据
- uRelocAddress = (PVOID)((pImageBaseRelocation->TypeOffset[i] & 0xfff) + pImageBaseRelocation->VirtualAddress + (ULONG_PTR)NewImage);
- //因为接下来的事要进行对地址的写入,所以判断下地址是否有效
- if (!MmIsAddressValid(uRelocAddress))
- {
- continue; //跳过本次循环然后,继续循环
- }
- /*
- 偏移里边的数据才是要重新定位的全局变量数据
- 计算出 uRelocAddress是需要重定位数据的地址
- 而要替换数据的地址(里的内容)需要加上的是 新模块基址-映像基址的偏移
- */
- *(ULONG_PTR*)uRelocAddress += offset;
- //DbgPrint("重定位后的地址%llx\r\n", *(ULONG_PTR*)uRelocAddress);
- }
- }
- //把指针移到下一个快,如果->SizeOfBlock为空了,表示没有块了退出循环
- pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pImageBaseRelocation + pImageBaseRelocation->SizeOfBlock);
- }
- MyDbgPrint("基地址重定位成功\n");
- return 1;
- }
- /*
- SSDT重定位
- */
- ULONG MySSDTRelocation(IN ULONG_PTR NewAddress, IN ULONG_PTR RawAddress)
- {
- /*
- 实验结果
- 新的内核地址是从磁盘读取到内存的,SSDT中的值是空的
- PSYSTEM_SERVICE_TABLE中的成员变量值都是空的
- 所以要进行一些关键数值复制
- */
- ULONG_PTR offset; //函数偏移
- offset = NewAddress - RawAddress;
- PSYSTEM_SERVICE_TABLE raw_ssdt=NULL,new_ssdt=NULL;
- raw_ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
- if (raw_ssdt ==NULL)
- {
- MyDbgPrint("MySSDTRelocation()->获取SSDT结构失败\n");
- return 0;
- }
- new_ssdt = (PSYSTEM_SERVICE_TABLE)((ULONG_PTR)raw_ssdt + offset);
- /*
- 关键数值替换
- */
- new_ssdt->NumberOfServices = raw_ssdt->NumberOfServices;
- new_ssdt->ServiceTableBase = (PVOID)((ULONG_PTR)raw_ssdt->ServiceTableBase + offset);
- typedef struct ssdt_recover
- {
- ULONG64 add;
- }shuzu,*pshuzu;
- typedef union asdf
- {
- ULONG a[2];
- shuzu s;
- }myunion, *pmyunion;
- myunion add;
- ULONG i;
- ULONG z; //用于过渡转换
- /*
- SSDT的偏移非常有意思.内核初始化的时候ServiceTableBase数组中的值
- 都是每8个字节存放一个函数的真实地址,然后再经过一些算法
- 把ServiceTableBase数组中每4个字节存放一个函数偏移的值
- (以ServiceTableBase * 8 * 0x191)\2 ==后的地址 可以用windbg查看
- dq 后 内存中存放的是函数的绝对地址 函数的名称则是从
- SSDT总序列号0x191\2 的函数开始,有兴趣的可以查看下;
- 然后说下算法,它是以ServiceTableBase地址为例,然后让每个
- ULONG A=(绝对函数地址[i] - ServiceTableBase ==的偏移)+ServiceTableBase的低4字节
- 存放在一个4字节的变量里. 然后 按照下面的算法后才是正确的地址
- (ServiceTableBase * i *4)= (A - ServiceTableBase的低4字节)<<4;
-
- 如果ULONG64 B > ULONG64 C , C - B =的偏移 L ;
- 如果 L 直接+B ==的值 将是一个错误的值.
- 因为B是 8个字节, 计算出的偏移是4个字节 低4节相加将会溢出到8字节的高4字节
- 所以地址就会错误.果断的让2个4字节的变量计算出正确的偏移后再写入内存
- */
- //记录SSDT真实的函数地址
- for (i = 0; i < new_ssdt->NumberOfServices; i++)
- {
- gu64_Raw_SSDT[i] = *(ULONG64 *)((ULONG_PTR)new_ssdt->ServiceTableBase + i * 8);
- //DbgPrint("%llx\n", gu64_Raw_SSDT[i]);
- }
- add.s.add = (ULONG64)raw_ssdt->ServiceTableBase;
- for (i = 0; i < raw_ssdt->NumberOfServices; i++)
- {
- z = *(ULONG64 *)((ULONG_PTR)new_ssdt->ServiceTableBase + i * 8) - add.s.add;
- z += add.a[0];
- *(ULONG *)((ULONG64)new_ssdt->ServiceTableBase + i * 4) = (z-add.a[0])<<4;
- }
- MyDbgPrint("raw_ssdt%p\n", raw_ssdt->ServiceTableBase);
- MyDbgPrint("new_ssdt%p\n", new_ssdt->ServiceTableBase);
-
- for (i = 0; i < new_ssdt->NumberOfServices; i++)
- {
- gu_SSDT[i] = *(ULONG *)((ULONG64)new_ssdt->ServiceTableBase + i * 4);
- //DbgPrint("0x%08x\n", gu_SSDT[i]);
- }
- return 1;
- }
复制代码
- BOOLEAN UnhookSSDT();
- BOOLEAN HookSSDT(ULONG64 NewFunction, ULONG index);
- //获取进程名称函数
- extern"C" NTKERNELAPI UCHAR * PsGetProcessImageFileName(IN PEPROCESS Process);
- typedef NTSTATUS(__fastcall *NTTERMINATEPROCESS)(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus);
- NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus);
- extern ULONG64 gu64_Raw_SSDT[];
- NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus)
- {
- PEPROCESS Process = NULL;
- NTTERMINATEPROCESS function;
- function = (NTTERMINATEPROCESS)gu64_Raw_SSDT[41];
- NTSTATUS st = ObReferenceObjectByHandle(ProcessHandle, 0, *PsProcessType, KernelMode, (PVOID*)&Process, NULL);
- if (NT_SUCCESS(st))
- {
- DbgPrint("%s\n", (char *)PsGetProcessImageFileName(Process));
- if (!_stricmp((char *)PsGetProcessImageFileName(Process), "notepad.exe") || !_stricmp((char *)PsGetProcessImageFileName(Process), "calc.exe"))
- return STATUS_ACCESS_DENIED;
- else
- ObDereferenceObject(Process);
- return function(ProcessHandle, ExitStatus);
- }
- else
- return STATUS_ACCESS_DENIED;
- }
- VOID MyDriverUnload(IN PDRIVER_OBJECT pDriverObject)
- {
- KdPrint(("成功进入卸载函数"));
-
- UnhookSSDT();
- KdPrint(("全部卸载完成"));
- }
- #pragma INITCODE
- extern"C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
- IN PUNICODE_STRING pRegistryPath)
- {
- KdPrint(("加载驱动成功\r\n"));
- pDriverObject->DriverUnload = MyDriverUnload;
-
- /*
- 为了验证可以HOOK的10个 SSDT 取中间一个数5个
- 每次HOOK 一次 然后内核空间内JMP的 那个地址是不一样的
- 如果能正常的进入Fake_NtTerminateProcess 这个函数
- 而且可以 无法结束进程 证明 这个通用性是可靠的
- */
- HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
- HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
- HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
- HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
- HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
- return STATUS_SUCCESS;
- }
复制代码
|
评分
-
查看全部评分
|