Назад |
Скачай демонстрационный проект (26,1 кб)
Наверное, когда ты прочитал первые два способа, тебе в голову закралась мысль: А как же они это делают? И тогда, как истинные хакер, ты берешь в руки дизассемблер и начинаешь ковыряться в коде библиотек. И тогда обнаруживается, что и CreateToolhelp32Snapshot и EnumProcesses в своем коде вызывают функцию NtQuerySystemInformation из ntdll.dll Ну значит и мы вполне можем воспользоваться этой функцией. На самом ntdll.dll экспортирует три функции, имеющие разные ординалы, но один адрес (так по крайней мере в Windows XP). Вот что я получил из экспорта ntdll.dll с помощью простенького просмотрщика ресурсов eXeScope:
|
У этих функций естественно абсолютно одинаковый список параметров и на данный момент ты можешь использовать любую из них.
|
Пользоваться функцией очень просто. Т.к. нам нужны данные о процессах, то в тип данных передаем константу SystemProcessesAndThreadsInformation = 5. Вызвать функцию будем дважды.
В первый раз определим, сколько памяти надо выделять для данных. Для этого вместо указателя на буфер передаем nil, а длину буфера ставим равную нулю. В переменной iReturnLength будет размер буфера. Резервируем этот буфер и снова вызываем NtQuerySystemInformation, но уже во втором и третьем параметре передаем указатель и длину буфера.
Дальше разбираемся с полученными данными. Они представляют собой односвязный список из структур SYSTEM_PROCESSES. Описание полей SYSTEM_PROCESSES смотри в коде модуля в комментариях. Информации она содержит достаточно. Поле NextEntryDelta указывает на смещение следующей структуры от начала текущей. Имя процесса содержится в поле ProcessName в виде структуры TUnicodeString. Нультерминальная UNICODE-строка с именем процесса содержится в буфере ProcessName.Buffer. Чтобы использовать эту строку в Delphi, ее надо преобразовать в ANSI-строку. Это можно сделать с помощью функции WideCharToString (модуль System).
Таким образом пробегаемся по списку, пока в поле NextEntryDelta не будет ноль. Извлекаем инфу о каждом процессе и добавляем ее в TStringList:
unit Sample3;
interface
uses Windows, Classes, SysUtils;
type
NTSTATUS = Cardinal;
const
SystemProcessesAndThreadsInformation = 5;
STATUS_SUCCESS = NTSTATUS($00000000);
type
.
. // Вырезано для краткости
.
PUnicodeString = ^TUnicodeString;
TUnicodeString = packed record
Length: Word; // Длина строки без терминального нуля
MaximumLength: Word; // Полная длина буфера
Buffer: PWideChar; // Указатель на буфер для UNICODE-строки
end;
.
. // Вырезано
.
PSYSTEM_PROCESSES = ^SYSTEM_PROCESSES;
SYSTEM_PROCESSES = packed record
NextEntryDelta, // Смещение следующей структуры от начала этой
ThreadCount: dword; // Количество потоков процесса
Reserved1 : array [0..5] of dword;
CreateTime, // Временные характеристики процесса
UserTime,
KernelTime: LARGE_INTEGER;
ProcessName: TUnicodeString; // Имя процесса
BasePriority: dword; // Базовый приоритет
ProcessId, // Идентификатор процесса – PID
InheritedFromProcessId, // PID родителя
HandleCount: dword; // Открытые дестрипторы
Reserved2: array [0..1] of dword;
// Счетчики с инфой об использовании памяти и системы ввода-вывода.
VmCounters: VM_COUNTERS;
IoCounters: IO_COUNTERS; // Windows 2000 only
// Массив структур с инфой о потоках процесса
Threads: array [0..0] of SYSTEM_THREADS;
end;
function FillProcessesList(var slProcesses: TStringList): Boolean;
implementation
function ZwQuerySystemInformation(dwSystemInformationClass: DWORD;
pSystemInformation: Pointer;
dwSystemInformationLength: DWORD;
var iReturnLength:DWORD): NTSTATUS;
stdcall;external 'ntdll.dll';
function FillProcessesList(var slProcesses: TStringList): Boolean;
var
ret: NTSTATUS;
pBuffer, pCur: PSYSTEM_PROCESSES;
ReturnLength: DWORD;
i: Integer;
ProcessName: String;
begin
Result := False;
ReturnLength := 0;
// Запрашиваем размер требуемого буфера
ret := ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,
nil,
0,
ReturnLength);
// Резервируем буфер
pBuffer := AllocMem(ReturnLength);
// Получаем информацию о процессах в буфер
ret := ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,
pBuffer,
ReturnLength,
ReturnLength);
if ret = STATUS_SUCCESS then // Проверяем успешность выполнения запроса
begin
pCur := pBuffer; // Инициируем указатель на текущую структуру
i := 0; // Инициируем счетчик процессов. Его можно и не использовать.
// Проходим в цикле по всей цепочке структур
repeat
inc(i); // Увеличиваем счетчик процессов.
// Смотрим длину имени процесса и если оно не равно 0,
// то читаем строку из буфера, иначе – имя = <неизвестно>
if pCur.ProcessName.Length = 0 then ProcessName := '<неизвестно>'
else ProcessName := WideCharToString(pCur.ProcessName.Buffer);
// Добавляем инфу о процессе в TStringList
slProcesses.Add(IntToStr(i) + ' -' +
' Имя: ' + ProcessName +
' PID: ' + IntToStr(pCur.ProcessId) +
' Приоритет: ' + IntToStr(pCur.BasePriority) +
' Потоков: ' + IntToStr(pCur.ThreadCount) +
' Дескрипторов: ' + IntToStr(pCur.HandleCount));
// Вычисляем указатель на следующую структуру SYSTEM_PROCESSES
// Для этого к адресу этой структуру прибавляем смещение следующей
// из поля NextEntryDelta
pCur := Ptr(DWORD(pCur) + pCur.NextEntryDelta);
// Крутим цикл, пока есть следующая структура
until pCur.NextEntryDelta = 0;
Result := True;
end;
FreeMem(pBuffer);
end;
end.
Назад |
(с) tripsin aka Орехов Роман, 2006 г.