使用PspTerminateThreadByPointer结束进程

前面我们在TerminateProcess流程追踪、PspTerminateThreadByPointer的实现这篇文章中讲到,TerminateProcess其实是通过遍历进程的线程链表,然后调PspTerminateThreadByPointer这个函数来结束进程的。

但是我们前面也说到可以hook NtTerminateProcess来实现进程防杀,如果我们直接使用PspTerminateThreadByPointer就能绕过这个api了。

但是我们发现PspTerminateThreadByPointer这个函数是未导出的,但是PsTerminateSystemThread这个函数是导出的,而这个函数里面又有调用PspTerminateThreadByPointer。所以我们可以通过导出函数和特征码搜索来得到PspTerminateThreadByPointer的地址。

image

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ULONG getPspTerminateThreadByPointerAddress()
{
    UNICODE_STRING  FunName;
    UCHAR FeatureCode[2]={0x50,0xe8};//特征码push    eax call XXXX
    ULONG   FeatureAddress = 0; //找到的特征码的首地址
    ULONG   FunAddress = 0;
    ULONG   RetAddress = 0;//最终返回
    ULONG   FunAdd = 0;

    RtlInitUnicodeString(&FunName,L"PsTerminateSystemThread");
    FunAdd =(ULONG) MmGetSystemRoutineAddress(&FunName);

    DbgPrint("PsTerminateSystemThread -- %08X",FunAdd);
    //首先获取PsTerminateSystemThread这个函数地址.其实这个就是直接调用的PspTerminateThreadByPointer
    //这个函数本身非常短小,所以通过搜索特征.定位call 然后将call的地址拿出来并进行运算即可

    FeatureAddress = SearchFeatureCodeForAddress(FunAdd,FeatureCode,2,0x27);
    //PsTerminateSystemThread函数需要搜索的长度0x27

    DbgPrint("FeatureAddress -- %08X",FeatureAddress);

    RtlCopyMemory(&FunAddress,(PULONG)(FeatureAddress + 2),4);

    //目标地址=下条指令的地址+机器码E8后面所跟的32位数
    //注意,机器码E8后面所跟的32位数 是 little-endian 格式的,低8位在前,高8位在后
    RetAddress = FunAddress + ( FeatureAddress + 2 + 4 );

    return RetAddress;
}

搜索特征码的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
ULONG SearchFeatureCodeForAddress(ULONG StartAddress,PUCHAR FeatureCode,int FeatureCodeSize,int Search_MaxLength)
{
    PUCHAR Start_scan = (PUCHAR)StartAddress;
    BOOLEAN IsTrue = FALSE;
    ULONG Real_address = 0;
    int i,fi;

    for (i = 0; i < Search_MaxLength; i++)
    {
        if (Start_scan[i] == FeatureCode[0])
        {
            for (fi = 0; fi < FeatureCodeSize; fi++)
            {
                if (Start_scan[i + fi] != FeatureCode[fi])
                {
                    //任何一个字节不同,都标记失败,跳出
                    IsTrue = FALSE;
                    break;
                }else
                {
                    if (fi == FeatureCodeSize-1)
                    {
                        IsTrue = TRUE;
                        //指定长度的字节都相同.认为找到了
                    }
                }
            }

            if (IsTrue == TRUE)
            {
                Real_address = (ULONG)&Start_scan[i];
                break;
            }
        }
    }
    return Real_address;
}

虽然线程都被结束了,那么进程结构又是怎么被销毁的呢?

进程线程都被销毁了就没有代码执行能力,从进程链中删除了也看不到了。

实际销毁对象结构本身是在句柄和引用计数都清0了完成,如果继续持有进程对象引用或句柄,进程对象不会销毁,但也只是留一个空壳 没有实际能力。

本文链接:http://www.alonemonkey.com/killprocess-by-killallthread.html