YJX_Driver_032_自写驱动保护XX进程

1、

自写驱动保护XX进程(HOOK SSDT)
  A、构建自己的内核函数(用来替换对应的内核函数)
  C、Hook和UnHook函数构建
  D、修改EXE和SYS对应源代码实现所谓保护
  E、测试效果

 

【135】以28课的代码为例 新建一个hook.h单元

  【200】实际上选用 第29/30课 的代码也是可以的

    【380】hook.h 里面的代码:

#pragma once
#ifdef __cplusplus
extern "C"
{
#endif

#include <NTDDK.h> //这里包含需要用C方式编译的头文件

#ifdef __cplusplus
}
#endif

//#include <windef.h>

bool ssdthook_flag=false;
ULONG RealNtOpenAddress;
HANDLE MyPID;

// A、构建自己的内核函数(用来替换对应的内核函数)

// 定义一下NtOpenProcess的原型
extern "C" typedef NTSTATUS __stdcall NTOPENPROCESS
(
    OUT PHANDLE ProcessHandle,
    IN ACCESS_MASK AccessMask,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN PCLIENT_ID ClientId
);
NTOPENPROCESS * RealNtOpenProcess;


PEPROCESS EP;

// 自定义的NtOpenProcess函数
#pragma PAGECODE
extern "C" NTSTATUS __stdcall MyNtOpenProcess(
    OUT PHANDLE ProcessHandle,
    IN ACCESS_MASK DesiredAccess,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN PCLIENT_ID ClientId )
{
  NTSTATUS rc;
  HANDLE PID;

  KdPrint(("++++++++++++Entry MyNtOpenProcess int ++++++++++++++\n"));

  rc = (NTSTATUS)RealNtOpenProcess( ProcessHandle, DesiredAccess, ObjectAttributes, ClientId );

  if( (ClientId != NULL) )
  {
    PID = ClientId->UniqueProcess; 
    KdPrint(( "------------------------- PID=%d--------------\n",(int*)PID ));

    // 如果是被保护的PID,则拒绝访问,并将句柄设置为空
    if(PID == MyPID)
    {
      KdPrint(("被保护进程 MyPID=%d \n",(int)MyPID));
      //调试输出 类似C语言的 Printf

      ProcessHandle = NULL; //这个是关键
      rc = STATUS_ACCESS_DENIED; //这个返回值
      //PsLookupProcessByProcessId((ULONG)PID,&EP);
      EP=PsGetCurrentProcess(); 
      KdPrint((" ACESS Process Name --:%s-- \n",(PTSTR)((ULONG)EP+0x174)));
    }
  }
  return rc;
}

 

//HOOK 函数构建
#pragma PAGECODE
VOID Hook()
{
  ssdthook_flag=true;//设置被HOOK标志
  KdPrint(("++++HOOK START ++++-\n"));
  LONG *SSDT_Adr,SSDT_NtOpenProcess_Cur_Addr,t_addr;

  KdPrint(("驱动成功被加载中.............................\n"));
  //读取SSDT表中索引值为0x7A的函数
  //poi(poi(KeServiceDescriptorTable)+0x7a*4)
  t_addr=(LONG)KeServiceDescriptorTable->ServiceTableBase;
  SSDT_Adr=(PLONG)(t_addr+0x7A*4);
  SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr; 
  RealNtOpenAddress = *SSDT_Adr;
  RealNtOpenProcess = ( NTOPENPROCESS *)RealNtOpenAddress;

  KdPrint(( "真实的NtOpenProcess地址: %x\n",(int) RealNtOpenAddress ));
  KdPrint((" 伪造NTOpenProcess地址: %x\n", (int)MyNtOpenProcess ));

  __asm //去掉页面保护
  {
    cli
      mov eax,cr0
      and eax,not 10000h //and eax,0FFFEFFFFh
      mov cr0,eax
  }

  *SSDT_Adr= (ULONG)MyNtOpenProcess; // 【1990】SSDT HOOK的关键

  __asm
  {
      mov eax, cr0
      or eax, 10000h
      mov cr0, eax
    sti
  } 
  return;
}

 

//UnHook函数构建
//////////////////////////////////////////////////////
#pragma PAGECODE

VOID UnHook()
{
  ULONG Old_ssdt;
  Old_ssdt = (ULONG)KeServiceDescriptorTable->ServiceTableBase + 0x7A * 4;
  if (ssdthook_flag)
  {
    ssdthook_flag=false;
    __asm
    {
      cli
        mov eax, cr0
        and eax, not 10000h
        mov cr0, eax
    }

    // 还原SSDT
    *((ULONG*)Old_ssdt) = (ULONG)RealNtOpenAddress;

    __asm
    {
        mov eax, cr0
        or eax, 10000h
        mov cr0, eax
      sti
    }
    KdPrint(("UnHook还原SSDT OK \n"));
  }
  return;
}

  【500】自定义的NtOpenProcess函数 (函数MyNtOpenProcess) 的参数原型,可以参照ZwOpenProcess。∵这个函数才是导出的,可以通过【570】MSDN能够查到它的参数的原型

    ZC: 命名 hook的是内核的NtOpenProcess,参数原型却是参照的ZwOpenProcess...这个如何理解的通...

    ZC: 如何 看一个函数 是导出的 还是 不是导出的?看MSDN上能否查到该函数??看是否是 ntdll.dll等相关dll的导出函数?

    ZC: 之前的课程里面有 跟踪 OpenProcess的过程,忘了 ZwOpenProcess 和 NtOpenProcess 是哪个在上层那个在底层了,貌似记得 应用层和内核 都有一个函数叫NtOpenProcess...比较乱,需要回顾前面的内容...

  【655】假设 现在我们的hook已经完成了 SSDT里面的 NtOpenProcess的函数地址 指向了 我们自己的MyNtOpenProcess,那么就会对传入的参数进行过滤处理

  【745】首先,调用真正的函数NtOpenProcess,得到真实的返回的值(函数返回值 和 第1个OUT参数ProcessHandle 比较关键)

    【755】视频作者的理解:(1)、ProcessHandle返回的是进程的句柄,(2)、函数返回值 返回的应该是 可访问的权限

  【910】比较 CLIENT_ID.UniqueProcess 和 我们要保护的进程的PID

  【1170】改变它的返回值。这个返回值 STATUS_ACCESS_DENIED 表示 拒绝访问(不允许访问它)

  ZC: 这里的“PsLookupProcessByProcessId((ULONG)PID,&EP);”,这个函数的作用是啥?看代码的上下内容,猜测是 通过 进程PID获取EPROCESS结构的内容

  【1395】【1440】“PsGetCurrentProcess();” 用于取得 当前进程的 进程结构。该结构的 0x174偏移处 是 进程名

    【1505】打印出 调试信息:调用NtOpenProcess打开被我们保护的PID的进程的 进程名。(即 打印出调用NtOpenProcess的进程的进程名,且 NtOpenProcess打开的是我们保护的进程)

 

  【1595】具体看一下,具体 hook里面是怎么样实现的

    【1670】ZC: 注意这里的函数指针的用法

      【1715】typedef

    【1785】NTOPENPROCESS 是一个函数结构。“NTOPENPROCESS *RealNtOpenProcess;”才是具体定义了函数指针

    ZC: 这里并非是 去修改 NtOpenProcess里面的首条汇编指令 使之jmp到MyNtOpenProcess去执行。SSDT只是一张存放函数地址的表,要调用某函数的时候 只要取得某函数的地址然后push*?-->call就行了,因此 我们改SSDT表里面的对应元素使之变成我们的函数地址即可,无需对原始的NtOpenProcess做任何改动。

    【1950】替换 SSDT中NtOpenProcess对应的表项

    【2065】ZC: 内联hook(inline hook) 是指 在函数内部修改汇编代码 使之跳转到我们的函数执行 的hook?【2020】

    【2220】这种hook(这里的SSDT的hook),有点像IAT的hook

  【2310】unhook 就更简单了

    【2370】这里特意用了另外一种方式(ZC: 不是一个意思么...)

 

【2655】修改ctl_code.h 头文件

  【2680】按照 第30课 的代码,加了 hook_code 和 unhook_code

#ifndef CTL_CODE
  #pragma message("\n \n-----------EXE模式 Include winioctl.h ")
  #include<winioctl.h> //CTL_CODE ntddk.h wdm.h
#else
  #pragma message("-\n \n---------SYS模式NO Include winioctl.h ")
#endif


#define add_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER,FILE_ANY_ACCESS)
#define sub_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_NEITHER,FILE_ANY_ACCESS)
#define hook_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_NEITHER,FILE_ANY_ACCESS)
#define unhook_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_NEITHER,FILE_ANY_ACCESS)

  ZC: 见【5965】处,这里是用的第28课的代码,应该是 METHOD_BUFFERED,非METHOD_NEITHER

  【2760】统一 exe和sys 两边的 ctl_code.h 头文件

  【2830】修改SYS部分源代码

    【2850】添加 hook_code的处理

    【2923】添加 unhook_code的处理

    【3025】如果 没有手动的“UnHook();”(即 unhook_code)的话,需要在 卸载例程中调用“UnHook();”

    【3400】函数Hook() 中有一个 标识 ssdthook_flag,函数 UnHook() 会判断该标识

    【3610】MyPID(全局变量)的获取

      【3850】输出缓冲区,返回 int型值为1,表示 操作成功(ZC: 这样做?能不能通过 pIrp->IoStatus.Status 的值 来指明操作是否成功?)

#include "hook.h"

 1         case hook_code:
 2             {  //从buffer获取MyPid 
 3                 //获取缓冲区数据    a,b        
 4                 int * InputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;
 5                 _asm
 6                 {
 7                     mov eax,InputBuffer
 8                     mov ebx,[eax]
 9                     mov MyPID,ebx
10                          
11                 }
12                 int* OutputBuffer = (int*)pIrp->AssociatedIrp.SystemBuffer;
13                 _asm
14                 {
15                     mov eax,1
16                     mov ebx,OutputBuffer
17                     mov [ebx],eax //
18 
19                 }
20                info = 4;
21                 Hook();
22                 break;
23             }
24         case unhook_code:
25             {   UnHook();
26                 break;
27             }
 1 #pragma PAGECODE
 2 VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject)
 3 {
 4   PDEVICE_OBJECT pDev;//用来取得要删除设备对象
 5   UNICODE_STRING symLinkName; // 
 6   UnHook();
 7   if (ishook)
 8   {//unhook
 9 
10 
11   __asm //去掉页面保护
12   {
13       cli
14           mov eax,cr0
15           and eax,not 10000h //and eax,0FFFEFFFFh
16           mov cr0,eax
17 
18   }
19 
20  
21  pcur->E9= oldCode.E9;//1字节
22  pcur->JMPADDR= oldCode.JMPADDR;//4字节
23   __asm //恢复页保护
24   {
25       mov eax,cr0
26           or  eax,10000h //or eax,not 0FFFEFFFFh
27           mov cr0,eax
28           sti
29   }
30   } //end unhook
31   pDev=pDriverObject->DeviceObject;
32   IoDeleteDevice(pDev); //删除设备
33   
34   //取符号链接名字
35    RtlInitUnicodeString(&symLinkName,L"\\??\\My_DriverLinkName");
36   //删除符号链接
37    IoDeleteSymbolicLink(&symLinkName);
38  KdPrint(("驱动成功被卸载...OK-----------")); //sprintf,printf
39  //取得要删除设备对象
40 //删掉所有设备
41  DbgPrint("卸载成功");
42 }

 

【4130】修改EXE部分源代码

  【4210】函数“int Hook(...)”

  【4303】函数“int UnHook(...)”

  【4650】修改 main 中的代码

int Hook(HANDLE hDevice, ULONG pid) //pid 需要被保护进程ID
{
    
    int port[2];
    int bufret;
    ULONG dwWrite;
    port[0]=pid;
 
    
    DeviceIoControl(hDevice, hook_code , &port, 8, &bufret, 4, &dwWrite, NULL);
    return bufret;
    
}
int UnHook(HANDLE hDevice)
{
    
    int port[2];
    int bufret;
    ULONG dwWrite;    
    
    DeviceIoControl(hDevice, unhook_code , &port, 8, &bufret, 4, &dwWrite, NULL);
    return bufret;
    
}

int main(int argc, char* argv[])
{
    //add
    //CreateFile 打开设备 获取hDevice
    HANDLE hDevice = 
        CreateFile("\\\\.\\My_DriverLinkName", //\\??\\My_DriverLinkName
        GENERIC_READ | GENERIC_WRITE,
        0,        // share mode none
        NULL,    // no security
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL );        // no template
    printf("start\n");
    if (hDevice == INVALID_HANDLE_VALUE)
    {
        printf("获取驱动句柄失败: %s with Win32 error code: %d\n","MyDriver", GetLastError() );
        getchar();
        return -1;
    }

  ULONG pid;
  printf("请键入需要被保护的进程PID=");
  scanf("%d",&pid);
  Hook(hDevice,pid);

 int a=55;
 int b=33;
 int r=add(hDevice,a,b);
  printf("\n %d+%d=%d \n",a,b,r);
  getchar();
    return 0;
}

 

【4950】把 exe和sys 放到虚拟机中 测试

  【5105】输入PID之后蓝屏

  【5285】重启 虚拟机,运行WinDBG

  【5430】看到 断下的地方了

  【5455】WinDBG命令"r"

  【5460】WinDBG命令"reg" “Bad register error in 'reg'”

  【5495】WinDBG命令"r eax"

  【5515】eax值为0

  【5965】这里是用的第28课的代码,应该是 METHOD_BUFFERED,非METHOD_NEITHER(ZC: 从 输入输出缓冲区的获取方式,也应该看出来是 METHOD_BUFFERED,但是我没有注意到这一点...)

  【6145】重启虚拟机,重新生成 exe/sys,重新测试

    【6420】ZC:输入 PID,回车之后,exe就直接退出了...他也不管这个现象,继续使用CE来附加记事本进程。想起来了,这个exe就是 执行完直接退出的,不会停留...

      【6500】CE报错“打开进程错误”

 

【6570】这种驱动保护,只是比较初级的驱动保护,效果不是很明显。比如 它就不能对抗OD(OD的StrongOD的内核模式,第31课的【830】处),【6645】OD-->文件-->附加 还是看不到记事本的进程,打开了"内核模式",它要加载一个驱动,重新启动OD一次,此时 OD-->文件-->附加 就能附加 记事本的进程 了。

【6680】StrongOD也是对NtOpenProcess之类的内核函数 进行了深层的模拟,它自己模拟了相应的函数,或者说 它恢复了SSDT

  【6750】用工具来看一下  【6760】打开 KernelDetective

    【6805】看到 这里有一个修改,具体是什么样的修改 它没有给出来,这个是SSDT表的修改,这个红色的 是我们自己修改的

    ZC: 如何确定这个是我们的驱动修改的?

  【6900】我们推出我们的驱动 再来看一下,看 函数UnHook() 是否写进去了

    【6980】看到 SSDT确实写回去了

  【7105】再次测试 hook,看到 KernelDetective中"当前地址"被改为"0xBAD6E000"

  【7212】解除 我们的Hook的方案: (不是将我们的MyNtOpenProcess的汇编首指令改为JMP,而是) 找到 NtOpenProcess之后的je判断  直接改为

    【7235】“je short BAD6E09B”-->右击--> "汇编+写入"

    【7310】我们让这个跳转永远不实现,我们可以把它nop掉(ZC: 貌似烦了吧?应该是让它永远实现)

    【7355】或者我们让“je short BAD6E09B”上面的比较的两个值,永远不相等(“cmp dword ptr [ebp+14],0”)

      ZC: 这里我想到,可以直接修改 零标识位 吗?后来又想到: (1)、不知道是否允许改 零标识位,(2)、就算允许改 零标识位,那么也必须在cmp和je中间增加语句,这个貌似比较麻烦(需要跳到别的地方,改好 零标识位,再跳回来...)

    【7378】“je short BAD6E09B”-->右击--> 二进制 --> 用NOP填充

    【7435】测试,输入PID之后,直接蓝屏了,可能改的不对,这里不再讨论了

      ZC: 这里的蓝屏,是∵我们改错了的缘故吗?我只能确定的是,这样改了之后,所有的需要通过NtOpenProcess的调用都会失败...

 

2、

 

posted @ 2016-04-09 10:44  DebugSkill  阅读(1832)  评论(0编辑  收藏  举报