Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
线程
1. _ETHREAD中重要的数据结构
2. 进程与线程的位置关系
3. ETHREAD+0x218 StartAddress
4. 几个重要的知识点
1. _ETHREAD中重要的数据结构
+0x000 Header 可等待对象
+0x018 InitialStack / +0x01c StackLimit / +0x028 KernelStack 线程切换相关
+0x020 TEB 在三环描述了该线程相关信息(fs:[0]在三环指向TEB)
+0x02c DebugActive 如果为-1,则不能使用调试寄存器 Dr0~Dr7
+0x034 ApcState / +0x0e8 ApcQueueLock / +0x138 ApcStatePointer / +0x14c SavedApcState Apc相关
+0x02d State 线程状态:就绪、运行还是等待
+ 0x06c BasePriority 当前线程优先级
+0x070 WaitBlock 等待哪个对象
+0x0e0 ServiceTable 系统服务表机制
+0x134 TrapFrame 进零环时保存的环境
+0x140 PreviousMode 某些内核函数会判断程序是0环调用的还是3环调用的
+0x1b0 ThreadListEntry 双向链表,一个进程的所有线程,都挂在这样一个链表中(图解)。
+0x1ec Cid 当前线程和进程的编号
+0x220 ThreadProcess 当前线程所属的进程
+0x22c ThreadListEntry 双向链表,一个进程的所有线程,都挂在这样一个链表中(图解)。
2. 进程与线程的位置关系
3. ETHREAD+0x218 StartAddress
其表示线程开始的位置,我们可以遍历其全部线程的起始地址,然后检测其属于哪个模块。
如果该线程起始地址不匹配所有模块,则该线程可能是被注入出来的,这个你应该明确。
反制措施:线程的伪装-将线程起始地址写到一个模块地址,该地址jmp回原线程继续执行线程代码。
4. 几个重要的知识点:
1)遍历线程
遍历线程的思路非常简单,从进程KThread+0x50开始一直遍历,注意其对应的偏移量即可。
// 遍历线程Id NTSTATUS TraverseThreadId(HANDLE pId) { PEPROCESS pEprocess; if (!NT_SUCCESS(PsLookupProcessByProcessId(pId,&pEprocess))) { return STATUS_UNSUCCESSFUL; } PULONG pPrcoessListEntry = (PULONG)((PUCHAR)pEprocess + 0x50); // 获取进程链表 PULONG pThreadListEntry = (PULONG)*pPrcoessListEntry; // 获取对应线程 while (pPrcoessListEntry != pThreadListEntry) { DbgPrint("该线程ID:%d ", *MACRO_GetThreadIdThreadListHead(pThreadListEntry)); pThreadListEntry = *pThreadListEntry; } return STATUS_SUCCESS; }
2)进程断链
使用之前的断链方式即可,注意其双向链表,一般情况下要两条线都断,这是必须要明确的。