「CMU 15-213」Lab(2) Bomb

Phase 1

Disassembled code of 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       	callq  401338 <strings_not_equal>
  400eee:	85 c0                	test   %eax,%eax
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>
  400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>
  400ef7:	48 83 c4 08          	add    $0x8,%rsp
  400efb:	c3                   	retq   

If the input equals the content at 0x402400 (which is a string), strings_not_equal will return 0 (set %eax to 0). Then after test, ZF will be set to 1 and code will jump to 400ef7.

Phase 2

  000000000040145c <read_six_numbers>:
  40145c:	48 83 ec 18          	sub    $0x18,%rsp
  401460:	48 89 f2             	mov    %rsi,%rdx
  401463:	48 8d 4e 04          	lea    0x4(%rsi),%rcx
  401467:	48 8d 46 14          	lea    0x14(%rsi),%rax
  40146b:	48 89 44 24 08       	mov    %rax,0x8(%rsp)
  401470:	48 8d 46 10          	lea    0x10(%rsi),%rax
  401474:	48 89 04 24          	mov    %rax,(%rsp)
  401478:	4c 8d 4e 0c          	lea    0xc(%rsi),%r9
  40147c:	4c 8d 46 08          	lea    0x8(%rsi),%r8
  401480:	be c3 25 40 00       	mov    $0x4025c3,%esi
  401485:	b8 00 00 00 00       	mov    $0x0,%eax
  40148a:	e8 61 f7 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  40148f:	83 f8 05             	cmp    $0x5,%eax
  401492:	7f 05                	jg     401499 <read_six_numbers+0x3d>
  401494:	e8 a1 ff ff ff       	callq  40143a <explode_bomb>
  401499:	48 83 c4 18          	add    $0x18,%rsp
  40149d:	c3                   	retq   

Notice that<__isoc99_sscanf@plt> seems to handle our input. The format of function sscanf in C is like:

sscanf( "1 2 3 4 5 6", "%d %d %d %d %d %d", &num1, &num2, ..., num6);

rdi saves the input string, rsi stores the format string"%d %d %d %d %d %d", and rdx, rcx, r8... saves the memory addresses of the six arguments:

rdx -> rsp
rcx -> rsp + 4
r8 -> rsp + 8
r9 -> rsp + 12
rsp - 24 -> rsp + 16
rsp - 16 -> rsp + 20

From function phase_2 we can know the first number should be 1, and the rest numbers are all twice of their previous number.

Phase 3

Phase 3 takes 2 integers. The core part of phase 3 is this code:

  400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8)

jmp *means jump to the content at the address 0x402470(,%rax,8). Becasue (%rax) equals to 0x8(%rsp)(first input number) and is less than or equal to 7, here is the possible values and corresponding addresses.

Phase 4

Notice the begainning part is similar to phase 3, it takes 2 integers and the first one should be less or equal to 0xe.

  401029:	83 f8 02             	cmp    $0x2,%eax
  40102c:	75 07                	jne    401035 <phase_4+0x29>
  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp)
  401033:	76 05                	jbe    40103a <phase_4+0x2e>

Inside func4: Always try to avoid explosion and getting into another function (which makes analysis harder). So I notice if the first number is 7, it won’t go into func4 inside func4 and instead get directly out while not triggering explosion.

  400fe2:	39 f9                	cmp    %edi,%ecx
  400fe4:	7e 0c                	jle    400ff2 <func4+0x24>
  
  400ff7:	39 f9                	cmp    %edi,%ecx
  400ff9:	7d 0c                	jge    401007 <func4+0x39>

From the end of function phase_4, we can see if the second number is not 0, it will explode.

  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)
  401056:	74 05                	je     40105d <phase_4+0x51>
  401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>
  40105d:	48 83 c4 18          	add    $0x18,%rsp

So the solution for phase 4 is 7 0.

Phase 5

Phase 5 takes a string with length of 6. The core part of phase 4 is this code (note that cl is the low 8 bits register of rcx, same as dl to rdx):

  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx
  40108f:	88 0c 24             	mov    %cl,(%rsp)
  401092:	48 8b 14 24          	mov    (%rsp),%rdx
  401096:	83 e2 0f             	and    $0xf,%edx
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)
  4010a4:	48 83 c0 01          	add    $0x1,%rax
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>

This is a loop that runs 6 times to get chars from the string we send in. ecxsaves the char (ascii code), and then and $0xf,%edxcalculates lowest 4 bits of the ascii code. movzbl 0x4024b0(%rdx),%edx: At adress 0x4024b0, there is a string that stores alphabet: In this code, rdxrepresents the index inside the string "maduiersnfotvbyl"and it returns the (rdx)th char. mov %dl,0x10(%rsp,%rax,1)put the char into the address (from rsp+16 to rsp+21).

  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>

From there we know the required string is "flyers" and corresponding indexes in the string "maduiersnfotvbyl"are 9 15 14 5 7. So the solution can be9on567. The answer is not unique. As long as the lowest 4 bits are9 15 14 5 7, it is right (For example, IONEFGis also a right answer).

Phase 6

The last phase, like the professor said, is pretty hard. I almost spent a whole day on this.

  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 <phase_6+0x51>
  401140:	e8 f5 02 00 00       	callq  40143a <explode_bomb>
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 <phase_6+0x41>

This loop will check if every number is sole (different from the rest).

  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 <phase_6+0xa3>
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 <phase_6+0x82>
  401181:	eb 05                	jmp    401188 <phase_6+0x94>
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab <phase_6+0xb7>
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 <phase_6+0x8f>
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>

This part transfer the six numbers to a array of integers (start at 0x6032d0 and save to 6 registers from rsp+32 to rsp+72). The loop in 401176 will do the job of transfering. And mov %rdx,0x20(%rsp,%rsi,2)give the value to the rsp. Pay attention to this code : mov 0x8(%rdx),%rdxand the values in the adresses : These values are also addresses. Here is the correspondence of numbers and addresses:

1 -> 0x6032d0
2 -> 0x6032e0
3 -> 0x6032f0
4 -> 0x603300
5 -> 0x603310
6 -> 0x603320

For example, if the input string is"5 6 4 3 2 1", after the operation of 7 - i, it becomes "2 1 3 4 5 6". So the addresses in rsp+32 to rsp+72 are:

  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 <phase_6+0xde>
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>

This loop resets the values at adresses 0x6032d8, 2e8, 2f8, 308....

addressOf(%rsp+32)+8 = %rsp+40
addressOf(%rsp+40)+8 = %rsp+48
addressOf(%rsp+48)+8 = %rsp+56
...

Actually it gives the address saved in next register to the address in this register plus 8. You will see why the code does this in next part.

  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
  4011d9:	00 
  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax
  4011e3:	8b 00                	mov    (%rax),%eax
  4011e5:	39 03                	cmp    %eax,(%rbx)
  4011e7:	7d 05                	jge    4011ee <phase_6+0xfa>
  4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb>
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
  4011f2:	83 ed 01             	sub    $0x1,%ebp
  4011f5:	75 e8                	jne    4011df <phase_6+0xeb>

The last part is to check whether (rsp+n) >= ((addressOf(rsp+n)+8)). There n=32, 40, 48... and if anyone fails the bomb explodes. Actually (addressOf(rsp+n)+8) is rsp+n+8(we set this in previous part). So the condition can be rewriten as (rsp+n) >= (rsp+n+8)and our task is to make sure the integer at this address is smaller than the previous one. The following image shows the integers corresponding to the addresses: So the right answer is :

# after 7 - i operation
3 4 5 6 1 2
# right answer
4 3 2 1 6 5