Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Injector.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=HMODULE/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=LUID/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=MBCS/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nefarius/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wstr/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=wstring/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
86 changes: 55 additions & 31 deletions Injector/Injector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <Windows.h>
#include <TlHelp32.h>
#include <Psapi.h>
#include <Shlwapi.h>
#include <malloc.h>
#include <Tchar.h>

Expand Down Expand Up @@ -46,6 +47,19 @@ bool Injector::icompare(const std::wstring& a, const std::wstring& b) const

// Check if a module is injected via process handle, and return the base address
BYTE* Injector::GetModuleBaseAddress(HANDLE Process, const std::wstring& Path) {
// Canonicalize the search path so it can be compared reliably against
// the canonical paths returned by GetModuleFileNameExW.
WCHAR CanonicalPath[MAX_PATH];
std::wstring SearchPath = Path;
if (GetFullPathNameW(Path.c_str(), MAX_PATH, CanonicalPath, NULL))
SearchPath = CanonicalPath;

// Extract the filename portion for comparison against module base names
std::wstring FileName = SearchPath;
auto LastSep = FileName.rfind(L'\\');
if (LastSep != std::wstring::npos)
FileName = FileName.substr(LastSep + 1);

// Grab a new snapshot of the process
std::vector<HMODULE> Modules;
DWORD SizeNeeded = 0;
Expand All @@ -57,7 +71,6 @@ BYTE* Injector::GetModuleBaseAddress(HANDLE Process, const std::wstring& Path) {
} while (SizeNeeded > Modules.size() * sizeof(HMODULE));

// Get the HMODULE of the desired library
bool Found = false;
for (const auto &Module : Modules)
{
WCHAR ModuleName[MAX_PATH];
Expand All @@ -68,8 +81,7 @@ BYTE* Injector::GetModuleBaseAddress(HANDLE Process, const std::wstring& Path) {
// The size of the ExePath buffer, in characters.
if (!GetModuleFileNameExW(Process, Module, ExePath, sizeof(ExePath) / sizeof(WCHAR)))
throw std::runtime_error("Could not get ExePath.");
Found = (icompare(ModuleName, Path) || icompare(ExePath, Path));
if (Found)
if (icompare(ModuleName, FileName) || icompare(ExePath, SearchPath))
return reinterpret_cast<BYTE*>(Module);
}
return nullptr;
Expand Down Expand Up @@ -236,42 +248,54 @@ void Injector::GetSeDebugPrivilege()
throw std::runtime_error("Could not adjust token privileges.");
}

// Get fully qualified path from module name. Assumes base directory is the
// directory of the currently executing binary.
// Resolve and validate a module path. Handles both relative and absolute paths.
// Relative paths are resolved against the CWD first, then against the injector's
// own directory as a fallback for backward compatibility.
std::tstring Injector::GetPath( const std::tstring& ModuleName )
{
// Get handle to self
HMODULE Self = GetModuleHandle(NULL);

// Get path to loader
std::vector<TCHAR> LoaderPath(MAX_PATH);
if (!GetModuleFileName(Self,&LoaderPath[0],static_cast<DWORD>(LoaderPath.size())) ||
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
throw std::runtime_error("Could not get path to loader.");

// Convert path to loader to path to module
std::tstring ModulePath(&LoaderPath[0]);
ModulePath = ModulePath.substr(0, ModulePath.rfind( _T("\\") ) + 1);
ModulePath.append(ModuleName);

TCHAR FullModulePath[MAX_PATH];
if (!GetFullPathName(ModulePath.c_str(), sizeof(FullModulePath) / sizeof(TCHAR), FullModulePath, NULL))
TCHAR FullPath[MAX_PATH];

// First: canonicalize the input as-is. For absolute paths this normalizes
// separators and resolves . / .. components. For relative paths this
// resolves against the current working directory.
if (!GetFullPathName(ModuleName.c_str(), MAX_PATH, FullPath, NULL))
throw std::runtime_error("Could not get full path to module.");
ModulePath = std::tstring(&FullModulePath[0]);

// Check path/file is valid
if (GetFileAttributes(ModulePath.c_str()) == INVALID_FILE_ATTRIBUTES)
std::tstring ModulePath(FullPath);

if (GetFileAttributes(ModulePath.c_str()) != INVALID_FILE_ATTRIBUTES)
return ModulePath;

// If the file wasn't found and the original input was relative, try
// resolving it relative to the injector executable's directory.
if (PathIsRelative(ModuleName.c_str()))
{
std::vector<TCHAR> LoaderPath(MAX_PATH);
const DWORD LoaderPathLen = GetModuleFileName(NULL, &LoaderPath[0], static_cast<DWORD>(LoaderPath.size()));
if (LoaderPathLen == 0)
throw std::runtime_error("Could not get path to loader.");
if (LoaderPathLen >= static_cast<DWORD>(LoaderPath.size()))
throw std::runtime_error("Path to loader exceeds MAX_PATH and was truncated.");

std::tstring LoaderDir(&LoaderPath[0]);
LoaderDir = LoaderDir.substr(0, LoaderDir.rfind(_T("\\")) + 1);
LoaderDir.append(ModuleName);

if (!GetFullPathName(LoaderDir.c_str(), MAX_PATH, FullPath, NULL))
throw std::runtime_error("Could not get full path to module.");

ModulePath = std::tstring(FullPath);

if (GetFileAttributes(ModulePath.c_str()) != INVALID_FILE_ATTRIBUTES)
return ModulePath;
}

#ifdef _UNICODE
std::string NarrowModulePath(ConvertWideToANSI(ModulePath));
std::string NarrowModulePath(ConvertWideToANSI(ModulePath));
#else
std::string NarrowModulePath(ModulePath.begin(), ModulePath.end());
std::string NarrowModulePath(ModulePath.begin(), ModulePath.end());
#endif
throw std::runtime_error("Could not find module. Path: '" + NarrowModulePath + "'.");
}

// Return module path
return ModulePath;
throw std::runtime_error("Could not find module. Path: '" + NarrowModulePath + "'.");
}

// Get process ID via name
Expand Down
30 changes: 8 additions & 22 deletions Injector/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <Windows.h>
#include <tchar.h>
#include <TlHelp32.h>
#include <Shlwapi.h>

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

// Injector version number
const std::tstring VerNum(_T("20240218"));
const std::tstring VerNum(_T("20260228"));

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

argh::parser cmdl;

Expand Down Expand Up @@ -150,14 +149,7 @@ int main(int, char* argv[])
{
for (auto& mod : modules)
{
if (PathIsRelative(mod.c_str()))
{
ModulePath = Injector::Get()->GetPath(mod);
}
else
{
ModulePath = mod;
}
ModulePath = Injector::Get()->GetPath(mod);

// Inject module
Injector::Get()->InjectLib(ProcID, ModulePath);
Expand All @@ -172,17 +164,11 @@ int main(int, char* argv[])
{
for (auto& mod : modules)
{
if (PathIsRelative(mod.c_str()))
{
ModulePath = Injector::Get()->GetPath(mod);
}
else
{
ModulePath = mod;
}

// Eject module
Injector::Get()->EjectLib(ProcID, ModulePath);
// No file-existence check: the module is already loaded in the
// target process and may no longer exist on disk. EjectLib uses
// GetModuleBaseAddress which canonicalizes the path and matches
// against both the module basename and full path.
Injector::Get()->EjectLib(ProcID, mod);
// If we get to this point then no exceptions have been thrown so we
// assume success.
std::tcout << "Successfully ejected module!" << std::endl;
Expand Down
Loading