看流星社区

 找回密码
 注册账号
查看: 2164|回复: 0

检测当前进程是否被挂起

[复制链接]

该用户从未签到

发表于 2017-6-1 12:52:13 | 显示全部楼层 |阅读模式
  1. KTHREAD的结构:
  2.    +0x16c SuspendApc       : _KAPC
  3.    +0x19c SuspendSemaphore : _KSEMAPHORE
  4.    +0x1b0 ThreadListEntry  : _LIST_ENTRY
  5.    +0x1b8 FreezeCount      : Char
  6.    +0x1b9 SuspendCount     : Char
复制代码

在调用SuspendThread时,SuspendCount 自加1;如果大于0表示挂起了。调用ResumeThread时,SuspendCount 自减1;如果等于0,系统会恢复线程。
当挂起和恢复时,FreezeCount必须为0,否则挂起和恢复操作都直接pass了 .所以,可以修改 SuspendCount 和 FreezeCount来达到反挂起的目的。


证据:
应用层或内核调用ZwSuspendThread/Process
最后会执行到NtSuspendThread/Process


IDA:
以NtSuspendProcess为例子
NtSuspendProcess-&gtsSuspendProcess-&gtsSuspendThread->KeSuspendThread->


NtSuspendProcess
  1. PAGE:006D5143 ; int __stdcall NtSuspendProcess(HANDLE Handle)
  2. PAGE:006D5143 _NtSuspendProcess@4 proc near           ; DATA XREF: .text:0045CCF4o
  3. PAGE:006D5143
  4. PAGE:006D5143 AccessMode      = byte ptr -8
  5. PAGE:006D5143 Object          = dword ptr -4
  6. PAGE:006D5143 Handle          = dword ptr  8
  7. PAGE:006D5143
  8. PAGE:006D5143                 mov     edi, edi
  9. PAGE:006D5145                 push    ebp
  10. PAGE:006D5146                 mov     ebp, esp
  11. PAGE:006D5148                 push    ecx
  12. PAGE:006D5149                 push    ecx
  13. PAGE:006D514A                 mov     eax, large fs:124h
  14. PAGE:006D5150                 mov     al, [eax+13Ah]
  15. PAGE:006D5156                 push    esi
  16. PAGE:006D5157                 push    0               ; HandleInformation
  17. PAGE:006D5159                 mov     [ebp+AccessMode], al
  18. PAGE:006D515C                 lea     eax, [ebp+Object]
  19. PAGE:006D515F                 push    eax             ; Object
  20. PAGE:006D5160                 push    dword ptr [ebp+AccessMode] ; AccessMode
  21. PAGE:006D5163                 push    ds:_PsProcessType ; ObjectType
  22. PAGE:006D5169                 push    800h            ; DesiredAccess
  23. PAGE:006D516E                 push    [ebp+Handle]    ; Handle
  24. PAGE:006D5171                 call    _ObReferenceObjectByHandle@24 ; 根据句柄获得Object
  25. PAGE:006D5176                 mov     esi, eax        ; mov esi pEprocess
  26. PAGE:006D5178                 test    esi, esi
  27. PAGE:006D517A                 jl      short Exit      ; 为NULL退出
  28. PAGE:006D517C                 push    [ebp+Object]    ; pEprocess
  29. PAGE:006D517F                 call    _PsSuspendProcess@4 ; PsSuspendProcess(PERPCOESS pProcessObject)
  30. PAGE:006D5184                 mov     ecx, [ebp+Object] ; Object
  31. PAGE:006D5187                 mov     esi, eax
  32. PAGE:006D5189                 call    @ObfDereferenceObject@4 ; ObfDereferenceObject(x)
  33. PAGE:006D518E
  34. PAGE:006D518E Exit:                                   ; CODE XREF: NtSuspendProcess(x)+37j
  35. PAGE:006D518E                 mov     eax, esi
  36. PAGE:006D5190                 pop     esi
  37. PAGE:006D5191                 leave
  38. PAGE:006D5192                 retn    4
  39. PAGE:006D5192 _NtSuspendProcess@4 endp
复制代码


