viernes, 31 de agosto de 2007

Pseudocompiladores REXX, la instrucción TRACE y la función SOURCELINE

Aunque IBM ha desarrollado compiladores del lenguaje de programación REXX que se han utilizado y se siguen utilizando en entornos de producción, el lenguaje es principalmente conocido por un gran abanico de intérpretes.

El abanico es tan amplio que la lista pasa por los comerciales como uni-REXX, los que forman parte del sistema operativo como el venerable REXX clásico de OS/2, los "shareware" para una plataforma concreta como REXX para Palm OS, los gratuitos que incluyen una herramienta de desarrollo RAD como Reginald o los libres presentes virtualmente en todas las plataformas de software existentes como Regina... Pero ocasionalmente el abanico se queda pequeño porque es necesario generar un ejecutable bien sea porque el programa en concreto lo requiere o porque nos interesa ocultar el código. Y en estos casos lo que necesitamos es lo que personalmente suelo denominar como pseudocompilador.

Un pseudocompilador genera un ejecutable a partir de un script REXX, pero en ningún caso lo hace traduciendo el código REXX a código máquina. Se limita a añadir el código de algún modo a la estructura del ejecutable generado y ese ejecutable se limita a usar las bibliotecas dinámicas de una implementación concreta de intérprete REXX para interpretar el código que lleva adjunto.

Por poner un ejemplo sencillo, partamos de un programa que nos pide una cadena y la devuelve invertida contenido en un fichero "bucle.cmd" para OS/2:


/* bucle.cmd */
signal on halt

do forever
call charout , 'Introduzca una cadena: '
parse pull cadena
say reverse(cadena)
end

exit

halt: exit


Ahora podemos recurrir a uno de los pseudocompiladores REXX disponibles para OS/2 como los de Dennis Bareis (rexx2exe), Veit Kannegieser (REXX_EXE) o Mark Hessling (Rexx/Wrapper) para generar un ejecutable:


[C:\home\salvador] rexx2exe bucle.cmd bucle.exe


Y ya tenemos un ejecutable que está enlazado dinámicamente con la biblioteca dinámica que implementa el intérprete REXX en OS/2 (REXX.DLL). Es decir, el ejecutable depende del intérprete REXX del sistema y utiliza el intérprete para ejecutar el código que lleva dentro.

La mayor parte de los pseudocompiladores permiten encriptar el código o tokenizarlo, lo que representa una oportunidad estupenda de ocultar el código. De hecho, las cuatro herramientas de desarrollo gráfico RAD disponibles para OS/2 (VisPro/REXX, VX-REXX, GPF REXX y DrDialog) pueden considerarse pseudocompiladores puesto que hacen exactamente lo mismo que los mencionados anteriormente y hay cientos de programas populares pseudocompilados para OS/2.

La tokenización, para aquellos de vosotros que hayais llegado hasta aquí que sintais algo de curiosidad es el proceso que realiza el intérprete REXX al pasar del código que escribe el programador a una representación intermedia que es lo que se ejecuta. La tokenización inevitablemente oculta el código original y permite ejecutar el código tokenizado sin problemas. Pero como el proceso de tokenización es tremendamente dependiente del intérprete, al decirle a un pseudocompilador tokenice el código nos encontramos con que el ejecutable generado en principio solamente funcionará con una versión concreta de un intérprete particular.

(Incidentalmente: Regina incluye opciones para generar programas tokenizados -no ejecutables- y ejecutarlos posteriormente y otro tanto podemos decir de IBM Object REXX y Open Object REXX.)

Eso nos deja con la encriptación del código como modo de ocultarlo sin que el ejecutable quede inutilizable por un cambio de intérprete o versión del intérprete y con una impresión de seguridad que echa por tierra una característica del intérprete REXX de OS/2 (y algunos otros): la variable de entorno RXTRACE.


[C:\home\salvador] rexx2exe bucle.cmd bucle.exe
[]-------------------------------------------------------------------------[]
| REXX2EXE version 99.349 (C)opyright Dennis Bareis 1994 |
| http://www.labyrinth.net.au/~dbareis/index.htm (dbareis@labyrinth.net.au) |
[]-------------------------------------------------------------------------[]

CHECKING : bucle.cmd

* Rexx Source length 176 bytes
* Rexx Binary length 176 bytes

* Target EXE length 27,618 bytes
* Target code will not run in OS/2 version 2.x
* Rexx code encrypted in target (Key=0xF772F1AC)
* Code can't be recovered
[C:\home\salvador] SET RXTRACE=ON

[C:\home\salvador] bucle.exe
2 *-* Signal On halt;
+++ Interactive trace. "Trace Off" to end debug, ENTER to Continue.


¿Qué ha sucedido?

La variable de entorno RXTRACE cuando tiene el valor "ON" tiene el efecto de añadir la sentencia TRACE '?R' al comienzo del cualquier programa REXX que ejecutemos en ese entorno. La instrucción TRACE es una de esas joyas del lenguaje REXX: es lo que activa el depurador del lenguaje que forma parte de todo intérprete en tiempo de ejecución.

A partir de este momento podemos inyectar código nuestro en la ejecución del ejecutable encriptado "bucle.exe". Pero puesto que tenemos la función sourceline que permite decirnos el contenido de una línea concreta de un script REXX, podemos inyectar lo siguiente:


do i=1 to sourceline();call lineout "source.cmd",sourceline(i);end;exit


Y ya tenemos todo el código encriptado sacado sin problemas del ejecutable. Cualquiera que hubiese leído esa pequeña joya que es el REXX Tips & Tricks de Bernd Schemmer conocería este detalle y habría experimentado con buenos resultados con ejecutables generados con todos los pseudocompiladores para OS/2 (salvo VX-REXX) y algunos para otras plataformas.

La solución es sencilla: añadir la sentencia "rc=trace('o')" como primera sentencia de nuestro programa para desactivar el rastreo del programa si se activa a través de la variable de entorno RXTRACE.

Desafortunadamente en las herramientas de desarrollo RAD para OS/2 como VisPro/REXX, es difícil determinar cuál es la primera sentencia. De hecho, en este caso particular la primera sentencia NO es responsabilidad del programador, puesto que VisPro/REXX añade un bloque de código sobre el cual no tenemos control salvo que hagamos un parche binario sobre el ejecutable generado o sobre el pseudocompilador (el ejecutable BLDNTEST.EXE). Es una tarea sencilla que se deja como ejercicio al lector.

No hay comentarios: