Merge pull request #660 from HyperDbg/linux
Some checks failed
vs2026-ci / win-amd64-build (debug, x64) (push) Has been cancelled
vs2026-ci / win-amd64-build (release, x64) (push) Has been cancelled
vs2026-ci / Deploy release (push) Has been cancelled

Added unix implementation of asm-vmx-checks.asm, and made naming conv…
This commit is contained in:
Sina Karvandi 2026-07-25 11:12:30 +02:00 committed by GitHub
commit e806f6e6a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 965 additions and 39 deletions

View file

@ -72,10 +72,34 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
#target_link_libraries(script-engine symbol-parser)
add_subdirectory(script-engine)
link_directories(libraries/zydis/user libraries/keystone/release-lib)
#link_directories(libraries/zydis/user libraries/keystone/release-lib)
add_subdirectory(libhyperdbg)
find_package(Threads REQUIRED)
target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone Threads::Threads ${CMAKE_DL_LIBS})
#target_link_libraries(libhyperdbg Zycore Zydis script-engine keystone Threads::Threads ${CMAKE_DL_LIBS})
target_link_libraries(libhyperdbg Zycore Zydis script-engine Threads::Threads ${CMAKE_DL_LIBS})
#
# Each library must define its own HYPERDBG_* macro so that the IMPORT_EXPORT_*
# annotations in include/SDK/imports/user/ resolve to the "export" form
# (visibility("default")) while it is being built, and to the "import" form in
# every other translation unit. This mirrors what the Windows .vcxproj files do
# with __declspec(dllexport)/dllimport.
#
target_compile_definitions(script-engine PRIVATE HYPERDBG_SCRIPT_ENGINE)
target_compile_definitions(libhyperdbg PRIVATE HYPERDBG_LIBHYPERDBG)
#
# The annotations above only mean something if the default visibility is hidden;
# otherwise every symbol is exported anyway and visibility("default") is a no-op.
# Hiding by default matches the Windows model (private unless dllexport-ed) and
# keeps each library's internal globals private, so same-named module-private
# globals in different libraries are no longer merged by the dynamic linker.
#
set_target_properties(script-engine libhyperdbg PROPERTIES
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON
)
add_subdirectory(hyperdbg-cli)
target_link_libraries(hyperdbg-cli libhyperdbg)

View file

@ -64,6 +64,19 @@ typedef const char * PCSTR;
typedef char * PSTR;
typedef short * PWCHAR;
// Windows generic type aliases (winnt.h): CONST is the const qualifier keyword,
// FLOAT is a plain float. Kept so shared source using the Win32 spellings
// compiles unchanged.
# define CONST const
typedef float FLOAT;
// MSVC secure-CRT truncation sentinel and status code (crtdefs.h / errno.h).
// _TRUNCATE passed as the count to strncpy_s and friends means "copy as much as
// fits and always null-terminate"; STRUNCATE is what they return when that
// truncation actually happened. Kept at their canonical MSVC values.
# define _TRUNCATE ((SIZE_T)-1)
# define STRUNCATE 80
// Windows socket type (Linux sockets are plain int)
typedef int SOCKET;
# define INVALID_SOCKET ((SOCKET)(-1))

View file