调用ObreferenceObjectByHandle获取EPROCESS
然后调用PsSuspendProcess
  1. NTSTATUS __stdcall NtSuspendProcess(HANDLE Handle)
  2. {
  3.   NTSTATUS v1; // esi@1
  4.   PVOID Object; // [sp+8h] [bp-4h]@1
  5.   v1 = ObReferenceObjectByHandle(Handle, 0x800u, PsProcessType, KeGetCurrentThread()->PreviousMode, &Object, 0);
  6.   if ( v1 >= 0 )
  7.   {
  8.     v1 = PsSuspendProcess((PEPROCESS)Object);
  9.     ObfDereferenceObject(Object);
  10.   }
  11.   return v1;
  12. }
复制代码


PsSuspendProcess 先看看RundownProtect是否有效
据我所知,这个结构是为了防止windows在操作过程中防止进程/线程退出 从返回0xC0000010A可以很明显的知道
如果将此结构置1 有保护进程的作用
然后通过PsGetNextProcessThread获得进程的全部线程 然后分别调用PsSuspendThread暂停
  1. // PsSuspendProcess(PERPCOESS pProcessObject)
  2. int __stdcall PsSuspendProcess(PEPROCESS pEprocess)
  3. {
  4.   _KTHREAD *v1; // esi@1
  5.   volatile signed __int32 *v2; // edi@1
  6.   signed __int32 v3; // ecx@1
  7.   int i; // eax@3
  8.   void *v5; // ebx@5
  9.   signed __int32 v6; // ecx@6
  10.   int v7; // edi@8
  11.   v1 = KeGetCurrentThread();
  12.   --v1->KernelApcDisable;
  13.   v2 = (volatile signed __int32 *)&pEprocess->RundownProtect;
  14.   v3 = pEprocess->RundownProtect.Count & 0xFFFFFFFE;
  15.   if ( _InterlockedCompareExchange((volatile signed __int32 *)&pEprocess->RundownProtect, v3 + 2, v3) != v3
  16.     && ExfAcquireRundownProtection(v2) != 1 )
  17.   {
  18.     v7 = 0xC000010A;//进程已退出
  19.   }
  20.   else
  21.   {
  22.     for ( i = PsGetNextProcessThread((int)pEprocess, 0); ; i = PsGetNextProcessThread((int)pEprocess, v5) )
  23.     {
  24.       v5 = (void *)i;
  25.       if ( !i )
  26.         break;
  27.       PsSuspendThread((_ETHREAD *)i, 0);
  28.     }
  29.     v6 = *v2 & 0xFFFFFFFE;
  30.     if ( _InterlockedCompareExchange(v2, v6 - 2, v6) != v6 )
  31.       ExfReleaseRundownProtection(v2);
  32.     v7 = 0;
  33.   }
  34.   ++v1->KernelApcDisable;
  35.   if ( !v1->KernelApcDisable
  36.     && ($0453357706F129727BD247106FA17C54 *)v1->ApcState.ApcListHead[0].Flink != &v1->64
  37.     && !v1->SpecialApcDisable )
  38.     KiCheckForKernelApcDelivery();
  39.   return v7;
  40. }
复制代码
PsSuspendThread
跟Process的操作差不多
这里可以发现如果我们将pEthead->CrossThreadFlags置0 也可以防止挂起
不过这不是主题...
  1. // PsSuspendThread(PETHREAD,OUT orgCount)
  2. unsigned int __stdcall PsSuspendThread(_ETHREAD *ThreadObject, int a2)
  3. {
  4.   _KTHREAD *v2; // esi@1
  5.   signed __int32 v3; // edx@1
  6.   signed __int32 v4; // edx@7
  7.   int v6; // [sp+18h] [bp-20h]@1
  8.   unsigned int v7; // [sp+1Ch] [bp-1Ch]@4
  9.   v6 = 0;
  10.   v2 = KeGetCurrentThread();
  11.   --v2->KernelApcDisable;
  12.   v3 = (unsigned int)ThreadObject->RundownProtect & 0xFFFFFFFE;
  13.   if ( _InterlockedCompareExchange((volatile signed __int32 *)&ThreadObject->RundownProtect, v3 + 2, v3) != v3
  14.     && !ExfAcquireRundownProtection((volatile signed __int32 *)&ThreadObject->RundownProtect) )
  15.   {
  16.     v7 = 0xC000004B;//线程退出
  17.     goto LABEL_10;
  18.   }
  19.   if ( !(ThreadObject->CrossThreadFlags & 1) )
  20.   {
  21.     v6 = KeSuspendThread(ThreadObject);
  22.     v7 = 0;
  23.     if ( !(ThreadObject->CrossThreadFlags & 1) )
  24.       goto LABEL_7;
  25.     KeForceResumeThread((int)ThreadObject);
  26.     v6 = 0;
  27.   }
  28.   v7 = 0xC000004B;//线程退出
  29. LABEL_7:
  30.   v4 = (unsigned int)ThreadObject->RundownProtect & 0xFFFFFFFE;
  31.   if ( _InterlockedCompareExchange((volatile signed __int32 *)&ThreadObject->RundownProtect, v4 - 2, v4) != v4 )
  32.     ExfReleaseRundownProtection(&ThreadObject->RundownProtect);
  33. LABEL_10:
  34.   ++v2->KernelApcDisable;
  35.   if ( !v2->KernelApcDisable
  36.     && ($0453357706F129727BD247106FA17C54 *)v2->ApcState.ApcListHead[0].Flink != &v2->64
  37.     && !v2->SpecialApcDisable )
  38.     KiCheckForKernelApcDelivery();
  39.   if ( a2 )
  40.     *(_DWORD *)a2 = v6;
  41.   return v7;
  42. }
复制代码


KeSuspendThread:
这下面大部分操作都在KTHREAD结构下
这里又发现可以保护线程的ThreadFlags~20 andFreezeCount >1 andSuspendApc.Inserted != 1
这不是主题...
++m_ThreadObject->SuspendCount;//这句才是重点

暂停一次这个+1
  1. int __usercall KeSuspendThread@<eax>(_KTHREAD *ThreadObject@<eax>)
  2. {
  3.   _KTHREAD *m_ThreadObject; // esi@1
  4.   char *v2; // ebx@1
  5.   volatile signed __int32 *v3; // edi@1
  6.   int v4; // eax@8
  7.   int v5; // esi@15
  8.   unsigned __int32 v7; // [sp+Ch] [bp-Ch]@1
  9.   KIRQL NewIrql; // [sp+10h] [bp-8h]@1
  10.   int v9; // [sp+14h] [bp-4h]@1
  11.   int v10; // [sp+14h] [bp-4h]@8
  12.   m_ThreadObject = ThreadObject;
  13.   v2 = (char *)&ThreadObject->SuspendSemaphore.SuspendSemaphore.Header.0;
  14.   v9 = 0;
  15.   NewIrql = KeRaiseIrqlToDpcLevel();
  16.   v7 = __readfsdword(32);
  17.   v3 = (volatile signed __int32 *)&m_ThreadObject->ApcQueueLock;
  18.   while ( _InterlockedExchange(v3, 1) )
  19.   {
  20.     do
  21.     {
  22.       ++v9;
  23.       if ( v9 & HvlLongSpinCountMask || !(HvlEnlightenments & 0x40) )
  24.         _mm_pause();
  25.       else
  26.         HvlNotifyLongSpinWait(v9);
  27.     }
  28.     while ( *v3 );
  29.   }
  30.   v4 = m_ThreadObject->SuspendCount;
  31.   v10 = v4;
  32.   if ( v4 == 0x7F )
  33.   {
  34.     _InterlockedAnd(v3, 0);
  35.     KfLowerIrql(NewIrql);
  36.     RtlRaiseStatus(0xC000004A);
  37.   }
  38.   if ( m_ThreadObject->ThreadFlags & 0x20 )
  39.   {
  40.     ++m_ThreadObject->SuspendCount;//这句很重要
  41.     if ( !v4 && !m_ThreadObject->FreezeCount )
  42.     {
  43.       if ( m_ThreadObject->SuspendApc.Inserted == 1 )
  44.       {
  45.         v5 = 0;
  46.         while ( _interlockedbittestandset((volatile signed __int32 *)v2, 7u) )
  47.         {
  48.           do
  49.           {
  50.             ++v5;
  51.             if ( v5 & HvlLongSpinCountMask || !(HvlEnlightenments & 0x40) )
  52.               _mm_pause();
  53.             else
  54.               HvlNotifyLongSpinWait(v5);
  55.           }
  56.           while ( (char)*(_DWORD *)v2 < 0 );
  57.         }
  58.         --*((_DWORD *)v2 + 1);
  59.         _InterlockedAnd((volatile signed __int32 *)v2, 0xFFFFFF7F);
  60.       }
  61.       else
  62.       {
  63.         m_ThreadObject->SuspendApc.Inserted = 1;
  64.         KiInsertQueueApc(NewIrql);
  65.       }
  66.     }
  67.   }
  68.   _InterlockedAnd(v3, 0);
  69.   KiExitDispatcher(v7, 0, 1, 0, NewIrql);
  70.   return v10;
  71. }
