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.
Author: Jean-Pierre LESUEUR (DarkCoderSc) / Target Platform: Windows
/**
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;
}
Author: External / Target Platform: Windows