Aparte de la conocida seguridad por oscuridad, o la inseguridad de sentirse seguro, podemos hablar de la “seguridad por código binario”, y me explico. Hay muchos proyectos en los que todo es auditable, inspeccionado y revisado… excepto el código binario. Es muy habitual que éste se considere como algo seguro, inescrutable y secreto, ya que se considera que la ingeniería inversa es algo así como una tarea inabordable y de demasiada complejidad para ser llevada a cabo, si acaso por superexpertos.
Como ejemplo de este tipo, hoy en día existen numerosos binarios cerrados por diversas razones como DRM, licencias de producto, aplicaciones cliente servidor, o programación cerrada en sí misma. Ni que decir tiene que la mayor parte de ellos no son a día de hoy tan cerrados como a las empresas responsables de su desarrollo les gustaría. Para desmontar esa creencia de que la ingeniería inversa es para superdotados, en este post me gustaría repasar algunas de la herramientas existentes que permiten a no tan expertos el averiguar qué y cómo lo hacen los programas cerrados.
Primero de todo, no hay que olvidar que los binarios de hoy en día no son como los de antes; la complejidad del software es tal que siempre están compuestos por varios módulos y librerías dinámicas reutilizables, que son llamadas y compartidas por varios binarios a través de sus nombres de función. De esta manera es bastante sencillo identificar el funcionamiento de un binario a “alto nivel” y hacerse con la idea del programa grosso modo, sin que sea necesario analizar con detalle las instrucciones de ensamblador.
Una vez entrados en materia, pasemos a ver algunas herramientas que son útiles para averiguar qué esconde un determinado programa. Lo más sencillo que se me ocurre es la herramienta strings disponible en Linux, que como dice su nombre, al aplicarlo sobre un ejecutable muestra las cadenas del binario, en los que a veces nos encontramos con alguna sorpresa (usuarios, rutas, claves, direcciones IP, etc.):
# strings a.out
Si nuestro propósito es ver qué operaciones hace un programa a través de la red, la primera herramienta útil para averiguarlo puede ser un simple sniffer de red, de los que hay infinidad: tcpdump, wireshark, dsniff, etc.
Una herramienta curiosa para Windows que nos permite capturar tráfico cuando hay SSL interceptando la llamadas a la librería es Traceplus / Webdetective, de modo que si el binario intenta conectarse de manera cifrada para que no seamos capaces de ver el tráfico “va apañado”.
Dependiendo del tipo de binario también podemos hacer una traza de las llamadas que realiza al sistema operativo, viendo de esta manera qué fichero abre, que llamadas al sistema realiza, etc. En Linux destaca por su simplicidad strace, truss en Solaris, y Api monitor en Windows. Subiendo un poquito más de complejidad, es posible ver qué llamadas hace el binario a librerías con ltrace en Linux y con Blade Api Monitor en Windows.
En cuando a la inspección de binarios específicos de Windows (.NET y derivados), cabe destacar Reflector, un software gratuito que nos permite ver el código .net (y derivados). Por último, una técnica utilizada para desentrañar los secretos de un binario consiste en modificar y usar solo partes del programa, como llamadas a librerías. Tomando un ejemplo muy simple, si hemos descubierto el nombre de una llamada interesante como ObtainSecret();, una tarea sencilla puede ser generar un programa que realice una llamada a esa librería e imprimir en pantalla el resultado.
Y hasta aquí todas esta herramientas nos permiten ver el funcionamiento del software, sin saber ni una linea de ensamblador, viéndolo todo en alto nivel. Si queremos llegar mas lejos y tenemos conocimientos de ensamblador existen otras herramientas de depuración como IDA debugger, Ollydbg o SoftICE, mucho más especializadas y que probablemente aquellos de nuestros lectores que realicen desarrollo de sistemas o ingeniería inversa de manera habitual conocerán.
Como conclusión, si como parte de un proyecto vamos a proporcionar un binario “cerrado” a los clientes, hay que considerar todas las funciones que contenga como públicas y por lo tanto la fuerza del sistema no debe basarse en su integridad o seguridad, ya que como hemos visto con más o menos esfuerzo los funcionamientos de los binarios puede ser desvelados y modificados (el DRM tiene una curiosa historia de sistemas teóricamente inviolables que han sido rotos en cuestión de horas). Tal como si se tratara de javascript o flash, el hecho de que el códifo esté compilado en un .exe, elf o lo que sea, no lo hace impenetrable.
PD: Me tendrán que disculpar si no he puesto herramientas orientadas a Java, pero no estoy demasiado familiarizado con este lenguaje. No obstante, se trata de un lenguaje de programación complejo de alto nivel donde hay desensambladores, decompiladores, y muchas otras herramientas. Si lo desean, en los comentarios pueden aportar sus sugerencias.
(Imagen de http://www.everyjoe.com/thegadgetblog/files/2009/12/drm-locked-cd.jpg)