added files for platform API

This commit is contained in:
maxraulea 2026-06-16 15:40:52 +02:00
parent 2d3941a43e
commit 81b16de62b
7 changed files with 359 additions and 15 deletions

View file

@ -0,0 +1,145 @@
/**
* @file nt-list.h
* @author Max Raulea (max.raulea@gmail.com)
* @brief Cross-platform NT-style intrusive doubly-linked list helpers + CONTAINING_RECORD
* @details The shared debugger code uses the NT LIST_ENTRY API (InitializeListHead,
* InsertHeadList, RemoveEntryList, CONTAINING_RECORD, ...). On Windows these
* come from <windows.h> (or, under USE_NATIVE_SDK_HEADERS, from the in-tree
* platform/user/header/Windows.h). Platforms whose system headers don't ship
* them (Linux) get them here, so the shared code compiles unchanged.
*
* These operate on the exact LIST_ENTRY {Flink, Blink} layout defined in
* SDK/headers/DataTypes.h, which is part of the kernel<->user IOCTL ABI --
* that's why we mirror the NT list rather than reaching for sys/queue.h
* (different layout + a colliding LIST_ENTRY name).
*
* The body is compiled only where the OS headers don't already provide these
* (i.e. not on Windows), so including this unconditionally is safe.
*
* @version 0.1
* @date 2026-06-16
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#pragma once
#ifndef _WIN32
# include <stddef.h> // offsetof
//
// Given the address of a LIST_ENTRY field embedded in a struct, recover the
// address of the containing struct.
//
# ifndef CONTAINING_RECORD
# define CONTAINING_RECORD(address, type, field) \
((type *)((char *)(address) - offsetof(type, field)))
# endif
//
// Initialize a list head to the empty (points-to-self) state.
//
static inline void
InitializeListHead(PLIST_ENTRY ListHead)
{
ListHead->Flink = ListHead->Blink = ListHead;
}
//
// TRUE if the list has no entries.
//
static inline BOOLEAN
IsListEmpty(const LIST_ENTRY * ListHead)
{
return (BOOLEAN)(ListHead->Flink == ListHead);
}
//
// Unlink an entry. Returns TRUE if the list is now empty.
//
static inline BOOLEAN
RemoveEntryList(PLIST_ENTRY Entry)
{
PLIST_ENTRY Flink = Entry->Flink;
PLIST_ENTRY Blink = Entry->Blink;
Blink->Flink = Flink;
Flink->Blink = Blink;
return (BOOLEAN)(Flink == Blink);
}
//
// Unlink and return the first entry.
//
static inline PLIST_ENTRY
RemoveHeadList(PLIST_ENTRY ListHead)
{
PLIST_ENTRY Entry = ListHead->Flink;
PLIST_ENTRY Flink = Entry->Flink;
ListHead->Flink = Flink;
Flink->Blink = ListHead;
return Entry;
}
//
// Unlink and return the last entry.
//
static inline PLIST_ENTRY
RemoveTailList(PLIST_ENTRY ListHead)
{
PLIST_ENTRY Entry = ListHead->Blink;
PLIST_ENTRY Blink = Entry->Blink;
ListHead->Blink = Blink;
Blink->Flink = ListHead;
return Entry;
}
//
// Insert an entry at the tail of the list.
//
static inline void
InsertTailList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry)
{
PLIST_ENTRY Blink = ListHead->Blink;
Entry->Flink = ListHead;
Entry->Blink = Blink;
Blink->Flink = Entry;
ListHead->Blink = Entry;
}
//
// Insert an entry at the head of the list.
//
static inline void
InsertHeadList(PLIST_ENTRY ListHead, PLIST_ENTRY Entry)
{
PLIST_ENTRY Flink = ListHead->Flink;
Entry->Flink = Flink;
Entry->Blink = ListHead;
Flink->Blink = Entry;
ListHead->Flink = Entry;
}
//
// Splice the entries of ListToAppend onto the tail of ListHead.
//
static inline void
AppendTailList(PLIST_ENTRY ListHead, PLIST_ENTRY ListToAppend)
{
PLIST_ENTRY ListEnd = ListHead->Blink;
ListHead->Blink->Flink = ListToAppend;
ListHead->Blink = ListToAppend->Blink;
ListToAppend->Blink->Flink = ListHead;
ListToAppend->Blink = ListEnd;
}
#endif // !_WIN32

View file

@ -0,0 +1,137 @@
/**
* @file platform-signal.c
* @author Max Raulea (max.raulea@gmail.com)
* @brief User mode cross-platform implementation of the console-control handler
* @details See platform-signal.h. The Windows branch forwards to SetConsoleCtrlHandler.
* The Linux branch blocks the handled signals and dispatches them from a
* dedicated sigwait() thread. sigwait() (rather than a sigaction() handler)
* is used deliberately: BreakController calls ShowMessages (printf), socket
* I/O and Sleep, none of which are async-signal-safe. Running the handler on
* a normal thread woken by sigwait() sidesteps that entirely and mirrors the
* Windows model where the console-control handler also runs on its own thread.
*
* @version 0.1
* @date 2026-06-16
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
#if defined(__linux__)
# include "../header/platform-signal.h"
#endif // defined(__linux__)
#if defined(_WIN32)
BOOLEAN
PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler)
{
return (BOOLEAN)SetConsoleCtrlHandler((PHANDLER_ROUTINE)Handler, TRUE);
}
#elif defined(__linux__)
# include <signal.h>
# include <pthread.h>
//
// The installed handler, read by the dispatch thread.
//
static PLATFORM_CTRL_HANDLER g_PlatformCtrlHandler = NULL;
//
// Map a POSIX signal onto the Win32 console-control event the shared handler
// understands. Only the two interactive break signals are mapped:
// SIGINT (CTRL+C) -> CTRL_C_EVENT
// SIGQUIT (CTRL+\) -> CTRL_BREAK_EVENT
// SIGTERM/SIGHUP are intentionally left at their default disposition so the
// process stays killable/terminable the usual way; the CLOSE/LOGOFF/SHUTDOWN
// console events have no clean Linux analog.
//
static DWORD
PlatformMapSignalToCtrlType(int Signal)
{
switch (Signal)
{
case SIGINT:
return CTRL_C_EVENT;
case SIGQUIT:
return CTRL_BREAK_EVENT;
default:
return (DWORD)-1;
}
}
//
// Dedicated thread: waits for the handled signals and dispatches them to the
// installed handler in ordinary (async-signal-safe) thread context.
//
static void *
PlatformSignalThread(void * Arg)
{
sigset_t WaitSet;
int Signal;
(void)Arg;
sigemptyset(&WaitSet);
sigaddset(&WaitSet, SIGINT);
sigaddset(&WaitSet, SIGQUIT);
while (1)
{
if (sigwait(&WaitSet, &Signal) != 0)
{
continue;
}
DWORD CtrlType = PlatformMapSignalToCtrlType(Signal);
if (g_PlatformCtrlHandler != NULL && CtrlType != (DWORD)-1)
{
g_PlatformCtrlHandler(CtrlType);
}
}
return NULL;
}
BOOLEAN
PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler)
{
sigset_t BlockSet;
pthread_t Thread;
g_PlatformCtrlHandler = Handler;
//
// Block the handled signals in this (main) thread. Done before any other
// thread is created, the mask is inherited by every thread, guaranteeing
// the signals are delivered to our sigwait() thread and nowhere else.
//
sigemptyset(&BlockSet);
sigaddset(&BlockSet, SIGINT);
sigaddset(&BlockSet, SIGQUIT);
if (pthread_sigmask(SIG_BLOCK, &BlockSet, NULL) != 0)
{
return FALSE;
}
if (pthread_create(&Thread, NULL, PlatformSignalThread, NULL) != 0)
{
return FALSE;
}
//
// Fire-and-forget: the thread lives for the life of the process.
//
pthread_detach(Thread);
return TRUE;
}
#else
# error "Unsupported platform"
#endif

View file

@ -0,0 +1,43 @@
/**
* @file platform-signal.h
* @author Max Raulea (max.raulea@gmail.com)
* @brief User mode cross-platform interface for the console-control (CTRL+C / CTRL+BREAK) handler
* @details HyperDbg installs a single handler (BreakController) that pauses the
* debuggee when the user hits CTRL+C / CTRL+BREAK. The handler body is
* platform independent; only the OS mechanism that delivers the event
* and the thread context it runs in are OS specific. This interface
* isolates that registration. Windows maps onto SetConsoleCtrlHandler;
* Linux maps onto POSIX signals serviced by a dedicated sigwait() thread
* so the handler runs in ordinary thread context (matching Windows, which
* also dispatches the handler on its own thread).
*
* @version 0.1
* @date 2026-06-16
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#pragma once
#if defined(__linux__)
# include "../../../../include/SDK/HyperDbgSdk.h"
#endif // defined(__linux__)
//
// Console-control handler signature. Matches the Win32 PHANDLER_ROUTINE shape
// (and BreakController) so the same function pointer is usable on both platforms.
// CtrlType is one of the CTRL_*_EVENT codes.
//
typedef BOOL (*PLATFORM_CTRL_HANDLER)(DWORD CtrlType);
//
// INSTALL the process console-control handler.
// Windows: registers Handler via SetConsoleCtrlHandler.
// Linux: blocks the relevant signals process-wide and starts a dedicated
// sigwait() thread that translates them into CTRL_*_EVENT codes and
// invokes Handler. Must be called once, before other threads spawn,
// so the blocked-signal mask is inherited by every thread.
// Returns TRUE on success, FALSE on failure.
//
BOOLEAN
PlatformInstallCtrlHandler(PLATFORM_CTRL_HANDLER Handler);

View file

@ -656,6 +656,7 @@ ShowErrorMessage(UINT32 Error)
UINT64
DebuggerGetNtoskrnlBase()
{
#ifdef _WIN32
UINT64 NtoskrnlBase = NULL;
PRTL_PROCESS_MODULES Modules = NULL;
@ -678,6 +679,14 @@ DebuggerGetNtoskrnlBase()
free(Modules);
return NtoskrnlBase;
#else
//
// TODO(Linux): NT-style system module enumeration (PRTL_PROCESS_MODULES via
// NtQuerySystemInformation) has no Linux analog. The kernel-module base lookup
// will need a Linux-specific mechanism once the kernel side exists.
//
return NULL64_ZERO;
#endif
}
/**
@ -719,7 +728,7 @@ DebuggerPauseDebuggee()
//
// Send a pause IOCTL
//
StatusIoctl = DeviceIoControl(g_DeviceHandle, // Handle to device
StatusIoctl = PlatformDeviceIoControl(g_DeviceHandle, // Handle to device
IOCTL_PAUSE_PACKET_RECEIVED, // IO Control Code (IOCTL)
&PauseRequest, // Input Buffer to driver.
SIZEOF_DEBUGGER_PAUSE_PACKET_RECEIVED, // Input buffer
@ -733,7 +742,7 @@ DebuggerPauseDebuggee()
if (!StatusIoctl)
{
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError());
return FALSE;
}
@ -1391,7 +1400,7 @@ SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
// Send IOCTL
//
Status = DeviceIoControl(g_DeviceHandle, // Handle to device
Status = PlatformDeviceIoControl(g_DeviceHandle, // Handle to device
IOCTL_DEBUGGER_REGISTER_EVENT, // IO Control Code (IOCTL)
Event, // Input Buffer to driver.
EventBufferLength, // Input buffer length
@ -1408,7 +1417,7 @@ SendEventToKernel(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
if (!Status)
{
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError());
return FALSE;
}
}
@ -1560,7 +1569,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
//
if (ActionBreakToDebugger != NULL)
{
Status = DeviceIoControl(
Status = PlatformDeviceIoControl(
g_DeviceHandle, // Handle to device
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL)
ActionBreakToDebugger, // Input Buffer to driver.
@ -1578,7 +1587,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
if (!Status)
{
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError());
return FALSE;
}
}
@ -1588,7 +1597,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
//
if (ActionCustomCode != NULL)
{
Status = DeviceIoControl(
Status = PlatformDeviceIoControl(
g_DeviceHandle, // Handle to device
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL)
ActionCustomCode, // Input Buffer to driver.
@ -1606,7 +1615,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
if (!Status)
{
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError());
return FALSE;
}
}
@ -1616,7 +1625,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
//
if (ActionScript != NULL)
{
Status = DeviceIoControl(
Status = PlatformDeviceIoControl(
g_DeviceHandle, // Handle to device
IOCTL_DEBUGGER_ADD_ACTION_TO_EVENT, // IO Control Code (IOCTL)
ActionScript, // Input Buffer to driver.
@ -1634,7 +1643,7 @@ RegisterActionToEvent(PDEBUGGER_GENERAL_EVENT_DETAIL Event,
if (!Status)
{
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError());
return FALSE;
}
}
@ -1800,7 +1809,7 @@ InterpretGeneralEventAndActionsFields(
//
PVOID BufferOfCommandString = malloc(BufferOfCommandStringLength);
RtlZeroMemory(BufferOfCommandString, BufferOfCommandStringLength);
PlatformZeroMemory(BufferOfCommandString, BufferOfCommandStringLength);
//
// Copy the string to the buffer
@ -2105,7 +2114,7 @@ InterpretGeneralEventAndActionsFields(
LengthOfEventBuffer = sizeof(DEBUGGER_GENERAL_EVENT_DETAIL) + ConditionBufferLength;
TempEvent = (PDEBUGGER_GENERAL_EVENT_DETAIL)malloc(LengthOfEventBuffer);
RtlZeroMemory(TempEvent, LengthOfEventBuffer);
PlatformZeroMemory(TempEvent, LengthOfEventBuffer);
//
// Check if buffer is available
@ -2187,7 +2196,7 @@ InterpretGeneralEventAndActionsFields(
TempActionCustomCode = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfCustomCodeActionBuffer);
RtlZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer);
PlatformZeroMemory(TempActionCustomCode, LengthOfCustomCodeActionBuffer);
memcpy(
(PVOID)((UINT64)TempActionCustomCode + sizeof(DEBUGGER_GENERAL_ACTION)),
@ -2226,7 +2235,7 @@ InterpretGeneralEventAndActionsFields(
LengthOfScriptActionBuffer = sizeof(DEBUGGER_GENERAL_ACTION) + ScriptBufferLength;
TempActionScript = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfScriptActionBuffer);
RtlZeroMemory(TempActionScript, LengthOfScriptActionBuffer);
PlatformZeroMemory(TempActionScript, LengthOfScriptActionBuffer);
memcpy((PVOID)((UINT64)TempActionScript + sizeof(DEBUGGER_GENERAL_ACTION)),
(PVOID)ScriptBufferAddress,
@ -2272,7 +2281,7 @@ InterpretGeneralEventAndActionsFields(
TempActionBreak = (PDEBUGGER_GENERAL_ACTION)malloc(LengthOfBreakActionBuffer);
RtlZeroMemory(TempActionBreak, LengthOfBreakActionBuffer);
PlatformZeroMemory(TempActionBreak, LengthOfBreakActionBuffer);
//
// Set the action Tag

View file

@ -137,6 +137,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R
<ClInclude Include="..\include\platform\user\header\platform-serial.h" />
<ClInclude Include="..\include\platform\user\header\platform-ioctl.h" />
<ClInclude Include="..\include\platform\user\header\platform-signal.h" />
<ClInclude Include="..\include\platform\general\header\nt-list.h" />
<ClInclude Include="..\include\platform\user\header\windows-only\windows-privilege.h" />
<ClInclude Include="..\include\platform\user\header\Windows.h" />
<ClInclude Include="header\assembler.h" />

View file

@ -206,6 +206,9 @@
<ClInclude Include="..\include\platform\user\header\platform-signal.h">
<Filter>header\platform</Filter>
</ClInclude>
<ClInclude Include="..\include\platform\general\header\nt-list.h">
<Filter>header\platform</Filter>
</ClInclude>
<ClInclude Include="header\messaging.h">
<Filter>header</Filter>
</ClInclude>

View file

@ -171,6 +171,12 @@ typedef const wchar_t *LPCWCHAR, *PCWCHAR;
//
#include "platform/user/header/platform-signal.h"
//
// NT-style intrusive linked-list helpers + CONTAINING_RECORD (self-guards to
// non-Windows; Windows gets these from <windows.h> / the native-SDK shim)
//
#include "platform/general/header/nt-list.h"
//
// Platform-specific intrinsics
//