Список процессов dll и получить список загруженных в данный момент dll в процессе

Introduction¶

It can be useful to know which .dlls an application has loaded when analysing DLL-related problems.

For this particular purpose you can use Microsoft’s
Process Explorer
tool.

Приветствую всех! Сегодня я покажу как можно узнать какие DLL библиотеки использует та или иная загруженная программа. Это может быть очень важно если вы владелец какого то игрового сервера, и вам необходимо встраивать части проверки в модификацию игр, на основе инъекций. Так же может быть полезно, тем кто хочет узнать какая из программ использует занятую библиотеку. И так рассмотрим сам код:

Я закомментировал весь код, думаю сложностей с пониманием его не возникнет.

Так же вам стоит взглянуть вот на эту статью где я смог вывести список всех инжектированных dll ссылка

Список загруженных DLL для процесса можно получить с помощью свойства Process.Modules. Для определения инжектированных DLL нужно составить базовый список загруженных модулей при работе данного приложения в чистой среде, затем при выполнении в реальной среде сравнить список DLL с базовым списком:

using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
namespace WindowsFormsTest1
{ public class DLL { /*Примерный базовый список загруженных DLL для WinForms-приложения в Windows 7*/ public static string[] DllWhiteList = "ntdll.dll MSCOREE.DLL KERNEL32.dll KERNELBASE.dll apphelp.dll AcLayers.DLL SspiCli.dll msvcrt.dll RPCRT4.dll CRYPTBASE.dll sechost.dll USER32.dll GDI32.dll LPK.dll USP10.dll ADVAPI32.dll SHELL32.dll SHLWAPI.dll ole32.dll OLEAUT32.dll USERENV.dll profapi.dll MPR.dll IMM32.DLL MSCTF.dll mscoreei.dll clr.dll MSVCR110_CLR0400.dll WINHTTP.dll webio.dll VERSION.dll PSAPI.DLL mscorlib.ni.dll uxtheme.dll CRYPTSP.dll CRYPT32.dll MSASN1.dll rsaenh.dll msi.dll clrjit.dll nlssorting.dll System.ni.dll System.Drawing.ni.dll System.Windows.Forms.ni.dll comctl32.dll comctl32.dll System.Configuration.ni.dll System.Xml.ni.dll dwmapi.dll gdiplus.dll RpcRtRemote.dll SXS.DLL System.Core.ni.dll System.Xml.Linq.ni.dll System.Data.DataSetExtensions.ni.dll Microsoft.CSharp.ni.dll System.Data.ni.dll System.Data.dll WS2_32.dll NSI.dll System.Deployment.ni.dll sxs.dll SETUPAPI.dll CFGMGR32.dll DEVOBJ.dll propsys.dll" .Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); /*Получение списка DLL, загруженных процессом*/ public static List<ProcessModule> GetProcessDLLs(Process pr) { var coll = pr.Modules; List<ProcessModule> res = new List<ProcessModule>(); foreach (ProcessModule y in coll) { if (Path.GetExtension(y.FileName).ToLower().Contains("exe")) continue; if (Path.GetExtension(y.FileName).ToLower().Contains("drv")) continue; res.Add(y); } return res; } /*Вывод списка предположительно инжектированных DLL*/ public static string PrintInjectedDLLs(List<ProcessModule> dllList) { StringBuilder sb = new StringBuilder(1000); sb.AppendLine("*** Injected DLLs: ***"); sb.AppendLine(); string s; foreach (var x in dllList) { if (DllWhiteList.Contains(x.ModuleName)) continue; sb.Append(x.ModuleName); try { s = x.FileVersionInfo.CompanyName; } catch (Exception ex) { s = ex.GetType().ToString(); Debug.WriteLine(ex.ToString()); } sb.Append(" by " + s); sb.AppendLine(); } return sb.ToString(); } }
}

Конечно, настоящие вредоносные программы могут перехватывать вызовы API-функций на обнаружение своих DLL. Но некоторые инжектированные модули такой способ позволяет обнаружить.

Опираясь на это,хочу получить список dll, которые использует каждый процесс.
Я использую stl list, где каждая нода имеет вот такие поля:

