随想录(关于signal的实验)

时间:2021-01-08 03:45:04

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com


    signal大概是linux和其他rtos之间最重要的区别之一。所谓signal,其实就是用户注册一个signal处理函数,系统会在收到这个signal的时候调用用户的注册函数。因为一般process都有内核堆栈和用户堆栈,而这个注册函数是用户注册的,所以这个signal函数只能在用户侧进行处理。同样,因为signal函数随时会被处理,所以怎么保护用户侧的堆栈也是一个问题。


    一般来说,注册函数会在中断返回或者系统调用返回时进行处理。因为signal函数需要的参数、入口不同,所以这个时候需要kernel修改一下register。那么,之前的用户侧register就没有地方放了,这个时候kernel一般会选择用户侧的空间来做这件事情。通常,os会选择当前sp寄存器下面的一段空间来做这件事情。当然,等到信号处理结束,函数会默认进行syscall,kernel会进入sys_sigreturn。此时,只需要在sys_sigreturn的时候从用户侧恢复register就可以了。一切就像没有发生过一样。


    为了验证我们的想法,可以编写一段测试代码。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
 
int
sig_process(int sig){

          exit(0);
}
 
int
main(int argc, char* argv[]){

        signal(SIGINT, sig_process);
        while(1);
        return 0;
}


    这个时候可以在while循环设置断点的同时,发送SIGINT信号。判断main中sp、sig_process的sp中间是不是对寄存器进行了保护。如果可以找到各个寄存器对应的值,那就可以验证我们判断的正确性了。


$ gdb hello
GNU gdb (GDB) 7.12
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin14.5.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello...Reading symbols from /Users/feixiaoxing/Desktop/hello.dSYM/Contents/Resources/DWARF/hello...done.
done.
(gdb) b main
Breakpoint 1 at 0x100000f52: file hello.c, line 15.
(gdb) b sig_process
Breakpoint 2 at 0x100000f1d: file hello.c, line 9.
(gdb) r
Starting program: /Users/feixiaoxing/Desktop/hello 
warning: `/BinaryCache/coreTLS/coreTLS-35.40.1~1/Objects/coretls.build/coretls.build/Objects-normal/x86_64/system_coretls_vers.o': can't open to read symbols: No such file or directory.
warning: Could not open OSO archive file "/BinaryCache/coreTLS/coreTLS-35.40.1~1/Symbols/BuiltProducts/libcoretls_ciphersuites.a"
warning: Could not open OSO archive file "/BinaryCache/coreTLS/coreTLS-35.40.1~1/Symbols/BuiltProducts/libcoretls_handshake.a"
warning: Could not open OSO archive file "/BinaryCache/coreTLS/coreTLS-35.40.1~1/Symbols/BuiltProducts/libcoretls_record.a"
warning: Could not open OSO archive file "/BinaryCache/coreTLS/coreTLS-35.40.1~1/Symbols/BuiltProducts/libcoretls_stream_parser.a"

Breakpoint 1, main (argc=1, argv=0x7fff5fbffbd0) at hello.c:15
15		signal(SIGINT, sig_process);
(gdb) n
16		while(1);
(gdb) i r
rax            0x0	0
rbx            0x0	0
rcx            0x0	0
rdx            0x0	0
rsi            0x7fff5fbffb38	140734799805240
rdi            0x2	2
rbp            0x7fff5fbffbb0	0x7fff5fbffbb0
rsp            0x7fff5fbffb90	0x7fff5fbffb90
r8             0x0	0
r9             0x7fff5fbfec78	140734799801464
r10            0x1	1
r11            0x202	514
r12            0x0	0
r13            0x0	0
r14            0x0	0
r15            0x0	0
rip            0x100000f60	0x100000f60 <main+48>
eflags         0x302	[ TF IF ]
cs             0x2b	43
ss             <unavailable>
ds             <unavailable>
es             <unavailable>
fs             0x0	0
gs             0x0	0
(gdb) signal SIGINT
Continuing with signal SIGINT.

Breakpoint 2, sig_process (sig=2) at hello.c:9
9		exit(0);
(gdb) i r
rax            0x0	0
rbx            0x7fff5fbffad8	140734799805144
rcx            0x7fff5fbffa70	140734799805040
rdx            0x7fff5fbffad8	140734799805144
rsi            0x7fff5fbffa70	140734799805040
rdi            0x2	2
rbp            0x7fff5fbff630	0x7fff5fbff630
rsp            0x7fff5fbff620	0x7fff5fbff620
r8             0x7fff5fbffad8	140734799805144
r9             0x7fff5fbfec78	140734799801464
r10            0x1	1
r11            0x202	514
r12            0x0	0
r13            0x0	0
r14            0x0	0
r15            0x0	0
rip            0x100000f1d	0x100000f1d <sig_process+13>
eflags         0x246	[ PF ZF IF ]
cs             0x2b	43
ss             <unavailable>
ds             <unavailable>
es             <unavailable>
fs             0x0	0
gs             0x0	0
(gdb) x /128xh $rsp
0x7fff5fbff620:	0x1018	0x0000	0x0001	0x0000	0x0002	0x0000	0x0001	0x0000
0x7fff5fbff630:	0xf640	0x5fbf	0x7fff	0x0000	0xdf1a	0x9101	0x7fff	0x0000
0x7fff5fbff640:	0xfbb0	0x5fbf	0x7fff	0x0000	0xa27c	0x5fc1	0x7fff	0x0000
0x7fff5fbff650:	0x2030	0x0000	0x0001	0x0000	0x0000	0x0000	0x0000	0x0000
0x7fff5fbff660:	0xb48a	0x5fc2	0x7fff	0x0000	0x0001	0x0000	0x0000	0x0000
0x7fff5fbff670:	0xc000	0x007f	0x0001	0x0000	0x0000	0x0000	0x0000	0x0000
0x7fff5fbff680:	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000
0x7fff5fbff690:	0x0000	0x0000	0x0000	0x0000	0x0002	0x0000	0x0000	0x0000
0x7fff5fbff6a0:	0xfb38	0x5fbf	0x7fff	0x0000	0xfbb0	0x5fbf	0x7fff	0x0000
0x7fff5fbff6b0:	0xfb90	0x5fbf	0x7fff	0x0000	0x0000	0x0000	0x0000	0x0000
0x7fff5fbff6c0:	0xec78	0x5fbf	0x7fff	0x0000	0x0001	0x0000	0x0000	0x0000
0x7fff5fbff6d0:	0x0202	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000
0x7fff5fbff6e0:	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000
0x7fff5fbff6f0:	0x0000	0x0000	0x0000	0x0000	0x0f60	0x0000	0x0001	0x0000
0x7fff5fbff700:	0x0202	0x0000	0x0000	0x0000	0x002b	0x0000	0x0000	0x0000
0x7fff5fbff710:	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000	0x0000
(gdb) quit
A debugging session is active.

	Inferior 1 [process 513] will be killed.

Quit anyway? (y or n) y

    明显,我们可以从sp指向的空间里面找到之前rsi、rdi、rbp、r8、rsp、rip的数值,这就可以达到验证的目的了。为了进一步说明问题,可以看看signal返回的汇编代码对不对。

(gdb) si
0x0000000100000f3b in sig_process (sig=-1862148326) at hello.c:9
9	}
(gdb) si
<signal handler called>
(gdb) disassemble $pc,+20
Dump of assembler code from 0x7fff9101df1a to 0x7fff9101df2e:
=> 0x00007fff9101df1a <_sigtramp+26>:	decl   -0x1d28ea50(%rip)        # 0x7fff73d8f4d0
   0x00007fff9101df20 <_sigtramp+32>:	mov    %rbx,%rdi
   0x00007fff9101df23 <_sigtramp+35>:	mov    $0x1e,%esi
   0x00007fff9101df28 <_sigtramp+40>:	callq  0x7fff9101f4d8
   0x00007fff9101df2d <_sigtramp+45>:	retq   
End of assembler dump.
(gdb) si
<signal handler called>
(gdb) si
<signal handler called>
(gdb) si
<signal handler called>
(gdb) si
0x00007fff9101f4d8 in ?? () from /usr/lib/system/libsystem_platform.dylib
(gdb) disassemble $pc,+20
Dump of assembler code from 0x7fff9101f4d8 to 0x7fff9101f4ec:
=> 0x00007fff9101f4d8:	jmpq   *-0x1d2901de(%rip)        # 0x7fff73d8f300
   0x00007fff9101f4de:	jmpq   *-0x1d2901dc(%rip)        # 0x7fff73d8f308
   0x00007fff9101f4e4:	jmpq   *-0x1d2901da(%rip)        # 0x7fff73d8f310
   0x00007fff9101f4ea:	jmpq   *-0x1d2901d8(%rip)        # 0x7fff73d8f318
End of assembler dump.
(gdb) si
0x00007fff86abf708 in __sigreturn () from /usr/lib/system/libsystem_kernel.dylib
(gdb) disassemble $pc,+20
Dump of assembler code from 0x7fff86abf708 to 0x7fff86abf71c:
=> 0x00007fff86abf708 <__sigreturn+0>:	mov    $0x20000b8,%eax
   0x00007fff86abf70d <__sigreturn+5>:	mov    %rcx,%r10
   0x00007fff86abf710 <__sigreturn+8>:	syscall 
   0x00007fff86abf712 <__sigreturn+10>:	jae    0x7fff86abf71c <__sigreturn+20>
   0x00007fff86abf714 <__sigreturn+12>:	mov    %rax,%rdi
   0x00007fff86abf717 <__sigreturn+15>:	jmpq   0x7fff86abac78 <cerror>
End of assembler dump.


    通过调试信号函数返回ip,可以看到最后cpu是走到syscall、准备调用sig_return,这和我们的判断是一样的。