#include #include #include #include #include #include #include #include #include "../src/public.h" #define PSAPI_VERSION 2 #include #include "proc.h" using common::memory::UniqueHandle; struct ProcessInfo { DWORD ProcessId; DWORD ParentProcessId; FILETIME CreationTime; std::wstring DevicePath; }; FILETIME GetProcessCreationTime(HANDLE processHandle) { FILETIME creationTime, dummy; const auto status = GetProcessTimes(processHandle, &creationTime, &dummy, &dummy, &dummy); if (FALSE == status) { THROW_WINDOWS_ERROR(GetLastError(), "GetProcessTimes"); } return creationTime; } std::wstring GetProcessDevicePath(HANDLE processHandle) { size_t bufferSize = 512; std::vector buffer; for (;;) { buffer.resize(bufferSize); const auto charsWritten = K32GetProcessImageFileNameW(processHandle, &buffer[0], static_cast(buffer.size())); if (0 == charsWritten) { if (ERROR_INSUFFICIENT_BUFFER == GetLastError()) { bufferSize *= 2; continue; } THROW_WINDOWS_ERROR(GetLastError(), "K32GetProcessImageFileNameW"); } // // K32GetProcessImageFileNameW writes a null terminator // but doesn't account for it in the return value. // return std::wstring(&buffer[0], &buffer[0] + charsWritten); } } // // CompileProcessInfo() // // Returns a set including all processes in the system. // // The return value uses the vector container type since it's perceived // the set will not be searched. // std::vector CompileProcessInfo() { auto snapshot = UniqueHandle(new HANDLE( (HANDLE)CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0))); if (INVALID_HANDLE_VALUE == *snapshot) { THROW_WINDOWS_ERROR(GetLastError(), "Snapshot processes"); } PROCESSENTRY32W processEntry { .dwSize = sizeof(PROCESSENTRY32W) }; if (FALSE == Process32First(*snapshot, &processEntry)) { THROW_WINDOWS_ERROR(GetLastError(), "Initiate process enumeration"); } std::map processes; // // Discover all processes. // do { auto handle = UniqueHandle(new HANDLE(OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processEntry.th32ProcessID))); if (NULL == *handle) { //THROW_WINDOWS_ERROR(GetLastError(), "Open process"); continue; } ProcessInfo pi; pi.ProcessId = processEntry.th32ProcessID; pi.ParentProcessId = processEntry.th32ParentProcessID; try { pi.CreationTime = GetProcessCreationTime(*handle); } catch (...) { pi.CreationTime = { 0 }; } try { pi.DevicePath = GetProcessDevicePath(*handle); } catch (...) { // // Including a process without a path might seem useless. // But it enables ancestor discovery. // pi.DevicePath = L""; } processes.insert(std::make_pair(pi.ProcessId, pi)); } while (FALSE != Process32NextW(*snapshot, &processEntry)); // // Find instances of PID recycling. // // This can be done by checking the creation time of the parent // process and discovering that the age of the claimed parent process // is lower than that of the child process. // for (auto& [pid, process] : processes) { auto parentIter = processes.find(process.ParentProcessId); if (parentIter != processes.end()) { ULARGE_INTEGER parentTime { .LowPart = parentIter->second.CreationTime.dwLowDateTime, .HighPart = parentIter->second.CreationTime.dwHighDateTime }; ULARGE_INTEGER processTime { .LowPart = process.CreationTime.dwLowDateTime, .HighPart = process.CreationTime.dwHighDateTime }; if (0 != parentTime.QuadPart && parentTime.QuadPart < processTime.QuadPart) { continue; } } process.ParentProcessId = 0; } // // Store process records into vector. // std::vector output; output.reserve(processes.size()); std::transform(processes.begin(), processes.end(), std::back_inserter(output), [](const std::map::value_type &entry) { return entry.second; }); return output; } // // MakeHandle() // // For some reason a PID is of type HANDLE in the kernel. // Casting to HANDLE, which is a pointer type, requires some sourcery. // HANDLE MakeHandle(DWORD h) { return reinterpret_cast(static_cast(h)); } std::vector PackageProcessInfo(const std::vector &processes) { if (processes.empty()) { THROW_ERROR("Invalid set of processes (empty set)"); } // // Determine required byte length for string buffer. // size_t stringBufferLength = 0; for (const auto &process : processes) { stringBufferLength += (process.DevicePath.size() * sizeof(wchar_t)); } size_t bufferLength = sizeof(ST_PROCESS_DISCOVERY_HEADER) + (sizeof(ST_PROCESS_DISCOVERY_ENTRY) * processes.size()) + stringBufferLength; std::vector buffer(bufferLength); // // Create pointers to various buffer areas. // auto header = reinterpret_cast(&buffer[0]); auto entry = reinterpret_cast(header + 1); auto stringBuffer = reinterpret_cast(entry + processes.size()); // // Serialize into buffer. // SIZE_T stringOffset = 0; for (const auto &process : processes) { entry->ProcessId = MakeHandle(process.ProcessId); entry->ParentProcessId = MakeHandle(process.ParentProcessId); if (process.DevicePath.empty()) { entry->ImageNameOffset = 0; entry->ImageNameLength = 0; } else { const auto imageNameLength = process.DevicePath.size() * sizeof(wchar_t); entry->ImageNameOffset = stringOffset; entry->ImageNameLength = static_cast(imageNameLength); RtlCopyMemory(stringBuffer + stringOffset, &process.DevicePath[0], imageNameLength); stringOffset += imageNameLength; } ++entry; } // // Finalize header. // header->NumEntries = processes.size(); header->TotalLength = bufferLength; return buffer; } std::vector BuildRegisterProcessesPayload() { return PackageProcessInfo(CompileProcessInfo()); }