struct info
{ wchar_t name_PID[100]; // the name of process //maybe DWORD not unsigned unsigned num_PID; // the id of the proc char* path;// the way like C:// char par_name[100]; // the name of parent unsigned num_par_PID;// the id of the parent char ownerName[256]="\0"; // host/adm/elv char* SID; unsigned x_bit; //bool DEP; char DEP[4]; //bool ASLR; char ASLR[4];
};

На момент выполнения этой функции(которая ищет dll у процесса), все представленные поля заполнены.
Вот так выглядит вариант из документации Microsoft:

int main( void )
{ DWORD aProcesses[1024]; DWORD cbNeeded; DWORD cProcesses; unsigned int i; // Get the list of process identifiers. if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) ) return 1; // Calculate how many process identifiers were returned. cProcesses = cbNeeded / sizeof(DWORD); // Print the names of the modules for each process. for ( i = 0; i < cProcesses; i++ ) { PrintModules( aProcesses[i] ); } return 0;
}

По аналогии и я пишу свою функцию:

void about_dll(list <info>& gqlist)
{ list <info> ::iterator lit; DWORD* mass; DWORD cbNeeded, cProcesses; mass = (DWORD*)malloc(1*sizeof(DWORD)); int i = 0; for (lit = gqlist.begin(); lit != gqlist.end(); ++lit) { mass[i] = lit->num_PID; mass = (DWORD*)realloc(mass, sizeof(DWORD)*(i + 2)); i++; } int k = 0; EnumProcesses(mass, sizeof(mass)*i, &cbNeeded); // Calculate how many process identifiers were returned. cProcesses = cbNeeded / sizeof(DWORD); for (i = 0; i < cProcesses; i++) { //PrintModules(aProcesses[i]); about_dll2(mass[i]); }
}

Я пробегаю по каждому элементу списка, чтобы записать в новый массив mass(который динамический) номер процесса. Это нужно для функции EnumProcesses, которая после исполнения изменит содержимое cbNeeded. Его я использую, для цикла (ровно как и в примере).

В документации вызывают из цикла функцию(в их примере из сайта):

int PrintModules( DWORD processID )
{ HMODULE hMods[1024]; HANDLE hProcess; DWORD cbNeeded; unsigned int i; // Print the process identifier. printf( "\nProcess ID: %u\n", processID ); // Get a handle to the process. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); if (NULL == hProcess) return 1; // Get a list of all the modules in this process. if( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { for ( i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) { TCHAR szModName[MAX_PATH]; // Get the full path to the module's file. if ( GetModuleFileNameEx( hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { // Print the module name and handle value. _tprintf( TEXT("\t%s (0x%08X)\n"), szModName, hMods[i] ); } } } // Release the handle to the process. CloseHandle( hProcess ); return 0;
}

Теперь и я вызываю about_dll2 каждый раз из цикла.

void about_dll2(DWORD processID)
{ HMODULE hMods[1024]; HANDLE hProcess; DWORD cbNeeded; unsigned int i; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, processID); // Get a list of all the modules in this process. if (NULL == hProcess) return; //GetModuleHandleExA(NULL, "chrome.exe", hMods); if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) { TCHAR szModName[MAX_PATH]; // Get the full path to the module's file. if (GetModuleFileNameEx(hProcess, hMods[i], szModName,sizeof(szModName) / sizeof(TCHAR))) { // Print the module name and handle value. _tprintf(TEXT("\t%s (0x%08X)\n"), szModName, hMods[i]); } } }
}

Но возникает несколько трудностей:

  1. Не всегда hProcess получает валидное значение – соответственно завершаю функцию на этой итерации.
  2. if( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) никогда не выполняется.
:/>  Как отключить Защитник Windows 10 навсегда: 6 эффективных способов

Последнее и есть причина моего вопроса – почему эта функция в if возвращает 0?
Я подумал, что дело может быть в неинициализированном hMods. Поэтому захотел инициализировать её(неудачно). Мне показалось, что GetModuleHandleExA должен иницилизировать этот hMods, но вышло так , что приходится самому называть эту dll, а я наоборот хочу узнать dll.

