Ok, so in the last week or so I'm getting a handle on how to properly code an ASM PROC. But now I'm running into a brick wall on what seems like should be a simple Exception Handler.
At this point, all I want to do if an exception happens in my PROC is to unwind the stack from before my PROC was called and return an error code. From what I've read, it should be as simple as calling RtlUnwindEx in the exception handler, passing in the return code as one of the arguments, and then moving ExceptionContinueExecution into rax. Based on setting a break point in my exception handler, all that is happening is the exception handler is getting called over and over.
Here's what I've got so far in some test code: (NOTE: Exception.inc defines the exception structures as found at http://blogs.msdn.com/b/geoffda/archive/2010/03/10/how-to-implement-try-except-in-ml64-masm.aspx)
option casemap :none
include ksamd64.inc
include Exception.inc
.data
.code
Test_Frame struct
;Storage for 128-bit non-volatile registers
RegXMM6 OWORD ?
RegXMM7 OWORD ?
;Storage for LOCAL variables
LocalVar1 BYTE ?
Filler1 BYTE*3 ?
LocalVar2 WORD ?
Filler2 WORD ?
LocalVar3 DWORD ?
LocalVar4 DWORD ?
LocalVar5 QWORD ?
LocalVar6 QWORD ?
LocalVar7 OWORD ?
;Filler7 QWORD ?
Test_Frame ends
Test_eh PROC ;ExceptionRecord:PEXCEPTION_RECORD, ExtablisherFrame:QWORD, ContextRecord:PCONTEXT, DispatcherContext:PDISPATCHER_CONTEXT
push rbp ;get the stack 16 byte aligned
sub rsp, 30h ;allocate shadow space
mov rax, 0 ;Load parameters for RtlUnwindeEx
mov [rsp+28h], rax
mov [rsp+20h], r9
mov r9, 012345678h ;Return Value
mov r8, rcx
mov rdx, 0
mov rcx, rdx
call RtlUnwindEx
mov rax, ExceptionContinueExecution
add rsp, 30h
pop rbp
ret
Test_eh ENDP
public Test_asm
Test_asm PROC FRAME : Test_eh ;Parm1:BYTE, Parm2:WORD, Parm3:DWORD, Parm4:QWORD, Parm5:DWORD, Parm6:QWORD
;Push non-volatile registers
push_reg rbx
push_reg rsi
push_reg rsi
;Alloc Frame Struct and Set Frame Pointer
alloc_stack( sizeof Test_Frame )
set_frame rsp, 0 ;only needed for dynamic stack allocation (alloca)
;Save 128-bit non-volatile registers
save_xmm128 xmm6, Test_Frame.RegXMM6
save_xmm128 xmm7, Test_Frame.RegXMM7
SizeOfStackFrame EQU <(sizeof Test_Frame) +8 +8 +8 +8>
;Parameters
Parm1 EQU < BYTE PTR [rsp + SizeOfStackFrame +00h]>
Parm2 EQU < WORD PTR [rsp + SizeOfStackFrame +08h]>
Parm3 EQU <DWORD PTR [rsp + SizeOfStackFrame +10h]>
Parm4 EQU <QWORD PTR [rsp + SizeOfStackFrame +18h]>
Parm5 EQU <DWORD PTR [rsp + SizeOfStackFrame +28h]>
Parm6 EQU <QWORD PTR [rsp + SizeOfStackFrame +28h]>
.ENDPROLOG
;Dump register parameters to their shadow space
mov Parm1, cl
mov Parm2, dx
mov Parm3, r8d
mov Parm4, r9
;LOCAL variables
Var1 EQU <[rbp].Test_Frame.LocalVar1>
Var2 EQU <[rbp].Test_Frame.LocalVar2>
Var3 EQU <[rbp].Test_Frame.LocalVar3>
Var4 EQU <[rbp].Test_Frame.LocalVar4>
Var5 EQU <[rbp].Test_Frame.LocalVar5>
Var6 EQU <[rbp].Test_Frame.LocalVar6>
;Your Code Goes HERE
mov al, Parm1
mov Var1, 01h
mov Var2, 0123h
mov Var3, 4567h
mov Var4, 012345678h
mov rbx, 0 ;!!! Cause an excepetion !!!
mov eax, [rbx]
;EPILOG
movdqa XMM7, [rbp].Test_Frame.RegXMM7
movdqu XMM6, [rbp].Test_Frame.RegXMM6
add rsp, ( sizeof Test_Frame )
pop rsi
pop rbx
ret
Test_asm ENDP
What am I missing?
first off, you need to make sure your exception handler also has xdata associated with it.
HOOKOODOOKU,
EDGAR (donkey) had a similar problem with Vectored Exception Handlers in 64-Bit Windows,...
The thread is here: AddVectoredExceptionHandler Single Stepping (http://www.masm32.com/board/index.php?topic=16242.0)
Quote from: tofu-sensei on February 10, 2012, 10:17:28 PM
first off, you need to make sure your exception handler also has xdata associated with it.
That's what I thought the Exception Handling MASM macros (the prolog) was supposed to do:
;Push non-volatile registers
push_reg rbx
push_reg rsi
push_reg rsi
;Alloc Frame Struct and Set Frame Pointer
alloc_stack( sizeof Test_Frame )
set_frame rsp, 0 ;only needed for dynamic stack allocation (alloca)
;Save 128-bit non-volatile registers
save_xmm128 xmm6, Test_Frame.RegXMM6
save_xmm128 xmm7, Test_Frame.RegXMM7
.ENDPROLOG
...
Is there something else I have to add?
Follow 2 simplest codes, I have tested it only using XP, so I do not know if this will work in others O.S.
.code
align 16
start proc FRAME:Structured_error
.ENDPROLOG
mov rax,0 ;intentional error
mov rax,[rax]
continue_program::
;int 3h
xor rcx,rcx
call ExitProcess
start endp
align 16
Structured_error proc
jmp continue_program
ret
Structured_error endp
END
public handle_error
handle_error PROTO
.code
align 16
start proc uses rbx rsi rdi hInstance:qword,command_line:qword
FROM_HERE::
mov rax,cr4 ;intentional error
TO_HERE::
xor rcx,rcx
call ExitProcess
start endp
align 16
handle_error proc
jmp TO_HERE
handle_error endp
pdata segment readonly ALIAS (".pdata")
align 8
dd imagerel FROM_HERE
dd imagerel TO_HERE
dd imagerel SCOPE ;look below
pdata ends
align 8
xdata segment read ALIAS (".xdata")
SCOPE db 19h,0,0,0
dd imagerel handle_error ;procedure to watch
xdata ends
END
Quote from: HooKooDooKu on February 10, 2012, 10:49:28 PM
That's what I thought the Exception Handling MASM macros (the prolog) was supposed to do:
yes, you need to use them in your exception handler function (Test_eh) as well.
Quote from: baltoro on February 10, 2012, 10:26:51 PM
HOOKOODOOKU,
EDGAR (donkey) had a similar problem with Vectored Exception Handlers in 64-Bit Windows,...
The thread is here: AddVectoredExceptionHandler Single Stepping (http://www.masm32.com/board/index.php?topic=16242.0)
It doesn't appear donkey's situation applies to me. I don't have an application for which I'm attempting to install a program level exception handler. I simply have a function that I'm using the exception macros defined by Microsoft to allow the stack to be unwound using simple prolog/epilog code. Where as donkey had to call a function to add a vectored exception handler, I'm simply using the FRAME keyword for PROC to specify the exception function.
I'm not having any problem getting the logic to jump to the exception function. And from what I THINK I understand, all I need to do is simply call RtlUnwindEx and it should unwind the stack for my back to the start of my function and set a return value for the function.
So I guess one question is whether I'm even attempting to go about this correctly. But assuming that I am, I can not find any useful example code that shows RtlUnwindEx being called (especially from an exception function). So far, here's the best logic I've been able to come up with, but when RtlUnwindEx is called, it simply jumps back to the start of my excption handler (i.e. apparently an exception is occuring inside of RtlUnwindEx).
Test_eh PROC FRAME ;ExceptionRecord:PEXCEPTION_RECORD, EstablisherFrame:QWORD, ContextRecord:PCONTEXT, DispatcherContext:PDISPATCHER_CONTEXT
push_reg rbx ;get the stack 16 byte aligned
alloc_stack( 30h ) ;allocate shadow space for call to RtlUnwindEx
set_frame rsp, 0
.ENDPROLOG
;Parameters
pExceptionRecord EQU <[rsp +30h +8+8 +00h]>
pEstablisherFrame EQU <[rsp +30h +8+8 +08h]>
pContextRecord EQU <[rsp +30h +8+8 +10h]>
pDispatcherContext EQU <[rsp +30h +8+8 +18h]>
;Dump register parameters to their shadow space
mov pExceptionRecord, rcx
mov pEstablisherFrame, rdx
mov pContextRecord, r8
mov pDispatcherContext, r9
;Call RtlUnwindEx with a Return code of 12345678h
mov rbx, pDispatcherContext
mov rax, [rbx].DISPATCHER_CONTEXT.HistoryTable
mov [rsp+28h], rax
mov rax, pContextRecord
mov [rsp+20h], rax
mov r9, 012345678h ;Return Value
mov r8, pExceptionRecord
mov rdx, [rbx].DISPATCHER_CONTEXT.TargetIp
mov rcx, pEstablisherFrame
call RtlUnwindEx
;RtlUnwindEx isn't supposet to 'return' back to here
; so there doesn't seem be be a need for any epilog
Test_eh ENDP
Check EXCEPTION_RECORD.ExceptionFlags for EXCEPTION_UNWIND, if that flag is set return ExceptionContinueSearch.