[TUTORIAL] Anti Debugging Tricks #3 – Enumeración de procesos

  • Hola Invitado ¿Quieres conversar con todos los usuarios de GamerzHacking?, No esperes mas y entra al canal de Discord dando clic AQUI
  • Hola Invitado, hemos decidido no subir mas videos de Game Hacking a la mierda de YouTube, mas informacion AQUI. Nuestro nuevo canal de videos ahora es COCOSCOPE.
  • Hola Invitado ¿Quieres formar parte del Staff de GamerzHacking?, No esperes mas y entra al siguiente enlace AQUI
  • Hola Invitado ¿Eres programador y quieres pertenercer GamerzHacking?, No esperes mas y entra a postular aqui AQUI
1 dAmerica/Bogota Noviembre dAmerica/Bogota 2015
1.124
202
63
28
Lima
gamerzhacking.com
#1
Este es un método bastante popular para evitar a los depuradores, se ha visto un reciente resurgimiento ya que ciertos sistemas Anti-Cheat lo han implementado para detectar "herramientas de trampa" como Cheat Engine. Cómo funciona es bastante simple. Una vez que se inicie el programa, intentará ubicar una ventana por su título (mediante una llamada a la API como FindWindowEx ), o enumerará todo el proceso en ejecución en el sistema en busca de nombres de ejecutables "prohibidos". Si encuentra algo que no le gusta, el programa termina inmediatamente.

Evitar esta detección es bastante sencillo. Cambiar el nombre del ejecutable y editar la cadena responsable de configurar el título de la ventana suele ser suficiente para omitirlo. Esto también tiene la ventaja adicional de cambiar la firma del ejecutable, por lo que los mecanismos de detección basados en hash también fallarán.

Si el programa también utiliza algún tipo de algoritmo de escaneo binario no estándar, podemos omitirlo nuevamente utilizando un empaquetador como UPX . O simplemente puede escribir el suyo, lo que garantiza que no se detectará.

Procesos de Padres

Otro truco que se usa comúnmente es adquirir el Identificador de proceso (PID) del proceso principal y compararlo con procesos específicos en el sistema: es decir, Explorer.exe. La mayoría de los procesos se iniciarán desde el shell, por lo que, naturalmente, su proceso "principal" será Explorer. Sin embargo, si se adjunta un depurador, su proceso principal será el depurador, que detectará y cerrará inmediatamente.

También he visto cómo se utiliza esta técnica para garantizar que los juegos se inicien desde su aplicación "lanzador". Si el ejecutable principal del juego se inicia solo abrirá el lanzador y se cerrará inmediatamente. Esto se hace normalmente para asegurarse de que el juego esté parcheado antes de ejecutarse, pero puede funcionar como un obstáculo al intentar depurarlo.

Hay un problema menor con esta técnica. Puede haber varias copias del explorador ejecutándose al mismo tiempo, es decir, si configura la opción "Ejecutar cada ventana del explorador en un proceso separado". Para solucionar esto, tienen que tomar el PID de cada proceso en el sistema que informa ser Explorer.exe. Como un obstáculo adicional, también deberán verificar que el proceso realmente reside en el directorio del sistema para asegurarse de que alguien no haya cambiado el nombre de su depurador.

Dependiendo de la sofisticación de los métodos de detección, es posible que no lleguen tan lejos para detectar su depurador. Sin embargo, supongamos que fueron todo el camino y salpicaron cada 'i'. Diseñemos nuestra detección para que sea lo más robusta posible y luego analizaremos los vectores de ataque para evitarlo.

El código

Nuestra aplicación de detección necesita hacer lo siguiente:
  1. Localice el PID del proceso padre.
  2. Localice el directorio de Windows.
  3. Escanee a través de los procesos disponibles en busca de Explorer.exe.
  4. Compruebe cada archivo ejecutable y asegúrese de que se encuentre en la ubicación correcta.
  5. Compare el PID principal con cada versión "correcta" de Explorer.exe.
  6. Termine si el PID principal no coincide con un proceso de Explorer.
Puede adquirir el PID llamando a NtQueryInformationProcess o usando algún ensamblaje en línea.

C:
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
 
typedef NTSTATUS (WINAPI *NAPI_QUERYPROC)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);
NAPI_QUERYPROC fnNtQueryInformationProcess;
 
int main(void)
{
    HMODULE hModule = LoadLibrary("ntdll.dll");
    if(!hModule)
          return -1;
 
    fnNtQueryInformationProcess = (NAPI_QUERYPROC)GetProcAddress(hModule, "NtQueryInformationProcess");
    if(!fnNtQueryInformationProcess) {
        FreeLibrary(hModule);
        return -1;
    }
 
    unsigned long lSize = 0;
    PROCESS_BASIC_INFORMATION pbi = { 0 };
    fnNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), &lSize);
 
    printf("Parent PID is: %ld\n", pbi.InheritedFromUniqueProcessId);
 
    FreeLibrary(hModule);
    return 0;
}
Sin embargo, si utilizamos la API de ayuda para herramientas , aún podemos adquirir el PID principal y también podemos buscar programas que no queremos que se ejecuten.

Considera lo siguiente:

C:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
 
DWORD_PTR GetParentPID(DWORD_PTR dwPid)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
        return -1;
 
    PROCESSENTRY32 pe = { 0 };
 
    pe.dwSize = sizeof(PROCESSENTRY32);
 
    DWORD_PTR dwRet = 0;
    BOOL bSuccess = Process32First(hSnapshot, &pe);
 
    while (bSuccess) {
        // You can insert a check here for unwanted programs and return if found here...
        if (dwPid == pe.th32ProcessID) {
            dwRet = pe.th32ParentProcessID;
            break;
        }
        bSuccess = Process32Next(hSnapshot, &pe);
    }
 
    CloseHandle(hSnapshot);
 
    return dwRet;
}
 
BOOL GetProcessNameByPID(DWORD_PTR dwPid, char *szBuffer, int nSize)
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE)
        return -1;
 
    PROCESSENTRY32 pe = { 0 };
 
    pe.dwSize = sizeof(PROCESSENTRY32);
 
    BOOL bRet = FALSE;
    DWORD_PTR dwRet = 0;
    BOOL bSuccess = Process32First(hSnapshot, &pe);
 
    while (bSuccess) {
        if (dwPid == pe.th32ProcessID) {
            strncpy(szBuffer, pe.szExeFile, nSize);
            bRet = TRUE;
            break;
        }
        bSuccess = Process32Next(hSnapshot, &pe);
    }
 
    CloseHandle(hSnapshot);
 
    return bRet;
}
 
int main(int argc, char*argv[])
{
    DWORD_PTR dwPID = GetCurrentProcessId();
    DWORD_PTR dwParentId = 0;
 
    dwParentId = GetParentPID(dwPID);
    if (dwParentId == 0) {
        return -1;
    }
 
    char szBuffer[MAX_PATH] = { 0 };
    if (!GetProcessNameByPID(dwParentId, szBuffer, MAX_PATH)) {
        return -1;
    }
 
    printf("INFO - Process: %s (%ld) was created by %s (%ld)\n\n", argv[0], dwPID, szBuffer, dwParentId);
    if (_strcmpi(szBuffer, "explorer.exe")) { // If process was started normally this should be explorer
        printf("STOP DEBUGGING ME!\n");
        return -1;
    }
 
    printf("Normal Execution\n");
    return 0;
}
Si ejecuta ese código desde dentro de Visual Studio, le dará el siguiente mensaje:

INFO - Process: Antidebug_ParentPid.exe (5132) was created by VsDebugConsole.exe (12948)

STOP DEBUGGING ME!
Y si lo ejecutas desde el directorio recibirás este mensaje:

INFO - Process: Antidebug_ParentPid.exe (8384) was created by explorer.exe (7916)

Normal Execution
Bastante elegante, ¿verdad? Sin embargo, puede notar que este código es vulnerable simplemente al cambiar el nombre del ejecutable de su depurador:



Así que tendremos que modificar nuestro código para hacerlo un poco más difícil de vencer.

Primero modifique el código principal a lo siguiente:

C:
// ....
    char szWinDir[MAX_PATH] = { 0 };
    GetWindowsDirectory(szWinDir, MAX_PATH);
 
    char *szExplorerPath = (char*)calloc(1, strlen(szWinDir) + 15);
    sprintf(szExplorerPath, "%s\\explorer.exe", szWinDir);
 
    printf("INFO - Process: %s (%ld) was created by %s (%ld)\n\n", argv[0], dwPID, szBuffer, dwParentId);
    if (_strcmpi(szBuffer, szExplorerPath)) {
        printf("STOP DEBUGGING ME!\n");
        free(szExplorerPath);
        return -1;
    }
 
    free(szExplorerPath);
// ...
Este código ahora resolverá la ruta al directorio de Windows añadiendo explorer.exe. Ahora solo necesitamos modificar nuestra función GetProcessName para tomar la ruta completa en lugar de solo el nombre del ejecutable.

C:
// ...
        while (bSuccess) {
        if (dwPid == pe.th32ProcessID) {
            HANDLE hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwPid);
            if (hProc) {
                GetModuleFileNameEx(hProc, NULL, szBuffer, nSize);
                CloseHandle(hProc);
                bRet = TRUE;
            }
            break;
        }
        bSuccess = Process32Next(hSnapshot, &pe);
    }
// ...
Ahora, si usamos nuestro truco de cambio de nombre dudoso, nuestro programa debería ser lo suficientemente inteligente como para detectarlo:

INFO - Process: Antidebug_ParentPid.exe (3152) was created by D:\x64dbg\release\x32\explorer.exe (10876)

STOP DEBUGGING ME!
Sí.

Vectores de ataque

Hay muchas formas de atacar este código. Para empezar, podemos enlazar GetWindowsDirectory o GetSystemWindowsDirectory cuando uno llama al otro. Podemos escanear el binario en busca de cadenas y modificar la cadena de comparación: asumiendo que no está encriptado o confuso para ocultarlo (lo que realmente debería ser) o simplemente podemos eliminar la comparación.

Opté por simplemente modificar la cadena para que apunte a una ubicación diferente:

DEBUG WINDIR - D:\x64dbg\release\x32\explorer.exe
INFO - Process: Antidebug_ParentPid.exe (4944) was created by D:\x64dbg\release\x32\explorer.exe (10272)

Normal Execution
Enlace Original
Anti Debugging Tricks #3 - Enumerating Processes - Game Phreakers

Donación
única : BTC 1DXcjix3FmcHYezFAjCrpzZA9FkbSC971e
Paypal PayPal.me/timb3r

Donación mensual:
Patreon

Creditos
timb3r