基于HOOK的应用层进程隐藏

基于HOOK的应用层进程隐藏

要求:开发一个可以将指定的用户进程进行隐藏工具,要求在“任务管理器”无法检测的该用户进程,也无法终止该进程。
一、HOOK原理
对于 Hook 技术,可以分为两块,第一块是在 Ring3 层的 Hook,俗称应用层 Hook 技术,而另外一块自然是在 Ring0 层得 Hook,俗称为内核层 Hook 技术,而在 Ring3 层的 Hook 基本上可以分为两种大的类型,第一类即是 Windows 消息的 Hook,第二类则是 Windows API 的 Hook。每个调用的 API 函数地址都保存在 IAT 表中。 API 函数调用时,每个输入节( IMPORT SECTION )所指向的 IAT 结构如下图所示。
基于HOOK的应用层进程隐藏
程序中每个调用API 函数的CALL指令所使用的地址都是相应函数登记在IAT表的地址。所以为了截获API 函数,我们将IAT表中的地址换成用户自己的API PROXY函数地址,这样每个API调用都是先调用用户自己的API PROXY函数。在这个函数中我们可以完成函数名称的记录、参数的记录、调用原来的过程,并在返回时记录结果。
二、常用的应用层获取进程的方法
①使用ToolHelp遍历获取到所有进程。
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
②使用 PSAPI 下的 EnumProcesses 获取到所有进程的 PID,然后提升进程权限为 SE_DEBUG 权限,再调用 OpenProcess 即可打开进程,从而获取到进程的基本信息。
③使用Ntdll.dll中的 API-NtQuerySystemInformation,而Windows 任务管理器就是通过这种方式来获取到所有的进程信息的。所以隐藏进程最简单的办法就是Hook 掉 NtQuerySystemInformation 函数 。
进程保护的话,第一种则是该进程自行终止,第二种情况则是该进程被其他进程给杀掉,第一种情况基本上对于窗口应用程序来说,一般都是用 户点击了右上角的 x 按钮,然后产生 WM_CLOSE 消息,最后由窗口过程退出进程,这种情况下,我们应该是需要允许退出的,也就是进程是可以正常退出的。而第二种情况的话,就是进程被别的进程杀掉,比如在 任务管理器中就可以杀掉绝大部分的应用程序进程,而这里的进程保护就是要实现进程不能够被任务管理器或者其他的进程管理工具杀掉。在 Ring3 中,由一个进程结束其他进程,先会调用API OpenProcess这个我已经在前篇说的很详细了,然后再去调用Kernel32.dll 中的 TerminateProcess,如果追溯这个 TerminateProcess,可以发现,其调用了 Ntdll.dll 中的 NtTerminateProcess API,然后再追溯下去就可以到 ntoskrnl.exe 中的 ZwTerminateProcess 和系统服务NtTerminateProcess 了。所以这里我们选择Hook掉NtTerminateProcess 函数。
三、代码说明
首先定义SSDT需要用到的结构体:
typedef struct _KSYSTEM_SERVICE_TABLE
{
PULONG ServiceTableBase; // SSDT (System Service Dispatch Table)的基地址
PULONG ServiceCounterTableBase; // 用于 checked builds, 包含 SSDT 中每个服务被调用的次数
ULONG NumberOfService; // 服务函数的个数, NumberOfService * 4 就是整个地址表的大小
ULONG ParamTableBase; // SSPT(System Service Parameter Table)的基地址
} KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE;
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe 的服务函数
KSYSTEM_SERVICE_TABLE win32k; // win32k.sys 的服务函数(GDI32.dll/User32.dll 的内核支持)
KSYSTEM_SERVICE_TABLE notUsed1;
KSYSTEM_SERVICE_TABLE notUsed2;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
//导出由 ntoskrnl.exe 所导出的 SSDT
extern PKSERVICE_TABLE_DESCRIPTOR KeServiceDescriptorTable;
在 Hook 任意的系统服务之前,将 SSDT 中的所有系统服务的地址保存或者说是备份到 ULONG 数组中即可。而后在解除 Hook 时,我们就可以从这个 ULONG 数组中取出原有系统服务的地址。
//定义 SSDT(系统服务描述表) 中服务个数的最大数目
//这里定义为 1024 个,实际上在win7 32是 0x0191个
#define MAX_SYSTEM_SERVICE_NUMBER 1024
//用来保存 SSDT 中所有的旧的服务函数的地址
ULONG oldSysServiceAddr[MAX_SYSTEM_SERVICE_NUMBER];
下面是备份、安装、解除的函数
//Name: VOID BackupSysServicesTable()
//Descripion: 用来备份 SSDT 中原有服务的地址,因为我们在解除 Hook 时需要还原 SSDT 中原有地址
VOID BackupSysServicesTable()
{
ULONG i;
for(i = 0; (i < KeServiceDescriptorTable->ntoskrnl.NumberOfService) && (i < MAX_SYSTEM_SERVICE_NUMBER); i++)
{
oldSysServiceAddr[i] = KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[i];
}
}
//Name: NTSTATUS InstallSysServiceHook()
//Descripion: 实现 Hook 的安装,主要是在 SSDT 中用 newService 来替换掉 oldService
NTSTATUS InstallSysServiceHook(ULONG oldService, ULONG newService)
{
ULONG uOldAttr = 0;
EnableWriteProtect(&uOldAttr);
SYSCALL_FUNCTION(oldService) = newService;
DisableWriteProtect(uOldAttr);
return STATUS_SUCCESS;
}
//Name: NTSTATUS UnInstallSysServiceHook()
//Descripion: 实现 Hook 的解除,主要是在 SSDT 中用备份下的服务地址来替换掉 oldService
NTSTATUS UnInstallSysServiceHook(ULONG oldService)
{
ULONG uOldAttr = 0;
EnableWriteProtect(&uOldAttr);
SYSCALL_FUNCTION(oldService) = oldSysServiceAddr[SYSCALL_INDEX(oldService)];
DisableWriteProtect(uOldAttr);
return STATUS_SUCCESS;
}
然后,SSDT 在内存中是具有只读属性保护的,如果你想修改 SSDT 中的内容,你必须先要解除只读属性,也就是要赋予 SSDT 所在的这块内存具有可写属性才行。而这个属性保存在寄存器CR0的第17位,index为16.如果为1表示只读,置为0才可写。
//Name: VOID DisableWriteProtect()
//Descripion: 用来去掉内存的可写属性,从而实现内存只读 //
VOID DisableWriteProtect(ULONG oldAttr)
{
_asm
{
mov eax, oldAttr
mov cr0, eax
sti;
}
}
//Name: VOID EnableWriteProtect()
//Descripion: 用来去掉内存的只读保护,从而实现可以写内存
VOID EnableWriteProtect(PULONG pOldAttr)
{
ULONG uAttr;
_asm
{
cli;
mov eax, cr0;
mov uAttr, eax;
and eax, 0FFFEFFFFh; // CR0 16 BIT = 0
mov cr0, eax;
};
//保存原有的 CRO 属性
pOldAttr = uAttr;
}
上面有两个很重要的宏,即 SYSCALL_FUNCTION 和 SYSCALL_INDEX 宏。
//根据 Zw_ServiceFunction 获取 Zw_ServiceFunction 在 SSDT 中所对应的服务的索引号
#define SYSCALL_INDEX(ServiceFunction) (
(PULONG)((PUCHAR)ServiceFunction + 1))
//根据 Zw_ServiceFunction 来获得服务在 SSDT 中的索引号,
//然后再通过该索引号来获取 Nt_ServiceFunction的地址
#define SYSCALL_FUNCTION(ServiceFunction) KeServiceDescriptorTable->ntoskrnl.ServiceTableBase[SYSCALL_INDEX(ServiceFunction)]
实现进程隐藏只需要Hook NtQuerySystemInformation函数,并实现自定义的函数。
核心代码:
基于HOOK的应用层进程隐藏
上面看到是先调用原来的NtQuerySystemInformation得到返回结果,然后根据我们需要隐藏的进程id,遍历返回结果的进程链表,从链表中摘除该节点,然后在返回给应用层。
四、结果展示
以记事本为例,在执行程序之前,可以看到有notepad进程,左边窗口也有记事本窗口。
全局变量Hide_Process(见第三幅图)用来指定要隐藏的进程名字。
基于HOOK的应用层进程隐藏
执行完隐藏程序之后,弹出隐藏成功的提示。此时任务管理器中已经没有notepad.exe进程了,但左边的记事本窗口还在。
基于HOOK的应用层进程隐藏
基于HOOK的应用层进程隐藏