W jednym z ostatnich postów (Jeśli nie chcesz zarobić na swoim programie…) jako jeden z punktów umieściłem niewykrywanie debuggera. Noe dobrze, łatwo napisać, ale jak tak na prawdę można wykryć, że ktoś podgląda nasz kod w C#?? Jest kilka metod:

Debugger.IsAttached

Pierwszą metodę udostępnia nam sam framework. W namespace’ie System.Diagnostic jest klasa Debugger. Wystarczy, że napiszemy:

  Debugger.IsAttached

Niestety wraz z prostotą tego rozwiązania przychodzi także jej ograniczenie. Wykryjemy nią (chyba) tylko uruchomienia programu pod Debuggerem VS.

API – IsDebuggerPresent()

W bibliotece Kernel32.dll możemy odnaleźć coś więcej. Wystarczy więc przy użyciu P/Invoke’a dodać definicję tej metody do naszego kodu. Zrobić to możemy tak:

  [DllImport(“Kernel32.dll”)]

  public static extern bool IsDebuggerPresent();

Metoda wykrywa WinDbg ale nie wykrywa debugger’a VS (??).

NtQueryInformationProcess

Istnieje jesszcze 3 metoda, wykorzystująca metodę NtQueryInformationProcess. Za jej pomocą możemy uzyskać informacje o procesie, który jest właśnie wykonywany. Jedną z informacji, która się tam znajduje jest wskaźnik na strukturę PEB, w której jest zawarta flaga czy program jest uruchomiony pod kontrolą debuggera. Kod troszkę bardziej używający P/Invoke’a.
Deklaracja niezbędnych struktur:

public enum PROCESSINFOCLASS : int

{

    ProcessBasicInformation = 0,

    ProcessQuotaLimits,

    ProcessIoCounters,

    ProcessVmCounters,

    ProcessTimes,

    ProcessBasePriority,

    ProcessRaisePriority,

    ProcessDebugPort,

    ProcessExceptionPort,

    ProcessAccessToken,

    ProcessLdtInformation,

    ProcessLdtSize,

    ProcessDefaultHardErrorMode,

    ProcessIoPortHandlers, // Note: this is kernel mode only

    ProcessPooledUsageAndLimits,

    ProcessWorkingSetWatch,

    ProcessUserModeIOPL,

    ProcessEnableAlignmentFaultFixup,

    ProcessPriorityClass,

    ProcessWx86Information,

    ProcessHandleCount,

    ProcessAffinityMask,

    ProcessPriorityBoost,

    MaxProcessInfoClass,

    ProcessWow64Information = 26

};

 

[StructLayout(LayoutKind.Sequential)]

public struct PROCESS_BASIC_INFORMATION

{

    public long ExitStatus;

    public long PebBaseAddress;

    public long AffinityMask;

    public long BasePriority;

    public long UniqueProcessId;

    public long InheritedFromUniqueProcessId;

 

    public static int Size

    {

        get { return (6 * sizeof(long)); }

    }

}

 

[StructLayout(LayoutKind.Sequential)]

public class _PEB

{

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]

    public byte[] Reserved1;

    public byte BeingDebugged;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]

    public byte[] Reserved2;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]

    public IntPtr[] Reserved3;

    public IntPtr Ldr;

    public IntPtr ProcessParameters;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 104)]

    public byte[] Reserved4;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 52)]

    public IntPtr[] Reserved5;

    public IntPtr PostProcessInitRoutine;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]

    public byte[] Reserved6;

    public IntPtr Reserved7;

    public ulong SessionId;       

}

 

public struct _PEB64

{

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]

    public byte[] Reserved1;

    public byte BeingDebugged;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 21)]

    public byte[] Reserved2;

    public IntPtr LoaderData;

    public IntPtr ProcessParameters;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 520)]

    public byte[] Reserved3;

    public IntPtr PostProcessInitRoutine;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 136)]

    public byte[] Reserved4;

    public ulong SessionId;

}

oraz import odpowiedniej metody z Ntdll.dll

[DllImport(“NTDLL.DLL”, SetLastError = true)]

static extern int NtQueryInformationProcess(IntPtr hProcess, PROCESSINFOCLASS pic,

ref PROCESS_BASIC_INFORMATION pbi, int cb, out int pSize);

Pozostaje tylko wywołać to co zaimportowaliśmy:

var pbi = new PROCESS_BASIC_INFORMATION();

int size;

var handle = Process.GetCurrentProcess().Handle;

NtQueryInformationProcess(handle, PROCESSINFOCLASS.ProcessBasicInformation,

                                ref pbi, PROCESS_BASIC_INFORMATION.Size, out size);

var p64 = (_PEB64) Marshal.PtrToStructure(new IntPtr(pbi.PebBaseAddress),

                                typeof (_PEB64));

Sprawdzenie to już drobnostka:

  p64.BeingDebugged != 0

Oczywiście w przypadku 32-bitów należy użyć struktury _PEB zamiast _PEB32. Jak wykryć system można znaleźć w poście – 32 czy 64-bity? Oto jest pytanie?