flat assembler
Message board for the users of flat assembler.

Index > Windows > [x64] Exception Handling?

Author
Thread Post new topic Reply to topic
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu 13 Feb 2012, 05:40
Hi there.
I would love receiving some help regarding the mentioned topic.
I googled it and found an article on OSR Online and multiple ones by Nynaeve.
Though, they are way too high for me to understand them..

Of course it'd be nice if I would understand many of x64 EH facilities, but as I don't understand a thing in the first place, having a handler skeleton to see the clear structure would be enough..

Like I'm not even capable of creating such a basic handler in x64:

Code:
push exception
push dword [fs:0]
mov [fs:0],esp

xor eax,eax
mov [eax],1

pop dword [fs:0]
add esp,4
;...

exception:
;...    


The few things I found out yet are, that x64 exception handling is specified via a table in the pe header like:

Code:
.start_eh:
  xor eax,eax
  mov [rax],0
.end_eh:

data 3
  dd rva .start_eh
  dd rva .end_eh
  dd rva unwind_info
end data

unwind_info:
  db 00001001b
  ;...    

AFAIK this is how exception handling looks like in x64, but to make it work one has to specify a correct unwind_info structure.
I've tried to read about how to fill it in the articles I mentioned above, but I just don't understand them...


Last edited by yoshimitsu on 13 Feb 2012, 06:10; edited 3 times in total
Post 13 Feb 2012, 05:40
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20355
Location: In your JS exploiting you and your system
revolution 13 Feb 2012, 06:01
While I am not experienced with 64-bit SEH I can already see that using dword is probably not a good idea. At least try with qword and rsp, rax etc.
Post 13 Feb 2012, 06:01
View user's profile Send private message Visit poster's website Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu 13 Feb 2012, 06:07
Good morning :)
This snippet is just a short x86 SEH-snippet to show what I would like to achieve in x64
Post 13 Feb 2012, 06:07
View user's profile Send private message Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 509
Location: Czech republic, Slovak republic
Feryno 13 Feb 2012, 06:47
http://board.flatassembler.net/topic.php?t=11266

another way:
RtlAddVectoredExceptionHandler
Post 13 Feb 2012, 06:47
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu 13 Feb 2012, 08:30
man, I've used the forum's internal search & google to search through the forum, but couldn't find one topic of interest..
Guess I was using the wrong keywords..

Thanks Feryno, I'll look into it.

Btw. I'm just a noob so it doesn't mean anything, but I enjoy using your neat 64bit debugger :)

Edit:
I was trying something like
Code:
  db 1+1 shl 3 ;Version 3 & Flags 5
  db 0 ;SizeOfProlog
  db 0 ;CountOfCodes
  db 00000000b ;FrameRegister 4 & FrameOffset 4
  dw 0 ;FrameOffset / CodeOffset + UnwindOp 4 & OpInfo 4
  dw 0 ;align
  dd rva handler ;ExceptionHandler
  dd 0 ;ExceptionData    

:s

I thought about using VEH, but I assumed SEH had less overhead and therefore chose SEH

So as x64 SEH uses a global address table it is logically not per-thread as x86 SEH, is it?..
Then what are the benefits or drawbacks compared to VEH?
Post 13 Feb 2012, 08:30
View user's profile Send private message Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 509
Location: Czech republic, Slovak republic
Feryno 14 Feb 2012, 13:12
Hi,
no worry, there is so much info in the fasm forum that sometimes it is hard to find what you need (I found the result because I already posted something you asked for, so I added my nickname to the search filter)

And yes, you are right, every thread hits the same exception handler in the above sample.
I didn't find any way of exception handling specific to thread.
Under x64 everything specific to thread is stored in GS (as under 32 bits everything is under FS), most of these fields expanded from dword to qword, also added some padding so e.g. every qword field is aligned at 8

at FS:[0] for i386 there is struct _EXCEPTION_REGISTRATION_RECORD *Used_ExceptionList
but under x64 there is union _KGDTENTRY64 *GdtBase at GS:[0] and nothing concerning exception handling

wait, I just now have some crazy idea: the FS:0 is still present under x64 for compatibility mode, there is also an easy way to switch native x64 code from x64 to 32 bit from usermode (no need any driver) and then back from 32 bit compatibility to native x64
you may do quick short research whether it is possible to do something sensefull in exception handling in that way (whether the installed SEH under 32 doesn't disappear when returning back into 64 or whether it works as expected)
N.B. that some code will be executed as 32 bit code so then you can't access registers r8-r15 either you can use only low dwords of remaining 8 GPRs (eax, ecx, ... edi), call/ret then operates on dword of stack not qword etc

64-32-64.asm
Code:
; ripped from ntdbg.h
AMD64_KGDT64_NULL    =       (0 * 16)        ; NULL descriptor
AMD64_KGDT64_R0_CODE       =       (1 * 16)        ; kernel mode 64-bit code
AMD64_KGDT64_R0_DATA       =       (1 * 16) + 8    ; kernel mode 64-bit data (stack)
AMD64_KGDT64_R3_CMCODE     =       (2 * 16)        ; user mode 32-bit code
AMD64_KGDT64_R3_DATA =       (2 * 16) + 8    ; user mode 32-bit data
AMD64_KGDT64_R3_CODE =       (3 * 16)        ; user mode 64-bit code
AMD64_KGDT64_SYS_TSS =       (4 * 16)        ; kernel mode system task state
AMD64_KGDT64_R3_CMTEB        =       (5 * 16)        ; user mode 32-bit TEB
AMD64_KGDT64_LAST     =       (6 * 16)


format PE64 GUI at 400000h on 'nul'
entry start

section '.text' code readable executable

start:
        sub     rsp,8*(4+1)

     xor     eax,eax
     or      rdx,-1

  lea     rcx,[cont1]
 mov     [rsp+8],ecx
 mov     word [rsp+8+4],AMD64_KGDT64_R3_CODE + 11b

       lea     rcx,[cont0]
 mov     [rsp],ecx
   mov     word [rsp+4],AMD64_KGDT64_R3_CMCODE + 11b
   jmp     pword [rsp]

cont0:
use32
      inc     eax
 nop
 call    subproc

 jmp     pword [esp+8]

use64
cont1:        xor     eax,eax
     add     rsp,8*(4+1)
 ret


use32
subproc:
        pusha
       or      edx,-1
      mov     ecx,edx
     popa
        ret
    


32-64-32.asm
Code:
; ripped from ntdbg.h
AMD64_KGDT64_NULL        =       (0 * 16)        ; NULL descriptor
AMD64_KGDT64_R0_CODE       =       (1 * 16)        ; kernel mode 64-bit code
AMD64_KGDT64_R0_DATA       =       (1 * 16) + 8    ; kernel mode 64-bit data (stack)
AMD64_KGDT64_R3_CMCODE     =       (2 * 16)        ; user mode 32-bit code
AMD64_KGDT64_R3_DATA =       (2 * 16) + 8    ; user mode 32-bit data
AMD64_KGDT64_R3_CODE =       (3 * 16)        ; user mode 64-bit code
AMD64_KGDT64_SYS_TSS =       (4 * 16)        ; kernel mode system task state
AMD64_KGDT64_R3_CMTEB        =       (5 * 16)        ; user mode 32-bit TEB
AMD64_KGDT64_LAST     =       (6 * 16)


format PE GUI at 400000h on 'nul'
entry start

section '.text' code readable executable

start:
  sub     esp,16

  xor     eax,eax

 mov     dword [esp+8],cont1
 mov     word [esp+8+4],AMD64_KGDT64_R3_CMCODE + 11b

     mov     dword [esp],cont0
   mov     word [esp+4],AMD64_KGDT64_R3_CODE + 11b
     jmp     pword [esp]

cont0:
use64
      inc     rax
 nop
 call    subproc

 jmp     pword [esp+8]

use32
cont1:        xor     eax,eax
     add     esp,16
      ret


use64
subproc:
        dec     rax
 dec     rax
 ret
    
Post 14 Feb 2012, 13:12
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
yoshimitsu



Joined: 07 Jul 2011
Posts: 96
yoshimitsu 14 Feb 2012, 18:26
With 64-32-64.asm, it crashes when trying to access either fs:0 or gs:0 in subproc.

Btw. I now got an apparently working rudimentary exception handler thanks to your topic (I wrote a macro to spare making up names for the begin and end-rvas which go into the exception directory):

test.asm
Code:
format PE64 GUI 4.0
entry start

include 'win64a.inc'

include 'seh64.inc'

seh equ 3
CONTEXT64.Rip = 0F8h

section '.code' code readable executable

  start:
        sub     rsp,8*(4+1)

       .try     handler
        xor     eax,eax
        mov     dword[eax],0
       .end

  safe_place:

       .try
        xor     eax,eax
        mov     dword[eax],0
       .catch
        mov     rsp,rdx
        xor     r9,r9
        xor     r8,r8
        lea     rdx,[handler2]
        xor     ecx,ecx
        call    [MessageBox]
       .end

        xor     r9,r9
        xor     r8,r8
        lea     rdx,[k]
        xor     ecx,ecx
        call    [MessageBox]
        xor     ecx,ecx
        call    [ExitProcess]

  handler:
        sub     rsp,8*(4+1)
        mov     qword [r8+CONTEXT64.Rip],safe_place
        xor     r9,r9
        xor     r8,r8
        lea     rdx,[handler1]
        xor     ecx,ecx
        call    [MessageBox]
        xor     eax,eax
        add     rsp,8*(4+1)
        retn

section '.data' data readable writeable

  k db 'k.',0
  handler1 db 'in handler1',0
  handler2 db 'in handler2',0

  data seh
  end data

section '.idata' import data readable

  library kernel32,'KERNEL32.DLL',\
          user32,'USER32.DLL'

  import kernel32,\
         ExitProcess,'ExitProcess'

  import user32,\
         MessageBox,'MessageBoxA'
    


seh64.inc
Code:
macro enqueue list,item
 { match any,list \{ list equ list,item \}
   match ,list \{ list equ item \} }

macro dequeue list,item
 { done@dequeue equ
   match first=,rest,list
   \{ item equ first
      list equ rest
      restore done@dequeue \}
   match :m,done@dequeue:list
   \{ item equ m
      list equ
      restore done@dequeue \}
   match ,done@dequeue
   \{ item equ
      restore done@dequeue
   \} }

macro queue list,index,item
 { local copy
   copy equ list
   rept index+1 \{ dequeue copy,item \} }

macro data directory
 { done@data equ
   match =3,directory
   \{ local l_infos,_info,_end
      l_infos equ
      align 4
      match list,l_handlers
      \\{
         irp _handler,list
         \\\{ local rva$
            rva$ = rva $
            enqueue l_infos,rva$
            db 19h,0,0,0
            dd _handler,0
         \\\}
      \\}
      data 3
      match list,l_begins
      \\{
         irp _begin,list
         \\\{
            dequeue l_ends,_end
            dequeue l_infos,_info
            dd _begin
            dd _end
            dd _info
         \\\}
      \\}
      restore done@data
   \}
   match ,done@data
   \{ data directory
      restore done@data \} }

l_begins equ
l_ends equ
l_handlers equ

macro .try handler
 { local ..try
   __TRY equ ..try
   local ..end
   __END equ ..end
   local ..catch
   __CATCH equ ..catch
   __TRY:
   if ~ handler eq
    virtual at handler
    __CATCH:
    end virtual
   end if }

macro .catch
 { jmp __END
   __CATCH: }

macro .end
 { __END:
   enqueue l_begins,rva __TRY
   enqueue l_ends,rva __END
   enqueue l_handlers,rva __CATCH
   restore __TRY
   restore __END
   restore __CATCH }
    
Post 14 Feb 2012, 18:26
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20355
Location: In your JS exploiting you and your system
revolution 15 Feb 2012, 04:33
Feryno wrote:
32-64-32.asm
This one looks to be useful. If we can guarantee that the segment descriptors are fixed as part of the architecture then it allow one to support only one executable for both 32-bit and 64-bit systems. On app startup we detect the OS bitness and decide which code path to execute. However, it all relies upon the hard-coded positions of those segment descriptors, thus making it a hacky solution.
Post 15 Feb 2012, 04:33
View user's profile Send private message Visit poster's website Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 509
Location: Czech republic, Slovak republic
Feryno 15 Feb 2012, 14:37
yoshimitsu - great work with the macro !!!

revolution - The 64 bit code launched from compatibility mode is very limited (32-64-32.asm)
its ring3 virtual memory space is limited to 32 bit
it doesn't contain anything at GS:[...]
DLLs loaded into PE executable are all 32 bit (so if you need to call some windows DLL you need to switch back to 32 bit before calling DLL)
...

fortunately it seems that OS saves/restores all 16 GPRs including upper dwords during task switching even the main executable image indicates 32 bit submode...

I just had an idea using 32-64-32.asm where it is perhaps possible to install SEH using FS:[0], then switch the CPU to 64 bit submode of long mode and try whether exception handler is still working well

There is a way how to obtain CS for 64 as well 32 bit no matter version of windows - if somebody change that in the feature, or if you recompile your own kernel - look for WRK (windows research kernel) - a leak is available when are googling enough

I would do it in a way like:

[0]
If you launch PE32+ executable (=64 bit code), then it must launch PE (32bit executable) which executes something like:
; PE 32 bit executable:
mov eax,cs
ret
then the primary PE32+ executable obtains the value of 32 bit CS using GetExitCode


[1]
If the primary executable is PE (32 bit), then it must launch PE32+ (64 bit) where PE32+ does only the same 2 instructions:
mov eax,cs
ret
and then the primary executable (PE) gets the 64 bit CS value by GetExitCode

Such approach is always compatible among all versions of ms windows (no matter released by microsoft or private compiled research kernel).
But MS may still develop a version of OS where exectables have various CS (currently all ring0 executables have the same CS, all ring3 64 bit executables have the same, all ring3 compatibility 32 bit have the same).
Or MS may decide to create different GDTs for 64 and 32 bit submodes where they will contain only necessary CS (GDT for 64 won't contain CS for 32 bit submode) etc.

More problems than real benefit...
Post 15 Feb 2012, 14:37
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20355
Location: In your JS exploiting you and your system
revolution 15 Feb 2012, 14:50
Feryno wrote:
If you launch PE32+ executable (=64 bit code), then it must launch PE (32bit executable) which executes something like:
; PE 32 bit executable:
mov eax,cs
ret
then the primary PE32+ executable obtains the value of 32 bit CS using GetExitCode
This is also hacky. MS only guarantee that ExitProcess will properly exit. Using only 'ret' is not such a good thing.
Feryno wrote:
Or MS may decide to create different GDTs for 64 and 32 bit submodes where they will contain only necessary CS (GDT for 64 won't contain CS for 32 bit submode) etc.
Yes, this is the problem. If it is not part of the architecture then future incompatibility can occur.
Feryno wrote:
More problems than real benefit...
You might be right about that. Pity really.
Post 15 Feb 2012, 14:50
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8356
Location: Kraków, Poland
Tomasz Grysztar 15 Feb 2012, 15:16
revolution wrote:
Feryno wrote:
If you launch PE32+ executable (=64 bit code), then it must launch PE (32bit executable) which executes something like:
; PE 32 bit executable:
mov eax,cs
ret
then the primary PE32+ executable obtains the value of 32 bit CS using GetExitCode
This is also hacky. MS only guarantee that ExitProcess will properly exit. Using only 'ret' is not such a good thing.
I would still argue that this is not "hacky", but a valid interpretation of documentation that states that function return is one of the ways to terminate thread. Wink
Post 15 Feb 2012, 15:16
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20355
Location: In your JS exploiting you and your system
revolution 16 Feb 2012, 01:40
Documentation is silent, or says nothing negative about it --leads to--> assumption that it is a valid technique --leads to--> frustration when it fails to work as assumed --leads to--> Crying or Very sad --leads to--> Mad --leads to--> spending lots of time updating old code that could have easily been avoided the first time around --leads to--> depression --leads to--> suicide. amirite? Razz
Post 16 Feb 2012, 01:40
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8356
Location: Kraków, Poland
Tomasz Grysztar 16 Feb 2012, 10:51
revolution wrote:
Documentation is silent, or says nothing negative about it --leads to--> assumption that it is a valid technique --leads to--> frustration when it fails to work as assumed --leads to--> Crying or Very sad --leads to--> Mad --leads to--> spending lots of time updating old code that could have easily been avoided the first time around --leads to--> depression --leads to--> suicide. amirite? Razz
Documentation is not silent, it just needs a right interpretation. Wink And if, hypothetically, it failed to work as assumed, it would lead to bug report and either bug would get fixed or one would get another reason to complain about other people not really caring about the standards and/or consistency. And after that one may as well give up using their software and turn to gardening or something. Wink
Post 16 Feb 2012, 10:51
View user's profile Send private message Visit poster's website Reply with quote
JohnFound



Joined: 16 Jun 2003
Posts: 3499
Location: Bulgaria
JohnFound 16 Feb 2012, 11:05
I am agree with Tomasz here, with one exception - ExitProcess stops all threads of the application, not only the main one. As long, as the main thread is not something special stopping it and leaving the other threads to run will leave the application running.
If you provide decent mechanism to stop all threads before exiting the main thread, "ret 4" seems to be absolutely legal and documented way to finish the main thread.
Even more, I think (but never try) that you can make all processing in some secondary thread and terminate the main thread just after the start of the application without any problems.
Post 16 Feb 2012, 11:05
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
f0dder



Joined: 19 Feb 2004
Posts: 3175
Location: Denmark
f0dder 09 Jul 2012, 17:15
JohnFound wrote:
If you provide decent mechanism to stop all threads before exiting the main thread, "ret 4" seems to be absolutely legal and documented way to finish the main thread.
Sure it is - but there's no guarantee that exiting the main thread is going to exit your process. There can be more threads started in your process than you think there might, and then you'll have a zombie process running.

The discussion has already taken place in the thread Tomasz refers to, so I'm not going to repeat it here. For the TL;DR version: use ExitProcess when you want to terminate your process. Don't even consider the "RET" approach unless you're writing 512byte intros.

_________________
Image - carpe noctem
Post 09 Jul 2012, 17:15
View user's profile Send private message Visit poster's website Reply with quote
Feryno



Joined: 23 Mar 2005
Posts: 509
Location: Czech republic, Slovak republic
Feryno 10 Jul 2012, 05:48
we are completely off topic
if somebody is interested I also have a sample of exception handling for win x64 drivers using exception directory
Post 10 Jul 2012, 05:48
View user's profile Send private message Visit poster's website ICQ Number Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.