• 【驱动笔记12】SSDT HOOK实现进程保护

    2009-02-17

    分类:内核编程

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://nokyo.blogbus.com/logs/35320995.html

    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,&hModule,sizeof(hModule),&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), 

    &lRet);

    pbi = (struct _PROCESS_BASIC_INFORMATION *)pBuffer;

    KdPrint(("ProcessHandle代表进程%d!", pbi.UniqueProcessId));

    有了PID剩下的就好办了,我们可以使用函数PsLookupProcessByProcessId来获取该进程的EPROCESS结构,这个结构中就有进程名的信息。

    与之类似,文件句柄可以通过Zw(Nt)QueryInformationFile函数,注册表句柄可以通过Zw(Nt)QueryKey函数来获取有关信息。

    另外还有一种较为通用的方法,即使用ObReferenceObjectByHandle函数和ObQueryNameString函数配合使用。它可以通过进程句柄、文件句柄、注册表句柄获取完整的路径信息。





    评论

  • 老写技术文章啊,多没意思,放松些撒。。
    grayfox回复城市流浪猫说:
    行,下次写点儿其他的东西
    2009-02-23 18:02:57