<Bomb Lab이란?>
https://studyforall.tistory.com/98
실습 환경에 대해서는 위의 게시물 하단에 적어놓았다.
본격적으로 phase1부터 시작해본다.
먼저 disassemble phase_1 명령어를 입력하여 phase_1 함수를 어셈블리 코드로 출력해보았다.
<+18>에 explode_bomb이 있다.
이 부분을 밟지 않고 phase_1 함수를 종료해야 폭탄을 해체할 수 있으므로 <+23>부터가 안전지대이다.
<+16>에서 je명령어가 <+23>으로 분기할 수 있는 부분인데,
je는 ZF가 1일 때 jump하는 명령어이므로
여기에서 <+23>으로 넘어가려면 ZF가 1이어야 한다.
<+14>에 있는 test 명령어 수행 결과 ZF가 설정될 것이다.
test 명령어는 두 피연산자를 AND 연산한 결과가 0이면 ZF를 1으로 설정한다.
이 명령어에서는 test eax, eax를 하고 있으므로 ZF가 1로 설정되려면
eax 값이 0x00000000 이어야 한다.
(eax 값에서 한 비트라도 1이 들어있으면 AND 연산했을 때 0이 나올 수 없다)
이로써 phase1에서의 목표설정을 다음과 같이할 수 있다.
"<+14>지점에 왔을 때 eax의 값이 0이 되도록 하는 문자열 입력값 찾기"
그렇다면 <+14>지점에서의 eax의 값은 언제 결정될까?
바로 이전 명령어에서 strings_not_equal 함수를 호출하고 있다.
이 함수의 실행 결과로 eax 값이 설정되는 것이 아닐까 하는 추측을 할 수 있다.
정말 그런가 strings_not_equal 함수를 disassemble 해보자.
함수의 후반부인 <+99>에서 edx 값을 eax에 저장하는 것을 확인할 수 있다.
그 위의 코드들을 보면 일련의 연산 과정에서 edx에는 0x0, 0x1 등의 특정 상수가 저장되는 것을 볼 수 있다.
이제 어떤 값을 입력해야 eax에 0이 저장되는지를 확인하기 위해
strings_not_equal 함수를 더욱 면밀히 분석해보자.
동적 분석을 시작한다.
먼저 strings_not_equal 을 호출하는 지점에 breakpoint를 건다.
그리고 run을 입력해서 프로그램을 실행시킨다.
(run 대신 r을 입력해도 됨)
Welcome ~가 출력되고 사용자의 입력을 대기한다.
phase_1() 실행 전에 있는 read_line()함수가 실행 중인 것 같다.
아직 어떤 입력값을 넣어야하는지 모르니까 일단 임의의 문자열로 interlude를 입력해보았다.
[DISASM] 영역을 보면 bp를 걸어놓은 strings_not_equal 지점에서 rip가 멈춘 것을 확인할 수 있다.
[REGISTERS] 영역을 보면 나의 입력값인 interlude는 rax, rdi, r9에 저장된 것을 볼 수 있는데,
우리는 rdi에 주목할 것이다.
왜냐하면 rdi가 함수 호출 시 전달된 인자를 저장하는 역할을 하는 레지스터이기 때문이다.
따라서 내가 함수에 입력한 값을 추적할 때는 rdi를 살펴봐야겠다는 판단이 든다.
여기에서 유용한 상식을 하나 짚고 넘어가면 좋을 것 같다.
함수 호출시 전달되는 인자는 rdi, rsi에 저장되어 전달된다.
구체적으로 rdi에 첫번째 인자, rsi에 두번째 인자가 저장된다.
그럼 인자가 3개 이상이면 어떻게 될까? -> 이건 나중에 알아보자.
rdi와 rsi를 통해 argument가 전달되었다는데,
rdi에는 내가 입력한 값이 있는 것은 확인하였다.
그런데 rsi에도 어떤 문자열이 저장되어있다.
strings_not_equal 함수에 전달된 문자열이 내가 입력한 값 말고 또 있는 것이다!
그렇다면 rsi에 있는 문자열이 내가 입력한 값과 비교할 문자열이지 않을까..?
일단 이런 느낌을 검증하기 위해 프로그램을 다시 실행해서 rsi에 있던 문자열을 입력해본다.
위와 같이 문자열을 입력하였고
c (혹은 continue)를 입력해서 프로그램을 계속 실행시켰더니
Phase 1 defused. 가 떴다!!
나의 추측이 맞았음을 확인할 수 있다.
하지만 여기에서 끝내면 뭔가 공부를 덜 한 느낌이니
rdi에 저장된 나의 입력값과 rsi에 들어있던 저 문자열을 비교하는 코드를 정확히 확인하고 넘어가자.
rdi와 rsi를 추적해보자.
<+4>에서 rdi는 rbx로 mov되었다.
<+7>에서 rsi는 rbp로 mov되었다.
그리고 string_length함수를 두번 호출한 뒤
<+34>에서 r12d와 eax를 비교한다.
<+34>에서는 jne문이 있는데 <+34>에서 r12d와 eax가 다르면 <+99>로 jump한다.
<+99>에서는 edx 값을 eax에 저장하는데, <+26>에서 edx 값에 0x1이 저장된 상태이므로
이대로라면 eax에는 0x1이 저장된다.
앞서 살펴본 바에 따르면 eax가 0x0이어야만 폭탄을 해체할 수 있었다.
따라서 폭탄이 안 터지려면 <+34>에서 r12d와 eax값이 같았어야 한다는 결론이 난다.
<stings_not_equal+10>에 bp를 걸고
해당 부분부터 한 줄씩 실행시켜봤다. (한 줄씩 실행하는 명령어: ni)
<+15>에 왔을 때 eax의 값은 0x9이다. 이 값은 r12d에 저장된다.
함수 이름이 string_length였던 것에서 유추하자면,
0x9는 "interlude"의 문자열 길이인 것 같다.
그리고 <+31>에 왔을 때 eax의 값은 0x34이다.
0x32는 "Border relations with Canada have never been better."의 문자열 길이인 52와 일치한다.
따라서 strings_not_equal함수에서는 먼저 입력받은 문자열이 정답 문자열과 길이가 일치하는지를 확인한다는 것을 알 수 있다.
이제 다음 명령어를 살펴보자.
다음 명령어들은 요약하자면 rbx(rdi에 입력했던 값)의 문자열과 rbp(rsi에 있던 문자열)을 한 글자씩 비교하는 내용이다.
<+36>부터 <+56>까지는 rbx의 첫 문자와 rbp의 첫문자를 비교하는 부분이다.
여기에서 문자가 서로 다르면 결국 폭탄이 터지는 flow를 타게 된다.
<+58><+62>는 다음 문자로 포인터를 이동하는 부분이다.
<+66>부터 <+73>까지는 종결문자를 만났는지 확인하고 종결문자를 만났으면 문자열 비교를 마치는 부분이다.
이런 알고리즘을 통해 종결문자를 만날 때까지 모든 문자가 일치하면 eax에 0x0이 들어간 상태로 함수가 종료된다.
이제 오늘치 공부는 충분히 한 것 같으니 끝.
'Bomb Lab' 카테고리의 다른 글
[Bomb Lab] phase4 (0) | 2024.05.10 |
---|---|
[Bomb Lab] Phase3 (0) | 2024.03.24 |
[Bomb Lab] Phase2 (0) | 2024.03.24 |
[Bomb Lab]Bomb Lab이란? (1) | 2024.03.24 |