复制代码
来看下恢复的
直接看KeResumeThread
重点看这句
if ( m_pThreadObject->SuspendCount )     // 如果暂停数大于0

暂停数大于0就准备下一步操作
这里我们用来判断这个线程/进程有没有被挂起
  1. int __usercall KeResumeThread@<eax>(_ETHREAD *pThreadObject@<eax>)
  2. {
  3.   _KTHREAD *m_pThreadObject; // esi@1
  4.   char *v2; // edi@1
  5.   volatile signed __int32 *v3; // ebx@1
  6.   char m_last_count; // cl@9
  7.   int v5; // esi@11
  8.   KIRQL NewIrql; // [sp+Ch] [bp-Ch]@1
  9.   unsigned __int32 v8; // [sp+10h] [bp-8h]@1
  10.   int v9; // [sp+14h] [bp-4h]@1
  11.   int ret_OrgSuspendCount; // [sp+14h] [bp-4h]@8
  12.   m_pThreadObject = (_KTHREAD *)pThreadObject;
  13.   v2 = &pThreadObject->gap_0[offsetof(_KTHREAD, SuspendSemaphore)];
  14.   v9 = 0;
  15.   NewIrql = KeRaiseIrqlToDpcLevel();
  16.   v8 = __readfsdword(32);
  17.   v3 = (volatile signed __int32 *)&m_pThreadObject->ApcQueueLock;
  18.   while ( _InterlockedExchange(v3, 1) )
  19.   {
  20.     do
  21.     {
  22.       ++v9;
  23.       if ( v9 & HvlLongSpinCountMask || !(HvlEnlightenments & 0x40) )
  24.         _mm_pause();
  25.       else
  26.         HvlNotifyLongSpinWait(v9);
  27.     }
  28.     while ( *v3 );
  29.   }
  30.   ret_OrgSuspendCount = m_pThreadObject->SuspendCount;
  31.   if ( m_pThreadObject->SuspendCount )          // 如果暂停数大于0
  32.   {
  33.     m_last_count = m_pThreadObject->SuspendCount - 1;// -1
  34.     m_pThreadObject->SuspendCount = m_last_count;
  35.     if ( !m_last_count && !m_pThreadObject->FreezeCount )
  36.     {
  37.       v5 = 0;
  38.       while ( _interlockedbittestandset((volatile signed __int32 *)v2, 7u) )
  39.       {
  40.         do
  41.         {
  42.           ++v5;
  43.           if ( v5 & HvlLongSpinCountMask || !(HvlEnlightenments & 0x40) )
  44.             _mm_pause();
  45.           else
  46.             HvlNotifyLongSpinWait(v5);
  47.         }
  48.         while ( (char)*(_DWORD *)v2 < 0 );
  49.       }
  50.       ++*((_DWORD *)v2 + 1);
  51.       KiSignalSynchronizationObject(v8, v2);
  52.       _InterlockedAnd((volatile signed __int32 *)v2, 0xFFFFFF7F);
  53.     }
  54.   }
  55.   _InterlockedAnd(v3, 0);
  56.   KiExitDispatcher(v8, 0, 1, 0, NewIrql);
  57.   return ret_OrgSuspendCount;
  58. }
复制代码
这也是Pchunter判断的方法,别问我怎么知道的....
点击按钮快速添加回复内容: 支持 高兴 激动 给力 加油 苦寻 生气 回帖 路过 感恩
您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

小黑屋|手机版|Archiver|看流星社区 |网站地图

GMT+8, 2024-3-19 17:04

Powered by Kanliuxing X3.4

© 2010-2019 kanliuxing.com

快速回复 返回顶部 返回列表