Skip to content

Commit 9ab9ebf

Browse files
authored
Merge pull request #36 from nefarius/path-handling-fix
Path building fixes
2 parents a67a67b + 75fdc60 commit 9ab9ebf

File tree

3 files changed

+64
-53
lines changed

3 files changed

+64
-53
lines changed

Injector.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
<s:Boolean x:Key="/Default/UserDictionary/Words/=HMODULE/@EntryIndexedValue">True</s:Boolean>
33
<s:Boolean x:Key="/Default/UserDictionary/Words/=LUID/@EntryIndexedValue">True</s:Boolean>
44
<s:Boolean x:Key="/Default/UserDictionary/Words/=MBCS/@EntryIndexedValue">True</s:Boolean>
5+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nefarius/@EntryIndexedValue">True</s:Boolean>
56
<s:Boolean x:Key="/Default/UserDictionary/Words/=wstr/@EntryIndexedValue">True</s:Boolean>
67
<s:Boolean x:Key="/Default/UserDictionary/Words/=wstring/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

Injector/Injector.cpp

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
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
4849
BYTE* 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.
241254
std::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

Injector/Main.cpp

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#include <Windows.h>
1010
#include <tchar.h>
1111
#include <TlHelp32.h>
12-
#include <Shlwapi.h>
1312

1413
// C++ Standard Library
1514
#include <iostream>
@@ -33,15 +32,15 @@ int main(int, char* argv[])
3332
SehGuard Guard;
3433

3534
// Injector version number
36-
const std::tstring VerNum(_T("20240218"));
35+
const std::tstring VerNum(_T("20260228"));
3736

3837
// Version and copyright output
3938
#ifdef _WIN64
4039
std::tcout << _T("Injector x64 [Version ") << VerNum << _T("]") << std::endl;
4140
#else
4241
std::tcout << _T("Injector x86 [Version ") << VerNum << _T("]") << std::endl;
4342
#endif
44-
std::tcout << _T("Copyright (c) 2009 Cypher, 2012-2024 Nefarius. All rights reserved.") << std::endl << std::endl;
43+
std::tcout << _T("Copyright (c) 2009 Cypher, 2012-2026 Nefarius. All rights reserved.") << std::endl << std::endl;
4544

4645
argh::parser cmdl;
4746

@@ -150,14 +149,7 @@ int main(int, char* argv[])
150149
{
151150
for (auto& mod : modules)
152151
{
153-
if (PathIsRelative(mod.c_str()))
154-
{
155-
ModulePath = Injector::Get()->GetPath(mod);
156-
}
157-
else
158-
{
159-
ModulePath = mod;
160-
}
152+
ModulePath = Injector::Get()->GetPath(mod);
161153

162154
// Inject module
163155
Injector::Get()->InjectLib(ProcID, ModulePath);
@@ -172,17 +164,11 @@ int main(int, char* argv[])
172164
{
173165
for (auto& mod : modules)
174166
{
175-
if (PathIsRelative(mod.c_str()))
176-
{
177-
ModulePath = Injector::Get()->GetPath(mod);
178-
}
179-
else
180-
{
181-
ModulePath = mod;
182-
}
183-
184-
// Eject module
185-
Injector::Get()->EjectLib(ProcID, ModulePath);
167+
// No file-existence check: the module is already loaded in the
168+
// target process and may no longer exist on disk. EjectLib uses
169+
// GetModuleBaseAddress which canonicalizes the path and matches
170+
// against both the module basename and full path.
171+
Injector::Get()->EjectLib(ProcID, mod);
186172
// If we get to this point then no exceptions have been thrown so we
187173
// assume success.
188174
std::tcout << "Successfully ejected module!" << std::endl;

0 commit comments

Comments
 (0)