Logging.c and UnloadDll.c compile and link into HyperDbg.ko, along with
the two components that were waiting on the logging layer (BinarySearch,
OptimizationsExamples).
- new PlatformStr.{h,c}: PlatformVsnprintf/Sprintf/Strnlen replace
vsprintf_s/sprintf_s/strnlen_s; PlatformSprintf moved here out of
PlatformMem so both spellings return -1 on truncation
- BasicTypes.h: IRP/IO_STACK_LOCATION/IO_STATUS_BLOCK/UNICODE_STRING
grow the members shared code touches, plus the NT status and access
constants the notify path needs
- Environment.h: RTL_NUMBER_OF, _Analysis_assume_, ASSERT -> WARN_ON
- PlatformEvent: ExEventObjectType placeholder token
- HyperLogCallback.c stays out of the module: it defines the same
LogCallback* entry points as Logging.c (it is the per-DLL forwarding
shim on Windows), so in one link unit it is a duplicate symbol
Route Spinlock.c's three MSVC intrinsics through the platform
intrinsics layer instead of the Windows builtins:
_mm_pause -> CpuPause (already existed)
_interlockedbittestandset -> CpuInterlockedBitTestAndSet (new)
InterlockedCompareExchange-> CpuInterlockedCompareExchange (new, 32-bit)
Add the two new Cpu* wrappers to the kernel PlatformIntrinsics
{.h,.c} with win + linux arms (Linux uses __atomic_* builtins,
mirroring the existing 64-bit CAS), keeping the Cpu* wrappers the
single intrinsic boundary.
Filled in the stubs for the windows functions:
KeQueryActiveProcessorCount(0) and KeGetCurrentProcessorNumberEx(NULL).
KeQueryActiveProcessorCount(0) maps one to one to num_online_cpus()
KeGetCurrentProcessorNumberEx(NULL) maps to raw_smp_processor_id(),
however this can also be smp_processor_id(), we have to decide based on
preemption, the latter checks for preemption and throws a warning if it
is executed in preemptible code. (I dont know the kernel code well
enough so this may need some discussion)
Code audit of the script engine's scanner/token handling and of the PCI ID
database parser. Each of the issues below was reproduced against the current
code before the fix and re-checked afterwards.
script-engine/scanner.c
* An unterminated string literal ("abc or L"abc) hung the scanner in an
endless loop: sgetc() returns EOF without consuming input, and neither
string loop tested for it, so the token grew until allocation failed.
Both loops now stop at EOF and report the token as UNKNOWN.
script-engine/common.c
* AppendByte()/AppendWchar() doubled Token->MaxLen before checking whether
the larger buffer was actually allocated. After a failed allocation MaxLen
described memory that did not exist and the next append wrote past the end
of the old buffer. MaxLen is now committed only on success.
* CopyToken() allocated strlen(Value) + 1 bytes but carried over the source
token's Len and MaxLen, so the copy's advertised capacity did not match its
allocation, and WSTRING payloads were truncated at their first embedded
null byte. The copy is now sized from Len/MaxLen and copied by length, with
a fallback to the string length for the grammar tokens in parse-table.c,
which only initialize Type and Value.
* NewToken() set MaxLen to the value length, which is zero for an empty
value. The 'Len >= MaxLen - 1' test in the append routines is unsigned, so
a zero MaxLen wrapped and disabled buffer growth entirely.
* IsUnderscore() tested 'c >= '_'', which also accepted the backtick, the
lowercase letters, '{', '|', '}', '~' and DEL. Register scanning uses it,
so '@rax|1' was lexed as one malformed register name instead of a register,
an operator and a number. The pseudo-register path already compared against
'_' directly.
* NewTokenList() did not check the allocation of its Head buffer.
* NewTemp() kept the last handed-out id in a static, so an exhausted temp
list produced a token aliasing a temporary still in use, and it derived
MaxTempNumber from an out-of-range index. It also dereferenced the new
token without a null check.
* FreeTemp() indexed the MAX_TEMP_COUNT-entry map with an unchecked value
parsed out of the token text.
* RotateLeftStringOnce() wrote to str[-1] when handed an empty string.
libhyperdbg/debugger/misc/pci-id.cpp
* The database file was read into a malloc(Length) buffer that was never
null-terminated, while ReadLine() walks it with strchr(). Looking up an
absent vendor scans to the end and reads past the allocation.
* The matched Vendor was allocated with malloc() and its Devices list head
was only assigned once a device line was parsed, so a vendor with no
device entries left it uninitialized and FreeVendor() walked a garbage
pointer.
* FreeVendor() released the device and subdevice lists but never the Vendor
itself, leaking one per lookup for every enumerated PCI device.
* The file handle leaked when the buffer allocation failed, ftell() and
fread() results were unused, and several error paths leaked the Vendor or
the not-yet-linked Device/SubDevice.
* strncmp() compared sizeof(VendorId) bytes, which is the size of the
pointer rather than the length of a vendor id.
* ReadLine() passed an unclamped count to strncpy_s(), which triggers the
invalid parameter handler for a line longer than the destination.
* GetVendorById() ignored the GetModuleFileName() result and overwrote the
tail of the path buffer without checking the room left in it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3C1DuhHtqK64eEkHjKCHM