Muchos de ustedes probablementa han leído el artículo de Iczelion's aquí en cornsoup sobre el uso de toolhelp32 para obtener una lista de procesos y de módulos de Win9x. Bien, no sé si recuerdas que hacia el final del ensayo él menciona que para hacer esto en winnt debes usar psapi.dll. Técnicamente, esto no es tan cierto, ya que psapi.dll obtiene información llamando a funciones ubicadas en winnt.dll. Sin embargo, estas funciones pueden cambiar, pero las funciones de psapi.dll se mantendrán iguales incluso en las siguientes versiones de Windows NT (incluyendo Windows 2000).
No voy a profundizar en todas las funciones en psapi.dl. Simplemente me concentraré en las funciones que tienen que ver directamente con Procesos y Módulos.
Estas funciones son:
La primera función que veremos será EnumProcesses. Esta función llena un array de DWORD's con el Process ID de todos los procesos que corren actualmente en el sistema. He aquí la declaración de la función:
local dSize:DWORD ; lo necesitará cb
local dwSize2:DWORD ; cb
local lpdwPID:DWORD ; Array de PID's
local dwPIDCnt:DWORD ; Número de PID's en el array
mov eax, 256 ; # de PIDS en el primer intento
shl eax, 2 ; multiplicado por 4 (sizeof(DWORD))
mov dwSize2, eax ; almacenar este valor
mov lpdwPID, 0
@loopy:
cmp lpdwPID, 0 ; si el array ya apunta a una dirección, tenemos que
jz Size2OK ; liberar y relocalizar memoria
invoke GetProcessHeap ; obtener el montículo del proceso actual
mov ebx, eax ; masm no debería compilar sin esto :/
invoke HeapFree, ebx, 0, addr lpdwPID ; Libera el espacio del motículo
mov edx, dwSize2
shl edx, 1 ; doblar el tamaño del montículo a ser localizado
mov dwSize2, edx
Size2OK:
invoke GetProcessHeap ; obtener el montículo del proceso actual
invoke HeapAlloc, eax, 0, dwSize2 ; localizar todo el espacio necesario
test eax, eax ; asegurar que la llamada a HeapAlloc fue exitosa
jnz AllocOK
jmp ErrorReturn
AllocOK:
mov lpdwPID, eax ; Hacer que lpdwPID apunte a la memoria localizada
invoke EnumProcesses, lpdwPID, dwSize2, addr dwSize ; call EnumProcesses
test eax, eax ; Probar el éxito de la llamada
jnz EnumProcsOK
invoke GetProcessHeap ; Si fue infructuosa, liberar memoria y salir
invoke HeapFree, eax, 0, lpdwPID
jmp ErrorReturn
EnumProcsOK:
mov eax, dwSize
cmp eax, dwSize2 ; comparar el tamaño del array con el tamaño necesario
jz @loopy ; si son los mismos, mantener el bucle
shr eax, 2 ; el # de PID es regresado = tamaño del array/4
mov dwPIDCnt, eax
ret
ErrorReturn:
invoke GetLastError ; Reportar el código de error ;)
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM, 0, eax,0, addr szError, 255, 0
invoke MessageBox, NULL, addr szError, addr AppName, 0
mov eax, -1
ret
Ok, ahora tenemos todo el montón de PID's, ¿qué vamos a hacer con él? Bien, realmente un PID no hace mucho por uno, pero nos permite llamar a OpenProcess para obtener el handle del proceso, que si es muy útil. Cuando llamamos a OpenProcess las mejores banderas [flags] a usar son PROCESS_QUERY_INFORMATION y PROCESS_VM_READ. Si intentas con PROCESS_ALL_ACCESS, muchos de los procesos no abrirán, pero si intentas sólo con PROCESS_QUERY_INFORMATION, algunas de las funciones de psapi.dll no trabajarán apropiadamente. Incluso con estas banderas [flags] algunos procesos del sistema no abrirán y tendrás que tratar a estos procesos de forma separada. Una vez que tengas el handle del proceso, puedes llamar a EnumProcessModules, cuyo prototipo es :
hProcess es el handle al proceso.
lphModule es un puntero a un array de DWORDs, que será llenado
con handles de módulos
cb tamaño del array anterior.
lpcbNeeded puntero a un dato tamaño DWORD, será el número
de bytes necesarios.
La misma regla aplicadas a EnumProcesses se aplican aquí, no sabes cuántos módulos habrán. Sin embargo, frecuenemente sólo tienes que obtener el primer módulo a partir de la lista. Hacer esto es muy simple, como lo muestra el siguiente código de ejemplo. Si quieres obtener todos los módulos, modifca el código para que en vez de llamar a EnumProcesses se llame a EnumProcessModules. Esto asegurará que localices la memoria necesaría para tu aplicación.
local hProcess:DWORD ; Handle del proceso
local hModule:DWORD ; handle DEL Módulo
local dwBytes:DWORD ; No usado realmente ;)
mov edi, lpdwPID ; Puntero a nuestro array de PIDs
; Ahora llamamos a openprocess con el primer PID en el array
invoke OpenProcess, PROCESS_QUERY_INFORMATION + PROCESS_VM_READ, \
FALSE, dword ptr [edi]
mov hProcess, eax ; Mover el valor regresado a Process Handle
test eax, eax ; EStarseguro que la llamada fue exitosa
jz OpenProcessFailed
; Llamar a EnumProcessModules (sólo obtendremos el handle del primer módulo)
invoke EnumProcessModules, hProcess, addr hModule, 4, addr dwBytes
test eax, eax ; Estar seguro de que la llamada fue exitosa
jz EnumFailed
¡Maravilloso! Ahora tenemos un handle de Proceso y un handle de Módulo. Esto es muy bueno y estupendo siempre que corramos aplicaciones de 32 bits. Windows NT maneja aplicaciones de 16 bits corriéndolas a través de una Máqina DOS Virtual (VDM: Virtual DOS Machine). Así que, con el fin de hacer una lista de los procesos de aplicaciones de 16 bits, hay que encontrar la VDM (NTVDM.EXE), y usar sus PID para llamar a VDMEnumTaskWOWEx. Esta función está localizada en vdmdbg.dll y toma entre sus argumentos la dirección de un procedimiento tipo callback que es llamado para cada aplicación de 16bits que está corriendo en el sistema. Estos son los prototipos:
VDMEnumTaskWOWEx PROTO dwProcessID:DWORD,
lpCallbackProc:DWORD, lParam:DWORD
Enum16Callback PROTO dwThreadID:DWORD, hMod16:WORD, hTask16:WORD, \
lpszModName:DWORD, lpszFileName:DWORD,
lParam:DWORD
Ahora, para VDMEnumTaskWOWEx aquí está la información:
dwProcessID es el PID de NTVDM.EXE
lpCallbackProc es un punetro a un procedimeinto callback
lParam es una variable definida por el usuario pasada al procedimiento
callback
Y para el procedimento callback:
dwThreadID el ID del hilo, *dummy* ;)
hMod16 el handle al módulo de 16bits.
hTask16 el handle a la tarea de 16bits.
lpszModName puntero al nombre del módulo
lpszFileName puntero al nombre del archivo (la diferencia entre estos
dos es explicada en la siguiente sección)
lParam Revisa en la información por VDMEnumTaskWOWEx
Código de ejemplo:
invoke lstrcmpi, addr szFileName, addr szNtVDM ; Prueba si este es el NTVDM
test eax, eax
jnz EnumFailed
invoke VDMEnumTaskWOWEx, dword ptr [edi], addr Enum16Proc, \
addr szBlank ; call VDMEnumTaskWOWEx
EnumFailed:
... ; The callback proc
Enum16Proc PROC dwThreadID:DWORD, hMod16:WORD, hTask16:WORD, szModName:DWORD, \
pszFileName:DWORD, lpUserDefined:DWORD
push ebx ; salva los registros que usamos (ocurrirán cosas malas si no hacemos esto)
inc dwCount ; ésta es una variable de nuestra listview
mov ebx, dwCount ; todo lo demás es el código del listview
mov eax, offset szBlank
mov _item.iitem, ebx
mov _item.pszText, eax
mov _item.iSubItem, 0
invoke SendDlgItemMessage, hWnd, IDC_LIST,LVM_INSERTITEM,ebx, addr _item
mov eax, szModName ; Queremos desplegar el nombre del módulo
mov _item.iitem, ebx
mov _item.pszText, eax
mov _item.iSubItem, 1
invoke SendDlgItemMessage, hWnd, IDC_LIST,LVM_SETITEMTEXT,ebx, \
addr _item xor eax, eax ; Si quieres continuar con la enumeración de módulos de 16bit,
; debes regeresar false
pop ebx
ret
Enum16Proc ENDP
Back to our Process Handle and Module Handle, ¿qué podemos hacer con ellos? Bien, aquí es donde el resto de los procedimientos serán cubiertos es este artículo. GetModuleBaseName y GetModuleFileNameEx son muy similares. La diferencia es que GetModuleFileNameEx regresa todo el camino [path] completo al módulo y GetModuleBaseName sólo regersa el nombre del archivo. Aquí están los prototipos:
Next in our bag of psapi.dll function es GetModuleInformation. Esta es una simple función que llena una estructura de información sobre el módulo. Ninguna de estas informaciones es particularmente difícil de obtener sin esta función, pero como se encuentran allí, serán cubiertas. He aquí el prototipo:
hProcess es el handle al proceso.
hModule es el handle al módulo.
lpmodinfo es el puntero a una estructura MODULEINFOt. (La cubriré
en un minuto)
cb es el tamaño de la estructura. (HINT: invoke blahblah, sizeof
MODULEINFO) ;)
La estrcutura MODULEINFO:
MODULEINFO STRUCT
lpBaseOfDll DWORD ? ;Valor imagebase del módulo (lo mismo que hModule) SizeOfImage DWORD ? ;Tamaño de la imágen (del encabezado PE en memoria) EntryPoint DWORD ? ;Punto de netrada del programa (del encabezado PE en memoria) MODULEINFO ENDS
GetProcessMemoryInfo es el último procedimientio que será tratado aquí. Como GetModuleInformation, llena una estructura con información sobre el proceso.* 'On With the Prototype!' you say*:
GetProcessMemoryInfo PROTO hProcess:DWORD, ppsmemCounters:DWORD, cb:DWORD
hProcess handle al proceso.
ppsmemCounters un puntero a la estructura llenada por la función.
cb tamaño de la estructura.
PROCESS_MEMORY_COUNTERS STRUCT cb DWORD ? ; Tamaño de la estructura PageFaultCount DWORD ? ; El # de Fallos de Página PeakWorkingSetSize DWORD ? ; Tamaño pico del conjunto activo [working set] WorkingSetSize DWORD ? ; Tamaño actual del conjunto activo [working set] QuotaPeakPagedPoolUsage DWORD ? ; Uso Pico de Reserva Paginada QuotaPagedPoolUsage DWORD ? ; Uso actual de Reserva Paginada QuotaPeakNonPagedPoolUsage DWORD ? ; Uso Pico de Reserva No Paginada QuotaNonPagedPoolUsage DWORD ? ; Uso actual de Reserva No Paginada PagefileUsage DWORD ? ; Uso del archivo de paginación PeakPagefileUsage DWORD ? ; Tamaño pico del archivo de páginación PROCESS_MEMORY_COUNTERS ENDS
Ok, clase. Esto concluye la lectura de hoy sobre psapi.dll. Espero que hayas
aprendido algo.
Descargas:
Archivo zip con los archivos psapi y vdmdbg .dll, .lib y sus correspondientes
archivos.inc -> aquí
Un ejemplo de código muy poco comentado, feo, *lame* -> aquí
(para compilar nscesitarás el acrhivo dlls.zip)
Saludos a:
+YoSHi - mantén esos lamer logs lo suficiente y podemos publicar
un libro. ;)
CrackZ - *even if you don't like our yo momma! jokes...*
Coreknee - buen sitio...
Tin0r - <Tin> soy un poco lento, eso es todo
JosephCo - <josephCo-> algo no está correcto en esa oración
:)
ok, estoy en deuda con muchas muchas personas, así que aquí pongo
una lista rápida: The Phrozen Coders, the #c4n crew, the #win32asm crew,
Nitallica, DaVinci, Klinkers, db, esotarius, tD, ok, es suficiente...