Skip to content

Commit 386f89d

Browse files
committed
Almost only-RPM module enumeration
1 parent 6bb863d commit 386f89d

File tree

1 file changed

+142
-59
lines changed

1 file changed

+142
-59
lines changed
Lines changed: 142 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,108 @@
11
#include <windows.h>
2+
#include <winternl.h>
23
#include <tlhelp32.h>
34
#include <vector>
45
#include <algorithm>
56

67
#include "NativeCore.hpp"
78

9+
template <typename Proc>
10+
static DWORD EnumerateRemoteModulesNative(HANDLE process, Proc proc)
11+
{
12+
const auto ntdll = GetModuleHandle(TEXT("ntdll"));
13+
if (!ntdll)
14+
return ERROR_MOD_NOT_FOUND;
15+
16+
using tRtlNtStatusToDosError = ULONG (NTAPI *)(
17+
_In_ NTSTATUS Status
18+
);
19+
const auto _RtlNtStatusToDosError = tRtlNtStatusToDosError(GetProcAddress(ntdll, "RtlNtStatusToDosError"));
20+
if (!_RtlNtStatusToDosError)
21+
return ERROR_NOT_FOUND;
22+
23+
using tNtQueryInformationProcess = NTSTATUS (NTAPI *)(
24+
_In_ HANDLE ProcessHandle,
25+
_In_ PROCESSINFOCLASS ProcessInformationClass,
26+
_Out_writes_bytes_(ProcessInformationLength) PVOID ProcessInformation,
27+
_In_ ULONG ProcessInformationLength,
28+
_Out_opt_ PULONG ReturnLength
29+
);
30+
31+
const auto _NtQueryInformationProcess = tNtQueryInformationProcess(GetProcAddress(ntdll, "NtQueryInformationProcess"));
32+
if (!_NtQueryInformationProcess)
33+
return ERROR_NOT_FOUND;
34+
35+
PROCESS_BASIC_INFORMATION pbi;
36+
const auto status = _NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), nullptr);
37+
if (!NT_SUCCESS(status))
38+
return _RtlNtStatusToDosError(status);
39+
40+
PPEB_LDR_DATA ldr;
41+
auto success = ReadRemoteMemory(process, &pbi.PebBaseAddress->Ldr, &ldr, 0, sizeof(ldr));
42+
if (!success)
43+
return ERROR_READ_FAULT; // we seem to swallow the error anyways, might aswell give a distinctive one back
44+
45+
const auto list_head = &ldr->InMemoryOrderModuleList; // remote address
46+
PLIST_ENTRY list_current; // remote address
47+
success = ReadRemoteMemory(process, &list_head->Flink, &list_current, 0, sizeof(list_current));
48+
if (!success)
49+
return ERROR_READ_FAULT;
50+
51+
while (list_current != list_head)
52+
{
53+
// TODO: error handling - what do we do if module list changed? We can't un-call the callback
54+
55+
LDR_DATA_TABLE_ENTRY mod;
56+
success = ReadRemoteMemory(process, CONTAINING_RECORD(list_current, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks), &mod, 0, sizeof(mod));
57+
if (!success)
58+
return ERROR_SUCCESS; // return success here to prevent running the other one
59+
60+
EnumerateRemoteModuleData data = {};
61+
data.BaseAddress = mod.DllBase;
62+
data.Size = *(ULONG*)&mod.Reserved2[1]; // instead of undocced member could read ImageSize from headers
63+
const auto path_len = std::min(sizeof(RC_UnicodeChar) * (PATH_MAXIMUM_LENGTH - 1), size_t(mod.FullDllName.Length));
64+
success = ReadRemoteMemory(process, mod.FullDllName.Buffer, data.Path, 0, int(path_len));
65+
if (!success)
66+
return ERROR_SUCCESS; // return success here to prevent running the other one
67+
68+
// UNICODE_STRING is not guaranteed to be null terminated
69+
data.Path[path_len / 2] = 0;
70+
71+
proc(&data);
72+
73+
list_current = mod.InMemoryOrderLinks.Flink;
74+
}
75+
76+
return ERROR_SUCCESS;
77+
}
78+
79+
template <typename Proc>
80+
static DWORD EnumerateRemoteModulesWinapi(HANDLE process, Proc proc)
81+
{
82+
const auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(process));
83+
if (handle == INVALID_HANDLE_VALUE)
84+
return GetLastError();
85+
86+
MODULEENTRY32W me32 = {};
87+
me32.dwSize = sizeof(MODULEENTRY32W);
88+
if (Module32FirstW(handle, &me32))
89+
{
90+
do
91+
{
92+
EnumerateRemoteModuleData data = {};
93+
data.BaseAddress = me32.modBaseAddr;
94+
data.Size = me32.modBaseSize;
95+
std::memcpy(data.Path, me32.szExePath, std::min(MAX_PATH, PATH_MAXIMUM_LENGTH));
96+
97+
proc(&data);
98+
} while (Module32NextW(handle, &me32));
99+
}
100+
101+
CloseHandle(handle);
102+
103+
return ERROR_SUCCESS;
104+
}
105+
8106
void RC_CallConv EnumerateRemoteSectionsAndModules(RC_Pointer process, EnumerateRemoteSectionsCallback callbackSection, EnumerateRemoteModulesCallback callbackModule)
9107
{
10108
if (callbackSection == nullptr && callbackModule == nullptr)
@@ -55,82 +153,67 @@ void RC_CallConv EnumerateRemoteSectionsAndModules(RC_Pointer process, Enumerate
55153
address = reinterpret_cast<size_t>(memInfo.BaseAddress) + memInfo.RegionSize;
56154
}
57155

58-
const auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(process));
59-
if (handle != INVALID_HANDLE_VALUE)
156+
const auto moduleEnumerator = [&](EnumerateRemoteModuleData* data)
60157
{
61-
MODULEENTRY32W me32 = {};
62-
me32.dwSize = sizeof(MODULEENTRY32W);
63-
if (Module32FirstW(handle, &me32))
158+
if (callbackModule != nullptr)
159+
callbackModule(data);
160+
161+
if (callbackSection != nullptr)
64162
{
65-
do
66-
{
67-
if (callbackModule != nullptr)
163+
auto it = std::lower_bound(std::begin(sections), std::end(sections), static_cast<LPVOID>(data->BaseAddress), [&sections](const auto& lhs, const LPVOID& rhs)
68164
{
69-
EnumerateRemoteModuleData data = {};
70-
data.BaseAddress = me32.modBaseAddr;
71-
data.Size = me32.modBaseSize;
72-
std::memcpy(data.Path, me32.szExePath, std::min(MAX_PATH, PATH_MAXIMUM_LENGTH));
73-
74-
callbackModule(&data);
75-
}
165+
return lhs.BaseAddress < rhs;
166+
});
76167

77-
if (callbackSection != nullptr)
78-
{
79-
auto it = std::lower_bound(std::begin(sections), std::end(sections), static_cast<LPVOID>(me32.modBaseAddr), [&sections](const auto& lhs, const LPVOID& rhs)
80-
{
81-
return lhs.BaseAddress < rhs;
82-
});
168+
IMAGE_DOS_HEADER DosHdr = {};
169+
IMAGE_NT_HEADERS NtHdr = {};
83170

84-
IMAGE_DOS_HEADER DosHdr = {};
85-
IMAGE_NT_HEADERS NtHdr = {};
171+
ReadRemoteMemory(process, data->BaseAddress, &DosHdr, 0, sizeof(IMAGE_DOS_HEADER));
172+
ReadRemoteMemory(process, PUCHAR(data->BaseAddress) + DosHdr.e_lfanew, &NtHdr, 0, sizeof(IMAGE_NT_HEADERS));
86173

87-
ReadRemoteMemory(process, me32.modBaseAddr, &DosHdr, 0, sizeof(IMAGE_DOS_HEADER));
88-
ReadRemoteMemory(process, me32.modBaseAddr + DosHdr.e_lfanew, &NtHdr, 0, sizeof(IMAGE_NT_HEADERS));
174+
std::vector<IMAGE_SECTION_HEADER> sectionHeaders(NtHdr.FileHeader.NumberOfSections);
175+
ReadRemoteMemory(process, PUCHAR(data->BaseAddress) + DosHdr.e_lfanew + sizeof(IMAGE_NT_HEADERS), sectionHeaders.data(), 0, NtHdr.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
176+
for (auto i = 0; i < NtHdr.FileHeader.NumberOfSections; ++i)
177+
{
178+
auto&& sectionHeader = sectionHeaders[i];
89179

90-
std::vector<IMAGE_SECTION_HEADER> sectionHeaders(NtHdr.FileHeader.NumberOfSections);
91-
ReadRemoteMemory(process, me32.modBaseAddr + DosHdr.e_lfanew + sizeof(IMAGE_NT_HEADERS), sectionHeaders.data(), 0, NtHdr.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
92-
for (auto i = 0; i < NtHdr.FileHeader.NumberOfSections; ++i)
180+
const auto sectionAddress = reinterpret_cast<size_t>(data->BaseAddress) + sectionHeader.VirtualAddress;
181+
for (auto j = it; j != std::end(sections); ++j)
182+
{
183+
if (sectionAddress >= reinterpret_cast<size_t>(j->BaseAddress) && sectionAddress < reinterpret_cast<size_t>(j->BaseAddress) + static_cast<size_t>(j->Size))
93184
{
94-
auto&& sectionHeader = sectionHeaders[i];
185+
// Copy the name because it is not null padded.
186+
char buffer[IMAGE_SIZEOF_SHORT_NAME + 1] = { 0 };
187+
std::memcpy(buffer, sectionHeader.Name, IMAGE_SIZEOF_SHORT_NAME);
95188

96-
const auto sectionAddress = reinterpret_cast<size_t>(me32.modBaseAddr) + sectionHeader.VirtualAddress;
97-
for (auto j = it; j != std::end(sections); ++j)
189+
if (std::strcmp(buffer, ".text") == 0 || std::strcmp(buffer, "code") == 0)
190+
{
191+
j->Category = SectionCategory::CODE;
192+
}
193+
else if (std::strcmp(buffer, ".data") == 0 || std::strcmp(buffer, "data") == 0 || std::strcmp(buffer, ".rdata") == 0 || std::strcmp(buffer, ".idata") == 0)
98194
{
99-
if (sectionAddress >= reinterpret_cast<size_t>(j->BaseAddress) && sectionAddress < reinterpret_cast<size_t>(j->BaseAddress) + static_cast<size_t>(j->Size))
100-
{
101-
// Copy the name because it is not null padded.
102-
char buffer[IMAGE_SIZEOF_SHORT_NAME + 1] = { 0 };
103-
std::memcpy(buffer, sectionHeader.Name, IMAGE_SIZEOF_SHORT_NAME);
104-
105-
if (std::strcmp(buffer, ".text") == 0 || std::strcmp(buffer, "code") == 0)
106-
{
107-
j->Category = SectionCategory::CODE;
108-
}
109-
else if (std::strcmp(buffer, ".data") == 0 || std::strcmp(buffer, "data") == 0 || std::strcmp(buffer, ".rdata") == 0 || std::strcmp(buffer, ".idata") == 0)
110-
{
111-
j->Category = SectionCategory::DATA;
112-
}
113-
114-
MultiByteToUnicode(buffer, j->Name, IMAGE_SIZEOF_SHORT_NAME);
115-
std::memcpy(j->ModulePath, me32.szExePath, std::min(MAX_PATH, PATH_MAXIMUM_LENGTH));
116-
117-
break;
118-
}
195+
j->Category = SectionCategory::DATA;
119196
}
120197

198+
MultiByteToUnicode(buffer, j->Name, IMAGE_SIZEOF_SHORT_NAME);
199+
std::memcpy(j->ModulePath, data->Path, std::min(MAX_PATH, PATH_MAXIMUM_LENGTH));
200+
201+
break;
121202
}
122203
}
123-
} while (Module32NextW(handle, &me32));
124-
}
125204

126-
CloseHandle(handle);
205+
}
206+
}
207+
};
208+
209+
if(EnumerateRemoteModulesNative(process, moduleEnumerator) != ERROR_SUCCESS)
210+
EnumerateRemoteModulesWinapi(process, moduleEnumerator);
127211

128-
if (callbackSection != nullptr)
212+
if (callbackSection != nullptr)
213+
{
214+
for (auto&& section : sections)
129215
{
130-
for (auto&& section : sections)
131-
{
132-
callbackSection(&section);
133-
}
216+
callbackSection(&section);
134217
}
135218
}
136219
}

0 commit comments

Comments
 (0)