DWARF 바이트코드에 대해서 정리를 해보자.
C++ 에서 try - catch 문이 있고, try 내부에서 익셉션이 발생했다고 치자.
이때 실행흐름이 catch 로 가야하는데, 어떻게해서 가게되는가? 이걸 가도록 해주는게
DWARF 바이트코드의 역할이다.
예를들어 아래의 경우를 생각해보자
#include <stdio.h>
#include <iostream>
#include <exception>
void func2(){ throw 1; }
void func1(){ func2(); }
int main(){
try{
func1();
}
catch(int e){
printf("Exception %d\n", e);
}
return 0;
}
throw 1; 에 해당하는 명령이 수행되는 시점에서 stack frame 은 func2 의 스택프레임이다.
여기서 곧바로 EIP 만 catch 속의 부분으로 바꿔버리면 어떻게될까? 당연히 크래시날것이다.
따라서 익셉션이 발생했을때 실행흐름을 catch 쪽의 핸들러로 옮기는것은 간단한 일이 아니다.
libgcc 속의 익셉션 핸들러 마지막부분을 IDA Trace 로 확인해보면 아래와같이 일련의 Stack Unwinding 작업들과 레지스터 값들의 복원작업등을 마치고 마지막에 RCX 를 Push 하고 ret 하는것을 볼 수 있다. 즉 DWARF 바이트코드들을 통해서 스택과 레지스터들을 핸들러쪽으로 급 이동했을때 문제가없도록 맞춰준다는 것이다.
libgcc_s.so.1:00007F28233E19F2 lea rcx, [rbp+rcx+8]
libgcc_s.so.1:00007F28233E19F7 mov rdx, [rbp-30h]
libgcc_s.so.1:00007F28233E19FB mov rbx, [rbp-28h]
libgcc_s.so.1:00007F28233E19FF mov r12, [rbp-20h]
libgcc_s.so.1:00007F28233E1A03 mov r13, [rbp-18h]
libgcc_s.so.1:00007F28233E1A07 mov r14, [rbp-10h]
libgcc_s.so.1:00007F28233E1A0B mov r15, [rbp-8]
libgcc_s.so.1:00007F28233E1A0F mov rbp, [rbp+0]
libgcc_s.so.1:00007F28233E1A13 mov rsp, rcx
libgcc_s.so.1:00007F28233E1A16 retn
throw 시에 일어나는 일은 libgcc 내부에 구현되어있는 DWARF 바이트코드 인터프리터로 실행흐름을 넘기고
해당 throw 가 발생한 영역의 EIP/RIP 에 따라서 eh_frame 섹션의 DWARF 바이트코드를 실행시키는 것이다.
DWARF 바이트코드들은 CIE 랑 FDE 라는 두종류로 구별되는데, CIE 는 모든 익셉션 핸들러들이 공통적으로
실행하게되는 초반의 바이트코드들을 의미하고 FDE 는 익셉션이 발생한 위치별로 catch 로 가기위해 복구해야하는
스택프레임이 달라지는부분이 반영되는 코드들이다.
용어정리를 조금더 하자면 CFA 는 Canonical Frame Address 인데 한마디로 익셉션이 발생한당시의 스택프레임 베이스주소라고 보면된다.
CIE 와 FDE 의 구조는 다음과 같다.
The FDE structure looks like this:
length
CIE_pointer
initial_location
address_range
LSDA pointer (see section 2.3)
instructions
padding
Whereas the CIE structure contains:
length
CIE_id
version
augmentation (string) (see section 2.3)
address_size
segment_size
code_alignment_factor
data_alignment_factor
return_address_register
initial_instructions
padding
자 그러면 DWARF 바이트코드들을 어떻게 덤프해서 볼수있나?
readelf --debug-dump=frames [appname]
이런식으로 볼 수 있다.
00000068 00000120 00000054 FDE cie=00000018 pc=00400c60..00400e10
DW_CFA_advance_loc: 2 to 00400c62
DW_CFA_def_cfa_offset: 16
DW_CFA_offset: r12 (r12) at cfa-16
DW_CFA_advance_loc: 1 to 00400c63
DW_CFA_def_cfa_offset: 24
DW_CFA_offset: r6 (rbp) at cfa-24
DW_CFA_advance_loc: 6 to 00400c69
DW_CFA_def_cfa_offset: 32
DW_CFA_offset: r3 (rbx) at cfa-32
DW_CFA_advance_loc: 12 to 00400c75
DW_CFA_def_cfa_offset: 160
DW_CFA_advance_loc1: 214 to 00400d4b
DW_CFA_remember_state
DW_CFA_def_cfa_offset: 32
DW_CFA_advance_loc: 1 to 00400d4c
DW_CFA_def_cfa_offset: 24
DW_CFA_advance_loc: 1 to 00400d4d
DW_CFA_def_cfa_offset: 16
DW_CFA_advance_loc: 2 to 00400d4f
DW_CFA_def_cfa_offset: 8
DW_CFA_advance_loc: 1 to 00400d50
DW_CFA_restore_state
DW_CFA_val_expression: r7 (rsp) (DW_OP_breg7 (rsp): -648)
DW_CFA_val_expression: r11 (r11) (DW_OP_breg7 (rsp): 0; DW_OP_deref; DW_OP_constu: 4; DW_OP_plus; DW_OP_deref; DW_OP_constu: 707804966; DW_OP_constu: 32; DW_OP_shl; DW_OP_constu: 1465865297; DW_OP_plus; DW_OP_xor; DW_OP_bra: 8; DW_OP_constu: 4198003; DW_OP_skip: 5; DW_OP_constu: 4196998)
DW_CFA_val_expression: r12 (r12) (DW_OP_constu: 6296768)
DW_CFA_val_expression: r13 (r13) (DW_OP_constu: 4197418)
DW_CFA_val_expression: r14 (r14) (DW_OP_constu: 6296782)
DW_CFA_val_expression: r15 (r15) (DW_OP_constu: 6296488; DW_OP_deref; DW_OP_lit5; DW_OP_swap; DW_OP_lit24; DW_OP_plus; DW_OP_deref; DW_OP_swap; DW_OP_lit1; DW_OP_minus; DW_OP_dup; DW_OP_bra: -11; DW_OP_drop; DW_OP_dup; DW_OP_deref; DW_OP_swap; DW_OP_lit16; DW_OP_plus; DW_OP_deref; DW_OP_constu: 136; DW_OP_plus; DW_OP_dup; DW_OP_deref; DW_OP_swap; DW_OP_lit16; DW_OP_minus; DW_OP_dup; DW_OP_deref; DW_OP_swap; DW_OP_constu: 32; DW_OP_minus; DW_OP_deref; DW_OP_dup; DW_OP_deref_size: 4; DW_OP_swap; DW_OP_lit8; DW_OP_plus; DW_OP_dup; DW_OP_pick: 2; DW_OP_lit4; DW_OP_mul; DW_OP_plus; DW_OP_constu: 7138214; DW_OP_pick: 3; DW_OP_mod; DW_OP_lit4; DW_OP_mul; DW_OP_pick: 2; DW_OP_plus; DW_OP_deref_size: 4; DW_OP_dup; DW_OP_constu: 24; DW_OP_mul; DW_OP_pick: 6; DW_OP_plus; DW_OP_dup; DW_OP_deref_size: 4; DW_OP_pick: 6; DW_OP_plus; DW_OP_constu: 6296806; DW_OP_over; DW_OP_deref_size: 1; DW_OP_over; DW_OP_deref_size: 1; DW_OP_over; DW_OP_lit0; DW_OP_eq; DW_OP_over; DW_OP_lit0; DW_OP_eq; DW_OP_bra: 6; DW_OP_bra: 31; DW_OP_skip: 6; DW_OP_bra: 36; DW_OP_skip: 22; DW_OP_ne; DW_OP_bra: 18; DW_OP_lit1; DW_OP_plus; DW_OP_dup; DW_OP_deref_size: 1; DW_OP_swap; DW_OP_rot; DW_OP_swap; DW_OP_lit1; DW_OP_plus; DW_OP_dup; DW_OP_deref_size: 1; DW_OP_rot; DW_OP_rot; DW_OP_skip: -43; DW_OP_drop; DW_OP_drop; DW_OP_drop; DW_OP_lit8; DW_OP_mul; DW_OP_over; DW_OP_plus; DW_OP_dup; DW_OP_skip: -77; DW_OP_drop; DW_OP_drop; DW_OP_drop; DW_OP_drop; DW_OP_lit8; DW_OP_plus; DW_OP_deref; DW_OP_rot; DW_OP_drop; DW_OP_drop; DW_OP_rot; DW_OP_drop; DW_OP_drop; DW_OP_rot; DW_OP_drop; DW_OP_drop; DW_OP_plus)
여기를 보면 FDE 섹션임을 알 수 있고, CIE=18 즉 18 번 CIE 를 일단 수행한다음 실행되는 부분임을 알 수 있다.
00000018 00000014 00000000 CIE
Version: 1
Augmentation: "zR"
Code alignment factor: 1
Data alignment factor: -8
Return address column: 16
Augmentation data: 1b
DW_CFA_def_cfa: r7 (rsp) ofs 8
DW_CFA_offset: r16 (rip) at cfa-8
DW_CFA_nop
DW_CFA_nop
이와같이 익셉션이 발생한 영역별로 어떤 DWARF 바이트코드들이 libgcc 를 통해서 실행될지를 조사할 수 있다.
이들을 더 효과적으로 조사하고 DWARF 바이트코드를 어셈블하기 위한 프레임워크로는 katana 라는것이 있다.
문제는 DWARF Bytecode 각각의 명령어에 대한 매뉴얼이 있는가? 인데... 이것이 딱히 구글링해도 나오지 않는다.
물론 어느정도 명령어 이름들이 직관적이라 약간의 guessing 으로 무슨 역할을 하는건지 알수있긴 한데
완전한 리스트가 있으면 좋을것 같다. 아마도 katana 가 오픈소스이니 그 속을 조사하면 알수있을것 같긴하다.
* 구글링좀 해보니 GLIBC 소스코드에서 어느정도 의미를 알수있다
http://osxr.org/glibc/source/sysdeps/generic/unwind-dw2.c?v=glibc-2.18
참조문서들
https://github.com/jhector/big-jims-map
https://www.defcon.org/images/defcon-20/dc-20-presentations/Branco-Oakley-Bratus/RodrigoBranco.txt
'Programming' 카테고리의 다른 글
Difference between context switch and mode switch in Linux (2) | 2014.12.19 |
---|---|
How Linux kernel implements raw_spin_lock (0) | 2014.12.10 |
Location of Stack Canary in x64 Linux Process (0) | 2014.12.02 |
QEMU and timer interrupt (0) | 2014.12.02 |
Install z3 for python from source (0) | 2014.11.12 |