개요
오늘 과외생 중 한 명이 디버거로 폭탄 프로그램을 해체하는 학교 과제를 들고왔기에
이 참에 어셈블리어 분석하는 방법을 포스트해보려 한다.
BombLab
문제 이름은 BombLab이다.
실행파일이 하나 주어지는데,
이걸 실행하고 알맞지 않은 문자열을 넣으면 터져버린다.
우리의 목표는 이게 터지지 않는 KEY 문자열 을 찾는 것이다. (예시로 넣은 키는 “hello”)
디버거
교수님이 디버거 사용법에 대해서 알려주신게 없단다.
아무거나 쓰라는 모양…
과제 맨 마지막에 써본 디버거가 없으면 GDB를 써보라고 적혀있길래
GDB를 사용해보자~
1. GDB 시작
gdb -tui bomb
2. 어셈블리 코드 보기
layout asm
JavaScript
복사
쉘에서 위와 같이 입력하면 gdb가 파일을 디스어셈블해서 어셈블리로 띄워줄 것이다~
자. 대강 살펴보니,
read_line으로 한 줄 받고 phase1,
read_line으로 한 줄 받고 phase2,
read_line으로 한 줄 받고 phase3
이런식으로 연달아 실행하는 전체 흐름을 알 수 있다.
Phase_2
phase_2 하나만을 기준으로 설명하겠다.
다음은 phase_2의 전체 코드다.
(at&t는 보기 불편해서 intel 문법으로 바꿨다)
천천히 분해해서 살펴보자.
함수 프롤로그/에필로그
일단 함수 시작과 끝
프롤로그와 에필로그 부분을 떼어내자.
rbp, rbx를 보존하고 스택 프레임을 40 byte[0x28] 만큼 잡는걸 볼 수 있다.
함수 본문
뭐 하는건지 모르겠는 부분은 과감하게 일단 지우고… (fs는 스레드 세그먼트임)
일단 함수 맨 처음부터 제일 눈에 띄는 건 read_six_numbers를 호출하는 것이다.
인자로 스택 포인터 주소를 주는것도 확인할 수 있다.
c언어로 따지면 이런 느낌.
void phase_2()
{
char buff[40];
read_six_numbers((int*)buff);
}
C
복사
그림으로 그리면 이런 느낌이다.
read_six_num 함수가 read_line으로 읽은 줄을
atoi처럼 숫자로 바꿔서 스택의 각 6칸에 집어넣는다.
문자열로 “1 2 3 4 5 6” 을 집어넣었다면
int num = { 1, 2, 3, 4, 5, 6 }
형태로 넣어진 것이다.
이제 그 다음을 더 보자.
num[0] > 2 이거나, num[0] == num[1] 이라면 explode_bomb 으로 폭탄을 터뜨려버린다.
그러면
num[0]은 0, 1, 2 중 하나. (사실 ja가 아닌 jg이므로 음수도 됨),
num[1]은 num[0]이 아닌 수. 이여야 한다.
ok. 더 보자.
추가로 전체를 크게 감싸는 루프를 발견할 수 있다.
rbx는 rsp, 즉 &num[0] 부터 시작하여 4바이트씩 증가하고,
rbp는 &num[4] 이다.
이때까지 내용을 c언어로 해석하면 다음과 같이 작성할 수 있을 것 같다.
void phase_2()
{
int num[10];
read_six_numbers(num);
if (num[0] > 2)
explode_bomb();
if (num[0] == num[1])
explode_bomb();
for (int* p = num; p != &num[4]; p++)
{
// something
}
defused();
}
C
복사
큰 틀이 잡혔다.
이제 for 내부만 보면 되겠다.
(p[1] * 2 + p[0]) == p[2]
를 만족하지 않으면 바로 explode_bomb이 된다!
이 조건을 계속 유지시켜야 한다.
c언어로 작성하면 다음과 같다.
void phase_2()
{
int num[10];
read_six_numbers(buff);
if (num[0] > 2)
explode_bomb();
if (num[0] == num[1])
explode_bomb();
for (int* p = num; p != &num[4]; p++)
{
if ((p[1] * 2 + p[0]) != p[2])
explode_bomb();
}
defused();
}
C
복사
해석은 끝났다.
이제 key 문자열을 찾자.
num[0]은 0, num[1]은 1로 잡고 식을 짜자.
f(0) = 0
f(1) = 1
f(x) = f(x - 1) * 2 + f(x - 2)
f(0) = 0
f(1) = 1
f(2) = 2
f(3) = 5
f(4) = 12
f(5) = 29
0 1 2 5 12 29
JavaScript
복사
야호.
정답 문자열은 “0 1 2 5 12 29” 이다.
다른 페이즈 들도 복잡할 뿐이지
똑같은 방법으로 분석할 수 있다.
중요한 것은 어셈블리를 어셈블리 그대로 해석하는게 아니라,
각 파트를 구분해내고 상위레벨(이번 예시에서는 c언어)로
추상화해서 해석하는게 중요하다는 것이다.
(과외 절찬 모집중)