@ -22,6 +22,7 @@
# include <strings.h>
# include <signal.h>
# include <dlfcn.h>
# include <time.h> // clock_gettime / CLOCK_MONOTONIC (PlatformQueryPerformanceCounter)
#endif // defined(__linux__)
/**
@ -270,6 +271,76 @@ PlatformStrCpy(char * Dest, SIZE_T DestSize, const char * Src)
#endif
}
/**
* @brief Platform independent wrapper for strncpy_s
*
* @details Copies the first D characters of Src into the DestSize-byte Dest
* buffer and appends a null terminator, where D is the lesser of Count and the
* length of Src. If those characters do not fit while still leaving room for the
* terminator, Dest is set to an empty string and a non-zero error is returned.
* Passing _TRUNCATE as Count instead copies as much of Src as fits, returning
* STRUNCATE when anything had to be dropped. On Linux there is no standard
* strncpy_s, so this reproduces those same rules.
*
* @param Dest destination buffer
* @param DestSize size of the destination buffer in bytes
* @param Src source string
* @param Count maximum characters to copy, or _TRUNCATE
* @return INT 0 on success, STRUNCATE if truncated, non-zero on failure
*/
INT
PlatformStrNCpy(char * Dest, SIZE_T DestSize, const char * Src, SIZE_T Count)
{
#if defined(_WIN32)
return strncpy_s(Dest, DestSize, Src, Count);
#elif defined(__linux__)
// NOT YET TESTED!! So needs some testing to see if it actually behaves the same as strncpy_s on windows
SIZE_T Length;
if (Dest == NULL || DestSize == 0 || Src == NULL)
{
if (Dest != NULL && DestSize != 0)
{
Dest[0] = '\0';
}
return -1;
}
//
// Never read past Count characters of Src; it need not be null-terminated
// within that span
//
Length = PlatformStrnlen(Src, Count == _TRUNCATE ? DestSize : Count);
if (Count == _TRUNCATE)
{
//
// Copy as much as fits and report whether anything was dropped
//
if (Length >= DestSize)
{
memcpy(Dest, Src, DestSize - 1);
Dest[DestSize - 1] = '\0';
return STRUNCATE;
}
}
else if (Length >= DestSize)
{
//
// Source does not fit (need room for the null terminator too)
//
Dest[0] = '\0';
return -1;
}
memcpy(Dest, Src, Length);
Dest[Length] = '\0';
return 0;
#else
# error "Unsupported platform"
#endif
}
/**
* @brief Platform independent wrapper for _stricmp
*

View file

@ -70,6 +70,18 @@ PlatformStrnlen(const char * Str, SIZE_T MaxLength);
INT
PlatformStrCpy(char * Dest, SIZE_T DestSize, const char * Src);
//
// BOUNDED COUNTED STRING COPY
//
// Mirrors strncpy_s: copies at most Count characters of Src into Dest (of
// DestSize bytes) and always null-terminates. Passing _TRUNCATE as Count means
// "copy as much as fits", returning STRUNCATE if it had to truncate. Otherwise
// returns 0 on success, non-zero if the arguments are invalid or Count does not
// fit (in which case Dest is left as an empty string).
//
INT
PlatformStrNCpy(char * Dest, SIZE_T DestSize, const char * Src, SIZE_T Count);
//
// CASE-INSENSITIVE STRING COMPARE
//

View file

@ -12,10 +12,15 @@ set(SourceFiles
"header/globals/globals.h"
"header/debugger/commands/help.h"
"header/hwdbg/hwdbg-interpreter.h"
"header/hwdbg/hwdbg-scripts.h"
"header/debugger/misc/inipp.h"
"header/debugger/driver-loader/install.h"
"header/debugger/kernel-level/kd.h"
"header/app/libhyperdbg.h"
"header/app/messaging.h"
"header/app/packets.h"
"header/debugger/core/steppings.h"
"header/debugger/misc/pci-id.h"
"header/common/list.h"
"header/debugger/communication/namedpipe.h"
"header/objects/objects.h"
@ -41,17 +46,30 @@ set(SourceFiles
"../script-eval/code/ScriptEngineEval.c"
"code/common/spinlock.cpp"
"code/debugger/commands/debugging-commands/a.cpp"
"code/debugger/commands/debugging-commands/continue.cpp"
"code/debugger/commands/debugging-commands/gg.cpp"
"code/debugger/commands/debugging-commands/core.cpp"
"code/debugger/commands/debugging-commands/dt-struct.cpp"
"code/debugger/commands/debugging-commands/gu.cpp"
"code/debugger/commands/debugging-commands/k.cpp"
"code/debugger/commands/debugging-commands/preactivate.cpp"
"code/debugger/commands/debugging-commands/prealloc.cpp"
"code/debugger/commands/extension-commands/apic.cpp"
"code/debugger/commands/extension-commands/crwrite.cpp"
"code/debugger/commands/extension-commands/idt.cpp"
"code/debugger/commands/extension-commands/ioapic.cpp"
"code/debugger/commands/extension-commands/lbr.cpp"
"code/debugger/commands/extension-commands/lbrdump.cpp"
"code/debugger/commands/extension-commands/pcicam.cpp"
"code/debugger/commands/extension-commands/pcitree.cpp"
"code/debugger/commands/extension-commands/pt.cpp"
"code/debugger/commands/extension-commands/smi.cpp"
"code/debugger/commands/extension-commands/xsetbv.cpp"
"code/debugger/commands/extension-commands/rev.cpp"
"code/debugger/commands/extension-commands/trace.cpp"
"code/debugger/commands/extension-commands/track.cpp"
"code/debugger/commands/extension-commands/mode.cpp"
"code/debugger/commands/hwdbg-commands/hw.cpp"
"code/debugger/commands/hwdbg-commands/hw_clk.cpp"
"code/debugger/commands/meta-commands/dump.cpp"
"code/debugger/commands/meta-commands/kill.cpp"
@ -78,11 +96,16 @@ set(SourceFiles
"code/debugger/user-level/user-listening.cpp"
"code/export/export.cpp"
"code/hwdbg/hwdbg-interpreter.cpp"
"code/hwdbg/hwdbg-scripts.cpp"
"code/objects/objects.cpp"
"code/rev/rev-ctrl.cpp"
"pch.cpp"
"code/app/dllmain.cpp"
"code/app/libhyperdbg.cpp"
"code/app/messaging.cpp"
"code/app/packets.cpp"
"code/debugger/core/steppings.cpp"
"code/debugger/misc/pci-id.cpp"
"code/common/common.cpp"
"code/debugger/commands/debugging-commands/bc.cpp"
"code/debugger/commands/debugging-commands/bd.cpp"
@ -115,6 +138,7 @@ set(SourceFiles
"code/debugger/commands/debugging-commands/wrmsr.cpp"
"code/debugger/commands/debugging-commands/x.cpp"
"code/debugger/commands/extension-commands/cpuid.cpp"
"ucpuid.cpp"
"code/debugger/commands/extension-commands/dr.cpp"
"code/debugger/commands/extension-commands/epthook.cpp"
"code/debugger/commands/extension-commands/epthook2.cpp"
@ -160,7 +184,7 @@ set(SourceFiles
"code/debugger/tests/tests.cpp"
"code/debugger/transparency/gaussian-rng.cpp"
"code/debugger/transparency/transparency.cpp"
"code/assembly/asm-vmx-checks.asm"
"code/assembly/asm-vmx-checks-masm-windows.asm"
)
include_directories(
"../dependencies/phnt"
@ -197,6 +221,24 @@ if(UNIX)
list(APPEND SourceFiles "code/debugger/driver-loader/install-linux.cpp")
list(REMOVE_ITEM SourceFiles "code/debugger/communication/namedpipe.cpp")
list(APPEND SourceFiles "code/debugger/communication/namedpipe-linux.cpp")
#
# pt.cpp (Intel PT command) is an un-ported Windows process-control TU
# (OpenProcess/CreateToolhelp32Snapshot/CreateThread/WaitForMultipleObjects).
# Keep it out of the Linux build until the process-control port lands and
# swap in the stub that provides its 4 externally visible functions.
#
list(REMOVE_ITEM SourceFiles "code/debugger/commands/extension-commands/pt.cpp")
list(APPEND SourceFiles "code/debugger/commands/extension-commands/pt-linux.cpp")
#
# The MASM (.asm) implementation only builds with the Microsoft assembler,
# so swap it for the GAS (AT&T syntax) port and enable the ASM language so
# CMake assembles the .s file with the system assembler.
#
list(REMOVE_ITEM SourceFiles "code/assembly/asm-vmx-checks-masm-windows.asm")
list(APPEND SourceFiles "code/assembly/asm-vmx-checks-gas-unix.s")
enable_language(ASM)
endif()
add_library(libhyperdbg SHARED ${SourceFiles})

View file

@ -54,7 +54,7 @@ SetTextMessageCallbackUsingSharedBuffer(PVOID Handler)
return NULL;
}
RtlZeroMemory(g_MessageHandlerSharedBuffer, COMMUNICATION_BUFFER_SIZE + TCP_END_OF_BUFFER_CHARS_COUNT);
PlatformZeroMemory(g_MessageHandlerSharedBuffer, COMMUNICATION_BUFFER_SIZE + TCP_END_OF_BUFFER_CHARS_COUNT);
return g_MessageHandlerSharedBuffer;
}

View file

@ -46,18 +46,11 @@ ReadIrpBasedBuffer()
// a pending IOCTL while the main debugger handle continues sending other
// synchronous IOCTLs.
//
Handle = CreateFileA(
"\\\\.\\HyperDbgDebuggerDevice",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, /// lpSecurityAttirbutes
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL); /// lpTemplateFile
Handle = PlatformOpenDevice("\\\\.\\HyperDbgDebuggerDevice");
if (Handle == INVALID_HANDLE_VALUE)
{
ErrorNum = GetLastError();
ErrorNum = PlatformGetLastError();
if (ErrorNum == ERROR_ACCESS_DENIED)
{
@ -92,9 +85,9 @@ ReadIrpBasedBuffer()
//
// Clear the buffer
//
ZeroMemory(OutputBuffer, UsermodeBufferSize);
PlatformZeroMemory(OutputBuffer, UsermodeBufferSize);
Status = DeviceIoControl(
Status = PlatformDeviceIoControl(
Handle, // Handle to device
IOCTL_REGISTER_EVENT, // IO Control Code (IOCTL)
&RegisterEvent, // Input Buffer to driver.
@ -253,7 +246,7 @@ ReadIrpBasedBuffer()
//
// Indicate that driver (Hypervisor) is loaded successfully
//
SetEvent(g_IsDriverLoadedSuccessfully);
PlatformSetEvent(g_IsDriverLoadedSuccessfully);
break;
@ -321,9 +314,9 @@ ReadIrpBasedBuffer()
//
// close handle
//
if (!CloseHandle(Handle))
if (!PlatformCloseHandle(Handle))
{
ShowMessages("err, closing handle 0x%x\n", GetLastError());
ShowMessages("err, closing handle 0x%x\n", PlatformGetLastError());
}
}

View file

@ -0,0 +1,44 @@
/* ------------------------------------------------------------------------
* GAS (AT&T syntax) port of asm-vmx-checks-masm-windows.asm
*
* AsmVmxSupportDetection: returns 1 in rax if the CPU reports VMX support
* (CPUID.1:ECX[5]), 0 otherwise.
* ------------------------------------------------------------------------ */
.text
.globl AsmVmxSupportDetection
.type AsmVmxSupportDetection, @function
/* ------------------------------------------------------------------------ */
AsmVmxSupportDetection:
push %rbx
push %rcx
push %rdx
xor %eax, %eax
inc %eax
cpuid
xor %rax, %rax
bt $0x05, %ecx
jc VMXSupport
VMXNotSupport:
jmp RetInst
VMXSupport:
mov $0x01, %rax
RetInst:
pop %rdx
pop %rcx
pop %rbx
ret
.size AsmVmxSupportDetection, .-AsmVmxSupportDetection
/* ------------------------------------------------------------------------ */
/* Mark the stack as non-executable (no executable-stack requirement). */
.section .note.GNU-stack,"",@progbits

View file

@ -90,7 +90,7 @@ CommandApicSendRequest(DEBUGGER_APIC_REQUEST_TYPE ApicType,
else
{
*IsUsingX2APIC = ApicRequest->IsUsingX2APIC;
RtlCopyMemory(ApicBuffer, (PVOID)(((CHAR *)ApicRequest) + sizeof(DEBUGGER_APIC_REQUEST)), ExpectedRequestSize);
PlatformCopyMemory(ApicBuffer, (PVOID)(((CHAR *)ApicRequest) + sizeof(DEBUGGER_APIC_REQUEST)), ExpectedRequestSize);
free(ApicRequest);
return TRUE;
@ -139,7 +139,7 @@ CommandApicSendRequest(DEBUGGER_APIC_REQUEST_TYPE ApicType,
// Fill the request buffer
//
*IsUsingX2APIC = ApicRequest->IsUsingX2APIC;
RtlCopyMemory(ApicBuffer, (PVOID)(((CHAR *)ApicRequest) + sizeof(DEBUGGER_APIC_REQUEST)), ExpectedRequestSize);
PlatformCopyMemory(ApicBuffer, (PVOID)(((CHAR *)ApicRequest) + sizeof(DEBUGGER_APIC_REQUEST)), ExpectedRequestSize);
free(ApicRequest);
return TRUE;

View file

@ -329,7 +329,7 @@ CommandLbrShowSuccessMessage(const HYPERTRACE_LBR_OPERATION_PACKETS * LbrRequest
VOID
CommandLbr(vector<CommandToken> CommandTokens, string Command)
{
HYPERTRACE_LBR_OPERATION_PACKETS LbrRequest = {0};
HYPERTRACE_LBR_OPERATION_PACKETS LbrRequest = {};
BOOLEAN ParseResult = FALSE;
if (CommandTokens.size() == 1)

View file

@ -239,7 +239,7 @@ CommandLbrdumpPrint(HYPERTRACE_LBR_DUMP_PACKETS * LbrdumpRequest)
VOID
CommandLbrdump(vector<CommandToken> CommandTokens, string Command)
{
HYPERTRACE_LBR_DUMP_PACKETS LbrdumpRequest = {0};
HYPERTRACE_LBR_DUMP_PACKETS LbrdumpRequest = {};
UINT32 CoreId = 0;
BOOLEAN ContinueDumpingAllCores = TRUE;

View file

@ -48,7 +48,7 @@ CommandPcicam(vector<CommandToken> CommandTokens, string Command)
{
BOOL Status;
ULONG ReturnedLength;
DEBUGGEE_PCIDEVINFO_REQUEST_RESPONSE_PACKET PcidevinfoPacket = {0};
DEBUGGEE_PCIDEVINFO_REQUEST_RESPONSE_PACKET PcidevinfoPacket = {};
UINT32 TargetBus = 0;
UINT32 TargetDevice = 0;
UINT32 TargetFunction = 0;

View file

@ -46,7 +46,7 @@ CommandPcitree(vector<CommandToken> CommandTokens, string Command)
{
BOOL Status;
ULONG ReturnedLength;
DEBUGGEE_PCITREE_REQUEST_RESPONSE_PACKET PcitreePacket = {0};
DEBUGGEE_PCITREE_REQUEST_RESPONSE_PACKET PcitreePacket = {};
if (CommandTokens.size() != 1)
{

View file

@ -0,0 +1,119 @@
/**
* @file pt-linux.cpp
* @author Max Raulea (max.raulea@hyperdbg.org)
* @brief Linux stub implementations of the !pt (Intel Processor Trace) command (pt.cpp)
* @details The Windows implementation (pt.cpp) drives Intel PT by attaching to a
* live process, so it is built almost entirely out of Win32
* process/thread management roughly 27 raw Win32 call sites, with no
* #ifdef guards anywhere in the file:
* - CreateToolhelp32Snapshot + Process32First/Next and
* Thread32First/Next to walk processes and threads by name/pid/tid,
* - OpenProcess / OpenThread plus the handle lifetime around them
* (CloseHandle x8),
* - SetThreadAffinityMask to pin the traced thread to a core,
* - CreateEvent / CreateThread / WaitForMultipleObjects for the
* background trace thread and its g_PtTraceStopEvent stop signal,
* - two DeviceIoControl + GetLastError pairs.
*
* Those last two are simple renames onto the existing Platform*
* wrappers, but everything above needs a real decision per call site
* either route it through a new cross-platform wrapper, or guard the
* whole enclosing function for Windows and give Linux a stub because
* the Win32 calls are interleaved with the surrounding walk and UI
* logic rather than sitting behind a clean boundary. Porting half the
* file would leave it in a worse state than leaving it whole, so until
* that work is done the entire translation unit is swapped out on Linux
* (CMake `if(UNIX)` REMOVE_ITEM pt.cpp + APPEND pt-linux.cpp). This
* mirrors how symbol.cpp, pe-parser.cpp, install.cpp and namedpipe.cpp
* are handled; pt.cpp itself is left 100% untouched for the Windows
* build.
*
* Only the 4 externally visible functions are provided here CommandPt
* and CommandPtHelp (declared in commands.h / help.h, reached from the
* command dispatch table) and HyperDbgPerformPtOperation /
* HyperDbgPtMmapSendRequest (declared in debugger.h). Everything else
* in pt.cpp is helper code reached only through those entry points, so
* it simply does not exist in the Linux translation unit.
*
* TODO(Linux) to make these real: the Toolhelp process/thread walks
* become /proc enumeration, OpenProcess/OpenThread become pid/tid
* handles (or a ptrace attach), SetThreadAffinityMask becomes
* sched_setaffinity(2), and the event/thread machinery becomes the
* existing Platform* wrappers. Note the underlying IOCTL transport
* (platform-ioctl) is itself still a Linux stub, so a working !pt also
* depends on the kernel module landing.
*
* @version 0.1
* @date 2026-07-24
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
#ifdef __linux__
/**
* @brief help of the !pt command
*
* @return VOID
*/
VOID
CommandPtHelp()
{
ShowMessages("!pt : enables, disables and configures Intel Processor Trace (PT).\n\n");
ShowMessages("err, the !pt command is not supported on Linux yet\n");
}
/**
* @brief !pt command handler
*
* @param CommandTokens
* @param Command
*
* @return VOID
*/
VOID
CommandPt(vector<CommandToken> CommandTokens, string Command)
{
UNREFERENCED_PARAMETER(CommandTokens);
UNREFERENCED_PARAMETER(Command);
ShowMessages("err, the !pt command is not supported on Linux yet\n");
}
/**
* @brief Send an Intel PT operation request to the kernel
*
* @param PtRequest
*
* @return BOOLEAN
*/
BOOLEAN
HyperDbgPerformPtOperation(HYPERTRACE_PT_OPERATION_PACKETS * PtRequest)
{
UNREFERENCED_PARAMETER(PtRequest);
ShowMessages("err, Intel PT operations are not supported on Linux yet\n");
return FALSE;
}
/**
* @brief Send an Intel PT trace-buffer mapping request to the kernel
*
* @param MmapRequest
*
* @return BOOLEAN
*/
BOOLEAN
HyperDbgPtMmapSendRequest(HYPERTRACE_PT_MMAP_PACKETS * MmapRequest)
{
UNREFERENCED_PARAMETER(MmapRequest);
ShowMessages("err, Intel PT buffer mapping is not supported on Linux yet\n");
return FALSE;
}
#endif // __linux__

View file

@ -122,7 +122,7 @@ HyperDbgPerformSmiOperation(SMI_OPERATION_PACKETS * SmiOperation)
VOID
CommandSmi(vector<CommandToken> CommandTokens, string Command)
{
SMI_OPERATION_PACKETS SmiOperationRequest = {0};
SMI_OPERATION_PACKETS SmiOperationRequest = {};
if (CommandTokens.size() != 2)
{

View file

@ -118,6 +118,7 @@ AssembleData::ParseAssemblyData()
INT
AssembleData::Assemble(UINT64 StartAddr, ks_arch Arch, INT Mode, INT Syntax)
{
#ifdef _WIN32
ks_engine * Ks;
KsErr = ks_open(Arch, Mode, &Ks);
@ -175,6 +176,24 @@ AssembleData::Assemble(UINT64 StartAddr, ks_arch Arch, INT Mode, INT Syntax)
}
ks_close(Ks);
return -1;
#else
//
// TODO(Linux): the Keystone assembler engine is not linked on Linux. Only a
// Windows keystone.lib is vendored (libraries/keystone/release-lib) and
// dependencies/keystone/ ships headers only, so the ks_* types and constants
// resolve but the 5 ks_* functions do not. Build upstream Keystone for Linux
// and restore link_directories()/target_link_libraries(keystone) in the
// top-level CMakeLists.txt to make this real.
//
UNREFERENCED_PARAMETER(StartAddr);
UNREFERENCED_PARAMETER(Arch);
UNREFERENCED_PARAMETER(Mode);
UNREFERENCED_PARAMETER(Syntax);
ShowMessages("err, the assembler is not supported on Linux yet\n");
return -1;
#endif
}
AssembleData *

View file

@ -73,7 +73,7 @@ ReadLine(CHAR * DestBuffer, UINT64 CharLimit, CHAR ** SrcBuffer)
}
else
{
strncpy_s(DestBuffer, CharLimit, *SrcBuffer, (Line - *SrcBuffer));
PlatformStrNCpy(DestBuffer, CharLimit, *SrcBuffer, (Line - *SrcBuffer));
*SrcBuffer += (Line - *SrcBuffer + 1);
return *SrcBuffer;
}
@ -155,7 +155,7 @@ GetVendorByIdStr(const CHAR * Filename, const CHAR * VendorId)
{
return NULL;
}
strncpy_s(MatchedVendor->VendorName, sizeof(MatchedVendor->VendorName), TrimWhitespace(VendorNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
PlatformStrNCpy(MatchedVendor->VendorName, sizeof(MatchedVendor->VendorName), TrimWhitespace(VendorNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
FoundVendorId = TRUE;
}
}
@ -182,7 +182,7 @@ GetVendorByIdStr(const CHAR * Filename, const CHAR * VendorId)
return NULL;
}
strncpy_s(NewDevice->DeviceName, sizeof(NewDevice->DeviceName), TrimWhitespace(DeviceNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
PlatformStrNCpy(NewDevice->DeviceName, sizeof(NewDevice->DeviceName), TrimWhitespace(DeviceNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
NewDevice->SubDevices = NULL;
NewDevice->Next = NULL;
@ -227,7 +227,7 @@ GetVendorByIdStr(const CHAR * Filename, const CHAR * VendorId)
return NULL;
}
strncpy_s(NewSubDevice->SubSystemName, sizeof(NewSubDevice->SubSystemName), TrimWhitespace(SubsystemNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
PlatformStrNCpy(NewSubDevice->SubSystemName, sizeof(NewSubDevice->SubSystemName), TrimWhitespace(SubsystemNameBuf, PCI_NAME_STR_LENGTH), _TRUNCATE);
NewSubDevice->Next = NULL;
if (LastSubDevice)
@ -304,6 +304,7 @@ FreePciIdDatabase()
Vendor *
GetVendorById(UINT16 VendorId)
{
#ifdef _WIN32
CHAR VendorIdAsStr[5];
CHAR ExecutablePath[MAX_PATH];
HMODULE hModule = GetModuleHandle(NULL);
@ -326,6 +327,17 @@ GetVendorById(UINT16 VendorId)
strncpy(ExecutableName, PCI_ID_DATABASE_PATH, sizeof(PCI_ID_DATABASE_PATH));
return GetVendorByIdStr(ExecutablePath, ToLower(VendorIdAsStr));
#else
//
// TODO(Linux): resolve the PCI ID database next to the executable via
// readlink("/proc/self/exe") once the path separator and
// PCI_ID_DATABASE_PATH ("constants\\pci.ids") are made portable. Until
// then no vendor/device names are available on Linux.
//
UNREFERENCED_PARAMETER(VendorId);
return NULL;
#endif
}
/**

View file

@ -412,7 +412,7 @@ HwdbgScriptSendScriptPacket(HWDBG_INSTANCE_INFORMATION * InstanceInfo,
return FALSE;
}
RtlZeroMemory(FinalBuffer, BufferLength + sizeof(HWDBG_SCRIPT_BUFFER));
PlatformZeroMemory(FinalBuffer, BufferLength + sizeof(HWDBG_SCRIPT_BUFFER));
//
// Copy the packet into the FinalBuffer

View file

@ -331,7 +331,7 @@ msbuild "$(SolutionDir)dependencies\zydis\msvc\Zydis.sln" /m /p:Configuration="R
<ClCompile Include="code\debugger\tests\tests.cpp" />
<ClCompile Include="code\debugger\transparency\gaussian-rng.cpp" />
<ClCompile Include="code\debugger\transparency\transparency.cpp" />
<MASM Include="code\assembly\asm-vmx-checks.asm" />
<MASM Include="code\assembly\asm-vmx-checks-masm-windows.asm" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View file

@ -747,7 +747,7 @@
</ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="code\assembly\asm-vmx-checks.asm">
<MASM Include="code\assembly\asm-vmx-checks-masm-windows.asm">
<Filter>code\assembly</Filter>
</MASM>
</ItemGroup>

View file

@ -2098,7 +2098,7 @@ CommandCpuidRequestCpuid(UINT32 FunctionId, UINT32 SubFunctionId)
// want to pass some other arguments to the kernel in
// the future
//
Status = DeviceIoControl(
Status = PlatformDeviceIoControl(
g_DeviceHandle, // Handle to device
IOCTL_DEBUGGER_CPUID, // IO Control Code (IOCTL)
CpuidRequest, // Input Buffer to driver.
@ -2112,7 +2112,7 @@ CommandCpuidRequestCpuid(UINT32 FunctionId, UINT32 SubFunctionId)
if (!Status)
{
ShowMessages("ioctl failed with code 0x%x\n", GetLastError());
ShowMessages("ioctl failed with code 0x%x\n", PlatformGetLastError());
return;
}

View file

@ -126,6 +126,39 @@ equivalent, behavior-preserving.
"%d", …)` → `PlatformSprintf(Buf, sizeof(Buf), "%d", …)`. Both buffers are
`CHAR[32]` formatting a single `%d`, so the dropped `_TRUNCATE` truncation
semantics are unreachable.
- `code/hardware.c` (+ `header/hardware.h`) — added to script-engine CMake
`SourceFiles` (was in the vcxproj, missing from the Linux build). Provides the
`HardwareScriptInterpreter*` family the hwdbg TUs call. One bucket-1 swap to
compile: `RtlZeroMemory``PlatformZeroMemory` ×2 (lines 499, 564). Resolves the
`HardwareScriptInterpreter*` link errors. NOTE: two more files are still in the
vcxproj but missing from script-engine's CMake — `code/script_include.c`
(undefined `ResolveIncludePath`/`ParseIncludeFile`/`FileExists`/`InsertStrNew`)
and `include/platform/user/code/platform-lib-calls.c` (undefined `Platform*` in
libscript-engine.so). Adding both is the next script-engine build step.
- `code/script_include.c` + `platform-lib-calls.c` — DONE (2026-07-24).
`libscript-engine.so` is now fully self-contained (zero undefined refs):
- Added `../include/platform/user/code/platform-lib-calls.c` to the base
`SourceFiles` — it's compiled into libhyperdbg too, but each `.so` needs its
own copy of the `Platform*` symbols (script-engine calls `PlatformSnprintf`/
`PlatformStrDup`/`PlatformVsnprintf`/`PlatformZeroMemory`). No swap: it builds
on both OSes. Needed one root-cause fix — added `#include <time.h>` to its
Linux include block (`clock_gettime`/`CLOCK_MONOTONIC` in
`PlatformQueryPerformanceCounter`); it previously only compiled because
libhyperdbg's pch pulled `<time.h>` in transitively, but script-engine's pch
doesn't.
- Added `code/script_include.c` to base `SourceFiles`, then swapped it for a new
empty stub `code/script_include-linux.c` under `if(UNIX)` (user chose stubs
over porting the Win32 path logic for now). The stub implements
`ResolveIncludePath`/`FileExists`/`ParseIncludeFile`/`InsertStrNew` as
no-op/failure; script `#include` resolution is unsupported on Linux until a
real resolver (`readlink("/proc/self/exe")` + `stat`) lands. `script_include.c`
left pristine (Windows path uses `GetModuleFileNameA`/`GetFileAttributesA`).
Result: every remaining CLI-link undefined ref (23) now belongs to
`liblibhyperdbg.so` alone — keystone (`ks_*`, 5), the excluded `pt.cpp`
(`CommandPt*`/`HyperDbgPt*`, 4), and missing libhyperdbg TUs behind the
PCI-ID/Vendor, Stepping, text-callback, `ShowMessages`, `IrpBasedBufferThread`
symbols (14).
### Kernel-level debugger (remote protocol)
- `kd.cpp` — largest sweep (~46 `Platform*`): serial open/configure/close via
@ -181,6 +214,13 @@ equivalent, behavior-preserving.
### hwdbg
- `hwdbg-interpreter.cpp``RtlCopyMemory``PlatformCopyMemory`, `RtlZeroMemory``PlatformZeroMemory`.
- `hwdbg-scripts.cpp` + `hwdbg-commands/hw.cpp` — added to libhyperdbg CMake
`SourceFiles` (were missing from the Linux build; present in the vcxproj all
along), plus the `header/hwdbg/hwdbg-scripts.h` list entry. `hw.cpp` built
clean; `hwdbg-scripts.cpp` needed one bucket-1 swap: `RtlZeroMemory`
`PlatformZeroMemory` (line 415). Both compile on Linux now. NOTE: their
`HardwareScriptInterpreter*` callees live in `script-engine/code/hardware.c`,
now added to script-engine's CMake (see the script-engine subproject section).
### objects
- `objects.cpp` — wrapper sweep: `RtlCopyMemory`×2→`PlatformCopyMemory`,
@ -272,6 +312,45 @@ reasons (`RTL_PROCESS_MODULES` / `RTL_PROCESS_MODULE_INFORMATION` undeclared, an
`WCHAR *` vs `wchar_t *` — the wide-char item below); none of those are on lines
this sweep touched.
### Command files wired into Linux CMake — DONE (2026-07-24)
The 2026-07-20 sweep ported these files' bucket-1 calls but never added them to
`libhyperdbg/CMakeLists.txt`, so they were compiled on Windows only and their
`Command*` symbols were unresolved at the Linux CLI link. Added the 12 missing
command TUs to `SourceFiles`:
- Debugging: `continue.cpp`, `gg.cpp`
- Extension: `apic.cpp`, `idt.cpp`, `ioapic.cpp`, `lbr.cpp`, `lbrdump.cpp`,
`pcicam.cpp`, `pcitree.cpp`, `smi.cpp`, `xsetbv.cpp`
- Plus top-level `ucpuid.cpp` (defines `CommandUserCpuid` / `CommandUserCpuidHelp`
/ `CommandCpuidRequestCpuid` / `CommandShowUserCpuidMessage`; lives at
`libhyperdbg/ucpuid.cpp`, not under `code/`, which is why the earlier diff
missed it).
Stragglers the 07-20 sweep didn't cover, fixed to compile (all mechanical):
- `apic.cpp` — 2× `RtlCopyMemory``PlatformCopyMemory` (sweep only did the
ZeroMemory family).
- Enum-first aggregate init `= {0}``= {}` (GCC rejects `int`→enum in `{0}`;
`{}` value-inits identically): `lbr.cpp:332`, `lbrdump.cpp:242`,
`pcicam.cpp:51`, `pcitree.cpp:49`, `smi.cpp:125`. (apic's `LAPIC_PAGE {0}` and
lbrdump's `CHAR[] {0}` are scalar-first and compile fine, left as-is.)
- `ucpuid.cpp``DeviceIoControl``PlatformDeviceIoControl`,
`GetLastError``PlatformGetLastError` (1 each; same drop-in as the sweep).
- `Environment.h` — added the two missing generic Win32 aliases `ucpuid.cpp`
needs: `#define CONST const` and `typedef float FLOAT;` (winnt.h spellings;
benefits any future file too).
**`pt.cpp` deliberately excluded from the Linux build** via an `if(UNIX)`
`REMOVE_ITEM` (like namedpipe/symbol/pe-parser). It's the un-started
process-control port (`OpenProcess(PROCESS_ALL_ACCESS)`,
`CreateToolhelp32Snapshot`, `CreateThread`, `WaitForMultipleObjects`, Win32
process/thread handles) — see the `pt.cpp` TODO below. `CommandPt`/`CommandPtHelp`
stay unresolved, same as before it was added to the list.
Result: every `Command*` link error is resolved except the two `CommandPt*`.
Remaining CLI-link buckets are unrelated: `Sym*` (symbol-linux stub, 15), `ks_*`
(keystone Linux lib, 5), `Platform*` + include-family (script_include.c /
platform-lib-calls.c missing from script-engine's CMake, 8).
### rdmsr.cpp core-count — DONE (2026-07-22)
Follow-up to the bucket-1 sweep of `rdmsr.cpp` above (this is a separate bucket-2
@ -411,9 +490,192 @@ shims in `Environment.h`.
Note the pre-existing latent teardown-ordering issue is unchanged; see the
`PlatformTerminateThread` TODO below (remote-connection's listening thread).
---
### asm-vmx-checks — DONE via GAS port + CMake swap (2026-07-24)
## TODO ledger — revisit before Linux is functional
`code/assembly/asm-vmx-checks-masm-windows.asm` (MASM, `AsmVmxSupportDetection`:
CPUID.1 → `bt ecx,5` → return 1/0 for VMX support) only assembles with ml64.
Ported to a new GAS/AT&T-syntax `code/assembly/asm-vmx-checks-gas-unix.s`
instruction-for-instruction equivalent, `.globl AsmVmxSupportDetection`, plus a
`.note.GNU-stack` non-exec-stack marker. No logic change. The Windows `.asm` is
left untouched. CMake: base `SourceFiles` entry renamed to `-masm-windows.asm`,
and the `if(UNIX)` block REMOVE_ITEMs it, APPENDs the `.s`, and calls
`enable_language(ASM)` so CMake assembles it with the system assembler. Windows
`libhyperdbg.vcxproj` + `.filters` `<MASM Include=...>` updated to the renamed
`-masm-windows.asm`. Assemble-verified with `as` (exports `AsmVmxSupportDetection`).
### Remaining libhyperdbg TUs wired into Linux CMake — DONE (2026-07-24)
Same gap class as the 2026-07-24 command-file batch: four TUs present in
`libhyperdbg.vcxproj` all along but never added to `libhyperdbg/CMakeLists.txt`,
so they were compiled on Windows only and their symbols were unresolved at the
Linux CLI link. Added to `SourceFiles` (plus their four header entries):
| TU | Symbols it was missing |
|----|------------------------|
| `code/app/messaging.cpp` | `ShowMessages`, `SetTextMessageCallback`, `SetTextMessageCallbackUsingSharedBuffer`, `UnsetTextMessageCallback` |
| `code/app/packets.cpp` | `IrpBasedBufferThread` |
| `code/debugger/core/steppings.cpp` | `SteppingStepOver`, `SteppingStepOverForGu`, `SteppingRegularStepIn`, `SteppingInstrumentationStepIn`, `SteppingInstrumentationStepInForTracking` |
| `code/debugger/misc/pci-id.cpp` | `GetVendorById`, `GetDeviceFromVendor`, `FreeVendor`, `FreePciIdDatabase` |
`steppings.cpp` compiled with no changes at all. The others needed:
- `messaging.cpp` — 1 bucket-1 swap: `RtlZeroMemory``PlatformZeroMemory` (line 57).
- `packets.cpp` — bucket-1 sweep: `ZeroMemory``PlatformZeroMemory`,
`DeviceIoControl``PlatformDeviceIoControl`, `SetEvent``PlatformSetEvent`,
`CloseHandle``PlatformCloseHandle`, `GetLastError`×2→`PlatformGetLastError`.
Plus the packet-reader's dedicated device handle: the same
`CreateFileA("\\.\HyperDbgDebuggerDevice", GENERIC_READ|GENERIC_WRITE, …)`
block already ported in `libhyperdbg.cpp``PlatformOpenDevice(...)`, with the
surrounding `ERROR_ACCESS_DENIED`/`ERROR_GEN_FAILURE` handling left at the call
site (identical shape to the libhyperdbg.cpp call site).
- `pci-id.cpp` — 4× `strncpy_s`→ new `PlatformStrNCpy` (below), and
`GetVendorById` body guarded `#ifdef _WIN32` (below).
**Pure addition: `PlatformStrNCpy(Dest, DestSize, Src, Count)`** in
`platform-lib-calls.{h,c}` — Windows `strncpy_s` verbatim; Linux reproduces the
documented rules: copies D = min(Count, strlen(Src)) chars and null-terminates,
or empties Dest + returns non-zero if D doesn't fit; `Count == _TRUNCATE` instead
copies as much as fits and returns `STRUNCATE`. Sibling of the existing
`PlatformStrCpy`; a plain `PlatformStrCpy` could not be reused because
`ReadLine` (pci-id.cpp:76) copies a *substring* out of a longer stream buffer.
Also **pure addition** to the `Environment.h` Linux block: `_TRUNCATE`
(`((SIZE_T)-1)`) and `STRUNCATE` (`80`) at their canonical MSVC values, matching
the existing `CBR_*`/`ERROR_*`/`PROCESS_*` constant blocks.
⚠️ Linux branch marked `NOT YET TESTED!!` in source, like `PlatformStrCpy`.
**`GetVendorById` body guarded `#ifdef _WIN32`** (pattern 1; user chose the stub
over porting). It resolves the PCI ID database *relative to the executable*:
`GetModuleHandle`/`GetModuleFileName`, then `strrchr(Path, '\\')` to strip the
exe name and append `PCI_ID_DATABASE_PATH`. The two Win32 calls would wrap
cleanly (`readlink("/proc/self/exe")`), but the surrounding logic is
Windows-path-shaped in two places — the `'\\'` separator and the constant itself
(`pci-id.h:44`, `"constants\\pci.ids"`) — so wrapping only the calls would leave
`strrchr` returning NULL, silently overwriting the whole path and resolving
against the cwd. That is a behaviour change, not a port, so the whole body is
Windows-only and Linux returns NULL. `GetVendorByIdStr`, `GetDeviceFromVendor`,
`FreeVendor` and `FreePciIdDatabase` are plain C and compile unchanged.
Consequence: `!pcitree` / `!pcicam` show no vendor or device names on Linux.
**Result: the CLI link is down from 23 undefined refs to 9**, and every
"missing TU" bucket is now closed. What is left is both known and deliberate:
`ks_*` (5 — keystone, no Linux lib linked) and `CommandPt*`/`HyperDbgPt*`
(4 — `pt.cpp` excluded from the Linux build, port not started).
### keystone assembler stubbed on Linux — DONE (2026-07-24)
The 5 `ks_*` link errors (`ks_open`/`ks_option`/`ks_asm`/`ks_errno`/`ks_close`).
Only a **Windows** `keystone.lib` is vendored (`libraries/keystone/release-lib/`,
PE/COFF) and `dependencies/keystone/` ships **headers only** — no Linux library,
no source, not a git submodule. The `link_directories(...keystone...)` and the
`keystone` entry in `target_link_libraries` were previously commented out in the
top-level `CMakeLists.txt` to get past `cannot find -lkeystone`, which is what
left the 5 symbols unresolved.
Because `dependencies/keystone/include/keystone/keystone.h` *is* present (and
included unconditionally from `pch.h:138`), every `ks_*` **type and constant**
(`ks_engine`, `ks_err`, `ks_arch`, `KS_ARCH_X86`, `KS_MODE_64`,
`KS_OPT_SYNTAX_INTEL`, …) resolves fine on Linux — only the 5 *functions* are
missing. That means no header surgery and no `-linux.cpp` fork were needed:
`assembler.h`'s class declaration (which has `ks_err KsErr` as a member and
`ks_arch`/`KS_*` as default arguments) compiles untouched.
All 5 calls are confined to one method, so this is pattern 1 — the body of
`AssembleData::Assemble` (`assembler.cpp:119`) is guarded `#ifdef _WIN32`
(Windows verbatim) with a Linux `#else` that emits
`"err, the assembler is not supported on Linux yet"` and returns `-1`, plus
`UNREFERENCED_PARAMETER` ×4 and a TODO(Linux). No call-site changes were needed:
both callers (`HyperDbgAssembleGetLength`, `HyperDbgAssemble`) already treat a
non-zero `Assemble()` return as failure and return `FALSE`, so the stub flows
through the existing error paths. The rest of the TU stays live on Linux —
notably `ParseAssemblyData`, which does the `<symbol>` resolution.
Affects the `a` (assemble) command and anything calling `HyperDbgAssemble`.
- [ ] Real fix: build upstream Keystone for Linux → `libkeystone.a`/`.so`, then
restore the two commented-out lines in the top-level `CMakeLists.txt` and drop
the guard.
**Result: the CLI link is down to 4 undefined refs**, all `pt.cpp`
(`CommandPt`, `CommandPtHelp`, `HyperDbgPtMmapSendRequest`,
`HyperDbgPerformPtOperation`) — the one remaining deliberate exclusion.
### pt.cpp stubbed on Linux — DONE (2026-07-24) — **THE LINK NOW SUCCEEDS**
The last 4 undefined refs (`CommandPt`, `CommandPtHelp`,
`HyperDbgPerformPtOperation`, `HyperDbgPtMmapSendRequest`). `pt.cpp` was already
`REMOVE_ITEM`'d from the Linux build; it now gets a replacement stub instead of
leaving the symbols dangling, following the same pattern as symbol.cpp,
pe-parser.cpp, install.cpp and namedpipe.cpp.
New `code/debugger/commands/extension-commands/pt-linux.cpp` (`#ifdef __linux__`)
implements only the 4 externally visible functions — the two command entry
points reached from the dispatch table (`CommandPt`, `CommandPtHelp`) and the two
kernel-request helpers declared in `debugger.h`. Each prints a "not supported on
Linux yet" note; the `BOOLEAN` pair returns FALSE. Everything else in `pt.cpp` is
helper code reached only through those entry points, so it does not exist in the
Linux TU. `pt.cpp` is still left 100% untouched. CMake `if(UNIX)` now does the
usual REMOVE_ITEM + APPEND pair.
**Result: `hyperdbg-cli` links and runs on Linux for the first time.** With the
symbol-visibility fix below also in place, the binary starts, reaches the
`HyperDbg>` prompt and executes host-side commands correctly — verified with
`.help`, `.help !monitor`, `.formats 0x1337` (full hex/decimal/octal/binary/char/
time/float/double output) and `? 5 * 8` (script engine) — then exits cleanly on
`.exit`. The port is out of the compile/link phase and into the runtime phase.
Run it with:
```bash
LD_LIBRARY_PATH=$PWD/libhyperdbg:$PWD/script-engine ./hyperdbg-cli/hyperdbg-cli
```
- [ ] Port `pt.cpp` for real — see the process-control entry in the TODO ledger.
### Symbol visibility: honour the existing IMPORT_EXPORT_* model — DONE (2026-07-24)
Build-system change in the top-level `CMakeLists.txt`, two parts:
```cmake
target_compile_definitions(script-engine PRIVATE HYPERDBG_SCRIPT_ENGINE)
target_compile_definitions(libhyperdbg PRIVATE HYPERDBG_LIBHYPERDBG)
set_target_properties(script-engine libhyperdbg PROPERTIES
C_VISIBILITY_PRESET hidden
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN ON)
```
**Why.** `include/SDK/imports/user/HyperDbg*Imports.h` already carries a Linux
branch for each library's export macro — `IMPORT_EXPORT_LIBHYPERDBG` and friends
expand to `__attribute__((visibility("default")))` when the library's own
`HYPERDBG_*` macro is defined, and to nothing otherwise, mirroring the
`__declspec(dllexport)`/`dllimport` pair used on Windows. 77 symbols are annotated
for libhyperdbg and 29 for script-engine. Neither half was active on Linux: the
`HYPERDBG_*` defines were never set by CMake, and `visibility("default")` is a
no-op unless the compiler's baseline visibility is `hidden` (it can only raise a
symbol above the baseline, and the baseline was already default). So the whole
export model existed in the headers but did nothing, and every symbol in both
libraries was exported.
That matters because ELF merges same-named exported symbols across shared objects,
whereas a Windows DLL's non-exported globals are private to it. Three globals were
defined independently in both libraries and were being silently collapsed into one
object at load time:
| Symbol | libhyperdbg | script-engine |
|--------|-------------|---------------|
| `g_MessageHandler` | `header/globals/globals.h:460` | `code/globals.c:24` |
| `g_HwdbgInstanceInfo` | ” | ” |
| `g_HwdbgInstanceInfoIsValid` | ” | ” |
Turning both halves on restores the Windows semantics (private unless explicitly
exported). Exported data symbols drop to **0** in both libraries; total exports go
from everything to 264 (libhyperdbg) and 25 (script-engine). The link stays clean
**0 undefined references** — so nothing was relying on an unannotated symbol
crossing a library boundary. No source file was touched.
⚠️ `VISIBILITY_INLINES_HIDDEN` is included to match the usual CMake pairing; if a
future change takes the address of an inline member across a library boundary and
compares it, that flag is the first thing to re-check.
Grouped by subsystem. These are the shortcuts taken to reach compilation.
@ -426,7 +688,30 @@ Grouped by subsystem. These are the shortcuts taken to reach compilation.
I/O and the user-debugger path can actually open files.
### Symbols
- [ ] Replace `symbol-linux.cpp` stubs with a real ELF/DWARF symbol parser.
- [x] symbol-parser (`Sym*`) Linux stubs — DONE (2026-07-24). The 15 `Sym*`
exports (`SymConvertNameToAddress`, `SymLoadFileSymbol`, `SymbolInitLoad`,
`SymGetFieldOffset`, `SymShowDataBasedOnSymbolTypes`, `SymSetTextMessageCallback`,
…) live in the Windows-only `symbol-parser/` subproject (DbgHelp + DIA-SDK
pdbex, ~3800 LOC, not built on Linux). They're called only by
`script-engine/code/script-engine.c`, so `libscript-engine.so` was the one with
the unresolved refs. Added `script-engine/code/symbol-stub-linux.c` (new file,
`#ifdef __linux__`) implementing all 15 as no-op/failure stubs (return `0`/`FALSE`,
out-params cleared), signatures mirroring `HyperDbgSymImports.h`. Wired into
`script-engine/CMakeLists.txt` under `if(UNIX)`. User chose the stub path over a
real backend port. Resolves all 15 `Sym*` link errors.
- [ ] Replace the `symbol-linux.cpp` (`Symbol*`) and `symbol-stub-linux.c`
(`Sym*`) stubs with a real ELF/DWARF (or LLVM DebugInfo/PDB) symbol parser.
### Assembler (keystone)
- [ ] `assembler.cpp::AssembleData::Assemble` — Linux body stubbed (`return -1`).
Needs a Linux Keystone build plus the two restored CMake lines; see the
keystone section above.
### PCI ID database
- [ ] `pci-id.cpp::GetVendorById` — Linux returns NULL (whole body Windows-only).
Needs `readlink("/proc/self/exe")` **plus** a portable path separator and a
portable `PCI_ID_DATABASE_PATH` (`pci-id.h:44` is `"constants\\pci.ids"`).
Until then `!pcitree` / `!pcicam` print no vendor/device names on Linux.
### PE parsing
- [ ] Recreate Windows `IMAGE_*` headers for Linux and port `pe-parser.cpp`

View file

@ -3,19 +3,41 @@ set(SourceFiles
"../include/platform/general/header/Environment.h"
"header/common.h"
"header/globals.h"
"header/hardware.h"
"header/parse-table.h"
"header/scanner.h"
"header/script-engine.h"
"header/type.h"
"header/pch.h"
"../include/platform/user/code/platform-lib-calls.c"
"code/common.c"
"code/globals.c"
"code/hardware.c"
"code/parse-table.c"
"code/scanner.c"
"code/script-engine.c"
"code/script_include.c"
"code/type.c"
"code/pch.c"
)
if(UNIX)
#
# The symbol-parser (Sym*) exports live in the Windows-only symbol-parser/
# subproject (DbgHelp + DIA-SDK pdbex). script-engine.c calls them directly,
# so provide Linux stubs here to satisfy the link until a real backend lands.
#
list(APPEND SourceFiles "code/symbol-stub-linux.c")
#
# script_include.c resolves script #include paths via Win32 (GetModuleFileNameA
# / GetFileAttributesA). Swap it for an empty Linux stub until a real resolver
# (readlink + stat) is implemented.
#
list(REMOVE_ITEM SourceFiles "code/script_include.c")
list(APPEND SourceFiles "code/script_include-linux.c")
endif()
include_directories(
"header"
"../include"

View file

@ -496,7 +496,7 @@ HardwareScriptInterpreterCompressBuffer(UINT64 * Buffer,
//
// Copy the compressed data back to the original buffer
//
RtlZeroMemory(Buffer, BufferLength);
PlatformZeroMemory(Buffer, BufferLength);
memcpy(Buffer, TempBuffer, *NewBufferSize);
//
@ -561,7 +561,7 @@ HardwareScriptInterpreterConvertSymbolToHwdbgShortSymbolBuffer(
//
// Zeroing the short symbol buffer
//
RtlZeroMemory(HwdbgShortSymbolBuffer, *NewBufferSize);
PlatformZeroMemory(HwdbgShortSymbolBuffer, *NewBufferSize);
//
// Filling the short symbol buffer from original buffer

View file

@ -0,0 +1,71 @@
/**
* @file script_include-linux.c
* @author Max Raulea (max.raulea@hyperdbg.org)
* @brief Linux stub implementations of the script include-file resolver
* @details The Windows implementation (script_include.c) uses Win32 APIs
* (GetModuleFileNameA / GetFileAttributesA) to resolve `#include`
* directives in scripts. Those calls have no drop-in Linux equivalent,
* so for now this file provides empty stubs that satisfy the link and
* keep every call site intact. Script includes are simply unsupported
* on Linux until a real resolver (readlink("/proc/self/exe") + stat) is
* implemented.
*
* Signatures mirror script-engine/header/script_include.h exactly.
*
* TODO: implement the real Linux path resolution and drop this file
* from the UNIX branch of script-engine/CMakeLists.txt.
*
* @version 0.1
* @date 2026-07-24
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
#include "script_include.h"
#ifdef __linux__
VOID
ResolveIncludePath(const char * IncludeFilePath, char * OutPath)
{
(void)IncludeFilePath;
if (OutPath != NULL)
{
OutPath[0] = '\0';
}
}
BOOLEAN
FileExists(const char * Path)
{
(void)Path;
return FALSE;
}
BOOLEAN
ParseIncludeFile(char * IncludeFile, char ** Buffer)
{
(void)IncludeFile;
if (Buffer != NULL)
{
*Buffer = NULL;
}
return FALSE;
}
char *
InsertStrNew(char * Str, int InputIdx, const char * Buf)
{
(void)Str;
(void)InputIdx;
(void)Buf;
return NULL;
}
#endif // __linux__

View file

@ -0,0 +1,199 @@
/**
* @file symbol-stub-linux.c
* @author Max Raulea (max.raulea@hyperdbg.org)
* @brief Linux stub implementations of the symbol-parser (Sym*) exports
* @details The Windows implementation lives in the symbol-parser/ subproject and
* is built on DbgHelp + PDB files (via the DIA-SDK-based pdbex), none of
* which is available on Linux. The script-engine library calls these
* Sym* functions directly, so without definitions libscript-engine.so
* fails to link. These stubs satisfy the link and keep every call site
* intact; symbol resolution is simply unavailable until a real Linux
* backend (ELF/DWARF, or an LLVM DebugInfo/PDB port of symbol-parser)
* is implemented.
*
* The signatures mirror include/SDK/imports/user/HyperDbgSymImports.h
* exactly. Return values indicate "nothing found / not supported"
* (0 / FALSE) and any out-parameters are cleared.
*
* TODO: replace with a real symbol backend and drop this file from the
* UNIX branch of script-engine/CMakeLists.txt.
*
* @version 0.1
* @date 2026-07-24
*
* @copyright This project is released under the GNU Public License v3.
*
*/
#include "pch.h"
#ifdef __linux__
VOID
SymSetTextMessageCallback(PVOID Handler)
{
(void)Handler;
}
VOID
SymbolAbortLoading()
{
}
UINT64
SymConvertNameToAddress(const CHAR * FunctionOrVariableName, PBOOLEAN WasFound)
{
(void)FunctionOrVariableName;
if (WasFound != NULL)
{
*WasFound = FALSE;
}
return 0;
}
UINT32
SymLoadFileSymbol(UINT64 BaseAddress, const CHAR * PdbFileName, const CHAR * CustomModuleName)
{
(void)BaseAddress;
(void)PdbFileName;
(void)CustomModuleName;
return 0;
}
UINT32
SymUnloadAllSymbols()
{
return 0;
}
UINT32
SymUnloadModuleSymbol(CHAR * ModuleName)
{
(void)ModuleName;
return 0;
}
UINT32
SymSearchSymbolForMask(const CHAR * SearchMask)
{
(void)SearchMask;
return 0;
}
BOOLEAN
SymGetFieldOffset(CHAR * TypeName, CHAR * FieldName, UINT32 * FieldOffset)
{
(void)TypeName;
(void)FieldName;
if (FieldOffset != NULL)
{
*FieldOffset = 0;
}
return FALSE;
}
BOOLEAN
SymGetDataTypeSize(CHAR * TypeName, UINT64 * TypeSize)
{
(void)TypeName;
if (TypeSize != NULL)
{
*TypeSize = 0;
}
return FALSE;
}
BOOLEAN
SymCreateSymbolTableForDisassembler(PVOID CallbackFunction)
{
(void)CallbackFunction;
return FALSE;
}
BOOLEAN
SymConvertFileToPdbPath(const CHAR * LocalFilePath, CHAR * ResultPath, SIZE_T ResultPathSize)
{
(void)LocalFilePath;
if (ResultPath != NULL && ResultPathSize > 0)
{
ResultPath[0] = '\0';
}
return FALSE;
}
BOOLEAN
SymConvertFileToPdbFileAndGuidAndAgeDetails(const CHAR * LocalFilePath,
CHAR * PdbFilePath,
CHAR * GuidAndAgeDetails,
BOOLEAN Is32BitModule)
{
(void)LocalFilePath;
(void)PdbFilePath;
(void)GuidAndAgeDetails;
(void)Is32BitModule;
return FALSE;
}
BOOLEAN
SymConvertLoadedModuleToPdbFileAndGuidAndAgeDetails(const BYTE * LoadedImageBytes,
SIZE_T LoadedImageSize,
const CHAR * LocalFilePath,
CHAR * PdbFilePath,
CHAR * GuidAndAgeDetails,
BOOLEAN Is32BitModule)
{
(void)LoadedImageBytes;
(void)LoadedImageSize;
(void)LocalFilePath;
(void)PdbFilePath;
(void)GuidAndAgeDetails;
(void)Is32BitModule;
return FALSE;
}
BOOLEAN
SymbolInitLoad(PVOID BufferToStoreDetails,
UINT32 StoredLength,
BOOLEAN DownloadIfAvailable,
const CHAR * SymbolPath,
BOOLEAN IsSilentLoad)
{
(void)BufferToStoreDetails;
(void)StoredLength;
(void)DownloadIfAvailable;
(void)SymbolPath;
(void)IsSilentLoad;
return FALSE;
}
BOOLEAN
SymShowDataBasedOnSymbolTypes(const CHAR * TypeName,
UINT64 Address,
BOOLEAN IsStruct,
PVOID BufferAddress,
const CHAR * AdditionalParameters)
{
(void)TypeName;
(void)Address;
(void)IsStruct;
(void)BufferAddress;
(void)AdditionalParameters;
return FALSE;
}
#endif // __linux__