首页
社区
课程
招聘
[旧帖] Hook NtOpenProcess 来实现简单的进程保护 0.00雪花
发表于: 2011-12-3 19:49 4671

[旧帖] Hook NtOpenProcess 来实现简单的进程保护 0.00雪花

2011-12-3 19:49
4671

自学编程也有大半年了, 直到现在还是感觉自己没学到些什么. 这段时间在学习驱动, 整理了一下自己写的 Hook NtOpenProcess 来实现简单的进程保护, 希望能申请到邀请码.

   都是利用前人的经验来实现的, 不过总算是自己写的. 过程偶也不说了, 看注释吧, 够详细的了.

   系统  Win7 SP1 专业版, 编译器 VS2010 +  WDK 7.1.0.7600  虚拟机环境 VM7.0 WinXP Sp3  项目设置属性在源代码中的  Driver_template.props

/*----------------------------------------------------------------------
*  文件名:  Hook_NtOpenProces_Driver.h  
*  文件描述: 演示应用程序通过驱动  Hook NtOpenProcess 的实现.
*            此文件用于定义必须的结构和函数声明
*  ------------------------------------------------------------------*/


#pragma   once            // 保证头文件只被编译一次

#ifdef  __cplusplus      // __cplusplus 这个宏的含义是; 如果这是一段 CPP 的代码, 那么加入
extern  "C"                  // "C" { 和 } 处理其中的代码.   
{
#endif
#include <ntddk.h>       // 包含着内核下数据结构的库文件
#ifdef  __cplusplus
}
#endif

#define   PAGECODE  code_seg("PAGE")    // 此宏后的代码放入页面文件
#define   INITCODE  code_seg("INIT")      //  此宏后的代码放入初始化

#define   PAGEDATA  data_seg("PAGE")   // 将代码载入非分页文件的节中
#define   INITDATA  data_seg("INIT")     // 此宏后的代码放入初始化的节中

/*----------------------------------------------------------------------------------------------------
* 下面定义的是方便在驱动程序中使用的各种数据结构或者宏定义
* ----------------------------------------------------------------------------------------------------*/

#define  arraysize(p) (sizeof(p) / sizeof((p)[0]))    // 用于 DriverEntry() 中对 对应 IRP 类型的调用函数

// 设备扩展结构, 用于存放设备的相关信息, 以便在不同的函数中调用
typedef  struct   _DEVICE_EXTENSION {
        PDEVICE_OBJECT   pDevice;                     // 指向设备对象的指针,
        UNICODE_STRING   ustrDeviceName;        // 设备的名称
        UNICODE_STRING   ustrSymLinkName;      // 设备的称号连接名称
} DEVICE_EXTENSION,  *PDEVICE_EXTENSION;

/*--------------------------------------------------------------------------------------
*        下面声明的函数例程,  是驱动程序中所必需的函数例程
*---------------------------------------------------------------------------------------- */

// 创建设备的函数例程
NTSTATUS   CreateDevice (IN  PDRIVER_OBJECT  pDriverObject);

// 设备卸载例程
VOID   UnloadDevice (IN  PDRIVER_OBJECT  pDriverObject);

// 通用派遣函数例程, 此例程不实现任何的功能
NTSTATUS   IRP_Routine  (IN  PDEVICE_OBJECT  pDevObj, IN  PIRP  pIrp);

/*-----------------------------------------------------------------------------------------
*        下面是派遣函数中所用到的宏定义和函数声明
*-----------------------------------------------------------------------------------------*/
// 定义 CTL_CODE 控制码, 用于应用程序使用  DeviceIoControl 方式的与驱动程序交互
// 四个参数分别对应的是: 设备对象的类型,  IOCTL 控制码, 操作模式(读写), 访问权限
#define  IOCTL_BUFFER  CTL_CODE (FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)   //缓冲区模式

// [IRP_MJ_DEVICE_CONTROL] 的派遣函数例程
NTSTATUS  DeviceIoControl_Buffer (IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp);

/*------------------------------------------------------------------------------------------
*         下面是对 SSDT 进行 Hook 所必须的宏定义, 结构, 及函数声明
*------------------------------------------------------------------------------------------*/

// 定义 SSDT 导出表的结构体,  此结构和 ServiceDescriptorTable 的结构一致
#pragma  pack(1)    // 对齐单位, 以一个字节对齐
typedef  struct  _SystemServiceEntry {
        ULONG  *ServiceTableBase;          // 指向系统服务函数地址表的基地址的指针 (即 SSDT 的首地址)
        ULONG  *ServiceCounterTableBase;   // 包含着 SSDT 中每个服务被调用次数的计数器
        ULONG  NumberOfServices;           // SSDT 中描述的服务的总数, 每个服务的长度为 4 字节
        ULONG  *ParamTableBase;            // 包括每个系统服务参数字节数的基地址
} SSDT_Entry, *PSSDT_Entry;
#pragma   pack()

// 为了实现可以直接导出 SSDT 中对应函数的当前地址, 必须使用上面定义的结构以指针类型来导出表
extern  "C"  PSSDT_Entry  KeServiceDescriptorTable;

/* ---------------------------------------------------------------------------------------------------
*         此区域为实现 HOOK 而定义的宏
*---------------------------------------------------------------------------------------------------*/

// 获取 Zw 系列函数的当前地址的宏.  
// 参数 _FuncName: Zw系列的函数,  
// 注意这里必须使用 -> 的指向类型

#define   GetSystemService(_FuncName)\
        KeServiceDescriptorTable->ServiceTableBase[*(PULONG]((PUCHAR) _FuncName+1)]

// 查询 Zw 系列函数在 SSDT 中的索引号的宏.
// 参数 _Function : Zw 系列的函数

#define   GetServiceIndex(_Function) (*(PULONG)((PUCHAR)_Function+1))

// 实现 Hook 的宏 通过 InterlockedExchange 来交换两个函数的地址.
//  _Function  :  Zw* 形式的函数.  此处是通过 NTSYSAPI 重定义一个 ZwOpenProcess         
//  _Hook      :  自己构造的 Hook 函数的地址          
//  _Orig      :  原 SSDT 表中被 Hook 的函数地址.
//  _OrigType  :  原始函数的类型.            
// _MappedSSDTBase:  MDL 映射后指向 SSDT 首地址的指针.

#define   HOOK_SSDT(_Function, _Hook, _Orig, _OrigType, _MappedSSDTBase)\
        _Orig = (_OrigType) InterlockedExchange ((PLONG)&_MappedSSDTBase [GetServiceIndex(_Function)], (LONG)_Hook)

// 用于解除 HOOK SSDT 的宏,  通过 InterlockedExchange 来交换两个函数的地址.
//  _Function  :  Zw* 形式的函数.  此处是通过 NTSYSAPI 重定义一个 ZwOpenProcess         
//  _Orig      :  原 SSDT 表中被 Hook 的函数地址.
// _MappedSSDTBase:  MDL 映射后指向 SSDT 首地址的指针.

#define  UNHOOK_SSDT(_Function, _Orig, _MappedSSDTBase)\
        InterlockedExchange((PLONG) &_MappedSSDTBase[GetServiceIndex(_Function)], (LONG)_Orig)

/*        ----------------------------------------------------------------------------------------------
*          此区域为上面实现的宏, 而所必须定义的函数
*-------------------------------------------------------------------------------------------------*/

// 重定义 ZwOpenProcess 函数, 用于保存 SSDT 中原来的 NtOpenProcess 函数的地址. 用于上面宏中参数 _Function 中调用
//  前缀使用 NTSYSAPI 宏.  NTSYSAPI 的定义:   __declspec(dllimport)
NTSYSAPI  NTSTATUS  NTAPI  ZwOpenProcess ( __out PHANDLE ProcessHandle,
        __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes, __in_opt PCLIENT_ID ClientId );

