Linux下的signal调试

Linux下的signal调试

最近逆向时候碰到了好几个关于signal的逆向题,因此来做个总结。

打开gdb输入info handle可以看到gdb对于所有信号量的默认处理方式,我们关注常用的前几个:
在这里插入图片描述

注意到圈起来的部分是gdb在调试时自己处理而不会传递给源程序的几个信号量,因此这几个信号量就大量用于逆向题中搞一些骚操作。对这四个信号量进行改写时,如果不更改gdb中的“pass to program”选项,则程序还是会安装默认的处理方式处理该信号。想要进行调试,需要用handle 信号量名 pass进行更改。

下面我们逐个讲解这四个信号量。

SIGINT

这是linux中的中断信号,当运行程序时按下ctrl+c时就出发了这个中断,正常情况下会结束程序。但是我们可以自己改写对该信号的处理函数。下面就是非常经典的一种用法:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void     INThandler(int);

void  main(void)
{
    signal(SIGINT, INThandler);// install SIGINT的处理函数
    while (1)
        pause();
}

void  INThandler(int sig)
{
    char  c;

    signal(sig, SIG_IGN); // uninstall 当前signal的处理函数
    printf("\nDo you really want to quit? [y/n] ");
    c =getchar();
    if (c == 'y' || c == 'Y')
        exit(0);
    else
        signal(SIGINT, INThandler);
    getchar();  // 读取\n
}

注意到我们在INThandler里先uninstall了对SIGINT的处理,之后再reinstall它。这是考虑到如果在处理上一个信号量时又有一个信号量输入进来导致程序发生错误。

SIGTRAP

The SIGTRAP signal is sent to a process when an exception (or trap) occurs: a condition that a debugger has requested to be informed of — for example, when a particular function is executed, or when a particular variable changes value.

我们的软件断点使用int 3实现,int 3会产生一个SIGTRAP的信号量,当在调试时如果不将该信号pass to program,那么程序就会停在这里作为一个断点。但有时候,程序本身注册了对于SIGTRAP的处理函数,如果不把信号量pass则会产生同一程序在调试与否的情况下会产生不同的运行结果。因此,很多逆向题会用这个方法来隐藏程序真正的逻辑。下面是一个案例:

#include <stdio.h>
#include <signal.h>
   
#define SPC_DEBUGGER_PRESENT (num_traps == 0)
static int num_traps = 0;
   
static void int3handler(int signo) {
  num_traps++;
}
 
int spc_trap_detect(void) {
  if (signal(SIGTRAP, int3handler) == SIG_ERR) return 0;
  raise(SIGTRAP);
  return 1;
}

如果上面的程序正常运行,那么会触发int3handler,因此SPC_DEBUGGER_PRESENT为false。而如果调试器在的话,因为SIGTRAP被调试器截获,因此不会触发num_traps++, SPC_DEBUGGER_PRESENT为True。

SIGSEGV

这个信号量相信大家都不陌生,写bug的程序员们最讨厌它了。SIGSEGV is the signal a program gets from referencing a place in memory far away from all the areas in use.

这个也可以用作反调试,当程序正常运行时,SIGSEGV会被用户写的函数处理,因此不会直接崩溃。但是如果调试时没有pass,则程序会直接崩溃。

void sigsegv_handler(int signo, siginfo_t *info, void *extra) {
    ucontext_t* p = (ucontext_t*)extra;
	char* pc = (void*)(p->uc_mcontext.gregs[REG_RIP]);//获取触发SIGSEGV时的RIP
    char n = pc[5], m = pc[4];
    char *d = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    decode_and_run(d, &pc[8], n, m); //对encrypted_func解密并运行
	munmap(d, 0x1000);
}

void fun() {
    encrypted_func(); //被加密的代码块,里面会触发SIGSEGV
    while(1);
}

SIGALRM

SIGALRM occurs when the alarm clock timer goes off (which happens only if your program has requested an alarm).

在逆向题目中往往用alarm来注册并隐藏用于检查输入是否合法的check函数。因为处理SIGALRM并不会暂停程序(在gdb的info handle中第一个stop是no),所以用alarm可以做出并行处理。下面是一个SIGALRM的样例:

#include  <stdio.h>
#include  <signal.h>

unsigned long  counter;            
int            MAX;                
int            ALARMcount;         
int            SECOND;             

void  ALARMhandler(int sig)
{
     signal(SIGALRM, SIG_IGN);      
     ALARMcount++;              
     printf("*** ALARMhandler --> alarm received no. %d.\n", ALARMcount);
     printf("*** ALARMhandler --> counter = %ld\n", counter);
     printf("*** ALARMhandler --> alarm reset to %d seconds\n", SECOND);
     if (ALARMcount == MAX) {          
          printf("*** ALARMhandler --> Maximum alarm count reached.  exit\n");
          exit(0);
     }          
     counter = 0;                       /* otherwise, reset counter */
     alarm(SECOND);                     /* set alarm for next run   */
     signal(SIGALRM, ALARMhandler);     /* reinstall the handler    */
}

void  main(int argc, char *argv[])
{
     if (argc != 3) {
          printf("Use: %s seconds max-alarm-count\n", argv[0]);
          printf("No. of seconds is set to 1\n");
          SECOND = 1;
          printf("Max number of alarms set to 5\n");
          MAX    = 5;
     }
     else {
          SECOND = atoi(argv[1]);
          MAX    = atoi(argv[2]);
     }
     counter    = 0;              
     ALARMcount = 0;                  
     signal(SIGALRM,ALARMhandler);    
     printf("Alarm set to %d seconds and is ticking now.....\n", SECOND);
     alarm(SECOND);                   
     while (1)                        
          counter++;
}

Reference

Android 平台 Native 代码的崩溃捕获机制及实现

Linux - signal处理的一些补充

Install a Signal Handler

Signals

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值