-
-
PWN入门-12-SROP拜师
-
发表于: 2024-10-9 20:54 3606
-
信号是用户态进程与内核进行通信的一种方式,它是陷阱(软中断)的一种。如果想要查看所有的信号类型可以查询Linux手册。
信号抵达进行需要经过两个步骤,一是发送信号,而是接收信号。
在Linux中进程的待处理的信号由task_struct
结构体中的signal
成员和pending
成员进行记录,signal
成员和pending
成员的区别在于,signal
成员中存放的待处理信号对整个进程组都是生效的,而pending
成员只对指定的线程有效。
signal
成员由signal_struct
结构体定义,该结构体中的shared_pending
成员是管理共享信号的主要成员,它由由sigpending
结构体定义,task_struct
结构体中的pending
成员也由sigpending
结构体定义。
sigpending
结构体中的list
成员指向了待处理信号队列,从下面的定义中可以看到info
记录了关键的信号信息。
sigpending
结构体中还可以看到一个list
成员的身影,既然sigpending
结构体中的list
成员已经可以管理待处理信号队列了,那么sigpending
结构体中的list
成员又有什么用呢?
要知道,在Linux中信号分成常规信号和实时信号,这里我们需要先了解一下它们的区别。
Linux中1号 - 31号是常规信号,32号+是实时信号。它们的区别在于,同进程下同类型的常规信号只能存在一个,当常规信号被响应后,下一个同类型的常规信号才可以进入队列。
对于实时信号来讲则不是这样,同进程下可以存在多个同类型的实时信号,系统会根据实时信号在队列中的数量进行多次响应。
因此sigpending
结构体中的list
成员管理着不同类型的信号,此链表中的信号类型是不能重复的,sigpending
结构体中的list
成员管理着同类型的信号,如果有需要且信号是实时信号,那么待处理信号就会被插入sigpending
结构体中的list
成员对应的队列中。
通过内核驱动(见附件)指定函数和进程ID,可以将进程尚未处理的信号信息打印出来,从下面可以看到,进程收到了信号SIGTERM
,SIGTERM
信号的序号是15,该信号是对整个进程组生效的。
信号发送的原因可以分成三种,一是内核检测到错误发送(比如段错误,但并不是所有的错误都会导致信号产生)进而向进程组发送信号,二是主动发送信号(比如调用kill
函数、alarm
函数或者使用kill
程序),三是外部事件触发的信号(如I/O设备、其他进程)。
通过Shell运行的进程,通过键盘输入CTRL + C
或CTRL + Z
可以向进程发送SIGINT
或SIGTSTP
信号。
进程接收到信号后,会根据信号的类型执行默认的行为(终止进程、终止进程并转储、挂起、忽略信号)。
在C语言中允许程序通过sigaction
函数(更加强大,signal
函数是sigaction
函数的子集)设置指定信号的处理方法,而不是按照默认行为处理。
C语言提供的信号处理函数并不是所有的信号都可以处理的,比如信号SIGKILL
和SIGSTOP
,它们就必须执行默认行为。
有时候程序接收到信号后,我们会想要知道信号发出方的信息,因此下面给出了一种自定义信号处理函数获取发出方信息的办法。
下方直接给出了自定义信号处理操作的示例代码,代码由信号处理、全局跳转、退出处理三个部分组成。
自定义的信号处理函数会打印信号信息以及发送信息方的信息,发送方的信息被存储在my_signal_handle
中的siginfo
变量内。
运行程序后向程序发送SIGTERM
信号后,程序出现如下的打印,从打印中可以看到程序收到信号15(对应SIGTERM
),si_code
为0对应着SI_USER
,代表信号由用户发出,si_uid
给出了该用户的用户ID,si_pid
给出了发出信号的进程ID。
通过echo $$
可以将kill
程序运行的进程ID打印出来,该进程ID是和si_pid
一致的。
当然这种方法仍然是不能处理某些信号的(如SIGKILL
、SIGSTOP
等等)。
此处以kill
程序为例,我们通过strace工具追踪该程序产生的系统调用。
在打印的内容中可以看到,kill
程序通过kill
函数向内核发出__NR_kill
系统调用。
内核会通过SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
对__NR_kill
系统调用进行接收,其中的kill_something_info
函数是实际处理的信号的地方。
从kill_something_info
函数中不难看出,函数由三个部分组成,它们分别是pid > 0
、pid = -1
、pid < 0
。,当pid > 0
时,发送信号给指定的进程,当pid = -1
时,发送信号给自身外的其余进程,当pid < 0
时,发送信号给自身作者的进程组。
这里我们重点关注pid > 0
的情况。
kill_proc_info
函数最终会调用__send_signal_locked
函数对信号进行处理。
在__send_signal_locked
函数的内部,首先会根据type
变量判断是添加到给进程组还是线程(是PIDTYPE_PID
时添加到线程队列),再通过__sigqueue_alloc
分配一个sigqueue
,然后sigqueue
通过list_add_tail
接口添加到task_strut
中pending
成员的链表内,作为待处理信号,最后将信号信息和发送方信息添加到sigqueue
内。
complete_signal
函数会决定由信号由谁接收。首先判断的条件是wants_signal
函数,在当前任务应该接收信号时,会将接收权限交给当前进程,之后如果发现信号是发送给指定线程或单线程进程的话,就会直接返回,最后会从多线程中找到一个可用的线程。
接下来如果发现发现信号是致命的,就会通过signal_wake_up
接口给每一个线程都添加上TIF_SIGPENDING
标志,反之则只给指定的线程添加TIF_SIGPENDING
标志。
TIF_SIGPENDING
标志代表存在待处理的信号。
不管出于哪种原因发送信号,它们第一个需要的抵达的目标地点都是相同的,这个目标地点就是内核,那么内核又是如何进一步处理信号的呢?
对于内核而言,它会通过do_signal
函数(它是架构指定的,具体名字可能不同)处理信号,下面通过kprobe
机制中的pre_handler
在arch_do_signal_or_restart
函数之前打印出栈回溯(详情可见驱动代码)。
从栈回溯中可以看到,此时用户空间触发系统调用进入内核空间,当do_syscall_64
函数执行完系统调用后,会调用syscall_exit_to_user_mode
函数从内核空间退回到用户空间,使用arch_do_signal_or_restart
函数处理信号的操作也发生在这一阶段。
exit_to_user_mode_loop
函数会接收ti_work
参数,该参数调用read_thread_flags
接口,该接口会从thread_info
结构体内读出flags
成员,接收ti_work
参数后,会检查TIF_SIGPENDING
标志位(上面说过,待处理信号会添加该标志位),如果发现TIF_SIGPENDING
标志位存在,就说明存在待处理信号此时就会调用arch_do_signal_or_restart
函数。
arch_do_signal_or_restart
函数响应的操作分成两部分,一是通过get_signal
函数获取信号信息,二是通过handle_signal
函数处理信号。
handle_signal
函数首先会通过test_thread_flag
函数检查TIF_SINGLESTEP
标志位,该标志位用于标记程序是否被中断下来,如果标志位存在,那就会通过user_disable_single_step
函数将TIF_SINGLESTEP
标志位清除掉,并通知调试器。
当调试器挂载到程序后,再触发信号时,会发现调试器会先收到通知,之后才是信号处理函数,原因就在这里。
setup_rt_frame
函数是一个关键操作,第一步通过get_sigframe
获取一个新的栈帧。
新的栈帧通过rt_sigframe
结构体描述,其中pretcode
代表着信号处理完成后下一步的返回地址,uc
记录了上下文信息,info
记录了信号信息。
通过user_access_end
结束之前的操作,可以将原始的上下文信息保存在用户态程序的栈上。
完成栈上数据的设置操作后,会继续更新用户态程序的寄存器信息,其中当前程序指针寄存器被放置了信号处理函数的地址。
我们在my_signal_handle
函数进行处理时将程序中断下来观察栈回溯。
1号栈帧被内核放置了信号处理结束后的操作__restore_rt
函数,这个函数非常简单,它会将系统调用号放入rax
寄存器内,然后执行系统调用,系统调用号15对应着__NR_rt_sigreturn
。
当__restore_rt
函数触发系统调用时就会再次陷入内核当中,内核根据系统调用__NR_rt_sigreturn
会触发__do_sys_rt_sigreturn
函数。
该函数操作并不复杂,主要就是还原之前保存在栈上的上下文信息。
此时再次回到用户态程序后,程序就会接着执行处理信号前的内容。
在整个信号处理的过程中,内核会将上下文信息保存在用户态程序的栈上,后续再通过sigreturn
系统调用发出恢复信号,因为用户态栈是可读可写的,这非常方便我们进行控制,当我么规划好sigreturn
所需要的栈数据并触发sigreturn
系统调用时,就会让程序跳入我们的控制之内。
那么栈上的上下文信息应该如何构造呢?
被压入栈的上下文信息通过ucontext_t
结构体进行描述,ucontext_t
结构体中的uc_mcontext
成员内的gregs
记录着信号处理函数执行前的寄存器信息。
gregs
中共包含23个寄存器,下面列出了元素0到元素22对应的寄存器名。
显然当我们控制rip
寄存器及传递形参的rdi
等寄存器中数值时,就可以借助sigreturn
的返回操作跳转到我们期望中的位置,除此之外rsp
寄存器也位于栈上,当通过pop rip
(如ret
)操作获取下一条程序指针时,我们就可以通过控制rsp
组成利用链。
程序的源代码和编译命令在下方给出了。
程序并不复杂,为了基于信号返回机制完成ROP,我们这里第一步需要构造sigreturn
需要的栈,在pwntool
中的SigreturnFrame
接口可以直接创造一个假的栈,然后再对里面的数据进行修改。
当我们想要通过execive
创建进程时,首先需要考虑的就是参数问题,由于我们需要给寄存器明确指示参数的所在位置,因此我们需要知道一个栈上的地址,并利用它作为基地址填充数据。
这个程序非常简单,因此原始的栈上只包含argc
、argv
、环境变量以及auxv
,从argv
开始任意的地址都是栈上的地址,程序读取0x400,如果我们可以越过首条指令,让rax
为1,那么就可以泄露rsp+0x0
到rsp+0x400
范围内的数据,并轻松的得到一个栈上的地址。
rax
寄存器非常好控制,它有一个特殊用途,就是保存返回值,如果我们只读取一个字节,并让程序在结束后从mov $0x400,%edx
继续运行,就可以控制rax
寄存器,新发送的一个字节会覆盖rsp+0x0
数据的最低位字节,当rsp+0x0
处原本就存储着一个程序地址,我们再发送mov $0x400,%edx
对应的最低字节数据,就可以跳过xor
指令。
经过上面的分析后,构造出下面的exploit。
运行exploit后成功获取Shell。
在运行调试脚本的时候发现只有打开pwntool
的调试开关后,才可以正常的完成PWN,否则就会失败。
失败之前会先进入交互模式,此时不管你输入什么都会立即失败,比如这里我们直接输入了回车键,然后直接收到了SIGSEGV
的崩溃错误。
观察rsp
上的数据可以发现回车键对应的ASCII码0x0a
被送进了缓冲区当中。
程序仍然在读取信息,这与我们进入交互模式时是与Shell进行交互的初衷有所背离。
显然有部分的信息没有发送给程序。
要知道这是一段极其简单的汇编代码,并且直接通过syscall
调用的read
接口,并没有给stdout
等文件处理缓冲区,由于脚本发送数据的速度过快,同时又没有缓冲区进行临时的存在,导致了数据的丢失,因为开启调试模式后,调试信息的输出需要占用一定的时间,所以send
会间隔一段时间后再发送,就不会产生数据丢失的情况。
我们在send
之后添加sleep
函数,也可也缓解这一问题。
struct signal_struct {
refcount_t sigcnt;
......
struct sigpending shared_pending;
......
struct rw_semaphore exec_update_lock;
} __randomize_layout;
struct sigpending {
struct list_head
list
;
sigset_t signal;
};
struct taks_struct {
......
struct signal_struct
*
signal;
struct sighand_struct __rcu
*
sighand;
struct sigpending pending;
......
}
struct signal_struct {
refcount_t sigcnt;
......
struct sigpending shared_pending;
......
struct rw_semaphore exec_update_lock;
} __randomize_layout;
struct sigpending {
struct list_head
list
;
sigset_t signal;
};
struct taks_struct {
......
struct signal_struct
*
signal;
struct sighand_struct __rcu
*
sighand;
struct sigpending pending;
......
}
#define __SIGINFO \
struct { \
int
si_signo; \
int
si_code; \
int
si_errno; \
union __sifields _sifields; \
}
typedef struct kernel_siginfo {
__SIGINFO;
} kernel_siginfo_t;
struct sigqueue {
struct list_head
list
;
int
flags;
kernel_siginfo_t info;
struct ucounts
*
ucounts;
};
#define __SIGINFO \
struct { \
int
si_signo; \
int
si_code; \
int
si_errno; \
union __sifields _sifields; \
}
typedef struct kernel_siginfo {
__SIGINFO;
} kernel_siginfo_t;
struct sigqueue {
struct list_head
list
;
int
flags;
kernel_siginfo_t info;
struct ucounts
*
ucounts;
};
arch_do_signal_or_restart
[
16176.561445
] pending signal
-
>
[
16176.561447
] shared pending signal
-
>
[
16176.561448
]
00000000
-
signal num
=
15
;
arch_do_signal_or_restart
[
16176.561445
] pending signal
-
>
[
16176.561447
] shared pending signal
-
>
[
16176.561448
]
00000000
-
signal num
=
15
;
void (
*
signal(
int
sig, void (
*
func)(
int
)))(
int
);
int
sigaction(
int
signum,
const struct sigaction
*
_Nullable restrict act,
struct sigaction
*
_Nullable restrict oldact);
特殊的处理函数
-
>
SIG_DFL:执行默认操作
SIG_IGN:忽略信号
void (
*
signal(
int
sig, void (
*
func)(
int
)))(
int
);
int
sigaction(
int
signum,
const struct sigaction
*
_Nullable restrict act,
struct sigaction
*
_Nullable restrict oldact);
特殊的处理函数
-
>
SIG_DFL:执行默认操作
SIG_IGN:忽略信号
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <ucontext.h>
typedef void (
*
signal_handle_func)(
int
, siginfo_t
*
, void
*
);
#define SIGNAL_REGISTER_FAILED ((signal_handle_func)-1)
#define SETJMP_RET_VAL_1 2333
#define KRNL_UCNTXT_ELE_CNT 5
typedef struct my_signal_info {
unsigned
long
sig_num;
signal_handle_func handle_func;
} my_siginfo;
static void my_signal_handle(
int
, siginfo_t
*
, void
*
);
static my_siginfo my_si[]
=
{
{
.sig_num
=
SIGKILL,
.handle_func
=
my_signal_handle,
},
{
.sig_num
=
SIGTERM,
.handle_func
=
my_signal_handle,
},
};
static
int
ret_num
=
0
;
static jmp_buf test_jmp_context;
static void my_atexit_func(void)
{
printf(
"enter %s, program will exit\n"
, __func__);
}
static void my_atexit_register(void (
*
func)(void))
{
int
ret;
ret
=
atexit(func);
if
(ret !
=
0
) {
printf(
"register atexit function failed\n"
);
exit(ret);
}
}
static void siginfo_dump(siginfo_t
*
si)
{
if
(si) {
printf(
"\n[**] signinfo (signinfo_t size 0x%llx) - (_sifields size 0x%llx):\n"
"si_signo = %08d ; si_errno = %08d ; si_code = %08d ;\n"
"si_pid = %08d ; si_uid = %08d ;\n"
"[--] _sifields will be displayed differently depending on the signal\n"
"[--] only pid and uid will be shown here\n"
,
sizeof(
*
si), sizeof(si
-
>_sifields),
si
-
>si_signo, si
-
>si_errno, si
-
>si_code,
si
-
>_sifields._pad[
0
], si
-
>_sifields._pad[
1
]
);
}
}
static void libc_fpstate_dump(fpregset_t fpregs)
{
printf(
"\tcwd = %d ; swd = %d ; ftw = %d ; fop = %d ;\n"
"\trip = 0x%016lx ; rdp = 0x%016lx ;\n"
"\tmxcsr = 0x%08x ; mxcr_mask = 0x%08x ;\n"
"\tno [_st] [_xmm]\n"
,
fpregs
-
>cwd,
fpregs
-
>swd,
fpregs
-
>ftw,
fpregs
-
>fop,
fpregs
-
>rip,
fpregs
-
>rdp,
fpregs
-
>mxcsr,
fpregs
-
>mxcr_mask
);
}
static void ucontext_dump(ucontext_t
*
ucontext)
{
ssize_t arr_size, ele_size, ele_cnt;
int
i;
if
(ucontext) {
printf(
"\n[**] ucontext (ucontext_t size 0x%llx):\n"
"uc_flags = 0x%016lx ; uc_link = 0x%016lx ;\n"
"uc_stack (stack_t size 0x%llx) ->\n"
"\tss_sp = 0x%016lx ; ss_flags = 0x%016lx ; ss_size = 0x%016lx\n"
"uc_mcontext (mcontext_t size 0x%llx) ->\n"
"\t---- gregs start ----"
,
sizeof(
*
ucontext),
ucontext
-
>uc_flags, (unsigned
long
)ucontext
-
>uc_link, sizeof(ucontext
-
>uc_stack),
ucontext
-
>uc_stack.ss_sp, ucontext
-
>uc_stack.ss_flags, ucontext
-
>uc_stack.ss_size,
sizeof(ucontext
-
>uc_mcontext)
);
i
=
0
;
while
(i < __NGREG) {
if
((i
%
4
)
=
=
0
) {
printf(
"\n\t"
);
}
printf(
"0x%016lx ; "
, ucontext
-
>uc_mcontext.gregs[i]
);
i
+
+
;
}
printf(
"\n\t---- gregs end ----\n"
"\t---- fpregs start -----\n"
);
libc_fpstate_dump(ucontext
-
>uc_mcontext.fpregs);
printf(
"\t---- fpregs end ----\n"
);
printf(
"no uc_sigmask (sigset_t size 0x%llx)\n"
"__fpregs_mem (_libc_fpstate size 0x%llx) ->\n"
,
sizeof(ucontext
-
>uc_sigmask), sizeof(ucontext
-
>__fpregs_mem)
);
libc_fpstate_dump(&ucontext
-
>__fpregs_mem);
i
=
0
;
arr_size
=
sizeof(ucontext
-
>__ssp);
ele_size
=
sizeof(unsigned
long
long
);
ele_cnt
=
arr_size
/
ele_size;
printf(
"__ssp (array size 0x%llx) ->\n\t"
,
arr_size
);
while
(i <
4
) {
printf(
"0x%016llx ; "
, ucontext
-
>__ssp[i]);
i
+
+
;
}
printf(
"\n"
);
}
}
static void my_signal_handle(
int
signum, siginfo_t
*
si, void
*
ucontext)
{
printf(
"\n[**] receive signal, signal base info:\n"
"signal num = %d \n"
"signal info = 0x%016lx\n"
"user context = 0x%016lx\n"
,
signum, (unsigned
long
)si, (unsigned
long
)ucontext
);
siginfo_dump(si);
ucontext_dump(ucontext);
}
static signal_handle_func my_customize_signal_register_process(my_siginfo
*
msi)
{
int
ret;
struct sigaction new_act, old_act;
memset(&new_act,
0
, sizeof(struct sigaction));
sigemptyset(&new_act.sa_mask);
new_act.sa_flags
=
SA_SIGINFO;
#ifdef SA_RESTART
new_act.sa_flags |
=
SA_RESTART;
#endif
new_act.sa_sigaction
=
msi
-
>handle_func;
ret
=
sigaction(msi
-
>sig_num, &new_act, &old_act);
if
(ret !
=
0
) {
return
SIGNAL_REGISTER_FAILED;
}
return
old_act.sa_sigaction;
}
static void my_signal_register(void)
{
signal_handle_func tmp_func;
size_t arry_size, ele_size, ele_cnt;
arry_size
=
sizeof(my_si);
ele_size
=
sizeof(my_siginfo);
ele_cnt
=
arry_size
/
ele_size;
do {
tmp_func
=
my_customize_signal_register_process(&my_si[ele_cnt
-
1
]);
if
(tmp_func
=
=
SIGNAL_REGISTER_FAILED) {
printf(
"cannot register signo %d, errno %d\n"
, my_si[ele_cnt
-
1
].sig_num, errno);
}
else
{
printf(
"register signo %d succeed\n"
, my_si[ele_cnt
-
1
].sig_num);
}
}
while
(
-
-
ele_cnt);
}
static void my_signal_setting(void)
{
my_atexit_register(my_atexit_func);
my_signal_register();
}
static void setting4globaljmp(void)
{
printf(
"enter %s\n"
, __func__);
longjmp(test_jmp_context, SETJMP_RET_VAL_1);
printf(
"leave %s\n"
, __func__);
}
static void global_jmp_test(void)
{
int
cur_ret_val;
cur_ret_val
=
setjmp(test_jmp_context);
printf(
"num %d -> setjmp return: %d\n"
, ret_num, cur_ret_val);
ret_num
+
+
;
if
(cur_ret_val
=
=
0
) {
setting4globaljmp();
}
}
int
main(void)
{
my_signal_setting();
global_jmp_test();
printf(
"pid = %d, waiting for a signal\n"
, getpid());
pause();
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#include <ucontext.h>
typedef void (
*
signal_handle_func)(
int
, siginfo_t
*
, void
*
);
#define SIGNAL_REGISTER_FAILED ((signal_handle_func)-1)
#define SETJMP_RET_VAL_1 2333
#define KRNL_UCNTXT_ELE_CNT 5
typedef struct my_signal_info {
unsigned
long
sig_num;
signal_handle_func handle_func;
} my_siginfo;
static void my_signal_handle(
int
, siginfo_t
*
, void
*
);
static my_siginfo my_si[]
=
{
{
.sig_num
=
SIGKILL,
.handle_func
=
my_signal_handle,
},
{
.sig_num
=
SIGTERM,
.handle_func
=
my_signal_handle,
},
};
static
int
ret_num
=
0
;
static jmp_buf test_jmp_context;
static void my_atexit_func(void)
{
printf(
"enter %s, program will exit\n"
, __func__);
}
static void my_atexit_register(void (
*
func)(void))
{
int
ret;
ret
=
atexit(func);
if
(ret !
=
0
) {
printf(
"register atexit function failed\n"
);
exit(ret);
}
}
static void siginfo_dump(siginfo_t
*
si)
{
if
(si) {
printf(
"\n[**] signinfo (signinfo_t size 0x%llx) - (_sifields size 0x%llx):\n"
"si_signo = %08d ; si_errno = %08d ; si_code = %08d ;\n"
"si_pid = %08d ; si_uid = %08d ;\n"
"[--] _sifields will be displayed differently depending on the signal\n"
"[--] only pid and uid will be shown here\n"
,
sizeof(
*
si), sizeof(si
-
>_sifields),
si
-
>si_signo, si
-
>si_errno, si
-
>si_code,
si
-
>_sifields._pad[
0
], si
-
>_sifields._pad[
1
]
);
}
}
static void libc_fpstate_dump(fpregset_t fpregs)
{
printf(
"\tcwd = %d ; swd = %d ; ftw = %d ; fop = %d ;\n"
"\trip = 0x%016lx ; rdp = 0x%016lx ;\n"
"\tmxcsr = 0x%08x ; mxcr_mask = 0x%08x ;\n"
"\tno [_st] [_xmm]\n"
,
fpregs
-
>cwd,
fpregs
-
>swd,
fpregs
-
>ftw,
fpregs
-
>fop,
fpregs
-
>rip,
fpregs
-
>rdp,
fpregs
-
>mxcsr,
fpregs
-
>mxcr_mask
);
}
static void ucontext_dump(ucontext_t
*
ucontext)
{
ssize_t arr_size, ele_size, ele_cnt;
int
i;
if
(ucontext) {
printf(
"\n[**] ucontext (ucontext_t size 0x%llx):\n"
"uc_flags = 0x%016lx ; uc_link = 0x%016lx ;\n"
"uc_stack (stack_t size 0x%llx) ->\n"
"\tss_sp = 0x%016lx ; ss_flags = 0x%016lx ; ss_size = 0x%016lx\n"
"uc_mcontext (mcontext_t size 0x%llx) ->\n"
"\t---- gregs start ----"
,
sizeof(
*
ucontext),
ucontext
-
>uc_flags, (unsigned
long
)ucontext
-
>uc_link, sizeof(ucontext
-
>uc_stack),
ucontext
-
>uc_stack.ss_sp, ucontext
-
>uc_stack.ss_flags, ucontext
-
>uc_stack.ss_size,
sizeof(ucontext
-
>uc_mcontext)
);
i
=
0
;
while
(i < __NGREG) {
if
((i
%
4
)
=
=
0
) {
printf(
"\n\t"
);
}
printf(
"0x%016lx ; "
, ucontext
-
>uc_mcontext.gregs[i]
);
i
+
+
;
}
printf(
"\n\t---- gregs end ----\n"
"\t---- fpregs start -----\n"
);
libc_fpstate_dump(ucontext
-
>uc_mcontext.fpregs);
printf(
"\t---- fpregs end ----\n"
);
printf(
"no uc_sigmask (sigset_t size 0x%llx)\n"
"__fpregs_mem (_libc_fpstate size 0x%llx) ->\n"
,
sizeof(ucontext
-
>uc_sigmask), sizeof(ucontext
-
>__fpregs_mem)
);
libc_fpstate_dump(&ucontext
-
>__fpregs_mem);
i
=
0
;
arr_size
=
sizeof(ucontext
-
>__ssp);
ele_size
=
sizeof(unsigned
long
long
);
ele_cnt
=
arr_size
/
ele_size;
printf(
"__ssp (array size 0x%llx) ->\n\t"
,
arr_size
);
while
(i <
4
) {
printf(
"0x%016llx ; "
, ucontext
-
>__ssp[i]);
i
+
+
;
}
printf(
"\n"
);
}
}
static void my_signal_handle(
int
signum, siginfo_t
*
si, void
*
ucontext)
{
printf(
"\n[**] receive signal, signal base info:\n"
"signal num = %d \n"
"signal info = 0x%016lx\n"
"user context = 0x%016lx\n"
,
signum, (unsigned
long
)si, (unsigned
long
)ucontext
);
siginfo_dump(si);
ucontext_dump(ucontext);
}
static signal_handle_func my_customize_signal_register_process(my_siginfo
*
msi)
{
int
ret;
struct sigaction new_act, old_act;
memset(&new_act,
0
, sizeof(struct sigaction));
sigemptyset(&new_act.sa_mask);
new_act.sa_flags
=
SA_SIGINFO;
#ifdef SA_RESTART
new_act.sa_flags |
=
SA_RESTART;
#endif
new_act.sa_sigaction
=
msi
-
>handle_func;
ret
=
sigaction(msi
-
>sig_num, &new_act, &old_act);
if
(ret !
=
0
) {
return
SIGNAL_REGISTER_FAILED;
}
return
old_act.sa_sigaction;
}
static void my_signal_register(void)
{
signal_handle_func tmp_func;
size_t arry_size, ele_size, ele_cnt;
arry_size
=
sizeof(my_si);
ele_size
=
sizeof(my_siginfo);
ele_cnt
=
arry_size
/
ele_size;
do {
tmp_func
=
my_customize_signal_register_process(&my_si[ele_cnt
-
1
]);
if
(tmp_func
=
=
SIGNAL_REGISTER_FAILED) {
printf(
"cannot register signo %d, errno %d\n"
, my_si[ele_cnt
-
1
].sig_num, errno);
}
else
{
printf(
"register signo %d succeed\n"
, my_si[ele_cnt
-
1
].sig_num);
}
}
while
(
-
-
ele_cnt);
}
static void my_signal_setting(void)
{
my_atexit_register(my_atexit_func);
my_signal_register();
}
static void setting4globaljmp(void)
{
printf(
"enter %s\n"
, __func__);
longjmp(test_jmp_context, SETJMP_RET_VAL_1);
printf(
"leave %s\n"
, __func__);
}
static void global_jmp_test(void)
{
int
cur_ret_val;
cur_ret_val
=
setjmp(test_jmp_context);
printf(
"num %d -> setjmp return: %d\n"
, ret_num, cur_ret_val);
ret_num
+
+
;
if
(cur_ret_val
=
=
0
) {
setting4globaljmp();
}
}
int
main(void)
{
my_signal_setting();
global_jmp_test();
printf(
"pid = %d, waiting for a signal\n"
, getpid());
pause();
}
程序运行结果:
register signo
15
succeed
cannot register signo
9
, errno
22
num
0
-
> setjmp
return
:
0
enter setting4globaljmp
num
1
-
> setjmp
return
:
2333
pid
=
10411
, waiting
for
a signal
[
*
*
] receive signal, signal base info:
signal num
=
15
signal info
=
0x00007ffd67435e30
user context
=
0x00007ffd67435d00
[
*
*
] signinfo (signinfo_t size
0x80
)
-
(_sifields size
0x70
):
si_signo
=
00000015
; si_errno
=
00000000
; si_code
=
00000000
;
si_pid
=
00009013
; si_uid
=
00001000
;
[
-
-
] _sifields will be displayed differently depending on the signal
[
-
-
] only pid
and
uid will be shown here
[
*
*
] ucontext (ucontext_t size
0x3c8
):
uc_flags
=
0x0000000000000006
; uc_link
=
0x0000000000000000
;
uc_stack (stack_t size
0x18
)
-
>
ss_sp
=
0x0000000000000000
; ss_flags
=
0x0000000000000000
; ss_size
=
0x0000000000000000
uc_mcontext (mcontext_t size
0x100
)
-
>
-
-
-
-
gregs start
-
-
-
-
0x0000000000000000
;
0x0000000000000064
;
0x00007ffd67436053
;
0x0000000000000202
;
0x0000000000000000
;
0x00007ffd674362a8
;
0x0000000000403d78
;
0x00007f8de3eec020
;
0x00007ffd67435c20
;
0x0000000001b612a0
;
0x00007ffd67436180
;
0x00007ffd67436298
;
0x0000000000000000
;
0xfffffffffffffffc
;
0x00007f8de3d93d10
;
0x00007ffd67436178
;
0x00007f8de3d93d10
;
0x0000000000000202
;
0x002b000000000033
;
0x0000000000000000
;
0x0000000000000000
;
0x0000000000000000
;
0x0000000000000000
;
-
-
-
-
gregs end
-
-
-
-
-
-
-
-
fpregs start
-
-
-
-
-
cwd
=
895
; swd
=
0
; ftw
=
0
; fop
=
0
;
rip
=
0x0000000000000000
; rdp
=
0x0000000000000000
;
mxcsr
=
0x00001f80
; mxcr_mask
=
0x0002ffff
;
no [_st] [_xmm]
-
-
-
-
fpregs end
-
-
-
-
no uc_sigmask (sigset_t size
0x80
)
__fpregs_mem (_libc_fpstate size
0x200
)
-
>
cwd
=
0
; swd
=
0
; ftw
=
0
; fop
=
0
;
rip
=
0x0000000000000000
; rdp
=
0x0000000000000000
;
mxcsr
=
0x0000037f
; mxcr_mask
=
0x00000000
;
no [_st] [_xmm]
__ssp (array size
0x20
)
-
>
0x0000000000000000
;
0x0000000000000000
;
0x0000000000000000
;
0x00007ffd67436160
;
enter my_atexit_func, program will exit
主动触发程序信息:
kill
-
s SIGTERM
10411
echo $$
9013
程序运行结果:
register signo
15
succeed
cannot register signo
9
, errno
22
num
0
-
> setjmp
return
:
0
enter setting4globaljmp
num
1
-
> setjmp
return
:
2333
pid
=
10411
, waiting
for
a signal
[
*
*
] receive signal, signal base info:
signal num
=
15
signal info
=
0x00007ffd67435e30
user context
=
0x00007ffd67435d00
[
*
*
] signinfo (signinfo_t size
0x80
)
-
(_sifields size
0x70
):
si_signo
=
00000015
; si_errno
=
00000000
; si_code
=
00000000
;
si_pid
=
00009013
; si_uid
=
00001000
;
[
-
-
] _sifields will be displayed differently depending on the signal
[
-
-
] only pid
and
uid will be shown here
[
*
*
] ucontext (ucontext_t size
0x3c8
):
uc_flags
=
0x0000000000000006
; uc_link
=
0x0000000000000000
;
uc_stack (stack_t size
0x18
)
-
>
ss_sp
=
0x0000000000000000
; ss_flags
=
0x0000000000000000
; ss_size
=
0x0000000000000000
uc_mcontext (mcontext_t size
0x100
)
-
>
-
-
-
-
gregs start
-
-
-
-
0x0000000000000000
;
0x0000000000000064
;
0x00007ffd67436053
;
0x0000000000000202
;
0x0000000000000000
;
0x00007ffd674362a8
;
0x0000000000403d78
;
0x00007f8de3eec020
;
0x00007ffd67435c20
;
0x0000000001b612a0
;
0x00007ffd67436180
;
0x00007ffd67436298
;
0x0000000000000000
;
0xfffffffffffffffc
;
0x00007f8de3d93d10
;
0x00007ffd67436178
;
0x00007f8de3d93d10
;
0x0000000000000202
;
0x002b000000000033
;
0x0000000000000000
;
0x0000000000000000
;
0x0000000000000000
;
0x0000000000000000
;
-
-
-
-
gregs end
-
-
-
-
-
-
-
-
fpregs start
-
-
-
-
-
cwd
=
895
; swd
=
0
; ftw
=
0
; fop
=
0
;
rip
=
0x0000000000000000
; rdp
=
0x0000000000000000
;
mxcsr
=
0x00001f80
; mxcr_mask
=
0x0002ffff
;
no [_st] [_xmm]
-
-
-
-
fpregs end
-
-
-
-
no uc_sigmask (sigset_t size
0x80
)
__fpregs_mem (_libc_fpstate size
0x200
)
-
>
cwd
=
0
; swd
=
0
; ftw
=
0
; fop
=
0
;
rip
=
0x0000000000000000
; rdp
=
0x0000000000000000
;
mxcsr
=
0x0000037f
; mxcr_mask
=
0x00000000
;
no [_st] [_xmm]
__ssp (array size
0x20
)
-
>
0x0000000000000000
;
0x0000000000000000
;
0x0000000000000000
;
0x00007ffd67436160
;
enter my_atexit_func, program will exit
主动触发程序信息:
kill
-
s SIGTERM
10411
echo $$
9013
strace
/
usr
/
bin
/
kill
-
s SIGTERM
2790
execve(
"/usr/bin/kill"
, [
"/usr/bin/kill"
,
"-s"
,
"SIGTERM"
,
"2790"
],
0x7ffc5e9561a8
/
*
31
vars
*
/
)
=
0
......
kill(
2790
, SIGTERM)
exit_group(
0
)
=
?
+
+
+
exited with
0
+
+
+
strace
/
usr
/
bin
/
kill
-
s SIGTERM
2790
execve(
"/usr/bin/kill"
, [
"/usr/bin/kill"
,
"-s"
,
"SIGTERM"
,
"2790"
],
0x7ffc5e9561a8
/
*
31
vars
*
/
)
=
0
......
kill(
2790
, SIGTERM)
exit_group(
0
)
=
?
+
+
+
exited with
0
+
+
+
#define __NR_kill 62
#define __NR_kill 62
SYSCALL_DEFINE2(kill, pid_t, pid,
int
, sig)
{
struct kernel_siginfo info;
prepare_kill_siginfo(sig, &info);
return
kill_something_info(sig, &info, pid);
}
SYSCALL_DEFINE2(kill, pid_t, pid,
int
, sig)
{
struct kernel_siginfo info;
prepare_kill_siginfo(sig, &info);
return
kill_something_info(sig, &info, pid);
}
static
int
kill_something_info(
int
sig, struct kernel_siginfo
*
info, pid_t pid)
{
int
ret;
if
(pid >
0
)
return
kill_proc_info(sig, info, pid);
/
*
-
INT_MIN
is
undefined. Exclude this case to avoid a UBSAN warning
*
/
if
(pid
=
=
INT_MIN)
return
-
ESRCH;
read_lock(&tasklist_lock);
if
(pid !
=
-
1
) {
......
}
else
{
......
}
read_unlock(&tasklist_lock);
return
ret;
}
static
int
kill_something_info(
int
sig, struct kernel_siginfo
*
info, pid_t pid)
{
int
ret;
if
(pid >
0
)
return
kill_proc_info(sig, info, pid);
/
*
-
INT_MIN
is
undefined. Exclude this case to avoid a UBSAN warning
*
/
if
(pid
=
=
INT_MIN)
return
-
ESRCH;
read_lock(&tasklist_lock);
if
(pid !
=
-
1
) {
......
}
else
{
......
}
read_unlock(&tasklist_lock);
return
ret;
}
kill_proc_info
-
> kill_pid_info
-
> group_send_sig_info
-
> do_send_sig_info
-
> send_signal_locked
-
> __send_signal_locked
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_TGID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX,
};
static
int
__send_signal_locked(
int
sig, struct kernel_siginfo
*
info,
struct task_struct
*
t, enum pid_type
type
,
bool
force)
{
......
pending
=
(
type
!
=
PIDTYPE_PID) ? &t
-
>signal
-
>shared_pending : &t
-
>pending;
......
q
=
__sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit,
0
);
......
if
(q) {
list_add_tail(&q
-
>
list
, &pending
-
>
list
);
switch ((unsigned
long
) info) {
case (unsigned
long
) SEND_SIG_NOINFO:
clear_siginfo(&q
-
>info);
q
-
>info.si_signo
=
sig;
q
-
>info.si_errno
=
0
;
q
-
>info.si_code
=
SI_USER;
q
-
>info.si_pid
=
task_tgid_nr_ns(current,
task_active_pid_ns(t));
rcu_read_lock();
q
-
>info.si_uid
=
from_kuid_munged(task_cred_xxx(t, user_ns),
current_uid());
rcu_read_unlock();
break
;
......
}
}
......
complete_signal(sig, t,
type
);
......
}
kill_proc_info
-
> kill_pid_info
-
> group_send_sig_info
-
> do_send_sig_info
-
> send_signal_locked
-
> __send_signal_locked
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_TGID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX,
};
static
int
__send_signal_locked(
int
sig, struct kernel_siginfo
*
info,
struct task_struct
*
t, enum pid_type
type
,
bool
force)
{
......
pending
=
(
type
!
=
PIDTYPE_PID) ? &t
-
>signal
-
>shared_pending : &t
-
>pending;
......
q
=
__sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit,
0
);
......
if
(q) {
list_add_tail(&q
-
>
list
, &pending
-
>
list
);
switch ((unsigned
long
) info) {
case (unsigned
long
) SEND_SIG_NOINFO:
clear_siginfo(&q
-
>info);
q
-
>info.si_signo
=
sig;
q
-
>info.si_errno
=
0
;
q
-
>info.si_code
=
SI_USER;
q
-
>info.si_pid
=
task_tgid_nr_ns(current,
task_active_pid_ns(t));
rcu_read_lock();
q
-
>info.si_uid
=
from_kuid_munged(task_cred_xxx(t, user_ns),
current_uid());
rcu_read_unlock();
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- PWN入门-15-偷吃特权-SetUID 1425
- PWN入门-14-整数溢出收徒 3054
- PWN入门-13-险走未知内存布局-BROP 2204
- PWN入门-12-SROP拜师 3607
- [原创]PWN入门-11-制服_dl_resolve_runtime 4572