flat assembler
Message board for the users of flat assembler.
Index
> Windows > win64 exception handling using PE32+ exception directory |
Author |
|
Feryno 08 Mar 2010, 09:22
my first working sample of win64 exception handling using exception directory
you may extend and improve it, then please post your experiments and successes Code: ; howto debug this using fdbg: ; if you intercept the EXCEPTION_INT_DIVIDE_BY_ZERO exception by debugger, place a breakpoint at expt_handler (that is at instruction sub rsp,28h) and pass exception back to application by Action -> Run unhandled Ctrl-F12 format PE64 GUI at (1 shl 32) entry start include '%fasminc%\win64a.inc' include 'exception.inc' IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 ; Exception Directory section '.code' code readable executable ex_handl_start: start: push rbx sub rsp,8*(4+0) xor ebx,ebx buggy_instruction: div ebx sizeog_buggy_instruction = $ - buggy_instruction ; I'm king if I go here: xor r9,r9 lea r8,[msg_empty] lea rdx,[msg_recovered] xor ecx,ecx call [MessageBoxA] exit: ;xor eax,eax ;add rsp,8*(4+0) ;pop rbx ;ret xor ecx,ecx call [ExitProcess] ex_handl_end: align 10h expt_handler: ; RCX = ? ; RDX = ? ; R8 = CONTEXT64 ; R9 = ? sub rsp,8*(4+1) virtual at r8 context CONTEXT64 end virtual ; Necessary to skip instruction causing exception. NTDLL.RtlRestoreContext will restore context after return from the exception handler. ; Opcode size is hardcoded here, but we can use routines from fdbg disasm engine to determine opcode size. add [context.Rip],sizeog_buggy_instruction xor r9,r9 xor r8,r8 lea rdx,[msg] xor ecx,ecx call [MessageBoxA] if EXCEPTION_CONTINUE_SEARCH = 0 xor eax,eax else mov eax,EXCEPTION_CONTINUE_SEARCH end if add rsp,8*(4+1) ret msg db 'An exception occured in the main application, trying to continue over it thanks to Exception Directory Entry.',0 msg_recovered db 'The main application recovered from the exception successfully by skipping the instruction causing the exception.' msg_empty db 0 section '.pdata' readable writeable data IMAGE_DIRECTORY_ENTRY_EXCEPTION ; typedef struct _RUNTIME_FUNCTION { ; DWORD BeginAddress; ; DWORD EndAddress; ; DWORD UnwindData; ; } RUNTIME_FUNCTION, *PRUNTIME_FUNCTION; ; All three fields are RVAs (otherwise there wouldn't be dwords). ; BeginAddress Points to the start address of the involved part of code. ; EndAddress Points to the end address of the same part of code. ; UnwindData Points to an UNWIND_INFO structure. dd RVA ex_handl_start dd RVA ex_handl_end dd RVA ex_handl_unwind end data ex_handl_unwind: ; The UNWIND_INFO structure tells how the portion of code should be handled. Here's the declaration I found on MSDN: ; typedef union _UNWIND_CODE { ; struct { ; UBYTE CodeOffset; ; UBYTE UnwindOp : 4; ; UBYTE OpInfo : 4; ; }; ; USHORT FrameOffset; ; } UNWIND_CODE, *PUNWIND_CODE; ; ; typedef struct _UNWIND_INFO { ; UBYTE Version : 3; ; UBYTE Flags : 5; ; UBYTE SizeOfProlog; ; UBYTE CountOfCodes; ; UBYTE FrameRegister : 4; ; UBYTE FrameOffset : 4; ; UNWIND_CODE UnwindCode[1]; ; /* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; ; * union { ; * OPTIONAL ULONG ExceptionHandler; ; * OPTIONAL ULONG FunctionEntry; ; * }; ; * OPTIONAL ULONG ExceptionData[]; */ ; } UNWIND_INFO, *PUNWIND_INFO; ; Here's the description of the UNWIND_INFO structure members taken directly from the MSDN: ; Version Version number of the unwind data, currently 1. ; Flags Three flags are currently defined: ; UNW_FLAG_EHANDLER The function has an exception handler that should be called when looking for functions that need to examine exceptions. ; UNW_FLAG_UHANDLER The function has a termination handler that should be called when unwinding an exception. ; UNW_FLAG_CHAININFO This unwind info structure is not the primary one for the procedure. Instead, the chained unwind info entry is the contents of a previous RUNTIME_FUNCTION entry. See the following text for an explanation of chained unwind info structures. If this flag is set, then the UNW_FLAG_EHANDLER and UNW_FLAG_UHANDLER flags must be cleared. Also, the frame register and fixed-stack allocation fields must have the same values as in the primary unwind info. ; SizeOfProlog Length of the function prolog in bytes. ; CountOfCodes This is the number of slots in the unwind codes array. Note that some unwind codes (for example, UWOP_SAVE_NONVOL) require more than one slot in the array. ; FrameRegister If nonzero, then the function uses a frame pointer, and this field is the number of the nonvolatile register used as the frame pointer, using the same encoding for the operation info field of UNWIND_CODE nodes. ; FrameOffset If the frame register field is nonzero, then this is the scaled offset from RSP that is applied to the FP reg when it is established. The actual FP reg is set to RSP + 16 * this number, allowing offsets from 0 to 240. This permits pointing the FP reg into the middle of the local stack allocation for dynamic stack frames, allowing better code density through shorter instructions (more instructions can use the 8-bit signed offset form). ; UnwindCode This is an array of items that explains the effect of the prolog on the nonvolatile registers and RSP. See the section on UNWIND_CODE for the meanings of individual items. For alignment purposes, this array will always have an even number of entries, with the final entry potentially unused (in which case the array will be one longer than indicated by the count of unwind codes field). ; ExceptionHandler This is an image-relative pointer to either the function's language-specific exception/termination handler (if flag UNW_FLAG_CHAININFO is clear and one of the flags UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER is set). ; Language-specific handler data (ExceptionData) This is the function's language-specific exception handler data. The format of this data is unspecified and completely determined by the specific exception handler in use. ; Chained Unwind Info (ExceptionData) If flag UNW_FLAG_CHAININFO is set then the UNWIND_INFO structure ends with three UWORDs. These UWORDs represent the RUNTIME_FUNCTION information for the function of the chained unwind. ; The possible values of the Flags field are: ; #define UNW_FLAG_EHANDLER 0x01 ; #define UNW_FLAG_UHANDLER 0x02 ; #define UNW_FLAG_CHAININFO 0x04 db 19h,0,0,0 dd RVA expt_handler dd 0 section '.idata' import data readable writeable library kernel32, 'KERNEL32.DLL',\ user32, 'USER32.DLL' include '%fasminc%\api\kernel32.inc' include '%fasminc%\api\user32.inc' exception.inc file: Code: EXCEPTION_CONTINUE_EXECUTION = -1 EXCEPTION_CONTINUE_SEARCH = 0 EXCEPTION_NONCONTINUABLE = 1 ; Noncontinuable exception EXCEPTION_MAXIMUM_PARAMETERS = 15 ; maximum number of exception parameters EXCEPTION_GUARD_PAGE = 80000001h EXCEPTION_DATATYPE_MISALIGNMENT = 80000002h EXCEPTION_BREAKPOINT = 80000003h EXCEPTION_SINGLE_STEP = 80000004h EXCEPTION_ACCESS_VIOLATION = 0C0000005h EXCEPTION_IN_PAGE_ERROR = 0C0000006h EXCEPTION_INVALID_HANDLE = 0C0000008h EXCEPTION_ILLEGAL_INSTRUCTION = 0C000001Dh EXCEPTION_NONCONTINUABLE_EXCEPTION = 0C0000025h EXCEPTION_INVALID_DISPOSITION = 0C0000026h EXCEPTION_ARRAY_BOUNDS_EXCEEDED = 0C000008Ch EXCEPTION_FLT_DENORMAL_OPERAND = 0C000008Dh EXCEPTION_FLT_DIVIDE_BY_ZERO = 0C000008Eh EXCEPTION_FLT_INEXACT_RESULT = 0C000008Fh EXCEPTION_FLT_INVALID_OPERATION = 0C0000090h EXCEPTION_FLT_OVERFLOW = 0C0000091h EXCEPTION_FLT_STACK_CHECK = 0C0000092h EXCEPTION_FLT_UNDERFLOW = 0C0000093h EXCEPTION_INT_DIVIDE_BY_ZERO = 0C0000094h EXCEPTION_INT_OVERFLOW = 0C0000095h EXCEPTION_PRIV_INSTRUCTION = 0C0000096h EXCEPTION_STACK_OVERFLOW = 0C00000FDh CONTROL_C_EXIT = 0C000013Ah EXCEPTION_POSSIBLE_DEADLOCK = 0C0000194h struc EXCEPTION_POINTERS { .PEXCEPTION_RECORD rq 1 ; ExceptionRecord .PCONTEXT rq 1 ; ContextRecord } struc EXCEPTION_RECORD64 { .ExceptionCode rd 1 .ExceptionFlags rd 1 .EXCEPTION_RECORD rq 1 ; ExceptionRecord .ExceptionAddress rq 1 .NumberParameters rd 1 .unusedAlignment rd 1 ; padding .ExceptionInformation rq EXCEPTION_MAXIMUM_PARAMETERS } struc XMM_SAVE_AREA32 { .ControlWord rw 1 .StatusWord rw 1 .TagWord rb 1 .Reserved1 rb 1 .ErrorOpcode rw 1 .ErrorOffset rd 1 .ErrorSelector rw 1 .Reserved2 rw 1 .DataOffset rd 1 .DataSelector rw 1 .Reserved3 rw 1 .MxCsr rd 1 .MxCsr_Mask rd 1 .FloatRegisters rb 8*16 ; every 128-bit holds st in low 80-bits or mm in low 64-bits .XmmRegisters rb 16*16 ; yeah, AMD64 in long mode (64-bit) has 16 xmm registers .Reserved4 rb 96 } struc CONTEXT64 { ; Register parameter home addresses. ; N.B. These fields are for convience - they could be used to extend the context record in the future. .P1Home rq 1 .P2Home rq 1 .P3Home rq 1 .P4Home rq 1 .P5Home rq 1 .P6Home rq 1 ; Control flags. .ContextFlags rd 1 .MxCsr rd 1 ; Segment Registers and processor flags. .SegCs rw 1 .SegDs rw 1 .SegEs rw 1 .SegFs rw 1 .SegGs rw 1 .SegSs rw 1 .EFlags rd 1 ; Debug registers .Dr0 rq 1 .Dr1 rq 1 .Dr2 rq 1 .Dr3 rq 1 .Dr6 rq 1 .Dr7 rq 1 ; Integer registers. .Rax rq 1 .Rcx rq 1 .Rdx rq 1 .Rbx rq 1 .Rsp rq 1 .Rbp rq 1 .Rsi rq 1 .Rdi rq 1 .R8 rq 1 .R9 rq 1 .R10 rq 1 .R11 rq 1 .R12 rq 1 .R13 rq 1 .R14 rq 1 .R15 rq 1 ; Program counter. .Rip rq 1 ; Floating point state. .FltSave XMM_SAVE_AREA32 ; Vector registers. .VectorRegister rb 16*26 .VectorControl rq 1 ; Special debug control registers. .DebugControl rq 1 .LastBranchToRip rq 1 .LastBranchFromRip rq 1 .LastExceptionToRip rq 1 .LastExceptionFromRip rq 1 }
|
|||||||||||
08 Mar 2010, 09:22 |
|
alorent 08 Mar 2010, 11:33
Great work as always Feryno!!!
Thanks!!! |
|||
08 Mar 2010, 11:33 |
|
vid 08 Mar 2010, 12:41
Nice!
|
|||
08 Mar 2010, 12:41 |
|
Feryno 09 Mar 2010, 13:29
I may try other return values and use debugger to watch what happens then.
I extracted the return values from DDK or WDK or something like this and the date of the exception.inc file at my disk / in my zip archives shows that I created it in year 2006. I didn't identify yet input parameters passed to exception handler, I identified only R8 which points to Context, I didn't identify RCX, RDX, R9, I have a feeling that OS put something into stack also, so I have a feeling that perhaps 5 or more input params are passed to exception handler (I used DebugCtl branch feature in debugger to indentify RIPs backward before OS transfered execution to exception handler). |
|||
09 Mar 2010, 13:29 |
|
baldr 09 Mar 2010, 16:23
MSDN/WDK wrote: typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) ( |
|||
09 Mar 2010, 16:23 |
|
Feryno 10 Mar 2010, 07:26
yes, great, that is the exact thing I was looking for!!!
|
|||
10 Mar 2010, 07:26 |
|
baldr 10 Mar 2010, 07:37
Feryno,
Note the following (VC\crt\src\excpt.h): Code: typedef enum _EXCEPTION_DISPOSITION { ExceptionContinueExecution, ExceptionContinueSearch, ExceptionNestedException, ExceptionCollidedUnwind } EXCEPTION_DISPOSITION; Funny constant name, sizeog_buggy_instruction. |
|||
10 Mar 2010, 07:37 |
|
Feryno 11 Mar 2010, 10:02
sizeog = typo error, at my type of keyboard the key G is just at the right side of the key F
|
|||
11 Mar 2010, 10:02 |
|
asmnewbie4 06 Apr 2012, 19:47
Many thanks, it's very usefull
_________________ While(1) = 0EBh, 0FEh ... |
|||
06 Apr 2012, 19:47 |
|
soloworker 04 Apr 2021, 05:47
Can you explain what does this part do?
Quote:
|
|||
04 Apr 2021, 05:47 |
|
bitRAKE 05 Apr 2021, 09:50
It's the UNWIND_INFO structure:
19h = 00011'001b ; version 1, and flags UNW_FLAG_EHANDLER or UNW_FLAG_UHANDLER _________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup |
|||
05 Apr 2021, 09:50 |
|
extra_12345 14 Jul 2024, 19:13
sorry for bumping an old thread I didn't want to start a new one, I'm having a weird issue regarding exception handling with directory based exception handling and I don't know how to fix it and I would appricate it if someone could help me with it.
here's how I defined the exception directory: format PE64 GUI 4.0 DLL entry DllEntryPoint include 'WIN64AXP.inc' include 'exception.inc' IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 include 'maker.ASM' include 'main_thread.ASM' section '.text' code readable executable and .pdata and .xdata section: section '.pdata' data readable writeable data IMAGE_DIRECTORY_ENTRY_EXCEPTION dd RVA TimerProcStart dd RVA TimerProcEnd dd RVA TimerProcUnwind dd RVA LocateEntryWithNameStart dd RVA LocateEntryWithNameEnd dd RVA LocateEntryWithNameUnwind end data section '.xdata' data readable writeable TimerProcUnwind: db 19h,0,0,0 dd RVA HandlerTimerProc dd 0 LocateEntryWithNameUnwind: db 19h,0,0,0 dd RVA LocateEntryWithNameHandler dd 0 please note that TimerProcStart with TimerProcUnwind and LocateEntryWithNameStart with LocateEntryWithNameHandler are in another.asm file which i included them: include 'maker.ASM' include 'main_thread.ASM' this works but with a small flaw, when i define it like this: data IMAGE_DIRECTORY_ENTRY_EXCEPTION dd RVA TimerProcStart dd RVA TimerProcEnd dd RVA TimerProcUnwind dd RVA LocateEntryWithNameStart dd RVA LocateEntryWithNameEnd dd RVA LocateEntryWithNameUnwind end data then exception handler doesn't works and it crashes but when i define it like this: data IMAGE_DIRECTORY_ENTRY_EXCEPTION dd RVA TimerProcStart dd RVA TimerProcEnd dd RVA TimerProcUnwind end data then it works and i'm not sure what the problem is. I made that definition for two different parts of the code. surprisingly though when everything is in the main .asm file where .pdata and .xdata is then it works perfectly even like this: data IMAGE_DIRECTORY_ENTRY_EXCEPTION dd RVA TimerProcStart dd RVA TimerProcEnd dd RVA TimerProcUnwind dd RVA LocateEntryWithNameStart dd RVA LocateEntryWithNameEnd dd RVA LocateEntryWithNameUnwind end data i double checked the lables and everything is properly defined. |
|||
14 Jul 2024, 19:13 |
|
Ali.Z 15 Jul 2024, 05:27
_________________ Asm For Wise Humans |
|||
15 Jul 2024, 05:27 |
|
extra_12345 15 Jul 2024, 06:22
Ali.Z wrote: i didnt take a look at your code, ngl. Thanks for the link, I’m using the same method but my problem is that I want to set up two exception handlers yet it does only works when everything is in the main module (main.asm) where .pdata and .xdata is and if I want to have my exception handler in another module (another .asm) file then I have to remove the second handler otherwise it doesn’t works and the handler is never called , my guess is that it has to do something with linking and setting up the unwind info when the handlers are in another module ( .asm) but i don’t know how to fix this issue. |
|||
15 Jul 2024, 06:22 |
|
bitRAKE 15 Jul 2024, 16:13
extra_12345 wrote: sorry for bumping an old thread I didn't want to start a new one, I'm having a weird issue regarding exception handling with directory based exception handling and I don't know how to fix it and I would appricate it if someone could help me with it. We don't know much from your explanation (obviously, we can't build your code fragment). So, my mind wanders: From the exception directory we can calculate the entries, array bytes divided by item bytes; but what if the consumer of the array doesn't bother - the array would need to be terminated. Continuing with our imagination, the item size is 12 bytes, alignment would add another DWORD of zero - perhaps terminating? If that were the solution then two items would not be terminated and produce an error. That would only be true for 8 byte alignment. My imagination could proceed no further, and I think it's heavily biased by my own experience. I've had errors of this type before: where reorganization of code/data caused unforseen problems. I would need to experiment further with exception directory termination trials to increase my discovery in this blind direction. A quick test by you would rule this out. Perhaps you could refine your code to produce a minimal problem case for us to examine further? _________________ ¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup |
|||
15 Jul 2024, 16:13 |
|
extra_12345 15 Jul 2024, 19:57
bitRAKE wrote:
I solved the puzzle! so I had my first exception handler in 'main.asm' and the second one in the 'maker.asm' but I linked them like this: include 'maker.asm' ; second exception handler include 'main.asm' ; first exception handler and I defined the unwind info like this: data IMAGE_DIRECTORY_ENTRY_EXCEPTION dd RVA TimerProcStart dd RVA TimerProcEnd dd RVA TimerProcUnwind dd RVA LocateEntryWithNameStart dd RVA LocateEntryWithNameEnd dd RVA LocateEntryWithNameUnwind end data section '.xdata' data readable writeable TimerProcUnwind: db 19h,0,0,0 dd RVA HandlerTimerProc ;first handler dd 0 LocateEntryWithNameUnwind: db 19h,0,0,0 dd RVA LocateEntryWithNameHandler ;second handler dd 0 I accidentally swapped their spot from: include 'maker.asm' ; second exception handler include 'main.asm' ; first exception handler to include 'main.asm' ; first exception handler include 'maker.asm' ; second exception handler and this solved the issue then both exception handlers started to work like a charm! got no idea why this causes any issues with exception handler though, i'm no expert but apparently observing the handler order in the whole code is the trick. |
|||
15 Jul 2024, 19:57 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.