在这篇心得开始,说点其他的。之前参加百度校园招聘的时候,面试官问我什么是SSDT,已经HOOK SSDT的实现过程。自己明明知道,可是我一下答上来,所以我找出来以前学习的心得,贴出来,好备份吧!我这篇心得呢!是代码与文字一起加载在一起的,学习就想读书的时候一样,书看到哪里笔记就做到哪里。这样方便理解。如果代价复制黏贴来学,不会影响的,一边看代码一边看笔记哟!其实我平时写代码不是这样的……都是规范化哟!这里是心得而已,大家不要见外。
//环境:XP + VS2008 +WDK
//SSDT
//我先讲讲 SSDT 保护进程与**OpenProcess 的关系
//在应用层中,如果想要使用API函数 结束某个进程 首先要得到这个进程的PID
//然后用 OpenProcess()函数 获取这个进程的 句柄 Handle
//最后用 TerminateProcess() 函数,使用获得的句柄 Handle 结束这个进程
//思路就是这样,内核就是换成内核函数
//那SSDT 如何修改 ZwOpenProcess(),修改后变成什么??
//因为SSDT表中 保存的都是系统服务函数地址(内核调用的函数地址)
//输入一个 函数名 通过KeServiceDescriptorTable.ServiceTableBase[*(PULONG)((PUCHAR)_function+1)]
//它就能找到函数的地址了
//说说里面的东西是什么吧!
//_function,将它强制转换成PUCHAR 就获得了这个函数的地址。
//为什么+1呢?
// Windows对内存的分配,是采用的分页管理。其中有个CR0寄存器,
//这个寄存器有一个位 管理 内存的 读或写 的标识
//CRO 如果为1,则可以读/写/执行;
//如果为0,则只可以读/执行。SSDT,IDT的页属性在默认下都是只读,可执行的,但不能写。
//所以 要 +1 则把这一位设置成1这样 SSDT页属性为可读/写/执行
//
//Protect.c
//
#include "ntddk.h"
#define NT_DEVICE_NAME L"\\Device\\ProtectProcess"
#define DOS_DEVICE_NAME L"\\DosDevices\\ProtectProcess"
//解释解释 内联汇编吧!
//取消内存保护 就是将内存属性设置为 1 变得可读、可写、可执行
//1--寄存器的中断标志位清0,不允许中断
//2--将CRO 内存页属性默认标识 放入寄存器 eax
//3--入栈
//4--0x10000 取反 ,与eax 相加 eax 变成1
//5--将CRO 置为 1
//恢复内存保护 我就不多说,举一反三
//取消内存保护
// 对于取消内存保护 和 恢复内存保护还有另外一个方法
// 取消用 _asm cli //不允许中断
// 恢复用 _asm sti //允许中断
//在 DriverEntry 验证了 我的说法。
#define DisableMemProtect()\
{\
__asm cli\
__asm mov eax,cr0\
__asm push eax\
__asm and eax,(~0x10000)\
__asm mov cr0,eax\
}
//恢复内存保护
#define RecoverMemProtect()\
{\
__asm pop eax\
__asm or eax,0x10000\
__asm mov cr0,eax\
__asm sti\
}
long pid = -1; //应用层 与 驱动层 通信的PID 进程 要记得初始化
//使用 DeviceIoControl函数 与 IoControl Code Ring3/Ring0 通信
#define IOCTL_PROTECT_CONTROL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
//函数声明
//操作派遣函数
NTSTATUS DispatchDeviceControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp); //
//卸载驱动
VOID OnUnload(IN PDRIVER_OBJECT DriverObject);
#pragma pack(1) //SSDT表的结构
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase;
unsigned int *ServiceCounterTableBase; //Used only in checked build
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()
//KeServiceDescriptorTable 是未公开的结构体,但是定义一次就可以使用。至于为什么我也不知道
//但是在《寒江独钓》里面又讲
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable; //变量名是不能变的,因为是从外部导入
//这个是查询某个函数的地址的一个宏
#define SYSTEMSERVICE(_function) KeServiceDescriptorTable.ServiceTableBase[*(PULONG)((PUCHAR)_function+1)]
//这是 未公开的 内核 函数ZwOpenProcess 的原型, 和KeServiceDescriptorTable 一样定义一次就可以用了
NTSYSAPI NTSTATUS NTAPI ZwOpenProcess(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId OPTIONAL);
//生成一个与原ZwOpenProcess()函数一样的 模板 用于保存
typedef NTSTATUS (*ZWOPENPROCESS)(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId OPTIONAL);
ZWOPENPROCESS OldZwOpenProcess; //保存原来的ZwOpenProcess() 函数
//用来替换的新函数 里面的操作 你自己看着办吧!爱咋整 咋整 技术牛大话
NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN PCLIENT_ID ClientId OPTIONAL)
{
NTSTATUS nStatus = STATUS_SUCCESS;
if((long)ClientId->UniqueProcess == pid)
{
DbgPrint("The Protect PID:%ld\n",pid);
return STATUS_ACCESS_DENIED;
}
//做其他处理
nStatus = OldZwOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId); //其他程序也需要调用这个函数
return STATUS_SUCCESS;
}
//卸载时会调用
//******************************************************
// 函数名称: DriverUnload(IN PDRIVER_OBJECT DriverObject)
// 功能描述: 负责驱动程序的卸载操作
// 参数列表: DriverObject驱动对象
// 返回值: 返回状态
//******************************************************
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING DeviceLinkString;
PDEVICE_OBJECT DeviceObjectTemp1=NULL;
PDEVICE_OBJECT DeviceObjectTemp2=NULL;
DbgPrint("Unload Driver...\n");
RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
IoDeleteSymbolicLink(&DeviceLinkString);
if(DriverObject)
{
DeviceObjectTemp1=DriverObject->DeviceObject;
while(DeviceObjectTemp1)
{
DeviceObjectTemp2=DeviceObjectTemp1;
DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;
IoDeleteDevice(DeviceObjectTemp2);
}
}
//卸载后,修复SSDT
DisableMemProtect();
//使用InterlockedExchangePointer(),函数进行交换,效果是一样的
//InterlockedExchangePointer(&(SYSTEMSERVICE(ZwOpenProcess)),OldZwOpenProcess);
(ZWOPENPROCESS)(SYSTEMSERVICE(ZwOpenProcess)) = OldZwOpenProcess;
RecoverMemProtect();
DbgPrint("Unload Compelete.\n");
}
//分发函数
//************************************************************************
// 函数名称: DeviceDispatch(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
// 功能描述: 负责驱动程序的卸载操作
// 参数列表: DeviceObject 设备对象
// Irp 请求
// 返回值: 返回状态
//************************************************************************
NTSTATUS DeviceDispatch(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
NTSTATUS nStatus = STATUS_SUCCESS;
ULONG IoControlCode = 0;
PIO_STACK_LOCATION IrpStack = NULL;
long* inBuf = NULL;
char* outBuf = NULL;
ULONG inSize = 0;
ULONG outSize = 0;
PCHAR buffer = NULL;
PMDL mdl = NULL;
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
switch(IrpStack->MajorFunction)
{
case IRP_MJ_CREATE:
break;
case IRP_MJ_CLOSE:
break;
case IRP_MJ_DEVICE_CONTROL:
IoControlCode = IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch(IoControlCode)
{
case IOCTL_PROTECT_CONTROL: //上面的宏
inSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
outSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;
inBuf = (long*)Irp->AssociatedIrp.SystemBuffer;
pid = *inBuf; //将从Ring3 获得 PID 传给 Ding0的 pid
strcpy(Irp->UserBuffer,"SUCCESS!\n");//
break;
default:
break;
}
break;
default:
DbgPrint("Other IRP\n");
break;
}
nStatus = Irp->IoStatus.Status;
IoCompleteRequest(Irp,IO_NO_INCREMENT);//IO请求完成
return nStatus;
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING theRegistryPath)
{
//驱动入口函数
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING DeviceName; //设备名
UNICODE_STRING DeviceLinkString; //设备连接符
PDEVICE_OBJECT deviceObject = NULL;
DbgPrint("驱动程序加载...\n");
RtlInitUnicodeString( &DeviceName, NT_DEVICE_NAME );
//创建一个驱动设备
Status = IoCreateDevice(
DriverObject,
0,
&DeviceName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&deviceObject );
if ( !NT_SUCCESS( Status ) )
{
DbgPrint("无法创建驱动设备");
return Status;
}
RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);//初始化连接符
Status=IoCreateSymbolicLink(&DeviceLinkString,&DeviceName);//创建连接符
if(!NT_SUCCESS(Status)) //创建失败
{
return Status;
}
//分发函数
DriverObject->MajorFunction[IRP_MJ_CREATE] = DeviceDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DeviceDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceDispatch;
DriverObject->DriverUnload = DriverUnload;
//修改 ZwOpenProcess 函数地址 这里修改成功 就可以实现功能了
//修改前要保存原来的函数地址
//DisableMemProtect(); //取消内存保护 _asm cli 看到没有其实是等效的
_asm cli
OldZwOpenProcess =(ZWOPENPROCESS)(SYSTEMSERVICE(ZwOpenProcess)); //保存
_asm sti
//RecoverMemProtect(); //恢复内存保护 _asm sti 看到没有其实是等效的
DisableMemProtect(); //取消内存保护
(ZWOPENPROCESS)(SYSTEMSERVICE(ZwOpenProcess)) = NewZwOpenProcess; //修改
RecoverMemProtect();//恢复内存保护
return STATUS_SUCCESS;
}
//----------------------------Ring3代码-----------------
//main.cpp
//主要功能就是为了传一个PID 给驱动程序
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winioctl.h>
#define IOCTL_CONTROL CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
int main()
{
long pid = 0;
char ret[4096];
DWORD ReBytes = 0;
HANDLE hDevice=CreateFile("\\\\.\\ProtectProcess",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hDevice==INVALID_HANDLE_VALUE)
{
printf("CreateFile() GetLastError reports %d\n",GetLastError());
return 0;
}
memset(ret,0,4096);
printf("Please Input a PID to Protect:");
scanf("%ld",&pid);
DeviceIoControl(hDevice,IOCTL_CONTROL,&pid,sizeof(long),ret,4096,&ReBytes,NULL);
printf("Protect %s\n",ret);
CloseHandle(hDevice);
system("pause");
return 0;
}
//操作工程: