YJX_Driver_031_再谈SSDT_HOOK驱动保护原理

1、

再谈SSDT HOOK驱动保护原理
  A、初识内核进程相关结构
  B、内核函数PsGetCurrentProcess
  C、进程保护原理
  D、实例测试

 

【100】本节课先讲理论,下节课具体代码

【160】看一个实例(代码已经写好了)

  将 mini_ddk.sys 和 test_exe.exe 复制到 虚拟机,载入驱动

  【245】以一个记事本进程为例

  【300】启动 WinDBG

    【485】ZC: WinDBG 中 打印了很多信息...

  【500】∵ 被hook了之后,系统的速度变慢了,可以看到 虚拟机中的CPU已经被占满了

    ZC: 看这个显现,貌似有点死循环的意思啊...不像变慢啊...【575】WinDBG中也看到信息一直在打印出来,个人判断,肯定是写的代码有问题,貌似是 无休止的调用某函数的缘故。

  【580】我们把调试去掉(ZC: WinDBG里面打了 命令"g",貌似这个操作与"去掉调试"没有关系吧...它说的"把调试去掉"应该是指把test_exe.exe关掉/mini_ddk.sys卸载掉?)

  【685】ZC: 貌似虚拟机重启过了

  【795】此时,再用 CE 来附加 记事本进程,CE报错"打开进程错误",它已经被保护起来了

  【830】并非说 记事本进程 就绝对的 能够得到很好的保护了,如 OD里面有一些过保护的插件,它就可以过这个保护

    【885】打开 OD1.1(郁金香OD2010.exe),此时(OD-->文件-->附加)找不到 记事本的进程

      【923】OD-->插件-->StrongOD-->Options-->"*KernelMode"勾上 -->关闭OD

      【945】再次打开 OD  【990】此时(OD-->文件-->附加) 能看到 记事本进程,也能正常的附加

      【1010】OD-->反汇编窗口-->右击-->StrongOD-->Detach --> 关闭OD

    【1025】只是一个简单的演示

    【1130】CE5.4 里面 也有一个 内核模式: CE-->设置-->其他 --> 隐藏模式(内核模式),但是这个是 调试模式,不知道这个有没有用。∵ 虚拟机里面测试非常的不方便,就不再做再多的演示了,有兴趣自己测试

 

【1265】看一下原理

  对 函数NtOpenProcess 做了一个 SSDT的 HOOK,它不是一个内联的(ZC: 讲的是"内联的"吗?)

  【1290】一般来说比较强的保护的话,它都是一个内联的更深层次的保护,今天我们就不说内联HOOK了,说SSDT的HOOK

  【1340】内核里面的 NtOpenProcess 如下:

NTSTATUS NtOpenProcess(
  __out PHANDLE ProcessHandle,  // 【1360】这个是用来回传参数的
  __in   ACCESS_MASK DesiredAccess,  // 【1410】访问权限的掩码
  __in   POBJECT_ATTRIBUTES ObjectAttributes,
  __in_opt PCLIENT_ID ClientId
);

  【1450】MSDN里面查看看 OpenProcess,返回值 对应 函数NtOpenProcess的参数ProcessHandle

HANDLE OpenProcess(
  DWORD dwDesiredAccess, // 【1485】对应函数NtOpenProcess的参数DesiredAccess
  BOOL bInheritHandle, // 【1680】对应函数NtOpenProcess的参数ObjectAttributes
  DWORD dwProcessId
);

    【1700】OpenProcess的参数dwProcessId 对应的不是 函数NtOpenProcess的参数ClientId

      【1740】包含这样一个结构 dt _CLIENT_ID  【1760】这个结构,在OD里面也能看到,【1775】在WInDBG里面能够看到(ZC: 到底 OD里能不能看到?)

      【1865】vs2003里面看到 ntddk.h中 有该结构的定义

typedef struct _CLIENT_ID
{
  HANDLE UniqueProcess;//进程PID
  HANDLE UniqueThread; //线程PID
} CLIENT_ID;

        【1930】根据传入的值,这个值(CLIENT_ID.UniqueProcess)如果是我们要打开的进程,做一个简单的过滤,就直接返回 函数NtOpenProcess的参数ProcessHandle 的值 为空。我们对 OpenProcess 进行SSDT的HOOK 进入我们自己的函数,【2060】自己构建一个假的函数 MyNtOpenProcess()

