flat assembler
Message board for the users of flat assembler.

Index > Linux > Exception handling

Goto page 1, 2, 3  Next
Author
Thread Post new topic Reply to topic
ronware



Joined: 08 Jan 2004
Posts: 179
Location: Israel
ronware 29 Aug 2005, 15:25
On the Windows forum, this thread discusses how to perform exception handling.

I want to do something similar on Linux, and am not quite certain the best way. Specifically, I want to be able to get an exception, determine its severity, call a user-installed handler and resume execution at some new EIP. All of this is pretty easy under Windows, but I'm less sure of how to do it on Linux.

Has anyone some code samples on how to do this in assembly?
Post 29 Aug 2005, 15:25
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 30 Aug 2005, 05:23
Exception raising and handling is not a big magic under unix either. Check out setjmp/longjmp libc function-pair. You can find lots of C-examples on the web. The problem is that you either use libc or you have to implement both setjmp and longjmp in assembly by yourself.
Post 30 Aug 2005, 05:23
View user's profile Send private message Reply with quote
ronware



Joined: 08 Jan 2004
Posts: 179
Location: Israel
ronware 30 Aug 2005, 14:36
The last is what I need to do -- are there any examples I might use, especially ones that use the kernel int 0x80 interface?
Post 30 Aug 2005, 14:36
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 03 Sep 2005, 11:34
here is a simple example for exception handling. In order to keep it undersandable I did not optimize it for size.
Code:
; example for exception handlig
format ELF executable
entry start

SYSCALL_EXIT  equ 1 ; syscall to function exit()
SYSCALL_WRITE equ 4 ; syscall to function write()
STDERR        equ 2 ; standard error

EXCEPTION_ID_DIVISION_BY_ZERO equ 2 ; exception id for 'division by zero'

section readable writeable
div_by_zero_exception_msg db 'Exception: Division by zero', 0xa
div_by_zero_exception_msg_size = $-div_by_zero_exception_msg
unknown_exception_msg db 'Exception: Unknown', 0xa
unknown_exception_msg_size = $-unknown_exception_msg

section readable executable
start:
; try
        mov     eax, 20
        mov     ebx, 0 ; <----- this will cause an exception!
; attempt to calculate eax/ebx
        call    do_complicated_calculations_1
        mov     eax, 30
        mov     ebx, 3
; attempt to calculate eax/ebx
        call    do_complicated_calculations_1
; success, set exit code to 0
        xor     ebx, ebx
        jmp     .finish
.catch_exception:
; check for exception id
        cmp     eax, EXCEPTION_ID_DIVISION_BY_ZERO
        jne     .prepare_unknown_exception_message
; prepare printing of 'division by zero' message
        mov     ecx, div_by_zero_exception_msg
        mov     edx, div_by_zero_exception_msg_size
        jmp     .print_exception_message
.prepare_unknown_exception_message:
; prepare printing of 'unknown' message
        mov     ecx, unknown_exception_msg
        mov     edx, unknown_exception_msg_size
.print_exception_message:
; print exception message
        mov     eax, SYSCALL_WRITE
        mov     ebx, STDERR
        int     0x80
; exception cought, set exit code to 1
        mov     ebx, 1
.finish:
        mov     eax, SYSCALL_EXIT
        int     0x80

;***************************************************************************
;  Some functions which the exception will jump over
;***************************************************************************
do_complicated_calculations_1:
        call    do_complicated_calculations_2
; more very long computations may be here
        ret

do_complicated_calculations_2:
        call    do_complicated_calculations_3
; more very long computations may be here
        ret

do_complicated_calculations_3:
        call    do_complicated_calculations_4
; more very long computations may be here
        ret

do_complicated_calculations_4:
        call    do_division
; more very long computations may be here
        ret

;***************************************************************************
;   Function
;      do_division
;   Description
;      tries to calculate result of eax / ebx
;      if ebx is zero then a division by zero exception is raised.
;   Parameters
;      eax - dividend
;      ebx - divisor
;   Return value
;      eax - result
;   Destroyed registers
;      eax, edx
;
;***************************************************************************
do_division:
        test    ebx, ebx
        jnz     .compute
; raise exception
        mov     eax, EXCEPTION_ID_DIVISION_BY_ZERO ; exception id is the parameter
; skip stack frames before that of start
        add     esp, 20
        jmp     start.catch_exception
.compute:
        xor     edx, edx
        div     ebx
        ret    

You can test exit value with the commands below (assumed the above little program is named exception) in a shell:
Code:
if ./exception; then echo ok; else echo not ok; fi    


Last edited by Endre on 03 Sep 2005, 19:33; edited 1 time in total
Post 03 Sep 2005, 11:34
View user's profile Send private message Reply with quote
comrade



Joined: 16 Jun 2003
Posts: 1150
Location: Russian Federation
comrade 03 Sep 2005, 15:57
how is that exception handling? its just manual error checking
does linux not provide catching of hardware-generated exceptions?
Post 03 Sep 2005, 15:57
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger ICQ Number Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 03 Sep 2005, 19:09
Hardware doesn't generate exceptions. They generate interrupts. Interrupt-handling is only in the kernel. Interrupts may cause signals. Signals are not exceptions either. Exceptions are always software generated and environment independent. Should any problems be detected manually or automagically (in case your HLL environment supports it <ada, c++, java, etc.>, and which automatically compiles in the appropriate check routine) in a function being deep in the hierarchy then it throws or raises an exception. This doesn't mean else than the execution will get back to the function (being for instance on the top of the hierarchy) which will handle this exception. So jumping over the functions being in the middle. Technically speaking it is a long jump over stack frames.

So if you think of division by zero (I admit it wasn't the best example, because to this we also have a HW-interrupt) then first the processor generates an interrupt (which is called by the user-manual an exception, but it's correctly a HW-interrupt). Due to this interrupt the appropriate interrupt handler in the kernel sends a signal to your application, which either processes it or not. If not then the default signal handler will stop the application at once. If the program processes the signal then it still can decide about what to do and may run further. Signals are implemented through IPC.

Exceptions work totally other. The ability of HW to detect an error is limited. The processor can't catch a simple overflow problem either. But you can write such a sophisticated detection algorithm as you just want to. For instance in the software you can check any kind of overflow, or you can check that the angle of the rocket you're just driving is plausible. If not you raise an exception and the function far above in the hierarchy will destroy the rocket (see the story of ariane-5) in the following few processor thicks.

I hope I could help.
Post 03 Sep 2005, 19:09
View user's profile Send private message Reply with quote
comrade



Joined: 16 Jun 2003
Posts: 1150
Location: Russian Federation
comrade 03 Sep 2005, 19:24
DWORD.

but how do u catch these hardware generated interrupts from linux? i am talking about that shit where u do push FS:[0] in windows
Post 03 Sep 2005, 19:24
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger ICQ Number Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 03 Sep 2005, 19:43
You write a signal handler to the appropriate signal.
In this case the signal is SIGSEGV. It's really easy.

Here is a little test example in C:
Code:
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>

void my_signal_handler(int status);
static jmp_buf env;

int main(void)
{
   signal(SIGSEGV, my_signal_handler);

   if(setjmp(env) == 0) {
      asm ("pushl 0");
   }

   printf("but I'm still alive\n");
   return 0;
}

void my_signal_handler(int status)
{
   printf("received SIGSEGV signal, status = %d\n", status);
   longjmp(env, 1);
}    

and with this code I get the result:
$ ./test
received SIGSEGV signal, status = 11
but I'm still alive
$
It's late night but morning I'll do it also in assembly. I hope I could help again.


Last edited by Endre on 03 Sep 2005, 21:02; edited 1 time in total
Post 03 Sep 2005, 19:43
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 03 Sep 2005, 20:36
According to the Intel nomenclature the exceptions are a special type of interrupts - and this is the standard meaning of this word in the assembly language. The interrupt numbers 0-31 are reserved for the exceptions, and all those exception are hardware-generated, generally in case of some kind of instruction execution fault.

The exceptions in HLL style, as mentioned by Endre, make actually no sense in assembly language in my opinion, since the ordinary and straightforward conditional jump does the job (and the try-catch and similar constructions were invented in structured high level languages just because they lack feature like conditional jumps, or due to the disliking of the any GOTO-like constructions by structured-programming-lovers).
Post 03 Sep 2005, 20:36
View user's profile Send private message Visit poster's website Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 03 Sep 2005, 21:17
Or because in HLL you cannot directly jump from one function into the other. And manipulaton of the stack pointer can only be done with inline assembly. On the other hand when you do such jumps and stack manipulations in assembly then you do the same thing as an exception machanism in HLL does, even if you don't realize it.
Post 03 Sep 2005, 21:17
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8353
Location: Kraków, Poland
Tomasz Grysztar 03 Sep 2005, 21:47
I just said it was designed as a structured-programming equivalent of the conditional control transfer - nothing more to realize here. Remember that machine code and GOTO-rich languages were first.
Post 03 Sep 2005, 21:47
View user's profile Send private message Visit poster's website Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 04 Sep 2005, 13:13
As I promised, here is the assembly code for handling SIGSEGV. It does almost the same thing as the above C-program. The only difference that I for simplicity didn't use the status parameter of function my_signal_handler.
Code:
; example for exception handlig
format ELF executable
entry start

SYSCALL_EXIT   equ 1  ; syscall to function exit()
SYSCALL_WRITE  equ 4  ; syscall to function write()
SYSCALL_SIGNAL equ 48 ; syscall to function signal()
SIGSEGV        equ 11 ; signal id SIGSEGV
STDERR         equ 2  ; standard error

section readable writeable
signal_handler_msg db 'Hmmm, SIGSEGV signal received', 0xa
signal_handler_msg_size = $-signal_handler_msg
reborn_msg db "but I'm still alive", 0xa
reborn_msg_size = $-reborn_msg

section readable executable
start:
; install signal handler
        mov     eax, SYSCALL_SIGNAL
        mov     ebx, SIGSEGV
        mov     ecx, my_signal_handler
        int     0x80
; do something dirty
        push    dword [fs:0]
; print reborn message
.print_reborn_msg:
        mov     eax, SYSCALL_WRITE
        mov     ebx, STDERR
        mov     ecx, reborn_msg
        mov     edx, reborn_msg_size
        int     0x80
.finish:
        xor     ebx, ebx
        mov     eax, SYSCALL_EXIT
        int     0x80

my_signal_handler:
; print exception message
        mov     eax, SYSCALL_WRITE
        mov     ebx, STDERR
        mov     ecx, signal_handler_msg
        mov     edx, signal_handler_msg_size
        int     0x80
; remove return adress from the stack
        add     esp, 4
; jump after the erroneous code
        jmp     start.print_reborn_msg    
Post 04 Sep 2005, 13:13
View user's profile Send private message Reply with quote
nixnoob



Joined: 23 Sep 2005
Posts: 10
nixnoob 23 Sep 2005, 17:43
at least a sensible post to linux exception handling
well after googling around i landed here
like my nick i am dabbling with linux assembly of late
as held me down for long time and none of the intel2gas a2i
were able to find the right % or l or movl Smile
then found nasm works with it and wrote a firstasm.asm Smile
but it was spitting some parser error
havent tried fasm yet well enough blah blah

what i was trying to do was some thing like this (done normally by even noob
kiddies in windows with masm or tasm or whatever they prefer to use)

assume fs:nothing
push offset handler
push fs:0
mov fs:[0],esp

xor eax,eax
mov [eax],eax < exception access violation


handler:
well i want to take a peek at those debug register dr0 to dr7 or may be nullify them here thats the only purpose of this exercise Smile

in windows its just getting the
context.dr0
and may be filling the dr0 to dr4 with nulls

now how could i go about doing th same or equivalent in linux with
nasm fasm or even with that grumpy as Smile

well i saw google saying one cant access debug registers in linux
directly etc some thing u_context.h has only gregs visible etc etc
but only ptrace with pokeusr or peekusr (request 3 or 6)
could acces them
but ptrace is finally a system call it seems
so one could be able to embed it in the handler
doesnt it work that way ??
could some one post a small working snippet or may be
some weird ideas to the objective

i saw in the above snippet one can write a reborn message ??
so instead of reborn message if i wish to modify the instruction pointer
to two bytes(i take two bytes as an arbitrary length actually the length of the offending codes mnemonic) below the offending code and just continue as if
nothing really happened how would i go about it
in assembly

any and all ideas are appreciated

hope you read till here and didnt dismiss this post as another cranks
useless whim

thanks and regards
Post 23 Sep 2005, 17:43
View user's profile Send private message Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 24 Sep 2005, 10:35
nixnoob wrote:

but only ptrace with pokeusr or peekusr (request 3 or 6)
could acces them
but ptrace is finally a system call it seems
so one could be able to embed it in the handler
doesnt it work that way ??
I've never tried peeking/poking in debug registers, but with ptrace they seem me possible. If you're a noob as you claim then first try to write a C-program with ptrace to get experencies with it. When it works you may convert it into assembly.
Quote:
i saw in the above snippet one can write a reborn message ??
so instead of reborn message if i wish to modify the instruction pointer
to two bytes(i take two bytes as an arbitrary length actually the length of the offending codes mnemonic) below the offending code and just continue as if
nothing really happened how would i go about it
in assembly
My reborn message isn't something linux specific magic. It was just to present that the program runs further as it had nothing happened.
Post 24 Sep 2005, 10:35
View user's profile Send private message Reply with quote
nixnoob



Joined: 23 Sep 2005
Posts: 10
nixnoob 24 Sep 2005, 13:21
endre ,
thanks a lot for replying

well i was not aware that a noob could write a c program let alone convert it into assembly Smile jokes apart

i think i understood what you were trying to show in the snippet above
you write two messages to console one from exception handler one out of it

if you are are aware of win context structure (should be googleable in those wine cvs of winnt.h or probably find reactos excellent documentation )

for example
typedef contextx86 {

ulong flags ;
..
ulong dr0;
ulong dr1;
ulong ...;
fillers of ....;
ulong regeip; (0xb8th dword member in the struct)
...
}contextx86;

now when the exception handler is called this structure is passed to the handler
pointer will be at esp+4
mov eax,esp+4 <--context pointer
add eax,0xb8 <points to 0xb8th member == regeip == instruction pointer
add [eax],2 modify the eip so tha execution continues from his plcae when
system returns control (exactly like you jump to reborn but instead of jumping to reborn everytime i can have a macro and embed trillions of
reborn messages and give them control everytime i hit an exceptions by modifying the regeip to point to a new place
now nullifying dr0 means no hardware breakpoints that may have been set
would be valid
all of these pertain to anti debugging techniques etc ec
and i was trying to get some insight into the process of exception handling
btw assembly being assembly would be able to give one a little more control to the coder i think thas why i was interested in trying out assembly
instead of c or c++ or lisualcrasic (yeah some visual basic clone for linux)
retn Exception_handled

any way thanks again for replying now that your interest is aroused i am sure some valuble insights would folllow Smile
well i keep tinkering on till the dent gets right or the car develops a gaping hole in the place Smile
Post 24 Sep 2005, 13:21
View user's profile Send private message Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 24 Sep 2005, 17:35
Here the code. It's simple, but because I'd got to declare some structures it seems a bit complex.
Code:
; example for exception handlig
format ELF executable
entry start

SYSCALL_EXIT      equ 1  ; syscall to function exit()
SYSCALL_WRITE     equ 4  ; syscall to function write()
SYSCALL_SIGACTION equ 67 ; syscall to function sigaction()
SA_NOCLDSTOP      equ 0x00000001
SA_NOCLDWAIT      equ 0x00000002 ; not supported yet
SA_SIGINFO        equ 0x00000004 ; use sa_sigaction instead of sa_handler
SA_ONSTACK        equ 0x08000000
SA_RESTART        equ 0x10000000
SA_NODEFER        equ 0x40000000
SA_RESETHAND      equ 0x80000000
SIGSEGV           equ 11 ; signal id SIGSEGV
STDERR            equ 2  ; standard error

; sigaction structure for installing signal handler
struc sigaction sa, m, f {
        .sa_sigaction dd sa ; void (*sa_sigaction)(int, siginfo_t *, void *);
        .sa_mask      dd m  ; sigset_t sa_mask;
        .sa_flags     dd f  ; int sa_flags;
        .sa_restorer  dd 0  ; void (*sa_restorer)(void); -- obsoleted, don't use it
}

; sigcontext structure used in ucontext below
; it contains eip we're going to overwrite when catching signal
struc sigcontext {
        .gs            rw 1
        .__gsh         rw 1
        .fs            rw 1
        .__fsh         rw 1
        .es            rw 1
        .__esh         rw 1
        .ds            rw 1
        .__dsh         rw 1
        .edi           rd 1
        .esi           rd 1
        .ebp           rd 1
        .esp           rd 1
        .ebx           rd 1
        .edx           rd 1
        .ecx           rd 1
        .eax           rd 1
        .trapno        rd 1
        .err           rd 1
        .eip           rd 1
        .cs            rw 1
        .__csh         rw 1
        .eflags        rd 1
        .esp_at_signal rd 1
        .ss            rw 1
        .__ssh         rw 1
        .fpstate       rd 1
        .oldmask       rd 1
        .cr2           rd 1
}

virtual at 0
        sigcontext sigcontext
end virtual

; sigset structure used in ucontext below
_NSIG             equ 64
_NSIG_BPW         equ 32
_NSIG_WORDS       equ (_NSIG / _NSIG_BPW)

struc sigset_t {
        .sig rd _NSIG_WORDS
}

; signalstack structure used in ucontext below
struc signaltstack {
        ss_sp    rd 1
        ss_flags rd 1
        ss_size  rd 1
}

; structure type for 3rd parameter of signal handler
struc ucontext {
        .uc_flags    rd 1
        .uc_link     rd 1
        .uc_stack    signaltstack
        .uc_mcontext sigcontext
        .uc_sigmask  sigset_t
}

virtual at 0
        ucontext ucontext
end virtual

; some dangereous statements
macro do_dirty {
        push    dword [ds:0]
        mov     dword [ds:0], eax
        push    dword [fs:0]
}

; determine size of those statements at compilation time
virtual at 0
        do_dirty
        sizeof.do_dirty=$
end virtual


; define section for code and constants
section readable executable

; new_act is constant that's why it is here
new_act sigaction my_signal_handler, 0,  SA_SIGINFO

start:
; install signal handler
        mov     eax, SYSCALL_SIGACTION
        mov     ebx, SIGSEGV
        mov     ecx, new_act
        xor     edx, edx
        int     0x80
; check result of installing signal handler
        test    eax, eax
        mov     ebx, eax ; set exit value
        jnz     .finish
; do something dirty
        do_dirty
; print reborn message
        mov     eax, SYSCALL_WRITE
        mov     ebx, STDERR
        mov     ecx, reborn_msg
        mov     edx, reborn_msg_size
        int     0x80
        xor     ebx, ebx
.finish:
        mov     eax, SYSCALL_EXIT
        int     0x80

my_signal_handler:
; print exception message
        mov     eax, SYSCALL_WRITE
        mov     ebx, STDERR
        mov     ecx, signal_handler_msg
        mov     edx, signal_handler_msg_size
        int     0x80
; get parameter ucontext
        mov     eax, [esp+12]
; modify eip
        add     dword [eax+ucontext.uc_mcontext+sigcontext.eip], sizeof.do_dirty
        ret

; define section for data
section readable writeable
signal_handler_msg db 'Hmmm, SIGSEGV signal received', 0xa
signal_handler_msg_size = $-signal_handler_msg
reborn_msg db "but I'm still alive", 0xa
reborn_msg_size = $-reborn_msg    

But now also I have a question: If I change the order of the two sections (code and data) then I get segfault. Why?
Post 24 Sep 2005, 17:35
View user's profile Send private message Reply with quote
nixnoob



Joined: 23 Sep 2005
Posts: 10
nixnoob 25 Sep 2005, 13:25
Endre,
thanks a lot for that fine code
now atleast i can understand how to alter eip to point to some other place
thanks a lot

hope you could find a few more of those precious seconds to have a go at
peeking and poking the debug registers and find if you would be able to
modify them (meaning you had set an awatch or rwatch(assuming they use hw break point in gdb on reborn message but clear the debug registers in the exception handler so that gdb never broke on a reborn message but
printed the reborn message and exited straight away Smile
that code would really rock the boat a little

thanks and regards
Post 25 Sep 2005, 13:25
View user's profile Send private message Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 25 Sep 2005, 13:33
Quote:
now atleast i can understand how to alter eip to point to some other place

Code:
macro mov arg1,arg2
{
   if arg1 eq eip
      jmp arg2
   else
     mov arg1,arg2
}    

Wink
Post 25 Sep 2005, 13:33
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
nixnoob



Joined: 23 Sep 2005
Posts: 10
nixnoob 26 Sep 2005, 11:21
Endre,
i did not notice you asked a question at the end yesterday
well let me try assembling the code and running along with it in gdb

is any one converting ollydbg to lollypopbug in linux damn thats an excellent debugger the gdb on shell and its x *(ulong *)$esp are making the debugging too gruesome in linux

@vid
its not only about altering eip but also about altering many things
thanks for macro hope i could use it some day
Post 26 Sep 2005, 11:21
View user's profile Send private message Reply with quote
Endre



Joined: 29 Dec 2003
Posts: 215
Location: Budapest, Hungary
Endre 26 Sep 2005, 12:05
I use ald instead of gdb. For assembly it's better than gdb.
BTW I don't know who (me, fasm, linux?) made here the mistake

For reading/writing debug registers you have to use ptrace system call (id = 26, see /usr/include/asm/unistd.h). For ptrace you must use the structure 'user' which can be found in /usr/include/asm/user.h. You should already be able to convert that C-structure to an equivalent fasm structure.
Post 26 Sep 2005, 12:05
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  
Goto page 1, 2, 3  Next

< 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.