0

I want to set up a basic signal trap.

global _start
%define stdout 1
%define sys_signal 48
%define SIGINT 2
%define SIGSEGV 11

section .text
exit:
    mov eax, 60
    mov rdi, 0
    syscall
catch:
    mov eax, 1
    mov rdi, stdout
    lea rsi, [message]
    mov rdx, 15
    syscall
    jmp exit

_start:
    ; jmp catch
    mov eax, sys_signal
    mov ebx, SIGINT
    mov ecx, catch
    int 80h
loop:
    jmp loop

section .data
    message: db "Signal caught!", 10

This segfaults which Control-C is pressed to interrupt the program instead of printing the "Signal caught!" message.

The above is a simplied version of this example. (I also don't know what options to pass to get that example running.)

This is on linux x86_64 using nasm. But int 80h seem to be supported on my system but ideally I'd like to do this with syscall.

My question is: Could someone give me a minimal example of signal handling for SIGINT or SIGSEGV on x86_64 using nasm without using C?

Thank you.

asrp
  • 31
  • 5
  • 1
    [This](https://stackoverflow.com/a/42356406/547981) is inline assembly but I trust you can turn it into nasm yourself. Also, don't do this :) – Jester Oct 08 '17 at 11:38
  • Thanks for your answer. Unfortunately, no, I don't know how to turn that from C into nasm (without C). (I *can* turn at&t syntax asm into intel syntax asm but the C part I don't know how.) – asrp Oct 08 '17 at 12:11
  • That has barely any C, what is causing you problem? – Jester Oct 08 '17 at 12:25
  • Well, *all of it*. Pretend I can only read assembly and not C (which is almost true: I can barely read any assembly in the first place). Some examples of things I can't read include the "struct kernel_sigaction" and this whole thing at the end: "::"i"(7),"p"(&act),"p"(0):"rax", ... – asrp Oct 08 '17 at 12:41
  • @asrp: That GNU C inline asm constraint block is kind of a mess from the OP of that question. The key point, though, is that you need to provide a sa_restorer function pointer to a function like `restorer: mov eax, 15` / `syscall`. – Peter Cordes Oct 08 '17 at 20:16
  • 1
    @asrp: re: using `int 0x80` from 64-bit code: it's supported, but has no advantages (except if you're messing around with something that you want to build at 32 or 64-bit code, without using a `%if` to select how you make the `sys_exit` at the end.) See https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code – Peter Cordes Oct 08 '17 at 20:18
  • @PeterCordes: Both of those are good to know. Thanks. But because of that mess, I don't know what to write in nasm. I also don't know how to build the kernel_sigaction struct where the restorer (amongst other things) should reside. – asrp Oct 09 '17 at 00:41
  • @asrp: You could run it through a C compiler and look at the intel-syntax output. [`gcc -O3 -masm=intel -S`](https://stackoverflow.com/questions/38552116/how-to-remove-noise-from-gcc-clang-assembly-output). You're going to have to be able to read C struct definitions in Linux header files if you want to use NASM instead of C for everything, so instead of looking at compiler output, you could look at the header files it includes. – Peter Cordes Oct 09 '17 at 00:46
  • See also [this question](https://stackoverflow.com/q/46479421/547981) for nasm code. – Jester Oct 09 '17 at 01:20
  • Thanks @Jester, [that question](https://stackoverflow.com/q/46479421/547981) seems to contain all elements of the answer. – asrp Oct 09 '17 at 12:27

1 Answers1

0

Here is an example of using sigaction on linux in x86 assembler code as abstracted from: NasmX86

  my $end   = Label;
  Jmp $end;                                                                     # Jump over subroutine definition
  my $start = SetLabel;
  Enter 0, 0;                                                                   # Inline code of signal handler
  Mov r15, rbp;                                                                 # Preserve the new stack frame
  Mov rbp, "[rbp]";                                                             # Restore our last stack frame
  PrintOutTraceBack;                                                            # Print our trace back
  Mov rbp, r15;                                                                 # Restore supplied stack frame
  Exit(0);                                                                      # Exit so we do not trampoline. Exit with code zero to show that the program is functioning correctly, else L<Assemble> will report an error.
  Leave;
  Ret;
  SetLabel $end;

  Mov r15, 0;                                                                   # Push sufficient zeros onto the stack to make a struct sigaction as described in: https://www.man7.org/linux/man-pages/man2/sigaction.2.html
  Push r15 for 1..16;

  Mov r15, $start;                                                              # Actual signal handler
  Mov "[rsp]", r15;                                                             # Show as signal handler
  Mov "[rsp+0x10]", r15;                                                        # Add as trampoline as well - which is fine because we exit in the handler so this will never be called
  Mov r15, 0x4000000;                                                           # Mask to show we have a trampoline which is, apparently, required on x86
  Mov "[rsp+0x8]", r15;                                                         # Confirm we have a trampoline

  Mov rax, 13;                                                                  # Sigaction from "kill -l"
  Mov rdi, 11;                                                                  # Confirmed SIGSEGV = 11 from kill -l and tracing with sde64
  Mov rsi, rsp;                                                                 # Sigaction structure on stack
  Mov rdx, 0;                                                                   # Confirmed by trace
  Mov r10, 8;                                                                   # Found by tracing "signal.c" with sde64 it is the width of the signal set and mask. "signal.c" is reproduced below.
  Syscall;
  Add rsp, 128;

  my $s = Subroutine                                                            # Subrotuine that will cause an error to occur to force a trace back to be printed
   {Mov r15, 0;
    Mov r15, "[r15]";                                                           # Try to read an unmapped memory location
   } [qw(in)], name => 'sub that causes a segv';                                # The name that will appear in the trace back

  $s->call(K(in, 42));

  ok Assemble(debug => 0, keep2 => 'signal', emulator=>0, eq => <<END);         # Cannot use the emulator because it does not understand signals

Subroutine trace back, depth: 0000 0000 0000 0001
0000 0000 0000 002A    sub that causes a segv

END

# /var/isde/sde64 -mix -ptr-check -debugtrace -- ./signal
##include <stdlib.h>
##include <stdio.h>
##include <signal.h>
##include <string.h>
##include <unistd.h>
#
#void handle_sigint(int sig)
# {exit(sig);
# }
#
#int main(void)
# {struct sigaction s;
#  memset(&s, 0, sizeof(s));
#  s.sa_sigaction = (void *)handle_sigint;
#
#  long a = 0xabcdef;
#  sigaction(SIGSEGV, &s, 0);
#  long *c = 0; *c = a;
# }
#
# gcc -finput-charset=UTF-8 -fmax-errors=7 -rdynamic -Wall -Wextra -Wno-unused-function -o signal signal.c  && /var/isde/sde64 -mix -ptr-check -debugtrace  -- ./signal; echo $?;
 
  • 1
    Is this perl? It's certainly not NASM or GAS syntax, with quotes around addressing modes, and shenanigans like `my $s = Subroutine { ... }`. Also unclear if / how you're building one or multiple executables from that C and perl source. Maybe use multiple separate code blocks for separate things, and explain in words what it's supposed to do. – Peter Cordes Aug 22 '21 at 01:53
  • 1
    Oh, there are comments way at the far right. Instead of `# Jump over subroutine definition`, don't put your subroutine definition in the way in the first place. Put it after or before `_start` or whatever entry point execution starts at. – Peter Cordes Aug 22 '21 at 02:05
  • @PeterCordes I also have trouble telling what syntax this is, even with your hint of looking to the right. I understand the bottom part is the relevant part of `signal.c` but I still can't quite make sense of all of the top part. In any case, I think I found a solution to my problem 2-3 days after I asked and this answer came almost 4 years later. Though, its still useful for folk coming by search engine. – asrp Aug 23 '21 at 03:21
  • It looks more like one day later and I posted my answer [here](https://stackoverflow.com/a/46645333/8740676) – asrp Aug 23 '21 at 03:27
  • 1
    @asrp: It seems that the NasmX86 linked in this answer (https://github.com/philiprbrenan/NasmX86) is actually a perl module that lets you write perl with parts that are similar to NASM syntax. I assume those parts end up emitting machine code, but this is a very obscure choice of assembler to use for an answer. But yeah, might be more helpful to someone than disassembling the glibc functions in a statically-linked C program that uses signals or something. – Peter Cordes Aug 23 '21 at 03:44