← 블로그

· TIL, 크래프톤 정글

250926 TIL: 정글 생존 25일차

오늘은 하루 종일 BombLab을 하느라 시간을 보냈다. 팀원들과의 코어타임에서도 3시간 동안 어셈블리어 코드를 헤집었는데, 그만큼 성과도 많았던 것 같아 공유해본다.

먼저 설정부터 난관이었다. 우리 셋 다 mac을 사용 중이라 x86 아키텍처 기반의 BombLab이 돌아가지 않았다. 그래서 두 가지 해결책이 나왔는데,

  • 내 방법: Docker에 Ubuntu 컨테이너를 띄워서 도커 터미널에서 작업.

  • 휘서님의 방법: EC2 컨테이너가 어차피 Ubuntu로 돌아가니 거기에 SSH로 접속.

듣고 보니 EC2를 쓰는 게 더 좋아 보일 뿐만 아니라, 내 M1 Air는 Docker를 돌리느라 힘들어했기 때문에 AWS 접속을 시도했다. 하지만 MFA 키 인증 문제로 접속이 되지 않아 결국 Docker로 진행했다.

초기 설정도 쉽지 않았다. 간신히 gdb를 설치했더니 I/O 문제가 생겨서 qemu를 사용했다. 그래서 한 터미널에서는 qemu를 1234 포트로 열어두고, 다른 터미널에서 접속하는 방식으로 해결했다. 그 과정을 정리해본다.


# 0) 컨테이너 들어가기

맥 터미널에서 bomb 파일 폴더로 이동 후 컨테이너 실행(이미 실행 중이면 docker exec로 2번째 쉘 열어도 됨):

cd ~/Downloads/bomb   # ← bomb 파일이 있는 경로
docker run --platform=linux/amd64 -it --rm -v "$(pwd)":/work -w /work ubuntu:22.04 bash

필요 패키지 설치:

apt update -y
apt install -y qemu-user gdb file binutils less

1) 터미널 A: QEMU를 gdb 서버로 띄우기

터미널 A(컨테이너 안)에서 대기 모드로 실행:

# 입력이 필요 없으면
qemu-x86_64 -g 1234 ./bomb

# stdin으로 테스트 문자열을 넣고 싶으면 (예: phase1 정답 후보)
# echo "teststring" > p1.txt
# qemu-x86_64 -g 1234 ./bomb < p1.txt
  • -g 1234 → 1234 포트에서 GDB 접속을 기다림.

  • 이 창은 “Waiting for gdb connection on port 1234” 같은 상태로 대기하게 된다.


2) 터미널 B: GDB로 접속

호스트에서 같은 컨테이너로 2번째 쉘 열기:

docker ps   # 컨테이너 ID 확인
docker exec -it <컨테이너ID> bash
cd /work

GDB 실행 후 심볼 로드 → 원격 접속:

gdb -q ./bomb
(gdb) file ./bomb          # 심볼/섹션 로드 (중요)
(gdb) target remote :1234  # QEMU gdb-stub에 붙기

브레이크포인트 & 진행:

(gdb) break explode_bomb
(gdb) break strings_not_equal
(gdb) break main
(gdb) c                    # continue (QEMU 쪽이 실행 시작)

비교 지점에서 멈추면 인자/문자열 확인:

(gdb) info registers rdi rsi
(gdb) x/s $rdi
(gdb) x/s $rsi
(gdb) bt                   # 누가 호출했는지(phase_1 등) 보기

3) 입력 바꿔가며 재시도하는 요령

  • 입력을 바꾸려면 터미널 A에서 QEMU를 종료하고, 새 입력으로 다시 띄워야 했다.

    # 터미널 A
    pkill -f "qemu-x86_64 -g 1234"  # 또는 Ctrl-C
    echo "새_시도_문자열" > p1.txt
    qemu-x86_64 -g 1234 ./bomb < p1.txt
  • 터미널 B(GDB)는 보통 연결이 끊어지므로, 다시:

    (gdb) target remote :1234
    (gdb) c

원하면 GDB에서 간단 단축키 만들어도 좋다:

(gdb) define rr
Type commands for definition of "rr".
End with a line saying just "end".
>target remote :1234
>c
>end
# 사용: (gdb) rr

4) 흔한 실수/트러블슈팅

  • Connection refused: 터미널 A에서 QEMU가 떠있는지 확인. 포트(1234) 충돌 시 다른 포트 사용(-g 4444) 후 target remote :4444.

  • 심볼이 안 잡히고 주소만 보일 때: file ./bomb를 반드시 target remote 전에 실행.

  • 레지스터가 말이 안 맞는다: remote 모드에서는 $rdi/$rsi가 정상적으로 x86_64 규약을 따른다. (x/s $rdi, x/s $rsi로 문자열 확인)

  • 여러 번 브레이크 걸릴 때: disable 1 같은 걸로 필요 없는 브레이크 비활성화.

  • ASLR 때문에 주소가 바뀌어 보일 때: 보통 qemu-user에서는 큰 문제 없지만, 반복 실행마다 달라지면 브레이크는 심볼 기반으로 건다(예: break strings_not_equal, break phase_1).


5) Bomb Lab Phase 1 빠른 루틴 (qemu-stub 가정)

터미널 A:

qemu-x86_64 -g 1234 ./bomb < /dev/null

터미널 B:

gdb -q ./bomb
(gdb) file ./bomb
(gdb) target remote :1234
(gdb) break strings_not_equal
(gdb) break explode_bomb
(gdb) c
# 멈추면
(gdb) x/s $rdi
(gdb) x/s $rsi
# 상수 문자열(.rodata 주소)이 보이면 그걸 정답 후보로 파일 생성

다시 테스트:

# 터미널 A
pkill -f "qemu-x86_64 -g 1234"
echo "방금_본_문자열" > p1.txt
qemu-x86_64 -g 1234 ./bomb < p1.txt
# 터미널 B
(gdb) target remote :1234
(gdb) c

폭발하지 않고 다음 단계로 넘어가면 성공.


여기까지가 설정 및 Phase 1 풀이 방법이다.

Phase 1 함수에서는 우리가 입력한 문자열(input)을 $rdi에 저장하고, 프로그램 내부에 저장된 특정 문자열을 $rsi로 불러와 비교한다.
즉, strings_not_equal() 함수를 통과시키려면 $rsi가 가리키는 주소의 값을 확인해야 한다. 이 값을 x/s <주소>로 출력하면 정답 문자열을 얻을 수 있다.


오늘은 팀원들과 함께 BombLab Phase 2까지 클리어했지만, 아직 배운 걸 정리하기에는 부족하다. 내일 Phase 3까지 해결한 뒤 Phase 2, 3을 묶어서 올릴 예정이다.

이번 문제들은 꽤 재미있어서 이번 주 TIL 소재는 풍부할 것 같다. 평소에는 11시 땡 하면 퇴근하지만, 요즘은 팀원들과 재밌게 공부하고 있다. 무엇보다 Lab을 하면서 어셈블리어가 예전보다 덜 어렵게 느껴지고, 오히려 재미있어졌다. DP 문제도 아이큐 테스트처럼 오기가 생겨서 재밌게 풀고 있다. 난이도가 올라가면 재미 없으려나..

BOOM!