오늘은 하루 종일 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 less1) 터미널 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 /workGDB 실행 후 심볼 로드 → 원격 접속:
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) rr4) 흔한 실수/트러블슈팅
-
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 문제도 아이큐 테스트처럼 오기가 생겨서 재밌게 풀고 있다. 난이도가 올라가면 재미 없으려나..
