CSAPP 二进制炸弹 binary bomb lab6 第六关 ——深入理解计算机系统

bomb lab 第六关详细分析

由于第六关的汇编代码太长且复杂,需要非常耐心地进行分析,故将整个汇编代码分为几个部分详细说明。

一、Part1
00000000004010f4 <phase_6>:
// arg1=input(input是从外部传入的字符串)
  4010f4:	41 56              push   %r14
  4010f6:	41 55              push   %r13
  4010f8:	41 54              push   %r12
  4010fa:	55                 push   %rbp
  4010fb:	53                 push   %rbx
  4010fc:	48 83 ec 50        sub    $0x50,%rsp	
  401100:	49 89 e5           mov    %rsp,%r13
  401103:	48 89 e6           mov    %rsp,%rsi
  401106:	e8 51 03 00 00     callq  40145c <read_six_numbers>
  
// read_six_numbers
  int read_six_numbers(input,a)	
  {//input是phase_6的参数1,a是在栈中分配的一个int*数组
  	return (sscanf(input,"%d %d %d %d %d %d",&a[0],&a[1],&a[2],&a[3],&a[4],&a[5]));
  }
//
  40110b:	49 89 e6           mov    %rsp,%r14	
  40110e:	41 bc 00 00 00 00  mov    $0x0,%r12d	
  
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环开始<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 令 r12d<=>i , rbx<=>j (后面注释的i和j代表这两个寄存器的值)
  401114:	4c 89 ed           mov    %r13,%rbp	
  401117:	41 8b 45 00        mov    0x0(%r13),%eax	# eax=a[i]
  40111b:	83 e8 01           sub    $0x1,%eax	# eax -= 1
  40111e:	83 f8 05           cmp    $0x5,%eax
  401121:	76 05              jbe    401128 <phase_6+0x34>
// if (eax>=0 && eax<=5) -> jmp     else -> bomb (所以eax必须在区间[0,5])
  401123:	e8 12 03 00 00     callq  40143a <explode_bomb>
  401128:	41 83 c4 01        add    $0x1,%r12d	# i++
// r12d一开始为0,这边加1,感觉会是个用于循环的计数器
  40112c:	41 83 fc 06        cmp    $0x6,%r12d
  401130:	74 21              je     401153 <phase_6+0x5f>	# 等于6时跳出循环
  401132:	44 89 e3           mov    %r12d,%ebx	# 否则继续,j初始化=i+1

>>>>>>>>>>>>>>>>>>>>>>>>>>> 内循环开始<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  401135:	48 63 c3           movslq %ebx,%rax
  401138:	8b 04 84           mov    (%rsp,%rax,4),%eax	# 取下一个数到%eax
  40113b:	39 45 00           cmp    %eax,0x0(%rbp) #该数不能与a[i]相同,否则爆炸
  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	# ebx+=1
  401148:	83 fb 05           cmp    $0x5,%ebx
  40114b:	7e e8              jle    401135 <phase_6+0x41>
// ebx开始时为i,if (ebx<=5) -> 跳出循环
>>>>>>>>>>>>>>>>>>>>>>>>>>> 内循环结束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

  40114d:	49 83 c5 04          	add    $0x4,%r13	 
  401151:	eb c1                	jmp    401114 <phase_6+0x20>
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环结束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// 这后面还一大段代码,暂作省略,在后面部分再进行分析

上面是一个多重循环,详细注释已在上写出,分析清楚逻辑不难写成C代码:

for (int i=0; i<6; i++){	
	if (a[i]-1>5 || a[i]-1<0)	bomb();
	for (int j=i+1;j<=5;j++){
		if (a[j] == a[i])	bomb(); 
	}
}

这段代码逻辑很清晰,就是保证6个数都处于区间[1,6]中且不能重复,下面进入代码分析的第二部分。

二、Part2

  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi # 边界&a[6]	
  401158:	4c 89 f0             	mov    %r14,%rax # rax=&a[0]
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx	
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环开始<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  401160:	89 ca                	mov    %ecx,%edx
  401162:	2b 10                	sub    (%rax),%edx # edx =7-a[0]
  401164:	89 10                	mov    %edx,(%rax) # a[0]=7-a[0]
  401166:	48 83 c0 04          	add    $0x4,%rax # rax指向下一个数
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 <phase_6+0x6c>
  上面这个循环很清楚是将a[i]线性变换为7-a[i].
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环结束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

这部分代码很简单,就是将a[i]变换为7-a[i],写成代码就是

for (int i=0;i<6;i++)
	a[i] = 7-a[i];

继续进入第三部分。

三、Part3
  40116f:	be 00 00 00 00     mov    $0x0,%esi
// esi = 0 感觉又是一个计数器 令:esi<=>i
  401174:	eb 21              jmp    401197 <phase_6+0xa3>
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环开始<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
#############小循环1开始:
  401176:	48 8b 52 08        mov    0x8(%rdx),%rdx
// rdx=mem[rdx+0x8],这种写法很奇怪,猜想rdx=是一个结构体,且0x8偏移为该结构体指针
  40117a:	83 c0 01           add    $0x1,%eax # i++
  40117d:	39 c8              cmp    %ecx,%eax  
// ecx=7,eax一开始=&a[6]
  40117f:	75 f5              jne    401176 <phase_6+0x82>
#############小循环1结束。

  401181:	eb 05              jmp    401188 <phase_6+0x94>
#############小循环2开始:
  401183:	ba d0 32 60 00     mov    $0x6032d0,%edx
  401188:	48 89 54 74 20     mov    %rdx,0x20(%rsp,%rsi,2)
  // mem(a+0x20+2i)=0x4032d0
  40118d:	48 83 c6 04        add    $0x4,%rsi # 另外一个循环
  401191:	48 83 fe 18        cmp    $0x18,%rsi # 保证每个a[i]都运行过
  401195:	74 14              je     4011ab <phase_6+0xb7> # 退出循环
  401197:	8b 0c 34           mov    (%rsp,%rsi,1),%ecx # 指针偏移,依次获取6个数
  40119a:	83 f9 01           cmp    $0x1,%ecx 
  40119d:	7e e4              jle    401183 <phase_6+0x8f> #<=1则跳回开头
#############小循环2结束。

  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>
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环结束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

这部分内容有点复杂且不知所云,那么先用gdb看看里面的 0x6032d0是个啥东西。
在这里插入图片描述这个名字叫node1,再结合上面的mov 0x8(%rdx),%rdx ,猜测这是一个结构体且8字节的位置是这个结构体的指针,我们尝试打印更多信息。
在这里插入图片描述从上面的信息可以看出,该0~3字节是一个不知道是什么的数据,4~7字节是node的编号,8~15字节是应该是一个指针,0x6032d0结构体中的指针为0x006032e0,这个指针指向node2,同样node2中的指针指向node3 …最后一个node6中的指针指向地址0。该结构体是一个链表啊!

可以大致确定这个结构体为:

struct node{
int val;//这个暂不知道是什么数据 
int id;//id是编号 
node *next;	//next是指向的node的地址
}

再看这段的汇编代码,将第三部分忠实地逆向得:

while(1){
	cx=*(sp+si*1);  // cx=sp+0,sp+0x4,sp+0x8,sp+0xc,....
	if(cx<=1) dx=0x6032d0; //dx=表头地址
	else {
		ax=1;
		dx=0x6032d0;
		//遍历链表,使得dx=第sp[si/4]项的地址
		do{	
			dx=*(dx+0x8);	//dx=(dx.next)->val
			ax+=1;
		}while(ax!=cx);
	}	
	*(sp+si*2+0x20)=dx;//从sp+0x20起,每8字节记录一个链表项地址
	si+=4;
	if(si==0x18) break; //sp+0x18,即&sp[6]
}

简化为C代码为:

node *n[6];	//n = sp+0x20
for (int i=0;i<6;i++)
{	
	addr = 0x6032d0;
	for (int j=0;j<val[i];j++)
		addr = *(addr+0x8);
	n[i]=addr;
}

C代码就很清楚了,一开始的节点规定为0x6032d0,即node1的地址,然后是不断地找当前节点的next节点(next的次数为数组的值),找到后放在新分配的sp+0x20内存中。也就是相当于根据数组值,给6个node节点重新排列,并放于其实地址为sp+0x20指针数组n[6]中。
第三部分真是费劲,但是基本搞清这个node是什么了,接下来进入Part4部分。

四、Part4
  4011ab:	48 8b 5c 24 20     mov    0x20(%rsp),%rbx # rbx=n[0]
  4011b0:	48 8d 44 24 28     lea    0x28(%rsp),%rax # rax=&n[1]
  4011b5:	48 8d 74 24 50     lea    0x50(%rsp),%rsi # 边界 rsi=&n[6]
  4011ba:	48 89 d9           mov    %rbx,%rcx	# rcx=rbx
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环开始<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// rax是数组n的下一个值的地址,rcx是数组n前一个值,记:rax=&n[i+1] rcx=n[i] rdx=n[i+1]
  4011bd:	48 8b 10           mov    (%rax),%rdx	# rdx=n[i+1]
  4011c0:	48 89 51 08        mov    %rdx,0x8(%rcx)	#n[i]->next=n[i+1]
  4011c4:	48 83 c0 08        add    $0x8,%rax 	# rax+=8
  4011c8:	48 39 f0           cmp    %rsi,%rax
  4011cb:	74 05              je     4011d2 <phase_6+0xde>
// if (rax==&n[6]),即 i->退出循环
  4011cd:	48 89 d1           mov    %rdx,%rcx
// else -> rcx=rdx , 回到循环开始循环
  4011d0:	eb eb              jmp    4011bd <phase_6+0xc9>
>>>>>>>>>>>>>>>>>>>>>>>>>>> 循环结束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
  4011d9:	00 

一开始i=0 rcx=n[0]rax=&n[1]rdx=n[1],使得rcx->next=rdx,即n[0]->next=n[1]
然后循环一次后变成 -> rcx=n[1]rax=&n[2]rdx=n[2],使得n[1]->next=n[2]
最后一次循环时,rcx=n[4]rax=&n[5]rdx=n[5],使得n[4]->next=n[5],再将当前的n[5]的next指向地址0,结束Part4的部分。

将其上述过程逆向得:

bx=n[0]
ax=&n[1]
si=&n[6]
cx=bx
while(1)
{	
   dx = *ax
   cx->next = dx
   ax+=0x8
   if(ax==si) {
// n[0]~n[4] 都已经分配了next,再给n[5]分配0地址为next
   		dx->next=0;
   		break;
   }	
}

这一部分就是把按照数组n的顺序去修改链表的next,使得该链表按数组的顺序串成顺链。
下面进入最后一部分的分析,答案近在咫尺!

五、Part5
  4011da: mov    $0x5,%ebp
  4011df: mov    0x8(%rbx),%rax # rax=rbx的下一个节点的指针
  4011e3: mov    (%rax),%eax # 结构体中val的值 保存在rax中
  4011e5: cmp    %eax,(%rbx) # 比较两个node的val值
  4011e7: jge    4011ee <phase_6+0xfa> # 如果靠前结点的val < 靠后结点的val
  4011e9: callq  40143a <explode_bomb> # 爆炸
  4011ee: mov    0x8(%rbx),%rbx # 移动指针
  4011f2: sub    $0x1,%ebp  
  4011f5: jne    4011df <phase_6+0xeb> # 循环
  4011f7: add    $0x50,%rsp
  4011fb: pop    %rbx
  4011fc: pop    %rbp
  4011fd: pop    %r12
  4011ff: pop    %r13
  401201: pop    %r14
  401203: retq

最后一段逻辑很清晰,即要求排序按val递减,否则爆炸。
上面分析了这么多,是时候全部整合起来并出结果了!
进入最后一部分,结果的呈现。

六、Part6在这里插入图片描述
val:
node1=14c	node2=0a8	node3=39c
node4=2b3	node5=1dd   node6=1bb

根据val从大到小排序分别为:3 4 5 6 1 2,但由于之前进行了一次a[i]=7-[i]的线性变换,所以现在的3 4 5 6 1 2即为之前的4 3 2 1 6 5
结果:4 3 2 1 6 5

至此,lab1~lab6已全部完成,运行一下来看看结果吧
在这里插入图片描述这个实验花了好久,尤其是最后一个Phase,太难搞了不过也终于把bomb lab给完成了,看到弹出的 Congratulations!还是挺激动的。
下面附上六关全部的结果吧

Border relations with Canada have never been better.
1 2 4 8 16 32
0 207
7 0
YONUVW
4 3 2 1 6 5


版权声明:本文为Eternitykc原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。