Tecnicas Anti-Analisis: Anti-Debug, Anti-VM y Anti-Sandbox
Tecnicas anti-analisis en malware: IsDebuggerPresent, timing checks, deteccion de VM via CPUID y MAC, deteccion de sandbox, evasion de analisis automatizado. Contramedidas y bypass para cada tecnica.
La carrera armamentista del analisis
El malware sofisticado no se limita a hacer dano: antes verifica si esta siendo observado. Las tecnicas anti-analisis son la tercera capa de defensa del malware, despues del packing y la ofuscacion. Mientras el packer oculta el codigo y la ofuscacion lo hace ilegible, las tecnicas anti-analisis detectan el entorno de analisis y modifican el comportamiento del malware.
Un malware con anti-debug puede parecer benigno en un debugger. Un malware con anti-VM puede no ejecutar su payload en las maquinas virtuales que usan las sandboxes automatizadas. Y un malware con anti-sandbox puede esperar minutos antes de hacer nada, agotando el timeout del analisis automatizado.
Entender estas tecnicas y sus contramedidas es fundamental para el reverse engineering efectivo.
Tecnicas Anti-Debug
IsDebuggerPresent
La tecnica mas basica. Llama a la API IsDebuggerPresent de kernel32.dll, que simplemente lee el campo BeingDebugged del PEB (Process Environment Block):
; Equivalente manual
mov eax, fs:[0x30] ; PEB
movzx eax, byte [eax+2] ; PEB.BeingDebugged
test eax, eax
jnz debugger_detected
Contramedida:
- En x64dbg: ir a PEB (usando
dump fs:[0x30]), cambiar byte en offset +2 a 0. - Plugin ScyllaHide en x64dbg: parchea automaticamente IsDebuggerPresent.
- En IDA: poner breakpoint en IsDebuggerPresent y cambiar el valor de retorno (EAX) a 0.
CheckRemoteDebuggerPresent
Verifica si un debugger remoto esta adjunto al proceso:
BOOL bDebuggerPresent = FALSE;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &bDebuggerPresent);
if (bDebuggerPresent) exit(0);
Contramedida: Hookear la funcion para que siempre escriba FALSE. ScyllaHide lo maneja automaticamente.
NtQueryInformationProcess
Consulta informacion del proceso con diferentes clases de informacion:
// ProcessDebugPort (0x07)
DWORD debugPort = 0;
NtQueryInformationProcess(
GetCurrentProcess(), 7, &debugPort, sizeof(debugPort), NULL
);
if (debugPort != 0) exit(0);
// ProcessDebugObjectHandle (0x1E)
HANDLE debugObject = NULL;
NtQueryInformationProcess(
GetCurrentProcess(), 0x1E, &debugObject, sizeof(debugObject), NULL
);
if (debugObject != NULL) exit(0);
// ProcessDebugFlags (0x1F)
DWORD debugFlags = 0;
NtQueryInformationProcess(
GetCurrentProcess(), 0x1F, &debugFlags, sizeof(debugFlags), NULL
);
if (debugFlags == 0) exit(0); // 0 = debugger presente
Contramedida: ScyllaHide hookea NtQueryInformationProcess para las tres clases de informacion.
PEB.NtGlobalFlag
Cuando un proceso se crea bajo un debugger, el campo NtGlobalFlag del PEB se establece con flags de heap debugging:
; Verificar NtGlobalFlag
mov eax, fs:[0x30] ; PEB
mov eax, [eax + 0x68] ; PEB.NtGlobalFlag (offset 0x68 en x86)
and eax, 0x70 ; FLG_HEAP_ENABLE_TAIL_CHECK |
; FLG_HEAP_ENABLE_FREE_CHECK |
; FLG_HEAP_VALIDATE_PARAMETERS
jnz debugger_detected
Contramedida: Parchear el campo NtGlobalFlag a 0 en el PEB.
Timing Checks
Medir el tiempo de ejecucion de un bloque de codigo. Bajo debugging paso a paso, el tiempo es enormemente mayor:
// RDTSC (Read Time-Stamp Counter)
unsigned __int64 time1, time2;
time1 = __rdtsc();
// ... codigo trivial ...
time2 = __rdtsc();
if ((time2 - time1) > 1000000) exit(0); // threshold
// GetTickCount
DWORD t1 = GetTickCount();
// ... codigo ...
DWORD t2 = GetTickCount();
if ((t2 - t1) > 100) exit(0);
// QueryPerformanceCounter
LARGE_INTEGER t1, t2;
QueryPerformanceCounter(&t1);
// ... codigo ...
QueryPerformanceCounter(&t2);
if ((t2.QuadPart - t1.QuadPart) > threshold) exit(0);
Contramedida:
- Evitar single-stepping en bloques con timing: poner breakpoint despues del bloque.
- Parchear la instruccion RDTSC para que retorne un valor fijo.
- ScyllaHide puede parchear RDTSC.
Deteccion de breakpoints
Software breakpoints (INT 3):
// Verificar si hay INT 3 (0xCC) en una funcion
unsigned char* func = (unsigned char*)&target_function;
for (int i = 0; i < 100; i++) {
if (func[i] == 0xCC) exit(0); // breakpoint detectado
}
Hardware breakpoints (debug registers):
// Leer debug registers via GetThreadContext
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
if (ctx.Dr0 || ctx.Dr1 || ctx.Dr2 || ctx.Dr3) {
exit(0); // hardware breakpoints activos
}
Contramedida:
- Para software BPs: usar hardware breakpoints en su lugar.
- Para hardware BPs: hookear GetThreadContext para limpiar los debug registers.
Exception-based anti-debug
El malware genera una excepcion intencionalmente. Si hay un debugger, este la captura. Si no hay debugger, el handler de excepciones del malware se ejecuta normalmente:
__try {
__asm { int 0x2D } // Genera excepcion
// Si llegamos aqui, hay debugger (absorbi la excepcion)
exit(0);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
// Normal: sin debugger, la excepcion fue manejada aqui
continue_execution();
}
Contramedida: Configurar el debugger para pasar la excepcion al programa. En x64dbg: Options, Exceptions, pasar INT 2D al programa.
Tecnicas Anti-VM
CPUID: Hypervisor Brand String
La instruccion CPUID con EAX=1 retorna feature flags. El bit 31 de ECX indica si se ejecuta en un hypervisor:
; Verificar hypervisor bit
mov eax, 1
cpuid
bt ecx, 31 ; Hypervisor present bit
jc vm_detected
; Obtener brand string del hypervisor (EAX=0x40000000)
mov eax, 0x40000000
cpuid
; ECX:EDX:EBX contiene el brand string
; "VMwareVMware" = VMware
; "Microsoft Hv" = Hyper-V
; "KVMKVMKVM" = KVM
; " lrXenXenXen" = Xen
; "VBoxVBoxVBox" = VirtualBox
Contramedida: Configurar la VM para ocultar el hypervisor:
# VMware: en .vmx
hypervisor.cpuid.v0 = "FALSE"
cpuid.1.ecx = "---0----:--------:--------:--------"
# VirtualBox:
VBoxManage modifyvm "VM" --paravirtprovider none
VBoxManage setextradata "VM" "VBoxInternal/CPUM/IsaExts/HYPERVISOR" 0
MAC Address
Los adaptadores de red virtuales usan rangos de MAC conocidos:
| Prefijo MAC | Producto |
|---|---|
| 00:0C:29 | VMware |
| 00:50:56 | VMware |
| 08:00:27 | VirtualBox |
| 00:1C:14 | VMware (Player) |
| 00:15:5D | Hyper-V |
| 52:54:00 | QEMU/KVM |
// Verificar MAC address
GetAdaptersInfo(...)
if (mac[0]==0x00 && mac[1]==0x0C && mac[2]==0x29) vm_detected(); // VMware
Contramedida: Cambiar la MAC del adaptador virtual a un rango de hardware real.
Registro de Windows
El malware busca claves de registro especificas de VMs:
| Clave | Producto |
|---|---|
| HKLM\SOFTWARE\VMware, Inc.\VMware Tools | VMware |
| HKLM\SOFTWARE\Oracle\VirtualBox Guest Additions | VirtualBox |
| HKLM\HARDWARE\ACPI\DSDT\VBOX | VirtualBox |
| HKLM\HARDWARE\Description\System "SystemBiosVersion" contiene VBOX | VirtualBox |
| HKLM\HARDWARE\Description\System "SystemBiosVersion" contiene VMWARE | VMware |
Contramedida: Eliminar o renombrar estas claves de registro en la VM de analisis.
Procesos y servicios de VM
| Proceso / Servicio | Producto |
|---|---|
| vmtoolsd.exe | VMware Tools |
| vmwaretray.exe | VMware Tray |
| VBoxService.exe | VirtualBox Guest Additions |
| VBoxTray.exe | VirtualBox Tray |
| xenservice.exe | Xen Guest Agent |
// Buscar procesos de VM
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
Process32First/Next()
if (strcmp(process_name, "vmtoolsd.exe") == 0) vm_detected();
Contramedida: Renombrar los ejecutables de guest additions o no instalarlos (sacrificando funcionalidad).
Hardware fingerprinting
| Verificacion | Indicador de VM |
|---|---|
| Tamano de disco | Menor a 60-80 GB |
| RAM | Menor a 4 GB |
| Numero de CPUs | 1 solo core |
| Resolucion de pantalla | 800x600 o dimensiones inusuales |
| Dispositivos USB | Ninguno conectado |
| Impresoras | Ninguna instalada |
| Archivos recientes | Directorio vacio |
| Programas instalados | Muy pocos programas |
Contramedida: Configurar la VM con recursos realistas: minimo 4 GB RAM, 2 cores, disco de 100+ GB, resolucion 1920x1080, y algunos programas instalados para simular un sistema real.
Tecnicas Anti-Sandbox
Delay execution
La mayoria de sandboxes tienen un timeout (2 a 5 minutos). El malware puede esperar antes de ejecutar su payload:
// Sleep largo
Sleep(300000); // 5 minutos
// Alternativas a Sleep que son mas dificiles de hookear
// 1. Loop de NtDelayExecution
// 2. WaitForSingleObject con timeout
// 3. SetTimer + MsgWaitForMultipleObjects
// 4. Loop computacionalmente intensivo
// Loop de calculo para gastar tiempo sin llamar a Sleep
volatile int x = 0;
for (int i = 0; i < 1000000000; i++) {
x = x + i * 3;
}
Contramedida:
- Hookear Sleep/NtDelayExecution para reducir el delay a 0.
- Las sandboxes modernas (ANY.RUN, Joe Sandbox) parchean Sleep automaticamente.
- Algunos sandboxes aceleran el reloj del sistema.
User interaction checks
El malware verifica que haya actividad de usuario real:
// Verificar movimiento del mouse
POINT p1, p2;
GetCursorPos(&p1);
Sleep(2000);
GetCursorPos(&p2);
if (p1.x == p2.x && p1.y == p2.y) sandbox_detected();
// Verificar historial de navegador
// Un sistema real tiene historial; una sandbox limpia no
// Verificar documentos recientes
// CSIDL_RECENT con archivos = sistema real
// Verificar numero de procesos
// Un sistema real tiene 50+ procesos; una sandbox puede tener pocos
Contramedida: Sandboxes interactivas como ANY.RUN permiten mover el mouse y hacer click manualmente. Algunas sandboxes automatizan la interaccion.
Environment checks
// Verificar nombre del equipo
GetComputerName()
// Sandboxes comunes: "DESKTOP-ABC123", "WIN-SANDBOX", "JOHN-PC"
// Verificar nombre de usuario
GetUserName()
// Sandboxes: "admin", "user", "sandbox", "malware", "analyst"
// Verificar dominio
GetComputerNameEx(ComputerNameDnsDomain)
// Sandboxes: no suelen estar en dominio
// Verificar zona horaria
GetTimeZoneInformation()
// Algunos malware solo se ejecuta en la zona horaria del objetivo
Sandbox-specific artifacts
El malware busca artefactos de sandboxes conocidas:
| Artefacto | Sandbox |
|---|---|
| SbieDll.dll cargada | Sandboxie |
| dbghelp.dll, api_log.dll | CWSandbox |
| pstorec.dll | SunBelt Sandbox |
| vmcheck.dll | Virtual PC |
| cmdvrt32.dll | Comodo Sandbox |
| Proceso cuckoomon.dll | Cuckoo Sandbox |
| C:\analysis | Multiples sandboxes |
ScyllaHide: el bypass universal
ScyllaHide es un plugin para x64dbg (y OllyDbg) que parchea automaticamente la mayoria de tecnicas anti-debug:
Protecciones que bypasea:
- IsDebuggerPresent / CheckRemoteDebuggerPresent
- NtQueryInformationProcess (ProcessDebugPort, DebugObjectHandle, DebugFlags)
- PEB.BeingDebugged, PEB.NtGlobalFlag
- Heap flags (HeapFlags, ForceFlags)
- OutputDebugString
- NtSetInformationThread (HideFromDebugger)
- NtClose (handle tracing)
- RDTSC timing
- GetTickCount
- Hardware breakpoint hiding
- NtYieldExecution
Configuracion en x64dbg: Plugins, ScyllaHide, Options, seleccionar las protecciones a habilitar.
Tabla resumen: tecnicas y contramedidas
| Tecnica | Tipo | Contramedida |
|---|---|---|
| IsDebuggerPresent | Anti-debug | ScyllaHide, parchear PEB |
| NtQueryInformationProcess | Anti-debug | ScyllaHide, hook NtQIP |
| RDTSC timing | Anti-debug | ScyllaHide, parchear RDTSC |
| INT 3 scanning | Anti-debug | Usar hardware breakpoints |
| Debug registers | Anti-debug | Hook GetThreadContext |
| Exception-based | Anti-debug | Pasar excepciones al programa |
| CPUID hypervisor | Anti-VM | Ocultar hypervisor en config VM |
| MAC address | Anti-VM | Cambiar MAC a rango real |
| VM registry keys | Anti-VM | Eliminar claves de VM |
| VM processes | Anti-VM | No instalar guest additions |
| Sleep | Anti-sandbox | Hookear Sleep, acelerar reloj |
| Mouse check | Anti-sandbox | Sandbox interactiva |
| User/Computer name | Anti-sandbox | Nombres realistas |
| Disk/RAM size | Anti-sandbox | Recursos realistas |
Script de deteccion de anti-analisis
import pefile
import sys
ANTI_DEBUG_APIS = [
"IsDebuggerPresent",
"CheckRemoteDebuggerPresent",
"NtQueryInformationProcess",
"OutputDebugStringA",
"OutputDebugStringW",
"GetTickCount",
"QueryPerformanceCounter",
"GetThreadContext",
"SetUnhandledExceptionFilter",
"NtSetInformationThread",
"NtClose",
"CloseHandle",
]
ANTI_VM_APIS = [
"GetAdaptersInfo",
"EnumDeviceDrivers",
"GetDiskFreeSpaceEx",
"GlobalMemoryStatusEx",
"GetSystemInfo",
]
ANTI_SANDBOX_APIS = [
"GetCursorPos",
"GetForegroundWindow",
"Sleep",
"GetComputerName",
"GetUserName",
"GetModuleHandle",
]
def detect_anti_analysis(filepath):
pe = pefile.PE(filepath)
anti_debug = []
anti_vm = []
anti_sandbox = []
if hasattr(pe, "DIRECTORY_ENTRY_IMPORT"):
for entry in pe.DIRECTORY_ENTRY_IMPORT:
for imp in entry.imports:
if imp.name:
name = imp.name.decode()
if name in ANTI_DEBUG_APIS:
anti_debug.append(name)
if name in ANTI_VM_APIS:
anti_vm.append(name)
if name in ANTI_SANDBOX_APIS:
anti_sandbox.append(name)
print("=== Anti-Analysis Detection Report ===")
print("File:", filepath)
print()
if anti_debug:
print("Anti-Debug APIs found:")
for api in anti_debug:
print(" [!] " + api)
if anti_vm:
print("\nAnti-VM APIs found:")
for api in anti_vm:
print(" [!] " + api)
if anti_sandbox:
print("\nAnti-Sandbox APIs found:")
for api in anti_sandbox:
print(" [!] " + api)
total = len(anti_debug) + len(anti_vm) + len(anti_sandbox)
if total == 0:
print("No anti-analysis APIs detected in static imports.")
print("Note: APIs may be resolved dynamically at runtime.")
elif total > 5:
print("\n[!] Alto numero de APIs anti-analisis detectadas")
print(" Probable evasion activa")
if __name__ == "__main__":
detect_anti_analysis(sys.argv[1])
Preparacion del entorno de analisis
Para minimizar la deteccion por parte del malware:
- VM realista: Minimo 4 GB RAM, 2+ cores, disco 100+ GB, resolucion 1920x1080.
- Software instalado: Office, navegador con historial, cliente de email, algunos archivos en Documentos.
- Guest additions: Instalar pero renombrar ejecutables.
- Red: Configurar red NAT o host-only, pero con resolucion DNS funcional.
- Usuario realista: Nombre de usuario y computadora plausibles.
- Plugins anti-anti-debug: ScyllaHide en x64dbg.
- Snapshots: Siempre tener un snapshot limpio antes de ejecutar muestras.
- Aislamiento: La VM de analisis nunca debe tener acceso a la red corporativa.
Conclusion
Las tecnicas anti-analisis son una carrera armamentista: los autores de malware implementan nuevos checks, los analistas desarrollan bypasses, y el ciclo continua. El analista efectivo conoce las tecnicas comunes, mantiene un entorno de analisis configurado para minimizar la deteccion, y usa herramientas como ScyllaHide para parchear automaticamente los checks mas comunes.
La regla practica: si un malware detecta el entorno de analisis y se cierra, no es el fin del analisis. Es una pista: el malware tiene algo que ocultar, y las tecnicas anti-analisis que usa (y como las implementa) son en si mismas indicadores valiosos para la clasificacion y atribucion.
Preguntas frecuentes
Libros recomendados
Artículos relacionados
Packing y Unpacking: UPX, Themida, VMProtect y Tecnicas
Ofuscacion de Codigo: Tecnicas y Estrategias de Deobfuscacion
Import Address Table: APIs Sospechosas y Resolucion Dinamica
Analisis de Shellcode: Emulacion, Decodificacion y Deteccion
Este contenido tiene fines exclusivamente educativos y de investigación en ciberseguridad defensiva. No se proporcionan binarios maliciosos ni payloads ejecutables. El uso indebido de esta información es responsabilidad exclusiva del usuario. Leer disclaimer completo.