New Unprotect Delphi Code Snippet added for technique Process Hollowing, RunPE
with support of both x86-32 and x86-64 in a single code.
New Unprotect Delphi Code Snippet added for technique ProcEnvInjection - Remote code injection by abusing process environment strings
for both x86-32 and x86-64.
New Unprotect Delphi Code Snippet added for technique DLL Injection via CreateRemoteThread and LoadLibrary
with both support of x86-32 and x86-64.
This code snippet demonstrate how Malware authors take advantage of certain Windows API's to detect the presence of Windows Services that might indicate the presence of Virtualization Technologies and/or Sandbox in order to adapt their behavior and escape detection.
Read more...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...