结构化异常处理程序在几乎相同的机器上以不同的方式捕获接近零的EIP陷阱?

时间:2014-09-11 21:35:09

标签: windows exception-handling crash config seh

我有一个相当复杂但经过严格测试的汇编语言x86-32应用程序,可在各种x86-32和x86-64机器上运行。这是语言编译器的运行时系统,因此它支持执行另一个已编译的二进制程序,即#34;目标代码"。

它使用Windows SEH捕获各种陷阱:除零,非法访问,...并使用Windows提供的上下文信息打印注册转储,显示陷阱时机器的状态。 (它会执行许多与问题无关的其他内容,例如打印函数回溯或根据需要从除法中恢复)。这允许"目标代码的编写者"了解他的计划出了什么问题。

在我认为非法内存访问的两个Windows 7-64系统上,它们或多或少相同,它的行为不同。具体问题是"目标代码" (不是经过良好测试的运行时系统)某处愚蠢地将0x82加载到EIP中;这是地址空间AFAIK中不存在的页面。我希望通过SEH获得Windows陷阱,并期望使用EIP = 00000082等进行注册转储。

在一个系统上,我完全得到了寄存器转储。我可以在这里展示它,但它并没有为我的问题添加任何内容。因此,很明显我的运行时系统中的SEH可以捕获它,并显示情况。这台机器上没有任何MS开发工具。

在另一个(" mystery")系统上,运行系统和目标代码具有相同的二进制文件,我得到的只是命令提示符。没有进一步的输出FWIW,这台机器上有MS Visual Studio 2010。神秘机器被大量用于其他目的,并且在正常使用中没有显示其他有趣的行为。

我认为行为差异是由某个地方的Windows配置引起的,或者是Visual Studio控制的。 DEP配置不是系统菜单;它们都被配置(vanilla)为" DEP用于标准系统进程"。我的运行时系统可执行文件有"否(/ NXCOMPAT:NO)"构造

这两款机器都是i7但不同的芯片,4芯,大量内存,不同的主板。我不认为这是相关的;当然这两个CPU都以相同的方式捕获陷阱。

运行时系统在启动时包含以下行:

SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); //在崩溃时停止Windows弹出

这是最近添加的,以防止"神秘"系统显示一个弹出窗口," xxx.exe已停止工作"崩溃发生时。弹出框的行为并不会发生在第一个系统上,所以这一切都是将问题推到了“神秘”的另一个角落。机。

我希望配置/控制它的任何线索?

我在这里提供了我正在使用的SEH代码。它已被编辑 删除大量的健全检查代码 我声称对所见的状态没有影响 在这段代码中。

运行时系统的顶层生成一组worker 线程(使用CreateThread)和指向执行ASMGrabGranuleAndGo; 每个线程都设置自己的SEH,并分支到工作窃取调度程序RunReadyGranule。据我所知,SEH没有改变 之后;至少,运行时系统和"目标代码"做 不这样做,但我不知道底层是什么(例如标准" C") 图书馆可能会这样做。

再往下我提供陷阱处理​​程序TopLevelEHFilter。 是的,它可能是套准印刷机械本身的打击 导致第二个例外。我会尽快再次检查, 但是IIRC我最后一次尝试在调试器中捕获这个 神秘的机器,没有将控制传递给调试器,只是 给了我弹出窗口。

public ASMGrabGranuleAndGo
ASSUME FS:NOTHING ; cancel any assumptions made for this register
ASMGrabGranuleAndGo:
;Purpose: Entry for threads as workers in PARLANSE runtime system.
;   Each thread initializes as necessary, just once,
;   It then goes and hunts for work in the GranulesQ
;   and start executing a granule whenever one becomes available

; install top level exception handler

; Install handler for hardware exceptions
cmp        gCompilerBreakpointSet, 0
jne        HardwareEHinstall_end ; if set, do not install handler
push       offset TopLevelEHFilter ; push new exception handler on Windows thread stack
mov        eax, [TIB_SEH]    ; expected to be empty
test       eax, eax
BREAKPOINTIF jne
push       eax               ; save link to old exception handler
mov        fs:[TIB_SEH], esp ; tell Windows that our exception handler is active for this thread
HardwareEHinstall_end:

;Initialize FPU to "empty"... all integer grains are configured like this   
finit
fldcw      RTSFPUStandardMode

lock sub   gUnreadyProcessorCount, 1  ; signal that this thread has completed its initialization

@@: push       0                           ; sleep for 0 ticks
call       MySleep                     ; give up CPU (lets other threads run if we don't have enuf CPUs)
lea        esp, [esp+4]                ; pop arguments
mov        eax, gUnreadyProcessorCount ; spin until all other threads have completed initialization
test       eax, eax
jne        @b

mov        gThreadIsAlive[ecx], TRUE ; signal to scheduler that this thread now officially exists
jmp        RunReadyGranule    
ASMGrabGranuleAndGo_end:

;-------------------------------------------------------------------------------

TopLevelEHFilter: ; catch Windows Structured Exception Handling "trap"
; Invocation:
;   call  TopLevelEHFilter(&ReportRecord,&RegistrationRecord,&ContextRecord,&DispatcherRecord)
;         The arguments are passed in the stack at an offset of 8 (<--NUMBER FROM MS DOCUMENT)
;   ESP here "in the stack" being used by the code that caused the exception
;   May be either grain stack or Windows thread stack
extern exit :proc
extern syscall @RTSC_PrintExceptionName@4:near ; FASTCALL

push       ebp                     ; act as if this is a function entry
mov        ebp, esp                ; note: Context block is at offset ContextOffset[ebp]

IF_USING_WINDOWS_THREAD_STACK_GOTO unknown_exception, esp ; don't care what it is, we're dead
    ; *** otherwise, we must be using PARLANSE function grain stack space
    ; Compiler has ensured there's enough room, if the problem is a floating point trap
    ; If the problem is illegal memory reference, etc,
    ; there is no guarantee there is enough room, unless the application is compiled 
    ; with -G ("large stacks to handle exception traps")

; check what kind of exception 
mov        eax, ExceptionRecordOffset[ebp]
mov        eax, ExceptionRecord.ExceptionCode[eax]
cmp        eax, _EXCEPTION_INTEGER_DIVIDE_BY_ZERO
je         div_by_zero_exception
cmp        eax, _EXCEPTION_FLOAT_DIVIDE_BY_ZERO
je         float_div_by_zero_exception
jmp        near ptr unknown_exception  

float_div_by_zero_exception:
mov        ebx, ContextOffset[ebp] ; ebx = context record
mov        Context.FltStatusWord[ebx], CLEAR_FLOAT_EXCEPTIONS    ; clear any floating point exceptions
mov        Context.FltTagWord[ebx], -1 ; Marks all registers as empty
div_by_zero_exception: ; since RTS itself doesn't do division (that traps),
; if we get *here*, then we must be running a granule and EBX for granule points to GCB
mov        ebx, ContextOffset[ebp] ; ebx = context record

mov        ebx, Context.Rebx[ebx] ; grain EBX has to be set for AR Allocation routines
ALLOCATE_2TOK_BYTES 5             ; 5*4=20 bytes needed for the exception structure
mov        ExceptionBufferT.cArgs[eax], 0
mov        ExceptionBufferT.pException[eax], offset RTSDivideByZeroException    ; copy ptr to exception

mov        ebx, ContextOffset[ebp] ; ebx = context record
mov        edx, Context.Reip[ebx]
mov        Context.Redi[ebx], eax  ; load exception into thread's edi

GET_GRANULE_TO ecx

; This is Windows SEH (Structured Exception Handler... see use of Context block below! 

mov        eax, edx
LOOKUP_EH_FROM_TABLE   ; protected by DelayAbort
TRUST_JMP_INDIRECT_OK eax
mov        Context.Reip[ebx], eax

mov        eax, ExceptionContinueExecution ; signal to Windows: "return to caller" (we've revised the PC to go to Exception handler)
leave
ret

TopLevelEHFilter_end:

unknown_exception:
<print registers, etc. here>

1 个答案:

答案 0 :(得分:0)

&#34; DEP用于标准系统流程&#34;不会帮助你;它是internally known as "OptIn"。您需要的是.exe文件的PE头中设置的IMAGE_DLLCHARACTERISTICS_NX_COMPAT标志。或者调用kernel32.dll中的SetProcessDEPPolicy函数SetProcessMitigationPolicy也不错......但是在Windows 8之前它不可用。

有一些nice explanation on Ed Maurer's blog,它解释了.NET如何使用DEP(你不会关心它)以及系统规则(你做过)。

BIOS设置也会影响硬件NX是否可用。