Phrozen Timeline

In the past two days, I released examples about how to enumerate DLL export table through the PE Header.

We will see one concreate example of using the UntEnumDLLExport.pas library to dynamically load API without using the famous Windows API > GetProcAddress()

This technique is quite known and often used by some Malware, to mask which API's they are dynamically loading and avoid Antivirus detection.

Read more...

This very small snippet is an adaptation of the previously released unit > UntEnumDLLExport.pas with just one goal, retrieve an exported function address by its name from any DLL (both 32 and 64bit).

This adaptation is also interesting because it remove the need of having both heavy units Generics.Collections and SysUtils to have a smaller binary.

Finally it is also quite interesting for tweaking our GetProcAddress alternative (you will find here) and only have the necesarry code.

Read more...

As promised, we will adapt our previous code grab an exported function directly from memory.

Serious advantage of this technique:

  • We don't have to use CreateToolHelp32Snapshot anymore to enumerate modules and catch target module base address.
  • We don't need to parse PE Header from disk anymore, we will parse PE Header directly from memory.

Read more...

Above snippet demonstrate how to enumerate files openned by running programs on Windows.

Some file unlocker use that technique to find where a specific file is attached and then force processes using that file to release it handle (via code injection techniques). I will write an example in a future snippet thread.

Read more...

Bellow code demonstrate our to retrieve both current and target process full image path. This technique is very uncommon but works perfectly.

// Jean-Pierre LESUEUR (@DarkCoderSc)

function PhysicalToVirtualPath(APath : String) : String;
var i          : integer;
    ADrive     : String;
    ABuffer    : array[0..MAX_PATH-1] of Char;
    ACandidate : String;
begin
  {$I-}
  for I := 0 to 25 do begin
    ADrive := Format('%s:', [Chr(Ord('A') + i)]);
    ///

    if (QueryDosDevice(PWideChar(ADrive), ABuffer, MAX_PATH) = 0) then
      continue;

    ACandidate := String(ABuffer).ToLower();

    if String(Copy(APath, 1, Length(ACandidate))).ToLower() = ACandidate then begin
      Delete(APath, 1, Length(ACandidate));

      result := Format('%s%s', [ADrive, APath]);
    end;
  end;
  {$I+}
end;
Read more...

Yet another technique to get the full image path of a target process using the NtQueryInformationProcess API documented Here

This technique from 32bit to 64bit / 64bit to 32bit.

// Jean-Pierre LESUEUR (@DarkCoderSc)

function PhysicalToVirtualPath(APath : String) : String;
var i          : integer;
    ADrive     : String;
    ABuffer    : array[0..MAX_PATH-1] of Char;
    ACandidate : String;
begin
  {$I-}
  for I := 0 to 25 do begin
    ADrive := Format('%s:', [Chr(Ord('A') + i)]);
    ///

    if (QueryDosDevice(PWideChar(ADrive), ABuffer, MAX_PATH) = 0) then
      continue;

    ACandidate := String(ABuffer).ToLower();

    if String(Copy(APath, 1, Length(ACandidate))).ToLower() = ACandidate then begin
      Delete(APath, 1, Length(ACandidate));

      result := Format('%s%s', [ADrive, APath]);
    end;
  end;
  {$I+}
end;

function GetProcessImagePath(const AProcessId : Cardinal) : String;
type PUnicodeString = ^TUnicodeString;
     TUnicodeString = record
        Length         : USHORT;
        MaximumLength  : USHORT;
        Buffer         : PWideChar;
    end;
// 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;
    hProc      : THandle;
    ALength    : ULONG;
    pImagePath : PUnicodeString;


const PROCESS_QUERY_LIMITED_INFORMATION = $00001000;
      ProcessImageFileName = 27;
begin
  hProc := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, AProcessId);
  if (hProc = 0) then
    Exit();
  try
    hNTDLL := LoadLibrary('NTDLL.DLL');
    if (hNTDLL = 0) then
      Exit();
    try
      @_NtQueryInformationProcess := GetProcAddress(hNTDLL, 'NtQueryInformationProcess');

      if NOT Assigned(_NtQueryInformationProcess) then
        Exit();
      ///

      ALength := (MAX_PATH + SizeOf(TUnicodeString)); // Should be enough :)

      GetMem(pImagePath, ALength);
      try
        if (_NtQueryInformationProcess(hProc, ProcessImageFileName, pImagePath, ALength, @ALength) <> 0) then
          Exit();
        ///

        result := PhysicalToVirtualPath(String(pImagePath^.Buffer));
      finally
        FreeMem(pImagePath, ALength);
      end;
    finally
      FreeLibrary(hNTDLL);
    end;
  finally
    CloseHandle(hProc);
  end;
end;
Read more...

This time we will use a quite well known API to get the full process image path GetProcessImageFileName documented here.

Nothing very complex and this technique works from 32bit to 64bit / 64bit to 32bit processes.

// Jean-Pierre LESUEUR (@DarkCoderSc)

function PhysicalToVirtualPath(APath : String) : String;
var i          : integer;
    ADrive     : String;
    ABuffer    : array[0..MAX_PATH-1] of Char;
    ACandidate : String;
begin
  {$I-}
  for I := 0 to 25 do begin
    ADrive := Format('%s:', [Chr(Ord('A') + i)]);
    ///

    if (QueryDosDevice(PWideChar(ADrive), ABuffer, MAX_PATH) = 0) then
      continue;

    ACandidate := String(ABuffer).ToLower();

    if String(Copy(APath, 1, Length(ACandidate))).ToLower() = ACandidate then begin
      Delete(APath, 1, Length(ACandidate));

      result := Format('%s%s', [ADrive, APath]);
    end;
  end;
  {$I+}
end;

function GetProcessImagePath(const AProcessId : Cardinal) : String;
// https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessimagefilenamew
var _GetProcessImageFileNameW : function(
                                          hProcess : THandle;
                                          lpImageFileName : LPWSTR;
                                          nSize : DWORD
                                ) : DWORD; stdcall;
    hPsAPI     : THandle;
    hProc      : THandle;
    ALength    : Cardinal;
    AImagePath : String;

const PROCESS_QUERY_LIMITED_INFORMATION = $00001000;
begin
  result := '';
  ///

  hProc := OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, AProcessId);
  if (hProc = 0) then
    Exit();
  try
    hPsAPI := LoadLibrary('psapi.dll');
    if (hPsAPI = 0) then
      Exit();
    try
      @_GetProcessImageFileNameW := GetProcAddress(hPsAPI, 'GetProcessImageFileNameW');
      if NOT Assigned(_GetProcessImageFileNameW) then
        Exit();
      ///

      SetLength(AImagePath, MAX_PATH);

      ALength := _GetProcessImageFileNameW(hProc, @AImagePath[1], MAX_PATH);
      if (ALength > 0) then begin
        SetLength(AImagePath, ALength);
        ///

        result := PhysicalToVirtualPath(AImagePath);
      end;
    finally
      FreeLibrary(hPsAPI);
    end;
  finally
    CloseHandle(hProc);
  end;
end;

I have few other methods in mind, I will post them in a near future so stay tuned :)

Read more...

Below code snippet demonstrate how to get DACL Security Descriptor in SDDL format for a targeted registry key.

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