当前位置:主页 > 查看内容

新的反调试

发布时间:2021-05-11 00:00| 位朋友查看

简介:首先,我们将介绍两种与线程挂起有关的新方法。它们不是最具革命性或有用的,但我会尽力做到最好。 绕过过程冻结 这是Microsoft在19H1中添加的一个可爱的小线程创建标记。有没有想过为什么 在线程创建标志中 存在 漏洞 ?好吧,这个漏洞已经充满了一个我称之……

首先,我们将介绍两种与线程挂起有关的新方法。它们不是最具革命性或有用的,但我会尽力做到最好。

绕过过程冻结

这是Microsoft在19H1中添加的一个可爱的小线程创建标记。有没有想过为什么在线程创建标志中存在漏洞?好吧,这个漏洞已经充满了一个我称之为的标志THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE(我不知道它的实际名字),其值自然是0x40

为了演示它的作用,我将展示PsSuspendProcess的工作原理:

NTSTATUS PsSuspendProcess(_EPROCESS* Process)
{
  const auto currentThread = KeGetCurrentThread();
  KeEnterCriticalRegionThread(currentThread);

  NTSTATUS status = STATUS_SUCCESS;
  if ( ExAcquireRundownProtection(&Process->RundownProtect) )
  {
    auto targetThread = PsGetNextProcessThread(Process, nullptr);
    while ( targetThread )
    {
      // Our flag in action
      if ( !targetThread->Tcb.MiscFlags.BypassProcessFreeze )
        PsSuspendThread(targetThread, nullptr);

      targetThread = PsGetNextProcessThread(Process, targetThread);
    }
    ExReleaseRundownProtection(&Process->RundownProtect);
  }
  else
    status = STATUS_PROCESS_IS_TERMINATING;

  if ( Process->Flags3.EnableThreadSuspendResumeLogging )
    EtwTiLogSuspendResumeProcess(status, Process, Process, 0);

  KeLeaveCriticalRegionThread(currentThread);
  return status;
}

如您所见,NtSuspendProcess该调用PsSuspendProcess将仅忽略带有此标志的线程。另一个好处是该线程也不会被挂起NtDebugActiveProcess!据我所知,一旦使用该线程创建了线程,就无法查询或禁用该标志,因此您不能对此做太多事情。

就其有用性而言,我想说这只是防止转储的一个好方法,当您在Processhacker中单击“暂停”时,会引起混乱,并且该过程继续进行,就像什么也没发生一样。

例子

例如,这是一个有点有趣的代码,它将继续打印I am running。我敢肯定,在倒车时看到这一点会引起很多困惑,为什么地狱会中止他自己的过程。

#define THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE 0x40

NTSTATUS printer(void*) {
    while(true) {
        std::puts("I am running\n");
        Sleep(1000);
    }
    return STATUS_SUCCESS;
}

HANDLE handle;
NtCreateThreadEx(&handle, MAXIMUM_ALLOWED, nullptr, NtCurrentProcess(),
                 &printer, nullptr, THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE,
                 0, 0, 0, nullptr);

NtSuspendProcess(NtCurrentProcess());

多挂点我

继续保持行为不检点的趋势NtSuspendProcess,我们将再次滥用它如何检测我们的流程是否被暂停。

诀窍在于,暂停计数是一个带符号的8位值。就像上一个一样,这里有一些代码可以让您了解内部工作原理:

ULONG KeSuspendThread(_ETHREAD *Thread)
{
  auto irql = KeRaiseIrql(DISPATCH_LEVEL);
  KiAcquireKobjectLockSafe(&Thread->Tcb.SuspendEvent);

  auto oldSuspendCount = Thread->Tcb.SuspendCount;
  if ( oldSuspendCount == MAXIMUM_SUSPEND_COUNT ) // 127
  {
    _InterlockedAnd(&Thread->Tcb.SuspendEvent.Header.Lock, 0xFFFFFF7F);
    KeLowerIrql(irql);
    ExRaiseStatus(STATUS_SUSPEND_COUNT_EXCEEDED);
  }

  auto prcb = KeGetCurrentPrcb();
  if ( KiSuspendThread(Thread, prcb) )
    ++Thread->Tcb.SuspendCount;

  _InterlockedAnd(&Thread->Tcb.SuspendEvent.Header.Lock, 0xFFFFFF7F);
  KiExitDispatcher(prcb, 0, 1, 0, irql);
  return oldSuspendCount;
}

如果您看一下第一个代码示例,PsSuspendProcess它没有错误检查,也不会在乎是否不再挂起线程。那么打电话时会发生什么NtResumeProcess呢?它减少了挂起计数!我们需要做的就是最大限度地利用它,当有人决定暂停并恢复我们时,他们实际上会将计数保持在以前未处于的状态。

例子

下面的简单代码相当有效:

  • Visual Studio-防止它在附加后暂停进程。
  • WinDbg-在连接时被检测到。
  • x64dbg-暂停按钮变得粗略,并带有诸如“程序未运行”之类的错误消息,直到您手动切换到主线程为止。
  • ScyllaHide-使用较旧的版本NtSuspendProcess并导致将其检测到,但是在我报告后已修复。
for(size_t i = 0; i < 128; ++i)
  NtSuspendThread(thread, nullptr);

while(true) {
  if(NtSuspendThread(thread, nullptr) != STATUS_SUSPEND_COUNT_EXCEEDED)
    std::puts("I was suspended\n");
  Sleep(1000);
}

结论

如果有的话,我希望这表明最好不要依赖于NtSuspendProcess您期望的工具来处理潜在的恶意代码或受保护的代码。希望您喜欢这篇文章,并希望在接下来的几周内能有更多的内容。


本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!
上一篇:kafka消费组信息采集异常(hang住)排查 下一篇:没有了

推荐图文


随机推荐