作者:wzt
原文链接:https://mp.weixin.qq.com/s/08KHfzVltDu3rwZefJ98-Q

NT内核在创建进程时,会将自身插入到nt!PsActiveProcessHead链表中,它保存的是当前系统中活跃的进程链表。

NTSTATUS

PspCreateProcess(

    OUT PHANDLE ProcessHandle,

    IN ACCESS_MASK DesiredAccess,

    IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,

    IN HANDLE ParentProcess OPTIONAL,

    IN ULONG Flags,

    IN HANDLE SectionHandle OPTIONAL,

    IN HANDLE DebugPort OPTIONAL,

    IN HANDLE ExceptionPort OPTIONAL,

    IN ULONG JobMemberLevel

)

{

    PspLockProcessList (CurrentThread);

    InsertTailList (&PsActiveProcessHead, &Process->ActiveProcessLinks);

  PspUnlockProcessList (CurrentThread);

}

在kd查看下相关的数据结构信息:

0: kd> dt nt!_eprocess

    +0x2e8 UniqueProcessId  : Ptr64 Void

    +0x2f0 ActiveProcessLinks : _LIST_ENTRY

Win32 api就是通过调用nt!NtQueryInformationProcess函数获取系统的进程列表,所以隐藏进程的一个思路就是将目标进程从nt!PsActiveProcessHead链表中摘除。

首先看下,如何编写kd脚本,来输出全部的进程列表:

r $t1=$proc;

r @$t2=@$t1;

.while (@$t1 != poi(@$t2+0x2f0)-0x2f0)

{

                .printf "%ma\n", @$t2+0x450;

                r @$t2=poi(@$t2+0x2f0)-0x2f0

}

PsActiveProcessHead是一个双向循环列表,因此只要知道一个节点的地址,就能循环遍历处所有的节点。

$proc 在kd环境中保存的是当前进程的eprocess地址。

Poi(@$t2+0x2f) 为Process->ActiveProcessLinks地址,poi函数用于取地址中的值,类似于c的取指针值操作。Poi(@$t2+0x2f)得到的就是当前节点的下一个节点地址。

那么删除双向链表中的节点,就可以使用如下脚本:

$$ 准备遍历PsActiveProcessHead链表

r @$t1=nt!PsActiveProcessHead;

r @$t2=poi($t1);



.while (@$t2 != @$t1)

{

    $$ 判断是否是需要隐藏的进程号

        .if (poi(@$t2-0x8) == 0n532)

                 {

                        .printf "start hide process %ma(%d)\n", @$t2-0x2f0+0x450,poi(@$t2-0x8) ;

      $$ 删除链表节点


                          eq poi(@$t2)+0x8 poi(@$t2+0x8)

                          eq poi(@$t2+0x8) poi(@$t2)

                          .break

                  }

    $$ 获取下一节点


                  r @$t2=poi(@$t2)

}

Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1611/