系统环境:win7 32
参考:逆向工程实战 3.3.2 work item
文章内容:
1,work item 的使用
2,系统是如何管理work item 的
3,如何遍历系统中所有的 work item
1,work item 的使用
work item 类似于系统线程,只是没有创建实际的对象(但实际上work item 还是在线程的环境下执行,只不过用的是已经存在的线程池里面的线程)。
相关结构体:
系统中使用work item 来干事的地方还不少,大概有100多处:
加载驱动函数NtLoadDriver内部会用到
错误日志记录会用到
通过函数 ExQueueWorkItem(PWORK_QUEUE_ITEM WrokItem,WORK_QUEUE_TYPE QueueType)可以完成大部分工作,将结构体WorkItem填好,设置对应的QueueType;ExQueueWorkItem执行之后,等等WorkItem被处理。
typedef enum _WORK_QUEUE_TYPE
{ CriticalWorkQueue =0,
DelayedWorkQueue =1,
HyperCriticalWorkQueue =2,
MaximumWorkQueu =3
}WORK_QUEUE_ITEM;
2,系统是如何管理 work item 的
从函数ExQueueWorkItem 名称不难看出,应该是将work item 插入队列来处理的。看一下ExQueueWorkItem 内部实现:
首先会根据QueueType 选择对应的队列,再调用KiInsertQueue将WorkQueueItem入队;之后检查一些条件,是否设置线程管理事件。使用 work item 的目的是要执行WorkerRoutine 来完成某些操作的,那么是谁负责处理_ExWorkerQueue里面的数据呢?在 ida 中,通过交叉引用可以看到,ExpWorkerThread操作过 _ExWorkerQueue,并发现下面代码片段:
可以看出是ExpWorkerThread 对work item 执行出队操作,并最终调用 WorkerRoutine。
通过相关工具可以查看到,当前系统所有的 worker thread
CriticalWorkQueue 中有5个,DelayedWorkQueue 中有7 个,HyperCriticalWorkQueue 中 有1 个。但是有的 工作线程对应的函数入口是缺失的,函数入口所在的模块也没有(这是为什么呢?)。
这几个类型的线程池中线程数量与系统刚初始化时的值相等,当有更多的 work item 需要处理而线程池中的线程被占用时,系统就会创建额外的线程来完成相应工作。
CriticalWorkQueue
DelayedWorkQueue
HyperCriticalWorkQueue
在win 7 以后的系统中,work item 的管理发生了些变化:
win 10 1803中,工作线程的数量明显增加,其类型也只有CriticalWorkQueue。在实际的测试中,即使插入类型为DelayedWorkQueue 的work item ,也是优先有CriticalWorkQueue 线程池中的线程处理。
3,如何遍历系统中所有的 work item
最开始,我以为通过结构体_work_queue_item 中的成员WorkerRoutine函数来遍历List ,List类型为_list_entry 双向链表来遍历;但是实际测试中WorkQueueItem->WorkerRoutine虽然执行,windbg 中查看 WorkQueueItem 对应内存中内容都是问号'????????',也就无法从_list_entry 入手。仔细想想就知道这是行不通的:WorkerRoutine执行之前肯定是无法遍历的,WorkerRoutine执行时也是无法遍历的;因为WorkerRoutine执行时代表WorkQueueItem已经从队列中移除,List双向链表已经断开。
那么从_ExWorkerQueue中来遍历,应该是可行的。不过这样就有点麻烦,不同版本系统work item 的管理可能不一样;这就导致无法通用,得找特征码。