1

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

它使用 Windows SEH 捕获各种类型的陷阱:除以零、非法访问……并使用 Windows 提供的上下文信息打印寄存器转储,显示陷阱时机器的状态。(它做了很多与问题无关的其他事情,例如打印函数回溯或根据需要从除以零中恢复)。这允许“目标代码”的编写者了解他的程序出了什么问题。

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

在一个系统上,我得到的正是那个寄存器转储。我可以在这里展示它,但它不会给我的问题添加任何东西。所以,很明显我的运行时系统中的 SEH 可以捕捉到这一点,并显示情况。这台机器上没有任何MS开发工具。

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

我假设行为差异是由某处的 Windows 配置或 Visual Studio 控制的东西引起的。系统菜单不是DEP配置;它们都被配置(vanilla)为“标准系统进程的 DEP”。我的运行时系统可执行文件配置了“No (/NXCOMPAT:NO)”。

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

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

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

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

我想在哪里配置/控制这个有什么线索吗?

我在这里提供了我正在使用的 SEH 代码。它已被编辑以删除大量的健全性检查代码,我声称这些代码对该代码中看到的明显状态没有影响。

运行时系统的顶层生成一组工作线程(使用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>
4

1 回答 1

1

“标准系统流程的 DEP”对您没有帮助;它在内部被称为“OptIn”。您需要的是在IMAGE_DLLCHARACTERISTICS_NX_COMPAT.exe 文件的 PE 标头中设置的标志。或者调用SetProcessDEPPolicykernel32.dll 中的函数SetProcessMitigationPolicy也不错...但是直到 Windows 8 才可用。

Ed Maurer 的博客上有一些很好的解释,它解释了 .NET 如何使用 DEP(您不会关心)以及系统规则(您会这样做)。

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

于 2014-09-11T21:41:37.547 回答