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.

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.

Additional Resources

Subscribe to our Newsletter and don't miss important updates