Search
Duplicate
🥕

심플한 어셈블리 분석하기

간단소개
팔만코딩경 컨트리뷰터
ContributorNotionAccount
주제 / 분류
Assembly
Scrap
태그
9 more properties

개요

오늘 과외생 중 한 명이 디버거로 폭탄 프로그램을 해체하는 학교 과제를 들고왔기에 이 참에 어셈블리어 분석하는 방법을 포스트해보려 한다.

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언어)로
추상화해서 해석하는게 중요하다는 것이다.
(과외 절찬 모집중)