세 가지 함수를 분석하려고 합니다.

phase_1

strings_no_equal

string_length

 


string_length

Dump of assembler code for function string_length:
   0x000000000000176f <+0>:	cmpb   $0x0,(%rdi)
   0x0000000000001772 <+3>:	je     0x1786 <string_length+23>
   0x0000000000001774 <+5>:	mov    %rdi,%rdx
   0x0000000000001777 <+8>:	add    $0x1,%rdx
   0x000000000000177b <+12>:	mov    %edx,%eax
   0x000000000000177d <+14>:	sub    %edi,%eax
   0x000000000000177f <+16>:	cmpb   $0x0,(%rdx)
   0x0000000000001782 <+19>:	jne    0x1777 <string_length+8>
   0x0000000000001784 <+21>:	repz retq
   0x0000000000001786 <+23>:	mov    $0x0,%eax
   0x000000000000178b <+28>:	retq

 

string_length는 문자열 길이를 반환하는 함수라고 과감히 예상할 수 있습니다.

그리고 바로 %rdi를 사용하는것으로 보아, %rdi에 문자열이 전달되었다고 생각할 수 있습니다.

전달될 때는 첫 element의 주소가 저장됩니다.

 

 

 


<+0 ~ 3> 

 

(%rdi) - 0x0을 계산하고 결과값이 0과 같으면 23번 줄로 점프합니다.

다시말하면, (%rdi)는 현재 배열에서 첫번째 value고 이 value가 '\0'임을 체크합니다.

만약 '\0'이면 <+23>으로 점프해서 eax에 0을 저장하고 리턴하고, 아니라면 다음으로 진행합니다.

   0x000000000000176f <+0>:	cmpb   $0x0,(%rdi)
   0x0000000000001772 <+3>:	je     0x1786 <string_length+23>
   0x0000000000001786 <+23>:	mov    $0x0,%eax
   0x000000000000178b <+28>:	retq

<+5 ~ 14>

 

%rdx에 %rdi값을 저장했습니다. 즉 %rdx도 첫번째 원소를 가리키는 포인터값을 지니게 됩니다.

그 후 1을 더하므로 다음 원소를 가리키게 되고, %eax가 %rdx의 포인터값의 일부를 가지도록 합니다.

그 후 %eax에서 %edi를 빼므로 %rdx와 %rdi의 인덱스 차이를 가지게 됩니다.

 

   0x0000000000001774 <+5>:	mov    %rdi,%rdx
   0x0000000000001777 <+8>:	add    $0x1,%rdx
   0x000000000000177b <+12>:	mov    %edx,%eax
   0x000000000000177d <+14>:	sub    %edi,%eax

<+16 ~ 19>

 

%rdx가 가리키는 주소값의 내용, 즉 두번째 원소가 null인지 비교합니다. (아까 했던 과정임)

'\0'이 아니라면 <+8>로 점프합니다.

   0x000000000000177f <+16>:	cmpb   $0x0,(%rdx)
   0x0000000000001782 <+19>:	jne    0x1777 <string_length+8>

 

 

결론

여기서부터 %rdx를 1더하고

%eax는 %rdx에서 %rdi의 간격차를 계산한 값을 가지게 되는데

이를 %rdx가 '\0'이 될때까지 반복하므로 %eax가 결국에는 %rdi에 전달받은 문자열이 길이를 가지게 됨을 알 수 있습니다.

 

 

 

 


string_no_equal