// 定义一个指向上面重定义的  ZwOpenProcess 函数 的函数指针,  用于上面宏中参数 _OrigType 中调用
typedef  NTSTATUS  (*_NtOpenProcess)( __out PHANDLE ProcessHandle,
        __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes, __in_opt PCLIENT_ID ClientId );

// 定义一个 *_ZwOpenProcess 类型的对象 , 用于上面宏中参数  _Orig 中调用
_NtOpenProcess  Old_ZwOpenProcess;

// 定义一个用于 Hook  NtOpenProcess 的函数,         用于上面宏中参数  _Hook 中调用         此处函数的实现为保护指定进程
LONG  Pid = 0;  // 用于存储用户层应用程序传入的 Pid , 用于在 MyOpenProcess 函数中进行处理
NTSTATUS  MyOpenProcess (OUT PHANDLE ProcessHandle,
        IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId);

/*------------------------------------------------------------------------------------
*        此区域实现二个函数, 一个用于 Hook SSDT , 一个用于恢复 Hook SSDT
*------------------------------------------------------------------------------------*/

// 用于解除 SSDT 的页面保护. 并对 SSDT 进行 Hook   
VOID  Hook_SSDT_MDL ();

// 用于解除 SSDT 的页面保护. 并恢复 被 Hook 的 SSDT
VOID  Resume_SSDT_MDL ();


/*----------------------------------------------------------------------------
*        Hook_NtOpenProces_Driver.cpp   函数实现代码
*----------------------------------------------------------------------------*/

#include "Hook_NtOpenProces_Driver.h"


/*----------------------------------------------------------------------------------------------
* 函数名称: DriverEntry
* 功能描述: 驱动程序的入口函数. 用于初始化驱动, 定位和申请硬件资源, 创建内核对象
* 参数列表: IN 驱动对象指针         pDriverObject  从 I/O 管理器中传进来的驱动对象
*           IN PUNICODE 字符串指针  pRegistryPath  驱动程序在注册表中的路径
* 返回  值: 返回初始化驱动状态
* --------------------------------------------------------------------------------------------*/

#pragma  INITCODE
extern  "C" NTSTATUS  DriverEntry (IN  PDRIVER_OBJECT  pDriverObject,  IN  PUNICODE_STRING  pRegistryPath)
{
        NTSTATUS  status;                         // 用于获取操作是否成功, 并作为函数的返回值使用
        KdPrint(("Enter  DriverEntry \n"));       // log

        // 注册卸载函数的地址.
        pDriverObject->DriverUnload = UnloadDevice;
                                     
        // 注册对应 IRP 派遣函数的地址, 注意, 如果单独注册派遣函数, 记得要注册 IRP_MJ_CREATE 的例程,
        for (int i = 0; i < arraysize(pDriverObject->MajorFunction); i++)
                pDriverObject->MajorFunction[i] = IRP_Routine;

        pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControl_Buffer;

        // 创建设备对象
        status = CreateDevice (pDriverObject);

        // SSDT hook
        Hook_SSDT_MDL();

        return status;
}


/*------------------------------------------------------------------------------------------------
* 函数名称: CreateDevice
* 功能描述: 初始化设备对象 (即创建并初始化)
* 参数列表: 驱动对象指针    pDriverObject : 从 I/O 管理器中传进来的驱动对象
* 返回  值: 返回初始化状态
* -----------------------------------------------------------------------------------------------*/

#pragma   INITCODE       
NTSTATUS  CreateDevice (IN PDRIVER_OBJECT pDriverObject)
{
        NTSTATUS  status;     // 用于获取操作是否成功, 并作为函数的返回值使用

        //------------------  1. 必须定义的类型对象,  创建设备名称并初始化该名称  -----------------

        PDEVICE_OBJECT     pDevObj;        // 创建的设备对象
        PDEVICE_EXTENSION  pDevExt;        // 定义一个设备扩展结构的对象, 用于保存设备的相关信息

        UNICODE_STRING  DevName;           // 创建的设备对象的名称
        RtlInitUnicodeString (&DevName, L"\\Device\\MyDevice");

        //------------------  2. 创建设备对象, 判断设备是否成功创建, 设置和保存设备的相关信息 -------

        status = IoCreateDevice (pDriverObject,               // 创建此设备对象的驱动对象
                                     sizeof(DEVICE_CAPABILITIES), // 指定设备扩展的大小, I/O 管理器负责分配及关联   
                                                         &(UNICODE_STRING)DevName,    // 设备对象的名称
                                                         FILE_DEVICE_UNKNOWN,         // 生成的设备类型, 此处为未知设备
                                                         0,                           // 设置设备对象的特征, 一般为 0
                                                         TRUE,                        // 设置此设备对象是否为内核模式下使用, 一般为 TRUE
                                                         &pDevObj);                   //  I/O 管理器负责创建这个设备对象, 并返回设备对象的地址

        // 判断设备是否成功创建
        if(!NT_SUCCESS(status))
        {
                KdPrint((" Error: 设备创建失败 \n"));
                return  status;
        }

        // 设置和保存设备的相关信息
        //pDevObj->Flags = DO_BUFFERED_IO;    // 设置此设备的读写方式, 此处为缓冲区方式
       
        pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;  // 将创建的设备的扩展子域映射到我们定义的扩展结构中
        pDevExt->pDevice = pDevObj;                             // 定义映射此设备的指针到自定义的扩展结构中
        pDevExt->ustrDeviceName = DevName;                      // 保存此设备的名称到自定义的扩展结构中

        //----------------------- 3. 创建设备的符号链接, 和设备进行绑定, 及判断绑定是否成功  -------------

        UNICODE_STRING  SymLinkName;                               // 设备的符号链接名称

        // 初始化此符号链接名称, 注意要和生成的驱动的名字一致, 否则将导致驱动无法卸载.
        RtlInitUnicodeString (&SymLinkName, L"\\??\\Hook_NtOpenProces_Driver");   

        pDevExt->ustrSymLinkName = SymLinkName;                    // 保存此设备链接符号名到自定义的扩展结构中

        status = IoCreateSymbolicLink (&SymLinkName, &DevName);    // 将创建的设备和此符号链接进行绑定

        // 判断符号链接和设备绑定是否成功
        if(!NT_SUCCESS(status))
        {
                KdPrint((" 设备和称号链接绑定失败 \n"));
                IoDeleteDevice (pDevObj);                              // 绑定失败则删除此设备对象
                return status;
        }

        KdPrint(("设备创建成功 \n"));                              // log 信息
        return STATUS_SUCCESS;
}

/*--------------------------------------------------------------------------------------------------------------
* 函数名称: UnloadDevice
* 功能描述: 负责驱动程序的卸载操作
* 参数列表: IN 驱动对象指针      pDriverObject : 传递进来的驱动对象
* 返回  值: 返回状态, 成功或失败
* ------------------------------------------------------------------------------------------------------------*/

#pragma   PAGECODE
VOID  UnloadDevice (IN PDRIVER_OBJECT  pDriverObject)
{
         PDEVICE_OBJECT  pNextObj;    // 驱动对象的设备对象子域是一个链表, pNextObj 指向链表中的下一个设备对象

         // 解除 SSDT Hook
         Resume_SSDT_MDL();

         pNextObj = pDriverObject->DeviceObject;    // 由传入的驱动对象得到此对象的设备链表的首个设备对象.

         while (pNextObj != NULL)     // 遍历驱动对象中的设备链表
         {
                 // 将设备对象的扩展结构映射到我们定义的扩展结构中, 以获取相关信息
                 PDEVICE_EXTENSION  pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;

                 UNICODE_STRING  SymLinkName = pDevExt->ustrSymLinkName;    // 获取将设备对象的符号链接并删除
                 IoDeleteSymbolicLink(&SymLinkName);

                 pNextObj = pNextObj->NextDevice;                          //  删除设备并从链表中获取下一个设备对象
                 IoDeleteDevice(pDevExt->pDevice);
         }

         KdPrint(("设备卸载成功 \n"));
}

