flat assembler
Message board for the users of flat assembler.
Index
> Linux > Exception handling Goto page 1, 2, 3 Next |
Author |
|
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? |
|||
29 Aug 2005, 15:25 |
|
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?
|
|||
30 Aug 2005, 14:36 |
|
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 |
|||
03 Sep 2005, 11:34 |
|
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? |
|||
03 Sep 2005, 15:57 |
|
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. |
|||
03 Sep 2005, 19:09 |
|
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 |
|||
03 Sep 2005, 19:24 |
|
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 |
|||
03 Sep 2005, 19:43 |
|
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). |
|||
03 Sep 2005, 20:36 |
|
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.
|
|||
03 Sep 2005, 21:17 |
|
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.
|
|||
03 Sep 2005, 21:47 |
|
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 |
|||
04 Sep 2005, 13:13 |
|
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 then found nasm works with it and wrote a firstasm.asm 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 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 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 |
|||
23 Sep 2005, 17:43 |
|
Endre 24 Sep 2005, 10:35
nixnoob wrote:
Quote: i saw in the above snippet one can write a reborn message ?? |
|||
24 Sep 2005, 10:35 |
|
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 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 well i keep tinkering on till the dent gets right or the car develops a gaping hole in the place |
|||
24 Sep 2005, 13:21 |
|
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? |
|||
24 Sep 2005, 17:35 |
|
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 that code would really rock the boat a little thanks and regards |
|||
25 Sep 2005, 13:25 |
|
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 } |
|||
25 Sep 2005, 13:33 |
|
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 |
|||
26 Sep 2005, 11:21 |
|
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. |
|||
26 Sep 2005, 12:05 |
|
Goto page 1, 2, 3 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.