首页 > 其他分享 >检测隐藏的系统线程

检测隐藏的系统线程

时间:2022-12-20 01:44:26浏览次数:72  
标签:kthread Uint4B 检测 list 线程 Ptr64 ready 隐藏

隐藏系统线程

线程内核对象KTHREAD的ThreadListEntry链接了属于同一个进程的所有线程内核对象。应用层通过ZwQueryInformationThread和进程快照枚举线程就是枚举的这个链表,将此链表进行断链后就可以隐藏线程。

上述隐藏手段只是一种欺骗手段,无法做到真正的隐藏。对抗方法是可以根据PspCidTable全局句柄表找到系统中所有的线程。所有还需要将PspCidTable全局句柄表擦除,之后将ETHREAD的StartAddress、Cid.UniqueThread、Cid.UniqueProcess、Win32StartAddress都擦除即可。

检测断链后的系统线程

因为线程要想运行一定会被操作系统调度执行,所以可以通过检测当前所有处理器正在运行的线程来检测是否存在隐藏的线程。

!running可以查看系统所有正在运行的线程

!ready可以查看系统所有处于就绪队列的线程

这些正在运行的线程和处于就绪队列中的线程信息都保存在各个处理器对应的KPRC结构中

0: kd> dt _KPCR
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x000 GdtBase          : Ptr64 _KGDTENTRY64
   +0x008 TssBase          : Ptr64 _KTSS64
   +0x010 UserRsp          : Uint8B
   +0x018 Self             : Ptr64 _KPCR
   +0x020 CurrentPrcb      : Ptr64 _KPRCB
   +0x028 LockArray        : Ptr64 _KSPIN_LOCK_QUEUE
   +0x030 Used_Self        : Ptr64 Void
   +0x038 IdtBase          : Ptr64 _KIDTENTRY64
   +0x040 Unused           : [2] Uint8B
   +0x050 Irql             : UChar
   +0x051 SecondLevelCacheAssociativity : UChar
   +0x052 ObsoleteNumber   : UChar
   +0x053 Fill0            : UChar
   +0x054 Unused0          : [3] Uint4B
   +0x060 MajorVersion     : Uint2B
   +0x062 MinorVersion     : Uint2B
   +0x064 StallScaleFactor : Uint4B
   +0x068 Unused1          : [3] Ptr64 Void
   +0x080 KernelReserved   : [15] Uint4B
   +0x0bc SecondLevelCacheSize : Uint4B
   +0x0c0 HalReserved      : [16] Uint4B
   +0x100 Unused2          : Uint4B
   +0x108 KdVersionBlock   : Ptr64 Void
   +0x110 Unused3          : Ptr64 Void
   +0x118 PcrAlign1        : [24] Uint4B
   +0x180 Prcb             : _KPRCB

_KPCR中内嵌了一个_KPRCB结构,此结构的CurrentThread,NextThread,DispatcherReadyListHead,SharedReadyQueue 分别存放了正在运行的,即将运行的,就绪的线程。

0: kd> dt _KPRCB
nt!_KPRCB
   +0x000 MxCsr            : Uint4B
   +0x004 LegacyNumber     : UChar
   +0x005 ReservedMustBeZero : UChar
   +0x006 InterruptRequest : UChar
   +0x007 IdleHalt         : UChar
   +0x008 CurrentThread    : Ptr64 _KTHREAD              //当前线程(Running)
   +0x010 NextThread       : Ptr64 _KTHREAD              //下一个要运行的线程
   +0x018 IdleThread       : Ptr64 _KTHREAD              //空闲线程
   .....
   +0x2d08 DeferredReadyListHead : _SINGLE_LIST_ENTRY    //延迟就绪
   +0x7c00 WaitListHead     : _LIST_ENTRY                //等待
   +0x7c10 WaitLock         : Uint8B
   +0x7c18 ReadySummary     : Uint4B                     
   +0x7c1c AffinitizedSelectionMask : Int4B
   +0x7c20 QueueIndex       : Uint4B
   +0x7c24 PrcbPad75        : [2] Uint4B
   +0x7c2c DpcWatchdogSequenceNumber : Uint4B            
   +0x7c30 TimerExpirationDpc : _KDPC
   +0x7c70 ScbQueue         : _RTL_RB_TREE
   +0x7c80 DispatcherReadyListHead : [32] _LIST_ENTRY    //就绪
   .....
   +0x8440 SharedReadyQueueMask : Uint8B
   +0x8448 SharedReadyQueue : Ptr64 _KSHARED_READY_QUEUE //共享就绪

设置DPC延迟过程调用

通过向所有的处理器设置DPC延迟调用来获取这些信息并判断是否存在隐藏的线程。

    PKPCR p_current_kpcr;
    p_current_kpcr = GetCurrentKpcr();
    if (!MmIsAddressValid(p_current_kpcr)) {
        return;
    }
    //CurrentThread
    PVOID current_kprcb = p_current_kpcr->CurrentPrcb;
    _KTHREAD* p_kthread = NULL;
    p_kthread = *(_KTHREAD**)((UCHAR*)current_kprcb + CurrentThread);
    if (MmIsAddressValid(p_kthread)) {
        DbgPrint(" irql = %d,count=%d ,number = %d, tid: %p\r\n", KeGetCurrentIrql(), ++count, KeGetCurrentProcessorNumber(), PsGetThreadId(p_kthread));
    }

    //NextThread
    p_kthread = *(_KTHREAD**)((UCHAR*)current_kprcb + NextThread);
    if (MmIsAddressValid(p_kthread)) {
        DbgPrint(" irql = %d,count=%d ,number = %d, tid: %p\r\n", KeGetCurrentIrql(), ++count, KeGetCurrentProcessorNumber(), PsGetThreadId(p_kthread));
    }

    //enum DispatcherReadyListHead
    for (int i = 0; i < 32; i++) {
        LIST_ENTRY* ready_list_head = (LIST_ENTRY*)((UCHAR*)current_kprcb + DispatcherReadyListHead) + i;
        LIST_ENTRY* ready_list_entry = ready_list_head->Flink;
        while (ready_list_entry != ready_list_head) {
            p_kthread = (PKTHREAD)((UCHAR*)ready_list_entry - WaitListEntry);
            if (MmIsAddressValid(p_kthread) ) {
                DbgPrint(" irql = %d,count=%d ,number = %d, tid: %p\r\n", KeGetCurrentIrql(), ++count, KeGetCurrentProcessorNumber(), PsGetThreadId(p_kthread));
            }
            ready_list_entry = ready_list_entry->Flink;
        }
    }

    //SharedReadyQueue
    for (int i = 0; i < 32; i++) {
        PVOID share_ready_queue = *(PVOID*)((UCHAR*)current_kprcb + SharedReadyQueue);
        LIST_ENTRY* ready_list_head = (LIST_ENTRY*)((UCHAR*)share_ready_queue + 0x10) + i;
        LIST_ENTRY* ready_list_entry = ready_list_head->Flink;
        while (ready_list_entry != ready_list_head) {
            p_kthread = (PKTHREAD)((UCHAR*)ready_list_entry - WaitListEntry);
            if (MmIsAddressValid(p_kthread)) {
                DbgPrint(" irql = %d,count=%d ,number = %d, tid: %p\r\n", KeGetCurrentIrql(), ++count, KeGetCurrentProcessorNumber(), PsGetThreadId(p_kthread));
            }
            ready_list_entry = ready_list_entry->Flink;
        }
    }

设置NMI回调

NMI是一种不可屏蔽的中断,CPU在收到此中断时必须要调用对应的中断处理程序。通过设置NMI回调并定期向每一个CPU发送NMI中断可以检测检测当前CPU核心的线程信息。

测试

可以发现被断链隐藏的系统线程,通过进一步的堆栈回溯可以判断线程是否合法。

参考:
https://bbs.pediy.com/thread-273980.htm

标签:kthread,Uint4B,检测,list,线程,Ptr64,ready,隐藏
From: https://www.cnblogs.com/revercc/p/16993457.html

相关文章

  • Condition-线程通信更高效的方式
     那么引入本篇的主角,Condition,Condition将Object监视器方法(wait、notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提......
  • [编程基础] C++多线程入门8-从线程返回值
    date:2020-05-2917:09:34+0800tags:-编程基础原始C++标准仅支持单线程编程。新的C++标准(称为C++11或C++0x)于2011年发布。在C++11中,引入了新的线程库。因此运行......
  • 毕设系列-检测专题-基于YOLOV5的手势识别系统
    毕设系列-基于YOLOV5的手势识别系统我们之前做过一期基于Yolov5的口罩检测系统(​​手把手教你使用YOLOV5训练自己的目标检测模型-口罩,里面的代码是基于YOLOV56.0开发的,并且......
  • uniapp 自定义app 退出提示 和 隐藏APP至后台
    自定义退出默人情况下,uniappAPP会在第一次按退出时,提示“APPName:再按一次退出应用”,其中APPName是配置的应用名称,“再按一次退出应用”是国际化自定义的内容。在......
  • GPU并行算法读书笔记-chapter3 CUDA线程模型
    目录《GPU并行算法》读书笔记-chapter3CUDA线程模型SIMD模型SIMT模型kernel函数CUDA线程结构blockgrid线程的全局IDCUDA结构与GPU硬件的映射关系CUDA线程设计执行配置什......
  • win32多线程编程与锁
    模拟售票程序。未加锁程序:#include<iostream.h>#include<windows.h>intindex=0;intti=100;DWORDWINAPIfun1(LPVOIDlpParameter);DWORDWINA......
  • Jmeter基础-属性(跨线程组调用函数)
    跨线程组调用函数,需要用到属性1、用_setProperty函数,将值保存成jmeter属性,需要通过BeanShell取样器来执行1.1打开函数助手,选择_setProperty,设置属性名称和属性值(此处的......
  • 二阶段目标检测网络-Mask RCNN 详解
    ROIPooling和ROIAlign的区别MaskR-CNN网络结构骨干网络FPNanchor锚框生成规则实验参考资料MaskRCNN是作者KaimingHe于2018年发表的论文ROIPoo......
  • linux多线程随机数,随机数SecureRandom在Linux下阻塞
    https://blog.csdn.net/weixin_35973521/article/details/116813890-Djava.security.egd=file:/dev/./urandomhttps://blog.csdn.net/weixin_32159771/article/details/1......
  • 并发编程(一)之线程的创建和启动
    并发编程之线程的创建和启动一、线程创建1.1.实现​​Runnable​​接口实现​​Runnable​​​接口,重写​​run​​​方法,实现​​Runnable​​​接口的实现类的实例对象作......