¿Cuáles son algunos trucos poco comunes utilizados para proteger el software de la ingeniería inversa? Específicamente, trucos que son sutiles o vengativos.

Bueno, use instrucciones no documentadas para su procesador. Tanto AMD como INTEL mantienen ciertas instrucciones fuera del alcance de su manual de desarrolladores (por qué continúan haciéndolo está más allá de mis esperanzas de entenderlo). Por lo tanto, si ejecutó un programa escrito con tales instrucciones, se ejecutará en una CPU en particular, pero una vez que intente desarmar dicho programa por parte de autores de diseminadores que no hayan tenido conocimiento de estas instrucciones (se atreven a asumir que los manuales de los desarrolladores contienen declaraciones verdaderas o que El manual contiene documentación completa de lo que importa, lamentablemente ese no es el caso) ¡Entonces te sorprenderás!

mientras que la biblioteca de desmontaje de Intel conoce muy bien estas instrucciones. Esta técnica todavía está siendo implementada por arquitectos e ingenieros de virus. Primero observado en Backdoor: Win32 / Farfli.AV

Otra técnica bastante funky, un error del kernel de Microsoft Windows, se conoce como la técnica de volteo de la puerta del cielo.

Un cambio de contexto que tiene lugar antes de la invocación de las funciones del modo kernel en procesos de 32 bits que se ejecutan en un kernel de Windows de 64 bits. Los procesos diseñados y compilados para ejecutarse en un entorno de 32 bits se cargan dentro del subsistema Windows-on-Windows64 (WoW64) y se les asignan hilos que se ejecutan en modo de compatibilidad IA-32e (modo de 32 bits). Cuando se realiza una solicitud de kernel a través de las bibliotecas estándar de WoW64, en algún momento, el subproceso cambia al modo de 64 bits, la solicitud se ejecuta, el subproceso vuelve al modo de compatibilidad y la ejecución se devuelve al llamante. El cambio del modo de compatibilidad de 32 bits al modo de 64 bits se realiza a través de un segmento específico denominado Puerta del Cielo. Todos los subprocesos que se ejecutan en el entorno WoW64 pueden ejecutar una LLAMADA LEJOS a través de este segmento y cambiar al modo de 64 bits.

La última vez que verifiqué un buen número de desensambladores conocidos y usados, tuve problemas para lidiar con esto, algunos incluso se estrellaron.

Pero dada la naturaleza bastante impredecible y esotérica de estas técnicas, la mayoría de los perdedores en el sector comercial simplemente implementarán una combinación de packer + protector + controlador del sistema para proteger sus aplicaciones.

Por lo tanto, no es tan raro ver que antes de que la aplicación se ejecute, pasa por una máquina virtual masiva de más de 20 mb que desempaqueta lentamente la aplicación mientras carga el controlador del sistema que luego mantendrá el uso correcto de la aplicación y manejará todo el uso potencial de los depuradores.

El controlador del sistema monitorea directamente las rutinas del kernel de Windows para la lectura / escritura de la memoria, los dispositivos cargados en la memoria, la ejecución de procesos y subprocesos y alguna información relativamente inútil (algunos de estos controladores también monitorean la entrada de los usuarios). Si intenta simplemente deshabilitar dicho controlador, no tendrá suerte, ya que implementa una generación única de latidos que se envía de vuelta al servidor de la aplicación (si está en relación servidor-cliente), por lo tanto, si desea deshabilitar este controlador, tiene que emular la generación de latidos en su máquina. Pero una vez que lo logras, eventualmente la aplicación (si requiere ser ejecutable en tu hardware) eventualmente se descomprimirá y podrás comenzar a retroceder libremente ¡yay! Por lo general, estas técnicas extremadamente aburridas son implementadas por Vac, Gameguard, PunkBuster y otros.

Todo es bastante controvertido porque, desde el punto de vista meramente técnico, estas rutinas / métodos implementados son completamente idénticos a los que ya se ven en lo que llamamos malware ilegal, spyware y, sin embargo, Microsoft no tiene problemas para certificar los controladores de dispositivos que utilizan estos métodos para el llamado bien mayor ( protección de aplicaciones). Un acto de hipocresía en su mejor momento.

También otra cosa es que, dado que estos controladores se están volviendo bastante potentes, no es muy difícil modificar sus rutinas, por lo que prefieren rastrear la entrada de los usuarios cuando usan navegadores web y escanear todas las pulsaciones de teclas (sí, vamos a obtener algunas tarjetas de crédito o inicios de sesión en cuentas bancarias).

Cuando estaba desarrollando juegos, creamos un software anti hacking en el código que era un NT Kernel Driver. Esencialmente, lo que hizo el controlador fue conectar la SSDT (Tabla de Despacho de Servicio del Sistema) y redirigió muchas llamadas del sistema, incluyendo:

  • ZwWriteProcessMemory: esto fue utilizado por herramientas como Cheat Engine para modificar la memoria del proceso, el controlador lo deshabilitaría para que no funcione en un proceso de destino
  • ZwReadProcessMemory: también utilizado por Cheat Engine para escanear direcciones de memoria, el controlador llenaría la memoria devuelta con datos aleatorios
  • ZwOpenProcess: utilizado para obtener un identificador de un proceso, el enlace devolvería un identificador no válido si se llamara a la función con permisos de depuración
  • ZwCreateRemoteThread: utilizado por los inyectores DLL para inyectar código ejecutable en un proceso de destino. El controlador haría que la función fallara silenciosamente
  • ZwProtectVirtualMemory: utilizado por herramientas de pirateo para hacer que la memoria sea ejecutable. El controlador haría que la función fallara silenciosamente

Entre estas cosas, el controlador también desvincularía el objeto del núcleo PEPROCESS que representa el proceso de destino de la lista de procesos activos. Esto esencialmente ocultaría el proceso del administrador de tareas, haciendo que sea mucho más difícil adjuntarlo al proceso.

Además de eso, el controlador también trampolinó algunas funciones internas del kernel, incluidas las que Cheat Engine usaría para omitir funciones como ZwOpenProcess y ZwWriteProcessMemory usando su propia mensajería IOCTL. Estas incluían las siguientes funciones:

  • KeAttachProcess: utilizado por la rutina OpenProcess personalizada de Cheat Engine
  • KeStackAttachProcess: también utilizado por el controlador personalizado de Cheat Engine

Finalmente, el controlador también conectó algunas funciones de modo de usuario para evitar la macro, incluidas, entre otras, las siguientes:

  • SendInput: se usa para crear bots de juegos
  • SendKey: también se usa para crear bots de juegos
  • FindWindow: enganchado para evitar que los programas de bot encuentren el juego

En general, esto fue excesivo y la mayor lección aprendida fue: no intente evitar la ingeniería inversa del código que se ejecuta en una máquina cliente, si las personas se esfuerzan lo suficiente, lo romperán 🙂

En los días de MSDOS hubo algunos trucos que encontré divertidos:

  1. Localice el SP (puntero de pila) en alguna área de código.
    ¿Por qué esta fue una buena protección? Los depuradores trabajaron colocando un código de operación especial ‘CC’ antes de la línea de código que deseaba detener. (Esta es una versión corta para INT 3.) o establecer una bandera especial, que invoca INT 1 después de cada instrucción. La llamada de interrupción requería empujar el puntero de retorno a la pila, y el depurador necesitaba guardar nuevamente todos los registros de uso de la pila. Con todo, si entraste en la trampa, entonces arruinaste una parte del código. Por supuesto, si notaste la trampa, entonces podrías saltar sobre ella.
  2. Anular INT1 / 3
    Como se señaló anteriormente, INT 1 e INT 3 son cruciales para la operación del depurador. Puede anularlos para proporcionar ayudantes u otras herramientas durante la ejecución. Combinado con el n. ° 1, este es un desperdicio de tiempo real para el hacker.
    Por supuesto, puede cambiar todas las llamadas INT 1/3 con un controlador de interrupción diferente y parchear el binario, pero requiere esfuerzo. Además, si se utiliza la versión abreviada (“CC”), necesita encontrar ingeniosamente espacio para el byte adicional para la llamada de interrupción alternativa. (La llamada de interrupción requiere dos bytes)
  3. Ocultar instrucciones
    Esto fue muy astuto: aprovechó el hecho de que los depuradores muestran los códigos OP línea por línea suponiendo que no hay datos adicionales entre ellos. Pero qué sucede si coloca uno o dos bytes, que es un prefijo para una gran instrucción, por ejemplo. MOV [eax + disp32], imm32. Esta instrucción necesita dos direcciones cada 4 bytes. Dichas instrucciones son bastante comunes, ya que son los ayudantes para el acceso a la estructura. Se ven inocentes. Ahora todo está configurado, todo lo que necesita hacer es saltar “a” la instrucción:

    Esto es lo que ve en su pantalla: (desplazamiento: instrucción; el desplazamiento puede ser algo arbitrario como C356)

    00: jmp 0 + 3
    02: mov [eax + 20CD2346], 56345673
    11: siguiente instrucción

    En su lugar, se ejecutaron las siguientes instrucciones:
    1) 00: jmp 3
    2) 03: instrucciones malvadas … como configurar SP (ver # 1 o anular int1 / 3 # 2)

    Los depuradores estaban tan confundidos por esto normalmente, que no reinterpretaron las instrucciones que mostraban la verdadera instrucción oculta al entrar “en”.

  4. Cifrado
    La mayoría de los trucos anteriores se podían ver desde afuera mirando el binario. Este último ingrediente elaborado es muy difícil de analizar: el código real podría estar encriptado. A veces con algo fácil como un simple XOR o ROR (rotar bits en bytes) el valor o más complicado como RC4. Además, el código podría reubicarse en otro lugar del espacio de direcciones, por lo que es difícil adivinar qué byte es el verdadero origen del código que desea modificar.

Había una herramienta llamada hackstop o algo así. Empleó todos estos trucos: encriptar el binario usando varias etapas. Todas las capas barajaron el contenido y lo cifraron nuevamente. Era prácticamente imposible descifrar un binario protegido con esa herramienta. (Aunque no es imposible, solo necesita tiempo y crear herramientas especiales)

Hechos graciosos:

  1. ¡También he visto el # 3 utilizado por los ofuscadores de Java!
  2. Algunas DLL de Windows también emplearon cifrado, descifraron la funcionalidad exportada en la llamada init (). Pasaron de moda debido a entornos de ejecución más seguros, lo que impide reescribir su código.
  3. He usado algunos de estos métodos para proteger mi trabajo en el pasado. También he escrito un encriptador (pero con una sola capa).

Hace años, en MSDOS, era responsable de mantener un controlador de gráficos para la versión MSDOS de AutoCAD. Hubiera sido realmente agradable poder ejecutar AutoCAD bajo un depurador, principalmente para depurar mi propio controlador de gráficos, no para depurar AutoCAD en sí.

Sin embargo, esa versión de AutoCAD se hizo cargo de los vectores de trampa de paso único y punto de interrupción (INT 1 e INT 3 en el x86) y los utilizó internamente para fines que no tenían nada que ver con el propósito original de esas trampas de depuración. (No recuerdo los detalles exactos, pero estoy bastante seguro de que redirigieron una de esas trampas a INT 21H, la trampa syscall de MSDOS, y luego la usaron para todas las interacciones del sistema operativo). Si activó AutoCAD bajo un depurador y acaba de ejecutarlo, el depurador se “desconectaría” si AutoCAD se hiciera cargo de esas trampas. Si usó el depurador para evitar que AutoCAD se haga cargo de esas trampas, como al colocar instrucciones NOP sobre las escrituras de memoria, podría continuar ejecutándose y acceder al depurador, pero AutoCAD en sí no funcionaría.

Ese fue un truco poco común, y la única vez que personalmente encontré esa técnica. Me frustraba muchísimo, y ni siquiera estaba tratando de realizar ingeniería inversa en AutoCAD, solo estaba tratando de depurar mi propio controlador de gráficos. Resultó que nunca pude “depurar en vivo” el controlador de gráficos, tuve que confiar en cosas como el estado de registro en un búfer de memoria y luego volcar el búfer de memoria después de salir de AutoCAD.

Cuando era niño (como alrededor de 14-15), siempre jugaba tratando de proteger el software que hice internamente para las empresas, incluso cuando una amenaza para el software en el sector privado no es una locura.

Pasaría más tiempo protegiendo sus llamadas y acceso a la API que en las pruebas de penetración. Consideraba que mi programa o suite de software era un gran edificio alto sin entrada ni salida, excepto por una sola carretera que controlaba.

La parte triste es que todos los edificios construidos desde cero pueden excavarse desde el subsuelo.

Uno de los intentos más extravagantes para proteger mi software fue hacer algo que llamé el enlace de 3 bits. Una vez que el programa y los módulos necesarios se cargan en la RAM, aislaría una parte separada del programa para piratear / inyectar en el proceso principal. Como si fuera un depurador o un hack externo. Luego cargaría un proceso completamente diferente justo al lado y haría que el depurador falso se inyectara en el nuevo proceso.

Desde el punto de vista de la ingeniería inversa, esto es inútil, ya que podría pasar por todo y darse cuenta de que simplemente puede hackear solo el proceso que necesita. A-ha, pero ¿cuál? Uno se parecía al programa, y ​​el otro parecía igualmente idéntico. Tal vez la pista está en donde estaba apuntando el depurador falso. O no

En última instancia, no puede usar un “truco” per se para proteger el software, ya que no existe tal cosa como “protección” de software por definición. El cifrado de tiempo de ejecución y el bloqueo de ID de hardware son lo más cercano que realmente podría llegar sin ser demasiado tedioso para ser honesto. Definitivamente es un viaje divertido tratar de proteger su propio programa o API, ya que tendría que ser paranoico y actuar como TODOS los piratas informáticos en lugar de solo una mentalidad.

O simplemente escapar de las comas.

La ofuscación de código es una forma de arte. Los programas de depuración utilizan principalmente las mismas características básicas de búsqueda de cadenas y asignación de órdenes de ejecución. La contramedida efectiva generalmente se enfoca en estos elementos:

  • Valores incrustados / cadenas fijas: nunca almacene información en texto sin formato. Los valores deben ser hash o encriptados si es posible.
  • Convenciones de nomenclatura: los nombres de funciones y variables deben oscurecerse con valores aleatorios que no se correlacionan con su uso.
  • Obscurecer el flujo de ejecución: los depuradores intentan reconstruir el orden de ejecución del código. Ejemplo: podrían buscar instrucciones JMP. Puede confundirlos en algunos casos usando equivalentes JZ y JNZ.
  • Comandos oscuros: en algunos casos, puede sustituir los comandos de ensamblaje con sus equivalentes de código de bits.
  • Código extraño: inserte código adicional (básicamente NOP) a través del cual JMP a veces, pero que en realidad no ejecuta.
  • Volteo del espacio de direcciones: algunos depuradores se confundirán al saltar entre espacios de nombres de 32 y 64 bits. Esto es menos común hoy, pero sigue siendo un buen truco.
  • Atacar el software de depuración: en algunos casos es posible incluir código que bloqueará la aplicación de depuración. Estos problemas tienden a resolverse rápidamente, por lo que no confiaría en este.

Uno de los enfoques de código más creativo que encontré fue en los módulos de licencia para un subsistema ERP. La validación de un archivo de claves de licencia binarias implicaba llamar de forma recursiva a un ejecutable y pasar la salida a la siguiente iteración. Cada pase leyó el archivo de clave en la memoria, descifró parte del contenido e intentó “validar” la licencia. Los primeros dos pases fallarían y solo devolverían lo que parecía basura picada. La tercera iteración produjo una picadura de valores que correspondería a los productos con licencia y activaría los módulos. No había ninguna indicación en el ejecutable de los requisitos para múltiples llamadas. Fue simple, confuso y efectivo.

De vuelta en las computadoras Acorn (alrededor de 1994) cuando estaba en la escuela (año 8–9) escribía ‘juegos’ para mis amigos en BASIC y los vendía por 50p. La gente pronto se dio cuenta de cómo obtener el código y ‘piratearlo’ simplemente abriendo el archivo de código en lugar de ejecutar el contenedor de la aplicación (por ejemplo, para descubrir cómo ganar la carrera de caracoles o el administrador de footies, vital cuando tienes 13 años) antiguo).

Así que comencé a ocultar el código renombrando la extensión del archivo como ‘código de máquina’ (no recuerdo cuál era la extensión Acorn para eso) en lugar de ‘.bas’ (para BASIC). Luego, cuando hice el contenedor de la aplicación, simplemente le dije que cambiara el nombre del archivo a ‘.bas’ antes de ejecutarlo, y luego cambia el nombre a ‘código de máquina’ al salir. Confundí a mis primitivos hackers / ingenieros inversos …

Mi protección de copia se limitaba a mostrar el nombre del usuario en letras grandes en la pantalla en todo momento, así que caminaba por la sala de computadoras y verificaba si alguien estaba jugando una versión ‘copiada’ que no era suya …

Ah, días felices, la vida era más simple entonces.

Creo que no hay ningún truco del que los hackers no estén al tanto, pero aquí hay algunos que no enumeró:

  • Trampa del tiempo. El más simple por bastante eficiente: mide el tiempo de ejecución de la función y sale si excede algún umbral. Esto evita que el depurador avance
  • Máquina virtual. Implementa la traducción binaria de las instrucciones de la CPU a sus propias instrucciones de VM. Cuando su programa carga VM se inicia y ejecuta este código. Por lo general, este enfoque se usa para algunas piezas de código pequeñas (como la verificación de licencias :), debido a razones de rendimiento. Bastante difícil de depurar, pero la implementación también es complicada.
  • Código de máquina enlodamiento. Usted toma alguna función (como verificar la licencia 🙂 e inyecta una gran cantidad de código de máquina sin sentido, que no rompe las cosas sino que complica la depuración
  • Controlador del kernel, que engancha la API de depuración del sistema operativo

Los productos comerciales pueden combinar algunos o todos estos métodos. Ver

StarForce – Wikipedia

En los días en que escribí shareware para MS-DOS, desarrollé lo que pensé que era un sistema de protección bastante bueno. Cuando el shareware no estaba registrado, tenía ciertas características, como GUARDAR, deshabilitado, y para evitar que se rompiera y se volviera a encender, cifré esa parte del código, 256 palabras. No era lo que podríamos llamar cifrado seguro: acabo de enviar un código XOR con la salida de un generador de números aleatorios. El objetivo era evitar que cualquiera que tuviera una copia comprada la piratee y lance una versión descifrada.

  • La semilla del RNG fue el CRC de la sección del código de descifrado. Cualquier intento de parchear esa parte del código cambiaría el CRC y el descifrado fallaría.
  • Inhabilité el teclado y la pantalla de impresión se interrumpe solo para hacer la vida más difícil. El código para hacer esto era parte de la sección CRCd para generar la clave, por lo que no se pudo eliminar.
  • El número aleatorio se almacenó en el vector INT 03. Ese es el vector utilizado por los depuradores para darles el control en un punto de interrupción, e hizo imposible enganchar la interrupción.
  • Todavía podrían seguir un solo paso utilizando DEBUG de Microsoft, si de alguna manera lograron volver a habilitar el teclado. Lo hice difícil construyendo la clave CRC en el vector de interrupción de un solo paso. Luego comencé el descifrado al final de la sección cifrada y lo ejecuté 256 veces, retrocediendo una palabra en cada pasada, de modo que la primera palabra después de la rutina de descifrado fue la última palabra descifrada. Eso detuvo a cualquiera que pusiera un punto de interrupción allí, porque habría sido XORed 256 veces antes de ejecutarse. Pensé que si alguien tenía la paciencia para dar un solo paso, a mano, a través de 256 pases de la rutina de descifrado, tenía paciencia sobrehumana.
  • Cuando se completó el descifrado, la parte descifrada del programa examinó un archivo clave que envié a los usuarios registrados, para asegurarse de que no hubiera sido manipulado. Si todo estaba bien, habilité todas las funciones.
  • Al final, si el descifrado y la verificación de clave fueron exitosos o no, borré toda la sección de código encriptado antes de volver a habilitar el teclado, para vencer los volcados de memoria.

Aquí hay parte del código, en caso de que a alguien le importe. Los lectores astutos pueden notar que el RNG es el mismo algoritmo que el CRC, ya que un registro de desplazamiento RNG es solo un generador CRC alimentado con una cadena infinita de 1.

; Aquí, la rutina de descifrado. Cualquier falla, alguna en absoluto, aborta
; el descifrado y destruye toda la sección, por lo que un deslizamiento
; del depurador y el hacker tiene que salir y comenzar de nuevo.
; ds y es DEBEN apuntar a cseg. DEBE haber una devolución inmediata
; antes de esta rutina
; Y … absolutamente _NO! _ Etiquetas entre corchetes !!!!! Porque
; el cargador EXE “los repara” con compensaciones de segmento al cargar
; y atornilla totalmente el sistema CRC

asumir ds: cseg

ret; Debe estar presente
descifrado:
en todo, 21h; bloquear el teclado
o al, 2; con el bit de hardware
fuera 21h, al; así que no funcionan las llaves
dc_debug:
mov si, offset cs: descifrado
mov di, cs offset: destruir + 7
mov bx, offset cs: continuar
mov cx, offset cs: destruir
xor hacha, hacha; cero para DS más tarde
sub di, si
mov dx, si
dec dx
empujar cs; es
empujar cs; ds
empujar si; empuje futuro DI
push di; empuje futuro CX
empujar dx; empujar el futuro SI

empujar cx; empujar la dirección de “llamada”
empujar bx; empuje continuar dirección
hacha de empuje; empuje futuro DS

mov si, offset cs: copyright_1
mov cx, security-copyright_1; longitud
mov bx, 4; dirección del vector de un solo paso
agregue cx, bx; incluirlo en este
mov di, bx
pop ds; ds es cero ahora! es es cseg
xor edx, edx; cero
dec edx; -1 semilla
xchg edx, [bx]; construir en el vector de un solo paso
llamar a crc1; tiene anulaciones de cseg explícitas
mov axe, [bx]; comprobar resultado, debe ser cero
mov cx, [bx] .2
o hacha, cx
jz short @F
hacha pop; descartar continuar
mov [bx], edx; reemplazar el vector
@@: ret; continuar o destruir

; no hay camino de ejecución hasta aquí
Hacer continuación:
mov di, offset cs: save1
mov dword ptr es: [di], edx; guardar vector Int 1
agregar di, guardar2-guardar1
mov edx, [bx] .8
mov dword ptr es: [di], edx; guardar vector Int 3
agregar di, guardar3-guardar2
mov edx, [bx] .16
mov dword ptr es: [di], edx; guardar vector Int 5
hacha pop; eliminar la salida anterior
; calcular un CRC diferente, que es nuestra clave de descifrado
mov axe, offset cs: rest_dest
mov [bx] .8, hacha; guardar clave inicial
mov [bx] .10, hacha; en vector de depuración
hacha de empuje; establecer una nueva salida
mov si, offset cs: descifrado
mov di, bx
agregar di, 8
mov cx, crypt_start-decrypto
mov axe, offset cs: key_made
hacha de empuje
; CRC un área de código (siempre en cseg)
; inicio del área en si, número de bytes en cx, CRC en ds: [di]
crc1: empujar cx
mov cx, 8
mov al, cs: [si]
inc si
crc2: clc; llevar claro
mov ah, al
xor ah, [di] .3
jns crc3; si el bit 7 está claro
xor byte ptr [di], 01010111b
stc
crc3: rcl dword ptr [di], 1
shl al, 1
dec cx
jnz crc2
pop cx
dec cx
jnz crc1
jubilado
; DS sigue siendo 0, ES sigue siendo CS
key_made:
mov di, offset cs: crypt_end
mov cx, di
sub cx, offset cs: crypt_start
mov si, 12; señalar al vector 3
std
dec di
dc0: push cx
empujar di
dc1: mov al, es: [di]
xor al, [si]
stosb; di de dec
clc
prueba byte ptr [si] .3,80h; Registro de desplazamiento de 32 bits RNG
jz short @F; número aleatorio en vector de depuración
xor byte ptr [si], 01010111b
stc
@@: rcl dword ptr [si], 1
dec cx
jnz dc1
di di
pop cx
dec di
dec cx
jnz dc0
; ir directamente a la sección encriptada
crypt_start:
read_key_file:

Cuando solía trabajar en software malicioso sofisticado en Microsoft, los servidores de software malicioso se negaban a enviar software malicioso a nuestro rango de direcciones IP. Escuchamos informes similares de otros proveedores de software y compañías de antivirus. Eso fue bastante fácil de evitar al descargarlo desde una conexión a Internet del consumidor.

Ahora, los atacantes asignan identificadores únicos o semi-únicos a cada objetivo individual, generalmente como parte de la URL de phishing, y enviarán el malware solo una o dos veces a ese objetivo. También usarán otros indicadores como la dirección IP, el agente de usuario y la zona horaria para determinar si la computadora que se conecta a su servidor es realmente el objetivo previsto y, si no, sirve una página benigna.

Algunos programas de análisis de malware afirman que existe cualquier URL en Internet, para capturar lo que el malware quiera enviar allí. El malware WannaCry intentó conectarse a un sitio inexistente como una forma de verificar si estaba siendo analizado por dicho software y se cerró si es así. Afortunadamente para nosotros, los autores olvidaron comprar ese sitio, por lo que alguien más lo hizo, y una vez que el sitio estuvo en línea, todas las instancias reales del malware pensaron que estaban en una herramienta de análisis y salieron.

Otra forma inteligente de ocultarse es aleatorizando cuándo se realiza el ataque: un atacante que sirve malware a través de una red publicitaria (especialmente) o un sitio web comprometido podría servir el malware solo a uno de cada mil visitantes. Es muy poco probable que cualquiera que intente analizar el ataque reciba el malware, pero el atacante aún compromete a suficientes víctimas para lograr su objetivo.

En realidad, es más simple realizar ingeniería inversa en base al análisis funcional en lugar de tratar de descifrar el código de alguien. Sin embargo, una técnica que utilicé que parece funcionar bastante bien es la siguiente combinación:

  1. Emplear punteros de función de lógica de teclas
  2. Use una tabla hash y un algoritmo para recuperar las direcciones de las funciones en lugar de “if”, “while”, etc.
  3. Cree funciones “ficticias” con muchos literales de cadena para confundir el esfuerzo de piratería
  4. Tira el binario para que todos los símbolos se hayan ido

Nada es infalible, pero incluso un desarrollador experimentado que use un depurador o un generador de perfiles tendrá dificultades para descubrir qué está sucediendo.

Una vez vi un proyecto escrito en lenguaje ensamblador COSMAC 1802 porque el procesador 1802 no estaba en uso general y porque podía saltar indirectamente a través de cualquiera de los 16 registros. Fue una instrucción bastante confusa establecida para el ensamblaje inverso.

La implementación de un procesador totalmente personalizado también es una herramienta bastante buena para evitar la ingeniería inversa. Trabajé para una compañía que construyó sus propios procesadores, aunque fue principalmente por diferentes razones que evitar la ingeniería inversa.

Cualquier hardware patentado totalmente personalizado en su sistema prácticamente evita la clonación, aunque no la ingeniería inversa per se . Lo sé, porque tuve que realizar ingeniería inversa en el hardware personalizado de mi empresa. [palma de la cara táctica]

Un competidor una vez robó el código basado en Z80 de nuestro producto y lo ejecutó en su dispositivo basado en 68000, bajo un emulador Z80. Estoy bastante seguro de que el propósito era ocultarnos su robo, pero lo descubrimos de inmediato.

Algunos virus excluyen su código con un valor constante y deshacen esta codificación justo antes de la ejecución para evitar que los investigadores de virus observen el código. Tiene que haber al menos una pequeña rutina que no esté codificada, para arrancar el resto del virus, por lo que este método no es perfecto.

A veces me sorprende pensar en todas las cosas raras que he tenido que hacer en mi carrera.

En los días en que estaba en el negocio de revertir los juegos de PSP, RidgeRacer usaba un buen truco. La siguiente es una representación de su ciclo principal:

time_t last = time (NULL);

while (verdadero) {

/ * actualiza el juego / render aquí * /

time_t next = time (NULL);
if (siguiente – último> 1) {
salida (0);
}
último = siguiente;
}

Así que, esencialmente, el juego se cierra si detecta un retraso anormal entre fotogramas. Puedes adjuntar un depurador al juego todo lo que quieras, pero tan pronto como rompas, el juego se cerrará cuando se reanude. Me tomó un tiempo encontrar esto.

La solución fue reemplazar la llamada de time con j al final del ciclo

He usado muchos de los trucos descritos en otras respuestas en mi programación.

Mi truco favorito es usar nombres de funciones de 1 o 2 letras y sus nombres de parámetros (si están visibles en el idioma) y reutilizar las variables varias veces. ¡Me dificulta incluso seguir mirando el código fuente sin comentarios explicativos! Tengo programas de edición que eliminan todos los comentarios de todo el código para aquellos idiomas donde se requiere la fuente para ejecutarse.

El otro que solía usar es construir dinámicamente una estructura de datos con el código de varios lugares (a menudo una cadena cifrada que se descifró), y luego ejecutar el código, y luego borrar inmediatamente la estructura de datos.

¿Fue esto un truco o mi imaginación?
En los primeros días de Home Computer, alrededor de 1979, tenía una computadora S-100 (similar a MITS Altair) con CP / M. Compré un software de juego que jugaba backgammon, pero su mapeo de pantalla del espacio de video era un poco diferente de lo que tenía en mi computadora. Pero me dijeron que podía modificarlo simplemente parcheando algunas ubicaciones en el código, y me dieron una hoja técnica que me mostraba qué bytes parchear. Después de hacerlo, parecía funcionar, así que jugué al backgammon.

Entonces comencé a notar algo. Cuando jugaba, parecía que el 90% del tiempo tiraba los dados y obtenía los valores exactos que necesitaba para avanzar y ganar el juego.

Ahora no sé si fue trampa, pero ciertamente se sintió de esa manera.

Entonces, al menos para los juegos, se me ocurrió la idea de programar un juego de tal manera que si detectara una alteración en el código o un uso sin licencia, cambiara su comportamiento.

Si hubiera dejado de funcionar, probablemente habría depurado el código y descubierto cuál era el problema. Pero si simplemente se estrellara mucho, probablemente me habría quejado en los foros y perderían ventas. Del mismo modo, si ganara cada vez, también me quejaría.

Pero, ¿qué pasaría si fuera astuto e inclinara un poco las probabilidades a su favor para que me desanime, pero no piense que fue una trampa? Entonces pensaría que era un mal jugador y me quedaría sin disfrute.

¡Y habrían frustrado efectivamente mi uso de él!

Vengar es una mala idea.

Oh, esa es otra buena manera de evitar la ingeniería inversa. Escriba el programa en adelante, luego decapítelo. Buena suerte en descubrir qué está haciendo qué. Solo un montón de direcciones. (R-Base tenía el hecho de que estaba escrito en Forth en el tiempo de ejecución).

Nunca me preocupé por la ingeniería inversa, porque en el ensamblaje puedes hacer todo tipo de cosas para evitarlo, como llamar o saltar a un operando. Hicimos eso para ocupar menos espacio de almacenamiento para el programa (cuando un disquete de 768k se considera enorme, calzas cada byte), pero ofuscó el programa lo suficiente como para volver locos a los desarrolladores experimentados. “¡No puedes hacer eso!” Sí, puedes.

Incluso en idiomas de alto nivel, puede incrustar, carácter por carácter, en diferentes lugares, su fecha de nacimiento, luego realizar algunas operaciones aritméticas y usarlo para algo crítico. Está utilizando constantes totalmente indocumentadas, por lo que es difícil darse cuenta de lo que está sucediendo, pero si llega a una demanda por infracción de derechos de autor, “¿Por qué tiene mi fecha de nacimiento codificada en lo que supuestamente es su programa?”. Hágalo con los valores ASCII de su nombre y es aún más difícil para alguien afirmar que lo escribió.

Quiero contarte un pequeño secreto: a nadie le importa tu código. O, más precisamente, a nadie le importa su código, a menos que haya escrito un proyecto popular de código abierto (donde el punto es que otras personas pueden leerlo, aprender de él y actualizarlo). He bromeado diciendo que si alguien quiere robar mi código, agradecería que envíe una versión depurada y mejor comentada.

A finales de los ochenta o principios de los noventa había un buen truco.

Por – uh – “alguna” razón hubo un código de operación completamente indocumentado en algunos modelos de procesadores Intel. A primera vista, parecía un salto condicional bastante normal, pero el chili agregado fue que después de eso, en el objetivo del salto, el contador del programa disminuyó en lugar de incrementarse .

Entonces, esencialmente podría escribir su algoritmo secreto hacia atrás y nadie que lea su código ni siquiera lo adivinaría. Todo lo que verían sería código que aparentemente no se ejecuta en absoluto, en este caso las líneas 5 a 8:

Opcode1
Opcode2
Opcode3
JumpToLine8AndReverse
JumpToLine9AndReverse (ahora de vuelta al orden normal de las cosas)
Opcode6
Opcode5
Opcode4
Opcode8
Opcode9
Opcode10

Realmente me encantó ese.

El viejo truco que utilicé solo es posible en un ensamblaje, pero puede ser una inserción corta en proyectos de idiomas de nivel superior.

Se basa en un mecanismo de almacenamiento en caché. El código se almacena en caché y se ejecuta desde la memoria caché.

Cuando se rastrea el programa en el depurador, el código se toma de la memoria, no de la memoria caché. Si cambia el código solo unas pocas instrucciones después del puntero de instrucción actual, el depurador ejecutará el nuevo código, mientras que el programa que se ejecuta a toda velocidad ejecutará el código antiguo. De esta manera, puede engañar al depurador y redirigirlo a un código complicado y recursivo preparado como una trampa sin salida.

En 68K días, las funciones de la caja de herramientas del sistema se enviaron a través de una instrucción ilegal que siempre comenzó con una “A” hexadecimal. Estos se llamaron A-Traps. Una de estas trampas A fue la interrupción del depurador. Un reproductor de música popular parcheó el depurador A-Trap con el vector de reinicio para que si intentaba ingresar al depurador reiniciara la máquina. Hicieron el parche varias veces en múltiples lugares y de varias maneras, así que pasé horas y horas rastreando todos los lugares donde copiaban el vector de reinicio al vector de depuración … Hasta que se apagó la luz y copié el vector de depuración al reinicio vector antes de ejecutar el programa. Entonces pude entrar en el depurador y aplicar ingeniería inversa a su código.

Disparar errores en depuradores!

Por ejemplo, esto: un error OllyDbg deshabilita los puntos de interrupción del software, pero debería haber potencial para muchas cosas interesantes.

PD: No estoy seguro acerca de “poco común”, pero es gracioso . Quiero decir, ¿quién depura a los depuradores, verdad? 😀 Es tan meta 😀