零零散散花了一个月的时间初步学习了windows驱动编程,接着开始要制定2013年的学习计划,即将步入另外一个学习战场,于是就将最近学习记录下来,也好将来再返回学习时有点基础。
一、windows驱动安装
1、到官方下载DDK安装,ex : 3790.1830.DDK
2、目录简介
示例代码: 安装目录/src/general/event/sys
编译:build
基本文件:源文件,sources, makefile
二、VC6 驱动设置
1、 Tools->Options->Directories->Executable files->新建 ...\3790.1830\BIN\X86 移到最上边
Tools->Options->Directories->Include files->新建 ...\3790.1830\INC\CRT
Tools->Options->Directories->Include files->新建 ...\3790.1830\INC\DDK\WXP
Tools->Options->Directories->Include files->新建 ...\3790.1830\INC\WXP
Tools->Options->Directories->Include files->新建 ...\3790.1830\INC\DDK\WDM\WXP
Tools->Options->Directories->Library files->新建 ...\3790.1830\LIB\WXP\I386
2、编译设置:适用于NT驱动,WDM驱动
Project->Configurations->Add... 新建编译选项
Project->Setting->C/C++->Project Options
/nologo /Gz /MLd /WZ /WX /Z7 /Od /D WIN32=100 /D _X86_=1 /D WINVER=0x501 /D DBG=1 /Fo "ddk_check1" /Fd "ddk_check1" /FD /c
Project->Setting->Link->Project Options
wdm.lib ntoskrnl.lib /nologo /base:"0x10000" /stack:0x400000,0x1000
/entry:"DriverEntry" /subsystem:console /incremental=no
/pdb:"ddk_check/event.pdb" /debug /machine:I386
/nodefaultlib /out:"ddk_check/event.sys" /subsystem:native
/driver /SECTION:INIT,D /RELEASE /IGNORE:4078
三、VS2003设置
一、配置移到最上面
工具->选项-》项目-》VC++目录-》包含文件-》新建... 3790.1830\inc\crt
工具->选项-》项目-》VC++目录-》包含文件-》新建... 3790.1830\inc\ddk\wxp
工具->选项-》项目-》VC++目录-》包含文件-》新建... 3790.1830\inc\wxp
工具->选项-》项目-》VC++目录-》包含文件-》新建... 3790.1830\inc\ddk\wdm\wxp
工具->选项-》项目-》VC++目录-》库文件-》新建 ...3790.1830\Bin\x86
工具->选项-》项目-》VC++目录-》可执行文件-》新建 ...3790.1830\Bin\x86
项目-》event属性-》配置管理器-》项目上下文-》新建项目配置 + "check"
项目-》event属性-》配置属性-》C/C++-》常规-》调试信息格式@c7
项目-》event属性-》配置属性-》C/C++-》常规-》警告等级@wz
项目-》event属性-》配置属性-》C/C++-》预处理器: WIN32=100;_X86_=1, WINVER=0X501; DBG=1
项目-》event属性-》配置属性-》C/C++-》代码生成-》运行时库 @多线程
项目-》event属性-》配置属性-》C/C++-》代码生成-》缓冲区安全检查 @否
项目-》event属性-》配置属性-》C/C++-》高级-》调用约定 @__stdcall
项目-》event属性-》链接器-》常规-》输出文件 @.sys
项目-》event属性-》链接器-》输入-》附加依赖项@wdm.lib
项目-》event属性-》高级-》入口点 @DriverEntry
项目-》event属性-》高级-》基址@ 0x10000
#include <ntddk.h>
#define INITCODE code_seg("INIT")
#pragma INITCODE
VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject); //前置说明 卸载例程
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
{
KdPrint(("驱动成功被加载...OK++++++++"));
pDriverObject->DriverUnload = DDK_Unload;
return (1);
}
VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject)
{
KdPrint(("驱动成功被卸载...OK-----------")); //sprintf,printf
DbgPrint("卸载成功");
}
五、原理介绍
windows API: user函数、gdi函数、kernel函数
user32.dll 管理窗口、菜单、对话框和控件
gdi32.dll 在物理设备上执行绘图操作
kernel32.dll 进程、线程、文件和同步服务
MFC库:在应用程序和win32子系统中间加了一层封装
SSDT: system service descriptor table 把ring3的win32 api 和ring0的内核api联系起来
应用层到内核层:
OpenProcess xx.dll->kernel32.OpenProcess->NtOpenProcess->ntdll.ZwOpenProcess->ntdll.KiFastSystemCall // eax参数+SSDT表
sysenter指令切换到内核
保护原理:inline hook这些API函数
内核:ntkrnlpa.ZwOpenProcess -> SSDT ->ntkrnlpa.NtOpenProcess
kernel32.dll -> SSDT
user32.dll gdi32.dll -> shadow SSDT -> win32k.sys
六、添加设备驱动例程
//_stdcall
#include <ntddk.h>
#define INITCODE code_seg("INIT")
#define PAGECODE code_seg("PAGE") /*表示内存不足时,可以被置换到硬盘*/
#pragma INITCODE /*指的代码运行后 就从内存释放掉*/
NTSTATUS CreateMyDevice (IN PDRIVER_OBJECT pDriverObject)
{
NTSTATUS status;
PDEVICE_OBJECT pDevObj;/*用来返回创建设备*/
//创建设备名称
UNICODE_STRING devName;
UNICODE_STRING symLinkName; //
RtlInitUnicodeString(&devName, L"\\Device\\yjxDDK_Device"); /*对devName初始化字串为 "\\Device\\yjxDDK_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"\\??\\yjx888");
status = IoCreateSymbolicLink( &symLinkName, &devName );
if (!NT_SUCCESS(status)) /*status等于0*/
{
IoDeleteDevice( pDevObj );
return status;
}
return STATUS_SUCCESS;
}
#pragma INITCODE
VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject); //前置说明 卸载例程
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
{
KdPrint(("驱动成功被加载...OK++++++++"));
//jmp指令
CreateMyDevice(pDriverObject);
pDriverObject->DriverUnload = DDK_Unload;
return (1);
}
VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject)
{
KdPrint(("驱动成功被卸载...OK-----------")); //sprintf,printf
//删掉所有设备
DbgPrint("卸载成功");
}
七、WinDBG安装
1、在调试目标机上创建串口设备,创建命名管理 \\.\ipip\com_1
2、主机增加winddbg快捷方式,在windbg属性的目标里添加参数:-b -k com:pipe, port=\\.\pipe\com1, baud=115200, reconnect -y
3、被调试机boot.ini中添加一行: /fastdetect /debug /debugport=com1 /baudrate=115200
4、windgb调试命令
代码中断点加:__asm int 3;
命令:
G 运行
U 汇编
F8, F11
F10 步过
shift + F11 跳出,返回到上层call执行
bp 下断点
bl 断点列表
bd / be 断点禁用启用
bc 清除断点
a 修改汇编代码
八、添加默认派遣例程
IRP : IO Request Package
用户模式下所有对驱动程序的IO请求,全部由OS转化为IRP数据结构,不同IRP派遣到不同派遣函数中。
常用IRP:
IRP_MJ_CREATE
IRP_MJ_CLOSE
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_DEVICE_CONTROL
过程:
1、创建IRP处理函数
2、在驱动入口DriverEntry注册IRP处理函数
3、编写IRP处理函数
//_stdcall
#include "mini_ddk.h"
#pragma INITCODE
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING B) //TYPEDEF LONG NTSTATUS
{
KdPrint(("驱动成功被加载...OK++++++++"));
//注册派遣函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_READ] = ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ddk_DispatchRoutine_CONTROL; //IRP_MJ_CREATE相关IRP处理函数
CreateMyDevice(pDriverObject);//创建相应的设备
pDriverObject->DriverUnload = DDK_Unload;
return (1);
}
//#pragma code_seg("PAGE")
#pragma PAGECODE
VOID DDK_Unload (IN PDRIVER_OBJECT pDriverObject)
{
PDEVICE_OBJECT pDev;//用来取得要删除设备对象
UNICODE_STRING symLinkName; //
pDev = pDriverObject->DeviceObject;
IoDeleteDevice(pDev); //删除设备
//取符号链接名字
RtlInitUnicodeString(&symLinkName, L"\\??\\yjx888");
//删除符号链接
IoDeleteSymbolicLink(&symLinkName);
KdPrint(("驱动成功被卸载...OK-----------")); //sprintf,printf
//取得要删除设备对象
//删掉所有设备
DbgPrint("卸载成功");
}
#pragma PAGECODE
NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp )
{
//对相应的IPR进行处理
pIrp->IoStatus.Information = 0; //设置操作的字节数为0,这里无实际意义
pIrp->IoStatus.Status = STATUS_SUCCESS; //返回成功
IoCompleteRequest(pIrp, IO_NO_INCREMENT); //指示完成此IRP
KdPrint(("离开派遣函数\n"));//调试信息
return STATUS_SUCCESS; //返回成功
}
九、保护理论知识
如何知道函数被HOOK?
工具Kernel Detective -》系统服务描述表-》查看函数对应“当前地址”与“源地址”是否相等-》转入反汇编-》修改汇编
SSDT结构:
typedef struct ServiceDescriptorTable
{
PVOID ServiceTableBase;
PVOID ServiceCounterTable(0);
unsigned int NumberOfServices;
PVOID ParamTableBase;
}
符号表:windbg中查看
srv *D:\winddk\symbols* http://msdl.microsoft.com/download/symbols
dd [KeServiceDescriptorTable]
dd poi[KeServiceDescriptorTable]
dd poi[KeServiceDescriptorTable] + 0x17 * 4
由SSDT索引号获取当前函数地址:[[KeServiceDescriptorTable] + index * 4 ]
如何获取索引号:
1、Kernel Detective 工具
2、OllDbg 断点看索引号
获取函数源地址: MmGetSystemRoutineAddress
extern long KeServiceDescriptorTable;
__asm {
push ebx
push eax
mov ebx, KeServiceDescriptorTable
mov ebx, [ebx]
mov eax, 0x7a
imul eax, eax, 4
add ebx, eax
mov ebx, [ebx]
mov NtOpenProcess_Addr, ebx
pop eax
pop ebx
}
法二:
extern pServiceDescriptorTable KeServiceDescriptorTable;
ULONG GetNt_CurAddr()
{
t_addr = (LONG)KeServiceDescriptorTable->ServiceTableBase;
SSDT_addr = (PLONG)(t_addr + 0x7a * 4);
SSDT_NtOpenProcess_Cur_Addr = *SSDT_addr;
return SSDT_NtOpenProcess_Cur_Addr;
}
获取系统原始NtOpenProcess地址
ULONG GetNt_OldAddr()
{
UNICODE_STRING Old_NtOpenProcess;
RtlInitUnicodeString(&Old_NtOpenProcess, L"NoOpenProcess");
ULONG Old_Addr;
Old_Addr = (ULONG) MmGetSystemRoutineAddress(&Old_NtOpenProcess);
KdPrint(("Old Addr is %u", Old_Addr);
}
被HOOK:GetNt_CurAddr() != GetNt_OldAddr()
十、NT式驱动加载和缷载
void CLoadsysDlg::OnButtonLoadsys()
{
// TODO: Add your control notification handler code here
CFileDialog sysFile(true, NULL, NULL, 0, "驱动文件sys|*.sys|所有文件|*.*|");
if (IDOK == sysFile.DoModal())
{
m_syspathname = sysFile.GetPathName();
m_syspathname = sysFile.GetFileName();
DriverName = sysFile.GetFileName();
UpdateData(false);
//LoadNtDriver;
LoadNTDriver(sysFile.GetFileName().GetBuffer(256), sysFile.GetPathName().GetBuffer(256));
}
}
/*
BOOL LoadNTDriver(char* lpszDriverName, char* lpszDriverPathName)
{
BOOL bRet=false;
SC_HANDLE hServiceDDK=NULL;
// A、OpenSCManager
SC_HANDLE hServiceMgr=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
if (hServiceMgr==NULL)
{
TRACE("OpenSCManager 调用失败");
goto BExit;
}
// B、CreateService
hServiceDDK= CreateService( hServiceMgr,//SCM管理器句柄
lpszDriverName, //驱动程序的在注册表中的名字
lpszDriverName, // 注册表驱动程序的 DisplayName 值
SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序
SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
lpszDriverPathName, // 注册表驱动程序的 ImagePath 值
NULL, //要开启服务的 用户组
NULL, //输出验证标签
NULL, //所依赖的服务的名称
NULL, //用户账户名称
NULL); //用户口令
// C、OpenService
if (hServiceDDK==NULL)
{ TRACE("CreateService 失败,继续调用OpenService");
hServiceDDK=OpenService(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);
if (hServiceDDK==NULL)
{ TRACE("OpenService 失败");
goto BExit;
}
}
// D、StartService
StartService(hServiceDDK,NULL,NULL);
// E、CloseServiceHandle
BExit:
//CloseServiceHandle
//CloseServiceHandle
return bRet;
} */
BOOL LoadNTDriver(char *lpDriverName, char *lpDriverPathName)
{
BOOL bRet = FALSE;
SC_HANDLE hServiceMgr = NULL; //SCM管理器的句柄
SC_HANDLE hServiceDDK = NULL; //NT驱动程序的服务句柄
//打开服务控制管理器
hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if( hServiceMgr == NULL )
{
//OpenSCManager失败
TRACE( "OpenSCManager() Faild %d ! \n", GetLastError() );
bRet = FALSE;
goto BExit;
}
else
{
OpenSCManager成功
TRACE( "OpenSCManager() ok ! \n" );
}
//创建驱动所对应的服务
hServiceDDK = CreateService( hServiceMgr,
lpDriverName, //驱动程序的在注册表中的名字
lpDriverName, // 注册表驱动程序的 DisplayName 值
SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限
SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序
SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值
SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值
lpDriverPathName, // 注册表驱动程序的 ImagePath 值
NULL,
NULL,
NULL,
NULL,
NULL);
DWORD dwRtn;
//判断服务是否失败
if( hServiceDDK == NULL )
{
dwRtn = GetLastError();
if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS )
{
//由于其他原因创建服务失败
TRACE( "CrateService() 失败 %d ! \n", dwRtn );
bRet = FALSE;
goto BExit;
}
else
{
//服务创建失败,是由于服务已经创立过
TRACE( "CrateService() 服务创建失败,是由于服务已经创立过 ERROR is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n" );
}
// 驱动程序已经加载,只需要打开
hServiceDDK = OpenService( hServiceMgr, lpDriverName, SERVICE_ALL_ACCESS );
if( hServiceDDK == NULL )
{
//如果打开服务也失败,则意味错误
dwRtn = GetLastError();
TRACE( "OpenService() 失败 %d ! \n", dwRtn );
bRet = FALSE;
goto BExit;
}
else
{
TRACE( "OpenService() 成功 ! \n" );
}
}
else
{
TRACE( "CrateService() 成功 ! \n" );
}
//开启此项服务
bRet = StartService( hServiceDDK, NULL, NULL );
if( !bRet ) //开启服务不成功
{
TRACE( "StartService() 失败 服务可能已经开启%d ! \n", dwRtn );
}
bRet = TRUE;
//离开前关闭句柄
BExit:
if(hServiceDDK)
{
CloseServiceHandle(hServiceDDK);
}
if(hServiceMgr)
{
CloseServiceHandle(hServiceMgr);
}
return bRet;
}
//卸载驱动程序
BOOL UnLoadSys( char *szSvrName )
{
//一定义所用到的变量
BOOL bRet = FALSE;
SC_HANDLE hSCM = NULL; //SCM管理器的句柄,用来存放OpenSCManager的返回值
SC_HANDLE hService = NULL; //NT驱动程序的服务句柄,用来存放OpenService的返回值
SERVICE_STATUS SvrSta;
//二打开SCM管理器
hSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
if( hSCM == NULL )
{
//带开SCM管理器失败
TRACE( "OpenSCManager() Faild %d ! \n", GetLastError() );
bRet = FALSE;
goto BeforeLeave;
}
else
{
//打开SCM管理器成功
TRACE( "OpenSCManager() ok ! \n" );
}
//三打开驱动所对应的服务
hService = OpenService( hSCM, szSvrName, SERVICE_ALL_ACCESS );
if( hService == NULL )
{
//打开驱动所对应的服务失败 退出
TRACE( "OpenService() Faild %d ! \n", GetLastError() );
bRet = FALSE;
goto BeforeLeave;
}
else
{
TRACE( "OpenService() ok ! \n" ); //打开驱动所对应的服务 成功
}
//四停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。
if( !ControlService( hService, SERVICE_CONTROL_STOP , &SvrSta ) )
{
TRACE( "用ControlService() 停止驱动程序失败 错误号:%d !\n", GetLastError() );
}
else
{
//停止驱动程序成功
TRACE( "用ControlService() 停止驱动程序成功 !\n" );
}
//五动态卸载驱动服务。
if( !DeleteService( hService ) ) //TRUE//FALSE
{
//卸载失败
TRACE( "卸载失败:DeleteSrevice()错误号:%d !\n", GetLastError() );
}
else
{
//卸载成功
TRACE ( "卸载成功 !\n" );
}
bRet = TRUE;
//六 离开前关闭打开的句柄
BeforeLeave:
if(hService > 0)
{
CloseServiceHandle(hService);
}
if(hSCM > 0)
{
CloseServiceHandle(hSCM);
}
return bRet;
}
void CLoadsysDlg::OnButtonUnloadsys()
{
// TODO: Add your control notification handler code here
//卸载驱动程序
UnLoadSys(DriverName.GetBuffer(256)) ;
}
驱动代码中C++代码编译问题:
#include<winioctl.h> //CTL_CODE
#define add_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED,FILE_ANY_ACCESS)
#define sub_code CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED,FILE_ANY_ACCESS)
int add(HANDLE hDevice, int a, int b)
{
int port[2];
int bufret;
ULONG dwWrite;
port[0] = a;
port[1] = b;
DeviceIoControl(hDevice, add_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;
}
int a = 55;
int b = 33;
int r = add(hDevice, a, b);
printf("%d+%d=%d \n", a, b, r);
getchar();
return 0;
}
#pragma PAGECODE
NTSTATUS ddk_DispatchRoutine_CONTROL(IN PDEVICE_OBJECT pDevobj, IN PIRP pIrp )
{
//
ULONG info;
//得到当前栈指针
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)
{
case add_code:
{
int a, b;
KdPrint(("add_code 1111111111111111111\n"));
//缓冲区方式IOCTL
//获取缓冲区数据 a,b
int *InputBuffer = (int *)pIrp->AssociatedIrp.SystemBuffer;
_asm
{
mov eax, InputBuffer
mov ebx, [eax]
mov a, ebx
mov ebx, [eax+4]
mov b, ebx
}
KdPrint(("a=%d,b=%d \n", a, b));
a = a + b;
//C、驱动层返回数据至用户层
//操作输出缓冲区
int *OutputBuffer = (int *)pIrp->AssociatedIrp.SystemBuffer;
_asm
{
mov eax, a
mov ebx, OutputBuffer
mov [ebx], eax //bufferet=a+b
}
KdPrint(("a+b=%d \n", a));
//设置实际操作输出缓冲区长度
info = 4;
break;
}
case sub_code:
{
break;
}
}//end code switch
break;
}
//对相应的IPR进行处理
pIrp->IoStatus.Information = info; //设置操作的字节数为0,这里无实际意义
pIrp->IoStatus.Status = STATUS_SUCCESS; //返回成功
IoCompleteRequest(pIrp, IO_NO_INCREMENT); //指示完成此IRP
KdPrint(("离开派遣函数\n"));//调试信息
return STATUS_SUCCESS; //返回成功
}
十二、一些理论知识
#define INIT code_seg("INIT") // 使用一次释放
#define PAGECODE code_seg("PAGE") // 表示内存不足可置换到硬盘
#define PAGEDATA data_seg("PAGE") // 定义数据和变量
中断请求在DISPATCH_LEVEL及之上程序只能使用非分页内存,否则蓝屏
HOOKAPIPROXY
typedef struct
{
byte PushCode1;
ULONG OrgAddr;
byte PushCode2;
ULONG NameAddr;
byte JmpCode;
ULONG JmpParam;
} *PHOOKAPIPROXY, HOOKAPIPROXY;
导入表遍历:
PVOID EnumAPI()
{
PBYTE ImageBase;
PIMAGE_THUNK_DATA r;
PIMAGE_NT_HEADERS pNtHeader;
PIMAGE_IMPORT_DESCRIPTOR pImport;
//取得DOS头基址
ImageBase=(PBYTE)GetModuleHandle(NULL);//0x400000
//PE头=ImageBase+[ImageBase+3c]
pNtHeader = (PIMAGE_NT_HEADERS) (ImageBase + ((PIMAGE_DOS_HEADER) ImageBase)->e_lfanew);
//IMAGE_DIRECTORY_ENTRY_IMPORT值为1 表示import tabale
pImport = (PIMAGE_IMPORT_DESCRIPTOR)
(ImageBase + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
遍历整个 输入表
for (; pImport->Name; pImport++)
{
printf("导入模块:%s\n",ImageBase+pImport->Name);
//遍历IAT信息 PIMAGE_THUNK_DATA基址
for (r = (PIMAGE_THUNK_DATA) (ImageBase + pImport->FirstThunk); r->u1.Function; r++) //枚举函数地址
{ if (Sleep==(PVOID)r->u1.Function)
{ DWORD pSleep=(DWORD)(&r->u1.Function);
__asm
{
mov ebx,pSleep /// mov ebx,0x42A190
lea eax,mySleep
mov [ebx],eax
}
}
printf("Function=%x \n", &(r->u1.Function));
}
}
return NULL;
}
int main(int argc, char* argv[])
{
EnumAPI();
Sleep(111);
return 0;
}
应用层Hook示例:
#include <windows.h>
typedef int (__stdcall *MessageBox_type)(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
) ;
MessageBox_type old_MessageBoxA;
#pragma pack(1)
typedef struct _JMPCODE
{
BYTE jmp;
DWORD addr;
}JMPCODE,*PJMPCODE;
_declspec(naked)
VOID __stdcall my_MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
)
{
// old_MessageBoxA(hWnd,"Hook ok",lpCaption,uType);
__asm
{
PUSH EBP
MOV EBP,ESP
}
printf("取得参数 %x,%s,%s,%x\n",hWnd,lpText,lpCaption,uType);
__asm
{
mov ebx,old_MessageBoxA
add ebx,5
jmp ebx
}
printf("hook Error\n");
}
VOID InLine_HookMessageBoxA()
{ JMPCODE jcode;
//取得MessageBoxA函数当前地址
HMODULE h=LoadLibraryA("user32.dll");
old_MessageBoxA=(MessageBox_type)(GetProcAddress(h,"MessageBoxA"));
//替换 jmp指令
jcode.jmp=0xe9;
jcode.addr=(DWORD)(&my_MessageBox)-(DWORD)(&MessageBoxA)-5;//目的地址-当前地址-5 //跳到my
old_MessageBoxA=&MessageBoxA;
WriteProcessMemory(GetCurrentProcess(),&MessageBoxA,&jcode,sizeof(JMPCODE),NULL);
CloseHandle(h);
}
int main(int argc, char* argv[])
{
printf("Hello World!\n");
InLine_HookMessageBoxA();
MessageBoxA(NULL,"333","222",MB_OK);
return 0;
}
十四、内核层HOOK
#pragma PAGECODE
DWORD Get_KeServiceDescriptorTableShadow_Addr()
{
DWORD KeServiceDescriptorTableShadow = 0;
DWORD Version = GetVersion();
switch (Version )
{
case VERSION_2K:
KeServiceDescriptorTableShadow = (DWORD)KeServiceDescriptorTable + 0xE0;
break;
case VERSION_2K3:
break;
case VERSION_XP:
KeServiceDescriptorTableShadow = (DWORD)KeServiceDescriptorTable - 0x40;
break;
default:
break;
}
return KeServiceDescriptorTableShadow;
}
#pragma PAGECODE
VOID Show_SSDTShadowList()
{
KdPrint(("Entry Show_SSDTShadowList \n"));
DWORD TableBase = Get_KeServiceDescriptorTableShadow_Addr();
TableBase = TableBase + 0x10; //表基址
DWORD TableCount = TableBase + 8; //表函数 数量
DWORD count = *((PDWORD)TableCount); //函数数量
KdPrint(("SSDT_Shadow Base=%x Count=%x\n", TableBase, count));
//__asm int 3
PDWORD CFun_Addr = PDWORD(TableBase); //+=355
CFun_Addr = PDWORD(*CFun_Addr);
for (DWORD i = 0; i < count; i++)
{
KdPrint(("\n %d=%x\n", i, *CFun_Addr ));
CFun_Addr++;
//__asm int 3
}
}
HWND myh;
typedef BOOL (__stdcall *PNtUserDestroyWindow)(HWND hwnd);
PNtUserDestroyWindow Old_NtUserDestroyWindow, Cur_NtUserDestroyWindow;
#pragma PAGECODE
BOOL __stdcall My_NtUserDestroyWindow(HWND h)
{
KdPrint(("h=%x \n", h));
if (h == myh)
{
KdPrint(("被保护窗口h=%x \n", h));
return FALSE;
}
else return Old_NtUserDestroyWindow(h);
}
#pragma PAGECODE
VOID SSDT_HOOK_NtUserDestroyWindow() //355
{
KdPrint(("Entry Show_SSDTShadowList \n"));
DWORD TableBase = Get_KeServiceDescriptorTableShadow_Addr();
TableBase = TableBase + 0x10;
DWORD TableCount = TableBase + 8;
DWORD count = *((PDWORD)TableCount);
KdPrint(("SSDT_Shadow Base=%x Count=%x\n", TableBase, count));
//__asm int 3
PDWORD CFun_Addr = PDWORD(TableBase);
CFun_Addr = PDWORD(*CFun_Addr);
CFun_Addr += 355;
Old_NtUserDestroyWindow = (PNtUserDestroyWindow)(*CFun_Addr);
KdPrint(("\n NtUserDestroyWindow当前地址=%x,%x \n", CFun_Addr, *CFun_Addr));
__asm //去掉页面保护
{
cli
mov eax, cr0
and eax, not 10000h //and eax,0FFFEFFFFh
mov cr0, eax
}
*CFun_Addr = (DWORD)(&My_NtUserDestroyWindow);
KdPrint(("\n NtUserDestroyWindow HOOK后地址=%x,%x \n", CFun_Addr, *CFun_Addr));
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
}
#pragma PAGECODE
VOID SSDT_UNHOOK_NtUserDestroyWindow() //335
{
KdPrint(("Entry Show_SSDTShadowList \n"));
DWORD TableBase = Get_KeServiceDescriptorTableShadow_Addr();
TableBase = TableBase + 0x10;
DWORD TableCount = TableBase + 8;
DWORD count = *((PDWORD)TableCount);
KdPrint(("SSDT_Shadow Base=%x Count=%x\n", TableBase, count));
//__asm int 3
PDWORD CFun_Addr = PDWORD(TableBase);
CFun_Addr = PDWORD(*CFun_Addr);
CFun_Addr += 355;
__asm //去掉页面保护
{
cli
mov eax, cr0
and eax, not 10000h //and eax,0FFFEFFFFh
mov cr0, eax
}
*CFun_Addr = (DWORD)(Old_NtUserDestroyWindow);
__asm
{
mov eax, cr0
or eax, 10000h
mov cr0, eax
sti
}
}
十五、绕过应用层HOOK
#pragma pack(1)
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
#pragma pack()
__declspec(naked) void sysFastCall()
{
__asm
{
// 7C92E510 > 8BD4 MOV EDX,ESP
//7C92E512 0F34 SYSENTER
mov edx, esp
__emit 0x0f
__emit 0x34
}
}
/*
77D28285 . FF75 18 PUSH DWORD PTR SS:[EBP+18] ; 0
77D28288 . FF75 E8 PUSH DWORD PTR SS:[EBP-18] ; PU_LCatipn
77D2828B . FF75 F8 PUSH DWORD PTR SS:[EBP-8] ; NULL
77D2828E . FF75 0C PUSH DWORD PTR SS:[EBP+C] ; 0
77D28291 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; 0
77D28294 . E8 13450000 CALL USER32.77D2C7AC ; NtUserFindWindow
*/
__declspec(naked) HWND __stdcall My_FindWindow(
int p1,
int p2,
PUNICODE_STRING pu_classname,
PUNICODE_STRING pu_catption,
int p5)
{
__asm
{
MOV EAX, 0x117A
call sysFastCall
RETN 0x14
}
}
void CTest_APIDlg::OnBUTTONsysfind()
{
// TODO: Add your control notification handler code here
HWND h =::FindWindow(NULL, "计算器");
::SendMessage(h, WM_CLOSE, 0, 0);
}
void CTest_APIDlg::OnButtonMyFindwindow()
{
// TODO: Add your control notification handler code here
UNICODE_STRING pu_className, pu_Caption;
typedef (__stdcall * PRtlInitUnicodeString)( PUNICODE_STRING , PCWSTR );
PRtlInitUnicodeString RtlInitUnicodeString;
RtlInitUnicodeString = (PRtlInitUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlInitUnicodeString");
RtlInitUnicodeString(&pu_className, L"SciCalc");
RtlInitUnicodeString(&pu_Caption, L"计算器");
HWND h = My_FindWindow(0, 0, &pu_className, &pu_Caption, 0);
::SendMessage(h, WM_CLOSE, 0, 0);
}
十六、内核HOOK
#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函数 ZwOpenProcess
#pragma PAGECODE
extern "C" NTSTATUS __declspec(naked) __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)));
__asm
{
retn 0x10
}
}
}
__asm
{
int 3
push 0C4h
mov eax, RealNtOpenProcess //
add eax, 5
jmp eax
}
//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
}
//[804e5a88]=MyNtOpenProcess
//[8058270a]=jmp MyNtOpenProcess=E9 jmpaddr
ULONG jmpaddr = (ULONG)MyNtOpenProcess - RealNtOpenAddress - 5;
SSDT_Adr = (PLONG) * SSDT_Adr; //SSDT HOOK jmp jz jnz
// in line hook
__asm
{
mov ebx, SSDT_Adr //RealNtOpenAddress
mov byte ptr ds:[ebx], 0xe9
mov eax, jmpaddr
mov DWORD ptr ds:[ebx+1], eax
}
__asm
{
int 3
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;
}
十七、IDT
typedef struct _IDTR
{
USHORT limit; // 范围
ULONG base; // 基地址
} IDTR, *PIDTR;
typdef struct _IDTR_ENTRY
{
USHORT offset_low;
USHORT selector;
UCHAR reserved;
UCHAR type:4;
UCHAR always0:1;
UCHAR dpl:2;
UCHAR present:1;
USHORT offset_high; // 中断处理函数地址
} IDT_ENTRY, *PIDT_ENTRY;
IDTR idtr;
__asm sidt idtr;
PIDT_ENTRY aidt = PIDT_ENTRY(idtr.base);
for (int i = 0; i < 0xff; i++)
{
ULONG cur_idt = aidt->offset_high;
cur_idt = cur_idt << 16 + ULONG(aidt->offset_low);
aidt++;
}
return idtr.base;
IDT HOOK实例:
#pragma pack(pop) //#pragma pack(pop)
//-----------全局变量--------------------------------
ULONG int3proc_addr; //用来存放int 3处理函数地址
ULONG jmpaddr_int3proc_9; //用来存放intproc+9处理函数地址
//-----------全局变量 定义结束-----------------------
#pragma PAGECODE
ULONG ReadIdtBase(ULONG CPUNUM)
{
IDTR idtr;//获取表基址
PIDT_ENTRY Aidt;
KdPrint(("IDT_ENTRY size=%d \n", sizeof(IDT_ENTRY)));
__asm sidt idtr;//获取表基址信息
KdPrint(("IDT BASE=%x \n", idtr.base));
Aidt = PIDT_ENTRY(idtr.base);
return idtr.base;
}
void __declspec(naked) int3UnHookcode()
{
__asm
{
push 0
mov word ptr [esp+2], 0
}
}
#pragma PAGECODE
void __declspec(naked) myInt3Proc()
{
//__asm retn 100;
__asm
{
pushad
pushfd
}
KdPrint(("\n entry my Int3Proc \n"));
//在这里添加自己的条件过滤代码
//获取进程上下文
PEPROCESS EP;
EP = PsGetCurrentProcess();
// (PTSTR)((ULONG)EP+0x174)是否等于 需要反断点的进程
if (strcmp((PTSTR)((ULONG)EP + 0x174), "notepad.exe") == 0)
{
//需要保护的进程 直接蓝屏
KdPrint(("\n 蓝屏 蓝屏 蓝屏 \n"));
__asm retn 100;
}
__asm
{
popfd
popad
}
__asm
{
push 0
mov word ptr [esp+2], 0
//前2条需要恢复的指令 占9字节
jmp jmpaddr_int3proc_9
}
}
#pragma PAGECODE
ULONG HookInt3Proc()
{
ULONG status = 1;
PIDT_ENTRY Pidt_info = (PIDT_ENTRY)ReadIdtBase(0);
ULONG jmpaddr;
Pidt_info += 3; //转到IDT 数组3 里边存放着 int 3 处理函数地址
//Pidt_info=Pidt_info+sizeof(Pidt_info)*3;
//begin计算出int3处理函数地址
int3proc_addr = Pidt_info->offset_high << 16; //makelong 0x804d0000
//MAKELONG(Pidt_info->offset_high,Pidt_info->offset_Slow) //0xfaa1 =804dfaa1
int3proc_addr = int3proc_addr + Pidt_info->offset_low;
KdPrint (("\n int proc addr=%x \n", int3proc_addr));
//end;
//begin inline hook int3Proc write
// E9+jmp地址//jmp地址=myInt3Proc-int3proc_addr-5;
jmpaddr = ULONG(&myInt3Proc) - int3proc_addr - 5;
jmpaddr_int3proc_9 = int3proc_addr + 9;
__asm
{
push ebx
push eax
mov ebx, int3proc_addr
mov byte ptr ds:[ebx], 0xE9
mov eax, jmpaddr
mov dword ptr ds:[ebx+1], eax
pop eax
pop ebx
}
//end;inline hook int3proc write
return status;
}
#pragma PAGECODE
ULONG UnHookInt3Proc()
{
ULONG status = 1;
KdPrint(("\n 卸载 Idt Hook \n"));
__asm
{
push ebx
push eax
push ecx
mov ebx, int3proc_addr //0x804dfaa1
lea ecx, int3UnHookcode
mov eax, [ecx+0]
mov dword ptr ds:[ebx], eax
mov eax, [ecx+4]
mov dword ptr ds:[ebx+4], eax
mov eax, [ecx+8]
mov byte ptr ds:[ebx+8], al
pop ecx
pop eax
pop ebx
}
//end;inline hook int3proc write
return status;
}