SSDT HOOK已经是很老的手艺了,但对新手来说照样有一些嚼头的。凭据通例的做法,我们应该挂钩ZwTerminateProcess函数,不外这个函数仅有两个参数,其中一个是历程句柄,它指定了需要被竣事的历程。 由于我们不能直接从历程句柄获取有关历程的一些信息,这就使得一些“懒惰”的家伙实验找一些捷径。由于要想获得句柄通常都需要首先挪用ZwOpenProcess,而ZwOpenProcess需要通报PID作为标识,于是有些人就不管ZwTerminateProcess函数了,改为挂钩ZwOpenProcess,这样你无法打开我们关注的历程,也就无法获得句柄,自然也就没办法竣事我们的历程。 乍一看,这照样真是一种思绪巧妙的做法。不外,点心毕竟是不能当正餐吃的,这样的做法作为暂且救火也没什么,要是一直抱着它不愿放弃就大错特错了。 实际上,这种做法是有副作用的,好比通常在ring3使用PSAPI枚举历程时,我们经常使用下面的代码来获取历程完整路径: HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessId[i]); if(hProcess) { EnumProcessModules(hProcess,amp;hModule,sizeof(hModule),amp;cbNeeded); GetModuleFileNameEx(hProcess,hModule,szPath,sizeof(szPath)); // 输出到列表框 } CloseHandle(hProcess); 若是我们挂钩了ZwOpenProcess,那么使用这种方式枚举历程的程序就可能无法获得我们的珍爱历程信息。有人会说这样还欠好?不要忘了,我们的目的不是隐藏,而是珍爱。自我珍爱是任何人都无可非议的做法,但实验隐藏自身却会让人嫌疑你的目的。 实在说穿了,我们挂钩ZwOpenProcess的基本原因是不会使用句柄获得历程信息。好吧,我们现在就先容若何通过历程句柄获取信息。 在炉子的《API HOOK实现ring3的历程珍爱》一文中给出了一种解决方式,即使用NTDLL导出的Zw(Nt)QueryInformationProcess函数。 下面我们看看这个函数的声明: NTSYSAPI NTSTATUS NTAPI NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength ); 这个函数的关键是第二个参数,它决议了第三个参数输出什么结构。现在我们可以将其填写为ProcessBasicInformation,这样我们会获得一个PROCESS_BASIC_INFORMATION结构的信息输出。而PROCESS_BASIC_INFORMATION结构的UniqueProcessId子域即是该句柄代表的历程ID。 下面是通过句柄获得PID的代码: ULONG lRet; PVOID pBuffer; PROCESS_BASIC_INFORMATION *pbi; pBuffer = ExAllocatePool(PagedPool, sizeof(PROCESS_BASIC_INFORMATION)); ZwQueryInformationProcess(ProcessHandle, ProcessBasicInformation, pBuffer, sizeof(PROCESS_BASIC_INFORMATION), amp;lRet); pbi = (struct _PROCESS_BASIC_INFORMATION *)pBuffer; KdPrint(("ProcessHandle代表历程d!", pbi.UniqueProcessId)); 有了PID剩下的就好办了,我们可以使用函数PsLookupProcessByProcessId来获取该历程的EPROCESS结构,这个结构中就有历程名的信息。