/*-----------------------------------------------------------------------------
* 函数名称: IRP_Routine
* 功能描述: 通用派遣函数例程, 此例程不实现任何的功能
* 参数列表: IN 设备对象指针:   pDevObj :  传入的设备对象
*           IN IRP 指针           pIrp :  从 I/O 管理器请求包, 以于判断 IRP 的类型
* 返回  值: 返回操作状态
* ----------------------------------------------------------------------------*/

#pragma  PAGECODE
NTSTATUS   IRP_Routine  (IN  PDEVICE_OBJECT  pDevObj, IN  PIRP  pIrp)
{
        NTSTATUS  status = STATUS_SUCCESS;    // 设置操作返回状态为成功

        pIrp->IoStatus.Status = status;       // 设置 IRP 完成状态

        pIrp->IoStatus.Information = 0;       // 此处不需要读写, 所以设置 IRP 操作字节数为 0

        IoCompleteRequest(pIrp, IO_NO_INCREMENT);  // 设置 IRP 请求成功

        return status;
}

/*---------------------------------------------------------------------------------------
* 函数名称: DeviceIoControl_All
* 功能描述:  应用程序以 IOCTL 方式的缓冲区模式来读写设备  对应 IRP [IRP_MJ_DEVICE_CONTROL]
* 参数列表: IN 设备对象指针      pDriverObject : 传递进来的驱动对象
*           IN  IRP 指针         pIrp: 指向 IRP 栈顶的指针
* 返回  值: 返回状态, 成功或失败
* -------------------------------------------------------------------------------------*/

#pragma PAGECODE
NTSTATUS  DeviceIoControl_Buffer (IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp)
{
        NTSTATUS  status = STATUS_SUCCESS;    // 设置操作返回状态为成功

        //-----------------  1. 获得必须的相关信息  ---------------------------------------------

        PIO_STACK_LOCATION  stack = IoGetCurrentIrpStackLocation(pIrp);      // 获得对应  IPR 的当前堆栈指针
        ULONG  CBIn = stack->Parameters.DeviceIoControl.InputBufferLength;   // 获得系统输入缓冲区的长度
        ULONG  CBOut = stack->Parameters.DeviceIoControl.OutputBufferLength; // 获得系统输出缓冲区的长度

        ULONG  IOCTL_Code = stack->Parameters.DeviceIoControl.IoControlCode; // 获得应用程序的 IOCTL 控制码

        //-----------------  2. 检测和处理过程  -----------------------------------------------------

        ULONG  info = 0;                       // 用于指示派遣函数需要实际操作的字节数

        if(IOCTL_Code == IOCTL_BUFFER)         // 如果获得的应用程序的控制码和驱动程序的控制码一致
        {
                ULONG *InputBuffer = (ULONG *)pIrp->AssociatedIrp.SystemBuffer;   // 指向系统缓冲区的指针

                // 获得系统缓冲区输入的数据, 即应用程序对驱动程序输入的数据
                Pid = (LONG)*InputBuffer;         // 此处是保存用户层应用程序传入的 Pid 值  Pid 为全局变量

                ULONG *OuputBuffer = (ULONG *)pIrp->AssociatedIrp.SystemBuffer;    // 指向系统缓冲区的指针

                // 向系统缓冲区输出数据, 即让应用程序从驱动程序中读取的数据

                info = CBOut;    // 指示派遣函数需要操作的字节数
        }

        /*-------------------  3. 设置 IRP 的完成及返回状态 ----------------------------------*/

        pIrp->IoStatus.Status = status;            //设置 IRP 的完成状态为成功
        pIrp->IoStatus.Information = info;         //设置 IRP 实际操作的字节数

        IoCompleteRequest(pIrp, IO_NO_INCREMENT);  // 指示 IRP 成功完成

        return status;
}

/*--------------------------------------------------------------------------------------------
*          临时区域;  此驱动程序的应用函数
*---------------------------------------------------------------------------------------------- */

// 对 NtOpenProcess 进行 Hook 的实现函数 , 此处实现的功能是对指定 Pid 的进程进程进行保护
NTSTATUS  MyOpenProcess (OUT PHANDLE ProcessHandle,
        IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId)
{
        NTSTATUS  Status = STATUS_SUCCESS;      // 设置操作状态为成功

        // 根据全局变量 Pid 判断是否要实现此函数的功能, 即对此 Pid 的进程进行保护
        if(ClientId->UniqueProcess == (HANDLE)Pid)
        {
                // 可以显示是那个进程调用此 PID (不是必须的)
                // PEPROCESS   EP;
                // EP = PsGetCurrentProcess();                   // 0x174 为 _EPROCESS 结构中的偏移位置, 存储进程的名称
                // KdPrint((" 试图访问此进程的进程名称为 %s \n", (PTSTR)(ULONG)EP+0x174));

                ProcessHandle = NULL;            // 如果是传入的 PID  OUT 参数 ProcessHandle 为空, 便达到保护的目的
                return  STATUS_ACCESS_DENIED;    // 并返回状态为拒绝访问.
        }   

        // 如果 PID 不是所指定的, 便交还原函数进行处理
        Status = Old_ZwOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);

        return  STATUS_SUCCESS;
}

// 用于解除 SSDT 的页面保护. 并对 SSDT 进行 Hook   
#pragma  PAGECODE
VOID  Hook_SSDT_MDL ()
{
        PMDL   pMdlSSDT = NULL;      // 用来创建原始的 SSDT 地址, 映射到我们的域中, 来达到解除页面保护
        PVOID  *MappedSSDTBase = 0;  // 存放解除页面保护后指向的 SSDT 首地址的指针

        // 调用 MmCreateMdl 创建一个映射 SSDT 的 MDL 结构的虚拟内存空间
        pMdlSSDT = MmCreateMdl (NULL, KeServiceDescriptorTable->ServiceTableBase,
                                    KeServiceDescriptorTable->NumberOfServices * 4);

        if(!pMdlSSDT)          // 检测创建是否成功
                KdPrint(("创建 MDL 失败 \n"));
       
        MmBuildMdlForNonPagedPool (pMdlSSDT);  // 将上面创建的 MDL 分配在非分页页面中, 因为 SSDT 在非分页页面中

        // 改变 MDL 的标记来实现可读写
        pMdlSSDT->MdlFlags = pMdlSSDT->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;

        // 锁定此 MDL 的虚拟内存空间, 以防止系统对其释放, 并返回指向此 MDL 首地址的指针
        MappedSSDTBase = (PVOID *) MmMapLockedPages (pMdlSSDT, KernelMode);

        // 检测返回首地址的指针是否成功, 成功便对 SSDT 进行 Hook
        if(MappedSSDTBase != 0)
        {
                HOOK_SSDT(ZwOpenProcess, MyOpenProcess, Old_ZwOpenProcess, _NtOpenProcess, MappedSSDTBase);
                KdPrint(("SSDT HOOK 成功 \n"));
        }

        // 对 MDL 分配的内存空间进行释放
        if(pMdlSSDT != NULL)
        {
                MmUnmapLockedPages (MappedSSDTBase, pMdlSSDT);
                IoFreeMdl(pMdlSSDT);
        }

}

