Signal μ‹œκ·Έλ„

SROP에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄κΈ° 전에 μ‹œκ·Έλ„(Signal)을 μ²˜λ¦¬ν•˜λŠ” 방식을 이해해야 ν•œλ‹€.

μš΄μ˜μ²΄μ œλŠ” μœ μ € λͺ¨λ“œμ™€ 컀널 λͺ¨λ“œλ‘œ λ‚˜λ‰˜μ–΄μ§€λŠ”λ°, νŒŒμΌμ„ μƒμ„±ν•˜κ³ , ν”„λ‘œκ·Έλž¨μ„ μ‹€ν–‰ν•˜λŠ” λͺ¨λ“  μž‘μ—…μ€ 이 두 개의 λͺ¨λ“œκ°€ μ„œλ‘œ μƒν˜Έμž‘μš©ν•˜λ©΄μ„œ 이뀄진닀.

 

μ‹œκ·Έλ„μ„ ν”„λ‘œμ„ΈμŠ€μ— νŠΉμ • 정보λ₯Ό μ „λ‹¬ν•˜λŠ” 맀개체

- λ¦¬λˆ…μŠ€μ—μ„œλŠ” λ‹€μ–‘ν•œ μ‹œκ·Έλ„μ„ μ œκ³΅ν•¨

include/linux/signal.h 일뢀

 *	+--------------------+------------------+
 *	|  POSIX signal      |  default action  |
 *	+--------------------+------------------+
 *	|  SIGHUP            |  terminate	|
 *	|  SIGINT            |	terminate	|
 *	|  SIGQUIT           |	coredump 	|
 *	|  SIGILL            |	coredump 	|
 *	|  SIGTRAP           |	coredump 	|
 *	|  SIGABRT/SIGIOT    |	coredump 	|
 *	|  SIGBUS            |	coredump 	|
 *	|  SIGFPE            |	coredump 	|
 *	|  SIGKILL           |	terminate(+)	|
 *	|  SIGUSR1           |	terminate	|
 *	|  SIGSEGV           |	coredump 	|
 *	|  SIGUSR2           |	terminate	|
 *	|  SIGPIPE           |	terminate	|
 *	|  SIGALRM           |	terminate	|
 *	|  SIGTERM           |	terminate	|
 *	|  SIGCHLD           |	ignore   	|
 *	|  SIGCONT           |	ignore(*)	|
 *	|  SIGSTOP           |	stop(*)(+)  	|
 *	|  SIGTSTP           |	stop(*)  	|
 *	|  SIGTTIN           |	stop(*)  	|
 *	|  SIGTTOU           |	stop(*)  	|
 *	|  SIGURG            |	ignore   	|
 *	|  SIGXCPU           |	coredump 	|
 *	|  SIGXFSZ           |	coredump 	|
 *	|  SIGVTALRM         |	terminate	|
 *	|  SIGPROF           |	terminate	|
 *	|  SIGPOLL/SIGIO     |	terminate	|
 *	|  SIGSYS/SIGUNUSED  |	coredump 	|
 *	|  SIGSTKFLT         |	terminate	|
 *	|  SIGWINCH          |	ignore   	|
 *	|  SIGPWR            |	terminate	|
 *	|  SIGRTMIN-SIGRTMAX |	terminate       |
 *	+--------------------+------------------+
 *	|  non-POSIX signal  |  default action  |
 *	+--------------------+------------------+
 *	|  SIGEMT            |  coredump	|
 *	+--------------------+------------------+

 

 

μ‹œκ·Έλ„ λ™μž‘ 방식

SIGALRM 예제 μ½”λ“œ

// Name: sig_alarm.c
// Compile: gcc -o sig_alarm sig_alarm.c 
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
 
void sig_handler(int signum){
  printf("sig_handler called.\n");
  exit(0);
}
int main(){
  signal(SIGALRM,sig_handler);
  alarm(5);
  getchar();
  return 0;
}

μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄λ©΄, signal ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ SIGALRM μ‹œκ·Έλ„μ΄ λ°œμƒν•˜λ©΄ sig_handler ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•œλ‹€.

SIGALRM μ‹œκ·Έλ„μ΄ λ°œμƒν•˜λ©΄ 컀널 λͺ¨λ“œλ‘œ μ§„μž…ν•œλ‹€.

* μ—¬κΈ°μ„œ μ‹œκ·Έλ„μ„ 컀널 λͺ¨λ“œμ—μ„œ μ²˜λ¦¬ν•˜κ³  λ‚˜μ„œ λ‹€μ‹œ μœ μ € λͺ¨λ“œλ‘œ λŒμ•„μ™€ ν”„λ‘œμ„ΈμŠ€μ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•œλ‹€.

 

=> 즉, μœ μ € λͺ¨λ“œμ˜ μƒνƒœλ₯Ό λͺ¨λ‘ κΈ°μ–΅ν•˜κ³  λ˜λŒμ•„μ˜¬ 수 μžˆμ–΄μ•Ό 함

(μƒνƒœ : μ‹œκ·Έλ„μ΄ λ°œμƒν–ˆμ„ λ•Œ ν”„λ‘œμ„ΈμŠ€μ˜ λ©”λͺ¨λ¦¬, λ ˆμ§€μŠ€ν„° λ“±)

 

 

 

do_signal

do_signal ν•¨μˆ˜λŠ” μ‹œκ·Έλ„μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ κ°€μž₯ λ¨Όμ € ν˜ΈμΆœλ˜λŠ” ν•¨μˆ˜

λ¦¬λˆ…μŠ€ 컀널 5.10μ΄ν•˜ λ²„μ „μ—μ„œλŠ” arch_do_signal, μƒμœ„ λ²„μ „μ—μ„œλŠ” arch_do_signal_or_restart ν•¨μˆ˜λ‘œ κ·Έ 이름이 λ³€κ²½λ˜μ—ˆλ‹€.

 

arch_do_signal_or_restart ν•¨μˆ˜

	void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
{
	struct ksignal ksig;
	if (has_signal && get_signal(&ksig)) {
		/* Whee! Actually deliver the signal.  */
		handle_signal(&ksig, regs);
		return;
	}
	/* Did we come from a system call? */
	if (syscall_get_nr(current, regs) >= 0) {
		/* Restart the system call - no handlers present */
		switch (syscall_get_error(current, regs)) {
		case -ERESTARTNOHAND:
		case -ERESTARTSYS:
		case -ERESTARTNOINTR:
			regs->ax = regs->orig_ax;
			regs->ip -= 2;
			break;
		case -ERESTART_RESTARTBLOCK:
			regs->ax = get_nr_restart_syscall(regs);
			regs->ip -= 2;
			break;
		}
	}
	/*
	 * If there's no signal to deliver, we just put the saved sigmask
	 * back.
	 */
	restore_saved_sigmask();
}

ν•΄λ‹Ή ν•¨μˆ˜μ—μ„œλŠ” μ‹œκ·Έλ„μ— ν•΄λ‹Ήν•˜λŠ” ν•Έλ“€λŸ¬κ°€ λ“±λ‘λ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•œλ‹€. 

λ§Œμ•½ ν•Έλ“€λŸ¬κ°€ λ“±λ‘λ˜μ–΄ μžˆλ‹€λ©΄ μ‹œκ·Έλ„μ— λŒ€ν•œ 정보와 λ ˆμ§€μŠ€ν„° 정보λ₯Ό 인자둜 handle_signal ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•¨

 

 

 

handle_signal

linux/arch/x86/kernel/signal.c λ‚΄ handle_signal ν•¨μˆ˜

static void
handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
    ...
	failed = (setup_rt_frame(ksig, regs) < 0);
	if (!failed) {
		fpu__clear_user_states(fpu);
	}
	signal_setup_done(failed, ksig, stepping);
}

λ‹€μŒμ€ handle_signal ν•¨μˆ˜μ˜ μΌλΆ€λ‘œ, setup_rt_frame ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œλ‹€.

ν•΄λ‹Ή ν•¨μˆ˜λŠ” μ‹œκ·Έλ„μ— 적용된 ν•Έλ“€λŸ¬κ°€ μ‘΄μž¬ν•  경우, ν•Έλ“€λŸ¬μ˜ μ£Όμ†Œλ₯Ό λ‹€μŒ μ‹€ν–‰ μ£Όμ†Œλ‘œ μ‚½μž…ν•œλ‹€.

SIGALRM이 λ°œμƒν•  경우 ν•΄λ‹Ή μ½”λ“œλ₯Ό 톡해 sig_handler ν•¨μˆ˜κ°€ μ‹€ν–‰λœλ‹€.

 

* signal handler  ν˜ΈμΆœ κ³Όμ •

regs->si = (unsigned long)&frame->info;
regs->dx = (unsigned long)&frame->uc;
regs->ip = (unsigned long) ksig->ka.sa.sa_handler;
regs->sp = (unsigned long)frame;

 

 

 

 

sigreturn

ν˜„μž¬ ν”„λ‘œμ„ΈμŠ€κ°€ λ°”λ€ŒλŠ” κ±Έ μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­(Context Switching)이라고 ν•œλ‹€.

μ•žμ„œ μ•Œμ•„λ³Έ 바와 같이 컀널이 μœ μ €κ°€ μ‹€ν–‰ν•œ ν”„λ‘œμ„ΈμŠ€λ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ λ‹€μ–‘ν•œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•œλ‹€. μ»¨ν…μŠ€νŠΈ μŠ€νŠ€μΉ­μ΄ μΌμ–΄λ‚˜λ©΄ λ‹€μ‹œ μœ μ € ν”„λ‘œμ„ΈμŠ€λ‘œ 볡귀해야 ν•œλ‹€. λ”°λΌμ„œ μŠ€μœ„μΉ­μ΄ 일어날 λ•Œ 상황을 μ»€λ„μ—μ„œ κΈ°μ–΅ν•˜κ³ , 컀널 μ½”λ“œμ˜ 싀행을 마치면 κΈ°μ–΅ν•œ 정보λ₯Ό 되돌렀 볡귀해야 ν•œλ‹€. 

μ΄λ•Œ μ‚¬μš©λ˜λŠ” μ‹œμŠ€ν…œ 콜이 sigreturn이닀.

 

λ‹€μŒμ€ restore_sigcontext ν•¨μˆ˜μ˜ μ½”λ“œμ΄λ‹€.

static bool restore_sigcontext(struct pt_regs *regs,
			       struct sigcontext __user *usc,
			       unsigned long uc_flags)
{
	struct sigcontext sc;
	/* Always make any pending restarted system calls return -EINTR */
	current->restart_block.fn = do_no_restart_syscall;
	if (copy_from_user(&sc, usc, CONTEXT_COPY_SIZE))
		return false;
#ifdef CONFIG_X86_32
	set_user_gs(regs, sc.gs);
	regs->fs = sc.fs;
	regs->es = sc.es;
	regs->ds = sc.ds;
#endif /* CONFIG_X86_32 */
	regs->bx = sc.bx;
	regs->cx = sc.cx;
	regs->dx = sc.dx;
	regs->si = sc.si;
	regs->di = sc.di;
	regs->bp = sc.bp;
	regs->ax = sc.ax;
	regs->sp = sc.sp;
	regs->ip = sc.ip;
#ifdef CONFIG_X86_64
	regs->r8 = sc.r8;
	regs->r9 = sc.r9;
	regs->r10 = sc.r10;
	regs->r11 = sc.r11;
	regs->r12 = sc.r12;
	regs->r13 = sc.r13;
	regs->r14 = sc.r14;
	regs->r15 = sc.r15;
#endif /* CONFIG_X86_64 */
	/* Get CS/SS and force CPL3 */
	regs->cs = sc.cs | 0x03;
	regs->ss = sc.ss | 0x03;
	regs->flags = (regs->flags & ~FIX_EFLAGS) | (sc.flags & FIX_EFLAGS);
	/* disable syscall checks */
	regs->orig_ax = -1;
#ifdef CONFIG_X86_64
	/*
	 * Fix up SS if needed for the benefit of old DOSEMU and
	 * CRIU.
	 */
	if (unlikely(!(uc_flags & UC_STRICT_RESTORE_SS) && user_64bit_mode(regs)))
		force_valid_ss(regs);
#endif
	return fpu__restore_sig((void __user *)sc.fpstate,
			       IS_ENABLED(CONFIG_X86_32));
}

sigreturn μ‹œμŠ€ν…œ μ½œμ„ ν˜ΈμΆœν•˜λ©΄ λ‚΄λΆ€μ μœΌλ‘œ ν•΄λ‹Ή ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•΄ μŠ€νƒμ— μ €μž₯ν•œ 값을 각각의 λ ˆμ§€μŠ€ν„°μ— λ³΅μ‚¬ν•˜μ—¬ κΈ°μ‘΄ 상황과 μ‹€ν–‰ν•  μ½”λ“œλ₯Ό κΈ°μ–΅ν•˜κ³  λ³΅κ·€ν•œλ‹€.

	struct sigcontext sc;
	/* Always make any pending restarted system calls return -EINTR */
	current->restart_block.fn = do_no_restart_syscall;
	if (copy_from_user(&sc, usc, CONTEXT_COPY_SIZE))
		return false;

