Skip to content
Open
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
12 changes: 6 additions & 6 deletions Debugger.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<LanguageStandard>stdcpp20</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
<BufferSecurityCheck>true</BufferSecurityCheck>
<StringPooling>true</StringPooling>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
Expand All @@ -89,8 +89,8 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>.\Debug\Syringe.pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<DataExecutionPrevention>true</DataExecutionPrevention>
<TargetMachine>MachineX86</TargetMachine>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand Down Expand Up @@ -121,7 +121,7 @@
<AdditionalOptions>/Zc:threadSafeInit- /Zc:throwingNew /Gw %(AdditionalOptions)</AdditionalOptions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<LanguageStandard>stdcpp20</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
<BufferSecurityCheck>true</BufferSecurityCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)external\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
Expand All @@ -133,8 +133,8 @@
<OutputFile>.\Release\Syringe.exe</OutputFile>
<ProgramDatabaseFile>.\Release\Syringe.pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention />
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<DataExecutionPrevention>true</DataExecutionPrevention>
<TargetMachine>MachineX86</TargetMachine>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand Down
15 changes: 14 additions & 1 deletion Handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ struct VirtualMemoryHandle
{
if (process && size)
{
this->Value = VirtualAllocEx(process, address, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
this->Value = VirtualAllocEx(process, address, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
}
}

Expand Down Expand Up @@ -217,6 +217,19 @@ struct VirtualMemoryHandle
return static_cast<BYTE*>(this->Value);
}

bool protect(SIZE_T size, DWORD protection) const noexcept
{
DWORD oldProtection;
return this->Value && this->Process
&& VirtualProtectEx(this->Process, this->Value, size, protection, &oldProtection) != FALSE;
}

bool flush_instruction_cache(SIZE_T size) const noexcept
{
return this->Value && this->Process
&& FlushInstructionCache(this->Process, this->Value, size) != FALSE;
}

void clear() noexcept
{
VirtualMemoryHandle(std::move(*this));
Expand Down
12 changes: 6 additions & 6 deletions Syringe.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ Global
{DC2E7848-31D1-43EA-90D5-A5F1FB28E8AC}.Release|Mixed Platforms.Build.0 = Release|Win32
{DC2E7848-31D1-43EA-90D5-A5F1FB28E8AC}.Release|Win32.ActiveCfg = Release|Win32
{DC2E7848-31D1-43EA-90D5-A5F1FB28E8AC}.Release|Win32.Build.0 = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Any CPU.ActiveCfg = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Any CPU.Build.0 = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Mixed Platforms.ActiveCfg = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Mixed Platforms.Build.0 = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Win32.ActiveCfg = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Win32.Build.0 = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Any CPU.ActiveCfg = Debug|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Any CPU.Build.0 = Debug|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Win32.ActiveCfg = Debug|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Debug|Win32.Build.0 = Debug|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Release|Any CPU.ActiveCfg = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{A1B2C3D4-1234-5678-9ABC-DEF012345678}.Release|Mixed Platforms.Build.0 = Release|Win32
Expand Down
108 changes: 75 additions & 33 deletions SyringeDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
#include <memory>
#include <numeric>
#include <set>
#include <vector>

#include <DbgHelp.h>

using namespace std;

void SyringeDebugger::DebugProcess(std::string_view const arguments)
{
STARTUPINFO startupInfo{ sizeof(startupInfo) };
STARTUPINFO startupInfo{};
startupInfo.cb = sizeof(startupInfo);

SetEnvironmentVariable("_NO_DEBUG_HEAP", "1");

Expand All @@ -42,6 +44,12 @@ bool SyringeDebugger::PatchMem(void* address, void const* buffer, DWORD size)
return (WriteProcessMemory(workingHandle, address, buffer, size, nullptr) != FALSE);
}

bool SyringeDebugger::PatchCode(void* address, void const* buffer, DWORD size)
{
return PatchMem(address, buffer, size)
&& FlushInstructionCache(workingHandle, address, size) != FALSE;
}

bool SyringeDebugger::ReadMem(void const* address, void* buffer, DWORD size)
{
return (ReadProcessMemory(workingHandle, address, buffer, size, nullptr) != FALSE);
Expand All @@ -57,14 +65,23 @@ VirtualMemoryHandle SyringeDebugger::AllocMem(void* address, size_t size)
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}

void SyringeDebugger::MakeExecutable(VirtualMemoryHandle const& memory, size_t size)
{
if (!memory.protect(size, PAGE_EXECUTE_READ)
|| !memory.flush_instruction_cache(size))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}
}

bool SyringeDebugger::SetBP(void* address)
{
// save overwritten code and set INT 3
if (auto& opcode = Breakpoints[address].original_opcode; opcode == 0x00)
{
auto const buffer = INT3;
ReadMem(address, &opcode, 1);
return PatchMem(address, &buffer, 1);
return PatchCode(address, &buffer, 1);
}

return true;
Expand Down Expand Up @@ -352,14 +369,20 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
{
auto const buffer = INT3;
context.EFlags &= ~0x100;
PatchMem(threadInfo.lastBP, &buffer, 1);
if (!PatchCode(threadInfo.lastBP, &buffer, 1))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}
}

// load DLLs and retrieve proc addresses
if (!bDLLsLoaded)
{
// restore
PatchMem(exceptAddr, &Breakpoints[exceptAddr].original_opcode, 1);
if (!PatchCode(exceptAddr, &Breakpoints[exceptAddr].original_opcode, 1))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}

if (loop_LoadLibrary == v_AllHooks.end())
{
Expand Down Expand Up @@ -387,7 +410,7 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
PatchMem(&GetData()->LibName, hook->lib, MaxNameLength);
PatchMem(&GetData()->ProcName, hook->proc, MaxNameLength);

context.Eip = reinterpret_cast<DWORD>(&GetData()->LoadLibraryFunc);
context.Eip = reinterpret_cast<DWORD>(GetLoaderCode());
}
else
{
Expand All @@ -402,7 +425,7 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
PatchMem(&GetData()->LibName, entry.lib, MaxNameLength);
PatchMem(&GetData()->ProcName, entry.symbol, MaxNameLength);

context.Eip = reinterpret_cast<DWORD>(&GetData()->LoadLibraryFunc);
context.Eip = reinterpret_cast<DWORD>(GetLoaderCode());
}
else
{
Expand All @@ -425,7 +448,10 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
if (!bFeaturesSet)
{
// restore
PatchMem(exceptAddr, &Breakpoints[exceptAddr].original_opcode, 1);
if (!PatchCode(exceptAddr, &Breakpoints[exceptAddr].original_opcode, 1))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}

// read the resolved address of the feature flag in the target process
void* flagAddr = nullptr;
Expand Down Expand Up @@ -454,7 +480,7 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
PatchMem(&GetData()->LibName, entry.lib, MaxNameLength);
PatchMem(&GetData()->ProcName, entry.symbol, MaxNameLength);

context.Eip = reinterpret_cast<DWORD>(&GetData()->LoadLibraryFunc);
context.Eip = reinterpret_cast<DWORD>(GetLoaderCode());
}
else
{
Expand Down Expand Up @@ -610,7 +636,11 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
p_code += sizeof(jmp_back);

auto const actual_sz = static_cast<size_t>(p_code - code.data());
PatchMem(base, code.data(), actual_sz);
if (!PatchMem(base, code.data(), actual_sz))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}
MakeExecutable(it.second.p_caller_code, sz);

// dump
/*
Expand All @@ -637,7 +667,10 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
ApplyPatch(code.data(), jmp);
ApplyPatch(code.data() + 0x01, rel2);

PatchMem(p_original_code, code.data(), code.size());
if (!PatchCode(p_original_code, code.data(), static_cast<DWORD>(code.size())))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}
}

Log::Flush();
Expand All @@ -646,7 +679,10 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
}

// restore
PatchMem(exceptAddr, &Breakpoints[exceptAddr].original_opcode, 1);
if (!PatchCode(exceptAddr, &Breakpoints[exceptAddr].original_opcode, 1))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}

// single step mode
context.EFlags |= 0x100;
Expand All @@ -673,7 +709,10 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
{
auto const buffer = INT3;
auto const& threadInfo = Threads[dbgEvent.dwThreadId];
PatchMem(threadInfo.lastBP, &buffer, 1);
if (!PatchCode(threadInfo.lastBP, &buffer, 1))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}

HANDLE hThread = threadInfo.Thread;
CONTEXT context;
Expand Down Expand Up @@ -785,27 +824,19 @@ DWORD SyringeDebugger::HandleException(DEBUG_EVENT const& dbgEvent)
return DBG_EXCEPTION_NOT_HANDLED;
}

return DBG_CONTINUE;
}

void SyringeDebugger::Run(std::string_view const arguments)
{
constexpr auto AllocDataSize = sizeof(AllocData);

Log::WriteLine(
__FUNCTION__ ": Running process to debug. cmd = \"%s %.*s\"",
exe.c_str(), printable(arguments));
DebugProcess(arguments);

Log::WriteLine(__FUNCTION__ ": Allocating 0x%u bytes...", AllocDataSize);
pAlloc = AllocMem(nullptr, AllocDataSize);

Log::WriteLine(__FUNCTION__ ": pAlloc = 0x%08X", pAlloc.get());

// write DLL loader code
Log::WriteLine(__FUNCTION__ ": Writing DLL loader & caller code...");

static BYTE const cLoadLibrary[] = {
static constexpr BYTE cLoadLibrary[] = {
0x50, // push eax
0x51, // push ecx
0x52, // push edx
Expand All @@ -823,17 +854,22 @@ void SyringeDebugger::Run(std::string_view const arguments)
INT3, NOP // int3 and some padding
};

std::array<BYTE, AllocDataSize> data;
static_assert(AllocData::CodeSize >= sizeof(cLoadLibrary));
ApplyPatch(data.data(), cLoadLibrary);
ApplyPatch(data.data() + 0x04, &GetData()->LibName);
ApplyPatch(data.data() + 0x0A, pImLoadLibrary);
ApplyPatch(data.data() + 0x13, &GetData()->ProcName);
ApplyPatch(data.data() + 0x1A, pImGetProcAddress);
ApplyPatch(data.data() + 0x1F, &GetData()->ProcAddress);
PatchMem(pAlloc, data.data(), data.size());
auto code = std::to_array(cLoadLibrary);
pLoaderCode = AllocMem(nullptr, code.size());
pExchangeData = AllocMem(nullptr, sizeof(ExchangeData));

Log::WriteLine(__FUNCTION__ ": pcLoadLibrary = 0x%08X", &GetData()->LoadLibraryFunc);
ApplyPatch(code.data() + 0x04, &GetData()->LibName);
ApplyPatch(code.data() + 0x0A, pImLoadLibrary);
ApplyPatch(code.data() + 0x13, &GetData()->ProcName);
ApplyPatch(code.data() + 0x1A, pImGetProcAddress);
ApplyPatch(code.data() + 0x1F, &GetData()->ProcAddress);
if (!PatchMem(pLoaderCode, code.data(), code.size()))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}
MakeExecutable(pLoaderCode, code.size());

Log::WriteLine(__FUNCTION__ ": pcLoadLibrary = 0x%08X", GetLoaderCode());

// breakpoints for DLL loading and proc address retrieving
bDLLsLoaded = false;
Expand All @@ -842,7 +878,10 @@ void SyringeDebugger::Run(std::string_view const arguments)
loop_LoadLibrary = v_AllHooks.end();

// set breakpoint
SetBP(pcEntryPoint);
if (!SetBP(pcEntryPoint))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}

DEBUG_EVENT dbgEvent;
ResumeThread(pInfo.hThread);
Expand Down Expand Up @@ -945,7 +984,10 @@ void SyringeDebugger::RemoveBP(LPVOID const address, bool const restoreOpcode)
{
if (restoreOpcode)
{
PatchMem(address, &i->second.original_opcode, 1);
if (!PatchCode(address, &i->second.original_opcode, 1))
{
throw_lasterror_or(ERROR_ERRORS_ENCOUNTERED, exe);
}
}

Breakpoints.erase(i);
Expand Down
Loading
Loading