庞大资源库的计算机教程网站!
设为首页
加入收藏
总编信箱
投稿或申请专栏请先 [登 陆]
首页 操作系统 程序设计 图形图像 媒体动画 机械电子 WEB开发 数 据 库 办公系列 路由技术 网络原理 网络应用
认证考试 安全技术
首页>安全技术>黑客攻防>黑客编程>正文
资料搜索
Google搜索
Google
返回上级列表

推荐文章

快速保存网页中所有图片的方法
Windows中让光驱巧妙“隐身”技
防范非法用户入侵Win 2000/XP系
两款比较典型的ASP木马防范方法
有关表格边框的css语法整理
Windows XP中可以被禁用的服务
SQL Server导出导入数据方法
Javascript所有对象的属性的获
网页(HTML)中的特殊字符
与篮球共舞,尽显模式本色
QQ病毒的手工清除方法
Photoshop为极品美女打造性感睫
天衣无缝:IIS与PHP水火也相容
SQL Server存储过程编写和优化

内核处理信号对应用层堆栈的影响

 作者:本站收集   日期:2005-5-23
字号选择〖 〗/ 双击滚屏 单击停止   
  本文着重点在内核信号处理对应用层堆栈的影响上,其他的一些在处理信号细节上被忽略。至于本文是否跟安全相关,那就是仁者见仁智者见智了。

1 发送信号过程:
发送信号的过程比接收信号的过程简单的多。当应用层用KILL命令向某个进程发送进程的时候,内核只在进程task_struct的sigpending结构中安排一个信号位。

2 接收信号过程
信号处理的时机。
当某个进程有悬而未决的信号的时候,内核就会调用do_signal函数
do_signal做一些其他功能上的事情,真正递送一个信号是在handle_signal函数。于是在最后
do_signal函数调用了handle_signal真正递送一个信号。当然要想到达这一步需要一些条件。比如说
应用层已经声明要处理该信号,信号不是些不可捕获的信号等等...


重点中的重点,我们来看看handle_signal函数


/*
* OK, we're invoking a handler
*/


static void
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
{
....
/* Set up the stack frame */
if (ka->sa.sa_flags & SA_SIGINFO)
setup_rt_frame(sig, ka, info, oldset, regs);
else
setup_frame(sig, ka, oldset, regs);


.....
}
去掉一些我们不想关心的东西,代码就剩下上面这些。
以上函数setup_rt_frame和setup_frame就是内核在应用层的堆栈上安排信号堆栈帧的过程,就是我们所
要关注的。setup_rt_frame和setup_frame雷同,我们就来分析下setup_frame函数



static void setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs * regs)
{
struct sigframe *frame;
int err = 0;


frame = get_sigframe(ka, regs, sizeof(*frame)); //决定要使用应用层堆栈的地址


if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) //判断是否可写
goto give_sigsegv;


err |= __put_user((current->exec_domain
&& current->exec_domain->signal_invmap
&& sig < 32
? current->exec_domain->signal_invmap[sig]
: sig),
&frame->sig);
if (err)
goto give_sigsegv;

/*保存寄存器信号到&frame->sc和&frame->fpstate中*/
err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
if (err)
goto give_sigsegv;


if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
if (err)
goto give_sigsegv;


/* Set up to return from userspace. If provided, use a stub
already in userspace. */
if (ka->sa.sa_flags & SA_RESTORER) {
err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
} else {
/*把frame->retcod的地址放到&frame->pretcode中,这样当信号处理函数返回时候就会*/
/*跳到frame->retcode地址去执行代码了*/
err |= __put_user(frame->retcode, &frame->pretcode);
/* This is popl %eax ; movl $,%eax ; int $0x80 */
err |= __put_user(0xb858, (short *)(frame->retcode+0));
err |= __put_user(__NR_sigreturn, (int *)(frame->retcode+2));
err |= __put_user(0x80cd, (short *)(frame->retcode+6));
/*以上在frame->retcode上安排了popl %eax ; movl $,%eax ; int $0x80指令*/
}


if (err)
goto give_sigsegv;


/* Set up registers for signal handler */
regs->esp = (unsigned long) frame; //让应用层的esp指向frame;
regs->eip = (unsigned long) ka->sa.sa_handler;//EIP为信号处理函数


set_fs(USER_DS);
regs->xds = __USER_DS;
regs->xes = __USER_DS;
regs->xss = __USER_DS;
regs->xcs = __USER_CS;
regs->eflags &= ~TF_MASK;


#if DEBUG_SIG
printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
current->comm, current->pid, frame, regs->eip, frame->pretcode);
#endif


return;


give_sigsegv:
if (sig == SIGSEGV)
ka->sa.sa_handler = SIG_DFL;
force_sig(SIGSEGV, current);
}


到此,内核在应用层的堆栈上就安排了一个帧,我们来看一下一个实际的例子。


[alert7@redhat73 sigal]$ cat test.c
test ()
{
printf("test");
return;
}
int main(int argv,char **argc) {
char buf[256];


signal(10,test);
while(1);
}
[alert7@redhat73]
(gdb) b main
Breakpoint 1 at 0x8048501
(gdb) r dd dd
Starting program: /home/alert7/sigal/test dd dd


Breakpoint 1, 0x08048501 in main ()
(gdb)
Breakpoint 2 at 0x42029098
(gdb) c
Continuing.


(gdb) x/5i 0x42029098
0x42029098 <__restore>: pop %eax
0x42029099 <__restore+1>: mov $0x77,%eax
0x4202909e <__restore+6>: int $0x80
0x420290a0 <__restore+8>: mov (%esp,1),%ebx
0x420290a3 <__restore+11>: ret
(gdb) i reg esp ebp eip
esp 0xbffff748 0xbffff748
ebp 0xbffffb38 0xbffffb38
eip 0x4202909e 0x4202909e
(gdb) x/50x $esp-8 //$esp-8就是内核构造的一个信号帧
0xbffff740: 0x42029098 0x0000000a 0x00000000 0x00000000
0xbffff750: 0x0000002b 0x0000002b 0xbffffba4 0x40013020
0xbffff760: 0xbffffb38 0xbffffa20 0x4213030c 0xbffffc00
0xbffff770: 0x08049752 0xbffffb2c 0x00000001 0x00000000
0xbffff780: 0x08048570 0x00000023 0x00000346 0xbffffa20
0xbffff790: 0x0000002b 0x00000000 0x00000000 0x00000000


0xbffff7a0: 0x4000083e 0x400005b8 0x40000218 0x400131e8
0xbffff7b0: 0x00000003 0x40013e48 0x00000003 0x42009e38
0xbffff7c0: 0x40013d68 0x0d1fc7ae 0x0d1fc7ae 0xbffff890
0xbffff7d0: 0x40013bc8 0x4200f624 0x00000000 0x00000000
0xbffff7e0: 0x42009e38 0x40013bc8 0x00000000 0x00000000
0xbffff7f0: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffff800: 0x00000000 0x00000000



struct sigframe
{
char *pretcode; //这里为0x42029098,在该程序中,ka->sa.sa_flags 有 SA_RESTORER标志,
//所以没有在堆栈中安排指令,而是使用了一个现成的地址
int sig; //信号为10
struct sigcontext sc;
struct _fpstate fpstate;
unsigned long extramask[_NSIG_WORDS-1];
char retcode[8];
};
struct sigcontext {
unsigned short gs, __gsh;//0,0
unsigned short fs, __fsh;//0,0
unsigned short es, __esh;//0x2b,0
unsigned short ds, __dsh;//0x2b,0
unsigned long edi; //0xbffffba4
unsigned long esi; //0x40013020
unsigned long ebp; //0xbffffb38
unsigned long esp; //0xbffffa20
unsigned long ebx; //0x4213030c
unsigned long edx; //0xbffffc00
unsigned long ecx; //0x08049752
unsigned long eax; //0xbffffb2c
unsigned long trapno; //0x00000001
unsigned long err; //0x00000000
unsigned long eip; //0x08048570
unsigned short cs, __csh; //0x23,0
unsigned long eflags; //0x00000346
unsigned long esp_at_signal; //0xbffffa20
unsigned short ss, __ssh; //0x2b,0
struct _fpstate * fpstate; //0x00000000
unsigned long oldmask; //0x00000000
unsigned long cr2; //0x00000000
};

内核在应用层的堆栈上安了一个帧后,当一返回到应用态的时候就跳到信号处理函数test去执行了。
此时图一 ①,应用层的堆栈多了一个帧,如下:
*************************************************************

(内存高址)
+--------------------------------------+
| ... |
+--------------------------------------+
| char retcode[8] | 8个字节
+--------------------------------------+
| long extramask[_NSIG_WORDS-1]; |
+--------------------------------------+
| struct _fpstate fpstate; |
+--------------------------------------+
| struct sigcontext sc; |
+--------------------------------------+ <---------esp指向这里 ③
| int sig; |
+--------------------------------------+ <---------esp指向这里 ②
| char *pretcode; |
+--------------------------------------+ <---------esp指向这里 ①
| ... |
+--------------------------------------+
(内存低址)


****************************************************************
由于内核是让应用程序跳到信号处理函数的,所以不象一般的调用会把当前的EIP压入堆栈,所以现在
esp指向的pretcode的值将来信号处理完就返回到那里去了。此时ESP情况如图一 ② 的情况

当test信号处理函数完成时候,将返回到frame->pretcode也就是0x42029098的地址去执行,在这里0x42029098地址代码如下:
0x42029098 <__restore>: pop %eax //弹出frame->sig,这里为10
0x42029099 <__restore+1>: mov $0x77,%eax
0x4202909e <__restore+6>: int $0x80 //请求sys_sigreturn系统调用
当执行完以上三条指令的时候,应用层的堆栈就变成了 ③ 的情况了。


忽略切入内核的细节,sys_sigreturn系统调用被调用。下面是该函数的实现细节。
asmlinkage int sys_sigreturn(unsigned long __unused)
{
struct pt_regs *regs = (struct pt_regs *) &__unused;
struct sigframe *frame = (struct sigframe *)(regs->esp - 8);//取得frame地址,-8是为了补上ret和pop
//这两个指令分别弹出的pretcode和sig
//看看上面的图会更清楚
sigset_t set;
int eax;


if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__get_user(set.sig[0], &frame->sc.oldmask)
|| (_NSIG_WORDS > 1
&& __copy_from_user(&set.sig[1], &frame->extramask,
sizeof(frame->extramask))))
goto badframe;


sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(¤t->sigmask_lock);
current->blocked = set;
recalc_sigpending(current);
spin_unlock_irq(¤t->sigmask_lock);

/*把frame保存的一些信息恢复出来,修改regs一些寄存器*/
if (restore_sigcontext(regs, &frame->sc, &eax))
goto badframe;
return eax;


badframe:
force_sig(SIGSEGV, current);
return 0;
}
restore_sigcontext函数好象也没有什么好说的,等到sys_sigreturn函数返回,regs的一些寄存器又恢复到信号来之前的值了。
所以等到内核态在返回到应用态的时候,又恢复到原来的地址去执行了。


参考资料:
linux 2.4.18 kernel src

上一篇:创建IA32下针对Unicode有效的ShellCodes 上    下一篇:利用jmp esp 执行shellcode  
[发送给好友]  [关闭窗口]  [返回顶部]   转载请注明来源:www.it00.com   
特别声明: 本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。
责任编辑: 原点 投稿作者: 本站收集
信息来源: 网络 录入时间: 2005-5-23
关于我们 - 广告服务 - 版权申明 - 网站地图 - 联系方式 - 总编信箱 - 会员投稿