紫水晶编程技术论坛 - 努力打造成全国最好的编程论坛

 找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 10330|回复: 10

x64 SSDT正确的偏移计算,一键恢复SSDT表中的所有HOOK

[复制链接]

8

主题

77

帖子

2

精华

钻石会员

Rank: 6Rank: 6

积分
3279
发表于 2015-4-5 14:10:04 | 显示全部楼层 |阅读模式
本帖最后由 陌路人 于 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字节的变量计算出正确的偏移后再写入内存
        */



E(CB4EMV}A(VTJ6T_TK$~O8.jpg

  1. #pragma once //只编译一次
  2. #ifdef __cplusplus
  3. extern"C"
  4. {
  5. #endif
  6. #include "ssdt.h"

  7. #ifdef __cplusplus
  8. }
  9. #endif

  10. #define  MyDbgPrint  //DbgPrint     //需要打印信息是 把前面的 {//} 去掉   
  11. #define _max(a,b)  a>b?a:b //计算ab值那个大用哪个
  12. ULONG gu_SSDT[0x300] = { NULL };
  13. ULONG64 gu64_Raw_SSDT[0x200] = { NULL };
  14. ULONG COUNT=0;
  15. BOOLEAN FLAG = FALSE;
  16. KIRQL WPOFFx64()
  17. {
  18.         /*
  19.         提升IRQL等级到Dispatch_Level
  20.         关闭页面保护,CR0寄存器的值16位清0  0~16
  21.         硬件中断标志位清0 ,不响应可屏蔽中断
  22.         实现了,多线程安全性   _disable为硬件中断不响应
  23.         Dispatch_Level为线程不可切换软中断
  24.         */
  25.         KIRQL irql = KeRaiseIrqlToDpcLevel();
  26.         UINT64 cr0 = __readcr0();
  27.         _disable();
  28.         cr0 &= 0xfffffffffffeffff;
  29.         __writecr0(cr0);
  30.         return irql;
  31. }

  32. void WPONx64(KIRQL irql)
  33. {
  34.         /*
  35.         恢复页面保护. CRO寄存器值16位值1  0~16
  36.         IF中断标志至1  可以响应可屏蔽中断
  37.         降低软中断级别
  38.         */
  39.         UINT64 cr0 = __readcr0();
  40.         cr0 |= 0x10000;
  41.         __writecr0(cr0);
  42.         _enable();
  43.         KeLowerIrql(irql);
  44. }

  45. /*
  46. 直接   在 DriverEntry 文件中
  47. extern  ULONG64 gu64_Raw_SSDT[];

  48. 然后声明下函数 后 就可以直接使用了
  49. HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);

  50. 卸载时   UnhookSSDT();

  51. */

  52. /*
  53.          输入要替换的函数地址,已经SSDT中要HOOK的函数索引;
  54.          成功 返回真    最多支持 HOOK 10 个函数
  55. */
  56. BOOLEAN HookSSDT(ULONG64 NewFunction,ULONG index)
  57. {
  58.        
  59.         if (FLAG == FALSE)
  60.         {
  61.                 BOOLEAN  Boo;
  62.                 Boo = Relocation();
  63.                 if (!Boo)
  64.                 {
  65.                         MyDbgPrint("HookSSDT()->if (!Boo),");
  66.                         return FALSE;
  67.                 }
  68.                 FLAG = TRUE;
  69.         }
  70.         if (COUNT>=10)
  71.         {
  72.                 MyDbgPrint("hookssdt上限,只支持10个");
  73.                 return FALSE;
  74.         }
  75.         if (!MmIsAddressValid((PVOID)NewFunction))
  76.         {
  77.                 MyDbgPrint("HookSSDT()->输入的函数地址无效");
  78.                 return FALSE;
  79.         }
  80.         if (index>0x191)
  81.         {
  82.                 MyDbgPrint("要输入的函数索引无效");
  83.                 return FALSE;
  84.         }
  85.         /*
  86.                 \x48\xB8\ == mov rax
  87.                 \xFF\xE0\ == jmp rax
  88.                 0x90==nop
  89.         */
  90.         UCHAR jmp_code[] = "\x48\xB8\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\xFF\xE0";
  91.         KIRQL irql;
  92.         ULONG64 add,address;
  93.         add = ((ULONG64)KeBugCheckEx + COUNT * 15);
  94.         address = NewFunction;
  95.         RtlCopyMemory(jmp_code + 2, &address, 8);
  96.         irql = WPOFFx64();
  97.         memset((PVOID)add , 0x90, 15);
  98.         RtlCopyMemory((PVOID)add, jmp_code, 12);
  99.         WPONx64(irql);
  100.         MyDbgPrint("add%llx\n", add);

  101.         /*
  102.                 然后计算出这个地址与SSDT中的偏移
  103.         */
  104.         ULONG offset=0;
  105.         offset = FuncOffset(add);
  106.         if (offset==0)
  107.         {
  108.                 MyDbgPrint("HookSSDT()->获得函数偏移失败");
  109.                 return FALSE;
  110.         }
  111.         PSYSTEM_SERVICE_TABLE ssdt = NULL;
  112.         ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
  113.         if (ssdt == NULL)
  114.         {
  115.                 MyDbgPrint("HookSSDT()->if (ssdt==NULL)");
  116.                 return 0;
  117.         }
  118.         irql = WPOFFx64();

  119.         *(ULONG *)((ULONG64)ssdt->ServiceTableBase + index * 4) = offset;

  120.         WPONx64(irql);
  121.         COUNT++;
  122.         return TRUE;
  123. }

  124. /*
  125.         根据传入的地址计算相对与SSDT的偏移
  126. */
  127. ULONG FuncOffset(ULONG64 FuncAdd)
  128. {
  129.         PSYSTEM_SERVICE_TABLE ssdt = NULL;
  130.         ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
  131.         if (ssdt == NULL)
  132.         {
  133.                 MyDbgPrint("FuncOffset()->if (ssdt==NULL)");
  134.                 return 0;
  135.         }
  136.         typedef        struct ssdt_recover
  137.         {
  138.                 ULONG64 add;
  139.         }shuzu, *pshuzu;
  140.         typedef union  asdf
  141.         {
  142.                 ULONG  a[2];
  143.                 shuzu  s;
  144.         }myunion, *pmyunion;
  145.         myunion   add;
  146.         add.s.add = (ULONG64)ssdt->ServiceTableBase;
  147.         ULONG address;
  148.         address = FuncAdd - add.s.add;
  149.         address += add.a[0];
  150.         address = (address - add.a[0]) << 4;
  151.         return address;
  152. }

  153. /*
  154.         一键恢复SSDT的所有HOOK
  155. */
  156. BOOLEAN UnhookSSDT()
  157. {
  158.         if (FLAG == FALSE)
  159.         {
  160.                 BOOLEAN  Boo;
  161.                 Boo = Relocation();
  162.                 if (!Boo)
  163.                 {
  164.                         MyDbgPrint("UnhookSSDT()->if (!Boo),");
  165.                         return FALSE;
  166.                 }
  167.                 FLAG = TRUE;
  168.         }
  169.         PSYSTEM_SERVICE_TABLE ssdt = NULL;
  170.         ULONG i = 0;
  171.         ULONG judge;//判断
  172.         if (gu_SSDT[0]==NULL)
  173.         {
  174.                 MyDbgPrint("UnhookSSDT()->全局数组变量无效");
  175.                 return FALSE;
  176.         }
  177.         ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
  178.         if (ssdt==NULL)
  179.         {
  180.                 MyDbgPrint("UnhookSSDT()->if (ssdt==NULL)");
  181.                 return FALSE;
  182.         }
  183.         KIRQL irql;
  184.         for (i = 0; i < ssdt->NumberOfServices;i++)
  185.         {
  186.                 judge = *(ULONG*)((ULONG64)ssdt->ServiceTableBase + i * 4) >> 4;
  187.                 if (judge!= gu_SSDT[i]>>4)
  188.                 {
  189.                         irql=WPOFFx64();
  190.                         *(ULONG*)((ULONG64)ssdt->ServiceTableBase + i * 4) = gu_SSDT[i];
  191.                         WPONx64(irql);
  192.                         MyDbgPrint("被HOOK的SSDT序列号=%d,正确的函数地址%llx:\n", i, GetSSDTFunctionAddress64_2(i));
  193.                 }

  194.         }
  195.        
  196.         MyDbgPrint("UnhookSSDT()->SSDT恢复完毕");
  197.         return TRUE;

  198. }


  199. /*
  200.     读取SSDTtable地址
  201.         返回0 表示失败;否则返回地址KeServiceDescriptorTable
  202. */
  203. ULONGLONG MyGetKeServiceDescriptorTable64() //我的方法
  204. {
  205.         //获取 KiSystemCall64 的地址
  206.         PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
  207.         PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
  208.         PUCHAR i = NULL;
  209.         UCHAR b1 = 0, b2 = 0, b3 = 0;
  210.         ULONG templong = 0;
  211.         ULONGLONG addr = 0;
  212.         for (i = StartSearchAddress; i < EndSearchAddress; i++)
  213.         {
  214.                 if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2))
  215.                 {
  216.                         b1 = *i;
  217.                         b2 = *(i + 1);
  218.                         b3 = *(i + 2);
  219.                         if (b1 == 0x4c && b2 == 0x8d && b3 == 0x15) //4c8d15   SSDT 的特征码
  220.                         {
  221.                                 memcpy(&templong, i + 3, 4);
  222.                                 addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
  223.                                 return addr;
  224.                         }
  225.                 }
  226.         }
  227.         return 0;
  228. }

  229. /*
  230.         根据序列号返回函数地址;
  231. */
  232. ULONGLONG GetSSDTFunctionAddress64_2(ULONGLONG Index)
  233. {
  234.         LONG dwTemp = 0;
  235.         ULONGLONG qwTemp = 0, stb = 0, ret = 0;
  236.         PSYSTEM_SERVICE_TABLE ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();

  237.         /*
  238.                 先获取KeServiceDescriptorTable的地址
  239.                 然后获取数组的地址KiServiceTable 基地址
  240.                 这个数组里边方的是一个函数的偏移地址,
  241.                 数组首地址+对应索引内的地址=实际的函数地址.
  242.                 (因为这里并没有执行任何指令所以不需要加上指令长度)

  243.                 值得注意的是,这个偏移要转化成二进制后右移4位才是正确的偏移地址
  244.         */
  245.         stb = (ULONGLONG)(ssdt->ServiceTableBase);
  246.         qwTemp = stb + 4 * Index;
  247.         dwTemp = *(PLONG)qwTemp;
  248.         dwTemp = dwTemp >> 4;
  249.         ret = stb + (LONG64)dwTemp;
  250.         return ret;
  251. }

  252. VOID  PrintSSDT()
  253. {
  254.        
  255.         ULONG i;
  256.         ULONG64 j;
  257.         PSYSTEM_SERVICE_TABLE  a;
  258.         a = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
  259.         for (i = 0; i < a->NumberOfServices;i++)
  260.         {
  261.                 j = GetSSDTFunctionAddress64_2(i);
  262.                 DbgPrint("%d:地址=%llx\r\n", i, j);
  263.         }

  264.         return;
  265. }

  266. /*
  267.         获取SSDT正确偏移,成功返回真;
  268. */
  269. BOOLEAN Relocation()
  270. {
  271.         ULONG a = 0;
  272.         char module_path[260] = { NULL }; //内核路径
  273.         ULONG_PTR base=NULL;//内核地址
  274.         PULONG  new_module_pointer; //新内核
  275.         base=MyModuleQuery(module_path);
  276.         if (base==NULL)
  277.         {
  278.                 MyDbgPrint("Relocation()->获取模块地址失败\n");
  279.                 return FALSE;
  280.         }
  281.         new_module_pointer = MyPeLoad(module_path, NULL);
  282.         if (new_module_pointer==0)
  283.         {
  284.                 MyDbgPrint("Relocation()->new_module_pointer==0)\n");
  285.                 return FALSE;
  286.         }
  287.         MyDbgPrint("新地址%p\n", new_module_pointer);
  288.        

  289.         a = MyRelocationTable(new_module_pointer, (PVOID)base);
  290.          if (a==0)
  291.          {
  292.                  MyDbgPrint("Relocation()->基地址重定位数据失败\n");
  293.                  ExFreePool(new_module_pointer);
  294.                  return FALSE;
  295.          }
  296.         a= MySSDTRelocation((ULONG_PTR)new_module_pointer, base);
  297.         if (a == 0)
  298.         {
  299.                 MyDbgPrint("Relocation()->SSDT重定位失败\n");
  300.                 ExFreePool(new_module_pointer);
  301.                 return FALSE;
  302.         }
  303.         if (gu_SSDT[0]==NULL)
  304.         {
  305.                 MyDbgPrint("Relocation()->获取SSDT偏移失败\n");
  306.                 ExFreePool(new_module_pointer);
  307.                 return FALSE;
  308.         }
  309.         if (gu64_Raw_SSDT[0] == NULL)
  310.         {
  311.                 MyDbgPrint("Relocation()->获取SSDT真实地址失败\n");
  312.                 ExFreePool(new_module_pointer);
  313.                 return FALSE;
  314.         }
  315.         ExFreePool(new_module_pointer);
  316.         return TRUE;
  317. }

  318. /*
  319.    获取内核模块 路径跟地址
  320. */
  321. ULONG_PTR MyModuleQuery(OUT char* ModulePath )
  322. {
  323.         PSYSTEM_MODULE_INFORMATION  module = NULL;
  324.         ULONG_PTR Base;
  325.         module = (PSYSTEM_MODULE_INFORMATION)MyZwQuerySystemInformation(11);
  326.         if (module==NULL)
  327.         {
  328.                 MyDbgPrint("MyModuleQuery(),获取模块错误\n\r");
  329.                 return 0;
  330.         }

  331.         MyDbgPrint("模块路径:%s\n", module->Module->ImageName);
  332.         MyDbgPrint("%s:%p\n", module->Module->ImageName + module->Module->ModuleNameOffset, module->Module->Base);

  333.         memcpy(ModulePath, module->Module->ImageName, strlen(module->Module->ImageName));
  334.         Base = (ULONG_PTR)module->Module->Base;
  335.         ExFreePool(module);
  336.         return Base;
  337. }

  338. ULONG_PTR MyZwQuerySystemInformation(IN ULONG Number)
  339. {
  340.         VOID * Buffer;
  341.         NTSTATUS status;
  342.         ULONG   buffer1 = 20;

  343.         Buffer = ExAllocatePool(NonPagedPool, buffer1);
  344.         //或许实际需要的缓冲区大小传入buffer1中
  345.         status = ZwQuerySystemInformation(Number, Buffer, buffer1, &buffer1);
  346.         ExFreePool(Buffer);
  347.         if (status == STATUS_INFO_LENGTH_MISMATCH)
  348.         {
  349.                 //分配一个无分页缓冲区NonPagedPool
  350.                 Buffer = ExAllocatePool(NonPagedPool, buffer1);
  351.                 if (!MmIsAddressValid(Buffer))
  352.                 {
  353.                         MyDbgPrint("GetSystemIniformation(),if (!MmIsAddressValid(Buffer)),Error\r\n");
  354.                         return 0;
  355.                 }
  356.                 memset(Buffer, 0, sizeof(Buffer));
  357.                 status = ZwQuerySystemInformation(Number, Buffer, buffer1, &buffer1);
  358.                 if (!NT_SUCCESS(status))
  359.                 {
  360.                         MyDbgPrint("错误状态=%d\r\n", RtlNtStatusToDosError(status));
  361.                         //分配缓冲区后如果失败就必须把分配的内存释放掉
  362.                         ExFreePool(Buffer);
  363.                         MyDbgPrint("GetSystemIniformation(),if (!NT_SUCCESS(status)),Error\r\n");
  364.                         return 0;
  365.                 }
  366.                 return (ULONG_PTR)Buffer;
  367.         }
  368.         MyDbgPrint("GetSystemIniformation(),错误状态=%d\r\n", RtlNtStatusToDosError(status));
  369.         return 0;
  370. }

  371. /*
  372.         输入导出函数名字 L""
  373.         获取函数地址
  374. */
  375. ULONG_PTR QueryFunction(PWCHAR name)
  376. {
  377.         UNICODE_STRING   na;
  378.         ULONG_PTR add;
  379.         RtlInitUnicodeString(&na, name);
  380.         add = (ULONG_PTR)MmGetSystemRoutineAddress(&na);
  381.         return add;
  382. }

  383. /*
  384.         加载PE到内存
  385.         传入ascii 字符格式文件路径,或者是unicode 字符格式文件路径
  386.         只能二选一,其中一个为0;
  387. */
  388. PULONG MyPeLoad(IN char* cStr, IN WCHAR *wcSrt)
  389. {
  390.         /*
  391.         读取pe文件到内存的流程,   (是先从硬盘读取文件,然后加载到内存)
  392.         1,先通过一个路径打开一个文件,
  393.         2,然后开始读取PE文件的各个信息
  394.         3,然后从获得的信息中获取文件需要的大小后 分配同样的大小的缓冲区
  395.         4,然后在依次按照规定,在把文件从DOS头 到模块依次读取到刚分配的缓冲区内存中
  396.         */

  397.         HANDLE    hfile;     //接受句柄
  398.         NTSTATUS  status;    //状态
  399.         PVOID sizeof_image=NULL;  //载入内存后的地址指针
  400.         IO_STATUS_BLOCK      io_status_block;    //接受状态结构
  401.         OBJECT_ATTRIBUTES    object_attributes;  //句柄属性
  402.         UNICODE_STRING       path_name;      
  403.         ANSI_STRING  ansi_path;
  404.         ULONG flag = 0;//标志
  405.        

  406.         /*
  407.                 判断传入的参数后初始化字符串
  408.         */
  409.         if (cStr != NULL&&wcSrt != NULL)
  410.         {
  411.                 MyDbgPrint("请输入一个路径,MyPeLoad()");
  412.                 return 0;
  413.         }
  414.         if (cStr == NULL&&wcSrt != NULL)
  415.         {
  416.                 RtlInitUnicodeString(&path_name, wcSrt);
  417.         }
  418.         else if (cStr != NULL&&wcSrt == NULL)
  419.         {
  420.                 RtlInitAnsiString(&ansi_path, cStr);
  421.                 status = RtlAnsiStringToUnicodeString(&path_name, &ansi_path, TRUE);
  422.                 if (!NT_SUCCESS(status))
  423.                 {
  424.                         MyDbgPrint("MyLoadModule(),(cStr != NULL&&wcSrt == NULL)错误=%d\n", RtlNtStatusToDosError(status));
  425.                         return 0;
  426.                 }
  427.                 flag = 1;
  428.         }
  429.         else
  430.         {
  431.                 MyDbgPrint("输入参数无效,MyLoadModule,else\n");
  432.                 return 0;
  433.         }




  434.         /*
  435.             初始化对象属性
  436.             ZwCreateFile函数呢不直接接受字符串只接受一个OBJECT_ATTRIBUTES的一个结构
  437.             所以我们要用InitializeObjectAttributes函数来初始化这个结构
  438.                 然后在当成参数传给ZwCreateFile
  439.         */
  440.         InitializeObjectAttributes(&object_attributes, //对象属性变量 POBJECT_ATTRIBUTES OUT  
  441.                 &path_name,                                                                   //文件名   PUNICODE_STRING
  442.                 OBJ_CASE_INSENSITIVE,                      //表示不区分大小写
  443.                 NULL,                                      //NULL
  444.                 NULL);                                     //NULL
  445.         MyDbgPrint("OBJECT_ATTRIBUTES后验证路径:%wZ\n", object_attributes.ObjectName);
  446.         /*
  447.                 打开文件后,开始读取到内存
  448.         */
  449.         status = ZwCreateFile(
  450.                 &hfile,                  //返回的句柄  OUT PHANDLE
  451.                 FILE_ALL_ACCESS,         //访问权限->所有权限
  452.                 &object_attributes,      //POBJECT_ATTRIBUTES 该结构包含要打开的文件名
  453.                 &io_status_block,        //PIO_STATUS_BLOCK 返回结果状态 OUT
  454.                 0,                       //初始分配大小,0是动态分配
  455.                 FILE_ATTRIBUTE_NORMAL,   //文件属性 一般为<-或者0;
  456.                 FILE_SHARE_READ,         //指定共享方式一般<- 或者0;
  457.                 FILE_OPEN,               //这个参数指定要对文件干嘛
  458.                 FILE_NON_DIRECTORY_FILE, //指定控制打开操作和句柄使用的附加标志位
  459.                 NULL,                    //指向可选的扩展属性区
  460.                 0);                      //扩展属性区的长度
  461.         if (!NT_SUCCESS(status))
  462.         {
  463.                 MyDbgPrint("MyLoadModule(),status = ZwCreateFile错误=%d\n", RtlNtStatusToDosError(status));
  464.                 return 0;
  465.         }
  466.         /*
  467.         InitializeObjectAttributes初始化对象属性,用的全局路径名字还是个指针
  468.         如果释放字符串缓冲区太早的话就会因路径参数为空而打开文件失败       
  469.         */
  470.         if (flag == 1) //表示如果传入的是ansi码需要把转换时分配的缓冲区释放掉
  471.         {
  472.                 RtlFreeUnicodeString(&path_name);
  473.         }





  474.         //读取DOS头******
  475.         IMAGE_DOS_HEADER        image_dos_header;//dos头结构
  476.         LARGE_INTEGER       large_integer;//记录偏移
  477.         large_integer.QuadPart = 0;
  478.         status = ZwReadFile(hfile,       //ZwCreateFile成功后得到的句柄  
  479.                 NULL,                        //一个事件  NULL
  480.                 NULL,                        //回调例程。NULL
  481.                 NULL,                        //NULL
  482.                 &io_status_block,            //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
  483.                 &image_dos_header,           //存放读取数据的缓冲区 OUT PVOID  
  484.                 sizeof(IMAGE_DOS_HEADER),    //试图读取文件的长度
  485.                 &large_integer,              //要读取数据相对文件的偏移量PLARGE_INTEGER
  486.                 0);                          //NULL
  487.         if (!NT_SUCCESS(status))
  488.         {
  489.                 ZwClose(hfile);
  490.                 MyDbgPrint("MyLoadModule(),status = ZwCreateFile错误=%d\n", RtlNtStatusToDosError(status));
  491.                 return 0;
  492.                
  493.         }
  494.         //读取NT头*******
  495.         IMAGE_NT_HEADERS64   image_nt_header;//NT头
  496.         large_integer.QuadPart = image_dos_header.e_lfanew; //PE头偏移
  497.         status = ZwReadFile(hfile,       //ZwCreateFile成功后得到的句柄  
  498.                 NULL,                        //一个事件  NULL
  499.                 NULL,                        //回调例程。NULL
  500.                 NULL,                        //NULL
  501.                 &io_status_block,            //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
  502.                 &image_nt_header,           //存放读取数据的缓冲区 OUT PVOID  
  503.                 sizeof(IMAGE_NT_HEADERS64),    //试图读取文件的长度
  504.                 &large_integer,              //要读取数据相对文件的偏移量PLARGE_INTEGER
  505.                 0);                          //NULL
  506.         if (!NT_SUCCESS(status))
  507.         {
  508.                 ZwClose(hfile);
  509.                 MyDbgPrint("MyLoadModule(),status = ZwCreateFile错误=%d\n", RtlNtStatusToDosError(status));
  510.                 return 0;
  511.         }
  512.         //读取区块*****
  513.         IMAGE_SECTION_HEADER * p_image_section_header;//指向多个区块结构
  514.         //分配所有模块总大小
  515.         p_image_section_header = (IMAGE_SECTION_HEADER*)ExAllocatePool(NonPagedPool,        //NonPagedPool  从非分页内存池中分配内存
  516.                 sizeof(IMAGE_SECTION_HEADER)*image_nt_header.FileHeader.NumberOfSections);
  517.         memset(p_image_section_header, 0, sizeof(p_image_section_header));
  518.         //读
  519.         large_integer.QuadPart += sizeof(IMAGE_NT_HEADERS64); //区块偏移
  520.         status = ZwReadFile(hfile,       //ZwCreateFile成功后得到的句柄  
  521.                 NULL,                        //一个事件  NULL
  522.                 NULL,                        //回调例程。NULL
  523.                 NULL,                        //NULL
  524.                 &io_status_block,            //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
  525.                 p_image_section_header,           //存放读取数据的缓冲区 OUT PVOID  
  526.                 sizeof(IMAGE_SECTION_HEADER)*image_nt_header.FileHeader.NumberOfSections,    //试图读取文件的长度
  527.                 &large_integer,              //要读取数据相对文件的偏移量PLARGE_INTEGER
  528.                 0);                          //NULL
  529.         if (!NT_SUCCESS(status))
  530.         {
  531.                 ExFreePool(p_image_section_header);
  532.                 ZwClose(hfile);
  533.                 MyDbgPrint("MyLoadModule(),large_integer.QuadPart +=错误=%d\n", RtlNtStatusToDosError(status));
  534.                 return 0;
  535.         }
  536.         sizeof_image = ExAllocatePool(NonPagedPool, image_nt_header.OptionalHeader.SizeOfImage);//NonPagedPool  从非分页内存池中分配内存
  537.         if (sizeof_image == 0)
  538.         {
  539.                 ZwClose(hfile);
  540.                 KdPrint(("sizeof_image ExAllocatePool Failed!"));
  541.                 ExFreePool(p_image_section_header);  //释放内存
  542.                 MyDbgPrint("MyLoadModule(),sizeof_image ExAllocatePool=错误=%d\n", RtlNtStatusToDosError(status));
  543.                 return 0;
  544.         }
  545.         //初始化下内存
  546.         memset(sizeof_image, 0, image_nt_header.OptionalHeader.SizeOfImage);
  547.         RtlCopyMemory(sizeof_image, &image_dos_header, sizeof(IMAGE_DOS_HEADER));        //dos头
  548.         RtlCopyMemory((PVOID)((ULONG_PTR)sizeof_image + image_dos_header.e_lfanew),
  549.                 &image_nt_header, sizeof(IMAGE_NT_HEADERS));                                 //nt头
  550.         RtlCopyMemory((PVOID)((ULONG_PTR)sizeof_image + image_dos_header.e_lfanew + sizeof(IMAGE_NT_HEADERS)),       //区块
  551.                 p_image_section_header, sizeof(IMAGE_SECTION_HEADER)*image_nt_header.FileHeader.NumberOfSections);//计算区块总大小
  552.         //读取各个数据段的实际地址
  553.         ULONG sizeof_raw_data;
  554.         for (ULONG i = 0; i < image_nt_header.FileHeader.NumberOfSections; i++)
  555.         {   //磁盘占用的大小选择最大的
  556.                 sizeof_raw_data = _max(p_image_section_header[i].Misc.VirtualSize, p_image_section_header[i].SizeOfRawData);
  557.                 large_integer.QuadPart = p_image_section_header[i].PointerToRawData;   //各个磁盘的偏移地址
  558.                 //读
  559.                 status = ZwReadFile(hfile,       //ZwCreateFile成功后得到的句柄  
  560.                         NULL,                        //一个事件  NULL
  561.                         NULL,                        //回调例程。NULL
  562.                         NULL,                        //NULL
  563.                         &io_status_block,            //PIO_STATUS_BLOCK 返回结果状态 OUT ,同上
  564.                         (PVOID)((ULONG_PTR)sizeof_image + p_image_section_header[i].VirtualAddress), //区块装入内存后的相对文件头偏移量         
  565.                         sizeof_raw_data,             //试图读取文件的长度
  566.                         &large_integer,              //要读取数据相对文件的偏移量PLARGE_INTEGER
  567.                         0);                          //NULL
  568.                 if (!NT_SUCCESS(status))
  569.                 {
  570.                         MyDbgPrint("循环区块出错[%s]%x\n",
  571.                                 p_image_section_header[i].Name,
  572.                                 (ULONG_PTR)sizeof_image + p_image_section_header[i].VirtualAddress);

  573.                         ExFreePool(sizeof_image);
  574.                         ExFreePool(p_image_section_header);  //释放内存
  575.                         ZwClose(hfile);
  576.                         MyDbgPrint("MyLoadModule(),错误=%d\n", RtlNtStatusToDosError(status));
  577.                         return 0;
  578.                 }
  579.         }
  580.         ExFreePool(p_image_section_header);  //释放内存
  581.         ZwClose(hfile);
  582.         return (PULONG)sizeof_image;
  583. }

  584. /*
  585.         模块全局变量地址重定位表
  586. */
  587. ULONG  MyRelocationTable(PVOID  NewImage,PVOID RawImage)
  588. {

  589.         //_IMAGE_OPTIONAL_HEADER64
  590.         ULONG                                        i;                       //for循环变量
  591.         ULONG                                        uRelocTableSize = 0;     //存放数据块中的数据总个数
  592.         ULONG                                        Type;                    //16位数据高4位
  593.         PVOID                                         uRelocAddress = 0;       //指向需要修改内容的地址
  594.         PIMAGE_BASE_RELOCATION        pImageBaseRelocation = 0;//重定位表
  595.         ULONG_PTR size;
  596.         ULONG_PTR  offset;//偏移
  597.         /*
  598.                 RtlImageNtHeader可以直接获取模块的NT头
  599.         */
  600.         PIMAGE_NT_HEADERS64 nt_header=NULL;
  601.         nt_header = RtlImageNtHeader(RawImage);
  602.         if (!nt_header)
  603.         {
  604.                 MyDbgPrint("MyRelocationTable()->if(!nt_header)错误\n");
  605.                 return 0;
  606.         }
  607.         //MyDbgPrint("重载模块ImageBase地址%p", nt_header->OptionalHeader.ImageBase);
  608.         MyDbgPrint("旧模块ImageBase地址%p\n", nt_header->OptionalHeader.ImageBase);
  609.         /*
  610.                 这里我们用的原始内核的偏移,目的就是定位到原始内核里的数据
  611.         */
  612.         offset = (ULONG_PTR)RawImage - nt_header->OptionalHeader.ImageBase;
  613.         /*
  614.              RtlImageDirectoryEntryToData函数
  615.                  可以获取数据目录表中的任意结构
  616.                  这里IMAGE_DIRECTORY_ENTRY_BASERELOC ==5 重定位表
  617.         */
  618.         pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData(NewImage, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, (PULONG)&size);
  619.         if (pImageBaseRelocation == NULL)
  620.         {
  621.                 MyDbgPrint("MyRelocationTable(),if (pImageBaseRelocation == NULL\n");
  622.                 return 0;
  623.         }

  624.         /*
  625.                 开始修改数据 重新定位

  626.                 等pImageBaseRelocation->VirtualAddress的值为空的时候就结束循环
  627.         */
  628.         while (pImageBaseRelocation->VirtualAddress)
  629.         {   //得到需要更改数据的个数

  630.                 uRelocTableSize = (pImageBaseRelocation->SizeOfBlock - 8) / 2;
  631.                 //循环遍历
  632.                 for (i = 0; i < uRelocTableSize; i++)
  633.                 {//判断高4位是否等于3
  634.                         Type = pImageBaseRelocation->TypeOffset[i] >> 12;
  635.                         if (Type == IMAGE_REL_BASED_DIR64)
  636.                         {
  637.                                 //让指针指向要重定位的数据
  638.                                 uRelocAddress = (PVOID)((pImageBaseRelocation->TypeOffset[i] & 0xfff) + pImageBaseRelocation->VirtualAddress + (ULONG_PTR)NewImage);

  639.                                 //因为接下来的事要进行对地址的写入,所以判断下地址是否有效
  640.                                 if (!MmIsAddressValid(uRelocAddress))
  641.                                 {
  642.                                         continue;  //跳过本次循环然后,继续循环
  643.                                 }

  644.                                 /*
  645.                                         偏移里边的数据才是要重新定位的全局变量数据
  646.                                         计算出  uRelocAddress是需要重定位数据的地址
  647.                                         而要替换数据的地址(里的内容)需要加上的是   新模块基址-映像基址的偏移
  648.                                 */
  649.                                 *(ULONG_PTR*)uRelocAddress +=  offset;
  650.                                 //DbgPrint("重定位后的地址%llx\r\n", *(ULONG_PTR*)uRelocAddress);

  651.                         }
  652.                 }
  653.                 //把指针移到下一个快,如果->SizeOfBlock为空了,表示没有块了退出循环
  654.                 pImageBaseRelocation = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pImageBaseRelocation + pImageBaseRelocation->SizeOfBlock);
  655.         }

  656.         MyDbgPrint("基地址重定位成功\n");
  657.         return 1;
  658. }

  659. /*
  660.         SSDT重定位
  661. */
  662. ULONG MySSDTRelocation(IN ULONG_PTR NewAddress, IN ULONG_PTR RawAddress)
  663. {            
  664.         /*
  665.               实验结果
  666.                 新的内核地址是从磁盘读取到内存的,SSDT中的值是空的
  667.             PSYSTEM_SERVICE_TABLE中的成员变量值都是空的
  668.                 所以要进行一些关键数值复制
  669.         */
  670.         ULONG_PTR offset; //函数偏移
  671.         offset = NewAddress - RawAddress;
  672.         PSYSTEM_SERVICE_TABLE  raw_ssdt=NULL,new_ssdt=NULL;
  673.         raw_ssdt = (PSYSTEM_SERVICE_TABLE)MyGetKeServiceDescriptorTable64();
  674.         if (raw_ssdt ==NULL)
  675.         {
  676.                 MyDbgPrint("MySSDTRelocation()->获取SSDT结构失败\n");
  677.                 return 0;
  678.         }
  679.         new_ssdt = (PSYSTEM_SERVICE_TABLE)((ULONG_PTR)raw_ssdt + offset);
  680.         /*
  681.                 关键数值替换
  682.         */
  683.         new_ssdt->NumberOfServices = raw_ssdt->NumberOfServices;
  684.         new_ssdt->ServiceTableBase = (PVOID)((ULONG_PTR)raw_ssdt->ServiceTableBase + offset);

  685. typedef        struct ssdt_recover
  686.         {          
  687.                 ULONG64 add;
  688.         }shuzu,*pshuzu;
  689.         typedef union  asdf
  690.         {
  691.                 ULONG  a[2];
  692.                 shuzu  s;
  693.         }myunion, *pmyunion;
  694.         myunion   add;
  695.         ULONG i;
  696.         ULONG  z; //用于过渡转换

  697.         /*
  698.                 SSDT的偏移非常有意思.内核初始化的时候ServiceTableBase数组中的值
  699.                 都是每8个字节存放一个函数的真实地址,然后再经过一些算法
  700.                 把ServiceTableBase数组中每4个字节存放一个函数偏移的值

  701.                 (以ServiceTableBase * 8 * 0x191)\2 ==后的地址 可以用windbg查看
  702.                 dq 后 内存中存放的是函数的绝对地址 函数的名称则是从
  703.                 SSDT总序列号0x191\2 的函数开始,有兴趣的可以查看下;

  704.                 然后说下算法,它是以ServiceTableBase地址为例,然后让每个
  705.                 ULONG A=(绝对函数地址[i] - ServiceTableBase ==的偏移)+ServiceTableBase的低4字节
  706.                 存放在一个4字节的变量里. 然后  按照下面的算法后才是正确的地址
  707.                 (ServiceTableBase * i *4)= (A - ServiceTableBase的低4字节)<<4;
  708.                
  709.                 如果ULONG64 B > ULONG64 C , C - B =的偏移  L ;
  710.                 如果  L 直接+B ==的值 将是一个错误的值.
  711.                 因为B是 8个字节, 计算出的偏移是4个字节  低4节相加将会溢出到8字节的高4字节
  712.                 所以地址就会错误.果断的让2个4字节的变量计算出正确的偏移后再写入内存
  713.         */
  714.         //记录SSDT真实的函数地址
  715.         for (i = 0; i < new_ssdt->NumberOfServices; i++)
  716.         {
  717.                 gu64_Raw_SSDT[i] = *(ULONG64 *)((ULONG_PTR)new_ssdt->ServiceTableBase + i * 8);
  718.                 //DbgPrint("%llx\n", gu64_Raw_SSDT[i]);
  719.         }

  720.         add.s.add = (ULONG64)raw_ssdt->ServiceTableBase;
  721.         for (i = 0; i < raw_ssdt->NumberOfServices; i++)
  722.         {
  723.                 z = *(ULONG64 *)((ULONG_PTR)new_ssdt->ServiceTableBase + i * 8) - add.s.add;
  724.                 z += add.a[0];
  725.                 *(ULONG *)((ULONG64)new_ssdt->ServiceTableBase + i * 4) = (z-add.a[0])<<4;

  726.         }
  727.         MyDbgPrint("raw_ssdt%p\n", raw_ssdt->ServiceTableBase);
  728.         MyDbgPrint("new_ssdt%p\n", new_ssdt->ServiceTableBase);
  729.        
  730.         for (i = 0; i < new_ssdt->NumberOfServices; i++)
  731.         {
  732.                 gu_SSDT[i] = *(ULONG *)((ULONG64)new_ssdt->ServiceTableBase + i * 4);
  733.                 //DbgPrint("0x%08x\n", gu_SSDT[i]);
  734.         }

  735.         return 1;
  736. }
复制代码


  1. BOOLEAN UnhookSSDT();
  2. BOOLEAN HookSSDT(ULONG64 NewFunction, ULONG index);
  3. //获取进程名称函数
  4. extern"C" NTKERNELAPI UCHAR * PsGetProcessImageFileName(IN PEPROCESS Process);
  5. typedef NTSTATUS(__fastcall *NTTERMINATEPROCESS)(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus);
  6. NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus);


  7. extern  ULONG64 gu64_Raw_SSDT[];

  8. NTSTATUS __fastcall Fake_NtTerminateProcess(IN HANDLE ProcessHandle, IN NTSTATUS ExitStatus)
  9. {
  10.         PEPROCESS Process = NULL;
  11.         NTTERMINATEPROCESS  function;
  12.         function = (NTTERMINATEPROCESS)gu64_Raw_SSDT[41];
  13.         NTSTATUS st = ObReferenceObjectByHandle(ProcessHandle, 0, *PsProcessType, KernelMode, (PVOID*)&Process, NULL);

  14.         if (NT_SUCCESS(st))
  15.         {
  16.                 DbgPrint("%s\n", (char *)PsGetProcessImageFileName(Process));
  17.                 if (!_stricmp((char *)PsGetProcessImageFileName(Process), "notepad.exe") || !_stricmp((char *)PsGetProcessImageFileName(Process), "calc.exe"))
  18.                         return STATUS_ACCESS_DENIED;
  19.                 else
  20.                         ObDereferenceObject(Process);
  21.                 return  function(ProcessHandle, ExitStatus);
  22.         }
  23.         else
  24.                 return STATUS_ACCESS_DENIED;
  25. }

  26. VOID MyDriverUnload(IN PDRIVER_OBJECT pDriverObject)
  27. {
  28.         KdPrint(("成功进入卸载函数"));
  29.        
  30.         UnhookSSDT();
  31.         KdPrint(("全部卸载完成"));
  32. }

  33. #pragma INITCODE
  34. extern"C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,
  35.         IN PUNICODE_STRING pRegistryPath)
  36. {
  37.         KdPrint(("加载驱动成功\r\n"));
  38.         pDriverObject->DriverUnload = MyDriverUnload;
  39.        
  40.         /*
  41.                 为了验证可以HOOK的10个 SSDT  取中间一个数5个
  42.                 每次HOOK 一次 然后内核空间内JMP的 那个地址是不一样的
  43.                 如果能正常的进入Fake_NtTerminateProcess 这个函数
  44.                 而且可以 无法结束进程  证明 这个通用性是可靠的
  45.         */
  46.         HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
  47.         HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
  48.         HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
  49.         HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);
  50.         HookSSDT((ULONG64)Fake_NtTerminateProcess, 41);

  51.         return STATUS_SUCCESS;
  52. }
复制代码



ssdthook.rar

8.23 KB, 下载次数: 4185

评分

参与人数 1水晶币 +60 收起 理由
Tesla.Angela + 60 赞一个!

查看全部评分

8

主题

77

帖子

2

精华

钻石会员

Rank: 6Rank: 6

积分
3279
 楼主| 发表于 2015-4-5 14:13:35 | 显示全部楼层
第一次发帖, 给自己顶一个

851

主题

3477

帖子

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

Rank: 125Rank: 125Rank: 125Rank: 125Rank: 125

积分
37093
发表于 2015-4-5 19:13:48 | 显示全部楼层
我就看到PoHuai_64了。。。{:soso_e113:}

8

主题

77

帖子

2

精华

钻石会员

Rank: 6Rank: 6

积分
3279
 楼主| 发表于 2015-4-5 19:29:22 | 显示全部楼层
Tesla.Angela 发表于 2015-4-5 19:13
我就看到PoHuai_64了。。。

````.哪看到的?    糗大了!!!    英语不会`` 只能用拼音了`````
我是刚看了你后边的教程,  才发现.  那个重定位哪 你有讲```
不过这部分代码,是我自己实现的还有计算偏移哪(想说明的就是 我不是拿你的代码 然后整体粘贴过来混经验)
我有认真你的代码啊,  大部分都注释了 比如
  1. KIRQL WPOFFx64()
  2. {
  3.         /*
  4.         提升IRQL等级到Dispatch_Level
  5.         关闭页面保护,CR0寄存器的值16位清0  0~16
  6.         硬件中断标志位清0 ,不响应可屏蔽中断
  7.         实现了,多线程安全性   _disable为硬件中断不响应
  8.         Dispatch_Level为线程不可切换软中断
  9.         */
  10.         KIRQL irql = KeRaiseIrqlToDpcLevel();
  11.         UINT64 cr0 = __readcr0();
  12.         _disable();
  13.         cr0 &= 0xfffffffffffeffff;
  14.         __writecr0(cr0);
  15.         return irql;
  16. }

  17. void WPONx64(KIRQL irql)
  18. {
  19.         /*
  20.         恢复页面保护. CRO寄存器值16位值1  0~16
  21.         IF中断标志至1  可以响应可屏蔽中断
  22.         降低软中断级别
  23.         */
  24.         UINT64 cr0 = __readcr0();
  25.         cr0 |= 0x10000;
  26.         __writecr0(cr0);
  27.         _enable();
  28.         KeLowerIrql(irql);
  29. }
复制代码

851

主题

3477

帖子

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

Rank: 125Rank: 125Rank: 125Rank: 125Rank: 125

积分
37093
发表于 2015-4-6 00:06:25 | 显示全部楼层
陌路人 发表于 2015-4-5 19:29
````.哪看到的?    糗大了!!!    英语不会`` 只能用拼音了`````
我是刚看了你后边的教程,  才发现.  那个 ...


我不是贴出了一个新版本的代码来计算正确地址么。。。旧版本的代码对于HOOK参数小于4个的函数是没问题的,HOOK参数多于4个的函数才有问题。。。。

不过看出你的学习态度很认真,赞一个。

顺带说句,WPOFFx64和WPONx64是X64ASM的坛主教我的。可惜这个站点已经关闭。

8

主题

77

帖子

2

精华

钻石会员

Rank: 6Rank: 6

积分
3279
 楼主| 发表于 2015-4-6 01:23:46 | 显示全部楼层
Tesla.Angela 发表于 2015-4-6 00:06
我不是贴出了一个新版本的代码来计算正确地址么。。。旧版本的代码对于HOOK参数小于4个的函数是没问题的 ...

新版本那个没看懂,```
刚弄了2小时机器码,想弄到IDA里反编译下 结果 半天没弄成.
然后发现,64的.如果把写好的函数 全部读取内存,把机器码存放在一个数组里.
然后动态分配内存,定义函数指针.  这样的话 想要用IDA 静态分析基本 是不可能的了.
分析出来都是 db 数组
  1. dwtmp=(LONG)(FuncAddr-(ULONGLONG)ServiceTableBase);
  2. return dwtmp<<4   

  3. 这样计算是有问题的,如果啊
  4. FuncAddr 这个函数地址  小于ServiceTableBase[?]这个函数地址
  5. 得出的结果dwtmp 直接<<4 位是错误的
  6. 比如 FuncAddr =FFFFF80011111111
  7. ServiceTableBase=FFFFF80022222222
  8. FFFFF80011111111--FFFFF80022222222=
  9. =EEEEEEEF;  
  10. FFFFF80022222222+EEEEEEEF=
  11. =FFFFF80031111111; 显然这是错误的
  12. 另一个函数这里  不正好是这么计算的吗
  13. dwtmp=dwtmp>>4;
  14. return dwtmp + (ULONGLONG)ServiceTableBase;
复制代码

851

主题

3477

帖子

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

Rank: 125Rank: 125Rank: 125Rank: 125Rank: 125

积分
37093
发表于 2015-4-6 05:21:55 | 显示全部楼层
陌路人 发表于 2015-4-6 01:23
新版本那个没看懂,```
刚弄了2小时机器码,想弄到IDA里反编译下 结果 半天没弄成.
然后发现,64的.如果把 ...


假设一个SSDT函数地址为X,那么(理论上)SSDT里记录的地址就是A=X-ServiceTableBase

但你要知道,这个A的低4位,肯定是0。简单点说,就是每个SSDT函数之间有一定距离,不会一个贴着一个。文字形式就是:0x12345670(最后一个数字为0,文本形式的最后一个数字对应的就是低4位)

既然大家都知道这个数字是0,干脆就废物利用,利用这个低4位的里存储一下数据,这个数据是:函数参数个数-4(如果函数参数大于4才记录,否则记为0)。

所以获取SSDT函数地址的时候,把这个低4位清零,当你要HOOK(填写代理函数地址)时,就要把这个低4位的正确值(函数参数个数-4)写入回去。

(自认为描述得不错了,如果还不理解我也没办法。)

8

主题

77

帖子

2

精华

钻石会员

Rank: 6Rank: 6

积分
3279
 楼主| 发表于 2015-4-6 13:02:50 | 显示全部楼层
Tesla.Angela 发表于 2015-4-6 05:21
假设一个SSDT函数地址为X,那么(理论上)SSDT里记录的地址就是A=X-ServiceTableBase

但你要知道,这个A ...

嗯,  这么一说 看明白了, 我也纳闷过 最后一位的 数字是咋出来的
受教了 , 谢谢了!
不过我确实发现了一个小问题 不过问题的实用性不大
昨天晚上  太困了 没有拿 实例 来说明,就大概的比喻了一下意思

  1. 0: kd> dd KiServiceTable
  2. fffff800`018c0800  04134b00 02f53600 fff6f000 02e80205

  3. 拿第 3个函数做例子  fff6f000    fff6f000>>4=ffff6f00
  4. 0: kd> u  fffff800`018c0800 + 0xffff6f00
  5. fffff800`118b7700 ??              ???
  6.                                        ^ Memory access error in 'u  fffff800`018c0800 + 0xfff6f00 '

  7. 按照计算实际函数的地址方法 是这样的

  8. dwtmp=dwtmp>>4;
  9. return dwtmp + (ULONGLONG)ServiceTableBase;

  10. 0xffff6f00 + fffff800`018c0800=FFFFF801018AE600;

  11. 这样结果就是错误的,  所以计算实际地址时,需要

  12. 0xffff6f00 +018c0800 =  0x018b7700  dwtmp 4字节变量

  13. 0x018b7700  +fffff800`00000000 = FFFFF800018B7700

  14. 1: kd> u FFFFF800018B7700
  15. nt!NtCallbackReturn:
  16. fffff800`018b7700 4883ec18        sub     rsp,18h

  17. 注: 我们自己去SSDT 提起函数地址显然是很少用的
  18. 所以这个问题也忽略不计了,
复制代码

30

主题

723

帖子

0

精华

钻石会员

Rank: 6Rank: 6

积分
2815
发表于 2015-5-14 12:07:50 | 显示全部楼层
很好很好 问题辩论才明了

0

主题

7

帖子

0

精华

铜牌会员

Rank: 2Rank: 2

积分
98
发表于 2017-7-21 21:42:22 | 显示全部楼层
啊,SSDT那部分看了好几天都没太明白为什么hook的时候是4位的偏移地址,而unhook的时候是8位的偏移地址,原来unhook读取的保存在磁盘里面的KiServiceTable,这里面保存的是8位的偏移地址,而被加载到内存之后就变成了4位的偏移地址了。
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

手机版|Archiver|紫水晶工作室 ( 粤ICP备05020336号 )

GMT+8, 2024-3-29 20:18 , Processed in 0.038294 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表