-
-
[原创]利用链表模拟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函数来释放引用
(^-^)
赞赏
他的文章
赞赏
雪币:
留言: