动机:实测同一处崩溃开 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 新增第十四节,第一/二/六节文档同步
225 lines
10 KiB
Plaintext
225 lines
10 KiB
Plaintext
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
|