网管联盟 | 网管论坛 | 网管u家 | 网管博客 | 网管软件 | 网管求职 | 小游戏 | 网管搜索 | 网管原创 | 网管聚合 | 网管读摘 | 网管焦点 | 世界素材 | 会员投稿 | 会员中心 
中国网管联盟
Windows Linux Cisco 网络技术 数据库 黑客攻防 DotNet Java PHP 认证 新闻资讯 服务器 存储资讯 网络设备 网管学堂 技术专题 焦点 网吧频道
 当前位置: > bitsCN.com > 网络攻防 > 安全防护 > 入侵检测 > 内核处理信号对应用层堆栈的影响  

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

2004-04-11  作者:BitsCN整理  来源:中国网管联盟  点评 投稿 收藏

好久没有为组织做点贡献了,真有点过意不去:(

本文着重点在内核信号处理对应用层堆栈的影响上,其他的一些在处理信号细节上被忽略。
至于本文是否跟安全相关,那就是仁者见仁智者见智了。

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 */
网管有家www.bitscn.net

  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

网管网www.bitscn.com


 ? 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中,这样当信号处理函数返回时候就会*/ 网管联盟bitsCN@com
  /*跳到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;

网管下载dl.bitscn.com



#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
网管u家u.bitsCN.com

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 网管下载dl.bitscn.com
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 网管下载dl.bitscn.com
  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去执行了。
网管u家u.bitsCN.com

此时图一 ①,应用层的堆栈多了一个帧,如下:
**********************************************************************************
图一
  (内存高址)
  +--------------------------------------+
  | ...|
  +--------------------------------------+
  | char retcode[8]| 8个字节
  +--------------------------------------+
  | long extramask[_NSIG_WORDS-1]; |
  +--------------------------------------+
  | struct _fpstate fpstate; |
  +--------------------------------------+
  |struct sigcontext sc; |
  +--------------------------------------+<---------esp指向这里 ③
  | int sig; |
  +--------------------------------------+<---------esp指向这里 ②
  | char *pretcode; |
网管有家www.bitscn.net

  +--------------------------------------+<---------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)

网管u家www.bitscn.net


{
  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(&current->sigmask_lock);
  current->blocked = set;
  recalc_sigpending(current);
  spin_unlock_irq(&current->sigmask_lock); 网管网www.bitscn.com
 
  /*把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

-------the end--------
TAGs影响   应用   信号   处理   0x00000000   函数   frame   if   err    
 上一篇:LKM 注射   下一篇:优化NFR之一 --MSSQL Hello Buffer Overflow
内核处理信号对应用层堆栈的影响 评论:
loading.. 评论加载中…
评论:请自觉遵守互联网相关政策法规,评论不得超过250字。

验证码: 注册用户
本类热门排行:
最新推荐文章:
网管论坛交流: