[转](54)线程结构体 ETHREAD,线程断链

 

一、回顾

 

上次课我们学习了进程,我们知道了进程是空间概念,最主要的功能是提供CR3,而线程才是CPU调度的最小单位;

 

更早的时候做SSDT HOOK FindWindow 时我们还了解到了,当3环程序向驱动发起通信时,驱动所属进程就是3环的程序;

 

学习了EPROCESS里的部分成员,其中 ActiveProcessLinks 是进程链表,我们可以通过对其断链实现进程隐藏,DebugPort 是调试端口,抹除它的值可以使三环调试器调试崩溃。

 

本次课我们来学习线程结构体 ETHREAD,了解其部分成员,完成线程链表断链的课后练习。

 

二、KTHREAD

 

nt!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 MutantListHead   : _LIST_ENTRY
   +0x018 InitialStack     : Ptr32 Void
   +0x01c StackLimit       : Ptr32 Void
   +0x020 Teb              : Ptr32 Void
   +0x024 TlsArray         : Ptr32 Void
   +0x028 KernelStack      : Ptr32 Void
   +0x02c DebugActive      : UChar
   +0x02d State            : UChar
   +0x02e Alerted          : [2] UChar
   +0x030 Iopl             : UChar
   +0x031 NpxState         : UChar
   +0x032 Saturation       : Char
   +0x033 Priority         : Char
   +0x034 ApcState         : _KAPC_STATE
   +0x04c ContextSwitches  : Uint4B
   +0x050 IdleSwapBlock    : UChar
   +0x051 Spare0           : [3] UChar
   +0x054 WaitStatus       : Int4B
   +0x058 WaitIrql         : UChar
   +0x059 WaitMode         : Char
   +0x05a WaitNext         : UChar
   +0x05b WaitReason       : UChar
   +0x05c WaitBlockList    : Ptr32 _KWAIT_BLOCK
   +0x060 WaitListEntry    : _LIST_ENTRY
   +0x060 SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x068 WaitTime         : Uint4B
   +0x06c BasePriority     : Char
   +0x06d DecrementCount   : UChar
   +0x06e PriorityDecrement : Char
   +0x06f Quantum          : Char
   +0x070 WaitBlock        : [4] _KWAIT_BLOCK
   +0x0d0 LegoData         : Ptr32 Void
   +0x0d4 KernelApcDisable : Uint4B
   +0x0d8 UserAffinity     : Uint4B
   +0x0dc SystemAffinityActive : UChar
   +0x0dd PowerState       : UChar
   +0x0de NpxIrql          : UChar
   +0x0df InitialNode      : UChar
   +0x0e0 ServiceTable     : Ptr32 Void
   +0x0e4 Queue            : Ptr32 _KQUEUE
   +0x0e8 ApcQueueLock     : Uint4B
   +0x0f0 Timer            : _KTIMER
   +0x118 QueueListEntry   : _LIST_ENTRY
   +0x120 SoftAffinity     : Uint4B
   +0x124 Affinity         : Uint4B
   +0x128 Preempted        : UChar
   +0x129 ProcessReadyQueue : UChar
   +0x12a KernelStackResident : UChar
   +0x12b NextProcessor    : UChar
   +0x12c CallbackStack    : Ptr32 Void
   +0x130 Win32Thread      : Ptr32 Void
   +0x134 TrapFrame        : Ptr32 _KTRAP_FRAME
   +0x138 ApcStatePointer  : [2] Ptr32 _KAPC_STATE
   +0x140 PreviousMode     : Char
   +0x141 EnableStackSwap  : UChar
   +0x142 LargeStack       : UChar
   +0x143 ResourceIndex    : UChar
   +0x144 KernelTime       : Uint4B
   +0x148 UserTime         : Uint4B
   +0x14c SavedApcState    : _KAPC_STATE
   +0x164 Alertable        : UChar
   +0x165 ApcStateIndex    : UChar
   +0x166 ApcQueueable     : UChar
   +0x167 AutoAlignment    : UChar
   +0x168 StackBase        : Ptr32 Void
   +0x16c SuspendApc       : _KAPC
   +0x19c SuspendSemaphore : _KSEMAPHORE
   +0x1b0 ThreadListEntry  : _LIST_ENTRY
   +0x1b8 FreezeCount      : Char
   +0x1b9 SuspendCount     : Char
   +0x1ba IdealProcessor   : UChar
   +0x1bb DisableBoost     : UChar

 

