By checking the parent process of a given process we can determine if the process is being debugged or not by expecting โexplorer.exeโ to be the usual parent process started by the user.
For this technique the following Windows APIs are used.
We also use a pointer to PROCESSENTRY32 structure which will store the information of each process taken from the snapshot.
typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage; DWORD th32ProcessID; ULONG_PTR th32DefaultHeapID; DWORD th32ModuleID; DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase; DWORD dwFlags; TCHAR szExeFile[MAX_PATH]; } PROCESSENTRY32, *PPROCESSENTRY32;
First we get the PID of the explorer.exe process by taking a snapshot of all process and enumerating through the list. Next we again take a snapshot and locate the current Process ID (PID) of our process by enumerating through the list and then we evaluate if the Parent Process ID (PPID) of our current process is equal to โexplorer.exeโ.
In case if someone rename something else as โexplorer.exeโ we can write more checks to determine if the parent process is equal โexplorer.exeโ.
Hereโs an example I wrote in C.
#include <windows.h> | |
#include <stdio.h> | |
#include <tchar.h> | |
#include "tlhelp32.h" | |
/* Title: Determining debugger present using the Parent Process detection. | |
* Author: Osanda Malith Jayathissa (@OsandaMalith) | |
* Website: http://osandamalith.wordpress.com | |
*/ | |
int main(int argc, char *argv[]) { | |
int pid = 0; | |
int exp_pid = 0; | |
HANDLE handle = INVALID_HANDLE_VALUE; | |
PROCESSENTRY32 pe = {0}; | |
pe.dwSize = sizeof(PROCESSENTRY32); | |
pid = GetCurrentProcessId(); | |
handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | |
if (handle == INVALID_HANDLE_VALUE) | |
fprintf(stderr, "Cannot get a snapshot. Error: %x\n", GetLastError()); | |
if(Process32First(handle, &pe)) { | |
do | |
if (!_tcsicmp(pe.szExeFile, _T("explorer.exe"))) | |
exp_pid = pe.th32ProcessID; | |
while(Process32Next(handle, &pe)); | |
} | |
if (exp_pid == –1) | |
fprintf(stderr, "Cannot get PID of explorer. Error: %x\n", GetLastError()); | |
if(Process32First(handle, &pe)) { | |
do if (pe.th32ProcessID == pid) | |
MessageBoxA(NULL, pe.th32ParentProcessID == exp_pid ? "Debugger NOT Present\n" : "Debugger Present\n","Status", pe.th32ParentProcessID == exp_pid ? 0x40 : 0x30); | |
while(Process32Next(handle, &pe)); | |
} | |
CloseHandle(handle); | |
return 0; | |
} |
https://github.com/OsandaMalith/Anti-Debug/blob/master/PPID/ppid.c
This is an example which I wrote in MASM.
.586 | |
.model flat, stdcall | |
option casemap :none | |
; ยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยครทยค | |
; Author : Osanda Malith Jayathissa (@OsandaMalith) | |
; Title: Test if process is being debugged if PPID != explorer.exe | |
; Website: http://osandamalith.wordpress.com | |
; ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค=รท=ยค | |
include windows.inc | |
include user32.inc | |
include kernel32.inc | |
includelib user32.lib | |
includelib kernel32.lib | |
.data | |
exp db "explorer.exe",0 | |
AppName db "Status",0 | |
errSnapshot db "CreateToolhelp32Snapshot failed.",0 | |
errProcFirst db "Process32First failed.",0 | |
errExolorer db "Explorer.exe Not Found!" | |
expfound db "Explorer.exe Found",0 | |
dbgFound db "Debugger Found!", 0 | |
dbgNotFound db "Debugger Not Found!", 0 | |
exp_pid dd 0 | |
pid dd 0 | |
.data? | |
hSnapshot HANDLE ? | |
ProcEnt PROCESSENTRY32 <?> | |
.code | |
start: | |
lea esi, offset ProcEnt | |
assume esi:ptr PROCESSENTRY32 | |
mov [esi].dwSize, sizeof PROCESSENTRY32 | |
invoke GetCurrentProcessId | |
mov pid, eax | |
invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS, NULL | |
.IF (eax != INVALID_HANDLE_VALUE) | |
mov hSnapshot, eax | |
invoke Process32First, hSnapshot, ADDR ProcEnt | |
.IF (eax) | |
@@: | |
invoke lstrcmpi, ADDR exp , ADDR [ProcEnt.szExeFile] | |
.IF (!eax) | |
lea ebx, [esi].th32ProcessID | |
push [ebx] | |
pop exp_pid | |
jmp nextLoop | |
.ELSE | |
.ENDIF | |
invoke Process32Next, hSnapshot, ADDR ProcEnt | |
test eax,eax | |
jnz @B | |
.ELSE | |
invoke MessageBox, NULL, ADDR errProcFirst, ADDR AppName, MB_OK or MB_ICONERROR | |
.ENDIF | |
invoke CloseHandle, hSnapshot | |
.ELSE | |
invoke MessageBox, NULL, ADDR errSnapshot, ADDR AppName, MB_OK or MB_ICONERROR | |
.ENDIF | |
nextLoop: | |
invoke CreateToolhelp32Snapshot, TH32CS_SNAPPROCESS, NULL | |
.IF (eax != INVALID_HANDLE_VALUE) | |
mov hSnapshot, eax | |
invoke Process32First, hSnapshot, ADDR ProcEnt | |
.IF (eax) | |
@@: | |
mov ebx, pid | |
.IF ( ebx == [esi].th32ProcessID ) | |
mov ebx, [exp_pid] | |
.if ( ebx == [esi].th32ParentProcessID ) | |
invoke MessageBox, NULL, ADDR dbgNotFound, ADDR AppName, MB_OK or MB_ICONINFORMATION | |
.else | |
invoke MessageBox, NULL, ADDR dbgFound, ADDR AppName, MB_OK or MB_ICONERROR | |
.endif | |
.ELSE | |
.ENDIF | |
invoke Process32Next, hSnapshot, ADDR ProcEnt | |
test eax,eax | |
jnz @B | |
.ELSE | |
invoke MessageBox, NULL, ADDR errProcFirst, ADDR AppName, MB_OK or MB_ICONERROR | |
.ENDIF | |
invoke CloseHandle, hSnapshot | |
.ELSE | |
invoke MessageBox, NULL, ADDR errSnapshot, ADDR AppName, MB_OK or MB_ICONERROR | |
.ENDIF | |
invoke ExitProcess, NULL | |
end start |
https://github.com/OsandaMalith/Anti-Debug/blob/master/PPID/ppid.asm
Example when we normally run the program.
When the PPID is not equal to explorer.exe.
One thought on “Parent Process Detection”