Documente Academic
Documente Profesional
Documente Cultură
de programas sospechosos
Bartosz Wójcik
A
finales de septiembre del 2004 en la lis- nes podemos decidir si pasar directamente
ta de discusión pl.comp.programming al análisis del código o, si el código ha sido
apareció un mensaje con el tema ¡¡¡EL comprimido, descomprimir primero el conteni-
CRACK UNIVERSAL PARA MKS-VIR!!!! Este do del fichero; analizar el código de un fichero
post contenía un enlace a un archivo crack.zip, comprimido no tiene mucho sentido.
el cual contenía un pequeño fichero ejecutable. La segunda y más importante etapa con-
De lo que decían los usuarios resultaba que el siste en analizar el programa mismo y, en otro
programa no era un crack, y que, además, pro- caso, extraer cualquier código que pueda haber
bablemente contenía código sospechoso. El sido escondido en alguno de los recursos de la
enlace a este mismo fichero se encontró en por aplicación. Esto nos permitirá ver cómo funcio-
lo menos otras cinco listas de discusión (donde na realmente el programa y cuáles son los efec-
ya no se hacía pasar por un crack universal tos de su ejecución. Veremos que este análisis
sino por un crack para robar contraseñas de es necesario para poner en evidencia que el
Gadu-Gadu, el mensajero instantáneo más supuesto crack no es para nada un programa
popular polaco). Nuestra curiosidad nos llevó inofensivo. Si algún día nuestro Lector encuen-
a analizar el fichero en cuestión. tra un fichero tan sospechoso como éste, le
Este tipo de análisis se compone de dos incitamos a realizar un análisis similar.
etapas. Al principio, debemos observar con
Defensa
Reconocimiento rápido
En el archivo descargado crack.zip
se encuentra el fichero patch.exe,
de unos 200 KB. ¡Cuidado!: antes
de proceder a examinarlo, recomen-
damos encarecidamente cambiar la
extensión de este fichero, por ejem-
plo a patch.bin. Esto nos protegerá
contra la ejecución accidental de
un programa desconocido; las con- Figura 2. Editor de recursos eXeScope
secuencias de tal error pueden ser
muy graves.
En la primera etapa del análisis
trataremos de conocer la estruc-
tura del fi chero sospechoso. Para
ello nos servirá el analizador de
fi cheros ejecutables PEiD. Esta
herramienta posee una base de
datos incorporada que permite de-
terminar el lenguaje utilizado para
crear una aplicación e identifi car
los tipos más populares de compre-
sores y protectores de fi cheros eje-
cutables. También podemos utilizar
FileInfo, un analizador de fi cheros
un poco más antiguo. Sin embargo,
este último no es desarrollado tan
dinámicamente como PEiD, y el re-
sultado obtenido puede ser menos
preciso. Figura 3. Función WinMain() en el desensamblador IDA
.text:00401114 retn
que patch.exe está precisamente
en este formato.
¿Cuáles son pues las informa- el programa fue escrito utilizando
ciones que podemos obtener de el MS Visual C++ 6.0. PEiD nos La función WinMain()
PEiD? Estructuralmente, patch.exe informa también que el fichero no Después de haber cargado el fi -
es un fichero ejecutable de 32 bit ha sido ni comprimido, ni protegido. chero patch.exe al desensamblador
en el formato típico de la platafor- Las demás informaciones, tales co- IDA (véase la Figura 3), nos encon-
ma Windows: Portable Executable mo el tipo de subsistema, el offset traremos en la función WinMain(), la
(PE). Sabemos (ver Figura 1), que del fichero, o el así llamado punto cual es el punto de entrada usual
unsigned int i = 0, j = 0, k;
Las misteriosas referencias unsigned int dwDimensiondelaImagen;
Veamos ahora de cerca las refe- // calcula cuántos bytes ocupan todos los píxeles en el fichero bitmap
rencias del handle hHandle. Uno de dwDimensiondelaImagen = anchura_de_la_imagen * altura_de_la_imagen * 3;
while (i < dwDimensiondelaImagen) {
estos puntos es la ya mencionada
// junta 8 bits de los componentes de colores RGB
función WinMain(), en la cual la va- //para crear 1 byte de código
riable es leída (como lo señala la for (k = 0; k < 8; k++) {
letra rr, de la palabra inglesa read). lpPunterodeCodigo[j] |= (lpPunterodeBitmap[i++] & 1) << k;
Sin embargo, la más interesante es }
// el siguiente byte de código
la segunda referencia (que aparece
j++;
en la lista como la primera), cuya }
descripción indica que la variable
hHandle es modifi cada en este lugar
(la letra w significa write). Ahora
basta con hacer clic en ella para Listado 9. Estructura interfaz
encontrar el fragmento de código 00000000 interfaz struc ; (sizeof=0X48)
responsable de escribir en la varia- 00000000 hKernel32 dd ? ; handle de la librería kernel32.dll
ble. Este fragmento es presentado 00000004 hUser32 dd ? ; handle de la librería user32.dll
en el Listado 3. 00000008 GetProcAddress dd ? ; direcciones de las funciones WinApi
0000000C CreateThread dd ?
He aquí una breve explicación de
00000010 bIsWindowsNT dd ?
este código: primero se introduce al 00000014 CreateFileA dd ?
registro eax el puntero de la región 00000018 GetDriveTypeA dd ?
de memoria en la que se encuentra 0000001C SetEndOfFile dd ?
el código (mov eax, lpPunterodeCodi- 00000020 SetFilePointer dd ?
00000024 CloseHandle dd ?
go). Luego se efectúa un salto hasta
00000028 SetFileAttributesA dd ?
una instrucción que invoca un pro- 0000002C SetCurrentDirectoryA dd ?
cedimiento (jmp short loc _ 401104). 00000030 FindFirstFileA dd ?
Una vez este procedimiento ha sido 00000034 FindNextFileA dd ?
ejecutado, en el registro eax se en- 00000038 FindClose dd ?
0000003C Sleep dd ?
cuentra el valor del handle (por lo
00000040 MessageBoxA dd ?
general todas las funciones colocan 00000044 stFindData dd ? ; WIN32_FIND_DATA
sus valores de regreso y sus códigos 00000048 interfaz ends
de error en este mismo registro del
procesador), el cual será asignado
a la variable hHandle.
Cualquiera que conozca bien el Listado 10. Arranque del hilo suplementario en el programa principal
lenguaje ensamblador, notará segu- ; al principio de la ejecución de este código en el registro eax se encuentra
ramente que este fragmento de có- ; la dirección del código, en el registro edx se encuentra la dirección de la
digo parece sospechoso (no parece ; estructura que permite lograr acceso a la función WinApi (interfaz)
código normal de C++ compilado). codigo_oculto:
; eax + 16 = inicio del código que será ejecutado en el hilo
Desafortunadamente, el desen-
lea ecx, codigo_ejecutado_en_el_hilo[eax]
samblador IDA no permite ocultar push eax
o borrar instrucciones. Utilicemos push esp
para ello el editor hexadecimal Hiew,
Hiew push 0
en el cual repetiremos el análisis del push edx ; parámetro para la función de hilo
; dirección de la estructura interfaz
mismo código (Listado 4).
push ecx ; dirección de la función a lanzar en el hilo
No vemos más la instrucción push 0
call eax, pues sus opcodes (bytes push 0
de instrucción) han sido puestos en call [edx+interfaz.CreateThread] ; ejecuta el código en un hilo
el interior de la instrucción mov eax, ; de ejecución independiente
loc_10:
0xD0FF. Sólo después de haber bo-
pop ecx
rrado el primer byte de la instrucción sub dword ptr [esp], -2
mov podremos ver el código que será retn
realmente ejecutado:
jmps .000401104 la instrucción call eax. Debemos mento clave del código responsable
; salto en medio de la instrucción saber a dónde lleva la dirección de la extracción de los datos de la
.00401103: 90 que se halla en el registro eax. Más imagen bitmap, se presenta en el
nop arriba de la instrucción call eax se Listado 6.
; 1 byte borrado encuentra una instrucción que es- En el código del Listado 6 pode-
; de la instrucción "mov" cribe en el registro eax el valor de mos distinguir dos bucles. Uno de
.00401104: FFD0 la variable lpPunterodeCodigo (en ellos (el interior) es responsable de
call eax IDA podemos libremente cambiar la descarga de los bytes sucesivos
; instrucción oculta el nombre de la variable para que que conforman los componentes de
sistema kernel32.dll y user32.dll. Sus CreateThread(), previamente coloca- una máquina virtual Vmware (en
handles se asignan a diversos cam- da en la estructura interfaz. Al lan- cuyo caso terminará inmediatamen-
pos de la estructura interfaz. Segui- zar CreateThread(), en el registro eax te su ejecución) haciendo uso de la
damente, en la estructura se colocan es devuelto el handle del hilo apenas instrucción de lenguaje ensamblador
las direcciones de las funciones creado (o 0 en caso de error), el cual, in. Esta instrucción se usa normal-
GetProcAddress() y CreateThread(), después del regreso al código del mente para leer de los puertos de
así como un flag el cual informa si programa principal, es asignado a la E/S y el sistema Vmware la utiliza
el programa ha sido lanzado bajo variable hHandle (ver Listado 10). para manejar su comunicación in-
Windows NT/XP. Los handles de las Observemos el Listado 11, en el terna, pero en sistemas de la familia
librerías de sistema y el acceso a la cual se muestra el hilo responsable Windows NT su ejecución hace que
función GetProcAddress() en práctica de ejecutar el código oculto. El pro- el programa se cuelgue (cosa que no
permiten obtener la dirección de cedimiento lanzado en este hilo re- sucede en las Windows 9x).
cualquier función o librería, no sólo cibe un solo parámetro: la dirección El siguiente paso consiste en
las del sistema. de una estructura interfaz. Nótese obtener las funciones adicionales
cómo el procedimiento verifica si el del WinApi utilizadas por el código
El hilo principal programa ha sido lanzado en un am- oculto y asignarlas a diferentes cam-
El funcionamiento del código oculto biente Windows NT, lo que en reali- pos de la estructura interfaz. Una
comienza cuando el programa prin- dad es parte de una astuta estrategia vez obtenidas todas las direcciones
cipal lanza un hilo suplementario, encaminada a detectar si el procedi- de los procedimientos, se ejecuta
utilizando la dirección de la función miento ha sido invocado dentro de el procedimiento buscar _ discos, el
������������������
���������������
��������������������
WinMain()� ������������� ��������������
��������������� ���������� ���������������������
�������������������
�������������������� �������������������� �����������������
���������� �����������������������
GetProcAddress() ���������������
�������������
���������������
DialogBoxParamA()�
����������
��������������������� �������������������� �������������
�������������������� ���������������
�������������� ��������� ������������������
���������� ���������������������
������������� ��������������������
����������������
������������������
�������������������
����������������������
WaitForSingleObject()� ��������������������
��������������
����������������������� ������������
������������
���������������������� ����������������������
�������������������
������������������� ������������
����������
����� �������������������
������������������
�������������������� �������������� �������������������������
���������������� ������������� ������������
�������������������
��������������
��������������
�������������
����������������������
����������
����������
poder de destrucción del código termina la posición en la que ha de este pequeño programa podría
oculto en el fichero gráfico. En el ser puesto el puntero (en nuestro hacer que todos los ficheros encon-
Listado 14 podemos apreciar la caso, al principio del fichero.) Una trados en todas las particiones de
prueba definitiva de las malas in- vez colocado el puntero, se procede todos los discos de nuestro ordena-
tenciones del autor del programa a invocar la función SetEndOfFile(), dor cambien su tamaño a cero bytes,
patch.exe. Se trata del procedimien- la cual tiene como tarea determinar con lo que prácticamente dejarían de
to truncar _ fichero, el cual se eje- el nuevo tamaño del fichero, utili- existir. Si en algunos de estos fiche-
cuta siempre que el procedimiento zando para ello la posición actual ros se encontrasen datos de valor, el
eliminar _ ficheros encuentra un del puntero. Como podemos ver, daño podría ser irreversible. n