Files
Viper_VRTE/w32/win32process.vp
T
TermiNexus 7651da8ac9 feat(traceback): 崩溃也要有栈——挂 SetUnhandledExceptionFilter 做未处理异常转储
动机:实测同一处崩溃开 ASan=0x80000003(int3)、关 ASan=0xC0000005(AV),两者都没有任何堆栈(ASan 过了报告窗口就不再输出)

Install 追加注册 SetUnhandledExceptionFilter(CrashHandler)(与 SetConsoleCtrlHandler 并列,均在 _State.Installed=1 之前挂好);CrashHandler/DumpCrashThread 在出错线程上转储:EXCEPTION_POINTERS/EXCEPTION_RECORD 一律经 ReadProcessMemory 读取,OS 的 CONTEXT 先拷进 ContextBuf 再走栈(StackWalk64 会回写它遍历的记录,直接传 OS 的记录会改坏异常上下文)

恒返回 EXCEPTION_CONTINUE_SEARCH:本路径只补栈,绝不吞异常(退出码/WER/ASan 报告行为原样保留);走栈 dbghelp 优先(崩溃点通常在系统 DLL 里)→ RBP 链 → 栈扫描,与 Ctrl+C 转储共用同一把重入锁

抽出 DbgHelpUsable() 与 PrintFrames() 给两条路径共用(DumpMainThread 同步改用,行为不变)

win32process.vp 新增 SetUnhandledExceptionFilter 声明 + EXCEPTION_POINTERS/EXCEPTION_RECORD 布局注释;traceback.vp 新增第十四节,第一/二/六节文档同步
2026-09-28 12:03:15 +08:00

225 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t
from stdint import *
from w32.win32base import *
PROCESS_TERMINATE: t.CDefine = 0x0001
PROCESS_CREATE_THREAD: t.CDefine = 0x0002
PROCESS_VM_OPERATION: t.CDefine = 0x0008
PROCESS_VM_READ: t.CDefine = 0x0010
PROCESS_VM_WRITE: t.CDefine = 0x0020
PROCESS_QUERY_INFORMATION: t.CDefine = 0x0400
PROCESS_ALL_ACCESS: t.CDefine = 0x001FFFFF
THREAD_TERMINATE: t.CDefine = 0x0001
THREAD_SUSPEND_RESUME: t.CDefine = 0x0002
THREAD_GET_CONTEXT: t.CDefine = 0x0008
THREAD_SET_CONTEXT: t.CDefine = 0x0010
THREAD_QUERY_INFORMATION: t.CDefine = 0x0040
THREAD_ALL_ACCESS: t.CDefine = 0x001FFFFF
CREATE_SUSPENDED: t.CDefine = 0x00000004
CREATE_NEW_CONSOLE: t.CDefine = 0x00000010
CREATE_NEW_PROCESS_GROUP: t.CDefine = 0x00000200
CREATE_NO_WINDOW: t.CDefine = 0x08000000
DETACHED_PROCESS: t.CDefine = 0x00000008
STARTF_USESHOWWINDOW: t.CDefine = 0x00000001
STARTF_USESTDHANDLES: t.CDefine = 0x00000100
SW_HIDE: t.CDefine = 0
SW_SHOW: t.CDefine = 5
SW_MINIMIZE: t.CDefine = 6
NORMAL_PRIORITY_CLASS: t.CDefine = 0x00000020
IDLE_PRIORITY_CLASS: t.CDefine = 0x00000040
HIGH_PRIORITY_CLASS: t.CDefine = 0x00000080
REALTIME_PRIORITY_CLASS: t.CDefine = 0x00000100
BELOW_NORMAL_PRIORITY_CLASS: t.CDefine = 0x00004000
ABOVE_NORMAL_PRIORITY_CLASS: t.CDefine = 0x00008000
STILL_ACTIVE: t.CDefine = 259
class STARTUPINFOA:
cb: DWORD
lpReserved: CHARPTR
lpDesktop: CHARPTR
lpTitle: CHARPTR
dwX: DWORD
dwY: DWORD
dwXSize: DWORD
dwYSize: DWORD
dwXCountChars: DWORD
dwYCountChars: DWORD
dwFillAttribute: DWORD
dwFlags: DWORD
wShowWindow: WORD
cbReserved2: WORD
lpReserved2: VOIDPTR
hStdInput: HANDLE
hStdOutput: HANDLE
hStdError: HANDLE
class STARTUPINFOW():
cb: DWORD
lpReserved: WCHARPTR
lpDesktop: WCHARPTR
lpTitle: WCHARPTR
dwX: DWORD
dwY: DWORD
dwXSize: DWORD
dwYSize: DWORD
dwXCountChars: DWORD
dwYCountChars: DWORD
dwFillAttribute: DWORD
dwFlags: DWORD
wShowWindow: WORD
cbReserved2: WORD
lpReserved2: VOIDPTR
hStdInput: HANDLE
hStdOutput: HANDLE
hStdError: HANDLE
class PROCESS_INFORMATION():
hProcess: HANDLE
hThread: HANDLE
dwProcessId: DWORD
dwThreadId: DWORD
extern def CreateProcessA(lpApplicationName: LPCSTR, lpCommandLine: CHARPTR, lpProcessAttributes: SECURITY_ATTRIBUTES | t.CPtr, lpThreadAttributes: SECURITY_ATTRIBUTES | t.CPtr, bInheritHandles: BOOL, dwCreationFlags: ULONG, lpEnvironment: VOIDPTR, lpCurrentDirectory: LPCSTR, lpStartupInfo: STARTUPINFOA | t.CPtr, lpProcessInformation: PROCESS_INFORMATION | t.CPtr) -> BOOL
extern def CreateProcessW(lpApplicationName: LPCWSTR, lpCommandLine: WCHARPTR, lpProcessAttributes: SECURITY_ATTRIBUTES | t.CPtr, lpThreadAttributes: SECURITY_ATTRIBUTES | t.CPtr, bInheritHandles: BOOL, dwCreationFlags: ULONG, lpEnvironment: VOIDPTR, lpCurrentDirectory: LPCWSTR, lpStartupInfo: STARTUPINFOW | t.CPtr, lpProcessInformation: PROCESS_INFORMATION | t.CPtr) -> BOOL
extern def TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL
extern def GetExitCodeProcess(hProcess: HANDLE, lpExitCode: ULONG | t.CPtr) -> BOOL
extern def GetExitCodeThread(hThread: HANDLE, lpExitCode: ULONG | t.CPtr) -> BOOL
extern def OpenProcess(dwDesiredAccess: ULONG, bInheritHandle: BOOL, dwProcessId: ULONG) -> HANDLE
extern def GetCurrentProcess() -> HANDLE
extern def GetCurrentProcessId() -> ULONG
extern def CreateThread(lpThreadAttributes: SECURITY_ATTRIBUTES | t.CPtr, dwStackSize: sizet, lpStartAddress: VOIDPTR, lpParameter: VOIDPTR, dwCreationFlags: ULONG, lpThreadId: ULONG | t.CPtr) -> HANDLE
extern def OpenThread(dwDesiredAccess: ULONG, bInheritHandle: BOOL, dwThreadId: ULONG) -> HANDLE
extern def SuspendThread(hThread: HANDLE) -> ULONG
extern def ResumeThread(hThread: HANDLE) -> ULONG
extern def TerminateThread(hThread: HANDLE, dwExitCode: ULONG) -> BOOL
extern def GetCurrentThread() -> HANDLE
extern def GetCurrentThreadId() -> ULONG
extern def GetThreadId(hThread: HANDLE) -> ULONG
extern def GetProcessId(hProcess: HANDLE) -> ULONG
extern def SetThreadPriority(hThread: HANDLE, nPriority: INT) -> BOOL
extern def GetThreadPriority(hThread: HANDLE) -> INT
extern def ExitProcess(uExitCode: UINT) -> VOID
extern def ExitThread(dwExitCode: ULONG) -> VOID
extern def TlsAlloc() -> ULONG
extern def TlsFree(dwTlsIndex: ULONG) -> BOOL
extern def TlsGetValue(dwTlsIndex: ULONG) -> VOIDPTR
extern def TlsSetValue(dwTlsIndex: ULONG, lpTlsValue: VOIDPTR) -> BOOL
# =============================================================================
# x86-64 CONTEXT(线程上下文;winnt.h 的 DECLSPEC_ALIGN(16) CONTEXT)
#
# 用途:SuspendThread 主线程后用 GetThreadContext 取 RIP/RSP/RBP,供栈回溯。
# 字段顺序与偏移**逐字段**对齐 winnt.h 的 AMD64 定义(偏移见每行注释);字段数
# 不能裁剪——GetThreadContext(..., CONTEXT_FULL) 会把整块(含 FltSave/向量寄存器)
# 写回调用方缓冲,缓冲小于 sizeof(CONTEXT)=0x4D0=1232 即写穿堆。同理
# dbghelp!StackWalk64 每走一帧都会把它自己那份 CONTEXT(同为 0x4D0)写回传入的
# ContextRecord —— 本结构原先漏了 0x4B0/0x4B8 的 LastBranchToRip/LastBranchFromRip,
# 于是尺寸只有 0x4C0,被 ASan 抓成 "heap-buffer-overflow ... WRITE of size 1232",
# 故这两个字段**必须**保留。
#
# 布局核对(x64,非 packed,自然对齐):
# 0x000 P1Home..P6Home 6×u64
# 0x030 ContextFlags, MxCsr 2×u32
# 0x038 SegCs..SegSs 6×u16
# 0x044 EFlags 1×u32
# 0x048 Dr0..Dr3,Dr6,Dr7 6×u64
# 0x078 Rax..R15 16×u64
# 0x0F8 Rip 1×u64
# 0x100 FltSave (XMM_SAVE_AREA32) 512 字节
# 0x300 VectorRegister[26] (M128A) 416 字节
# 0x4A0 VectorControl 1×u64
# 0x4A8 DebugControl 1×u64
# 0x4B0 LastBranchToRip 1×u64
# 0x4B8 LastBranchFromRip 1×u64
# 0x4C0 LastExceptionToRip 1×u64
# 0x4C8 LastExceptionFromRip 1×u64
# 合计 0x4D0 = 1232 字节(16 的倍数;结构自身对齐要求 16,见下方调用方约定)
#
# FltSave / VectorRegister 只需占位保证偏移与总长:本模块不读其内部字段,
# 故用定长字节数组表示(起始偏移 0x100 / 0x300 本身即 16 的倍数,不引入对齐偏差)。
# 调用方约定:GetThreadContext 要求 lpContext 16 字节对齐 —— 本仓由
# stdlib.malloc/calloc 分配(x64 CRT 保证 16 字节对齐),不要用栈上局部变量。
# =============================================================================
CONTEXT_AMD64: t.CDefine = 0x00100000
CONTEXT_CONTROL: t.CDefine = 0x00100001
CONTEXT_INTEGER: t.CDefine = 0x00100002
CONTEXT_SEGMENTS: t.CDefine = 0x00100004
CONTEXT_FLOATING_POINT: t.CDefine = 0x00100008
CONTEXT_DEBUG_REGISTERS: t.CDefine = 0x00100010
CONTEXT_FULL: t.CDefine = 0x0010000B
class CONTEXT():
P1Home: ULONGLONG # 0x000
P2Home: ULONGLONG # 0x008
P3Home: ULONGLONG # 0x010
P4Home: ULONGLONG # 0x018
P5Home: ULONGLONG # 0x020
P6Home: ULONGLONG # 0x028
ContextFlags: DWORD # 0x030
MxCsr: DWORD # 0x034
SegCs: WORD # 0x038
SegDs: WORD # 0x03A
SegEs: WORD # 0x03C
SegFs: WORD # 0x03E
SegGs: WORD # 0x040
SegSs: WORD # 0x042
EFlags: DWORD # 0x044
Dr0: ULONGLONG # 0x048
Dr1: ULONGLONG # 0x050
Dr2: ULONGLONG # 0x058
Dr3: ULONGLONG # 0x060
Dr6: ULONGLONG # 0x068
Dr7: ULONGLONG # 0x070
Rax: ULONGLONG # 0x078
Rcx: ULONGLONG # 0x080
Rdx: ULONGLONG # 0x088
Rbx: ULONGLONG # 0x090
Rsp: ULONGLONG # 0x098
Rbp: ULONGLONG # 0x0A0
Rsi: ULONGLONG # 0x0A8
Rdi: ULONGLONG # 0x0B0
R8: ULONGLONG # 0x0B8
R9: ULONGLONG # 0x0C0
R10: ULONGLONG # 0x0C8
R11: ULONGLONG # 0x0D0
R12: ULONGLONG # 0x0D8
R13: ULONGLONG # 0x0E0
R14: ULONGLONG # 0x0E8
R15: ULONGLONG # 0x0F0
Rip: ULONGLONG # 0x0F8
FltSave: t.CArray[t.CUInt8T, 512] # 0x100(XMM_SAVE_AREA32)
VectorRegister: t.CArray[t.CUInt8T, 416] # 0x300(M128A[26])
VectorControl: ULONGLONG # 0x4A0
DebugControl: ULONGLONG # 0x4A8
LastBranchToRip: ULONGLONG # 0x4B0
LastBranchFromRip: ULONGLONG # 0x4B8
LastExceptionToRip: ULONGLONG # 0x4C0
LastExceptionFromRip: ULONGLONG # 0x4C8
# 取线程上下文(processthreadsapi.h)。hThread 需带 THREAD_GET_CONTEXT 权限;
# ContextFlags 必须先写入 CONTEXT_AMD64 | ...(x64;本仓用 CONTEXT_FULL)。
# 返回 0 = 失败(GetLastError 取原因)。
extern def GetThreadContext(hThread: HANDLE, lpContext: CONTEXT | t.CPtr) -> BOOL
# LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER);
# 注册进程级「未处理异常」过滤器(errhandlingapi.h,kernel32)。
# 回调签名:LONG CALLBACK Filter(struct _EXCEPTION_POINTERS *)
# struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; // +0x00
# PCONTEXT ContextRecord; // +0x08 }
# struct _EXCEPTION_RECORD { DWORD ExceptionCode; // +0x00
# DWORD ExceptionFlags; // +0x04
# struct _EXCEPTION_RECORD* Nested; // +0x08
# PVOID ExceptionAddress; // +0x10
# ... } // 共 0x98 字节
# 回调在**出错线程**上运行;返回 EXCEPTION_CONTINUE_SEARCH(0) = 交还系统默认处理(不吞异常)。
# 第一个形参按本仓「函数指针用 t.CPtr 传」声明(先例同 SetConsoleCtrlHandler);返回值是
# **上一个**过滤器(首次注册时为 NULL),故本仓不检查返回值。用法见 includes/traceback.vp
# 的 Install / DumpCrashThread(崩溃栈转储)。
extern def SetUnhandledExceptionFilter(TopLevelExceptionFilter: t.CPtr) -> t.CPtr