22#include < Windows.h>
33#include < TlHelp32.h>
44#include < Psapi.h>
5+ #include < Shlwapi.h>
56#include < malloc.h>
67#include < Tchar.h>
78
@@ -46,6 +47,19 @@ bool Injector::icompare(const std::wstring& a, const std::wstring& b) const
4647
4748// Check if a module is injected via process handle, and return the base address
4849BYTE* Injector::GetModuleBaseAddress (HANDLE Process, const std::wstring& Path) {
50+ // Canonicalize the search path so it can be compared reliably against
51+ // the canonical paths returned by GetModuleFileNameExW.
52+ WCHAR CanonicalPath[MAX_PATH];
53+ std::wstring SearchPath = Path;
54+ if (GetFullPathNameW (Path.c_str (), MAX_PATH, CanonicalPath, NULL ))
55+ SearchPath = CanonicalPath;
56+
57+ // Extract the filename portion for comparison against module base names
58+ std::wstring FileName = SearchPath;
59+ auto LastSep = FileName.rfind (L' \\ ' );
60+ if (LastSep != std::wstring::npos)
61+ FileName = FileName.substr (LastSep + 1 );
62+
4963 // Grab a new snapshot of the process
5064 std::vector<HMODULE> Modules;
5165 DWORD SizeNeeded = 0 ;
@@ -57,7 +71,6 @@ BYTE* Injector::GetModuleBaseAddress(HANDLE Process, const std::wstring& Path) {
5771 } while (SizeNeeded > Modules.size () * sizeof (HMODULE));
5872
5973 // Get the HMODULE of the desired library
60- bool Found = false ;
6174 for (const auto &Module : Modules)
6275 {
6376 WCHAR ModuleName[MAX_PATH];
@@ -68,8 +81,7 @@ BYTE* Injector::GetModuleBaseAddress(HANDLE Process, const std::wstring& Path) {
6881 // The size of the ExePath buffer, in characters.
6982 if (!GetModuleFileNameExW (Process, Module, ExePath, sizeof (ExePath) / sizeof (WCHAR)))
7083 throw std::runtime_error (" Could not get ExePath." );
71- Found = (icompare (ModuleName, Path) || icompare (ExePath, Path));
72- if (Found)
84+ if (icompare (ModuleName, FileName) || icompare (ExePath, SearchPath))
7385 return reinterpret_cast <BYTE*>(Module);
7486 }
7587 return nullptr ;
@@ -236,42 +248,54 @@ void Injector::GetSeDebugPrivilege()
236248 throw std::runtime_error (" Could not adjust token privileges." );
237249}
238250
239- // Get fully qualified path from module name. Assumes base directory is the
240- // directory of the currently executing binary.
251+ // Resolve and validate a module path. Handles both relative and absolute paths.
252+ // Relative paths are resolved against the CWD first, then against the injector's
253+ // own directory as a fallback for backward compatibility.
241254std::tstring Injector::GetPath ( const std::tstring& ModuleName )
242255{
243- // Get handle to self
244- HMODULE Self = GetModuleHandle (NULL );
245-
246- // Get path to loader
247- std::vector<TCHAR> LoaderPath (MAX_PATH);
248- if (!GetModuleFileName (Self,&LoaderPath[0 ],static_cast <DWORD>(LoaderPath.size ())) ||
249- GetLastError () == ERROR_INSUFFICIENT_BUFFER)
250- throw std::runtime_error (" Could not get path to loader." );
251-
252- // Convert path to loader to path to module
253- std::tstring ModulePath (&LoaderPath[0 ]);
254- ModulePath = ModulePath.substr (0 , ModulePath.rfind ( _T (" \\ " ) ) + 1 );
255- ModulePath.append (ModuleName);
256-
257- TCHAR FullModulePath[MAX_PATH];
258- if (!GetFullPathName (ModulePath.c_str (), sizeof (FullModulePath) / sizeof (TCHAR), FullModulePath, NULL ))
256+ TCHAR FullPath[MAX_PATH];
257+
258+ // First: canonicalize the input as-is. For absolute paths this normalizes
259+ // separators and resolves . / .. components. For relative paths this
260+ // resolves against the current working directory.
261+ if (!GetFullPathName (ModuleName.c_str (), MAX_PATH, FullPath, NULL ))
259262 throw std::runtime_error (" Could not get full path to module." );
260- ModulePath = std::tstring (&FullModulePath[0 ]);
261263
262- // Check path/file is valid
263- if (GetFileAttributes (ModulePath.c_str ()) == INVALID_FILE_ATTRIBUTES)
264+ std::tstring ModulePath (FullPath);
265+
266+ if (GetFileAttributes (ModulePath.c_str ()) != INVALID_FILE_ATTRIBUTES)
267+ return ModulePath;
268+
269+ // If the file wasn't found and the original input was relative, try
270+ // resolving it relative to the injector executable's directory.
271+ if (PathIsRelative (ModuleName.c_str ()))
264272 {
273+ std::vector<TCHAR> LoaderPath (MAX_PATH);
274+ const DWORD LoaderPathLen = GetModuleFileName (NULL , &LoaderPath[0 ], static_cast <DWORD>(LoaderPath.size ()));
275+ if (LoaderPathLen == 0 )
276+ throw std::runtime_error (" Could not get path to loader." );
277+ if (LoaderPathLen >= static_cast <DWORD>(LoaderPath.size ()))
278+ throw std::runtime_error (" Path to loader exceeds MAX_PATH and was truncated." );
279+
280+ std::tstring LoaderDir (&LoaderPath[0 ]);
281+ LoaderDir = LoaderDir.substr (0 , LoaderDir.rfind (_T (" \\ " )) + 1 );
282+ LoaderDir.append (ModuleName);
283+
284+ if (!GetFullPathName (LoaderDir.c_str (), MAX_PATH, FullPath, NULL ))
285+ throw std::runtime_error (" Could not get full path to module." );
286+
287+ ModulePath = std::tstring (FullPath);
288+
289+ if (GetFileAttributes (ModulePath.c_str ()) != INVALID_FILE_ATTRIBUTES)
290+ return ModulePath;
291+ }
292+
265293#ifdef _UNICODE
266- std::string NarrowModulePath (ConvertWideToANSI (ModulePath));
294+ std::string NarrowModulePath (ConvertWideToANSI (ModulePath));
267295#else
268- std::string NarrowModulePath (ModulePath.begin (), ModulePath.end ());
296+ std::string NarrowModulePath (ModulePath.begin (), ModulePath.end ());
269297#endif
270- throw std::runtime_error (" Could not find module. Path: '" + NarrowModulePath + " '." );
271- }
272-
273- // Return module path
274- return ModulePath;
298+ throw std::runtime_error (" Could not find module. Path: '" + NarrowModulePath + " '." );
275299}
276300
277301// Get process ID via name
0 commit comments