sábado, 7 de mayo de 2011

Usando GetProcAddress y LoadLibrary para localizar APIs 2/2

En la anterior entrada vimos la necesidad de utilizar las funciones GetModuleHandle y GetProcAddress para invocar a otras funciones de Windows.

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.

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.

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 SectorVirus.

;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

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.

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.

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.

Lo he explicado muy resumidamente, en el capítulo 7 de SectorVirus que cuelgo aquí está explicado con todo detalle la estructura de la ExportTable y como recorrerla para encontrar una determinada función.

A continuación dejo la función en ensamblador que hace esto. Necesita saber la dirección de kernel32 hallada anteriormente.

.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
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!

No hay comentarios:

Publicar un comentario