­

反調試——5——檢測調試對象

反調試——5——檢測調試對象

有一些內容採用的是WRK裡面的定義。因為這個算是沒有公開的文檔,公開的不能這樣使用。

查詢父進程實現反調試

正常打開(雙擊運行)的程式的父進程是explorer.exe(資源管理器)(Windows的內置機制),通過查詢父進程的ID是否是explorer.exe來判斷程式是否被調試了。

這裡我直接上程式碼了。

NTSTATUS
NtQueryInformationProcess(
   __in HANDLE ProcessHandle,
   __in PROCESSINFOCLASS ProcessInformationClass,
   __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
   __in ULONG ProcessInformationLength,
   __out_opt PULONG ReturnLength
  )
#include<iostream>
#include<Windows.h>
#include<winternl.h>
#include<tlhelp32.h>
#include<string.h>
using namespace std;


typedef struct _PROCESS_BASIC_INFORMATION_1 {
   NTSTATUS ExitStatus;
   PPEB PebBaseAddress;
   ULONG_PTR AffinityMask;
   KPRIORITY BasePriority;
   ULONG_PTR UniqueProcessId;
   ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION_1;

//定義函數指針
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
   __in HANDLE ProcessHandle,
   __in PROCESSINFOCLASS ProcessInformationClass,
   __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
   __in ULONG ProcessInformationLength,
   __out_opt PULONG ReturnLength
);

_NtQueryInformationProcess NtQueryInformationProcess_My;

int main()
{
//獲得NtQueryInformation函數指針
   HMODULE hNtdll = GetModuleHandleA("ntdll.dll");//因為每個程式都會有ntdll,所以就不用動態loadlibrary載入了,直接獲取dll的句柄
   if (hNtdll == NULL)
  {
       cout << "獲取ntdll句柄錯誤" << endl;
       return 0;
  }
   NtQueryInformationProcess_My = (_NtQueryInformationProcess)GetProcAddress(hNtdll, "NtQueryInformationProcess");

   //獲取當前進程的資訊
   HANDLE hCurrentProcess = GetCurrentProcess();
   PROCESS_BASIC_INFORMATION_1 CurrentInfo = { 0 };
   NtQueryInformationProcess_My(hCurrentProcess, ProcessBasicInformation, &CurrentInfo,sizeof(CurrentInfo),NULL);
   auto CurrentParentProcessId = CurrentInfo.InheritedFromUniqueProcessId;

   //遍歷所有進程資訊,對比進程ID
   PROCESSENTRY32 SnapshotInfo = { 0 };
   SnapshotInfo.dwSize = sizeof(PROCESSENTRY32);
   HANDLE  hSnapshot =  CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
   if (hSnapshot == INVALID_HANDLE_VALUE)
  {
       cout << "創建進程快照失敗" << endl;
       return 0;
  }
   auto Sucess = Process32First(hSnapshot, &SnapshotInfo);
   if (Sucess == TRUE)
  {
       do {
           if (wcscmp(SnapshotInfo.szExeFile, L"explorer.exe")==0)//這裡使用寬字元
          {
               if (SnapshotInfo.th32ProcessID == CurrentParentProcessId)
              {
                   cout << "程式通過父進程檢測,程式的父進程正常" << endl;
              }
               else
              {
                   cout << "檢測到父進程不對,程式正在被調試" << endl;
              }
               break;
          }
      } while (Process32Next(hSnapshot, &SnapshotInfo));
  }
   else
  {
       cout << "獲取第一個進程資訊失敗" << endl;
       return 0;
  }
   system("pause");
   return 0;
}

 

 

 

查詢調試內核調試對象

當調試器調試某進程時,會創建一個內核的調試對象,通過檢測它是否存在來實現反調試。(這裡就採用WRK裡面的內容了,官方文檔的內容不好使。)

通過NtQueryObject可以檢測是否存在調試對象,通過這個東西來看程式是否在被調試。

__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
 HANDLE                   Handle,
 OBJECT_INFORMATION_CLASS ObjectInformationClass,
 PVOID                    ObjectInformation,
 ULONG                    ObjectInformationLength,
 PULONG                   ReturnLength
);//官方文檔版本

NTSTATUS
NtQueryObject (
   __in HANDLE Handle,//需要查詢的句柄
   __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
    //查詢對象枚舉類
   __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
       //輸出結構緩衝區
   __in ULONG ObjectInformationLength,
    //緩衝區大小

   __out_opt PULONG ReturnLength
  //實際需要大小
  );//wrk版本

NtQueryObject中的OBJECT_INFORMATION_CLASS參數:

typedef enum _OBJECT_INFORMATION_CLASS {
   ObjectBasicInformation,
   ObjectNameInformation,
   ObjectTypeInformation,
   ObjectTypesInformation,//所有內核對象類型資訊
   ObjectHandleFlagInformation,
   ObjectSessionInformation,
   MaxObjectInfoClass  // MaxObjectInfoClass should always be the last enum
} OBJECT_INFORMATION_CLASS;

 

//UNICODE_STRING結構體
typedef struct _UNICODE_STRING {
   USHORT Length;
   USHORT MaximumLength;
#ifdef MIDL_PASS
  [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
   PWSTR  Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;

然後初步程式碼是這樣:

#include"Anti-Debug.h"
int main()
{
   //先獲取ntdll的句柄
   HMODULE hNtdll = GetModuleHandleA("ntdll.dll");

   //獲取函數指針
   NtQueryObject = (_NtQueryObject)GetProcAddress(hNtdll, "NtQueryObject");

   //來一個很大的緩衝區
   char* buffer = (char*)malloc(0x4000);
   DWORD realSize = 0;
   auto ret = NtQueryObject(NULL, ObjectTypesInformation, buffer, 0x4000, &realSize);
   if(ret != 0)
  {
       printf("NtQueryObject error\n");
  }

   CloseHandle(hNtdll);
   return 0;
}

//Anti-Debug.h
#pragma once
#include<iostream>
#include<Windows.h>
using namespace std;
typedef enum _OBJECT_INFORMATION_CLASS {
   ObjectBasicInformation,
   ObjectNameInformation,
   ObjectTypeInformation,
   ObjectTypesInformation,
   ObjectHandleFlagInformation,
   ObjectSessionInformation,
   MaxObjectInfoClass  // MaxObjectInfoClass should always be the last enum
} OBJECT_INFORMATION_CLASS;
typedef  NTSTATUS(NTAPI* _NtQueryObject)(
   __in HANDLE Handle,
   __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
   __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
   __in ULONG ObjectInformationLength,
   __out_opt PULONG ReturnLength
  );
_NtQueryObject NtQueryObject;
typedef struct _UNICODE_STRING {
   USHORT Length;
   USHORT MaximumLength;
#ifdef MIDL_PASS
  [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer;
#else // MIDL_PASS
   PWSTR  Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;

然後來通過對這個buffer進行一個處理。

處理緩衝區

這個緩衝區是一個比較麻煩的結構,存放了所有內核對象的內容。

typedef struct _OBJECT_TYPES_INFORMATION
{
   ULONG numberOfTypesInfo;
   OBJECT_TYPE_INFORMATION typeInfo[1];
};
//也就是說TYPES包含了type在裡面,因為TYPES是所有的type的內容

//緩衝區結構體
//   __out_bcount_opt(ObjectInformationLength) PVOID //ObjectInformation,
typedef struct _OBJECT_TYPE_INFORMATION {
   UNICODE_STRING TypeName;
//內核對象類型的名稱,比如互斥體,事件等等  
   ULONG TotalNumberOfObjects;//對象的數量
   ULONG TotalNumberOfHandles;
   ULONG TotalPagedPoolUsage;
   ULONG TotalNonPagedPoolUsage;
   ULONG TotalNamePoolUsage;
   ULONG TotalHandleTableUsage;
   ULONG HighWaterNumberOfObjects;
   ULONG HighWaterNumberOfHandles;
   ULONG HighWaterPagedPoolUsage;
   ULONG HighWaterNonPagedPoolUsage;
   ULONG HighWaterNamePoolUsage;
   ULONG HighWaterHandleTableUsage;
   ULONG InvalidAttributes;
   GENERIC_MAPPING GenericMapping;
   ULONG ValidAccessMask;
   BOOLEAN SecurityRequired;
   BOOLEAN MaintainHandleCount;
   ULONG PoolType;
   ULONG DefaultPagedPoolCharge;
   ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

然後大致上是完成了:

#include"Anti-Debug.h"
int main()
{
   //先獲取ntdll的句柄
   HMODULE hNtdll = GetModuleHandleA("ntdll.dll");

   //獲取函數指針
   NtQueryObject = (_NtQueryObject)GetProcAddress(hNtdll, "NtQueryObject");

   //來一個很大的緩衝區
   char* buffer = (char*)malloc(0x4000);
   DWORD realSize = 0;
   auto ret = NtQueryObject(NULL, ObjectTypesInformation, buffer, 0x4000, &realSize);
   if(ret != 0)
  {
       printf("NtQueryObject error\n");
       return 0;
  }

   //定義TYPES對於的結構體來獲取緩衝區的內容
   POBJECT_TYPES_INFORMATION typesInfo = NULL;
   typesInfo = (POBJECT_TYPES_INFORMATION)buffer;
   //循環遍歷typesInfo
   POBJECT_TYPE_INFORMATION typeInfo = typesInfo->typeInfo;
   //採用單個的type結構體來獲取每個type的內容
   for(int i = 0; i < typesInfo->numberOfTypesInfo; i++)
  {
       if (wcscmp(L"DebugObject", typeInfo->TypeName.Buffer)==0)//是調試類型的內核對象
      {
           if (typeInfo->TotalNumberOfObjects > 0)
          {
               cout << "有調試內核對象,正在進行調試" << endl;
          }
           else
          {
               cout << "沒有檢測到內核調試對象" << endl;
          }
           break;
           typeInfo++;
      }
  }
   return 0;
}

但是有一個問題出現了,由於typeinfo這個單type的內容的結構體裡面有一個UNICODE_STRING欄位這個欄位裡面又有個buffer,這個buffer根據內容的不同而大小不同,所以不能直接++

//修正指針偏移
      DWORD temp = typeInfo->TypeName.MaximumLength;
      temp +=temp % 4;
      typeInfo = (POBJECT_TYPE_INFORMATION)((DWORD)typeInfo + temp);
      typeInfo++;

最終版本:

#include"Anti-Debug.h"
int main()
{
  //先獲取ntdll的句柄
  HMODULE hNtdll = GetModuleHandleA("ntdll.dll");

  //獲取函數指針
  NtQueryObject = (_NtQueryObject)GetProcAddress(hNtdll, "NtQueryObject");

  //來一個很大的緩衝區
  char* buffer = (char*)malloc(0x4000);
  DWORD realSize = 0;
  auto ret = NtQueryObject(NULL, ObjectTypesInformation, buffer, 0x4000, &realSize);
  if(ret != 0)
  {
      printf("NtQueryObject error\n");
      return 0;
  }

  //定義TYPES對於的結構體來獲取緩衝區的內容
  POBJECT_TYPES_INFORMATION typesInfo = NULL;
  typesInfo = (POBJECT_TYPES_INFORMATION)buffer;
  //循環遍歷typesInfo
  POBJECT_TYPE_INFORMATION typeInfo = typesInfo->typeInfo;
  //採用單個的type結構體來獲取每個type的內容
  for(int i = 0; i < typesInfo->numberOfTypesInfo; i++)
  {
      if (wcscmp(L"DebugObject", typeInfo->TypeName.Buffer)==0)//是調試類型的內核對象
      {
          if (typeInfo->TotalNumberOfObjects > 0)
          {
              cout << "有調試內核對象,正在進行調試" << endl;
          }
          else
          {
              cout << "沒有檢測到內核調試對象" << endl;
          }
          break;
      }
      DWORD temp = typeInfo->TypeName.MaximumLength;
      temp +=temp % 4;
      typeInfo = (POBJECT_TYPE_INFORMATION)((DWORD)typeInfo + temp);
      typeInfo++;
  }
  return 0;
}
//頭文件
#pragma once
#include<iostream>
#include<Windows.h>
using namespace std;
typedef enum _OBJECT_INFORMATION_CLASS {
  ObjectBasicInformation,
  ObjectNameInformation,
  ObjectTypeInformation,
  ObjectTypesInformation,
  ObjectHandleFlagInformation,
  ObjectSessionInformation,
  MaxObjectInfoClass // MaxObjectInfoClass should always be the last enum
} OBJECT_INFORMATION_CLASS;
typedef NTSTATUS(NTAPI* _NtQueryObject)(
  __in HANDLE Handle,
  __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
  __out_bcount_opt(ObjectInformationLength) PVOID ObjectInformation,
  __in ULONG ObjectInformationLength,
  __out_opt PULONG ReturnLength
  );
_NtQueryObject NtQueryObject;

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
#ifdef MIDL_PASS
  [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer;
#else // MIDL_PASS
  PWSTR Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;

typedef struct _OBJECT_TYPE_INFORMATION {
  UNICODE_STRING TypeName;
  ULONG TotalNumberOfObjects;
  ULONG TotalNumberOfHandles;
  ULONG TotalPagedPoolUsage;
  ULONG TotalNonPagedPoolUsage;
  ULONG TotalNamePoolUsage;
  ULONG TotalHandleTableUsage;
  ULONG HighWaterNumberOfObjects;
  ULONG HighWaterNumberOfHandles;
  ULONG HighWaterPagedPoolUsage;
  ULONG HighWaterNonPagedPoolUsage;
  ULONG HighWaterNamePoolUsage;
  ULONG HighWaterHandleTableUsage;
  ULONG InvalidAttributes;
  GENERIC_MAPPING GenericMapping;
  ULONG ValidAccessMask;
  BOOLEAN SecurityRequired;
  BOOLEAN MaintainHandleCount;
  ULONG PoolType;
  ULONG DefaultPagedPoolCharge;
  ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;


typedef struct _OBJECT_TYPES_INFORMATION
{
  ULONG numberOfTypesInfo;
  OBJECT_TYPE_INFORMATION typeInfo[1];
}OBJECT_TYPES_INFORMATION,*POBJECT_TYPES_INFORMATION;

 

Tags: