| 205 | } |
| 206 | |
| 207 | InjectStatus InjectLibrary(uint32_t pid, const std::filesystem::path& libraryPath) { |
| 208 | Windows::UniqueHandle process(pid ? OpenProcess(kInjectAccess, FALSE, pid) : nullptr); |
| 209 | if (!process) { |
| 210 | OSTP_LOG_WARN("OpenProcess(pid={}, access=0x{:X}) failed (error={})", pid, kInjectAccess, GetLastError()); |
| 211 | return InjectStatus::OpenProcessFailed; |
| 212 | } |
| 213 | |
| 214 | const Architecture architecture = DetectArchitecture(process.get()); |
| 215 | if (architecture == Architecture::Unknown) { |
| 216 | OSTP_LOG_WARN("InjectLibrary(pid={}): target architecture is unknown", pid); |
| 217 | return InjectStatus::UnknownArchitecture; |
| 218 | } |
| 219 | |
| 220 | LPTHREAD_START_ROUTINE loadLibrary = ResolveRemoteLoadLibraryW(pid, architecture); |
| 221 | if (!loadLibrary) { |
| 222 | return InjectStatus::ResolveLoadLibraryFailed; |
| 223 | } |
| 224 | |
| 225 | const std::wstring nativePath = libraryPath.wstring(); |
| 226 | const size_t byteSize = (nativePath.size() + 1) * sizeof(wchar_t); |
| 227 | void* remotePath = VirtualAllocEx(process.get(), nullptr, byteSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); |
| 228 | if (!remotePath) { |
| 229 | OSTP_LOG_WARN("VirtualAllocEx(size={}) failed (error={})", byteSize, GetLastError()); |
| 230 | return InjectStatus::AllocFailed; |
| 231 | } |
| 232 | // Free the remote path on every exit path. It must outlive the remote |
| 233 | // thread, which reads it before LoadLibraryW returns; function scope (after |
| 234 | // the WaitForSingleObject below) satisfies that. |
| 235 | Windows::ScopeExit freeRemotePath([&] { |
| 236 | VirtualFreeEx(process.get(), remotePath, 0, MEM_RELEASE); |
| 237 | }); |
| 238 | |
| 239 | SIZE_T written = 0; |
| 240 | if (!WriteProcessMemory(process.get(), remotePath, nativePath.c_str(), byteSize, &written) || |
| 241 | written != byteSize) { |
| 242 | OSTP_LOG_WARN("WriteProcessMemory(size={}) failed (written={}, error={})", |
| 243 | byteSize, static_cast<size_t>(written), GetLastError()); |
| 244 | return InjectStatus::WriteFailed; |
| 245 | } |
| 246 | |
| 247 | Windows::UniqueHandle thread( |
| 248 | CreateRemoteThread(process.get(), nullptr, 0, loadLibrary, remotePath, 0, nullptr)); |
| 249 | if (!thread) { |
| 250 | OSTP_LOG_WARN("CreateRemoteThread failed (error={})", GetLastError()); |
| 251 | return InjectStatus::CreateThreadFailed; |
| 252 | } |
| 253 | |
| 254 | const DWORD wait = WaitForSingleObject(thread.get(), kRemoteLoadTimeoutMs); |
| 255 | if (wait != WAIT_OBJECT_0) { |
| 256 | if (wait == WAIT_FAILED) { |
| 257 | OSTP_LOG_WARN("WaitForSingleObject(remote thread) failed (error={})", GetLastError()); |
| 258 | } else if (wait == WAIT_TIMEOUT) { |
| 259 | OSTP_LOG_WARN("WaitForSingleObject(remote thread) timed out after {} ms", kRemoteLoadTimeoutMs); |
| 260 | } else { |
| 261 | OSTP_LOG_WARN("WaitForSingleObject(remote thread) returned unexpected status {}", wait); |
| 262 | } |
| 263 | return InjectStatus::WaitFailed; |
| 264 | } |
no test coverage detected