Blog

[CS:APP] BombLab Phase1 Walkthrough

Published 7 min read

I'm going to analyze three functions.

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

We can boldly guess that string_length is a function that returns the length of a string.

And since it uses %rdi right away, we can assume the string was passed in %rdi.

When it's passed, what gets stored is the address of the first element.

<+0 ~ 3>

It computes (%rdi) - 0x0, and if the result equals 0, it jumps to line 23.

In other words, (%rdi) is the first value in the current array, and it checks whether this value is '\0'.

If it's '\0', it jumps to <+23>, stores 0 in eax and returns; otherwise it moves on.

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

<+5 ~ 14>

It stores the value of %rdi in %rdx. So %rdx also holds a pointer to the first element.

Then it adds 1, so it points to the next element, and makes %eax hold part of %rdx's pointer value.

Then it subtracts %edi from %eax, so %eax holds the index difference between %rdx and %rdi.

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

<+16 ~ 19>

It checks whether the contents at the address %rdx points to, i.e. the second element, is null. (the same thing we did earlier)

If it's not '\0', it jumps to <+8>.

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

Conclusion

From here it adds 1 to %rdx,

and %eax ends up holding the computed gap between %rdx and %rdi,

and since this repeats until %rdx hits '\0', we can see that %eax ends up holding the length of the string passed in %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 looks like it takes two strings and compares whether they're different.

<+4 ~ 7>

It moves %rdi and %rsi, which are commonly used for passing arguments, into %rbx and %rbp.

We can tell this is to store them in callee-saved registers so that the values from %rdi and %rsi, which are caller-saved registers, don't get changed inside string_no_eqaul.

Now %rbx and %rbp each hold a pointer to the first element of a string.

<+10 ~ 31>

As we found from the analysis above, string_length is a function that takes %rdi as its argument.

So in the strings above, "abcdefg" is passed, 7 is returned in %eax, and that gets stored in %r12d.

Then at <+18> it passes the value of %rbp to %rdi. We can tell this is to pass the lower string as the argument to string_length.

When the function returns, 7 will be passed in %eax.

<+26>

It stores 1 in %edx. ( I'll explain what it's for below )

<+31 ~ 34>

It compares whether %r12d and %eax are equal.

In other words, it compares the length of the first string with the length of the second string.

  • If they're equal, it jumps to <+43>.

  • If they're different, the two strings have been judged different, so it doesn't jump, and at <+36 ~ 42> it stores %edx in %eax and returns it, which ends up returning 1.

-> So %edx is the register that plays the role of the return value telling whether the strings are different or the same.

<+43 ~ 48>

From here on, it proceeds after guaranteeing the two strings have the same length. (if they were different, it would have already returned)

(%rbx) is the value currently pointed to in the first string. In this case it's a. And it stores a in %eax.

And since the stored %eax is a 1-byte value, it checks whether %al is '\0' or not.

  • If it's '\0' : If it's '\0', that means both strings are empty, and since their lengths are equal, the two strings are the same. Then it moves to <+89>, sets %edx to 0, and goes to <+36> to return 0.

  • If it's not '\0' : We know neither string is empty. From here on, we have to compare each character one by one in order.

First, at <+50 ~ 53> it compares the first element.

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

%al holds the first value of the first string, and 0x0(%rbp) holds the first value of the second string.

At <53>, if the two strings are different, it moves to <+96>,

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

stores 1 (the two strings are different) in %edx, and jumps to <+36> to return 1.

<+55 ~ 73>

It adds 1 to each of %rbx and %rbp so they point to the next element.

Again, %eax is made to hold the value %rbx points to in the first string,

so the %al that shows up from now on is an element of the first string. (%al is part of %eax)

And it's in the state shown on the right.

Now at <+66> it checks whether %al is '\0' or not

If it's '\0' : The two strings have the same length, and the element pointed to in the first string is '\0', so the element pointed to in the second string is also '\0'. So the two strings are the same. That's why it goes to <+82> and returns 0.

If it's not '\0' : It has to check whether the two elements being pointed to are the same. If they're different, the two strings are different, so it goes to <+75> and returns 1.

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

If they're the same, it goes back to <+55> and repeats the <+55 ~ 73> process, returning in %rax whether the strings are the same.

<+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>

When the two strings are the same, the final moment looks like the state below.

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>

As we observed above, the strings_not_eqaul function takes %rdi and %rsi as parameters.

<+4 ~ 18>

Looking at <+4>, it stores 0x17e1(%rip) in %rsi.

At <+11> it calls strings_not_eqaul, and %eax gets the value that tells whether the two strings are the same or different.

At <+16 ~ 18>, if %eax is 1 it goes to <+25> and the bomb explodes, so we need to make the two strings the same so that phase_1 returns normally at <+24>.

In other words, of strings_not_eqaul's %rdi and %rsi, one is the value we entered and the other is the answer, so we just need to run up to right before <+11> and pull out the values.

Entering abcd and pulling out the values,

since the value we entered went into %rdi, we can infer that the answer is in %rsi.

So the answer is the following.

When I get angry, Mr. Bigglesworth gets upset.

Comments

Leaving a comment stores your nickname (or GitHub profile info). Privacy policy

GitHub