标题: 内核编程练习作品->UnSSDTHOOK [打印本页]

作者: liuyuxi    时间: 2015-1-10 23:24
标题: 内核编程练习作品->UnSSDTHOOK
       前段时间在研究51单片机,花了两个月的时间,总算是摸熟了单片机的工作方式以及程序设计的方法以及焊接技术的提升。写程序,时序图很重要啊...可惜因为资金不足,想做很多实验都没法...例如自己想整个语音控制程序,实现通过以说话的方式控制单片机执行。也想整个GPS+GPRS,实现追踪器,也没钱买相应的硬件。想整个遥控车+摄像头,实现移动监控。也想整个自动导航飞机,实现高空巡逻。.... 钱钱钱...真让人纠结.....

        所以在没有稳定的工作前,就先暂时放下单片机,复习一下编程知识。这个编程作品,也是在复习的时候,蒋哥想在需要的时候结束几个隐藏的进程。隐藏进程的实现方法通常都是通过DLL远程注入的方式,也有RING3 下HOOK相关API。而蒋哥的增值程序采用的是 SSDT HOOK技术(具体请看我的另外一篇《学习笔记 -> HOOK SSDT表的理解(1) 》http://www.51hei.com/bbs/dpj-30383-1.html  )。那个增值程序仅仅只是HOOK了 NtQuerySystemInformation,修改里它的返回值,所以普通的任务管理器自然就看不到该进程。就帮他整了一个简单的程序。后来想想,他在网吧工作,所以可能会有别的需要,所以索性就改进成通用版的,可以恢复指定被HOOK 的NtAPI。方便他用批处理器调用,也算是自己的第一个内核驱动编程作品吧。

      恢复比HOOK要简单多。只需要把被修改的地址改回原来的即可。留下驱动代码供日后自己复习用:

GetAPI_Addr.h :用来获取原始API地址和被修改的API地址
======================================================================================================
typedef struct _ServiceDescriptorTable
{
PVOID ServiceTableBase;// SSDT 基地址
PVOID ServiceCounterTable;// 包含SSDT中每个服务被调用次数的计数器,一般由sysenter更新
unsigned int NumberOfServices;// 由ServiceTableBase 描述的服务的数目
PVOID ParamTableBase;// 包含每个系统服务参数字节数表的基地址-系统服务参数表
}*PServiceDescriptorTable;

extern PServiceDescriptorTable KeServiceDescriptorTable;        // 导出

#pragma PAGECODE
// 获取当前Nt函数的地址
ULONG GetNt_DQ_Addr(int PianYi_Addr)
{
LONG SSDT_Addr, NtAPI_Addr, *t_addr;

// 读取SSDT基地址
SSDT_Addr = (LONG)KeServiceDescriptorTable->ServiceTableBase;

// 根据SSDT基址确定存放指定Nt函数的地址位置
t_addr = (PLONG)(SSDT_Addr+PianYi_Addr*4);

// 取出Nt函数的地址
NtAPI_Addr = *t_addr;

return NtAPI_Addr;
}
#pragma PAGECODE
// 获取原来的Nt函数的地址
ULONG GetNt_YL_Addr(WCHAR NtAPIName[])
{
UNICODE_STRING YL_NtAPI;
ULONG YL_NtAPI_Addr;

RtlInitUnicodeString(&YL_NtAPI, NtAPIName);

// 读取原来的地址
YL_NtAPI_Addr = (ULONG)MmGetSystemRoutineAddress(&YL_NtAPI);

return YL_NtAPI_Addr;

}

//UnHook函数构建
//////////////////////////////////////////////////////
#pragma PAGECODE
VOID UnHook(int PianYi_Addr, ULONG YL_Nt_Addr)
{
ULONG  SSDT_Nt_Addr;
SSDT_Nt_Addr = (ULONG)KeServiceDescriptorTable->ServiceTableBase + PianYi_Addr * 4;

/*修改 cr0 寄存器,关闭写保护*/
__asm
{
cli
mov     eax, cr0
and     eax, not 10000h
mov     cr0, eax
}

// 还原SSDT
*((ULONG*)SSDT_Nt_Addr) = YL_Nt_Addr;

/*恢复写保护*/
__asm
{
mov     eax, cr0
or     eax, 10000h
mov     cr0, eax
sti
}

return;
}

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

#include <windef.h>

#define INITCODE code_seg("INIT")
#define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/

// 定义宏,用来判断应用程序传递下来的数据。应用程序也需要相应的宏定义
#define UHook CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED,FILE_ANY_ACCESS)

VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject); //前置说明 卸载例程
NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp);//派遣函数

// 创建设备
#pragma INITCODE /*指的代码运行后 就从内存释放掉*/
NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status;
PDEVICE_OBJECT pDevObj;/*用来返回创建设备*/

//创建设备名称
UNICODE_STRING devName;
UNICODE_STRING symLinkName; //
RtlInitUnicodeString(&devName,L"\\Device\\L_Device");/*对devName初始化字串为 "\\Device\\L_Device"*/

//创建设备
status = IoCreateDevice( pDriverObject,0, &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);

if (!NT_SUCCESS(status))
{
if (status==STATUS_INSUFFICIENT_RESOURCES)
{
KdPrint(("资源不足 STATUS_INSUFFICIENT_RESOURCES"));
}
if (status==STATUS_OBJECT_NAME_EXISTS )
{
KdPrint(("指定对象名存在"));
}
if (status==STATUS_OBJECT_NAME_COLLISION)
{
KdPrint(("//对象名有冲突"));
}
KdPrint(("设备创建失败...++++++++"));
return status;
}
KdPrint(("设备创建成功...++++++++"));

pDevObj->Flags |= DO_BUFFERED_IO;


RtlInitUnicodeString(&symLinkName,L"\\??\\LoveMengx_UnSSDTHOOK_Driver");// 初始化
status = IoCreateSymbolicLink( &symLinkName,&devName );// 创建符号链接
if (!NT_SUCCESS(status)) /*status等于0*/
{
IoDeleteDevice( pDevObj );
return status;
}
return STATUS_SUCCESS;
}

main.cpp
======================================================================================================#include "main.h"
#pragma  INITCODE

// 此函数形同应用程序的main()函数
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
{  

//注册派遣函数
pDriverObject->MajorFunction[IRP_MJ_CREATE]=DispatchRoutine; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_CLOSE]=DispatchRoutine; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_READ]=DispatchRoutine; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_CLOSE]=DispatchRoutine; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=DispatchRoutine; //IRP_MJ_CREATE相关IRP处理函数

CreateMyDevice(pDriverObject);//创建相应的设备

pDriverObject->DriverUnload=DDK_Unload;        // 指定卸载驱动的时候要执行的函数 如果未指定则无法卸载

return (1);
}

// 卸载驱动的时候必须提供,否则无法卸载
#pragma PAGECODE
VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pDev;//用来取得要删除设备对象
UNICODE_STRING symLinkName; //

pDev=pDriverObject->DeviceObject;
IoDeleteDevice(pDev); //删除设备

//取符号链接名字
RtlInitUnicodeString(&symLinkName,L"\\??\\LoveMengx_UnSSDTHOOK_Driver");
//删除符号链接
IoDeleteSymbolicLink(&symLinkName);
KdPrint(("驱动成功被卸载...OK-----------")); //sprintf,printf

}

/* 不知道为什么 atoi 在驱动中无法使用 就只能用这个替代 */
int my_atoi(const char* p)
{
bool neg_flag = false;// 符号标记
int res = 0;// 结果
if(p[0] == '+' || p[0] == '-')
neg_flag = (*p++ != '+');
while(isdigit(*p)) res = res*10 + (*p++ - '0');
return neg_flag ?0 -res : res;
}


#pragma PAGECODE
NTSTATUS DispatchRoutine(IN PDEVICE_OBJECT pDevobj,IN PIRP pIrp)
{   //
ULONG info = 0;

//得到当前栈指针
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

ULONG mf=stack->MajorFunction;        //区分IRP
switch (mf)
{
case IRP_MJ_DEVICE_CONTROL:
{
KdPrint(("Enter myDriver_DeviceIOControl\n"));
NTSTATUS status = STATUS_SUCCESS;

//得到输入缓冲区大小
ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
//得到输出缓冲区大小
ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
//得到IOCTL码
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;

switch (code)                // 通过 code分析
{
case UHook:        // 如果是自己的定义的消息
{  
char NtAPIName[100] = {0};
char Tmp[100] = {0};
int Index = 0;


                                                        // 取得应用程序传递下来的数据

char* InputBuffer = (char*)pIrp->AssociatedIrp.SystemBuffer;
char* OutputBuffer = (char*)pIrp->AssociatedIrp.SystemBuffer;

strcpy(NtAPIName, InputBuffer);        
                           
                                                        // 检查格式。如果格式不对,容易导致蓝屏
if (strstr(NtAPIName,"-") && strstr(NtAPIName, "+"))
{

KdPrint(("从应用程序中得到的数据:%s\n", NtAPIName));

*(strstr(NtAPIName,"-")) = '\0';
Index = my_atoi(NtAPIName);// 分析得到第一个参数 NtAPI 的序号
if(Index >= 0 && Index <=295)
                                {
        strcpy(Tmp, InputBuffer);

        char *Tou = strstr(Tmp, "-")+1;
        Tou = strstr(Tmp, "-") +1;
        *(strstr(Tou,"+")) = '\0';// 分析得到第二个参数 NtAPI 的名字
        
                    KdPrint(("Tou:%s\n", Tou));
                    
        UNICODE_STRING wText;

        ANSI_STRING Name;
        
        RtlInitString(&Name, Tou);// 初始化 Name
        
        RtlAnsiStringToUnicodeString(&wText,&Name, TRUE);// 转换为UNICODE_STRING
        
        ULONG YL_NtAPI = (ULONG)MmGetSystemRoutineAddress(&wText);// 获取指定的 NtAPI 原始的地址

                                        KdPrint(("YL_NtAPI:0x%X\n", YL_NtAPI));

                                        if(YL_NtAPI > 0x80000000 && YL_NtAPI < 0x90000000)
                                        {
                                                    UnHook(Index, YL_NtAPI);// 恢复 SSDT
                                                    strcpy(Tmp, "OK...");                                                
                                         }
                                         else
                                                    strcpy(Tmp, "获取NtAPI地址失败,请检查NtAPI名。");                                         
                                }
                                else
                                     strcpy(Tmp, "NtAPI的序号超出大小限制。WinXP:0-283 Win2003:0-295");


}
else
strcpy(Tmp, "参数格式不对。");

KdPrint(("处理结果:%s\n", Tmp));
                            strcpy(OutputBuffer, Tmp);                            // 将数据返回至应用层。
info = strlen(Tmp);
break;
}
}
break;
}
case IRP_MJ_CREATE:
break;

case IRP_MJ_CLOSE:
break;

case IRP_MJ_READ:
break;
}

//对相应的IPR进行处理
pIrp->IoStatus.Information=info;//设置操作的字节数为0,这里无实际意义
pIrp->IoStatus.Status=STATUS_SUCCESS;//返回成功

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

//KdPrint(("离开派遣函数\n"));//调试信息

return STATUS_SUCCESS; //返回成功
}

==========================================================================================
程序下载地址: SSDT_UnHOOK.rar (19.35 KB, 下载次数: 4)

调用方法:

UnSSDTHOOK [ID]-[NtAPI]+

ID:SSDT表中函数服务号

NtAPI:函数名字

通过XueTr可以知道有那些API被HOOK,ID就是
里面的序号。NtAPI就是函数的名字。

例如:UnSSDTHOOK 122-NtOpenProcess+

注意,在执行前需要确定序号与函数名是相对应的。

可以通过批处理(.bat),循环调用,也可以自己写程
序调用。

UnSSDTHOOK 执行后会加载驱动,所以驱动名字不得修改。
恢复指定的函数地址后,会自动卸载驱动。






欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1