Phrozen Unprotect Project - Papers - Phrozen
External Process Hollowing, RunPE 24 Jun 2022

New Unprotect Delphi Code Snippet added for technique Process Hollowing, RunPE with support of both x86-32 and x86-64 in a single code.


External ProcEnvInjection - Remote code injection by abusing process environment strings 17 Jun 2022

New Unprotect Delphi Code Snippet added for technique ProcEnvInjection - Remote code injection by abusing process environment strings for both x86-32 and x86-64.


External DLL Injection via CreateRemoteThread and LoadLibrary 17 Jun 2022

New Unprotect Delphi Code Snippet added for technique DLL Injection via CreateRemoteThread and LoadLibrary with both support of x86-32 and x86-64.


External Checking Mouse Activity 17 Jun 2022

New Unprotect Delphi Code Snippet added for technique Checking Mouse Activity


Detecting Active Services 22 Feb 2021

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...
Update PEB Debug Flag 23 Nov 2020

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...
NtQueryObject 17 Oct 2020

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...
NtSetInformationThread 17 Oct 2020

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...
NtSetDebugFilterState 17 Oct 2020

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...
IsDebuggerPresent 17 Oct 2020

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...