Tiny delphi unit to get and update debug flag from PEB (Process Environment Block).
This unit was created while working on a friend project called Unprotect (@fr0gger_), https://github.com/fr0gger/unprotect
Indeed, some Malware often check the value of Debug flag to know whether or not they are getting debugged and apply anti debug techniques if this is the case.
Example of implementation can be found there
(*******************************************************************************
Author:
-> Jean-Pierre LESUEUR (@DarkCoderSc)
https://github.com/DarkCoderSc
https://gist.github.com/DarkCoderSc
https://www.phrozen.io/
License:
-> MIT
*******************************************************************************)
unit UntPEBDebug;
interface
uses Windows;
const PROCESS_QUERY_LIMITED_INFORMATION = $1000;
PROCESS_BASIC_INFORMATION = 0;
// https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
var _NtQueryInformationProcess : function(
ProcessHandle : THandle;
ProcessInformationClass : DWORD;
ProcessInformation : Pointer;
ProcessInformationLength :
ULONG; ReturnLength : PULONG) : LongInt; stdcall;
hNTDLL : THandle;
{$IFDEF WIN64}
type
PProcessBasicInformation = ^TProcessBasicInformation;
TProcessBasicInformation = record
ExitStatus : Int64;
PebBaseAddress : Pointer;
AffinityMask : Int64;
BasePriority : Int64;
UniqueProcessId : Int64;
InheritedUniquePID : Int64;
end;
{$ELSE}
type
PProcessBasicInformation = ^TProcessBasicInformation;
TProcessBasicInformation = record
ExitStatus : DWORD;
PebBaseAddress : Pointer;
AffinityMask : DWORD;
BasePriority : DWORD;
UniqueProcessId : DWORD;
InheritedUniquePID : DWORD;
end;
{$ENDIF}
function GetProcessDebugStatus(AProcessID : Cardinal; var ADebugStatus : boolean) : Boolean;
function SetProcessDebugStatus(AProcessID : Cardinal; ADebugStatus : Boolean) : Boolean;
implementation
{-------------------------------------------------------------------------------
Open a process and retrieve the point of debug flag from PEB.
If function succeed, don't forget to call close process handle.
-------------------------------------------------------------------------------}
function GetDebugFlagPointer(AProcessID : Cardinal; var AProcessHandle : THandle) : Pointer;
var PBI : TProcessBasicInformation;
ARetLen : Cardinal;
begin
result := nil;
///
AProcessHandle := 0;
if NOT Assigned(_NtQueryInformationProcess) then
Exit();
///
AProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_WRITE or PROCESS_VM_READ, false, AProcessID);
if (AProcessHandle = 0) then
Exit;
if _NtQueryInformationProcess(AProcessHandle, PROCESS_BASIC_INFORMATION, @PBI, sizeOf(TProcessBasicInformation), @ARetLen) = ERROR_SUCCESS then
result := Pointer(NativeUInt(PBI.PebBaseAddress) + (SizeOf(Byte) * 2))
else
CloseHandle(AProcessHandle);
end;
{-------------------------------------------------------------------------------
Retrieve the target process debug status from PEB.
ADebugStatus = True : Target process debug flag is set.
ADebugStatus = False : Target process debug flag is not set.
-------------------------------------------------------------------------------}
function GetProcessDebugStatus(AProcessID : Cardinal; var ADebugStatus : boolean) : Boolean;
var hProcess : THandle;
pDebugFlagOffset : Pointer;
pDebugFlag : pByte;
ABytesRead : SIZE_T;
begin
result := false;
///
pDebugFlagOffset := GetDebugFlagPointer(AProcessID, hProcess);
if not Assigned(pDebugFlagOffset) then
Exit();
///
try
getMem(pDebugFlag, sizeOf(Byte));
try
if NOT ReadProcessMemory(hProcess, pDebugFlagOffset, pDebugFlag, sizeOf(Byte), ABytesRead) then
Exit;
///
ADebugStatus := (pDebugFlag^ = 1);
finally
FreeMem(pDebugFlag);
end;
///
result := (ABytesRead = SizeOf(Byte));
finally
CloseHandle(hProcess);
end;
end;
{-------------------------------------------------------------------------------
Update target process debug flag.
ADebugStatus = True : Set target process debug flag.
ADebugStatus = False : Unset target process debug flag.
-------------------------------------------------------------------------------}
function SetProcessDebugStatus(AProcessID : Cardinal; ADebugStatus : Boolean) : Boolean;
var hProcess : THandle;
pDebugFlagOffset : Pointer;
ADebugFlag : Byte;
ABytesWritten : SIZE_T;
begin
result := false;
///
pDebugFlagOffset := GetDebugFlagPointer(AProcessID, hProcess);
if not Assigned(pDebugFlagOffset) then
Exit();
///
try
if ADebugStatus then
ADebugFlag := 1
else
ADebugFlag := 0;
if NOT WriteProcessMemory(hProcess, pDebugFlagOffset, @ADebugFlag, SizeOf(Byte), ABytesWritten) then
Exit;
///
result := (ABytesWritten = SizeOf(Byte));
finally
CloseHandle(hProcess);
end;
end;
initialization
{
Load NtQueryInformationProcess from NTDLL.dll
}
_NtQueryInformationProcess := nil;
hNTDLL := LoadLibrary('ntdll.dll');
if (hNTDLL <> 0) then
@_NtQueryInformationProcess := GetProcAddress(hNTDLL, 'NtQueryInformationProcess');
finalization
_NtQueryInformationProcess := nil;
if (hNTDLL <> 0) then
FreeLibrary(hNTDLL);
end.
Read more...
Read more about this technique HERE
Delphi
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.
Read more...
Read more about this technique HERE
Delphi
program ADB_NtSetInformationThread;
{$APPTYPE CONSOLE}
uses
WinAPI.Windows, System.SysUtils;
type
// ntddk.h
TThreadInfoClass = (
ThreadBasicInformation,
ThreadTimes,
ThreadPriority,
ThreadBasePriority,
ThreadAffinityMask,
ThreadImpersonationToken,
ThreadDescriptorTableEntry,
ThreadEnableAlignmentFaultFixup,
ThreadEventPair_Reusable,
ThreadQuerySetWin32StartAddress,
ThreadZeroTlsCell,
ThreadPerformanceCount,
ThreadAmILastThread,
ThreadIdealProcessor,
ThreadPriorityBoost,
ThreadSetTlsArrayAddress,
ThreadIsIoPending,
ThreadHideFromDebugger, {<--}
ThreadBreakOnTermination,
ThreadSwitchLegacyState,
ThreadIsTerminated,
ThreadLastSystemCall,
ThreadIoPriority,
ThreadCycleTime,
ThreadPagePriority,
ThreadActualBasePriority,
ThreadTebInformation,
ThreadCSwitchMon,
ThreadCSwitchPmu,
ThreadWow64Context,
ThreadGroupInformation,
ThreadUmsInformation,
ThreadCounterProfiling,
ThreadIdealProcessorEx,
ThreadCpuAccountingInformation,
ThreadSuspendCount,
ThreadActualGroupAffinity,
ThreadDynamicCodePolicyInfo,
MaxThreadInfoClass
);
var hNtDll : THandle;
AThread : THandle;
AThreadId : Cardinal;
NtSetInformationThread : function(
ThreadHandle : THandle;
ThreadInformationClass : TThreadInfoClass;
ThreadInformation : PVOID;
ThreadInformationLength : ULONG
) : NTSTATUS; stdcall;
const
STATUS_SUCCESS = $00000000;
{-------------------------------------------------------------------------------
Hide Thread From Debugger
-------------------------------------------------------------------------------}
function HideThread(AThreadHandle : THandle) : Boolean;
var AThreadInformation : ULONG;
AStatus : NTSTATUS;
begin
result := False;
///
if not assigned(NtSetInformationThread) then
Exit();
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntsetinformationthread
AStatus := NtSetInformationThread(AThreadHandle, ThreadHideFromDebugger, nil, 0);
case AStatus of
{
STATUS_INFO_LENGTH_MISMATCH
}
NTSTATUS($C0000004) : begin
WriteLn('Error: Status Info Length Mismatch.');
end;
{
STATUS_INVALID_PARAMETER
}
NTSTATUS($C000000D) : begin
WriteLn('Error: Invalid Parameter.');
end;
{
STATUS_SUCCESS
}
NTSTATUS($00000000) : begin
WriteLn(Format('Thread: %d is now successfully hidden from debuggers.', [AThreadHandle]));
result := True;
end;
{
Other Errors
}
else begin
WriteLn('Error: Unknown.');
end;
end;
end;
{-------------------------------------------------------------------------------
___thread:example
-------------------------------------------------------------------------------}
procedure ThreadExample(pParam : PVOID); stdcall;
begin
WriteLn('Example Thread Begin.');
{
If we are attached to a debugger, we trigger a new breakpoint.
If thread is set with hidden from debugger, process should crash.
}
if IsDebuggerPresent() then begin
asm
int 3
end;
end;
WriteLn('Example Thread Ends.');
///
ExitThread(0);
end;
{-------------------------------------------------------------------------------
___entry
-------------------------------------------------------------------------------}
begin
try
hNtDll := LoadLibrary('NTDLL.DLL');
if (hNtDll = 0) then
Exit();
try
@NtSetInformationThread := GetProcAddress(hNtDll, 'NtSetInformationThread');
if NOT Assigned(NtSetInformationThread) then
Exit();
{
Create an example thread
}
SetLastError(0);
AThread := CreateThread(nil, 0, @ThreadExample, nil, CREATE_SUSPENDED, AThreadId);
if (AThread <> 0) then begin
WriteLn(Format('Example thread created. Thread Handle: %d , Thread Id: %d', [AThread, AThreadid]));
HideThread(AThread);
///
ResumeThread(AThread);
WaitForSingleObject(AThread, INFINITE);
end else begin
WriteLn(Format('Could not create example thread with error: .', [GetLastError()]));
end;
finally
FreeLibrary(hNtDll);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Read more...
Read more about this technique HERE
Delphi
program NtSetDebugFilterState;
{$APPTYPE CONSOLE}
uses
WinAPI.Windows, System.SysUtils;
var
NtSetDebugFilterState : function(AComponentId : ULONG; ALevel : ULONG; AState : Boolean) : NTSTATUS; stdcall;
hNTDLL : THandle;
AStatus : NTSTATUS;
begin
try
hNTDLL := LoadLibrary('ntdll.dll');
if (hNTDLL = 0) then
Exit();
try
@NtSetDebugFilterState := GetProcAddress(hNTDLL, 'NtSetDebugFilterState');
if NOT Assigned(NtSetDebugFilterState) then
Exit();
AStatus := NtSetDebugFilterState(0, 0, True);
writeln(AStatus);
if (AStatus <> 0) then
WriteLn('Not Debugged.')
else
WriteLn('Debugged.');
finally
FreeLibrary(hNTDLL);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Read more...
Read more about this technique HERE
Delphi
program IsDebuggerPresent;
{$APPTYPE CONSOLE}
uses
WinAPI.Windows, System.SysUtils;
begin
try
if IsDebuggerPresent() then
WriteLn('Process is currently getting debugged.')
else
WriteLn('Process is not likely getting debugged.');
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Read more...
Read more about this technique HERE
Delphi
program OutputDebugString;
{$APPTYPE CONSOLE}
uses
WinAPI.Windows,
System.SysUtils;
var AErrorValue : Byte;
begin
try
randomize;
AErrorValue := Random(High(Byte));
SetLastError(AErrorValue);
OutputDebugStringW('TEST');
if (GetLastError() = AErrorValue) then
WriteLn('Debugger detected using OutputDebugString() technique.')
else
WriteLn('No debugger detected using OutputDebugString() technique.');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Read more...
Read more about this technique HERE
Delphi
program SuspendThread;
{$APPTYPE CONSOLE}
uses
WinAPI.Windows, System.SysUtils, Generics.Collections, tlHelp32, Classes;
type
TProcessItem = class
private
FName : String;
FProcessId : Cardinal;
FThreads : TList;
{@M}
procedure EnumThreads();
public
{@C}
constructor Create(AName : String; AProcessId : Cardinal; AEnumThreads : Boolean = True);
destructor Destroy(); override;
{@G}
property Name : String read FName;
property ProcessId : Cardinal read FProcessId;
property Threads : TList read FThreads;
end;
TEnumProcess = class
private
FItems : TObjectList;
public
{@C}
constructor Create();
destructor Destroy(); override;
{@M}
function Refresh() : Cardinal;
procedure Clear();
function Get(AProcessId : Cardinal) : TProcessItem; overload;
function Get(AName : String) : TProcessItem; overload;
{@G}
property Items : TObjectList read FItems;
end;
{
Import API's From Kernel32
}
const THREAD_SUSPEND_RESUME = $00000002;
function OpenThread(
dwDesiredAccess: DWORD;
bInheritHandle: BOOL;
dwThreadId: DWORD
) : THandle; stdcall; external kernel32 name 'OpenThread';
{
Global Vars
}
var LFindWindowSignatures : TDictionary;
LProcessNameSignatures : TStringList;
LProcesses : TEnumProcess;
{
Read more...
Read more about this technique HERE
Delphi
program FindWindowAPI;
{$APPTYPE CONSOLE}
uses
System.SysUtils, WinAPI.Windows, Generics.Collections, psAPI;
{
Read more...
Read more about this technique HERE
Delphi
unit UntPEBDebug;
interface
uses Windows;
const PROCESS_QUERY_LIMITED_INFORMATION = $1000;
PROCESS_BASIC_INFORMATION = 0;
// https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
var _NtQueryInformationProcess : function(
ProcessHandle : THandle;
ProcessInformationClass : DWORD;
ProcessInformation : Pointer;
ProcessInformationLength :
ULONG; ReturnLength : PULONG) : LongInt; stdcall;
hNTDLL : THandle;
{$IFDEF WIN64}
type
PProcessBasicInformation = ^TProcessBasicInformation;
TProcessBasicInformation = record
ExitStatus : Int64;
PebBaseAddress : Pointer;
AffinityMask : Int64;
BasePriority : Int64;
UniqueProcessId : Int64;
InheritedUniquePID : Int64;
end;
{$ELSE}
type
PProcessBasicInformation = ^TProcessBasicInformation;
TProcessBasicInformation = record
ExitStatus : DWORD;
PebBaseAddress : Pointer;
AffinityMask : DWORD;
BasePriority : DWORD;
UniqueProcessId : DWORD;
InheritedUniquePID : DWORD;
end;
{$ENDIF}
function GetProcessDebugStatus(AProcessID : Cardinal; var ADebugStatus : boolean) : Boolean;
function SetProcessDebugStatus(AProcessID : Cardinal; ADebugStatus : Boolean) : Boolean;
implementation
{-------------------------------------------------------------------------------
Open a process and retrieve the point of debug flag from PEB.
If function succeed, don't forget to call close process handle.
-------------------------------------------------------------------------------}
function GetDebugFlagPointer(AProcessID : Cardinal; var AProcessHandle : THandle) : Pointer;
var PBI : TProcessBasicInformation;
ARetLen : Cardinal;
begin
result := nil;
///
AProcessHandle := 0;
if NOT Assigned(_NtQueryInformationProcess) then
Exit();
///
AProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_WRITE or PROCESS_VM_READ, false, AProcessID);
if (AProcessHandle = 0) then
Exit;
if _NtQueryInformationProcess(AProcessHandle, PROCESS_BASIC_INFORMATION, @PBI, sizeOf(TProcessBasicInformation), @ARetLen) = ERROR_SUCCESS then
result := Pointer(NativeUInt(PBI.PebBaseAddress) + (SizeOf(Byte) * 2))
else
CloseHandle(AProcessHandle);
end;
{-------------------------------------------------------------------------------
Retrieve the target process debug status from PEB.
ADebugStatus = True : Target process debug flag is set.
ADebugStatus = False : Target process debug flag is not set.
-------------------------------------------------------------------------------}
function GetProcessDebugStatus(AProcessID : Cardinal; var ADebugStatus : boolean) : Boolean;
var hProcess : THandle;
pDebugFlagOffset : Pointer;
pDebugFlag : pByte;
ABytesRead : SIZE_T;
begin
result := false;
///
pDebugFlagOffset := GetDebugFlagPointer(AProcessID, hProcess);
if not Assigned(pDebugFlagOffset) then
Exit();
///
try
getMem(pDebugFlag, sizeOf(Byte));
try
if NOT ReadProcessMemory(hProcess, pDebugFlagOffset, pDebugFlag, sizeOf(Byte), ABytesRead) then
Exit;
///
ADebugStatus := (pDebugFlag^ = 1);
finally
FreeMem(pDebugFlag);
end;
///
result := (ABytesRead = SizeOf(Byte));
finally
CloseHandle(hProcess);
end;
end;
{-------------------------------------------------------------------------------
Update target process debug flag.
ADebugStatus = True : Set target process debug flag.
ADebugStatus = False : Unset target process debug flag.
-------------------------------------------------------------------------------}
function SetProcessDebugStatus(AProcessID : Cardinal; ADebugStatus : Boolean) : Boolean;
var hProcess : THandle;
pDebugFlagOffset : Pointer;
ADebugFlag : Byte;
ABytesWritten : SIZE_T;
begin
result := false;
///
pDebugFlagOffset := GetDebugFlagPointer(AProcessID, hProcess);
if not Assigned(pDebugFlagOffset) then
Exit();
///
try
if ADebugStatus then
ADebugFlag := 1
else
ADebugFlag := 0;
if NOT WriteProcessMemory(hProcess, pDebugFlagOffset, @ADebugFlag, SizeOf(Byte), ABytesWritten) then
Exit;
///
result := (ABytesWritten = SizeOf(Byte));
finally
CloseHandle(hProcess);
end;
end;
initialization
{
Load NtQueryInformationProcess from NTDLL.dll
}
_NtQueryInformationProcess := nil;
hNTDLL := LoadLibrary('ntdll.dll');
if (hNTDLL <> 0) then
@_NtQueryInformationProcess := GetProcAddress(hNTDLL, 'NtQueryInformationProcess');
finalization
_NtQueryInformationProcess := nil;
if (hNTDLL <> 0) then
FreeLibrary(hNTDLL);
end.
Read more...