how to watch source code in pwngdb 2 ( deep analyze )

이 글에서 이어진다.

일단 기본적인 base는 이전글이랑 같다. 하지만, 왜 작동을 하고 원리를 파악하기 위해 이글을 다룬다. 그리고 이글을 읽어야 앞으로 유동적이게 대처할 수 있다.

C
echo "set substitute-path /build/glibc-OTsEL5/glibc-2.27 /home/bypass_valid_vtable/usr/src/glibc/glibc-2.27" >> ~/.gdbinit

일단 먼저, 이전글에서 다음과 같이 set 문장을 gdbinit에 넣으라고 하고 끝이났다.

그럼 set substitute-path 이 명령어는 뭘까?

set substitute-path A B 는 A(기존 DWARF에 명시된 소스파일 경로)를 B 경로와 연결해주겠다. 라는 의미이다.

그럼 우리 입장에서는 B는 웬만하면 소스.deb파일을 압축해제한 홈디렉토리에 있을 거기에 대충 알겠고

A는 뭘까? 라는 의구심을 가질 수 있겠다. 이전글에서는 pwndbg 에서 info source 명령어로 확인해보면

안뜨면 → info sources

라고 하였다. 일단 알아가야 할것 중 하나는 info source 명령어은 glibc 코드가 진행중인 상태에서 써야 뜬다는 것 → 이게 귀찮으면 info sources 를 사용하면 되긴 한다.

그리고

C
/build/glibc-OTsEL5/glibc-2.27/libio
C
./libio

이런식으로 뜨는 경로가 있으면 그걸 바꿔주면 된다고 설명하였다. → 근데 놀랍게도 완전 정확하고 최적화된 방법이다.

그럼 저 경로는 뭐고 왜 항상 바뀔까? 저 경로는 DWARF에 대해 알아야 한다.

DWARF 란,

리눅스/ELF에서 가장 흔히 쓰는 디버그 정보 포맷이야. 바이너리/라이브러리 안(또는 분리된 디버그 파일)의 .debug_* 섹션들에 들어있고, GDB가 이걸로

  • 주소 ↔ 소스파일/라인(line table) 매핑
  • 함수/타입/변수 이름/스코프(DIE 트리) 해석
  • 컴파일 당시 작업 디렉토리(DW_AT_comp_dir)와 소스 경로(DW_AT_name, .debug_line의 directory/file 테이블)

    같은 걸 알아낸다. 그래서 어떤 소스 파일을 어디서 찾을지도 DWARF를 보고 판단하는 거지.

주요 섹션 예:

  • .debug_info(DIE 트리), .debug_abbrev, .debug_str
  • .debug_line(라인 테이블: 디렉토리/파일 목록 + PC↔라인 매핑)
  • .debug_aranges 등

    분리 디버그 파일을 쓸 때는 .gnu_debuglink나 Build-ID(.note.gnu.build-id)로 연결된다.

즉, 뭐다? 디버깅 심볼에 있는 내용이다. 즉, 본질적으로 우리는 디버깅 심볼이 있어야 소스코드를 정상적으로 붙일 수 있다.

그리고 DW_AT_comp_dir 이 경로가 바로 gdb에서 소스코드를 찾을때 사용하는 경로( A 경로 ) 이다

그래서 깔끔하게 직접 DW_AT_comp_dir 알 수 있는 방법을 설명하겠다.

C
apt-get install -y debuginfod libdebuginfod1 elfutils ca-certificates

debuginfod 및 기타 유틸 다운로드

C
export DEBUGINFOD_URLS="https://debuginfod.elfutils.org/ https://debuginfod.fedoraproject.org/ https://debuginfod.debian.net/ https://debuginfod.ubuntu.com/"
C
DF="$(debuginfod-find debuginfo 89c3cb85f9e55046776471fed05ec441581d1969)"
file "$DF"   # 확인 한번
C
readelf --debug-dump=info "$DF" | egrep 'DW_AT_(comp_dir|name)' | sed -n '1,120p'

이 명령어를 실행하면

(pwnenv) root@6c2f7c50be3f:/home/pwn# readelf --debug-dump=info "$DF" | egrep 'DW_AT_(comp_dir|name)' | sed -n '1,120p' readelf: Error: Unable to find program interpreter name <12> DW_AT_name : (indirect line string, offset: 0x0): ../sysdeps/x86/abi-note.c <16> DW_AT_comp_dir : (indirect line string, offset: 0x77): ./csu <21> DW_AT_name : (indirect string, offset: 0x27c): unsigned char <28> DW_AT_name : (indirect string, offset: 0x2d7): short unsigned int <2f> DW_AT_name : (indirect string, offset: 0x215): unsigned int <36> DW_AT_name : (indirect string, offset: 0x210): long unsigned int <3d> DW_AT_name : (indirect string, offset: 0x27e): signed char <42> DW_AT_name : (indirect string, offset: 0x22e): __uint8_t <50> DW_AT_name : (indirect string, offset: 0x4c): short int <55> DW_AT_name : (indirect string, offset: 0x41): __uint16_t <61> DW_AT_name : (indirect string, offset: 0x350): __int32_t <6f> DW_AT_name : int

다음과 같이 덤프를 볼 수 있는데

<16> DW_AT_comp_dir : (indirect line string, offset: 0x77): ./csu

여기를 보면 ./ 라는 경로에서 glibc내부에 있는 csu 폴더가 있음을 알 수 있다. 즉, 가장 높은 경로가 ./ 라는 것 이게 우리가 찾고 있는 A 경로임을 알 수 있다.

그럼 우리는

C
echo "set substitute-path ./ ./usr/src/glibc/glibc-2.27" >> ~/.gdbinit

이런식으로 하면 되는 것이다