miércoles, 21 de mayo de 2008

Articulo de Programacion #3 (Parte 2)

Recabando información del sistema 2

Ya vimos lo necesario para recabar procesos y su información de cada uno, como fue dicho en el articulo anterior, ahora vamos a poner todo en practica, crearemos un sencillo taskmanager dándonos información básica de los procesos y haremos operaciones sobre ellos. En este ejemplo usaremos el compilador Dev C++, y un programa en modo consola (el gui se vera en otro articulo).

Como siempre, en cualquier programa que hagamos donde usemos las Apis Win32. Tenemos que incluir la librería Windows.h asi como es stdio para los programas en modo consola de c. En este fichero de inclusión de encuentran las declaraciones y estructuras de estas Apis, las definiciones de tipos y las importaciones. Tambien requeriremos usar las funciones del psapi mencionado en el articulo anterior, el problema es que estas definiciones no se encuentran en Windows.h y estas Apis tampoco están incluidas en las librerías kernel32.dll o ntdll.dll, aunque las funciones mencionadas hacen uso de las funciones ntdll.dll, estas serian las Apis nativas que veremos mas adelante. Tenemos entonces que hacer un enlace a psapi.dll mediante su lib, psapi.lib, sin embargo, vamos a aplicar lo aprendido sobre dlls, entonces nosotros dinámicamente enlazaremos nuestro programa con estas funciones. Debemos asegurarnos sin embargo que la dll psapi.dll este en nuestro sistema, generalmente esta ya se encuentra instalada en los sistemas operativos desde Windows 2000.

Tenemos entonces las definiciones de las funciones, por ejemplo, la de EnumProcesses:

BOOL WINAPI EnumProcesses(

__out DWORD* pProcessIds,

__in DWORD cb,

__out DWORD* pBytesReturned

);

Lo que hay que hacer entonces es declararlo como un puntero a función, quedaría de la siguiente forma:


typedef BOOL (WINAPI* TEnumProcesses)(PDWORD, DWORD, PDWORD);


Es importante especificar la opcion de declaracion WINAPI, que es un typedef a la convencion stdcall, lo cual es importante declarar explicitamente ya que de lo contrario se usara otra convencion de llamado de funcion que ocasionara que los parámetros sean empujados a la pila de forma inversa y por lo tanto ocurrirá una excepción. De la misma forma haremos lo mismo para las demás funciones:


typedef DWORD (WINAPI* TGetProcessImageFileName)(HANDLE, LPSTR, DWORD);

typedef DWORD (WINAPI* TGetModuleFileNameEx)(HANDLE, HMODULE, LPSTR, DWORD);

typedef BOOL (WINAPI* TEnumProcessModules)(HANDLE, HMODULE*, PDWORD);

typedef DWORD (WINAPI* TGetModuleBaseName)(HANDLE, HMODULE, LPSTR, DWORD);

typedef BOOL (WINAPI* TEnumProcesses)(PDWORD, DWORD, PDWORD);


Ahora usaremos la api LoadLibrary como vimos en el primer articulo para cargar la dll en el proceso, en caso de que ya este cargada, esta api solo incrementara la referencia de cargado una unidad mas. Esta funcion nos retornara la dirección base de la dll cargada, que en este caso será la psapi.dll.

Usaremos entonces la técnica de punteros a funciones que ofrece C, esto es de las declaraciones que hicimos vamos a declarar estas variables:


TGetProcessImageFileName GetProcessImageFileName;

TGetModuleFileNameEx GetModuleFileNameEx;

TEnumProcessModules EnumProcessModules;

TGetModuleBaseName GetModuleBaseName;

TEnumProcesses EnumProcesses;


Ahora bien, tenemos que depositar en ellos una dirección de memoria, para saber a donde deben empujar los parámetros y hacer el call respectivo, en este usamos la api GetProcAddress y casteamos el tipo que retorna al tipo de puntero de nuestro contenedor, por ejemplo:


GetProcessImageFileName=(TGetProcessImageFileName)GetProcAddress(PspAddress, "GetProcessImageFileNameA");


Codigo:


bool Inicializa(){

HMODULE PspAddress;

PspAddress=LoadLibrary("psapi.dll");

if(!PspAddress){

printf("No se pudo cargar psapi.dll");

return false;

}

GetProcessImageFileName=(TGetProcessImageFileName)GetProcAddress(

PspAddress, "GetProcessImageFileNameA");

if(!GetProcessImageFileName){

printf("No se encontro funcion GetProcessImageFileName");

return false;

}

GetModuleFileNameEx=(TGetModuleFileNameEx)GetProcAddress(

PspAddress, "GetModuleFileNameExA");

if(!GetModuleFileNameEx){

printf("No se encontro funcion GetModuleFileNameEx");

return false;

}

EnumProcessModules=(TEnumProcessModules)GetProcAddress(

PspAddress, "EnumProcessModules");

if(!EnumProcessModules){

printf("No se encontro funcion EnumProcessModules");

return false;

}

GetModuleBaseName=(TGetModuleBaseName)GetProcAddress(

PspAddress, "GetModuleBaseNameA");

if(!GetModuleBaseName){

printf("No se encontro funcion EnumProcessModules");

return false;

}

}


Ahora si ya podremos usar estas funciones, comenzaremos primero listando los procesos en el sistema mediante la ya explicada técnica del EnumProcesses.


void MostrarInformacion(HANDLE hProcess, DWORD ProcessId){

LPSTR buffer;

buffer=(char*)malloc(MAX_PATH);

memset(buffer, 0, MAX_PATH);

printf("ProcessId: %d\n", ProcessId);

if(!GetModuleBaseName(hProcess, NULL, buffer, MAX_PATH)){

printf("Proceso: ?????\n");

}

else{

printf("Proceso: %s\n", buffer);

}

if(!GetModuleFileNameEx(hProcess, NULL, buffer, MAX_PATH)){

printf("Ruta: ??????\n");

}

else{

printf("Ruta: %s\n\n", buffer);

}

free(buffer);

}


bool ListaProcesos(){

HANDLE hProcess;

DWORD Returned, i;

PDWORD Lista;

Lista=(PDWORD)malloc(100*sizeof(DWORD));

memset(Lista, 0, 100*sizeof(DWORD));

if(!EnumProcesses(Lista, 100*sizeof(DWORD), &Returned)){

printf("Ocurrio un error al listar los procesos");

free(Lista);

return false;

}

for(i=0; i<(Returned/sizeof(DWORD)); i++){

hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, Lista[i]);

if(hProcess){

MostrarInformacion(hProcess, Lista[i]);

CloseHandle(hProcess);

}

}

free(Lista);

return true;

}


Como podemos ver en la funcion ListaProcesos, primero alojamos un buffer los suficientemente largo para poder obtener los Process Ids de todos los procesos del sistema, no sabemos cuantos pudiesen ser, incluso pueden llegar a ser mas de 100 (lo máximo que nuestro buffer alojado en esa funcion pudiese soportar), para poder determinar el numero de procesos en el sistema antes de guardarlos en el buffer hay que llamar una vez a la funcion y lo que nos retorne el parámetro de bytes returned usarlo en otra llamada a EnumProcesses, de esta forma ahora ya sabemos que tamaño debe tener el buffer para alojar los PIDs. Si la funcion falla retorna 0 y si no retorna un valor distinto a este lo cual lo checamos en nuestro if. Una vez que nuestro buffer ha sido llenado, el parámetro bytes returned nos da el numero de bytes escritos a este, para saber cuantos elementos son en nuestro arreglo tenemos que dividir el numero de bytes entre 4, asi sabemos cuantas casillas de nuestro arreglo tenemos. Despues solo queda usar cada Process Id recabado y abrirlo con OpenProcess y si podemos obtener referencia a el (existe), lo pasamos a nuestra subrutina MostrarInformacion. En esta subrutina aplicamos la teoría del articulo anterior para obtener el nombre y la ruta completa del proceso.

El ejemplo posee tres funciones sobre los procesos, terminarlos, congelarlos y descongelarlos. Para terminar un proceso, usamos la api de la dll kernel32, TerminateProcess. A esta api recibe dos parámetros, el primero es el handle del proceso a terminar y el segundo el código de la razón de terminación. Las otras dos funciones no se encuentran dentro de las Apis de la categoría win32, son nativas en la dll ntdll.dll. Para poder usarlas, usamos la misma técnica que usamos con las funciones del psapi.dll, las declaramos, obtenemos los punteros de las funciones y las usamos finalmente:


typedef DWORD (WINAPI *TNtSuspendProcess)(HANDLE);

typedef DWORD (WINAPI *TNtResumeProcess)(HANDLE);

ntdllAddress=GetModuleHandle("ntdll.dll");

NtSuspendProcess=(TNtSuspendProcess)GetProcAddress(ntdllAddress, "ZwSuspendProcess");

NtResumeProcess=(TNtResumeProcess)GetProcAddress(ntdllAddress, "NtResumeProcess");


Estas apis nativas reciben en su primer parametro una referencia del proceso a suspender o continuar, mas adelante se analizaran mas a fondo estas apis nativas.

Y con esto termina este articulo de programación, jaja espero que haya sido de tu agrado, hasta la próxima XD.


Descarga el código fuente completo:


No hay comentarios: