SuspendThread

The kernel32 SuspendThread function or the NTDLL NtSuspendThread function can be a very effective way to disable user-mode debuggers. This can be achieved by enumerating the threads of a given process or searching for a named window and opening its owner thread, then suspending that thread.

U0101

Code Snippets

Jean-Pierre LESUEUR

Description

You can compile this unit as a classic Delphi Console Application. Feel free to edit both LFindWindowSignatures and LProcessNameSignatures to support more debuggers.

program SuspendThread;

{$APPTYPE CONSOLE}

uses
  WinAPI.Windows, System.SysUtils, Generics.Collections, tlHelp32, Classes;

type
  TProcessItem = class
  private
    FName      : String;
    FProcessId : Cardinal;
    FThreads   : TList<Cardinal>;

    {@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<Cardinal> read FThreads;
  end;

  TEnumProcess = class
  private
    FItems : TObjectList<TProcessItem>;
  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<TProcessItem> 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<String, String>;
    LProcessNameSignatures : TStringList;
    LProcesses             : TEnumProcess;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Process Item (Process Name / Process Id / Process Main Thread Id)
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-------------------------------------------------------------------------------
  ___constructor
-------------------------------------------------------------------------------}
constructor TProcessItem.Create(AName : String; AProcessId : Cardinal; AEnumThreads : Boolean = True);
begin
  FName      := AName;
  FProcessId := AProcessId;

  FThreads := TList<Cardinal>.Create();

  if AEnumThreads then
    self.EnumThreads();
end;

{-------------------------------------------------------------------------------
  ___destructor
-------------------------------------------------------------------------------}
destructor TProcessItem.Destroy();
begin
  if Assigned(FThreads) then
    FreeAndNil(FThreads);

  ///
  inherited Destroy();
end;

{-------------------------------------------------------------------------------
  Enumerate Threads of process object
-------------------------------------------------------------------------------}
procedure TProcessItem.EnumThreads();
var ASnap        : THandle;
    AThreadEntry : TThreadEntry32;

    procedure InitializeItem();
    begin
      ZeroMemory(@AThreadEntry, SizeOf(TThreadEntry32));

      AThreadEntry.dwSize := SizeOf(TThreadEntry32);
    end;

    procedure AppendItem();
    begin
      if (AThreadEntry.th32OwnerProcessID <> FProcessId) then
        Exit();
      ///

      FThreads.Add(AThreadEntry.th32ThreadID);
    end;
begin
  if NOT Assigned(FThreads) then
    Exit();
  ///

  FThreads.Clear();
  ///

  ASnap := CreateToolHelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  if (ASnap = INVALID_HANDLE_VALUE) then
    Exit();
  try
    InitializeItem();

    if NOT Thread32First(ASnap, AThreadEntry) then
      Exit();

    AppendItem();

    while True do begin
      InitializeItem();

      if NOT Thread32Next(ASnap, AThreadEntry) then
        break;

      AppendItem();
    end;
  finally
    CloseHandle(ASnap);
  end;
end;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Enumerate Process Class
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-------------------------------------------------------------------------------
  ___constructor
-------------------------------------------------------------------------------}
constructor TEnumProcess.Create();
begin
  FItems := TObjectList<TProcessItem>.Create();
  FItems.OwnsObjects := True;

  ///
  self.Refresh();
end;

{-------------------------------------------------------------------------------
  ___destructor
-------------------------------------------------------------------------------}
destructor TEnumProcess.Destroy();
begin
  if Assigned(FItems) then
    FreeAndNil(FItems);

  ///
  inherited Destroy();
end;

{-------------------------------------------------------------------------------
  Enumerate Running Process.
  @Return: Process Count
-------------------------------------------------------------------------------}
function TEnumProcess.Refresh() : Cardinal;
var ASnap         : THandle;
    AProcessEntry : TProcessEntry32;

    procedure InitializeItem();
    begin
      ZeroMemory(@AProcessEntry, SizeOf(TProcessEntry32));

      AProcessEntry.dwSize := SizeOf(TProcessEntry32);
    end;

    procedure AppendItem();
    var AItem : TProcessItem;
    begin
      AItem := TProcessItem.Create(
                                    AProcessEntry.szExeFile,
                                    AProcessEntry.th32ProcessID,
                                    True {Enum Threads: Default}
      );

      FItems.Add(AItem);
    end;

begin
  result := 0;
  ///

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

  self.Clear();

  ASnap := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  if (ASnap = INVALID_HANDLE_VALUE) then
    Exit();
  try
    InitializeItem();

    if NOT Process32First(ASnap, AProcessEntry) then
      Exit();

    AppendItem();

    while True do begin
      InitializeItem();

      if NOT Process32Next(ASnap, AProcessEntry) then
        break;

      AppendItem();
    end;
  finally
    CloseHandle(ASnap);
  end;
end;

{-------------------------------------------------------------------------------
  Clear Items (Process Objects)
-------------------------------------------------------------------------------}
procedure TEnumProcess.Clear();
begin
  if Assigned(FItems) then
    FItems.Clear;
end;

{-------------------------------------------------------------------------------
  Get Process Item by Process Id or Name
-------------------------------------------------------------------------------}
function TEnumProcess.Get(AProcessId : Cardinal) : TProcessItem;
var AItem : TProcessItem;
    I     : Integer;
begin
  result := nil;
  ///

  for I := 0 to self.Items.count -1 do begin
    AItem := self.Items.Items[I];
    if NOT Assigned(AItem) then
      continue;
    ///

    if (AItem.ProcessId = AProcessId) then begin
      result := AItem;

      Break;
    end;
  end;
end;

function TEnumProcess.Get(AName : String) : TProcessItem;
var AItem : TProcessItem;
    I     : Integer;
begin
  result := nil;
  ///

  for I := 0 to self.Items.count -1 do begin
    AItem := self.Items.Items[I];
    if NOT Assigned(AItem) then
      continue;
    ///

    if (AItem.Name.ToLower = AName.ToLower) then begin
      result := AItem;

      Break;
    end;
  end;
end;

{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  Main
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}

{-------------------------------------------------------------------------------
  Suspend Threads of target process.
-------------------------------------------------------------------------------}
function SuspendThreadsByProcessId(AProcessId : Cardinal) : Boolean;
var AItem     : TProcessItem;
    AThreadId : Cardinal;
    I         : Integer;
    AThread   : THandle;
begin
  result := False;
  ///

  if NOT Assigned(LProcesses) then
    Exit();

  AItem := LProcesses.Get(AProcessId);
  if NOT Assigned(AItem) then
    Exit();
  ///

  if (AItem.Threads.count = 0) then
    Exit();
  ///

  for I := 0 to AItem.Threads.Count -1 do begin
    AThreadId := AItem.Threads.Items[I];
    ///

    AThread := OpenThread(THREAD_SUSPEND_RESUME, False, AThreadId);
    if (AThread = 0) then
      continue;
    try
      WriteLn(Format('Suspending: %s(%d), Thread Id: %d...', [
                                                                    AItem.Name,
                                                                    AItem.ProcessId,
                                                                    AThreadId
      ]));

      WinAPI.Windows.SuspendThread(AThread);

      result := True;
    finally
      CloseHandle(AThread);
    end;
  end;
end;

{-------------------------------------------------------------------------------
  FindWindow API Example
-------------------------------------------------------------------------------}
function method_FindWindow() : Boolean;
var AHandle     : THandle;
    AProcessId  : Cardinal;
    AClassName  : String;
    AWindowName : String;
    pClassName  : Pointer;
    pWindowName : Pointer;
begin
  result := False;
  ///

  for AClassName in LFindWindowSignatures.Keys do begin
    if NOT LFindWindowSignatures.TryGetValue(AClassName, AWindowName) then
      continue;
    ///

    pClassName  := nil;
    pWindowName := nil;

    if NOT AClassName.isEmpty then
      pClassName := PWideChar(AClassName);

    if NOT AWindowName.isEmpty then
      pWindowName := PWideChar(AWindowName);

    AHandle := FindWindowW(pClassName, pWindowName);
    if (AHandle > 0) then begin
      GetWindowThreadProcessId(AHandle, @AProcessId);
      if (AProcessId > 0) then
        SuspendThreadsByProcessId(AProcessId);

      ///
      result := True;
    end;
  end;
end;

{-------------------------------------------------------------------------------
  Find Process Example (Uses the TEnumProcess Class) - See above
-------------------------------------------------------------------------------}
function method_FindProcess() : Boolean;
var AItem : TProcessItem;
    AName : String;
    I     : Integer;
begin
  result := False;
  ///

  for I := 0 to LProcessNameSignatures.count -1 do begin
    AName := LProcessNameSignatures.Strings[I];

    AItem := LProcesses.Get(AName);
    if (NOT Assigned(AItem)) then
      continue;
    ///

    SuspendThreadsByProcessId(AItem.ProcessId);

    ///
    result := True;
  end;
end;

{-------------------------------------------------------------------------------
  ___entry
-------------------------------------------------------------------------------}
begin
  try
    LProcesses := TEnumProcess.Create();
    try
      // FindWindow API
      LFindWindowSignatures := TDictionary<String, String>.Create();
      try
        {
          ...

          @Param1: ClassName  (Empty = NULL)
          @Param2: WindowName (Empty = NULL)

          Add your own signatures bellow...
        }
        LFindWindowSignatures.Add('OLLYDBG', '');
        {
          ...
        }
        method_FindWindow();
      finally
        if Assigned(LFindWindowSignatures) then
          FreeAndNil(LFindWindowSignatures);
      end;

      // Find by Process Name
      LProcessNameSignatures := TStringList.Create();
      try
        {
          ...

          @Param1: Process Name (Example: OllyDbg.exe) - Case Insensitive

          Add your own signatures bellow...
        }
        LProcessNameSignatures.Add('ImmunityDebugger.exe');
        {
          ...
        }
        method_FindProcess();
      finally
        if Assigned(LProcessNameSignatures) then
          FreeAndNil(LProcessNameSignatures);
      end;
    finally
      if Assigned(LProcesses) then
        FreeAndNil(LProcesses);
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Additional Resources

Subscribe to our Newsletter


The information entered into this form is mandatory. It will be subjected to computer processing. It is processed by computer in order to support our users and readers. The recipients of the data will be : contact@unprotect.it.

According to the Data Protection Act of January 6th, 1978, you have at any time, a right of access to and rectification of all of your personal data. If you wish to exercise this right and gain access to your personal data, please write to Thomas Roccia at contact@unprotect.it.

You may also oppose, for legitimate reasons, the processing of your personal data.