|
1 | 1 | #include <windows.h> |
| 2 | +#include <winternl.h> |
2 | 3 | #include <tlhelp32.h> |
3 | 4 | #include <vector> |
4 | 5 | #include <algorithm> |
5 | 6 |
|
6 | 7 | #include "NativeCore.hpp" |
7 | 8 |
|
| 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 | + |
8 | 106 | void RC_CallConv EnumerateRemoteSectionsAndModules(RC_Pointer process, EnumerateRemoteSectionsCallback callbackSection, EnumerateRemoteModulesCallback callbackModule) |
9 | 107 | { |
10 | 108 | if (callbackSection == nullptr && callbackModule == nullptr) |
@@ -55,82 +153,67 @@ void RC_CallConv EnumerateRemoteSectionsAndModules(RC_Pointer process, Enumerate |
55 | 153 | address = reinterpret_cast<size_t>(memInfo.BaseAddress) + memInfo.RegionSize; |
56 | 154 | } |
57 | 155 |
|
58 | | - const auto handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetProcessId(process)); |
59 | | - if (handle != INVALID_HANDLE_VALUE) |
| 156 | + const auto moduleEnumerator = [&](EnumerateRemoteModuleData* data) |
60 | 157 | { |
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) |
64 | 162 | { |
65 | | - do |
66 | | - { |
67 | | - if (callbackModule != nullptr) |
| 163 | + auto it = std::lower_bound(std::begin(sections), std::end(sections), static_cast<LPVOID>(data->BaseAddress), [§ions](const auto& lhs, const LPVOID& rhs) |
68 | 164 | { |
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 | + }); |
76 | 167 |
|
77 | | - if (callbackSection != nullptr) |
78 | | - { |
79 | | - auto it = std::lower_bound(std::begin(sections), std::end(sections), static_cast<LPVOID>(me32.modBaseAddr), [§ions](const auto& lhs, const LPVOID& rhs) |
80 | | - { |
81 | | - return lhs.BaseAddress < rhs; |
82 | | - }); |
| 168 | + IMAGE_DOS_HEADER DosHdr = {}; |
| 169 | + IMAGE_NT_HEADERS NtHdr = {}; |
83 | 170 |
|
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)); |
86 | 173 |
|
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]; |
89 | 179 |
|
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)) |
93 | 184 | { |
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); |
95 | 188 |
|
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) |
98 | 194 | { |
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; |
119 | 196 | } |
120 | 197 |
|
| 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; |
121 | 202 | } |
122 | 203 | } |
123 | | - } while (Module32NextW(handle, &me32)); |
124 | | - } |
125 | 204 |
|
126 | | - CloseHandle(handle); |
| 205 | + } |
| 206 | + } |
| 207 | + }; |
| 208 | + |
| 209 | + if(EnumerateRemoteModulesNative(process, moduleEnumerator) != ERROR_SUCCESS) |
| 210 | + EnumerateRemoteModulesWinapi(process, moduleEnumerator); |
127 | 211 |
|
128 | | - if (callbackSection != nullptr) |
| 212 | + if (callbackSection != nullptr) |
| 213 | + { |
| 214 | + for (auto&& section : sections) |
129 | 215 | { |
130 | | - for (auto&& section : sections) |
131 | | - { |
132 | | - callbackSection(§ion); |
133 | | - } |
| 216 | + callbackSection(§ion); |
134 | 217 | } |
135 | 218 | } |
136 | 219 | } |
0 commit comments