Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Сканирование списков модулей, загруженных в АП тек.процесса


Страницы:   1 2 3 4

Digitman ©   (2002-09-03 12:34:00) 

К сожалению, информация о нижеследующем слишком разрозненна, неполна и противоречива.

Исх.данные :

Вот структуры для доступа к неким спискам, ассоциированным с процессом :


PListEntry = ^TListEntry;
TListEntry = packed record
FLink: PListEntry;
BLink: PListEntry;
end;

PModuleHeader = ^TModuleHeader;
TModuleHeader = packed record
dw00: DWord;
dw04: DWord;
list1,
list2,
list3: TListEntry;

end;

PProcessModuleInfo = ^TProcessModuleInfo;
TProcessModuleInfo = packed record
RecordSize: DWord;
ModuleHeader: TModuleHeader;
end;

PProcessEnvironmentBlock = ^TProcessEnvironmentBlock;
TProcessEnvironmentBlock = packed record
.....
ProcessHinstance: THandle;
ModuleInfo: PProcessModuleInfo;
Unknown2: DWord;
DefaultHeap: THandle;
.....
end;


Здесь TListEntry - управляющий заголовок элемента некоего двунаправленного списка.
Собственно элемент списка, видимо, следует сразу за заголовком (?) и формат его может быть произвольным, в зависимости от контекста.

Вопрос :

1. Назначение списков TModuleHeader.list1(,2,3)
2. Полная структура элемента списка (какого конкретно из 3-х ?), перечисляющего модули, загруженные в АП процесса, PEB которого рассматривается. В дан.случае интересен лишь handle (image base) каждого модуля.


paul_shmakov   (2002-09-04 14:38:00) [41]

у меня (w2k) эти поля указывают на RtlEnterCriticalSection и RtlLeaveCriticalSection, а FastPebLock - критическая секция.
вот программа для определения на других операционках (чтобы руками не проверять):

// locktest.dpr
program locktest;

{$APPTYPE CONSOLE}

uses Windows, SysUtils;

type
PPeb = ^TPeb;
TPeb = packed record
AllocSize: Pointer;
Unknown1: Pointer;
ProcessHinstance: Pointer;
ModuleInfo: Pointer;
ProcessParameters: Pointer;
SubSystemData: Pointer;
ProcessHeap: Pointer;
FastPebLock: Pointer;
FastPebLockRoutine: Pointer;
FastPebUnlockRoutine: Pointer;
end;

procedure PrintPointer(const Text: string; P: Pointer);
begin
WriteLn(Text + " " + IntToHex(Integer(P), 8));
end;

var
Peb: PPeb;
RtlEnterCriticalSection: Pointer;
RtlLeaveCriticalSection: Pointer;

begin
asm
mov eax, fs:[$30]
mov [Peb], eax
end;

RtlEnterCriticalSection := Pointer(GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlEnterCriticalSection"));
RtlLeaveCriticalSection := Pointer(GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlLeaveCriticalSection"));

PrintPointer("Peb.FastPebLockRoutine:", Peb.FastPebLockRoutine);
PrintPointer("RtlEnterCriticalSection:", RtlEnterCriticalSection);
PrintPointer("Peb.FastPebUnlockRoutine:", Peb.FastPebUnlockRoutine);
PrintPointer("RtlLeaveCriticalSection:", RtlLeaveCriticalSection);
end.


paul_shmakov   (2002-09-04 13:56:00) [42]


TPEB
...
FastPebLock : Ptr32; { _RTL_CRITICAL_SECTION }
FastPebLockRoutine : Ptr32;
FastPebUnlockRoutine : Ptr32;
...

интересно, это не то самое?


Digitman   (2002-09-04 11:51:00) [43]

>Игорь Шевченко

Не могу, к сож., проверить это - нет под рукой XP.

А ты можешь у себя посмотреть значение поля PEB.LoaderLock в XP ?
Если там таки что-то похожее на хэндл крит.секции, думаю, можно предположить, что LdrLockLoaderLock() просто инкапсулирует функц-ть той же [Rtl]EnterCriticalSection() и в конечном итоге все же использует это поле


Игорь Шевченко   (2002-09-04 11:23:00) [44]

Digitman © (04.09.02 11:13)

Я знаю, что в WinXP и в WinNT4 блокировка осуществляется по-разному. В NT через RtlEnterCriticalSection (@PEB^.LoaderLock),
а в WinXP через вызов функции LdrLockLoaderLock.

С уважением,


Digitman   (2002-09-04 11:13:00) [45]

>paul_shmakov
>Игорь Шевченко

Благодарю за помощь. Все встало на свои места.

P.S.
Проверка показала, что приведенные декларации структур актуальны и для W2k . По кр.мере , в части полей, которые используются механизмом загрузки/выгрузки/блокировки.


Игорь Шевченко   (2002-09-04 09:58:00) [46]

Часть 3:
unit HsNtUtils;

interface
uses
NtDll;

function HSUnicodeStringToAnsiString (usString : TUNICODE_STRING) : AnsiString;

implementation

function HSUnicodeStringToAnsiString (usString : TUNICODE_STRING) : AnsiString;
begin
Result := WideCharLenToString(usString.Buffer,
usString.Length DIV SizeOf(WideChar));
end;

end.


В дополнение:
Закомментрированные части в unit"е main показаны, как идея и не реализованы.
Функция HSCanLockLoader должна проверять, есть ли в NTDLL.DLL фукнции с именами LdrLockLoaderLock и LdrUnlockLoaderLock.

С уважением,


Игорь Шевченко   (2002-09-04 09:56:00) [47]

Часть 2:
unit NtPEB;

interface
uses
Windows, NtDll; { для типа Unicode_string }

type
Uint4B = Cardinal;
Uint2B = Word;
UChar = Byte;
Ptr32 = Pointer;

PLIST_ENTRY = ^TLIST_ENTRY;
TLIST_ENTRY = packed record
Flink : PLIST_ENTRY;
Blink : PLIST_ENTRY;
end;
PPLIST_ENTRY = ^PLIST_ENTRY;

TPEB_LDR_DATA = packed record
Length : Uint4B;
Initialized : UChar;
AlignmentBytes : array[1..3] of Byte;
SsHandle : Ptr32;
{ Список элементов TLDR_DATA_TABLE_ENTRY }
InLoadOrderModuleList : PLIST_ENTRY;
InMemoryOrderModuleList : PLIST_ENTRY; { Неизвестный список }
InInitializationOrderModuleList : PLIST_ENTRY; { Неизвестный список }
EntryInProgress : Ptr32;
end;
PPEB_LDR_DATA = ^TPEB_LDR_DATA;

TLDR_DATA_TABLE_ENTRY = packed record
InLoadOrderLinks : TLIST_ENTRY;
InMemoryOrderLinks : TLIST_ENTRY;
InInitializationOrderLinks : TLIST_ENTRY;
DllBase : Ptr32;
EntryPoint : Ptr32;
SizeOfImage : Uint4B;
FullDllName : TUNICODE_STRING;
BaseDllName : TUNICODE_STRING;
Flags : Uint4B;
LoadCount : Uint2B;
TlsIndex : Uint2B;
HashLinks : TLIST_ENTRY;
SectionPointer : Ptr32;
CheckSum : Uint4B;
TimeDateStamp : Uint4B;
LoadedImports : Ptr32;
EntryPointActivationContext : Ptr32; { Может, только в XP }
end;
PLDR_DATA_TABLE_ENTRY = ^TLDR_DATA_TABLE_ENTRY;

{ Текущий каталог }
type
TCURDIR = packed record
DosPath : TUNICODE_STRING;
Handle : Ptr32;
end;

{ User Process Parameters - пользовательские параметры процесса }
type
TRTL_USER_PROCESS_PARAMETERS = packed record
MaximumLength : Uint4B;
Length : Uint4B;
Flags : Uint4B;
DebugFlags : Uint4B;
ConsoleHandle : Ptr32;
ConsoleFlags : Uint4B;
StandardInput : Ptr32;
StandardOutput : Ptr32;
StandardError : Ptr32;
CurrentDirectory : TCURDIR;
DllPath : TUNICODE_STRING;
ImagePathName : TUNICODE_STRING;
CommandLine : TUNICODE_STRING;
Environment : Ptr32;
StartingX : Uint4B;
StartingY : Uint4B;
CountX : Uint4B;
CountY : Uint4B;
CountCharsX : Uint4B;
CountCharsY : Uint4B;
FillAttribute : Uint4B;
WindowFlags : Uint4B;
ShowWindowFlags : Uint4B;
WindowTitle : TUNICODE_STRING;
DesktopInfo : TUNICODE_STRING;
ShellInfo : TUNICODE_STRING;
RuntimeData : TUNICODE_STRING;
// +0x090 CurrentDirectores : [32] _RTL_DRIVE_LETTER_CURDIR
end;

PRTL_USER_PROCESS_PARAMETERS = ^TRTL_USER_PROCESS_PARAMETERS;
{ Process Environment Block - блок окружения процесса }
type
TPEB = packed record
InheritedAddressSpace : UChar;
ReadImageFileExecOptions : UChar;
BeingDebugged : UChar;
SpareBool : UChar;
Mutant : Ptr32;
ImageBaseAddress : Ptr32;
Ldr : PPEB_LDR_DATA; { Данные загрузчика }
ProcessParameters : PRTL_USER_PROCESS_PARAMETERS { _RTL_USER_PROCESS_PARAMETERS };
SubSystemData : Ptr32;
ProcessHeap : Ptr32;
FastPebLock : Ptr32; { _RTL_CRITICAL_SECTION }
FastPebLockRoutine : Ptr32;
FastPebUnlockRoutine : Ptr32;
EnvironmentUpdateCount : Uint4B;
KernelCallbackTable : Ptr32;
SystemReserved : Uint4B;
ExecuteOptions : Uint4B; {Pos 0, 2 Bits, SpareBits : Pos 2, 30 Bits }
FreeList : Ptr32; { _PEB_FREE_BLOCK }
TlsExpansionCounter : Uint4B;
TlsBitmap : Ptr32;
TlsBitmapBits : array[0..1] of Uint4B;
ReadOnlySharedMemoryBase : Ptr32;
ReadOnlySharedMemoryHeap : Ptr32;
ReadOnlyStaticServerData : Ptr32;
AnsiCodePageData : Ptr32;
OemCodePageData : Ptr32;
UnicodeCaseTableData : Ptr32;
NumberOfProcessors : Uint4B;
NtGlobalFlag : Uint4B;
CriticalSectionTimeout : TLARGEINTEGER;
HeapSegmentReserve : Uint4B;
HeapSegmentCommit : Uint4B;
HeapDeCommitTotalFreeThreshold : Uint4B;
HeapDeCommitFreeBlockThreshold : Uint4B;
NumberOfHeaps : Uint4B;
MaximumNumberOfHeaps : Uint4B;
ProcessHeaps : Ptr32;
GdiSharedHandleTable : Ptr32;
ProcessStarterHelper : Ptr32;
GdiDCAttributeList : Uint4B;
LoaderLock : Ptr32;
OSMajorVersion : Uint4B;
OSMinorVersion : Uint4B;
OSBuildNumber : Uint2B;
OSCSDVersion : Uint2B;
OSPlatformId : Uint4B;
ImageSubsystem : Uint4B;
ImageSubsystemMajorVersion : Uint4B;
ImageSubsystemMinorVersion : Uint4B;
ImageProcessAffinityMask : Uint4B;
{ Остальные данные опущены }
end;

PPEB = ^TPEB;

implementation

end.


Игорь Шевченко   (2002-09-04 09:55:00) [48]

Пример только для NT. Проверялся на WinNT 4.0, WinXP Pro
Часть 1:
unit main;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, NtPEB;

type
TfMain = class(TForm)
ListView: TListView;
CloseButton: TButton;
procedure FormCreate(Sender: TObject);
procedure CloseButtonClick(Sender: TObject);
private
FLoaderLock : Pointer;
FCurrentPeb : PPEB;
procedure BuildModulesList;
procedure DisplayModule (ModuleEntry : PLDR_DATA_TABLE_ENTRY);
procedure LockLoader();
procedure UnlockLoader();
end;

var
fMain: TfMain;

implementation
uses
NTDll, HSNtUtils;

{$R *.dfm}

procedure TfMain.BuildModulesList;
var
LdrData : PPEB_LDR_DATA;
pModule : PLDR_DATA_TABLE_ENTRY;
pLoadOrderList : PPLIST_ENTRY;
begin
LockLoader();
try
LdrData := FCurrentPEB^.Ldr;
pLoadOrderList := @(LdrData^.InLoadOrderModuleList);
pModule := PLDR_DATA_TABLE_ENTRY(pLoadOrderList^);
while (PPLIST_ENTRY(pModule) <> pLoadOrderList) do begin
DisplayModule(pModule);
pModule := PLDR_DATA_TABLE_ENTRY(pModule^.InLoadOrderLinks.Flink);
end;
finally
UnlockLoader();
end;
end;

procedure TfMain.DisplayModule (ModuleEntry : PLDR_DATA_TABLE_ENTRY);
var
LI : TListItem;
begin
LI := ListView.Items.Add();
LI.Caption := Format("%.8x", [Integer(ModuleEntry^.DllBase)]);
LI.SubItems.Add(Format("%.8x", [ModuleEntry^.SizeOfImage]));
LI.SubItems.Add(HSUnicodeStringToAnsiString(ModuleEntry^.FullDllName));
end;

procedure TfMain.LockLoader();
begin
// if HSCanLockLoader() then
// NTDLL.LdrLockLoaderLock(1, 0, @LoaderLock)
// else
// NTDLL.RtlEnterCriticalSection(CurrentPEB.LoaderLock);
end;

procedure TfMain.UnlockLoader();
begin
// if HSCanLockLoader() then
// NTDLL.LdrUnlockLoaderLock(1, LoaderLock)
// else
// NTDLL.RtlLeaveCriticalSection(CurrentPEB.LoaderLock);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
FCurrentPEB := PPEB(NTDLL.RtlGetCurrentPEB());
BuildModulesList();
end;

procedure TfMain.CloseButtonClick(Sender: TObject);
begin
Close();
end;

end.


Digitman   (2002-09-04 08:12:00) [49]

>paul_shmakov

Благодарю за идею. Буду взвешивать все "за" и "против"


paul_shmakov   (2002-09-03 20:43:00) [50]

"Блокировка нужна для исключения ситуации, когда в момент работы с таблицей импорта очередного модуля из получаемого списка этот модуль будет выгружен другим потоком. Исключение, видимо, можно сделать только для ситуации работы енумератора в контексте исполнения DLLProc любого из модулей процесса (имеется ввиду - библиотечные модули) - DLLProc исполняется в крит.секции и загрузчик будет ждать ее освобождения."

как вариант можно увеличивать счетчик пользователей модуля тем или иным способом на время работы, а потом уменьшать. например, если это dll:

LoadLibrary()
try
// работаем с импортом модуля
finally
FreeLibrary();
end;

что-то вроде этого.

"В твоем же варианте - как он ни красив на первый взгляд - будет масса "холостых" циклов из-за фильтрации модулей вызовом GetModuleFileName(), каждый из которых "лезет" в PEB и занимается символьным копированием, и далеко не каждый из которых вернет достоверный факт того, что очередной регион есть очередной искомый мной PE-образ."

все верно - вариант тормозной. но я его использую по причине его работы везде - мне win9x тоже важны :(
поэтому приходится жертвовать производительностью.
если только nt, то конечно стоит поискать более производительные варианты.
но копать имхо нужно именно в сторону инкремента/декремена reference count модулей на время работы с ними.


Digitman   (2002-09-03 18:23:00) [51]

>paul_shmakov


> зачем блокировать загрузчик?


Блокировка нужна для исключения ситуации, когда в момент работы с таблицей импорта очередного модуля из получаемого списка этот модуль будет выгружен другим потоком. Исключение, видимо, можно сделать только для ситуации работы енумератора в контексте исполнения DLLProc любого из модулей процесса (имеется ввиду - библиотечные модули) - DLLProc исполняется в крит.секции и загрузчик будет ждать ее освобождения.


> в моем блокировать ничего не надо


А толку-то собственно от енумератора ? Мне ж не просто перечислить хэндлы модулей требуется - для каждого полученного хэндла возможно потребуется модификация таблицы импорта. А в момент модификации незаблокированная система загрузки/выгрузки запросто может выгрузить модуль в другом кодовом потоке или список претертип изменения из-за загрузки нового модуля (и я его упущу из рассмотрения)

MEM_IMAGE меня вполне устраивает, т.к. речь идет в первую очередь об NT-платформах. Другой вопрос, что енумератор должен работать предельно быстро (вместе с подразумеваемым кодом сканирования и возможной модификации import table очер.модуля), В твоем же варианте - как он ни красив на первый взгляд - будет масса "холостых" циклов из-за фильтрации модулей вызовом GetModuleFileName(), каждый из которых "лезет" в PEB и занимается символьным копированием, и далеко не каждый из которых вернет достоверный факт того, что очередной регион есть очередной искомый мной PE-образ.

Собственно, в теле GetModuleFileName() и работает то, что я пытаюсь представить своим вариантом, так что получается не очень хорошо и эффективно, imho.. Так что пока вот теряюсь я в выборе решения..


paul_shmakov   (2002-09-03 17:34:00) [52]

2 Digitman:
кстати, вы добавили "if mbi.Type_9 = MEM_IMAGE then" к моему коду. изначально этой проверки не было, т.к., если я не ошибаюсь, win9x/me не поддерживают этот флаг в MEMORY_BASIC_INFORMATION.
поэтому без этой проверки код должен работать везде, а с ней - только на nt/2k/xp.


paul_shmakov   (2002-09-03 17:29:00) [53]

2 Digitman:
"Ни один вариант не учитывает то, что в момент енумерации системный загрузчик продолжает работать ! И его оч.желательно блокировать."

если не секрет - зачем блокировать загрузчик? хотя в вашем варианте данные в peb действительно могут измениться, что вызовет av. кто знает..
в моем блокировать ничего не надо.


Digitman   (2002-09-03 17:01:00) [54]

минимально необходимые фрагменты деклараций к варианту 2 :

PListEntry = ^TListEntry;
TListEntry = packed record
FLink: PListEntry;
BLink: PListEntry;
end;

PModuleHeader = ^TModuleHeader;
TModuleHeader = packed record
dw00: Pointer;
dw04: Pointer;
LoadedModuleList, list2, list3: TListEntry; // что за списки 2 и 3 ? пока неясно
end;

PProcessModuleInfo = ^TProcessModuleInfo;
TProcessModuleInfo = packed record
Size: DWord;
ModuleHeader: TModuleHeader;
end;

PPeb = ^TPeb;
TPeb = packed record
AllocSize: Pointer;
Unknown1: Pointer;
ProcessHinstance: Pointer;
ModuleInfo: PProcessModuleInfo;
Unknown2: Pointer;
DefaultHeap: Pointer;
end;


Digitman   (2002-09-03 16:32:00) [55]

Два варианта для сравнения :

1. От <paul_shmakov>


var
hModule: THandle;
mbi: TMemoryBasicInformation;
pRegion: PChar;
cName: array[0..256] of char;

...
pRegion := nil;

while VirtualQuery(pRegion, mbi, sizeof(mbi)) = sizeof(mbi) do
begin
if mbi.State = MEM_FREE then
mbi.AllocationBase := mbi.BaseAddress;

if (mbi.AllocationBase = mbi.BaseAddress) and Assigned(mbi.AllocationBase) then
begin
hModule := THandle(mbi.AllocationBase);
if mbi.Type_9 = MEM_IMAGE then
begin
GetModuleFileName(hModule, @cName, 255);
ShowMessage(inttohex(hModule, 8) + " : " + string(cName));
end;
end;
Inc(pRegion, mbi.RegionSize);
end;



2. От <Digitman> (быстрее работающий, но - "корявый" без нормальных деклараций структур)


var
pNextModuleInfo: PListEntry;
peb: PPeb;
mh: PModuleHeader;
pmi: PProcessModuleInfo;
hModule: THandle;
...
asm
mov eax, fs:[$18]
mov eax, [eax + $30]
mov [peb], eax
end;
pmi := peb.ModuleInfo;
mh := @pmi.ModuleHeader;
pNextModuleInfo := mh.LoadedModuleList.FLink;
while pNextModuleInfo <> @pmi.ModuleHeader.LoadedModuleList do
begin
hModule:= PInteger(DWord(pNextModuleInfo) + $18)^;
showmessage(inttohex(hModule, 8));
pNextModuleInfo := pNextModuleInfo.FLink;
end;


Ни один вариант не учитывает то, что в момент енумерации системный загрузчик продолжает работать ! И его оч.желательно блокировать. И желательно - с использованием PEB.LoaderLock- поля. Что, в принципе, не вызывает проблем при наличии деклараций PEB-структуры для нескольких платформ ...

Какие еще будут варианты ?


Digitman   (2002-09-03 16:06:00) [56]

>paul_shmakov

Тогда уж так :


var
hModule: THandle;
mbi: TMemoryBasicInformation;
pRegion: PChar;
begin

pRegion := nil;

while VirtualQuery(pRegion, mbi, sizeof(mbi)) = sizeof(mbi) do
begin
if mbi.State = MEM_FREE then
mbi.AllocationBase := mbi.BaseAddress;

if (mbi.AllocationBase = mbi.BaseAddress) and Assigned(mbi.AllocationBase) then
begin
hModule := THandle(mbi.AllocationBase);
if mbi.Type_9 = MEM_IMAGE then
showmessage(inttohex(hModule, 8));
end;
Inc(pRegion, mbi.RegionSize);
end;


Только вот в момент перечисления системный загрузчик никак не блокирован( .. и состояние AП в части загруженных PE-образов вполне может измениться


paul_shmakov   (2002-09-03 15:24:00) [57]

могу такой вариант предложить - мне он нравится больше всего

#include
#include
#include

#ifdef UNICODE
#define _tcout std::wcout
#else
#define _tcout std::cout
#endif

int main(void)
{
TCHAR szModuleName[MAX_PATH];
HMODULE hMod = NULL;

PBYTE pb = NULL;
MEMORY_BASIC_INFORMATION mbi;

while(VirtualQuery(pb, &mbi, sizeof(mbi)) == sizeof(mbi))
{
if(mbi.State == MEM_FREE)
mbi.AllocationBase = mbi.BaseAddress;

if((mbi.AllocationBase == mbi.BaseAddress) && (NULL != mbi.AllocationBase))
{
hMod = static_cast(mbi.AllocationBase);
if(GetModuleFileName(hMod, szModuleName,
sizeof(szModuleName) / sizeof(szModuleName[0])) > 0)
{
_tcout


Игорь Шевченко   (2002-09-03 14:45:00) [58]

Эти смещения не меняются в зависимости от версии ОС ? :-)
Кстати, минуту, ntdll гляну :-)))


Digitman   (2002-09-03 14:31:00) [59]

Нет у меня, к сож., этого букваря под рукой сейчас.

И - опять же - imho, не в динамике работать будет эта ф-ция. Вернет мне статический снимок списка на момент запроса. А при модификации список может измениться. Можно, конечно, блокировать загрузчик перед вызовом ф-ции и после модификации модулей из полученного списка, но это тоже через зад получится ...


Игорь Шевченко   (2002-09-03 14:18:00) [60]

Гэри Неббет, справочник по базовым функциям NT/Windows 2000 (Native API reference)
RtlQueryProcessDebugInformation с параметром PDI_MODULES

С уважением,


Страницы:   1 2 3 4

Наверх

Титульная страница Поиск, карта сайта Написать письмо