NtQueryObject

Created the Monday 18 March 2019. Updated 3 years, 6 months ago.

This function retrieves object information. By calling this function with the class ObjectTypeInformation will retrieve the specific object type (debug) to detect the debugger.


Technique Identifier

U0118

Technique Tag

NtQueryObject


Code Snippets

External

Description

Original source code available here: https://gist.github.com/soxfmr/16c495d6e4ad99e9e46f5bfd558d152f

/**
Anti-Debugging in NtQueryObject - ObjectAllTypesInformation
Copyright (C) 2018 soxfmr@foxmail.com

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
#include <tchar.h>
#include <Windows.h>

#define DEBUG_OBJECT_NAME L"DebugObject"

typedef struct _UNICODE_STRING {
	USHORT Length;
	USHORT MaximumLength;
	PWSTR Buffer;
} UNICODE_STRING;

typedef enum _OBJECT_INFORMATION_CLASS {
    ObjectBasicInformation = 0,
    ObjectNameInformation = 1,
    ObjectTypeInformation = 2,
    ObjectAllTypesInformation = 3,
    ObjectHandleInformation = 4
} OBJECT_INFORMATION_CLASS;


/**
* The following structure contains partial members of the entire data structure
* As the public documentation shown, the TotalNumberOfObjects should follows by
* TotalNumberOfHandles instead of in reverseing order (Represent in code snippet of book <<逆向工程核心原理>>)
* For the completely structure information: https://doxygen.reactos.org/d6/d07/struct__OBJECT__TYPE__INFORMATION.html
*/
typedef struct _OBJECT_TYPE_INFORMATION {
	UNICODE_STRING TypeName;
	ULONG TotalNumberOfObjects;
	ULONG TotalNumberOfHandles;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

typedef struct _OBJECT_ALL_TYPES_INFORMATION {
	ULONG NumberOfObjectTypes;
	OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];
} OBJECT_ALL_TYPES_INFORMATION, *POBJECT_ALL_TYPES_INFORMATION;

typedef NTSTATUS (NTAPI *PNTQUERYOBJECT)(
	HANDLE Handle,
	OBJECT_INFORMATION_CLASS ObjectInformationClass,
	PVOID ObjectInformation,
	ULONG ObjectInformationLength,
	PULONG ReturnLength
);

VOID WriteLog(TCHAR* lpMessage)
{
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	WriteConsole(hOutput, lpMessage, _tcslen(lpMessage), NULL, NULL);
}

/**
* If you were compile the program in Debug mode, you must disable the RTC runtime check option (/RTCu)
* Otherwise the RTC runtime function will be call at the end of this function and overwrite the return value of `eax`
*/
BOOL NtQueryObjectDebuggerDetect()
{
	ULONG i = 0;
	BOOL bRet = FALSE;

	PNTQUERYOBJECT pNtQueryObject = NULL;
	POBJECT_TYPE_INFORMATION pObjectTypeInfo = NULL;
	POBJECT_ALL_TYPES_INFORMATION pObjectAllTypesInfo = NULL;
	
	UCHAR *pNextTypeLocation = NULL;
	UCHAR *pObjectTypeLocation = NULL;
	
	ULONG dwObjAllTypesLen = 0;
	PVOID pObjectAllTypesBuffer = NULL;

	pNtQueryObject = (PNTQUERYOBJECT) GetProcAddress(GetModuleHandle(L"ntdll.dll"), 
		"NtQueryObject");
	if (pNtQueryObject == NULL)
	{
		WriteLog(L"Cannot obtain the address of NtQueryObject\n");
		return bRet;
	}

	pNtQueryObject(NULL, ObjectAllTypesInformation, &dwObjAllTypesLen, sizeof(dwObjAllTypesLen), &dwObjAllTypesLen);

	pObjectAllTypesBuffer = VirtualAlloc(NULL, dwObjAllTypesLen, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 
	if (0 != pNtQueryObject((HANDLE) -1, ObjectAllTypesInformation, pObjectAllTypesBuffer, dwObjAllTypesLen, NULL))
	{
		WriteLog(L"Cannot obtain the object of all types info\n");
		goto release;
	}

	pObjectAllTypesInfo = (POBJECT_ALL_TYPES_INFORMATION) pObjectAllTypesBuffer;
	pObjectTypeLocation = (UCHAR*) pObjectAllTypesInfo->ObjectTypeInformation;

	for (i = 0; i < pObjectAllTypesInfo->NumberOfObjectTypes; i++)
	{
		pObjectTypeInfo = (POBJECT_TYPE_INFORMATION) pObjectTypeLocation;
		if (wcscmp(DEBUG_OBJECT_NAME, pObjectTypeInfo->TypeName.Buffer) == 0)
		{
			bRet = pObjectTypeInfo->TotalNumberOfObjects > 0 ? TRUE : FALSE;
			break;
		}
		
		// This make me confuse a bit, but seems the Buffer is locate at the end
		// of OBJECT_TYPE_INFORMATION object (I guess :) )
		pObjectTypeLocation = (UCHAR*) pObjectTypeInfo->TypeName.Buffer;
		pObjectTypeLocation += pObjectTypeInfo->TypeName.MaximumLength;
		// Address align, 0xFFFFFFFC on x86, and 0xFFFFFFFFFFFFFFF8 for AMD64
		pNextTypeLocation = (UCHAR*) ((ULONG) pObjectTypeLocation & -sizeof(void*));
		// If the alignment operation reduce the address which is lesser than 
		// the desire size of the buffer, then we should add the alignment size to it
		if (pNextTypeLocation < pObjectTypeLocation)
			pNextTypeLocation += sizeof(ULONG);

		pObjectTypeLocation = pNextTypeLocation;
	}

release:
	
	if (pObjectAllTypesBuffer != NULL)
	{
		VirtualFree(pObjectAllTypesBuffer, 0, MEM_RELEASE);
	}

	return bRet;
}

int main()
{
	if (NtQueryObjectDebuggerDetect())
	{
		WriteLog(L"[-] Debugger Detected!\n");
	} else 
	{
		WriteLog(L"[+] Pass!\n");
	}
	
	system("pause");

	return 0;
}

Jean-Pierre LESUEUR

Description

Support both 32/64 bit.

program NtQueryObject;

{$APPTYPE CONSOLE}

{$ALIGN ON}
{$MINENUMSIZE 4}

uses
  WinAPI.Windows, System.SysUtils;

type
  TUnicodeString = record
    Length: USHORT;
    MaximumLength: USHORT;
    Buffer: PWideChar;
  end;

  TObjectInformationClass = (
                                    ObjectBasicInformation    = 0,
                                    ObjectNameInformation     = 1,
                                    ObjectTypeInformation     = 2,
                                    ObjectAllTypesInformation = 3,
                                    ObjectHandleInformation   = 4
  );

  OBJECT_TYPE_INFORMATION = record
    Name: TUnicodeString;
    ObjectCount: ULONG;
    HandleCount: ULONG;
    Reserved1: array[0..3] of ULONG;
    PeakObjectCount: ULONG;
    PeakHandleCount: ULONG;
    Reserved2: array[0..3] of ULONG;
    InvalidAttributes: ULONG;
    GenericMapping: GENERIC_MAPPING;
    ValidAccess: ULONG;
    Unknown: UCHAR;
    MaintainHandleDatabase: ByteBool;
    Reserved3: array[0..1] of UCHAR;
    PoolType: Byte;
    PagedPoolUsage: ULONG;
    NonPagedPoolUsage: ULONG;
  end;
  POBJECT_TYPE_INFORMATION = ^OBJECT_TYPE_INFORMATION;
  TObjectTypeInformation = OBJECT_TYPE_INFORMATION;
  PObjectTypeInformation = ^TObjectTypeInformation;

  OBJECT_ALL_TYPE_INFORMATION = record
    NumberOfObjectTypes : ULONG;
    ObjectTypeInformation : array[0..0] of TObjectTypeInformation;
  end;
  POBJECT_ALL_TYPE_INFORMATION = ^OBJECT_ALL_TYPE_INFORMATION;
  TObjectAllTypeInformation = OBJECT_ALL_TYPE_INFORMATION;
  PObjectAllTypeInformation = ^TObjectAllTypeInformation;

// https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryobject
var
  _NtQueryObject : function (
                                ObjectHandle : THandle;
                                ObjectInformationClass : TObjectInformationClass;
                                ObjectInformation : PVOID;
                                ObjectInformationLength : ULONG;
                                ReturnLength : PULONG
                              ): ULONG; stdcall;
var hNTDLL              : THandle;
    ARet                : ULONG;
    ARequiredSize       : ULONG;
    pAllTypeInformation : PObjectAllTypeInformation;
    pTypeInformation    : PObjectTypeInformation;
    i                   : Integer;
    pRow                : PObjectTypeInformation;
    pDummy              : Pointer;
    ADebuggerFound      : Boolean;

begin
  try
    ADebuggerFound := False;

    @_NtQueryObject := nil;
    ///

    hNTDLL := LoadLibrary('NTDLL.DLL');
    if (hNTDLL = 0) then
      Exit();
    try
      @_NtQueryObject := GetProcAddress(hNTDLL, 'NtQueryObject');
      if NOT Assigned(_NtQueryObject) then
        Exit();
      ///

      ARet := _NtQueryObject(0, ObjectAllTypesInformation, @ARequiredSize, SizeOf(ULONG), @ARequiredSize);
      if (ARequiredSize <= 0) then
        Exit();
      ///

      GetMem(pAllTypeInformation, ARequiredSize);
      try
        ARet := _NtQueryObject(0, ObjectAllTypesInformation, pAllTypeInformation, ARequiredSize, nil);
        if (ARet <> 0) then
          Exit();
        ///

        pRow := @pAllTypeInformation^.ObjectTypeInformation;

        for I := 0 to pAllTypeInformation^.NumberOfObjectTypes -1 do begin
            if String.Compare(String(pRow^.Name.Buffer), 'DebugObject', True) = 0 then
              ADebuggerFound := (pRow^.ObjectCount > 0);
            ///

            if ADebuggerFound then
              break;

            pRow := Pointer (
              (NativeUInt(pRow^.Name.Buffer) + pRow^.Name.Length) and (NOT (SizeOf(Pointer)-1)) + SizeOf(Pointer)
            );
        end;
      finally
        FreeMem(pAllTypeInformation, ARequiredSize);
      end;
    finally
      FreeLibrary(hNTDLL);
    end;

    if ADebuggerFound then
      WriteLn('A Debugger Was Found!')
    else
      WriteLn('No Debugger Found!');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Detection Rules

rule Detect_NtQueryObject: AntiDebug {
    meta: 
        description = "Detect NtQueryObject as anti-debug"
        author = "Unprotect"
        comment = "Experimental rule"
    strings:
        $1 = "NtQueryObject" fullword ascii
    condition:   
       uint16(0) == 0x5A4D and filesize < 1000KB and $1
}

Additional Resources

External Links

Subscribe to our Newsletter


The information entered into this form is mandatory. It will be subjected to computer processing. It is processed by computer in order to support our users and readers. The recipients of the data will be : contact@unprotect.it.

According to the Data Protection Act of January 6th, 1978, you have at any time, a right of access to and rectification of all of your personal data. If you wish to exercise this right and gain access to your personal data, please write to Thomas Roccia at contact@unprotect.it.

You may also oppose, for legitimate reasons, the processing of your personal data.