// 用于解除 SSDT 的页面保护. 并恢复 被 Hook 的 SSDT
#pragma  PAGECODE
VOID  Resume_SSDT_MDL ()
{
       
        PMDL   pMdlSSDT = NULL;      // 用来创建原始的 SSDT 地址, 映射到我们的域中, 来达到解除页面保护
        PVOID  *MappedSSDTBase = 0;  // 存放解除页面保护后指向的 SSDT 首地址的指针

        // 调用 MmCreateMdl 创建一个映射 SSDT 的 MDL 结构的虚拟内存空间
        pMdlSSDT = MmCreateMdl (NULL, KeServiceDescriptorTable->ServiceTableBase,
                KeServiceDescriptorTable->NumberOfServices * 4);

        if(!pMdlSSDT)          // 检测创建是否成功
                KdPrint(("创建 MDL 失败 \n"));

        MmBuildMdlForNonPagedPool (pMdlSSDT);  // 将上面创建的 MDL 分配在非分页内存中, 因为 SSDT 在非分页内存中

        // 改变 MDL 的标记来实现可读写
        pMdlSSDT->MdlFlags = pMdlSSDT->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;

        // 锁定此 MDL 的虚拟内存空间, 以防止其他进程来访问, 并返回指向此 MDL 首地址的指针
        MappedSSDTBase = (PVOID *) MmMapLockedPages (pMdlSSDT, KernelMode);

        // 检测返回首地址的指针是否成功, 成功便对 SSDT 进行 Hook
        if(MappedSSDTBase != 0)
        {
                UNHOOK_SSDT(ZwOpenProcess, Old_ZwOpenProcess, MappedSSDTBase);
                KdPrint(("恢复 SSDT HOOK 成功 \n"));
        }

        // 对 MDL 分配的内存空间进行释放
        if(pMdlSSDT != NULL)
        {
                MmUnmapLockedPages (MappedSSDTBase, pMdlSSDT);
                IoFreeMdl(pMdlSSDT);
        }

}

// 用控制台实现对此驱动进行调用的源代码

// Hook_NtOpenProcess_Test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>       // 为了返回错误信息
#include <winioctl.h>      // 使用 CTL_CODE 必须加入的头文件
#include <iostream>

using namespace std;

// 此定义是用于  DeviceIoControl 的第一种 (缓冲区模式读写方式)  
// 注意: 要和在驱动程序中的定义的 CTL_CODE 一致.
// 四个参数分别对应的是: 设备对象的类型, IOCTL 码, 操作模式(读写), 访问权限
#define  IOCTL_BUFFER  CTL_CODE (\
        FILE_DEVICE_UNKNOWN,\
        0x800,\
        METHOD_BUFFERED,\
        FILE_ANY_ACCESS)

int _tmain(int argc, _TCHAR* argv[])
{
        // 打开设备句柄,
        HANDLE  hDevice = CreateFile (
                L"\\\\.\\Hook_NtOpenProces_Driver",  // 指向文件名的指针, 即驱动程序的设备符号链接名称
                GENERIC_WRITE | GENERIC_READ,       // 访问模式 (写/读)
                0,                                  // 共享方式,  0 代表不共享
                NULL,                               // 指向文件安全属性的指针, 此处为空
                OPEN_EXISTING,                      // 创建设置, 此处为打开现有的
                FILE_ATTRIBUTE_NORMAL,              // 文件属性, 此处为默认属性
                NULL );                             // 如果不为 NULL, 则指定一个文件句柄

        if (hDevice == INVALID_HANDLE_VALUE)    // 如果打开失败
        {
                printf("未能获得文件句柄, 打开 DispatchDemo 失败, 错误代码 %d \n ", GetLastError());
        }

        long Pid = 0;       //存储用户输入的 Pid  用于 DeviceIoControl 的输入缓冲区

        cout << "控制台版进程保护器\n" << "请输入需要保护的进程PID:";
    cin >> Pid;   

        UCHAR  OutputBuffer[20];                 //作为输出缓冲区 (即应用程序对设备的读取操作)
        DWORD  dwOutput;                         // 计数输出的字符数 (Read)

   DeviceIoControl(
                hDevice,               // 已经打开的设备句柄
                IOCTL_BUFFER,          // 自定义的控制码    //*注意*: 此应该为定义的直接模式的 IOCTL
                &Pid,                  // 输入缓冲区
                sizeof(Pid),           // 输入缓冲区的大小
                OutputBuffer,          // 输出缓冲区
                sizeof(OutputBuffer),  // 输出缓冲区的大小
                &dwOutput,            // 实际返回的字节数
                NULL);

   // 关闭文件句柄
   CloseHandle(hDevice);

   system("PAUSE");
        return 0;
}


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 6
支持
分享
最新回复 (16)
雪    币: 239
活跃值: (133)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
2
我一直有个疑惑,WDK里面的函数,有什么中文文档可以直接查看里面函数的使用方法
2011-12-3 20:03
0
雪    币: 678
活跃值: (101)
能力值: ( LV2,RANK:150 )
在线值:
发帖
回帖
粉丝
3
应该都是只有英文的帮助文档,就像MSDN一样。其实这些英文理解起来应该也没有太大问题吧。具体函数是如何实现的可以参考WRK和ReactOS中的实现。另外可以Windbg查看。
2011-12-3 20:26
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
虽然注释的很详细,但个人感觉代码并不严谨。。(你没有做参数检测,存在被本地拒绝服务的可能,造成了一定的内核不稳定)
2011-12-3 20:27
0
雪    币: 43
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
     谢谢指点, 偶的水平菜的很, 因为是自学的编程, 加上基础差, 英文基本是学编程后才达到能看懂一点代码的水平.  这里基本是参照张帐那本驱动开发大全的驱动流程.

     在虚拟机试过好多次, 没有出现过被拒绝服务的情况. 偶以为这个参数检测是在装载驱动的程序里进行判断的? 如果在驱动中要检测的话, 是检测什么参数? 谢谢了.
2011-12-3 20:43
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
你的这句代码

if(ClientId->UniqueProcess == (HANDLE)Pid)
以及
ProcessHandle = NULL;

并没有检查应用层的参数,如果应用层将此地址恶意设置为内核地址比如0xFEDCBA98的话应该会蓝屏吧。。 如果恶意应用层恶意PHANDLE 地址会改写内核地址为0吧。。。

ps:这方面我也非专业,只是个人拙见。
2011-12-3 21:08
0
雪    币: 43
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
不太明白,  Pid 的数据类型不是 LONG 么? 通用寄存器处理这个数据时, 是将它当做数据值来处理的吧. 就算这个数据值为 0xFEDCBA98 , 寄存器也不会把这个当做一个内存地址来进行处理吧?
2011-12-3 21:19
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
2011-12-3 21:30
0
雪    币: 43
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
typedef struct _CLIENT_ID {
    HANDLE UniqueProcess;
    HANDLE UniqueThread;
} CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;

typedef void *HANDLE;

哦,  HANDLE 是无类型的指针,  如果  UniqueProcess == 0xFEDCBA98

那么就会到 0xFEDCBA98 处的内存地址去取值? 结果 -.-    谢谢指点
2011-12-3 21:31
0
雪    币: 43
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
...这个代码是半个月前写的,  那时还不懂用异常处理,  和用 IsBadReadPtr 检测内存地址是否可读写.  前二天才知道用异常处理和 IsBadReadPtr 来进行检测,  之前看 C++ 的书到异常处理这段都是跳过不看.   谢谢指点.  呵呵
2011-12-3 21:36
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
客气了,难得见到同是自学驱动的人。。看来我还不孤单。。
2011-12-3 21:56
0
雪    币: 43
活跃值: (29)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
你是程序员吧? 至少你的思维已经是了.
2011-12-3 22:06
0
雪    币: 61
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
我还在大学中。。。。而且还是一和计算机毫无关系的专业。。
2011-12-3 22:23
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
都是自己一个人瞎捣鼓呢
2011-12-3 22:48
0
雪    币: 2503
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
不错 加油!!!!!!!!
2011-12-3 23:13
0
雪    币: 221
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
不错 加油。。。
2012-3-19 22:16
0
雪    币: 111
活跃值: (57)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
17
呵呵,不错
不过记得看过论坛一个帖子说到pid,pid+1,pid+2,pid+3都表示是同一个进程(具体原理还不清楚,我要去补补课),也就是说用一个PID值做保护应该不行的,是不是用EPROCESS作为判断好点,我也去测试测试
2012-3-19 22:36
0
游客
登录 | 注册 方可回帖
返回
//