首页
社区
课程
招聘
[原创]利用链表模拟PsLookupProcessByProcessId的操作
发表于: 2025-2-16 11:13 2637

[原创]利用链表模拟PsLookupProcessByProcessId的操作

2025-2-16 11:13
2637

介绍

Windows的EPROCESS与ETHREAD结构都被放在双向链表中管理,可以通过遍历链表模拟PsLookupProcessByProcessId与PsLookupThreadByThreadId函数的操作

参数介绍与说明

模拟PsLookupProcessByProcessId:
ExPsLookupProcessByProcessId的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
BOOLEAN ExPsLookupProcessByProcessId(
    IN  HANDLE thProcessId,
    IN  UCHAR* tszImageName,
    OUT PEPROCESS* tpEprocess
)
/*
* 名称: ExPsLookupProcessByProcessId
* 用途: 模拟 PsLookupProcessByProcessId 函数功能
*
* 参数:
*      IN HANDLE thProcessId:   进程ID,可选参数,可为NULL
*      IN UCHAR* tszImageName:  进程名,可选参数,可为NULL
*      OUT PEPROCESS tpEprocess: 目标进程的EPROCESS结构指针
*
* 返回值:
* 若例程成功完成,返回 TRUE
*
* 注意:
* 建议优先考虑使用 PsLookupProcessById
* thProcessId与tszImageName不能同为空
* !!! 仅在例程成功完成后使用 ObDereferenceObject()
*/

模拟PsLookupThreadByThreadId
ExPsLookupThreadByEProcess定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
BOOLEAN ExPsLookupThreadByEProcess(
    IN PEPROCESS pEprocess,
    IN HANDLE hThreadId,
    OUT PETHREAD* pEThread
)
/*
* 名称: ExPsLookupThreadByEProcess
* 用途: 获取进程中的所有线程并返回目标线程
*
* 参数:
* IN PEPROCESS pEprocess:    线程所属进程的EPROCESS结构
* IN HANDLE hThreadId:[Opt]  线程ID(可选参数,NULL表示列出所有线程)
* OUT PETHREAD* pEThread:    目标线程的ETHREAD结构指针
*                            如果hThreadId为NULL,返回进程中的第一个ETHREAD
*
* 返回值: 若返回TRUE表示例程成功完成
*
* 注意:
* 建议优先使用 PsLookupThreadByThreadId
* !!! 调用后必须解除对象引用(Dereference)
*/

实现

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
BOOLEAN ExPsLookupProcessByProcessId(
    IN  HANDLE thProcessId,
    IN  UCHAR* tszImageName,
    OUT PEPROCESS* tpEprocess
)
{
    PEPROCESS pStartEProcess = NULL;
    PEPROCESS pEProcess = NULL;
    PLIST_ENTRY pListEntry = NULL;
    ULONG ulOffset = 0;
    HANDLE hCurrentPid = 0;
    UCHAR szCurrentImageName[16] = { 0 }; // 15 character limit
 
     
    ulOffset = GetActiveProcessLinksOffset();
    if (ulOffset == 0) {
        DbgPrint("[Error] Failed to get ActiveProcessLinks offset.\n");
        return FALSE;
    }
 
    pStartEProcess = PsGetCurrentProcess();
    //Reference the object
    ObReferenceObject(pStartEProcess);
    pEProcess = pStartEProcess;
 
    do {
        hCurrentPid = PsGetProcessId(pEProcess);
        if (hCurrentPid == NULL) {
            DbgPrint("[Warning] PsGetProcessId returned NULL.\n");
            goto NextProcess;
        }
 
        RtlZeroMemory(szCurrentImageName, sizeof(szCurrentImageName));
        UCHAR* pImageName = PsGetProcessImageFileName(pEProcess);
        if (pImageName != NULL) {
            RtlCopyMemory(szCurrentImageName, pImageName, 15);
        }
 
        DbgPrint("[Info] Scanning PID=0x%p, Name=%s\n", hCurrentPid, szCurrentImageName);
 
        BOOLEAN isMatch = FALSE;
        if (thProcessId != NULL && hCurrentPid == thProcessId) {
            isMatch = TRUE;
        }
        else if (tszImageName != NULL &&
            strcmp((const char*)szCurrentImageName, (const char*)tszImageName) == 0) {
            isMatch = TRUE;
        }
 
        if (isMatch) {
            DbgPrint("[Success] Found target process: PID=0x%p, Name=%s\n", hCurrentPid, szCurrentImageName);
            *tpEprocess = pEProcess;
            //If the routine finished successfully,
            //Reference the OUT EPROCESS for further operation
            //Dereference previous EPROCESS,or BSOD will occur :(
            ObReferenceObject(pEProcess);
            ObDereferenceObject(pStartEProcess);
            return TRUE;
        }
 
    NextProcess:
        //Move to next
        pListEntry = (PLIST_ENTRY)((PUCHAR)pEProcess + ulOffset);
        PEPROCESS pNextEProcess = (PEPROCESS)((PUCHAR)pListEntry->Flink - ulOffset);
 
        // Reference the next
        ObDereferenceObject(pEProcess);
        pEProcess = pNextEProcess;
        ObReferenceObject(pEProcess);
 
    } while (pEProcess != pStartEProcess);
 
    //If the routine failed,
    //Dereference it.
    ObDereferenceObject(pStartEProcess);
    DbgPrint("[Info] Target process not found.\n");
    return FALSE;
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
BOOLEAN ExPsLookupThreadByEProcess(
    IN PEPROCESS pEprocess,
    IN HANDLE hThreadId,
    OUT PETHREAD* pEThread
)
{
    BOOLEAN Status = FALSE;
    BOOLEAN fReachLast = FALSE;
    PETHREAD pStartEThread = NULL;
    PETHREAD lpEThread = NULL;
    PETHREAD pEndEThread = NULL;
    PLIST_ENTRY pListEntry = NULL;
    HANDLE hCurrentTid = NULL;
    HANDLE hBelongingPid = NULL;
    ULONG ulTOffset = 0x0;
    ULONG ulThreadLTOffset = 0x0;
 
    // The offset of ThreadListHead in EPROCESS only use once
    // ThreadListEntry act as ActiveProcessLink
    ulTOffset = GetThreadListHeadOffset();
    ulThreadLTOffset = GetThreadListEntryOffset();
    if (ulTOffset == 0 || ulThreadLTOffset == 0) {
        DbgPrint("[Error] Failed to get the offset.\n");
        return FALSE;
    }
 
    //Get the first and final ETHREAD
    //Use Blink to get the last one,
    pListEntry = (PLIST_ENTRY)((PUCHAR)pEprocess + ulTOffset);
    pStartEThread = (PETHREAD)((PUCHAR)pListEntry->Flink - ulThreadLTOffset);
    pEndEThread = (PETHREAD)((PUCHAR)pListEntry->Blink - ulThreadLTOffset);
 
    if (pStartEThread == NULL || pEndEThread == NULL) {
        DbgPrint("[Error] Failed to get first or last ethread.\n");
        return FALSE;
    }
 
    //Use ObReferenceObject to avoid interruption
    //Rember to dereference it
    ObReferenceObject(pStartEThread);
    ObReferenceObject(pEndEThread);
    lpEThread = pStartEThread;
 
    hBelongingPid = PsGetProcessId(pEprocess);
    DbgPrint("[Info] Started scanning threads in process: 0x%p,pid: %p\n", pEprocess, hBelongingPid);
 
     
    do {
        hCurrentTid = PsGetThreadId(lpEThread);
        if (hCurrentTid == NULL)
        {
            DbgPrint("[Warning] PsGetThreadId returned NULL.\n");
            goto NextThread;
        }
 
        DbgPrint("[Info] Scanning Thread ID: 0x%p, ETHREAD:0x%p\n", hCurrentTid, lpEThread);
 
        if (hCurrentTid == hThreadId) //found it!
        {
            DbgPrint("[Success] Found target thread: TID=0x%p\n", hCurrentTid);
            *pEThread = lpEThread;
            //If the routine finished successfully,
            //Reference the OUT ETHREAD(pEThread) for further operation
            //Dereference previous ETHREAD,or BSOD will occur :(
            ObReferenceObject(lpEThread);
            ObDereferenceObject(pStartEThread);
            ObDereferenceObject(pEndEThread);
            Status = TRUE;
            return Status;
        }
 
    NextThread:
        pListEntry = (PLIST_ENTRY)((PUCHAR)lpEThread + ulThreadLTOffset);
        PETHREAD pNextEThread = (PETHREAD)((PUCHAR)pListEntry->Flink - ulThreadLTOffset);
 
        //Dereference current ETHREAD and reference next
        ObDereferenceObject(lpEThread);
        lpEThread = pNextEThread;
 
        if (lpEThread != pEndEThread && !fReachLast) {
            //DbgBreakPoint();//test
            ObReferenceObject(lpEThread);
        }
        else {
            //Check it first.
            DbgPrint("[Info]Got the last\n");
            fReachLast = TRUE;
        }
    } while (lpEThread != pEndEThread && fReachLast);
 
    ObDereferenceObject(pStartEThread);
    ObDereferenceObject(pEndEThread);
    if (!Status && hThreadId != NULL) {
        DbgPrint("[Info] Target thread not found.\n");
    }
    else {
        DbgPrint("[Info] All thread are listed here.\n");
        DbgPrint("[Info] return the first ETHREAD\n");
        *pEThread = pStartEThread;
        Status = TRUE;
        ObReferenceObject(pStartEThread);
    }
    return Status;
}

Windbg的源代码调试显示中文字符有时乱码,所以采用了英文注释。

附注

使用的其他函数

1
2
3
4
ULONG GetActiveProcessLinksOffset();
ULONG GetThreadListHeadOffset();
ULONG GetThreadListEntryOffset();
ULONG GetThreadListLockOffset();

这几个偏移获取函数采用硬编码,在Windbg看一下即可
调用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
BOOLEAN exRet;
HANDLE exHandle=NULL;
PEPROCESS expEprocess=NULL;
PETHREAD expEthread = NULL;
UCHAR* exszImageName = (UCHAR*)"notepad.exe";
DbgPrint("Started call function\n");
exRet = ExPsLookupProcessByProcessId(exHandle, exszImageName ,&expEprocess);
if (exRet) {
    DbgPrint("Get eprocess:%p\n", expEprocess);
    if (expEprocess != NULL) {
        ExPsLookupThreadByEProcess(expEprocess, NULL, &expEthread);
        DbgPrint("Get ethread:%p\n", expEthread);
        ObDereferenceObject(expEprocess);
        ObDereferenceObject(expEthread);
    }
     
}
else {
    DbgPrint("Error!%d\n", exRet);
}

这几个函数无法找到断链隐藏的线程或进程
一定要调用ObDereferenceObject函数来释放引用
(^-^)


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回