SROP (SigReturn-Oriented Programming)
: sigreturn 시스템 콜을 이용해 레지스터에 원하는 값을 저장하는 방식 >> 원하는 시스템 함수를 호출할 수 있다.
Signal
: 프로세스에게 이벤트가 발생했음을 알리는 역할
- 다른 프로세스에게 시그널을 전송할 수 있다.
= 시그널이 발생하면 그 시그널에 대한 결과로 다른 프로세스에게 시그널이 전송되는 것이 가능
원시적인 형태의 IPC (interprocess communication; 프로세스 간 통신)로 사용
자기 자신에게 시그널을 전송하는 것도 가능
- 일반적으로 커널이 시그널을 송신
- 이벤트 종류
하드웨어 예외가 발생한 경우
사용자가 터미널 특수 문자 중 하나(Ctrl+C; interrupt character)(Ctrl+Z; suspend character) 입력한 경우
소프트웨어 이벤트 발생한 경우: 파일 디스크립터에 입력 발생 / 타이머 만료 / 해당 프로세스의 자식 프로세스 종료
- 시그널 생성 → 프로세스에 전달 → 종류에 따라 동작 실행
: 시그널 무시 / 프로세스 종료 / 코어 덤프 파일 생성 후 프로세스 종료 / 프로세스 중지 / 프로세스 실행 재개
Signal handler
= 프로그램이 특정 시그널의 기본 동작을 수행하는 대신 프로그래머가 원하는 동작을 수행할 수 있도록 변경
- User Mode 프로세스에 정의, User Mode code segment에 포함
- Signal handler가 User Mode에서 실행 중 : Kernel Mode에서 handle_signal()함수 실행
handle_signal()
:
User Mode → Kernel Mode 진입: User Mode에서 사용중이던 context를 Kernel Stack에 저장
Kernel Mode → User Mode 진입: Kernel Stack 모두 초기화
초기화 문제를 해결하기 위해 setup_frame(), sigreturn() 함수 사용
setup_frame(): User Mode의 스택을 설정
sigreturn(): Kernel Mode 스택에 하드웨어 context 복사 + User Mode 스택의 원래 content 저장
- Signal handler 처리 방식
인터럽트/예외 발생: 프로세스 Kernel Mode로 전환
User Mode로 돌아가기 전: 커널은 do_signal()함수 실행
do_signal(): handle_signal() 호출해 signal 처리
handle_signal(): setup_frame() 호출해 User Mode 스택에 context 저장
User Mode로 전환: signal_handler 실행
signal handler 종료: setup_frame()함수에 의해 User Mode 스택에 저장된 리턴 코드 실행 → 리턴 코드에 의해 sigreturn() 시스템 함수 실행
sigreturn() 시스템 함수: Kernel Mode 스택에서 일반 프로그램의 하드웨어 context를 User Mode의 스택에 복사
restore_sigcontext() 호출해 User Mode 스택을 원래 상태로 복원
(restore_sigcontext(): User Mode 스택을 원래 상태로 복원)
시스템 호출 종료: 일반 프로그램 재개
sigreturn()
sigreturn() 시스템 함수; signal을 처리하는 프로세스가 Kernel Mode에서 User Mode로 돌아올 때 스택을 복원하기 위해 사용하는 함수
스택 복원을 위해 restore_sigcontext() 함수를 호출
- x86 sigreturn()
#ifdef CONFIG_X86_32
asmlinkage unsigned long sys_sigreturn(void){
struct pt_regs *regs = current_pt_regs();
struct sigframe __user *frame;
...
if (restore_sigcontext(regs, &frame->sc, 0))
goto badframe;
...
}
#endif /* CONFIG_X86_32 */
https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L604
- x64 sigreturn()
asmlinkage long sys_rt_sigreturn(void){
struct pt_regs *regs = current_pt_regs();
struct rt_sigframe __user *frame;
...
if (restore_sigcontext(regs, &frame->uc.uc_mcontext, uc_flags))
goto badframe;
...
}
https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L636
restore_sigcontext()
COPY_SEG(), COPY() 등을 이용해 스택에 저장된 값을 각 레지스터에 복사
ROP에서는 값을 레지스터에 저장하는 gadget이 필요했지만
gadget없어도 sigreturn()함수를 이용해 각 레지스터에 원하는 값을 저장 가능
- x86 restore_sigcontext()
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long uc_flags){
unsigned long buf_val;
void __user *buf;
unsigned int tmpflags;
unsigned int err = 0;
/* Always make any pending restarted system calls return -EINTR */
current->restart_block.fn = do_no_restart_syscall;
get_user_try {
#ifdef CONFIG_X86_32
set_user_gs(regs, GET_SEG(gs));
COPY_SEG(fs);
COPY_SEG(es);
COPY_SEG(ds);
#endif /* CONFIG_X86_32 */
COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx);
COPY(dx); COPY(cx); COPY(ip); COPY(ax);
...
}
https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L96
#define sigframe_ia32 sigframe
...
#if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION)
struct sigframe_ia32 {
u32 pretcode;
int sig;
struct sigcontext_32 sc;
struct _fpstate_32 fpstate_unused;
#ifdef CONFIG_IA32_EMULATION
unsigned int extramask[_COMPAT_NSIG_WORDS-1];
#else /* !CONFIG_IA32_EMULATION */
unsigned long extramask[_NSIG_WORDS-1];
#endif /* CONFIG_IA32_EMULATION */
char retcode[8];
/* fp state follows here */
};
스택에 저장된 레지스터 값들은 restore_sigcontext()함수의 인자값 &frame->sc에 의해 전달
&frame->sc : sigcontext 구조체; SROP를 이용할 때 스택에 아래와 같은 형태로 값을 저장해야 함
# ifdef __i386__
struct sigcontext {
__u16 gs, __gsh;
__u16 fs, __fsh;
__u16 es, __esh;
__u16 ds, __dsh;
__u32 edi;
__u32 esi;
__u32 ebp;
__u32 esp;
__u32 ebx;
__u32 edx;
__u32 ecx;
__u32 eax;
__u32 trapno;
__u32 err;
__u32 eip;
__u16 cs, __csh;
__u32 eflags;
__u32 esp_at_signal;
__u16 ss, __ssh;
struct _fpstate __user *fpstate;
__u32 oldmask;
__u32 cr2;
};
- x64 restore_sigcontext()
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long uc_flags){
...
#ifdef CONFIG_X86_64
COPY(r8);
COPY(r9);
COPY(r10);
COPY(r11);
COPY(r12);
COPY(r13);
COPY(r14);
COPY(r15);
#endif /* CONFIG_X86_64 */
COPY_SEG_CPL3(cs);
COPY_SEG_CPL3(ss);
#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
...
}
https://elixir.bootlin.com/linux/latest/source/arch/x86/kernel/signal.c#L96
x64의 경우
스택에 저장된 레지스터 값들은 restore_sigcontext()함수의 인자값 &frame->uc.uc_mcontext에 의해 전달
struct rt_sigframe_x32 {
u64 pretcode;
struct ucontext_x32 uc;
compat_siginfo_t info;
/* fp state follows here */
};
struct ucontext_x32 {
unsigned int uc_flags;
unsigned int uc_link;
compat_stack_t uc_stack;
unsigned int uc__pad0; /* needed for alignment */
struct sigcontext uc_mcontext; /* the 64-bit sigcontext type */
compat_sigset_t uc_sigmask; /* mask last for extensibility */
};
x64이기 때문에 사용되는 레지스터가 다름 >> sigcontext 구조체의 형태도 조금 다름
# else /* __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];
};
https://www.lazenca.net/display/TEC/01.SROP%28Sigreturn-oriented+programming%29+-+x86
https://www.lazenca.net/display/TEC/02.SROP%28Sigreturn-oriented+programming%29+-+x64
'System > System Hacking' 카테고리의 다른 글
SROP x64 (0) | 2020.07.24 |
---|---|
SROP x86 (0) | 2020.07.23 |
64bit ROP (0) | 2020.06.14 |
32bit ROP (0) | 2020.06.14 |
RTL (Return to libc) (0) | 2020.05.24 |