Dump of assembler code for function strings_not_equal:
   0x000000000000178c <+0>:	push   %r12
   0x000000000000178e <+2>:	push   %rbp
   0x000000000000178f <+3>:	push   %rbx
   0x0000000000001790 <+4>:	mov    %rdi,%rbx
   0x0000000000001793 <+7>:	mov    %rsi,%rbp
   0x0000000000001796 <+10>:	callq  0x176f <string_length>
   0x000000000000179b <+15>:	mov    %eax,%r12d
   0x000000000000179e <+18>:	mov    %rbp,%rdi
   0x00000000000017a1 <+21>:	callq  0x176f <string_length>
   0x00000000000017a6 <+26>:	mov    $0x1,%edx
   0x00000000000017ab <+31>:	cmp    %eax,%r12d
   0x00000000000017ae <+34>:	je     0x17b7 <strings_not_equal+43>
   0x00000000000017b0 <+36>:	mov    %edx,%eax
   0x00000000000017b2 <+38>:	pop    %rbx
   0x00000000000017b3 <+39>:	pop    %rbp
   0x00000000000017b4 <+40>:	pop    %r12
   0x00000000000017b6 <+42>:	retq
   0x00000000000017b7 <+43>:	movzbl (%rbx),%eax
   0x00000000000017ba <+46>:	test   %al,%al
   0x00000000000017bc <+48>:	je     0x17e5 <strings_not_equal+89>
   0x00000000000017be <+50>:	cmp    0x0(%rbp),%al
   0x00000000000017c1 <+53>:	jne    0x17ec <strings_not_equal+96>
   0x00000000000017c3 <+55>:	add    $0x1,%rbx
   0x00000000000017c7 <+59>:	add    $0x1,%rbp
   0x00000000000017cb <+63>:	movzbl (%rbx),%eax
   0x00000000000017ce <+66>:	test   %al,%al
   0x00000000000017d0 <+68>:	je     0x17de <strings_not_equal+82>
   0x00000000000017d2 <+70>:	cmp    %al,0x0(%rbp)
   0x00000000000017d5 <+73>:	je     0x17c3 <strings_not_equal+55>
   0x00000000000017d7 <+75>:	mov    $0x1,%edx
   0x00000000000017dc <+80>:	jmp    0x17b0 <strings_not_equal+36>
   0x00000000000017de <+82>:	mov    $0x0,%edx
   0x00000000000017e3 <+87>:	jmp    0x17b0 <strings_not_equal+36>
   0x00000000000017e5 <+89>:	mov    $0x0,%edx
   0x00000000000017ea <+94>:	jmp    0x17b0 <strings_not_equal+36>
   0x00000000000017ec <+96>:	mov    $0x1,%edx
   0x00000000000017f1 <+101>:	jmp    0x17b0 <strings_not_equal+36>

 

string_no_eqaul, 문자열 두 개 받아서 서로 다른지 비교하는 기능을 가졌을 것으로 보입니다.


 

 

<+4 ~ 7>

 

인자 전달로 자주쓰이는 %rdi, %rsi를 %rbx, %rbp로 전달했습니다.

이는 callee-saved register인 레지스터에 저장해서 caller-saved register인 %rdi, %rsi가 string_no_eqaul 함수에서 변하지 않도록 하기위함을 알 수 있습니다.

이제 %rbx, %rbp가 각각 문자열의 첫 원소를 가리키는 포인터를 가집니다.

 

 

 

 

 


<+10 ~ 31>

 

위의 분석해서 알 수 있듯이 string_length 함수는 %rdi를 인자로 전달받는 함수입니다.

즉, 위 문자열에서 "abcdefg" 문자열이 전달되고 %eax에 7이 반환되고 이를 %r12d에 저장합니다.

 

그리고 <+18> 에서 %rbp의 값을 %rdi에 전달합니다. 이는 아래 문자열을 string_length의 인자로 전달하기 위함임을 알 수 있습니다.

함수리 리턴되면 %eax에 7이 전달될겁니다.


<+26>

 

%edx 에 1을 저장하고 있습니다.( 어떤 역할인지는 밑에서 설명합니다 )

 


 

<+31 ~ 34>

 

%r12d과 %eax 둘이 같은지 비교합니다.

다시말하면, 첫번째 문자열의 길이와 두번째 문자열의 길이를 비교합니다.

- 만약 같다면 <+43>으로 점프합니다.

- 만약 다르면, 두 문자열이 서로 다름이 판정되었으므로 점프하지 않고 <+36 ~ 42>에서 %edx를 %eax에 저장해 반환함으로써 결과적으로 1을 반환합니다.

-> 즉 %edx는 문자열이 다른지 같은지를 알려주는 반환값의 역할을 하는 레지스터입니다.

 

 

 


<+43 ~ 48>

 

여기부터는 두 문자열의 길이가 같음을 보장한 후 진행하게됩니다. (다른경우 이미 리턴됐으므로)

 

(%rbx)는 첫번째 문자열에서 가리키고 있는 값입니다. 이 경우에는 a입니다. 그리고 a를 %eax에 저장합니다.

그리고 저장한 %eax는 1바이트 값이므로 %al이 '\0'인지 아닌지 체크합니다.

 

- '\0' 인경우 : 만약 '\0'이라면 두 문자열이 모두 비었음을 의미하고, 길이가 같으므로 두 문자열이 같습니다. 후에 <+89>로 이동해서 %edx를 0으로 만들어 <+36>으로 이동해 0을 반환하게 됩니다.

 

- '\0' 이 아닌경우 : 두 문자열이 모두 빈 문자열이 아님을 알 수 있습니다. 이제부터는 각 문자를 순서대로 하나씩 비교해봐야합니다.

우선 <+50 ~ 53>에서 첫번째 원소를 비교합니다.

   0x00000000000017be <+50>:	cmp    0x0(%rbp),%al
   0x00000000000017c1 <+53>:	jne    0x17ec <strings_not_equal+96>

%al은 첫번째 문자열의 첫번째값을 지니고, 0x0(%rbp)는 두번째 문자열의 첫번째 값을 가집니다.

<53>에서 두 문자열이 다르면 <+96> 으로 이동해서

   0x00000000000017ec <+96>:	mov    $0x1,%edx
   0x00000000000017f1 <+101>:	jmp    0x17b0 <strings_not_equal+36>

%edx에 1(두 문자열이 다름)을 저장하고 <+36>으로 점프해서 1을 반환합니다.


<+55 ~ 73>

 

%rbx, %rbp에 각각 1을 더해서 다음 원소를 가리키도록합니다.

다시 %eax에는 첫번째 문자열에서 %rbx가 가리키는 값을 가지도록하므로,

앞으로 나오는 %al은 첫번째 문자열의 원소입니다. (%al은 %eax의 일부입니다)

그리고 오른쪽의 상태를 가집니다.

 

이제 <+66>에서 %al이 '\0'인지 아닌지 체크합니다

 

'\0'인경우 : 두 문자열이 길이가 같지만, 가리키는 첫번째 문자열의 원소가 '\0'이므로 가리키는 두번째 문자열의 원소 또한 '\0'입니다. 즉 두 문자열이 같습니다. 때문에 <+82>로 넘어가서 0을 리턴합니다.

'\0'이 아닌경우 : 가리키는 두 원소가 같은지 체크해야합니다. 만약 다르다면 두 문자열이 다르므로 <+75>로 가서 1을 리턴합니다.

   0x00000000000017d7 <+75>:	mov    $0x1,%edx
   0x00000000000017dc <+80>:	jmp    0x17b0 <strings_not_equal+36>

만약 같다면 <+55>로 돌아가서 <+55 ~ 73> 과정을 반복해서 문자열의 같은지 여부를 %rax에 리턴하게 됩니다.

<+55>:	add    $0x1,%rbx
<+59>:	add    $0x1,%rbp
<+63>:	movzbl (%rbx),%eax
<+66>:	test   %al,%al
<+68>:	je     0x17de <strings_not_equal+82>
<+70>:	cmp    %al,0x0(%rbp)
<+73>:	je     0x17c3 <strings_not_equal+55>

두 문자열이 같은경우에 마지막 순간은 아래와 같은 상태가 됩니다.


phase_1

Dump of assembler code for function phase_1:
=> 0x00005555554012e4 <+0>:	sub    $0x8,%rsp
   0x00005555554012e8 <+4>:	lea    0x17e1(%rip),%rsi        # 0x555555402ad0
   0x00005555554012ef <+11>:	callq  0x55555540178c <strings_not_equal>
   0x00005555554012f4 <+16>:	test   %eax,%eax
   0x00005555554012f6 <+18>:	jne    0x5555554012fd <phase_1+25>
   0x00005555554012f8 <+20>:	add    $0x8,%rsp
   0x00005555554012fc <+24>:	retq
   0x00005555554012fd <+25>:	callq  0x555555401a90 <explode_bomb>
   0x0000555555401302 <+30>:	jmp    0x5555554012f8 <phase_1+20>

위에서 관찰했듯이 strings_not_eqaul 함수는 %rdi, %rsi를 매개변수로 가집니다.


<+4 ~ 18>

 

<+4>를 보면 0x17e1(%rip)를 %rsi에 저장하고 있습니다.

<+11>에서 strings_not_eqaul을 호출해서 %eax에 두 문자열이 같은지 다른지를 판별한 값이 들어갑니다.

<+16 ~ 18>에서 %eax가 1이면 <+25>로 가서 폭탄이 터지므로 두 문자열이 같도록해서 <+24>에서 정상적으로 phase_1이 리턴되도록 해야합니다.

즉, strings_not_eqaul의 %rdi, %rsi 중 하나는 입력한 값이고, 하나는 답이므로 <+11>전까지 진행해서 값을 꺼내보면 됩니다.

abcd를 입력해서 값을 꺼내보면,

 

%rdi에 입력한 값이 들어갔으므로, %rsi에 답이 있다고 추론할 수 있습니다.

즉, 답은 다음과 같습니다.

When I get angry, Mr. Bigglesworth gets upset.

 

'CS:APP' 카테고리의 다른 글

[CS:APP] BombLab Phase6 해설  (0) 2023.10.20
[CS:APP] BombLab Phase5 해설  (0) 2023.10.20
[CS:APP] BombLab Phase4 해설  (0) 2023.10.20
[CS:APP] BombLab Phase3 해설  (0) 2023.10.20
[CS:APP] BombLab Phase2 해설  (0) 2023.10.19

+ Recent posts