страничка tripsin'а


Главная | Статьи | Заметки | Файлы| Ссылки | я
Назад

Дальше

Получение списка процессов. 3 часть. Native API.

Скачай демонстрационный проект (26,1 кб)

Наверное, когда ты прочитал первые два способа, тебе в голову закралась мысль: А как же они это делают? И тогда, как истинные хакер, ты берешь в руки дизассемблер и начинаешь ковыряться в коде библиотек. И тогда обнаруживается, что и CreateToolhelp32Snapshot и EnumProcesses в своем коде вызывают функцию NtQuerySystemInformation из ntdll.dll Ну значит и мы вполне можем воспользоваться этой функцией. На самом ntdll.dll экспортирует три функции, имеющие разные ординалы, но один адрес (так по крайней мере в Windows XP). Вот что я получил из экспорта ntdll.dll с помощью простенького просмотрщика ресурсов eXeScope:

Export, ntdll.dll

Ordinal     Address     Name

00000107    7C90E1AA    NtQuerySystemInformation

00000261    7C90E1AA    RtlGetNativeSystemInformation

00000430    7C90E1AA    ZwQuerySystemInformation

У этих функций естественно абсолютно одинаковый список параметров и на данный момент ты можешь использовать любую из них.

function NtQuerySystemInformation(

     dwSystemInformationClass: DWORD; // Тип запрашиваемых данных

     pSystemInformation: Pointer; // Указатель на буфер для результата

     dwSystemInformationLength: DWORD; // Размер буфера

     var iReturnLength:DWORD // Сюда возвращается требуемая длина буфера

     ): NTSTATUS;

При успешном выполнении возвращает 0. STATUS_SUCCESS = NTSTATUS($00000000);

Пользоваться функцией очень просто. Т.к. нам нужны данные о процессах, то в тип данных передаем константу 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 г.

Hosted by uCoz