MyNtOpenProcess()
{
  if 要打开进程是我们要保护的
  {
    ProcessHandle=NULL
  }
  else
  {
    ProcessHandle = NtOpenProcess()//HANDLE hProcess
  }
}

          这样就起到一个简单的保护作用

  【2250】MSDN查看 WriteProcessMemory

  【2318】也就是说 我们的战场就是在 OpenProcess这里

  【2430】看一下这个结构 "dt _eprocess"

    打开虚拟机,启动 WinDBG

    【2495】∵ 进程相关的结构,在程序(vs2003)里面是看不到的,∵该结构是一个未公开的一个结构

      【2558】VS2003中 _EPROCESS  【2565】此时 提示“未定义符号"_EPROCESS"”

      ZC: 根据 应用层的结构-->得到内核未公开的结构名-->在WinDBG中用"dt"命令 一点一点往下挖

    【2595】WinDBG 命令"dt _eprocess"

      【2665】_EPROCESS.DebugPort 比较重要,常用来 反调试

      【2685】_EPROCESS.ImageFileName 进程名。也可以通过过滤进程名来保护进程

    【2753】WinDBG 命令"dt _CLIENT_ID"

 

【2925】看一下 大致的代码

  【2960】

// 定义一下NtOpenProcess的原型
extern "C" typedef NTSTATUS (__stdcall NTOPENPROCESS)
(
    OUT PHANDLE ProcessHandle,
    IN ACCESS_MASK AccessMask,
    IN POBJECT_ATTRIBUTES ObjectAttributes,
    IN PCLIENT_ID ClientId
);
NTOPENPROCESS * RealNtOpenProcess; // 用于存放真实的 NtOpenProcess函数 的地址

2、

dd nt!KeServiceDescriptorTable

 

typedef struct _CLIENT_ID
{
  HANDLE UniqueProcess;//进程PID
  HANDLE UniqueThread; //线程PID
} CLIENT_ID;

dt _CLIENT_ID
dt _eprocess
dt -v -r _eprocess (加上-v -r显示详细结构)



#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <NTDDK.h> //这里包含需要用C方式编译的头文件
#ifdef __cplusplus
}
#endif
#include <windef.h>


bool ssdthook_flag=false;
extern "C" extern PServiceDescriptorTable KeServiceDescriptorTable;

//extern "C" NTSTATUS __stdcall PsLookupProcessByProcessId( IN ULONG ulProcId, OUT PEPROCESS *pEProcess );



PEPROCESS EP;

// 真实的函数地址
ULONG RealServiceAddress;

HANDLE MyPID; //要保护的进程ID

// 自定义的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",MyPID ));
      ProcessHandle = NULL; //这个是关键
      rc = STATUS_ACCESS_DENIED;
      //PsLookupProcessByProcessId((ULONG)PID,&EP);
      EP=PsGetCurrentProcess();
      KdPrint(("AAAAAAAAAAAAAAAAAAAAAAAAAAAAA ACESS Process Name --:%s-- AAAAAAAAAAAAAAAAAA \n",(PTSTR)((ULONG)EP+0x174)));
      KdPrint(( "++++++++++++++++++++++++++++=======================================================+++++++++++++++++++++++\n" ));
    }
  }
return rc;
}

 

 

#pragma PAGECODE

VOID 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;
  KdPrint(("当前ServiceTableBase地址为%x \n",t_addr));
  SSDT_Adr=(PLONG)(t_addr+0x7A*4);
  KdPrint(("当前t_addr+0x7A*4=%x \n",SSDT_Adr));
  SSDT_NtOpenProcess_Cur_Addr=*SSDT_Adr;

  RealServiceAddress = *SSDT_Adr;

  RealNtOpenProcess = ( NTOPENPROCESS *)RealServiceAddress;
  KdPrint(( "真实的NtOpenProcess地址: %x\n",(int) RealServiceAddress ));
  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;
  __asm
  {
      mov eax, cr0
      or eax, 10000h
      mov cr0, eax
    sti
  }
  return;
}

 

//////////////////////////////////////////////////////

#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)RealServiceAddress;
    __asm
    {
        mov eax, cr0
        or eax, 10000h
        mov cr0, eax
      sti
    }
    KdPrint(("UnHook还原SSDT OK \n"));
  }
  return;
}

#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)

 

posted @ 2016-04-08 15:28  DebugSkill  阅读(310)  评论(0编辑  收藏  举报