Таким образом, более общий вопрос задать так: Как узнать, какие dll использует процесс?
А поконкретнее – описал выше. Может я вообще не в ту сторону иду с описанной выше идеей?

Всегда пользовался для получения списка загруженных определенным процессом DLL следующими механизмами:

Первое что пришло в голову:

 comment(lib, ) comment(lib, ) <stdio.h> <windows.h>
GetPages(DWORD PID)
{ MEMORY_BASIC_INFORMATION mbi; DWORD CountForPages; BOOL BAddrOK, FreeOK, ReservedOK; DWORD size = 0; CHAR buf[255]; HANDLE p; (p = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, PID)) { (CountForPages = 0; VirtualQueryEx(p, (LPVOID)(CountForPages + 1), &mbi,(MEMORY_BASIC_INFORMATION)); CountForPages = (DWORD)mbi.BaseAddress + mbi.RegionSize + 1) { BAddrOK = (DWORD)mbi.RegionSize; FreeOK = mbi.State != MEM_FREE; ReservedOK = mbi.State != MEM_RESERVE; (BAddrOK && FreeOK && ReservedOK) { GetModuleFileName((HMODULE)mbi.BaseAddress, (LPSTR)&buf, (buf)); printf("%0.8X - %s\n", mbi.BaseAddress, buf); size += mbi.RegionSize; } } CloseHandle(p); }
}
main()
{ GetPages(GetCurrentProcessId());
}

00400000 — X:\WORK\!PROGRAMMING\cpp\test.exe
00401000 — X:\WORK\!PROGRAMMING\cpp\test.exe
00407000 — X:\WORK\!PROGRAMMING\cpp\test.exe
00409000 — X:\WORK\!PROGRAMMING\cpp\test.exe
7C800000 — C:\WINDOWS\system32\kernel32.dll
7C801000 — C:\WINDOWS\system32\kernel32.dll
7C802000 — C:\WINDOWS\system32\kernel32.dll
7C803000 — C:\WINDOWS\system32\kernel32.dll
7C884000 — C:\WINDOWS\system32\kernel32.dll
7C885000 — C:\WINDOWS\system32\kernel32.dll
7C888000 — C:\WINDOWS\system32\kernel32.dll
7C88A000 — C:\WINDOWS\system32\kernel32.dll
7C900000 — C:\WINDOWS\system32\ntdll.dll
7C901000 — C:\WINDOWS\system32\ntdll.dll
7C97B000 — C:\WINDOWS\system32\ntdll.dll
7C97E000 — C:\WINDOWS\system32\ntdll.dll
7C980000 — C:\WINDOWS\system32\ntdll.dll
7F6F0000 — C:\WINDOWS\system32\ntdll.dll
7FFB0000 — C:\WINDOWS\system32\ntdll.dll
7FFDE000 — C:\WINDOWS\system32\ntdll.dll
7FFDF000 — C:\WINDOWS\system32\ntdll.dll
7FFE0000 — C:\WINDOWS\system32\ntdll.dll

Прямого системного вызова нет для функции EnumProcessModules. EnumProcessModules вызывает NtQueryInformationProcess. А вот у NtQueryInformationProcess есть номер системного вызова.

.7C90D7E0| mov eax,00000009A
.7C90D7E5: mov edx,07FFE0300 ; KiFastSystemCallRet
.7C90D7EA: d,[]
.7C90D7EC: 00014

P>3) Не пробовал — прямой системный вызов EnumProcessModules через sysenter

здесь

 я предлагал вариант через GetModuleFileName.

Здравствуйте, McQwerty, Вы писали:

GetModuleFileName ищет данные в PED.Ldr, то есть от авторского варианта 2) этот отличается только документированностью и худьшей скоростью (за счет перебора).

People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth

Module32First/Next. В MSDN есть пример

Приветствую всех, последние две недели я помогал в  разработке чита для одной из популярных игр на Unity. Но админы использовали «античит» программы для получения всех dll игры, и конечно отображалась и наша dll с читом которая была заинжекчена. Одна из моих задач была обойти античит и скрыть заинжектированную dll. Я хоть и сталкивался в работе с низкоуровневым программированием, но много вещей не понимал, и в результате столкнулся с множеством проблем.

По началу я решил выполнить поиск всех модулей в процессе вот таким образом:

Но он находил все dll за исключением инжектированных.

Перерыв множество статей в поиске решения проблемы, я решил использовать слепок процесса, как говорилось, этим методо можно получить весь список dll спомощью winapi. И я его испытал, готового решения не было, пришлось переписывать код с C++, в результате получилось:

К сожалению он вывел тот список что и первый вариант все стандартные dll, а мне все таких надо было найти dll которая была мною инжектирована.

Я приступил к тестированию античита, да бы понять его принцип работы. И обнаружил что он инжектил свою dll в процесс, и получал по всей видимости список модуле. Однако античит был на Delphi и разобраться с ним полностью мне не удалось. Я так же решил использовать инъекцию для получения всех сборок путем рефлексии. Для этого я написал код под Unity который выводит список всех, модулей, и к моему удивлению, я нашел и свою инжектированную dll. И так я приведу пример получения всех сборок под C# приложение, думаю вам без труда составит его перенести под Unity , я не стал его сюда приводить, слишком он большой.

Надеюсь мои начинания помогут вам, избежать тех ошибок с которыми столкнулся я, и с экономить ваше время.


Получить список dll процесса.

Мне необходимо получить список всех DLL любого процесса (64/32 бит)c 32-разрядного приложения.