KTHREAD 是 ETHREAD前 0x1c0 字节的TCB属性。下面介绍 KTHREAD 部分属性:

 

+0x000 Header : _DISPATCHER_HEADER

 

和 KPROCESS 类似,开头是一个 Header,拥有此属性的内核对象可以“被等待”(WaitForSingleObject)

 

+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x028 KernelStack : Ptr32 Void

 

InitialStack 初始栈顶,也可以理解成ebp0
StackLimit 栈的大小
KernelStack 线程切换时,旧线程的esp0存到它的KernelStack,然后把新线程的KernelStack写到TSS

 

+0x020 Teb : Ptr32 Void

 

TEB,Thread Environment Block,线程环境块。
大小4KB,位于用户地址空间。
3环可以通过 FS:[0] 找到TEB

 

+0x02c DebugActive : UChar

 

是否处于调试状态

 

+0x034 ApcState : _KAPC_STATE
+0x0e8 ApcQueueLock : Uint4B
+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x14c SavedApcState : _KAPC_STATE

 

APC相关属性。

 

+0x02d State : UChar

 

线程状态:就绪、等待、运行。

 

+0x06c BasePriority : Char

 

其初始值是所属进程的BasePriority值(KPROCESS->BasePriority),以后可以通过KeSetBasePriorityThread()函数重新设定

 

+0x070 WaitBlock : [4] _KWAIT_BLOCK

 

等待哪个对象(WaitForSingleObject)

 

+0x0e0 ServiceTable : Ptr32 Void

 

指向系统服务表基址

 

+0x134 TrapFrame

 

进0环时保存环境

 

+0x140 PreviousMode : Char

 

某些内核函数会判断程序是0环调用还是3环调用的

 

+0x1b0 ThreadListEntry : _LIST_ENTRY

 

双向链表,一个进程所有的线程都挂在一个链表中,挂的就是这个位置,有两个这样的链表

 

在这里插入图片描述

 

三、ETHREAD

 

nt!_ETHREAD
   +0x000 Tcb              : _KTHREAD
   +0x1c0 CreateTime       : _LARGE_INTEGER
   +0x1c0 NestedFaultCount : Pos 0, 2 Bits
   +0x1c0 ApcNeeded        : Pos 2, 1 Bit
   +0x1c8 ExitTime         : _LARGE_INTEGER
   +0x1c8 LpcReplyChain    : _LIST_ENTRY
   +0x1c8 KeyedWaitChain   : _LIST_ENTRY
   +0x1d0 ExitStatus       : Int4B
   +0x1d0 OfsChain         : Ptr32 Void
   +0x1d4 PostBlockList    : _LIST_ENTRY
   +0x1dc TerminationPort  : Ptr32 _TERMINATION_PORT
   +0x1dc ReaperLink       : Ptr32 _ETHREAD
   +0x1dc KeyedWaitValue   : Ptr32 Void
   +0x1e0 ActiveTimerListLock : Uint4B
   +0x1e4 ActiveTimerListHead : _LIST_ENTRY
   +0x1ec Cid              : _CLIENT_ID
   +0x1f4 LpcReplySemaphore : _KSEMAPHORE
   +0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
   +0x208 LpcReplyMessage  : Ptr32 Void
   +0x208 LpcWaitingOnPort : Ptr32 Void
   +0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION
   +0x210 IrpList          : _LIST_ENTRY
   +0x218 TopLevelIrp      : Uint4B
   +0x21c DeviceToVerify   : Ptr32 _DEVICE_OBJECT
   +0x220 ThreadsProcess   : Ptr32 _EPROCESS
   +0x224 StartAddress     : Ptr32 Void
   +0x228 Win32StartAddress : Ptr32 Void
   +0x228 LpcReceivedMessageId : Uint4B
   +0x22c ThreadListEntry  : _LIST_ENTRY
   +0x234 RundownProtect   : _EX_RUNDOWN_REF
   +0x238 ThreadLock       : _EX_PUSH_LOCK
   +0x23c LpcReplyMessageId : Uint4B
   +0x240 ReadClusterSize  : Uint4B
   +0x244 GrantedAccess    : Uint4B
   +0x248 CrossThreadFlags : Uint4B
   +0x248 Terminated       : Pos 0, 1 Bit
   +0x248 DeadThread       : Pos 1, 1 Bit
   +0x248 HideFromDebugger : Pos 2, 1 Bit
   +0x248 ActiveImpersonationInfo : Pos 3, 1 Bit
   +0x248 SystemThread     : Pos 4, 1 Bit
   +0x248 HardErrorsAreDisabled : Pos 5, 1 Bit
   +0x248 BreakOnTermination : Pos 6, 1 Bit
   +0x248 SkipCreationMsg  : Pos 7, 1 Bit
   +0x248 SkipTerminationMsg : Pos 8, 1 Bit
   +0x24c SameThreadPassiveFlags : Uint4B
   +0x24c ActiveExWorker   : Pos 0, 1 Bit
   +0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit
   +0x24c MemoryMaker      : Pos 2, 1 Bit
   +0x250 SameThreadApcFlags : Uint4B
   +0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit
   +0x250 LpcExitThreadCalled : Pos 1, 1 Bit
   +0x250 AddressSpaceOwner : Pos 2, 1 Bit
   +0x254 ForwardClusterOnly : UChar
   +0x255 DisablePageFaultClustering : UChar

 

+0x1ec Cid : _CLIENT_ID

 

进程ID、线程ID

 

+0x220 ThreadsProcess : Ptr32 _EPROCESS

 

指向自己所属进程

 

+0x22c ThreadListEntry : _LIST_ENTRY

 

双向链表,一个进程所有的线程都挂在一个链表中,挂的就是这个位置,有两个这样的链表

 

在这里插入图片描述

 

四、将线程链表中的某个线程进行断链(两个双向链表都要断)

 

问题

 

  1. 观察程序是否正常运行
  2. 在OD/Windbg中观察线程数量是否有变化

 

答案

 

程序仍然正常运行,OD,windbg中线程数量是0。

 

实验步骤

 

!process EPROCESS 可以查看进程里有几个线程,一般来说,第一个就是GUI线程或者主线程。

 

这次作业就不写代码了,直接打开一个程序,用windbg断链,我这里打开的是dbgview.exe,它有两个线程

 

在这里插入图片描述

 

然后在windbg里找到dbgview的EPROCESS(82017990),把 +50 和 +190 两个链表断掉:

 

kd> ed 82017990+194 82017990+194
kd> ed 82017990+190 82017990+190
kd> ed 82017990+50 82017990+50
kd> ed 82017990+54 82017990+54

 

然后用windbg看看线程,发现都没了

 

kd> !process 82017990  
PROCESS 82017990  SessionId: 0  Cid: 00b0    Peb: 7ffde000  ParentCid: 01d4
    DirBase: 095002c0  ObjectTable: e1b610d0  HandleCount:  59.
    Image: Dbgview.exe
    VadRoot 81c4ca50 Vads 65 Clone 0 Private 339. Modified 22. Locked 0.
    DeviceMap e1b8e9e8
    Token                             e1207940
    ElapsedTime                       00:12:24.734
    UserTime                          00:00:00.078
    KernelTime                        00:00:00.500
    QuotaPoolUsage[PagedPool]         57940
    QuotaPoolUsage[NonPagedPool]      2680
    Working Set Sizes (now,min,max)  (3060, 50, 345) (12240KB, 200KB, 1380KB)
    PeakWorkingSetSize                3060
    VirtualSize                       30 Mb
    PeakVirtualSize                   32 Mb
    PageFaultCount                    3198
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      403
    DebugPort                         81ed9ac0

TYPE mismatch for thread object at 82017830


 

重新用OD附加,发现附加失败:

 

在这里插入图片描述

 

打开任务管理器,发现线程数是0

 

在这里插入图片描述

 

但是!程序还在运行!!!

 

在这里插入图片描述


---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/109529264
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

要在 Qt 线程之间传递结构体,可以通过以下步骤实现: 1. 在结构体所在的头文件中包含 QDataStream 头文件。 2. 在结构体中添加一个函数,用于将结构体的数据写入到 QDataStream 中。 3. 在结构体中添加一个函数,用于从 QDataStream 中读取数据并将其填充到结构体中。 4. 在源线程中创建一个 QDataStream 对象,并使用结构体的写入函数将结构体数据写入到流中。 5. 将 QDataStream 对象换为 QByteArray 对象,并使用信号和槽机制将 QByteArray 对象传递到目标线程。 6. 在目标线程中,将 QByteArray 对象换为 QDataStream 对象,并使用结构体的读取函数将数据填充到结构体中。 下面是一个示例代码: ```c++ // MyStruct.h 文件 #include <QDataStream> struct MyStruct { int value1; QString value2; void writeToStream(QDataStream& stream) const { stream << value1 << value2; } void readFromStream(QDataStream& stream) { stream >> value1 >> value2; } }; Q_DECLARE_METATYPE(MyStruct) // 声明 MyStruct 类型,以便在信号和槽中使用 // MainWindow.cpp 文件 void MainWindow::onButtonClicked() { MyStruct data; data.value1 = 123; data.value2 = "Hello"; QByteArray buffer; QDataStream stream(&buffer, QIODevice::WriteOnly); data.writeToStream(stream); emit sendData(buffer); } void MainWindow::onDataReceived(const QByteArray& buffer) { MyStruct data; QDataStream stream(buffer); data.readFromStream(stream); // 处理接收到的数据 } // WorkerThread.cpp 文件 void WorkerThread::run() { qRegisterMetaType<MyStruct>(); // 注册 MyStruct 类型,以便在信号和槽中使用 connect(this, &WorkerThread::sendData, this, &WorkerThread::processData); } void WorkerThread::processData(const QByteArray& buffer) { MyStruct data; QDataStream stream(buffer); data.readFromStream(stream); // 处理接收到的数据 QByteArray newBuffer; QDataStream newStream(&newBuffer, QIODevice::WriteOnly); data.writeToStream(newStream); emit sendDataBack(newBuffer); } void WorkerThread::onDataSentBack(const QByteArray& buffer) { // 处理返回的数据 } ``` 在这个示例中,MyStruct 结构体包含两个成员变量:一个整数和一个字符串。writeToStream 和 readFromStream 函数分别用于将结构体数据写入到 QDataStream 中和从 QDataStream 中读取数据并填充到结构体中。在 MainWindow 类中,onButtonClicked 函数创建一个 MyStruct 实例,并将其写入到 QDataStream 中,然后将 QByteArray 对象发送到 WorkerThread 类中。在 WorkerThread 类中,processData 函数从 QByteArray 对象中读取 MyStruct 实例,并将其处理后,将其再次写入到 QByteArray 对象中,并将其发送回到 MainWindow 类中。在 onDataSentBack 函数中,MainWindow 类处理返回的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值