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...This technique is often used by Malware to hide their presence on system after execution. The application self-delete after the end of its execution. The best method to archive a such goal is through process injection.
Read more...ADS (Alternate Data Stream) is a channel often abused by Malware authors to hide files by abusing the property of NTFS File Systems.
It is mainly used to store: additional payloads, collected data or settings.
An ADS file is completely hidden from explorer. In modern version of Windows, you can display ADS files using the command dir /r
or through using third part tools.
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...