Классический метод с MSDN не проходит:

 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); if (hProcess) { HMODULE hMod = NULL; DWORD cbNeeded = 0; if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { //do something here... } }

EnumProcessModules() выдает 299 ошибку. Тоже самое и для EnumProcessModulesEx().

Кто, что посоветует?


Re: Получить список dll процесса.

Здравствуйте, Vicul, Вы писали:

V>Кто, что посоветует?

If this function is called from a 32-bit application running on WOW64, it can only enumerate the modules of a 32-bit process. If the process is a 64-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).

This function is intended primarily for 64-bit applications. If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option is ignored and the function provides the same results as the EnumProcessModules function.

Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.

:/>  Как активировать Windows 7 ключом | Советы прохожего 3

У меня вопрос был не по функции EnumProcessModules(), а вообще, реально ли получить список длл любого процесса с 32-битного апп.

Здравствуйте, Vicul, Вы писали:

V>У меня вопрос был не по функции EnumProcessModules(), а вообще, реально ли получить список длл любого процесса с 32-битного апп.

Нет ничего невозможного, но мне представляется, что проще будет запускать 64-битный процесс-помошник и получать эти списки через него.

Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.

VTT>Нет ничего невозможного, но мне представляется, что проще будет запускать 64-битный процесс-помошник и получать эти списки через него.

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

VTT>>Нет ничего невозможного, но мне представляется, что проще будет запускать 64-битный процесс-помошник и получать эти списки через него.
V>Тоже вариант, но не удобно, я пишу библиотеку, а к ней привязывать дополнительный экзешник не хотелось бы.
rundll32 спешит на помощь. Будет дополнительная дллСписок процессов dll и получить список загруженных в данный момент dll в процессе

Маленький вопрос остается : а как данные-то перебросить? В этой DLL же все 64-битное, а вернуть надо в 32-битный процесс ?

With best regards
Pavel Dvorkin

PD>Маленький вопрос остается : а как данные-то перебросить? В этой DLL же все 64-битное, а вернуть надо в 32-битный процесс ?
Адресное пространство у них одно и тоже. Просто для 64х битного кода оно длиннее, но по адресам, влезающим в 32 бита — лежат одни и теже страницы, а 64хбитный адрес еще выделить нужно постараться (в нынешних виндах все что выше 4ГБ в wow64 процессах — резервируется, ибо нефиг). Так что с этим проблем вообще никаких нет.

Подождите ...

Wait...

Пока на собственное сообщение не было ответов, его можно удалить.

PD>Маленький вопрос остается : а как данные-то перебросить? В этой DLL же все 64-битное, а вернуть надо в 32-битный процесс ?
Адресное пространство у них одно и тоже. Просто для 64х битного кода оно длиннее, но по адресам, влезающим в 32 бита — лежат одни и теже страницы, а 64хбитный адрес еще выделить нужно постараться (в нынешних виндах все что выше 4ГБ в wow64 процессах — резервируется, ибо нефиг). Так что с этим проблем вообще никаких нет.

Здравствуйте, Vicul, Вы писали:

V>У меня вопрос был не по функции EnumProcessModules(), а вообще, реально ли получить список длл любого процесса с 32-битного апп.

Нет ничего невозможного, но мне представляется, что проще будет запускать 64-битный процесс-помошник и получать эти списки через него.

Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.


Получить список dll процесса.

Мне необходимо получить список всех DLL любого процесса (64/32 бит)c 32-разрядного приложения.

Классический метод с MSDN не проходит:

 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid); if (hProcess) { HMODULE hMod = NULL; DWORD cbNeeded = 0; if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { //do something here... } }

EnumProcessModules() выдает 299 ошибку. Тоже самое и для EnumProcessModulesEx().

Кто, что посоветует?


Re: Получить список dll процесса.

Здравствуйте, Vicul, Вы писали:

V>Кто, что посоветует?

If this function is called from a 32-bit application running on WOW64, it can only enumerate the modules of a 32-bit process. If the process is a 64-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).

This function is intended primarily for 64-bit applications. If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option is ignored and the function provides the same results as the EnumProcessModules function.

Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.

У меня вопрос был не по функции EnumProcessModules(), а вообще, реально ли получить список длл любого процесса с 32-битного апп.

VTT>Нет ничего невозможного, но мне представляется, что проще будет запускать 64-битный процесс-помошник и получать эти списки через него.

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

VTT>>Нет ничего невозможного, но мне представляется, что проще будет запускать 64-битный процесс-помошник и получать эти списки через него.
V>Тоже вариант, но не удобно, я пишу библиотеку, а к ней привязывать дополнительный экзешник не хотелось бы.
rundll32 спешит на помощь. Будет дополнительная дллСписок процессов dll и получить список загруженных в данный момент dll в процессе

Маленький вопрос остается : а как данные-то перебросить? В этой DLL же все 64-битное, а вернуть надо в 32-битный процесс ?

With best regards
Pavel Dvorkin

Подождите ...

Wait...

Пока на собственное сообщение не было ответов, его можно удалить.

#include <stdlib.h>
#include <tchar.h>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include <windows.h>
#define NT_SUCCESS(x) ((x) >= 0)
// Namespace is present Not to collide with "winbase.h"
// definition of PROCESS_INFORMATION_CLASS and others.
namespace sys
{
typedef enum _PROCESS_INFORMATION_CLASS { ProcessBasicInformation, ProcessQuotaLimits, ProcessIoCounters, ProcessVmCounters, ProcessTimes, ProcessBasePriority, ProcessRaisePriority, ProcessDebugPort, ProcessExceptionPort, ProcessAccessToken, ProcessLdtInformation, ProcessLdtSize, ProcessDefaultHardErrorMode, ProcessIoPortHandlers, ProcessPooledUsageAndLimits, ProcessWorkingSetWatch, ProcessUserModeIOPL, ProcessEnableAlignmentFaultFixup, ProcessPriorityClass, ProcessWx86Information, ProcessHandleCount, ProcessAffinityMask, ProcessPriorityBoost, MaxProcessInfoClass
} PROCESS_INFORMATION_CLASS, *PPROCESS_INFORMATION_CLASS;
// ------------------------------------------------------------------------
// Structs.
// ------------------------------------------------------------------------
typedef struct _PROCESS_BASIC_INFORMATION64 { ULONGLONG Reserved1; ULONGLONG PebBaseAddress; ULONGLONG Reserved2[2]; ULONGLONG UniqueProcessId; ULONGLONG Reserved3;
} PROCESS_BASIC_INFORMATION64;
typedef struct _PEB_LDR_DATA64 { ULONG Length; BOOLEAN Initialized; ULONGLONG SsHandle; LIST_ENTRY64 InLoadOrderModuleList; LIST_ENTRY64 InMemoryOrderModuleList; LIST_ENTRY64 InInitializationOrderModuleList;
} PEB_LDR_DATA64, *PPEB_LDR_DATA64;
// Structure is cut down to ProcessHeap.
typedef struct _PEB64 { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; BOOLEAN Spare; ULONGLONG Mutant; ULONGLONG ImageBaseAddress; ULONGLONG LoaderData; ULONGLONG ProcessParameters; ULONGLONG SubSystemData; ULONGLONG ProcessHeap;
} PEB64;
typedef struct _UNICODE_STRING64 { USHORT Length; USHORT MaximumLength; ULONGLONG Buffer;
} UNICODE_STRING64;
typedef struct _LDR_DATA_TABLE_ENTRY64 { LIST_ENTRY64 InLoadOrderModuleList; LIST_ENTRY64 InMemoryOrderModuleList; LIST_ENTRY64 InInitializationOrderModuleList; ULONGLONG BaseAddress; ULONGLONG EntryPoint; DWORD64 SizeOfImage; UNICODE_STRING64 FullDllName; UNICODE_STRING64 BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; LIST_ENTRY64 HashTableEntry; ULONGLONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;
} // namespace sys
// ------------------------------------------------------------------------
// Function prototypes.
// ------------------------------------------------------------------------
typedef NTSTATUS(NTAPI *_NtWow64QueryInformationProcess64)( IN HANDLE ProcessHandle, ULONG ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL);
typedef NTSTATUS(NTAPI *_NtWow64ReadVirtualMemory64)( IN HANDLE ProcessHandle, IN DWORD64 BaseAddress, OUT PVOID Buffer, IN ULONG64 Size, OUT PDWORD64 NumberOfBytesRead);
//Functions to print error messages
void ErrorMes(LPTSTR lpszFunction,WCHAR* buf)
{ // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); // Print the error message lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); swprintf(buf,L"%s returned error 0x%x: %s", lpszFunction, dw, lpMsgBuf); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf);
}
void RaiseError(LPTSTR lpszFunction){ WCHAR buf[1024]={0}; ErrorMes(lpszFunction,buf); std::wcout << buf << std::endl; throw std::exception("WINAPI call failed");
}
#ifndef WIN32
# error "This application must be built as an x86 executable"
#endif
#define GET_FUNC_ADDR(name) _##name name = (_##name)::GetProcAddress(::GetModuleHandleA("ntdll.dll"), #name)
#define CHECK_ERROR(clause, func) if (!(clause)) { RaiseError(func); }
namespace
{
struct close_on_exit
{ close_on_exit(HANDLE ptr) : ptr_(ptr) { }; ~close_on_exit() { if (ptr_) { ::CloseHandle(ptr_); ptr_ = nullptr; } }
private: HANDLE ptr_;
};
// Create C++ string based on unicode data
std::wstring CreateString(std::vector<uint8_t> &raw_bytes)
{ std::vector<uint16_t> unicode(raw_bytes.size() >> 1, 0); memcpy(unicode.data(), raw_bytes.data(), raw_bytes.size()); const std::wstring wide_string(unicode.begin(), unicode.end()); return wide_string;
}
//Open process handle by its ID
void *get_handle(uint32_t id)
{ HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, id); CHECK_ERROR(NULL != handle, L"OpenProcess"); return handle;
}
void check_if_process_is_x64(HANDLE handle)
{ BOOL is_wow64_process = TRUE; CHECK_ERROR(::IsWow64Process(handle, &is_wow64_process), L"IsWow64Process"); if(FALSE != is_wow64_process) throw std::exception( "Target process is not x64 one");
}
std::vector<uint8_t> read_mem(HANDLE handle, uint64_t address, uint32_t length)
{ if(handle == NULL)throw std::exception("Process handle is null"); std::vector<uint8_t> data(length, 0); GET_FUNC_ADDR(NtWow64ReadVirtualMemory64); NTSTATUS status = NtWow64ReadVirtualMemory64(handle, address, data.data(), data.size(), FALSE); if(!NT_SUCCESS(status)){ std::wcout << L"NtWow64ReadVirtualMemory64 status: " << status <<std::endl; throw std::exception("NT API call failed"); } return data;
}
void read_pbi(HANDLE handle, sys::PROCESS_BASIC_INFORMATION64 &pbi)
{ if(handle == NULL)throw std::exception("Process handle is null"); GET_FUNC_ADDR(NtWow64QueryInformationProcess64); NTSTATUS status = NtWow64QueryInformationProcess64(handle, sys::ProcessBasicInformation, &pbi, sizeof(pbi), NULL); if(!NT_SUCCESS(status)){ std::wcout << L"NtQueryInformationProcess status: " << status << std::endl; throw std::exception("NT API call failed"); }
}
std::vector<uint8_t> read_peb_data(HANDLE handle)
{ sys::PROCESS_BASIC_INFORMATION64 pbi = { 0 }; read_pbi(handle, pbi); return read_mem(handle, pbi.PebBaseAddress, sizeof(sys::PEB64));
}
//Prints 64-bit process modules
bool get_modules_load_order_via_peb(HANDLE handle)
{ std::vector<uint8_t> read_peb = read_peb_data(handle); sys::PEB64 *peb = (sys::PEB64 *)read_peb.data(); // ------------------------------------------------------------------------ // Read memory from pointer to loader data structures. // ------------------------------------------------------------------------ std::vector<uint8_t> read_peb_ldr_data = read_mem(handle, (uintptr_t)peb->LoaderData, sizeof(sys::PEB_LDR_DATA64)); sys::PEB_LDR_DATA64 *peb_ldr_data = (sys::PEB_LDR_DATA64 *)read_peb_ldr_data.data(); sys::PEB_LDR_DATA64 *loader_data = (sys::PEB_LDR_DATA64 *)peb->LoaderData; const uintptr_t addr_of_ptr_to_first_ldr_module = (uintptr_t)loader_data + ((uintptr_t)&loader_data->InLoadOrderModuleList - (uintptr_t)&loader_data->Length); ULONGLONG address = peb_ldr_data->InLoadOrderModuleList.Flink; uint32_t counter = 1; // ------------------------------------------------------------------------ // Traversing loader data structures. // ------------------------------------------------------------------------ do { std::vector<uint8_t> read_ldr_table_entry = read_mem(handle, address, sizeof(sys::LDR_DATA_TABLE_ENTRY64)); sys::LDR_DATA_TABLE_ENTRY64 *ldr_table_entry = (sys::LDR_DATA_TABLE_ENTRY64 *)read_ldr_table_entry.data(); std::vector<uint8_t> unicode_name = read_mem(handle, ldr_table_entry->BaseDllName.Buffer, ldr_table_entry->BaseDllName.MaximumLength); std::wstring name = CreateString(unicode_name); std::wcout << name << std::endl; ldr_table_entry = (sys::LDR_DATA_TABLE_ENTRY64 *)read_ldr_table_entry.data(); address = (uintptr_t)ldr_table_entry->InLoadOrderModuleList.Flink; } while (addr_of_ptr_to_first_ldr_module != address); return true;
}
} // namespace
/********************************************************************************/
int _tmain(int argc, _TCHAR* argv[])
{ try { HANDLE handle = get_handle(2564); close_on_exit auto_close_handle(handle); check_if_process_is_x64(handle); std::wcout << L"------------- Modules: ------------------------\n"; get_modules_load_order_via_peb(handle); std::wcout << L"-----------------------------------------------\n"; } catch (const std::exception &e) { std::wcout << L"\n----------------------------------------------------\n"; std::wcout << "Exception occurred: " << e.what(); std::wcout << L"\n----------------------------------------------------\n"; } return 0;
}

Getting list of DLLs with Process Explorer¶

Viewing the list of currently loaded DLLs

⚠️ After starting Process Explorer select the process or application that you want to inspect. We have used notepad++.exe in this example:

:/>  «Эффективное управление цифровыми рабочими пространствами в Windows 10 с помощью интуитивно понятной навигации по виртуальному рабочему столу с помощью сочетаний клавиш и специальных горячих клавиш»
Список процессов dll и получить список загруженных в данный момент dll в процессе
Список процессов dll и получить список загруженных в данный момент dll в процессе

⚠️ Now the lower pane view is visible; it lists the DLLs loaded by the selected process only!

Список процессов dll и получить список загруженных в данный момент dll в процессе

Saving the list of DLLs of the selected process

Список процессов dll и получить список загруженных в данный момент dll в процессе

The resulting file starts with the process list and after that it lists the selected process’s DLLs:

[...]
Process: notepad++.exe Pid: 148
Name	Description	Company Name	Version
{AFBF9F1A-8EE8-4C77-AF34-C647E37CA0D9}.1.ver0x000000000000001f.db
advapi32.dll	Erweiterte Windows 32 Base-API	Microsoft Corporation	6.1.7601.17514
apisetschema.dll	ApiSet Schema DLL	Microsoft Corporation	6.1.7600.16385
apphelp.dll	Clientbibliothek für Anwendungskompatibilität	Microsoft Corporation	6.1.7601.17514
cfgmgr32.dll	Configuration Manager DLL	Microsoft Corporation	6.1.7601.17514
[...]

Getting list of DLLs with ListDLLs¶

The command line tool
ListDLLs
from Microsoft can also list the DLLs loaded by a process:

C:\Users\myuser>listdlls notepad.exe
ListDLLs v3.1 - List loaded DLLs
Copyright (C) 1997-2011 Mark Russinovich
Sysinternals - www.sysinternals.com
------------------------------------------------------------------------------
notepad.exe pid: 7972
Command line: "C:\Windows\system32\notepad.exe"
Base Size Path
0x00000000ff880000 0x35000 C:\Windows\system32\notepad.exe
0x0000000077000000 0x1a9000 C:\Windows\SYSTEM32\ntdll.dll
0x0000000076ee0000 0x11f000 C:\Windows\system32\kernel32.dll
0x00000000fd720000 0x6c000 C:\Windows\system32\KERNELBASE.dll
0x00000000fed20000 0xdb000 C:\Windows\system32\ADVAPI32.dll
0x00000000fd830000 0x9f000 C:\Windows\system32\msvcrt.dll
0x00000000fdb10000 0x1f000 C:\Windows\SYSTEM32\sechost.dll
0x00000000ff0d0000 0x12d000 C:\Windows\system32\RPCRT4.dll
0x00000000ff200000 0x67000 C:\Windows\system32\GDI32.dll
0x0000000076920000 0xfa000 C:\Windows\system32\USER32.dll
0x00000000feeb0000 0xe000 C:\Windows\system32\LPK.dll
0x00000000fd9e0000 0xc9000 C:\Windows\system32\USP10.dll
0x00000000ff270000 0x97000 C:\Windows\system32\COMDLG32.dll
0x00000000fee30000 0x71000 C:\Windows\system32\SHLWAPI.dll
0x00000000fbcc0000 0x1f4000 C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.17514_none_fa396087175ac9ac\COMCTL32.dll
0x00000000fdf90000 0xd88000 C:\Windows\system32\SHELL32.dll
0x00000000f8c50000 0x71000 C:\Windows\system32\WINSPOOL.DRV
0x00000000feec0000 0x203000 C:\Windows\system32\ole32.dll
0x00000000fdcd0000 0xd7000 C:\Windows\system32\OLEAUT32.dll
0x00000000fc3b0000 0xc000 C:\Windows\system32\VERSION.dll
0x00000000fee00000 0x2e000 C:\Windows\system32\IMM32.DLL
0x00000000fd8d0000 0x109000 C:\Windows\system32\MSCTF.dll
0x00000000fd390000 0xf000 C:\Windows\system32\CRYPTBASE.dll
0x00000000fb9d0000 0x56000 C:\Windows\system32\uxtheme.dll
0x00000000fb5d0000 0x18000 C:\Windows\system32\dwmapi.dll

Saving the list of DLLs

For this standard I/O redirection can be used:

listdlls notepad.exe >temp.txt

The output of the command is then contained in the file temp.txt.