IsDebugged Flag

While a process is running, the location of the PEB can be referenced by the location fs:[30h]. For anti-debugging, malware will use that location to check the BeingDebugged flag, which indicates whether the specified process is being debugged.

U0113

Code Snippets

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.

Detection Rules

rule:
  meta:
    name: check for PEB BeingDebugged flag
    namespace: anti-analysis/anti-debugging/debugger-detection
    author: moritz.raabe@fireeye.com
    scope: basic block
    mbc:
      - Anti-Behavioral Analysis::Debugger Detection::Process Environment Block BeingDebugged [B0001.035]
    references:
      - Practical Malware Analysis, Chapter 16, p. 353
    examples:
      - Practical Malware Analysis Lab 16-01.exe_:0x403530
  features:
    - and:
      - match: PEB access
      - offset: 2 = PEB.BeingDebugged

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.