Гайд Анализ работы KM дебаггера

✊Rot Front✊
Пользователь
Статус
Оффлайн
Регистрация
2 Июл 2020
Сообщения
132
Реакции[?]
258
Поинты[?]
86K
Итак,поскольку мне было скучно и мне было интересно как античиты обнаруживают KM дебаггер,то я решил провести маленький анализ.
Начнём по порядку.

Я есть закон!

Первая функция,которая встречает нас в экспорте - KdEnableDebugger.
Я решил выбрать данную функцию т.к она экспортируется и вызывает другие интересные нас функции.

Сама функция выглядит следующем образом:
C++:
NTSTATUS KdEnableDebugger()
{
  int v0; // edi
  NTSTATUS nt_status; // ebx

  v0 = KeRelaxTimingConstraints(1);
  nt_status = KdEnableDebuggerWithLock(TRUE);
  KeRelaxTimingConstraints(v0);
  return nt_status;
}

Поскольку для нас тут нет ничего интересного,то переходим к KdEnableDebuggerWithLock

C++:
NTSTATUS __fastcall KdEnableDebuggerWithLock(BOOLEAN TakeLock)
{
  unsigned __int8 CurrentIrql; // bl
  int v4; // eax

  CurrentIrql = 0;
  if ( KdPitchDebugger )
    return STATUS_DEBUGGER_INACTIVE;
  if ( KdBlockEnable )
    return STATUS_ACCESS_DENIED;
  if ( TakeLock )
  {
    CurrentIrql = KeGetCurrentIrql();
    __writecr8(DISPATCH_LEVEL);
    KxAcquireSpinLock(&KdDebuggerLock);
  }
  KdDisableCountSaved = KdDisableCount;
  if ( KdDisableCount )
  {
    --KdDisableCount;
    if ( KdDisableCountSaved == 1 && KdPreviouslyEnabled )
    {
      if ( TakeLock )
      {
        KdPowerTransitionEx(0x40000001i64, 0i64);
        KdpDebugRoutineSelect = TRUE;
        LOBYTE(KdDebuggerEnabled) = TRUE;
        KERNEL_KUSER_SHARED_DATA ->KdDebuggerEnabled = TRUE;   //         MEMORY[0xFFFFF780000002D4] = 1;
        KdpRestoreAllBreakpoints();
      }
      else
      {
        PoHiberInProgress = TRUE;
        KdInitSystem(0, 0i64);
        KdpRestoreAllBreakpoints();
        PoHiberInProgress = 0;
      }
    }
    if ( TakeLock )
    {
      KxReleaseSpinLock(&KdDebuggerLock);
      __writecr8(CurrentIrql);
    }
  }
  else
  {
    if ( TakeLock )
    {
      KxReleaseSpinLock(&KdDebuggerLock);
      __writecr8(CurrentIrql);
      return STATUS_INVALID_PARAMETER;
    }
    KdInitSystem(0, 0i64);
  }
  return STATUS_SUCCESS;
}

Итак,здесь происходят главные моменты.
Во-первых,при вызове TakeLock с true функция присваивает KdpDebugRoutineSelect,KdDebuggerEnabled and
KERNEL_KUSER_SHARED_DATA ->KdDebuggerEnabled = TRUE;
Вызов KdpRestoreAllBreakpoints произойдёт ,если KdDisableCount != 0 && (KdDisableCountSaved == 1 && KdPreviouslyEnabled)


Мы немного отвлечёмся от последовательности и разберём KdpRestoreAllBreakpoints.
Я решил отвлечься т.к лучше понимать,как устроены bp в ядре.
Сама функция KdpRestoreAllBreakpoints вызывает KdpLowRestoreBreakpoint.
Я не буду показывать саму функцию т.к нас интересует только глобальные значения и что происходит.
Сама функция перезаписывает 2 bp на 0.
Если мы посмотрим на KdpLowRestoreBreakpoint,то увидим :
v1 = (char *)&KdpBreakpointTable + 0x28 * a1;
Размер структуры KdpBreakpointTable = 0x28.
Саму структуру я украду у
Пожалуйста, авторизуйтесь для просмотра ссылки.
.
Структура ≈ выглядит так:

C++:
typedef struct _BREAKPOINT_ENTRY
{
    PVOID        BreakPoint;
    ULONG64        DirectoryTableBase;
    DWORD32        InterruptByte_CC;
    DWORD32        UnknownField_0;
    DWORD32        OriginalByte;
    DWORD32        UnknownField_1;
    ULONG64        UnknownField_2;
}BREAKPOINT_ENTRY, *PBREAKPOINT_ENTRY;

Вернёмся к функции KdEnableDebuggerWithLock.
Нас интересует вызов KdInitSystem.
Сама функция хоть и большая,но я попытался её разобрать с моей больной башкой, поэтому могут быть небольшие ошибки.
Сама функция выглядит ≈ так:
C++:
BOOLEAN __fastcall KdInitSystem(ULONG Phase, PLOADER_PARAMETER_BLOCK KeLoaderBlock ) 
{
  char v3; // r12
  char v4; // r15
  char v6; // r14
  __int64 v7; // rcx
  struct _KPRCB *CurrentPrcb; // rcx
  __int64 v9; // rcx
  char *LoadOptions; // rbp
  char v11; // bl
  char *v12; // rax
  __int64 Extension; // rdi
  unsigned int v14; // eax
  const char *LoadOptions2; // rsi
  char *v16; // rdx
  unsigned __int64 v17; // rax
  __int64 v18; // rcx
  const char *j; // rcx
  const char *v20; // rsi
  __int64 v21; // rdx
  unsigned int v22; // ebp
  __int64 *k; // rbx
  __int64 v24; // rdx
  char *v25; // r9
  __int64 v26; // r8
  char v27; // al
  __int64 v28; // rcx
  PVOID PoolWithTag; // rax
  PVOID v31; // rbx
  int v32[8]; // [rsp+0h] [rbp-178h] BYREF
  STRING DestinationString; // [rsp+20h] [rbp-158h] BYREF
  char SourceString[256]; // [rsp+30h] [rbp-148h] BYREF

  v3 = NULL;
  v4 = NULL;
  if ( Phase == -1 )
  {
  if(KeLoaderBlock->Extension-> BootDebuggerActive != 0)
    __debugbreak();

  }
  else
  {
    if ( Phase )
    {
      KeQueryPerformanceCounter(&KdPerformanceCounterRate);
      if ( !KdPitchDebugger )
      {
        for ( unsigned int i = 0; i < (unsigned int)KeNumberProcessors_0; ++i )
        {
          PoolWithTag = ExAllocatePoolWithTag(NonPagedPoolNx, 0x1000ui64, 'oIdK');
          v31 = PoolWithTag;
          if ( PoolWithTag )
          {
            memset(PoolWithTag, 0, 0x1000ui64);
            _InterlockedOr(v32, FALSE);
            KdLogBuffer[i] = v31;
          }
        }
      }
      KdpLoaderDebuggerBlock = FALSE;
      return TRUE;
    }
    if ( (_BYTE)KdDebuggerEnabled )
      goto LABEL_31;
    KdpDebugRoutineSelect = FALSE;
    BYTE1(KdDebuggerEnabled) = FALSE;
    if ( !KdPitchDebugger || (v6 = TRUE, !KdLocalDebugEnabled) )
      v6 = FALSE;
    if ( KdDebugDevice && KdDebugDevice->TransportType == 3 )
      KdTransportMaxPacketSize = 0x480;
    if ( !KdpDebuggerDataListHead )
    {
      *((_QWORD *)&KdpContext + 1) = KdDebugDevice;
      qword_140C00C90 = MmGetPagedPoolCommitPointer();
      KdpPowerSpinLock = FALSE;
      qword_140C40738 =  &KdpPowerListHead;
      KdpPowerListHead =  &KdpPowerListHead;
      qword_140C40748 =  &KdpDebuggerDataListHead;
      KdpDebuggerDataListHead =  &KdpDebuggerDataListHead;
      KdRegisterDebuggerDataBlock(v7, &KdDebuggerDataBlock);
      WORD1(KdVersionBlock) = NtBuildNumber;
      WORD3(KdVersionBlock) |= TRUE;
      LOWORD(KdVersionBlock) = (unsigned int)NtBuildNumber >> 28;
      *((_QWORD *)&xmmword_140C0F388 + 1) = &PsLoadedModuleList;
      *(_WORD *)((char *)&KdVersionBlock + 11) = 0x3303;
      qword_140C0F398 = (__int64)&KdpDebuggerDataListHead;
    }
    CurrentPrcb = KeGetCurrentPrcb();
    if ( !CurrentPrcb->Context )
    {
      CurrentPrcb->ContextFlagsInit = CONTEXT_FULL;
      CurrentPrcb->Context = &CurrentPrcb->ProcessorState.ContextFrame;
    }
    if ( KeLoaderBlock )
    {
      v9 = *(_QWORD *)(*(_QWORD *)(KeLoaderBlock + 16) + 48i64);
      off_140C00DF8 = &KdpLoaderDebuggerBlock;
      KdpLoaderDebuggerBlock = KeLoaderBlock + 16;
      LoadOptions = KeLoaderBlock->LoadOptions
      *(_QWORD *)&xmmword_140C0F388 = v9;
      if ( LoadOptions )
      {
        strupr(LoadOptions);
        LODWORD(KdPrintBufferAllocateSize) = FALSE;
        v11 = FALSE;
        v12 = strstr(LoadOptions, "DBGPRINT_LOG_SIZE=");
        if ( v12 )
        {
          v14 = (atol(v12 + 18) + 4095) & 0xFFFFF000;
          LODWORD(KdPrintBufferAllocateSize) = v14;
          if ( v14 > 0x1000000 )
          {
            LODWORD(KdPrintBufferAllocateSize) = 0x1000000;
            v14 = 0x1000000;
          }
          if ( v14 <= 0x1000 )
            LODWORD(KdPrintBufferAllocateSize) = FALSE;
        }
        if ( strstr(LoadOptions, "NODEBUG") )
        {
          KdPitchDebugger = TRUE;
          KdPageDebuggerSection = TRUE;
          KdpBootedNodebug = TRUE;
        }
        else if ( strstr(LoadOptions, "DEBUGPORT=LOCAL") )
        {
          KdPitchDebugger = TRUE;
          v6 = TRUE;
          KdPageDebuggerSection = TRUE;
          LOBYTE(KdDebuggerNotPresent) = TRUE;
          KdLocalDebugEnabled = TRUE;
          KdpBootedNodebug = FALSE;
        }
        else
        {
          LoadOptions2 = LoadOptions;
          do
          {
            v16 = strstr(LoadOptions2, " DEBUG=");
            if ( !v16 )
            {
              v16 = strstr(LoadOptions2, " DEBUG");
              if ( !v16 )
                break;
            }
            LoadOptions2 = v16 + 6;
            v17 = v16[6];
            if ( (unsigned __int8)v17 <= 0x3Du )
            {
              v18 = 0x2000000100000001i64;
              if ( _bittest64(&v18, v17) )
              {
                KdpBootedNodebug = FALSE;
                v11 = 1;
                if ( v16[6] == 61 )
                {
                  for ( j = v16 + 7; ; j = v20 + 1 )
                  {
                    LOBYTE(v17) = *j;
                    v20 = j;
                    while ( (_BYTE)v17 )
                    {
                      if ( (unsigned __int8)v17 <= 0x2Cu )
                      {
                        v21 = 0x100100000200i64;
                        if ( _bittest64(&v21, v17) )
                          break;
                      }
                      LOBYTE(v17) = *++v20;
                    }
                    v17 = (unsigned int)((_DWORD)v20 - (_DWORD)j);
                    if ( (_DWORD)v20 == (_DWORD)j )
                      break;
                    switch ( (_DWORD)v17 )
                    {
                      case 0xA:
                        LODWORD(v17) = strncmp(j, "AUTOENABLE", 0xAui64);
                        if ( !(_DWORD)v17 )
                        {
                          v3 = 1;
                          KdAutoEnableOnEvent = 1;
                          v4 = FALSE;
                        }
                        break;
                      case 7:
                        LODWORD(v17) = strncmp(j, "DISABLE", 7ui64);
                        if ( !(_DWORD)v17 )
                        {
                          v3 = 1;
                          KdAutoEnableOnEvent = FALSE;
                          v4 = 1;
                        }
                        break;
                      case 6:
                        LODWORD(v17) = strncmp(j, "NOUMEX", 6ui64);
                        if ( !(_DWORD)v17 )
                          KdIgnoreUmExceptions = TRUE;
                        break;
                    }
                    if ( *v20 != 44 )
                      break;
                  }
                }
                break;
              }
            }
          }
          while ( v16 != (char *)-6i64 );
        }
        if ( strstr(LoadOptions, "NOEVENT") )
        {
          KdEventLoggingEnabled = FALSE;
        }
        else if ( strstr(LoadOptions, "EVENT") )
        {
          KdEventLoggingEnabled = TRUE;
          v11 = TRUE;
          KdPageDebuggerSection = FALSE;
        }
      }
      else
      {
        KdPitchDebugger = TRUE;
        v11 = FALSE;
        KdPageDebuggerSection = TRUE;
      }
    }
    else
    {
      v11 = TRUE;
      *(_QWORD *)&xmmword_140C0F388 = PsNtosImageBase;
    }
    qword_140C00B38 = xmmword_140C0F388;
    if ( !v6 )
    {
      if ( KeLoaderBlock &&  KeLoaderBlock->OsLoaderSecurityVersion != TRUE )
        v11 = FALSE;
      if ( !v11 )
      {
        LOBYTE(KdDebuggerNotPresent) = TRUE;
        goto LABEL_31;
      }
      if ( (int)KdInitialize(0i64, KeLoaderBlock, &KdpContext) < 0 )
      {
        KdPitchDebugger = FALSE;
        v11 = FALSE;
        LOBYTE(KdDebuggerNotPresent) = TRUE;
        KdLocalDebugEnabled = TRUE;
      }
      else
      {
        KdpDebugRoutineSelect = TRUE;
      }
    }
    if ( !KdpDebuggerStructuresInitialized )
    {
      BYTE4(KdpContext) = 0;
      LODWORD(KdpContext) = 0x14;
      KeInitializeDpc(&KdpTimeSlipDpc, (PKDEFERRED_ROUTINE)KdpTimeSlipDpcRoutine, 0i64);
      KeInitializeTimerEx(&KdpTimeSlipTimer, NotificationTimer);
      KdpTimeSlipWorkItem.Parameter = FALSE;
      KdpTimeSlipWorkItem.WorkerRoutine = (void (__fastcall *)(void *))KdpTimeSlipWork;
      KdpTimeSlipWorkItem.List.Flink = (_LIST_ENTRY *)FALSE;
      KdpDebuggerStructuresInitialized = TRUE;
    }
    KdTimerStart = FALSE;
    if ( KdEventLoggingEnabled && KdpBootedNodebug )
    {
      KdPitchDebugger = TRUE;
      KdEventLoggingPresent = v11;
      LOBYTE(KdDebuggerNotPresent) = TRUE;
      KdLocalDebugEnabled = FALSE;
    }
    else
    {
      LOBYTE(KdDebuggerEnabled) = TRUE;
      KERNEL_KUSER_SHARED_DATA->KdDebuggerEnabled = TRUE;
      if ( KdLocalDebugEnabled )
        goto LABEL_31;
    }
    if ( KdEventLoggingEnabled && !(_BYTE)KdDebuggerEnabled )
    {
LABEL_31:
      if ( KeLoaderBlock )
      {
       Extension = KeLoaderBlock->Extension
        if ( Extension )
          memset((void *)Extension->KdEntropy, 0, 0x20);
      }
      return TRUE;
    }
    KdPitchDebugger = FALSE;
    if ( v3 )
    {
      KdDisableDebuggerWithLock();
      KdBlockEnable = v4;
      goto LABEL_31;
    }
    if ( KeLoaderBlock )
    {
      v22 = NULL;
      for ( k = KeLoaderBlock->LoadOrderListHead; k != KeLoaderBlock -> LoadOrderListHead ; ++v22 )
      {
        if ( v22 >= 3 )
          break;
        DestinationString = 0i64;
        LODWORD(v24) = FALSE;
        v25 = (char *)k[10];
        v26 = *((unsigned __int16 *)k + 36) >> 1;
        if ( (unsigned int)v26 >= 0x100 )
          v26 = 255i64;
        do
        {
          v27 = *v25;
          v25 += 2;
          v28 = (unsigned int)v24;
          v24 = (unsigned int)(v24 + 1);
          SourceString[v28] = v27;
        }
        while ( (unsigned int)v24 < (unsigned int)v26 );
        if ( (unsigned int)v24 >= 0x100ui64 )
          _report_rangecheckfailure(v28, v24, v26, v25, *(_QWORD *)&DestinationString.Length, DestinationString.Buffer);
        SourceString[v24] = FALSE;
        RtlInitAnsiString(&DestinationString, SourceString);
        DbgLoadImageSymbols(&DestinationString, k[6], -1);
        k = (__int64 *)*k;
      }
    }
    else
    {
      DbgLoadImageSymbols(0i64, qword_140C00B38, -1);
    }
    if ( KeLoaderBlock )
    {
      BYTE1(KdDebuggerEnabled) = KdPollBreakIn();
      goto LABEL_31;
    }
  }
  return TRUE;
}

Тут много чего происходит вкусного.
Во-первых, инициализация завершается неудачно, если Phase == -1 и KeLoaderBlock->Extension->BootDebuggerActive не равен 0.
Во-вторых,строки "DBGPRINT_LOG_SIZE=", "DEBUGPORT=LOCAL" являются опциями при загрузке в windows( KeLoaderBlock->LoadOptions)
и они сохраняются в SystemStartOptions (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control),
благодаря этому можно сделать мемный детект дебаг мода и тест мода через реестр :D
Если KdDebuggerEnabled != 0 ,то функция перезаписывает KeLoaderBlock->Extension->KdEntropy на ноль и возвращает TRUE.
Я не буду всё расписывать, ибо я не извращенец, чтобы писать про 20+ глобальных переменных и анализировать их тип :D
Сразу говорю,что в KdInitialize( kdcom.dll) нет ничего интересного,поэтому не вижу смысла анализировать эту функцию.
Сама функция(KdInitSystem) будет вызвана во время инициализации системы,а точнее будет вызвана из KdInitSystem.
Мне уже лень её разбирать, поэтому можете самостоятельно проанализировать её.

Теперь проанализируем KdDisableDebugger т.е как выключается km дебаггер:
C++:
NTSTATUS KdDisableDebugger()
{
  return KdDisableDebuggerWithLock();
}

Сразу переходим к KdDisableDebuggerWithLock т.к ловить нечего
C++:
NTSTATUS __stdcall KdDisableDebuggerWithLock()
{
  unsigned __int8 CurrentIrql; // bl
  NTSTATUS nt_status; // edi

  if ( KdPitchDebugger )
    return STATUS_DEBUGGER_INACTIVE;
  if ( KdBlockEnable )
    return STATUS_ACCESS_DENIED;
  CurrentIrql = KeGetCurrentIrql();
  __writecr8(DISPATCH_LEVEL);
  KxAcquireSpinLock(&KdDebuggerLock);
  if ( KdDisableCount )
    goto LABEL_11;
  KdPreviouslyEnabled = (char)KdDebuggerEnabled;
  if ( !(_BYTE)KdDebuggerEnabled || (nt_status = KdpAllowDisable(), nt_status >= 0) )
  {
    if ( (_BYTE)KdDebuggerEnabled )
    {
      KdpSuspendAllBreakpoints();
      KERNEL_KUSER_SHARED_DATA ->KdDebuggerEnabled = FALSE;                                 //MEMORY[0xFFFFF780000002D4] = FALSE;
      KdpDebugRoutineSelect = FALSE;
      LOBYTE(KdDebuggerNotPresent) = TRUE;
      LOBYTE(KdDebuggerEnabled) = FALSE;
      KdPowerTransitionEx(0x40000004i64, 0i64);
    }
LABEL_11:
    ++KdDisableCount;
    KxReleaseSpinLock(&KdDebuggerLock);
    nt_status = STATUS_SUCCESS;
    goto LABEL_12;
  }
  KxReleaseSpinLock(&KdDebuggerLock);
LABEL_12:
  __writecr8(CurrentIrql);
  return nt_status;
}
KERNEL_KUSER_SHARED_DATA ->KdDebuggerEnabled,KdpDebugRoutineSelect,KdDebuggerEnabled присваивается FALSE и KdDebuggerNotPresent присваивается TRUE.

Итак, небольшая статья подходит к концу т.к анализировать все переменные - скука смертная, то я оставлю небольшой подарок.
Вот переменные, которые можно использовать для обнаружения KM дебаггера/
Здесь написано с какой версии windows они присутствуют(я анализировал с windows 7 -11,но мне лень было устанавливать windows 8,поэтому мог ошибиться с KdLocalDebugEnabled):

Just bool:
KdLocalDebugEnabled windows 8.1(maybe windows 8)
(BOOLEAN)KdDebuggerNotPresent windows 7
(BOOLEAN)KdDebuggerEnabled windows 7
KdpBootedNodebug windows 7
KdpDebuggerStructuresInitialized windows 7
KdPageDebuggerSection windows 10
(BOOLEAN)KdIgnoreUmExceptions ? windows 7
(BOOLEAN)KdBlockEnable windows 7
(BOOLEAN)KUSER_SHARED_DATA->KdDebuggerEnabled windows 2000

Time(struct):
KdpTimeSlipDpc windows 7
KdpTimeSlipTimer windows 7
KdpTimeSlipWorkItem windows 7

Over stuff:
(xmmword)KdpContext windows 7
Сюрприз-сюрприз!
В winows 8.1 данная переменная не существует и она вычисляется следующем образом:KdpContext = (адрес)KdDebuggerNotPresent + 7
Screenshot_177.png
(struct)KdpBreakpointTable windows 7

Все кредиты идут colby57 т.к они мне не нужны :D.
Прости,что решил написать статью не потом,а сегодня.

Пожалуйста, зарегистрируйтесь или авторизуйтесь, чтобы увидеть содержимое.

 
Последнее редактирование:
Забаненный
Статус
Оффлайн
Регистрация
5 Сен 2020
Сообщения
986
Реакции[?]
275
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
я нихуя не понял но вообще очень интересно хорошая работа :hearteyecat:
 
Сверху Снизу