Developer's Diary

15 noviembre, 2014

Inyeccion DLL. Mostrando un ejemplo II

Filed under: .net, API, ASP.Net, Awesome, Internet — Etiquetas: , , , , , — jnavero @ 7:31 PM

Anteriormente en el post Inyección DLL. Mostrando un ejemplo hablé de los cuatro proyectos que conforman estos artículos y me faltaron dos por explicar.
En este post finalmente hablaré de estos dos últimos proyectos, de la DLL en .NET que vamos a inyectar en la victima y de la ayuda en C++.

Así que vamos a ponernos manos a la obra.

La librería DLL .NET contiene el siguiente código:


class ataque
{
[DllImport("USER32.DLL")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);

public static int EP(string aux)
{
IntPtr victima = FindWindow(null, "Victima");

if (victima != IntPtr.Zero)
{
IntPtr child = FindWindowEx(victima, IntPtr.Zero, null, "statusStrip1");

Control statusControl = Control.FromHandle(child);
StatusStrip testStatus = (StatusStrip)statusControl;

ToolStripStatusLabel labelStatus = new ToolStripStatusLabel();
labelStatus = (ToolStripStatusLabel)testStatus.Items[0];

testStatus.Items[0].Text = "Ok!";
}
return 0;
}
}

Este trozo de código, es el responsable del cambio en la etiqueta del programa victima. Para ello, uso las Apis FindWindows que retorna el Handle de la ventana (o clase a buscar) y con FindWindowEx podemos recorrer los hijos (los diferentes controles) de la ventana. Este nos retorna el Handle del hijo. Con el Handle del hijo podemos hacer lo que se nos antoje, desde enviar un mensaje (con SendMessage) o bien usar Control.FromHandle y nos retornará un control que en realidad es mas manejable que SendMessage.
Después de esto, podemos castear nuestro control de la forma deseada, o bien cambiar el texto directamente…
En labelStatus, muestro el ejemplo de como hacer el cast del control (no lo uso, pero sirve para que se vea la la forma de hacer el casting).

Con estos tres componentes debería ser suficiente para poder hacer las pruebas… Así que vamos a ello y a ver que sucede…
1º. Ejecutamos la victima…
2º. Ejecutamos el programa .NET para la inyeccion DLL.
En este programa, seleccionamos la victima y la dll que hemos creado (ataque.dll)
y pulsamos sobre el botón Inyectar

inject1

Vaya, después de esto no hace absolutamente nada…
¿Se habrá inyectado?
Bueno, esto se puede comprobar con SirPE
inject2

Vemos que efectivamente, la dll está cargada en la memoria y por lo tanto debería hacer algo pero, no hace nada.
¿Por que?…

La explicación pasa por un montón de puntos así que trataré de ser lo mas breve y como siempre explicarlo de la forma mas sencilla posible.
Podríamos decir, que hoy día hay dos tipos de compilaciones (principalmente):
a) Código nativo.
b) Código interpretado.

Un ejemplo de código nativo sería un programa escrito en C o C++ compilado con G++ o DevCpp que en windows genera un archivo .EXE y no requiere de nada mas para que se ejecute
Por otra parte tenemos el código interpretado, que se suelen compilar en un lenguaje intermedio y necesita el Runtime para poder ejecutarlo, en .NET supongo que todo el mundo conoce el IL o el CLR. En java, existe la JVM.

Basándonos en lo dicho anteriormente, igualmente, podríamos decir que hay dos tipos de Dlls, las Dll en codigo nativo que tienen un entry point (static DllMain()) y las Dlls Manejadas (lo de manejadas, es la traducción que le doy por que no se como hacer la traducción de Dll Managed, podrían ser gestionadas o administradas), el caso es que el nombre da igual, lo importante es el concepto.
Es decir, por un lado tenemos las Dll en código nativo y por otro lado, las Dll que necesitan un runtime para funcionar.

¿Entonces como hago para poder inyectar la Dll de .Net a una aplicación?

La respuesta, la dí en el primer post, con el 4º punto. y es lo ultimo que nos queda para completar esta tarea.
Necesitamos una Dll escrita con código nativo, que llame al runtime de .Net y ejecute la dll escrita en .Net

Entonces la idea nos queda de la siguiente forma:
Tenemos una aplicación victima.
Nuestra aplicación inyectora, no va a inyectar la Dll que tenemos por que es inutil, debemos inyectar una dll escrita en C++ y esta dll escrita en C++ llamará al runtime de .NET y gracias a ExecuteInDefaultAppDomain al metodo escogido (EP) este metodo debe ser public static.

Quizá este trozo escrito en C++, sea el mas complejo de todos:


#include "stdafx.h"

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
IniciarDLL(L"");

return TRUE;
}

Este trozo de código define el ep (entry point) de la dll.

y la chicha de la dll:

#include "stdafx.h"
#include "MSCorEE.h"

DWORD WINAPI IniciarDLL( LPVOID lpParameter)
{
ICLRMetaHost * lpMetaHost = NULL;
HRESULT hr;

wchar_t error[256];

hr = CLRCreateInstance(
CLSID_CLRMetaHost,
IID_ICLRMetaHost,
(LPVOID *)&lpMetaHost);
if (FAILED(hr))
{
wsprintf(error, L"CLRCreateInstance %08x", hr);
MessageBox(NULL, error, L"::1", MB_OK | MB_ICONERROR);
return 1;
}

DWORD dwVersion = 0;
DWORD dwImageVersion = 0;
ICLRRuntimeInfo * lpRuntimeInfo = NULL;
// Referencia al RunTime (En mi caso Framework 4, tambien se puede poner la del 2
hr = lpMetaHost->GetRuntime(
L"v4.0.30319", // 4.0
IID_ICLRRuntimeInfo,
(LPVOID *)&lpRuntimeInfo);
if (FAILED(hr))
{
wsprintf(error, L"GetRuntime %08x", hr);
MessageBox(NULL, error, L"::2", MB_OK | MB_ICONERROR);
return 2;
}

ICLRRuntimeHost * lpRuntimeHost = NULL;
// Carga el CLR.
hr = lpRuntimeInfo->GetInterface(
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(LPVOID *)&lpRuntimeHost);
if (FAILED(hr))
{
wsprintf(error, L"GetInterface %08x", hr);
MessageBox(NULL, error, L"::3", MB_OK | MB_ICONERROR);
return 3;
}

// Inicia el CLR
hr = lpRuntimeHost->Start();
if (FAILED(hr))
{
wsprintf(error, L"Start %08x", hr);
MessageBox(NULL, error, L"::4", MB_OK | MB_ICONERROR);
return 4;
}

LPWSTR strDLLPath1 = new WCHAR[_MAX_PATH];

::GetModuleFileNameW((HINSTANCE)&__ImageBase, strDLLPath1, _MAX_PATH);

std::wstring tempPath = strDLLPath1;
int index = tempPath.rfind('\');
tempPath.erase(index, tempPath.length() - index);
tempPath += L"C:\temp\ataque.dll"; //Aqui es donde se encuentra mi libreria que está en .NET

DWORD dwRetCode = 0;
//Esto ha sido lo peor, por que en una linea no funciona y en dos si... inexplicable...
//hr = lpRuntimeHost->ExecuteInDefaultAppDomain( (LPWSTR)tempPath.c_str(), L"ataque.ataque", L"EP", L"jj", &dwRetCode);

hr = lpRuntimeHost->ExecuteInDefaultAppDomain(
L"C:\temp\ataque.dll",
L"ataque.ataque", L"EP", L"^_^", &dwRetCode);

if (FAILED(hr))
{
wsprintf(error, L"ExecuteInDefaultAppDomain %08x", hr);
MessageBox(NULL, error, L"::5", MB_OK | MB_ICONERROR);
return 5;
}

// Para el CLR (a mi no me funciona).
hr = lpRuntimeHost->Stop();

//Limpia el CLR...
lpRuntimeHost->Release();

return 0;
}

Como ya comenté este código es un poco mas duro, creo que lo he comentado bastante bien pero, la verdad, es que la documentación que he encontrado de esto, no era muy buena 🙂
Con CLRCreateInstance, creamos una instancia del CLR de .NET, con la función GetRuntime instanciamos el runtime que deseamos, en mi caso, Framework 4. Con el runtime que deseamos ejecutar usamos la función GetInterface que nos provee de la funcionalidad del CLR y ya solo nos queda arrancarlo.
Podríamos decir que hasta aquí es la preparación (instanciacion e iniciación) y ahora arrancarlo y usarlo.
Con Start lo ejecutamos y por ultimo debemos llamar a nuestra dll para que esta comience a funcionar. Esto se hace con la función ExecuteInDefaultAppDomain. He de decir, que tuve muchisimos problemas con esta función ya que en una sola linea no funcionaba y cuando la puse en dos si, no entiendo el motivo, quizá mi compilador no funcione bien.
Para finalizar todo, se hace un Stop y un Release con lo que el CLR queda parado y todo finalizado.
Esa es la teoría por que la practica es que a mi no me funciona, se queda en ejecución todo el tiempo y cuando sale de la victima, se queda el runtime que como no existe la dll que debe ejecutar, pues da un cartel de error.

Si hacemos la prueba obtenemos el siguiente resultado:

injec3

Es decir, hemos modificado la ventana de la victima, sin usar SendMessage, es decir, tocando el control directamente de la ventana, como si se tratase de una funcionalidad mas de nuestra aplicación.

Con esto doy por finalizado la serie de los dos post de inyección dll.

Saludos!

Anuncios

Dejar un comentario »

Aún no hay comentarios.

RSS feed for comments on this post. TrackBack URI

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: