티스토리 뷰

반응형

CSAPP 랩의 두 번째 실습은 bomb lab이다. "폭탄 랩"이라는 무시무시한 이름을 달고 있고, 실제 난이도도 무시무시하게 어렵다. 왜냐면 object code를 리버싱하여, 각 레지스터에 담긴 데이터를 확인한 다음, 각 레지스터에 알맞은 값이 있도록 해야하기 때문이다.

 

이 랩의 목적은 binary 파일인 "bomb"을 해제하는 것이다. 정확히 말하면, 각 함수에 대하여 알맞은 input을 넣어주어야 하며, 알맞은 input은 어셈블리어를 분석하여 알 수 있다.

 

이번 실습의 특이한 점으로는 실수를 허용하지 않는다는 것이다. 만약 잘못된 input을 넣을 경우, "폭탄이 터진다". 실시간으로 폭탄이 터지는 횟수를 세며, 만약 폭탄이 터질 경우 0.5점이 감점된다. 즉 2번 터뜨릴때마다 1점 감점이라는 이야기란 말씀.

 

다만, 폭탄이 터지는 것을 막는 것은 gdb tool을 이용하면 쉽게 할 수 있다. 각 함수들은 직관적인 이름을 가지고 있고, (예를 들면 explode_bomb은 폭탄을 터뜨리는 함수이고, read_six_numbers는 6개의 정수를 읽는 함수이다.) 폭탄이 터지는 함수에 breakpoint을 걸거나, 하나의 함수에 breakpoint을 걸고 instruction 하나하나씩 천천히 실행하면서 함수가 어떤 control flow를 가지는지 확인하면서 해도 된다. 어느 것을 선택하든 자유다.

 

분석해야 하는 함수는 총 6개로, 총 6개의 phase가 있다고 해도 무방하다. 6개의 phase는 각각 다른 테마를 가지고 있으며, 모두 자주 사용하는 함수들이다. 다만 어셈블리어로 되어있어 우리가 많이 사용하는 함수라고 알아차리는 것은 눈썰미가 좋지 않은 이상 어렵다.

 

0. 폭탄 해체에 앞서

이번 실습에서는 어셈블리어에 대한 내용이므로 이와 관련된 함수들을 알아보아야 한다. 어셈블리어는 AT&T 기준으로 작성되었다.

0-1) Objdump

Objdump는 object file을 dump해주는 함수로, 뒤의 추가 옵션에 따라 다른 결과를 얻을 수 있다.

$ objdump -t bomb

Objdump -t 옵션을 실행하면 bomb의 symbol table을 얻을 수 있다. Symbol table 안에는 bomb 안의 전역 변수와 모든 함수에 대한 정보, 각 함수의 주소를 알 수 있으므로 각 함수의 이름으로 어떤 phase인지 유추할 수 있다!


$ objdump -d bomb

$ objdump -d bomb > bomb.s (to asm file)

-d 옵션은 binary file을 어셈블리어로 바꾸어주는 명령어이다. 이 어셈블리의 control flow를 따라가다 보면, 어떤 역할을 하는 코드인 지 알 수 있을 것이다. 하지만, 어셈블리어가 모든 것을 말해주진 않으니 본인만의 추리력으로 각 함수가 어떤 역할을, 어떤 레지스터에 어떤 정보를 가지고 있는지 알아내야한다!

 

예를 들면, sscanf 함수는 다음과 같은 방식으로 출력된다.

8048c36 : e8 99 fc ff ff	call	80488d4 <_init+0x1a0>

따라서 call instruction이 어떤 함수를 호출하는지 제대로 알아보기 위해서는, gdb를 이용하여 disassemble 해봐야한다.


$ strings bomb

Strings 명령어는 bomb 안에 담겨있는 모든 출력 가능 문자열을 보여준다. 대부분은 쓸모 없는 정보이지만, 안을 뒤지다보면 쓸모있는 정보를 찾을 수도?

 

0-2) GDB

GDB는 디버깅 툴이다. 프로그램을 실행하면서 레지스터 혹은 메모리 주소와 같은 정보를 얻을 수 있는 도구라 생각하면 된다. 다음과 같이 명령어를 입력하여 bomb 관련 gdb를 실행할 수 있다.

$ gdb bomb

gdb와 관련된 명령어는 너무나도 많으니, 이에 대한 항목은 구글링해서 찾아보면 알 수 있다. 다만, 몇 가지 주요 명령어들을 모아보면 다음과 같다.

$ run(=r) <args> : <args> 인자를 넘긴 상태로 프로그램을 실행한다.

$ break(=b) <location> : 프로그램이 <location>전까지 실행되고 <location>에 멈춘다.
이때 location은 실제 메모리 주소이거나, 함수 이름이거나, 줄을 넣을 수도 있다.

$ step(=s) <cnt> / next(=n) <cnt> : <cnt>줄만큼 프로그램을 실행할 수 있다.
s와 n의 차이는 s는 그 줄까지 실행하고, n은 그 줄 전까지 실행한다.

$ continue(=c) : 다음 breakpoint까지 실해한다.

$ info register : 모든 레지스터 안에 담긴 값을 hex로 표현한다.
$ info stack : stack의 backtrace를 보여준다.
$ info breakpoint : breakpoint 상태를 알려준다.

$ print(=p) (/x or /d) (register or address) : 레지스터 혹은 메모리에 담긴 값을 hex(/x) 혹은 dec(/d)으로 나타낸다.

$ x ($register or 0x....) : 레지스터 혹은 메모리에 어떤 값이 담겨있는지 살펴본다.

 

어셈블리 지옥에 온 것을 환영한다!

 

(중요! 여러분들이 받은 bomb은 저와 다른 bomb이고, 다른 답을 가지고 있을 겁니다! 저의 답을 그대로 입력하시지 말고, 논리적인 흐름을 따라와 주세요!)


1. Phase 1

Objdump을 실행하여 <phase_1> 함수의 어셈블리어를 살펴보자.

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi
  400ee9:	e8 4a 04 00 00       	call   401338 <strings_not_equal>
  400eee:	85 c0                	test   %eax,%eax
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>
  400ef2:	e8 43 05 00 00       	call   40143a <explode_bomb>
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	ret

 

 

핵심 줄을 보면, %esi 레지스터 안에 0x402400이 담기는 것을 확인할 수 있다. 그리고 함수 "strings_not_equal"을 호출하고, test를 통해 %eax를 테스트한 이후, 그 결과 같으면 0x17만큼 만큼, 아닐 경우 계속 진행된다.

 

explode_bomb는 je가 false일 때 실행되므로, [test %eax, %eax]에서 반드시 true가 나와야 한다.

 

je는 ZF가 1일 경우 실행된다. 또한, test 명령어는 두 피연산자(여기서 %eax 레지스터)에 대하여 AND 연산을 수행한다. 따라서, %eax가 0일 경우 0, 1일 경우 1을 return하므로 %eax의 값이 0인지 아닌지 판단하는 구절이라 생각할 수 있다.

 

결국, <strings_not_equal>의 반환값이 1이 되면 je 명령어가 수행되지 않아 폭탄이 터지게 된다.

 

그럼, strings_not_equal의 구조를 살피러 가보자!

0000000000401338 <strings_not_equal>:
  401338:	41 54                	push   %r12
  40133a:	55                   	push   %rbp
  40133b:	53                   	push   %rbx
  40133c:	48 89 fb             	mov    %rdi,%rbx
  40133f:	48 89 f5             	mov    %rsi,%rbp
  401342:	e8 d4 ff ff ff       	call   40131b <string_length>
  401347:	41 89 c4             	mov    %eax,%r12d
  40134a:	48 89 ef             	mov    %rbp,%rdi
  40134d:	e8 c9 ff ff ff       	call   40131b <string_length>
  401352:	ba 01 00 00 00       	mov    $0x1,%edx
  401357:	41 39 c4             	cmp    %eax,%r12d
  40135a:	75 3f                	jne    40139b <strings_not_equal+0x63>
  40135c:	0f b6 03             	movzbl (%rbx),%eax
  40135f:	84 c0                	test   %al,%al
  401361:	74 25                	je     401388 <strings_not_equal+0x50>
  401363:	3a 45 00             	cmp    0x0(%rbp),%al
  401366:	74 0a                	je     401372 <strings_not_equal+0x3a>
  401368:	eb 25                	jmp    40138f <strings_not_equal+0x57>
  40136a:	3a 45 00             	cmp    0x0(%rbp),%al
  40136d:	0f 1f 00             	nopl   (%rax)
  401370:	75 24                	jne    401396 <strings_not_equal+0x5e>
  401372:	48 83 c3 01          	add    $0x1,%rbx
  401376:	48 83 c5 01          	add    $0x1,%rbp
  40137a:	0f b6 03             	movzbl (%rbx),%eax
  40137d:	84 c0                	test   %al,%al
  40137f:	75 e9                	jne    40136a <strings_not_equal+0x32>
  401381:	ba 00 00 00 00       	mov    $0x0,%edx
  401386:	eb 13                	jmp    40139b <strings_not_equal+0x63>
  401388:	ba 00 00 00 00       	mov    $0x0,%edx
  40138d:	eb 0c                	jmp    40139b <strings_not_equal+0x63>
  40138f:	ba 01 00 00 00       	mov    $0x1,%edx
  401394:	eb 05                	jmp    40139b <strings_not_equal+0x63>
  401396:	ba 01 00 00 00       	mov    $0x1,%edx
  40139b:	89 d0                	mov    %edx,%eax
  40139d:	5b                   	pop    %rbx
  40139e:	5d                   	pop    %rbp
  40139f:	41 5c                	pop    %r12
  4013a1:	c3                   	ret

이 함수를 잘 보면, 두 문자열의 길이을 통해 비교하여 같은 문자열이면 0, 같지 않은 문자열이면 1을 반환하는 구조를 띄고 있다. 굳이 모든 글자를 해석하지 않아도, %rdi, %rsi, %eax와 %edx에 어떤 값이 담겨있는지를 중심으로 살펴보자. (지금 이해가 안가도 뒤에서 이해될 것이다!)

 

따라서, 두 문자열은 같아야 폭탄이 터지지 않는다는 결론에 이르게 된다.

 

다시 본래의 phase_1을 보자. 원래 %rdi, %rsi에는 함수의 인자값(arguments)가 들어가는 것이 국룰이다. 따라서, x/s 명령어를 통해 어떤 값이 들어있는지 살펴보자. 400ee4까지 실행한 후, 각각에 대한 string에 담긴 값을 보면 다음과 같다. 테스트를 위하여 "testString"을 넣어주었다.

아하! %rdi 레지스터에는 우리가 넣어준 값을, %rsi 레지스터에는 메모리 0x402400에서 문자열을 가져와, 이 둘을 비교하는구나! 라고 결론지을 수 있다.

 

그럼 이걸 바탕으로 첫 번째 답으로 문자열을 넣어주면? 된다!

 

답 : Border relations with Canada have never been better.

반응형
댓글
Total
Today
Yesterday
공지사항
최근에 올라온 글
최근에 달린 댓글
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함