μ½”λ“œλ₯Ό 보면, sigcontext ꡬ쑰체에 μ‘΄μž¬ν•˜λŠ” 각 멀버 λ³€μˆ˜μ— 값을 μ‚½μž…ν•˜λŠ” κ±Έ 확인할 수 μžˆλ‹€.

 

 

 

 

sigcontext

λ‹€μŒμ€ sigcontext ꡬ쑰체이닀.

/* __x86_64__: */
struct sigcontext {
  __u64               r8;
  __u64               r9;
  __u64               r10;
  __u64               r11;
  __u64               r12;
  __u64               r13;
  __u64               r14;
  __u64               r15;
  __u64               rdi;
  __u64               rsi;
  __u64               rbp;
  __u64               rbx;
  __u64               rdx;
  __u64               rax;
  __u64               rcx;
  __u64               rsp;
  __u64               rip;
  __u64               eflags;     /* RFLAGS */
  __u16               cs;
  __u16               gs;
  __u16               fs;
  union {
      __u16           ss; /* If UC_SIGCONTEXT_SS */
      __u16           __pad0; /* Alias name for old (!UC_SIGCONTEXT_SS) user-space */
  };
  __u64               err;
  __u64               trapno;
  __u64               oldmask;
  __u64               cr2;
  struct _fpstate __user      *fpstate;   /* Zero when no FPU context */
#  ifdef __ILP32__
  __u32               __fpstate_pad;
#  endif
  __u64               reserved1[8];
};

μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄λ©΄ λ ˆμ§€μŠ€ν„° λͺ…칭을 κ°€μ§„ 각각의 멀버 λ³€μˆ˜κ°€ μ‘΄μž¬ν•˜λŠ” κ±Έ 확인할 수 μžˆλ‹€.

ν‘œμ— μžˆλŠ” μ½”λ“œλŠ” x86_64 μ•„ν‚€ν…μ²˜μ— ν•΄λ‹Ήν•˜λŠ” ꡬ쑰체이닀. λ”°λΌμ„œ λ‹€λ₯Έ μ•„ν‚€ν…μ²˜μ˜ λ°”μ΄λ„ˆλ¦¬λ₯Ό 곡격할 땐 λŒ€μƒ μ•„ν‚€ν…μ²˜λ₯Ό νŒŒμ•…ν•˜κ³  ν•΄λ‹Ή ꡬ쑰체와 컀널 μ½”λ“œλ₯Ό λΆ„μ„ν•΄μ„œ μ§„ν–‰ν•΄μ•Ό ν•œλ‹€.

 

 

 

 

SROP

SigReturn-Oriented Programming(SROP)λŠ” μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­μ„ μœ„ν•΄ μ‚¬μš©ν•˜λŠ” sigreturn μ‹œμŠ€ν…œ μ½œμ„ μ΄μš©ν•œ ROP기법이닀.

ROPκ°€ μ½”λ“œ 쑰각을 λͺ¨μ•„μ„œ μž„μ˜μ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” 곡격 기법인 κ±Έ 보아, SROPλŠ” sigreturn μ‹œμŠ€ν…œ μ½œμ„ ν˜ΈμΆœν•˜κ³ , λ ˆμ§€μŠ€ν„°μ— 볡사할 값을 미리 μŠ€νƒμ— μ €μž₯ν•΄ μž„μ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” κ±Έ μ•Œ 수 μžˆλ‹€.

λͺ¨λ“  λ ˆμ§€μŠ€ν„°λ₯Ό μ‘°μž‘ν•  수 μžˆλŠ” 만큼 μ΅μŠ€ν”Œλ‘œμž‡ ν™œμš©λ„κ°€ 맀우 λ†’λ‹€.

 

SROP 예제 μ½”λ“œ

// Name: sigrt_call.c
// Compile: gcc -o sigrt_call sigrt_call.c 
#include <string.h>
int main()
{
        char buf[1024];
        memset(buf, 0x41, sizeof(buf));
        asm("mov $15, %rax;"
            "syscall");
}

 

λ‹€μŒ 예제λ₯Ό μ»΄νŒŒμΌν•˜κ³  λ””λ²„κ±°μ—μ„œ 싀행해보면 λ‹€μŒκ³Ό 같이 λ ˆμ§€μŠ€ν„°λ₯Ό μ‘°μž‘ν•  수 μžˆλŠ” κ±Έ 확인할 수 μžˆλ‹€.

 

λ³΅μ‚¬ν–ˆμŠ΅λ‹ˆλ‹€!