首页 > 其他分享 >CSAPP Lab02——Bomb Lab完成思路详解

CSAPP Lab02——Bomb Lab完成思路详解

时间:2024-06-05 13:01:16浏览次数:20  
标签:CSAPP Bomb 48 00 Lab mov eax rsp rax

看见的看不见的

瞬间的永恒的

青草长啊大雪飘扬

——月亮之上

完整代码见:CSAPP/bomb at main · SnowLegend-star/CSAPP (github.com)

01 字符串比较

简单的把输入的字符串和地址“0x402400”内早已存储的字符串相比较。如果两个字符串相等则函数返回,否则炸弹爆炸。

这里有一个困扰我很久的疑惑——我之前一直以为每个寄存器自身就是代表一个地址,同时寄存器自己内部存储着一个数据。就比如%eax=0xFFFF ABCD,那(%eax)也是对寄存器内部操作从而把存储在它里面的数据给提取出来。这种想法实在是大错特错!所谓寄存器,访问它直接用这个寄存器的名字即可,寄存器的名字并不是一个无用的标签,而是相当于可以直接访问寄存器的一个引用。至于寄存器它自己存储的那个数字,可以是地址也可以是一个数据。如mov (%eax),%esi就是访问存储在0xFFFF ABCD这个地址里面的数据传递给%esi,是要访问内存的。而mov %eax,%esi就是直接令%esi=0xFFFF ABCD。

值得一提的是,一般%eax里面存储的是可以直接拿来使用的数据而非地址,%rsp作为栈帧寄存器,一般存储的数字表示一个地址。

0000000000400ee0 <phase_1>:
  400ee0:	48 83 ec 08          	sub    $0x8,%rsp                      ;rsp-=8 准备腾出空间
  400ee4:	be 00 24 40 00       	mov    $0x402400,%esi                 ;把这个地址存入esi,我们输入的内容作为参数1
  400ee9:	e8 4a 04 00 00       	callq  401338 <strings_not_equal>     ;如果字符串不相等 *(esi)="Border relations with Canada have never been better."
  400eee:	85 c0                	test   %eax,%eax                     
  400ef0:	74 05                	je     400ef7 <phase_1+0x17>          ;if(eax==0)
  400ef2:	e8 43 05 00 00       	callq  40143a <explode_bomb>          ;bomb!
  400ef7:	48 83 c4 08          	add    $0x8,%rsp                      ;else return ;
  400efb:	c3                   	retq   

02循环

lea和mov两条指令的区别。给出如下两个例子

lea  (%eax,%ebx,1),%esi

mov (%ebx,%ebp,1),%eax

在lea指令中,“()”并不是进行地址引用的意思,而是单纯拿来对括号里的三元组进行运算,即经过lea命令后,%esi=%eax+%ebx。但是在mov命令中,“()”表示的是取地址的意思,即经过mov命令后,% eax=(%ebx+%ebp)。

这题主要就是要理解输入的6个数组元素整齐存在于刚才开辟的栈空间中,用rbx来遍历这个栈空间。

0000000000400efc <phase_2>:
  400efc:	55                   	push   %rbp                           ;
  400efd:	53                   	push   %rbx                           ;
  400efe:	48 83 ec 28          	sub    $0x28,%rsp                     ;rsp-0x28=rsp,准备腾出空间
  400f02:	48 89 e6             	mov    %rsp,%rsi                      ;rsi=rsp      现在把栈的首地址传递给rsi,一会儿<read_six_numbers>会调用rsi这个参数
  400f05:	e8 52 05 00 00       	callq  40145c <read_six_numbers>      ;读入6个数字
  400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp)                    ;if((rsp)==1)
  400f0e:	74 20                	je     400f30 <phase_2+0x34>          ;goto 400f30:lea 0x4(%rsp),%rbx    所以(rsp)=1
  400f10:	e8 25 05 00 00       	callq  40143a <explode_bomb>          ;else boom!
  400f15:	eb 19                	jmp    400f30 <phase_2+0x34>          ;这句话能运行得到吗
  400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax                ;eax=(rbx-0x4)
  400f1a:	01 c0                	add    %eax,%eax                      ;eax*=2
  400f1c:	39 03                	cmp    %eax,(%rbx)                    ;if((rbx)==eax)
  400f1e:	74 05                	je     400f25 <phase_2+0x29>          ;goto 400f25
  400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb>          ;else bomb!
  400f25:	48 83 c3 04          	add    $0x4,%rbx                      ;rbx=rbx+0x4
  400f29:	48 39 eb             	cmp    %rbp,%rbx                      ;if(rbx!=rbp)
  400f2c:	75 e9                	jne    400f17 <phase_2+0x1b>          ;goto 400f17:mov -0x4(%rbx),%eax
  400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40>          ;goto 400f3c return
  400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx                 ;rbx=rsp+0x4
  400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp                ;rbp=rsp+0x18
  400f3a:	eb db                	jmp    400f17 <phase_2+0x1b>          ;goto 400f17
  400f3c:	48 83 c4 28          	add    $0x28,%rsp
  400f40:	5b                   	pop    %rbx
  400f41:	5d                   	pop    %rbp
  400f42:	c3                   	retq   

汇编代码改写如下
 

int phase_2(){
	rsi=rsp;
	if((rsp)==1){
		rbx=rsp+0x4;
		rbp=rsp+0x18;
		
		eax=rbx-0x4;
		eax*=2;
		if((rbx)==eax){
			rbx=rbx+0x4;
			if(rbx!=rbp)
				goto line9;
			else
				return ;
		}
		else boom!
	}
	else 
		boom!
} 

03 条件与分支(switch)

这题的关键点在于认识到“jmpq   *0x402470(,%rax,8)”是一个跳转表,从而理解下面的那几条cmp命令相当于switch的几条case。

这题和上一题的输入对我还是有很大的启发效果的,那就是不用再纠结到底是用哪个寄存器来存储输入的数据了,直接看函数开始的%rsp自减操作和紧跟着的push操作。本质上就是抓住一点,输入的参数说到底还是存储在刚才开辟的栈空间站中。

0000000000400f43 <phase_3>:
  400f43:	48 83 ec 18          	sub    $0x18,%rsp       ;栈指针减24,腾出空间
  400f47:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx   ;
  400f4c:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx   ;传入这个栈的两个参数地址从(rsp+0x8)开始
  400f51:	be cf 25 40 00       	mov    $0x4025cf,%esi   ;给esi传入内容 "%d %d" (可以强制类型转换为一个字符串)
  400f56:	b8 00 00 00 00       	mov    $0x0,%eax        ;eax=0
  400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt> ;读入输入
  400f60:	83 f8 01             	cmp    $0x1,%eax                    ;if(eax>1)吧   所以eax必为1
  400f63:	7f 05                	jg     400f6a <phase_3+0x27>        ;跳转
  400f65:	e8 d0 04 00 00       	callq  40143a <explode_bomb>        ;否则爆炸
  400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp)               ;比较(rsp+8)这个地址里面存储的数据和0x7的大小 if((rsp+8)>0x7)
  400f6f:	77 3c                	ja     400fad <phase_3+0x6a>        ;跳转到400fad 即引爆炸弹
  400f71:	8b 44 24 08          	mov    0x8(%rsp),%eax               ;否则eax=(rsp+8) 所以exa的值是小于等于0x7的
  400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8)           ;我们发现*0x402470的值为4198268  跳转到4198268+rax*8  十进制的4198268=0x400f7c
  400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax                   ;eax=0xcf
  400f81:	eb 3b                	jmp    400fbe <phase_3+0x7b>        ;跳转到400fbe
  400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax                  ;eax=0x2c3
  400f88:	eb 34                	jmp    400fbe <phase_3+0x7b>         
  400f8a:	b8 00 01 00 00       	mov    $0x100,%eax
  400f8f:	eb 2d                	jmp    400fbe <phase_3+0x7b>
  400f91:	b8 85 01 00 00       	mov    $0x185,%eax
  400f96:	eb 26                	jmp    400fbe <phase_3+0x7b>
  400f98:	b8 ce 00 00 00       	mov    $0xce,%eax
  400f9d:	eb 1f                	jmp    400fbe <phase_3+0x7b>
  400f9f:	b8 aa 02 00 00       	mov    $0x2aa,%eax
  400fa4:	eb 18                	jmp    400fbe <phase_3+0x7b>
  400fa6:	b8 47 01 00 00       	mov    $0x147,%eax
  400fab:	eb 11                	jmp    400fbe <phase_3+0x7b>
  400fad:	e8 88 04 00 00       	callq  40143a <explode_bomb>      
  400fb2:	b8 00 00 00 00       	mov    $0x0,%eax
  400fb7:	eb 05                	jmp    400fbe <phase_3+0x7b>
  400fb9:	b8 37 01 00 00       	mov    $0x137,%eax
  400fbe:	3b 44 24 0c          	cmp    0xc(%rsp),%eax               ;if(eax==(rsp+0xc))
  400fc2:	74 05                	je     400fc9 <phase_3+0x86>        ;跳转到400fc9 所以eax必然=(rsp+0xc)
  400fc4:	e8 71 04 00 00       	callq  40143a <explode_bomb>        ;否则爆炸   所以eax=(rsp+0xc)
  400fc9:	48 83 c4 18          	add    $0x18,%rsp                   ;恢复栈指针
  400fcd:	c3                   	retq   

04 递归调用和栈

这题的麻烦点我感觉在于phase_4和func4之间传递的参数。我们假设函数A调用函数B。如果函数B中突然就出现了用一个参数寄存器Ri 来给其他寄存器赋值,或者用一个还没在函数B中进行赋值的寄存器Rj 和一个数相比较,且RiRj 都在A中被赋值过,那么RiRj 大概率就是在两个函数间传递的参数。

0000000000400fce <func4>:
  400fce:	48 83 ec 08          	sub    $0x8,%rsp                     ;栈指针自减8
  400fd2:	89 d0                	mov    %edx,%eax                     ;eax=edx=14
  400fd4:	29 f0                	sub    %esi,%eax                     ;eax=eax-esi  esi=0
  400fd6:	89 c1                	mov    %eax,%ecx                     ;ecx=eax=edx-esi       
  400fd8:	c1 e9 1f             	shr    $0x1f,%ecx                    ;逻辑右移ecx31位,即判断ecx的符号位 汇编的右移会改变寄存器存储的值,这一段和c语言的不同
  400fdb:	01 c8                	add    %ecx,%eax                     ;eax=ecx+eax
  400fdd:	d1 f8                	sar    %eax                          ;把eax算数右移一位
  400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx            ;ecx=rax+rsi
  400fe2:	39 f9                	cmp    %edi,%ecx                     ;if(ecx<=edi)
  400fe4:	7e 0c                	jle    400ff2 <func4+0x24>           ;goto 400ff2
  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx               ;else edx=rcx-1
  400fe9:	e8 e0 ff ff ff       	callq  400fce <func4>                ;goto 400fce 开始递归了  为了跳出递归,就有ecx=edi
  400fee:	01 c0                	add    %eax,%eax                     ;eax=2*eax
  400ff0:	eb 15                	jmp    401007 <func4+0x39>           ;goto 401007 返回
  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax                     ;eax=0
  400ff7:	39 f9                	cmp    %edi,%ecx                     ;if(ecx>=edi)
  400ff9:	7d 0c                	jge    401007 <func4+0x39>           ;goto 401007 返回
  400ffb:	8d 71 01             	lea    0x1(%rcx),%esi                ;else esi=rcx+1
  400ffe:	e8 cb ff ff ff       	callq  400fce <func4>                ;goto 400fce 又开始递归了他奶奶滴
  401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax         ;eax=2*rax+1
  401007:	48 83 c4 08          	add    $0x8,%rsp                     ;恢复栈指针
  40100b:	c3                   	retq                                 ;返回0才行

000000000040100c <phase_4>:
  40100c:	48 83 ec 18          	sub    $0x18,%rsp                      ;栈指针自减24
  401010:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx                  ;rcx  这个空间存num1       这两句lea有什么作用呢
  401015:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx                  ;这个空间存num2 给两个变量rcx和rdx腾出空间  不是给两个变量腾出空间
  40101a:	be cf 25 40 00       	mov    $0x4025cf,%esi                  ;往esi里面传入数据 “%d %d”
  40101f:	b8 00 00 00 00       	mov    $0x0,%eax                       ;eax=0
  401024:	e8 c7 fb ff ff       	callq  400bf0 <__isoc99_sscanf@plt>    ;从stdin接收数据 
  401029:	83 f8 02             	cmp    $0x2,%eax                       ;if(eax!=0x2)   这里eax里面存储的是刚刚从函数400bfo传来的scanf的输出参数
  40102c:	75 07                	jne    401035 <phase_4+0x29>           ;boom!
  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp)                  ;else if(rdx=<0xe)  所以eax必然等于2
  401033:	76 05                	jbe    40103a <phase_4+0x2e>           ;goto 40403a 所以rdx<=0xe
  401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>           ;else bomb!
  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx                       ;edx=0xe
  40103f:	be 00 00 00 00       	mov    $0x0,%esi                       ;esi=0
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi                  ;edi=edx=0xe rdx不是刚刚被赋值了吗? mov指令和lea指令的区别
  401048:	e8 81 ff ff ff       	callq  400fce <func4>                  ;goto func4
  40104d:	85 c0                	test   %eax,%eax                       ;if(eax!=0)        所以eax=0
  40104f:	75 07                	jne    401058 <phase_4+0x4c>           ;goto 401058  boom!
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)                  ;else if(rcx==0)   所以rcx=0=num2
  401056:	74 05                	je     40105d <phase_4+0x51>           ;goto 40105d恢复栈指针
  401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>
  40105d:	48 83 c4 18          	add    $0x18,%rsp
  401061:	c3                   	retq  

汇编代码改写如下 

//x=edx=14,y=esi=0,z=ecx,val=eax,k=edi 
int func4(int x,int y,int k){
	val=x;
	val=val-y;
	z=val;
	z=z>>31;
	val=val+z;
	val=val>>1;
	z=val+y;
	if(z>k){
		x=z-1;
		func4();
		val=val*2;
		return val;
	}
	else{
		val=0;
		if(z<k){
			y=z+1;
			func4();
			val=val*2+1;
		}
		else return val;
	}
}

05 指针与数组

刚才忽然悟了一个东西,之前的几个phase里面上来就是对esi这个寄存器进行操作,搞得我就很疑惑esi不是一般作为传递第二个参数的寄存器吗,为什么不用edi这个寄存器呢?而且我们的输入又是从什么地方被接受到这个phase的呢?

后来我在一篇题解里看到了这样一句话,“(phase_1)将0x402400赋值到%esi寄存器,根据X86-64的参数传递规则,我们知道%esi寄存器保存的是函数调用的第二个参数。第一个参数就是我们通过标准输入/指定的输入设备文件输入的数据,这里我们简单输入“1234”,存放在%rax指向的地址处”这句话看下来我对为什么第一个直接操作esi似懂非懂。昨晚在知乎上看到了一篇题解,也提到了我们输入的内容是作为第一个参数传入的。我这才恍然大悟,原来rdi只是没有被显示地使用啊,汇编里面的函数参数传递还是一般遵循相应寄存器保存第几个参数这一规律的。我们可以看看bomb.c文件,可以发现每个函数都会有形如“phase_i(input)”这种函数调用,这就更证实了第一个传递的参数是我们的输入内容,而bomb.asm里面的esi保存的参数则更像是隐式参数,是通过内存中预先存好的值来进行传递的。而且通过观察phase_2~4,我们可以发现函数都是调用了函数“__isoc99_sscanf@plt”来读入输入内容,在这里就得用宏观的眼光审视这个函数了——要牢记输入的内容会按序存储在栈帧上,更准确地说是存储在rsp刚才开辟的空间里面,也就是用rsp可以顺序访问到输入内容,而不用去想我们的输入到底是通过哪个寄存器来传递的。

在本题中,通过“mov %dl,0x10(%rsp,%rax,1)”我们可以判断出处理后的输入内容按顺序存储在以“rsp+0x10”为起始的地址中。由分析可知这一关通过取我们输入六个字符的ASCII码的低四位作为索引值,查找maduiersnfotvbyl里的字符,最后返回的字符串应该是flyers

maduiersnfotvbyl中f为第9位,l为第15位,y第14位,e第5位,r第6位,s第7

即我们需要输入6个字符,使它们ASCII码低四位分别是:1001, 1111, 1110, 0101, 0110, 0111。查看ASCII表可找到对应字符,a的ASCII码为01100001,因此,其中一种解码可为ionuvw;ionefg;9?>567

0000000000401062 <phase_5>:
  401062:	53                   	push   %rbx                           ;先保存rbx   rbx存放着输入的字符串的地址
  401063:	48 83 ec 20          	sub    $0x20,%rsp                     ;栈指针腾出32byte,是准备开辟数组了吗
  401067:	48 89 fb             	mov    %rdi,%rbx                      ;rbx=rdi  把输入的参数地址传递到rbx
  40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax                  ;什么意思
  401071:	00 00 
  401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp)                ;(rsp+0x18)=rax
  401078:	31 c0                	xor    %eax,%eax                      ;将eax清0
  40107a:	e8 9c 02 00 00       	callq  40131b <string_length>
  40107f:	83 f8 06             	cmp    $0x6,%eax                      ;if(eax==6)  所以字符串长度为6=eax
  401082:	74 4e                	je     4010d2 <phase_5+0x70>          ;goto 4010d2
  401084:	e8 b1 03 00 00       	callq  40143a <explode_bomb>          ;else bomb!
  401089:	eb 47                	jmp    4010d2 <phase_5+0x70>          ;goto 4010d2 这句话不是多此一举吗
  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx             ;进行0扩展字节到双字,但是rbx不是四字吗?这是(%rbx,%rax,1)才是操作源数,是双字的  ecx=rbx+rax还是ecx=(rbx+rax)呢   后者
  40108f:	88 0c 24             	mov    %cl,(%rsp)                     ;(rsp)=cl   假设数组名字是A,此处应该是A[0]=cl cl是ecx的低8位
  401092:	48 8b 14 24          	mov    (%rsp),%rdx                    ;rdx=(rsp)  
  401096:	83 e2 0f             	and    $0xf,%edx                      ;将edx和F相与
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx            ;edx=(0x4024b0+rdx) 0x4024b0 对应字符串:maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)          ;rsp+rax*1+0x10=dl dl是edx的低8位
  4010a4:	48 83 c0 01          	add    $0x1,%rax                      ;rax=rax+1;
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax                      ;if(eax!=0x6)
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>          ;goto 40108b
  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)                ;(rsp+0x16)=0;    是不是字符串的结尾
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi                 ;esi="flyers"
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi                ;rdi=(rsp+0x10) 
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal>     
  4010c2:	85 c0                	test   %eax,%eax                      ;if(rax==0)
  4010c4:	74 13                	je     4010d9 <phase_5+0x77>          ;
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>          ;bomb
  4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)               ;什么意思?
  4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>          ;goto 4010d9
  4010d2:	b8 00 00 00 00       	mov    $0x0,%eax                      ;eax=0
  4010d7:	eb b2                	jmp    40108b <phase_5+0x29>          ;goto 40108b
  4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax
  4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  4010e5:	00 00 
  4010e7:	74 05                	je     4010ee <phase_5+0x8c>
  4010e9:	e8 42 fa ff ff       	callq  400b30 <__stack_chk_fail@plt>
  4010ee:	48 83 c4 20          	add    $0x20,%rsp
  4010f2:	5b                   	pop    %rbx
  4010f3:	c3                   	retq   

phase_05改写如下

int phase_5(){
	char *A;
	A= 0x4024b0;
	string str;
	cin>>str;
	eax=srt.length();
	if(eax!=6)
		bomb!
	else{
		eax=0;
	L1:
		ecx=rbx+rax*1;
		(rsp)=cl;
		rdx=(rsp);
		edx=edx&1111;
		edx=A[rdx];
		rsp+rax*1+0x10=dl;
		rax++;
		
		if(eax==0x6){
			(rsp+0x16)=0;
			esi="flyers";
			rdi=0x10+rsp;
			if(eax==0){
				rax=rsp+0x18;
			}		
			else
				boom!
		}
		else
			goto L1;
			
		
	}
}

06  链表与结构体

这题可谓是极尽恶心,折磨得我晕头转向。解决这题最好的方法是把代码分成几部分来看,但是把phase_6的代码分块对代码阅读的能力要求又极高,新手不完成这个phase基本分不好块,有“循环论证”那感觉的哈哈哈哈。

处理这题我的方法是先照例给每行指令写好注释,相当于通读一遍代码,然后把用if-else、for等分支循环改写代码,让代码初步变得直观。这个时候就可以去休息会儿了——tmd改完代码一百来行,还有一堆goto语句,真的再看一眼就会爆炸……OK,我们继续看改好后的代码。可以很明显地发现代码的第一部分由两层嵌套循环组成,旨在判定输入的数字都不大于6且互不相同。这里有个小技巧,就是自己跟着循环走一两遍,把rsp+4*i看成num[i]这种数组形式,之后循环要表达什么就较为容易理解了。第二部分则是形如“num[i]=7-num[i]”。

第三部分是对链表进行操作。我们使用命令“print (char*)0x6032d0”发现输出结果是“<node1> "L\001"”,看到node1要迅速反应出来这有可能是一个链表或者树,接着使用“x/66 0x6032d0”一探究竟,发现这块连续的内存大概率保存的是链表。

通过分析node的结构,可以猜测它抽象为结构体可以表示为

struct node{

    int value;

    int number;

    node* next;

}

然后就到了一个最晦涩的循环部分,该循环根据输入数将链表中对应的第“num[i]”个结点的地址复制到 0x20(%rsp) 开始的栈中。

       第四部分是要求在rsp中排序好的链表是按照值降序排列的,通过比较node[i].value,可以发现node[3]>node[4]>node[5]>node[6]>node[1]>node[2],又因为这个顺序,是经过了numx = 0x7 - numx 则原输入数据应该是4 3 2 1 6 5

00000000004010f4 <phase_6>:
  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                     ;rsp-=90,准备腾出空间
  401100:	49 89 e5             	mov    %rsp,%r13                      ;r13=rsp  把栈的起始地址传递给r13
  401103:	48 89 e6             	mov    %rsp,%rsi                      ;rsi=rsp  把栈的起始地址传递给rsi
  401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>      ;读入六个数,两个函数通过rsi进行参数传递
  40110b:	49 89 e6             	mov    %rsp,%r14                      ;r14=rsp
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d                     ;r12d=0
  401114:	4c 89 ed             	mov    %r13,%rbp                      ;rbp=r13  把栈的起始地址传递给rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax                 ;eax=(r13)  获取首地址的数据
  40111b:	83 e8 01             	sub    $0x1,%eax                      ;eax-=0x1
  40111e:	83 f8 05             	cmp    $0x5,%eax                      ;if(eax<=0x5)
  401121:	76 05                	jbe    401128 <phase_6+0x34>          ;goto 401128:add  $0x1,%r12d
  401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>          ;else boom!
  401128:	41 83 c4 01          	add    $0x1,%r12d                     ;r12d+=0x1
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d                     ;if(r12d==0x6)
  401130:	74 21                	je     401153 <phase_6+0x5f>          ;goto 401153:lea    0x18(%rsp),%rsi
  401132:	44 89 e3             	mov    %r12d,%ebx                     ;else ebx=r12d
  401135:	48 63 c3             	movslq %ebx,%rax                      ;rax=ebx
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax             ;eax=rsp+rax*4
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)                 ;if(eax!=(rbp))
  40113e:	75 05                	jne    401145 <phase_6+0x51>          ;goto 401145 所以eax!=(rbp)
  401140:	e8 f5 02 00 00       	callq  40143a <explode_bomb>          ;else boom!
  401145:	83 c3 01             	add    $0x1,%ebx                      ;ebx+=0x1
  401148:	83 fb 05             	cmp    $0x5,%ebx                      ;if(ebx<=0x5)
  40114b:	7e e8                	jle    401135 <phase_6+0x41>          ;goto 401135:movslq %ebx,%rax
  40114d:	49 83 c5 04          	add    $0x4,%r13                      ;else r13+=0x4
  401151:	eb c1                	jmp    401114 <phase_6+0x20>          ;goto 401114:	mov    %r13,%rbp

  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi                ;rsi=(rsp+0x18)
  401158:	4c 89 f0             	mov    %r14,%rax                      ;rax=r14
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx                      ;ecx=0x7
  401160:	89 ca                	mov    %ecx,%edx                      ;edx=ecx
  401162:	2b 10                	sub    (%rax),%edx                    ;edx-=(rax)
  401164:	89 10                	mov    %edx,(%rax)                    ;(rax)=edx
  401166:	48 83 c0 04          	add    $0x4,%rax                      ;rax+=0x4
  40116a:	48 39 f0             	cmp    %rsi,%rax                      ;if(rax!=rsi)
  40116d:	75 f1                	jne    401160 <phase_6+0x6c>          ;goto 401160:mov    %ecx,%edx
  
  40116f:	be 00 00 00 00       	mov    $0x0,%esi                      ;else esi=0
  401174:	eb 21                	jmp    401197 <phase_6+0xa3>          ;goto 401197:ecx=rsp+rsi*1
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx                 ;rdx=(rdx+0x8)
  40117a:	83 c0 01             	add    $0x1,%eax                      ;eax+=0x1
  40117d:	39 c8                	cmp    %ecx,%eax                      ;if(eax!=ecx)
  40117f:	75 f5                	jne    401176 <phase_6+0x82>          ;goto 401176
  401181:	eb 05                	jmp    401188 <phase_6+0x94>          ;else goto 101188
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx                 ;edx=0x6032d0 *0x6032d0=<node1> "L\001"
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)         ;rdx=0x20+rsp+rsi*2
  40118d:	48 83 c6 04          	add    $0x4,%rsi                      ;rsi+=0x4
  401191:	48 83 fe 18          	cmp    $0x18,%rsi                     ;if(rsi==0x18)
  401195:	74 14                	je     4011ab <phase_6+0xb7>          ;goto 4011ab:0x20(%rsp),%rbx
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx             ;这是数组的形式吧  ecx=rsp+rsi*1
  40119a:	83 f9 01             	cmp    $0x1,%ecx                      ;if(ecx<=0x1)
  40119d:	7e e4                	jle    401183 <phase_6+0x8f>          ;goto 401183:mov    $0x6032d0,%edx
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax                      ;eax=0x1
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx                 ;edx=0x6032d0 *0x6032d0="L\001" 用0x6032d0查看!
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>          ;goto 401176:mov    0x8(%rdx),%rdx
  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx                ;rbx=(rsp+0x20)
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax                ;rax=(rsp+0x28)
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi                ;rsi=(rsp+0x50)
  4011ba:	48 89 d9             	mov    %rbx,%rcx                      ;rcx=rbx
  4011bd:	48 8b 10             	mov    (%rax),%rdx                    ;rdx=(rax)    rax里面存的是地址而不是普通数据了
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)                 ;(rcx+0x8)=rdx
  4011c4:	48 83 c0 08          	add    $0x8,%rax                      ;rax+=0x8
  4011c8:	48 39 f0             	cmp    %rsi,%rax                      ;if(rax!=rsi)
  4011cb:	74 05                	je     4011d2 <phase_6+0xde>          ;goto 4011d2:movq   $0x0,0x8(%rdx)
  4011cd:	48 89 d1             	mov    %rdx,%rcx                      ;else rcx=rdx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>          ;goto 4011bd:	mov    (%rax),%rdx
  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)                 ;(rdx+8)=0
  4011d9:	00 
  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp                      ;ebp=0x5
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax                 ;rax=(rbx+0x8)
  4011e3:	8b 00                	mov    (%rax),%eax                    ;eax=(rax)
  4011e5:	39 03                	cmp    %eax,(%rbx)                    ;if((rbx)>=eax)
  4011e7:	7d 05                	jge    4011ee <phase_6+0xfa>          ;goto 4011ee  所以(rbx)>=eax
  4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb>          ;else boom
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx                 ;rbx=(rbx+0x8)
  4011f2:	83 ed 01             	sub    $0x1,%ebp                      ;ebp-=0x1
  4011f5:	75 e8                	jne    4011df <phase_6+0xeb>          ;if(ebp!=0) goto 4011df:mov  0x8(%rbx),%rax  
  4011f7:	48 83 c4 50          	add    $0x50,%rsp                     ;开始恢复栈
  4011fb:	5b                   	pop    %rbx
  4011fc:	5d                   	pop    %rbp
  4011fd:	41 5c                	pop    %r12
  4011ff:	41 5d                	pop    %r13
  401201:	41 5e                	pop    %r14
  401203:	c3                   	retq   


000000000040145c <read_six_numbers>:
  40145c:	48 83 ec 18          	sub    $0x18,%rsp                       ;rsp自减来腾出空间
  401460:	48 89 f2             	mov    %rsi,%rdx                        ;rdx=rsi        这里的rsi是由pahse_2传递过来的
  401463:	48 8d 4e 04          	lea    0x4(%rsi),%rcx                   ;rcx=rsi+0x4
  401467:	48 8d 46 14          	lea    0x14(%rsi),%rax                  ;rax=rsi+0x14
  40146b:	48 89 44 24 08       	mov    %rax,0x8(%rsp)                   ;(rsp+0x8)=rax
  401470:	48 8d 46 10          	lea    0x10(%rsi),%rax                  ;rax=rsi+0x10
  401474:	48 89 04 24          	mov    %rax,(%rsp)                      ;(rsp)=rax
  401478:	4c 8d 4e 0c          	lea    0xc(%rsi),%r9                    ;(rsi+0xc)=%r9
  40147c:	4c 8d 46 08          	lea    0x8(%rsi),%r8                    ;(rsi+0x8)=%r8
  401480:	be c3 25 40 00       	mov    $0x4025c3,%esi                   ;esi=0x4025c3
  401485:	b8 00 00 00 00       	mov    $0x0,%eax                        ;eax=0
  40148a:	e8 61 f7 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  40148f:	83 f8 05             	cmp    $0x5,%eax                        ;if(eax>5)
  401492:	7f 05                	jg     401499 <read_six_numbers+0x3d>   ;return 所以eax=6
  401494:	e8 a1 ff ff ff       	callq  40143a <explode_bomb>            ;else bomb!
  401499:	48 83 c4 18          	add    $0x18,%rsp
  40149d:	c3                   	retq   

可以把汇编改写为类似c语言的格式
 

int phase_6(){
	r13=rsp;
	rsi=rsp;
	//读入六个数,这六个数在(rsp)~(rsp+0x18)
	r14=rsp;
	r12d=0; 
	rbp=r13;
	eax=(r13);
	eax-=0x1;
	if(eax<=0x5){
		r12d+=0x1;
		if(r12d!=0x6){
			ebx=r12d;
			rax=ebx;
			eax=rsp+rax*4;
			if(eax!=(rbp)){
				ebx+=0x1;
				if(ebx>0x5){
					r13+=0x4;
					goto line94:rbp=r13;
				}
				else{
					goto line101:rax=ebx;
				}
			}
			else boom!
		}
		else{
			rsi=(rsp+0x18);
			rax=r14;
			ecx=0x7;
			ecx=edx;
			edx-=(rax);
			(rax)=edx;
			rax+=0x4;
			if(rax==rsi){
				esi=0;
				ecx=rsp+rsi*1;
				if(ecx>0x1){
					eax=0x1;
					edx=0x6032d0;
					rdx=(rdx+0x8);
					eax+=0x1;
					if(eax==ecx){
						rdx=0x20+rsp+rsi*2;
						rsi+=0x4;
						if(rsi==0x18)
							goto line149:rbx=(rsp+0x20);
						else
							goto line125:ecx=rsp+rsi*1;
					}
					else{
						goto 
					}
				}
				else{
					edx=0x6032d0;
					rdx=0x20+rsp+rsi*2;
					rsi+=0x4;
					if(rsi!=0x18){
						goto line125:ecx=rsp+rsi*1;
					}
					else{
						rbx=(rsp+0x20);
						rax=(rsp+0x28);
						rsi=(rsp+0x50);
						rcx=rbx;
						rdx=(rax);    rax里面存的是地址而不是普通数据了;
						(rcx+0x8)=rdx;
						rax+=0x8;
						if(rax==rsi){
							rcx=rdx;
							goto line141:rdx=(rax);
						}
						else{
							(rdx+8)=0;
							ebp=0x5;
							rax=(rbx+0x8);
							eax=(rax);
							if((rbx)>=eax){
								rbx=(rbx+0x8);
								ebp-=0x1;
								if(ebp!=0)
									goto line151:rax=(rbx+0x8);
							}
							else 
								bomb!
						}
					}
				}
			}
			else{
				ecx=edx;
				edx-=(rax);
				(rax)=edx;
				rax+=0x4;
				goto line123;
			}
		}
	}
	else 
		boom!
	
}

07 Secret_Phase

首先要发现secret_phase不像前面6个phase,它属于一个被其他调用的函数,类似于phase1~6调用的func函数。这里还有个小坑,进入gdb模式后直接“print (char*)0x603870”,会发现输出“<input_strings+240> ""”,这说明要把解决前面几个phase的答案输入后再打断点才能输出这里应有的内容。其实这里有个取巧的地方,“print (char*)0x402619”得到输出“%d %d %s”。说明输入两个整形和一个字符串才可以进入这个phase,而前面6个phase只有phase_4的答案是输入两个整形。再结合“print (char*)0x0x402622”得到“DrEvil”,可以猜测进入这个阶段完整的答案应该是“7 0 DrEvil”。

进如secret_phase后,发现 “print (char*)0x6030f0”得到“<n1>"$"”,猜测这个地址存储的又是链表的形式,而且好像是一棵二叉树,画出对应的二叉树。

从退出func7后eax的值必须为2可以知道func7只能返回2。在func7里面“mov 0x10(%rdi),%rdi”比较直观,是进入右节点。但是“mov 0x8(%rdi),%rdi”就让人有些费解了,这时候直接“print *0x6030f8”查看这个地址的内容是“6304016即0x603110”,不要自己瞎想,发现这句话的意思是进入左节点。递归过程不再赘述,还是转化成if-else等语句再自行判断。

总的来说有了phase_6的经验,这题难度比phase_6略低,不过相较于其他phase还是难不少的。

可使用命令 touch answers.txt 新建名为answers的.txt文件,将所有拆弹密码写入该文件,然后用命令 ./bomb answers.txt 运行bomb可执行文件 (之前的每一关调试结束后,也可以将新的拆弹密码写入.txt文件,用这个方法验证是否爆炸,就不用每次重复输入之前关卡的拆弹密码了)。

0000000000401204 <fun7>:
  401204:	48 83 ec 08          	sub    $0x8,%rsp
  401208:	48 85 ff             	test   %rdi,%rdi                      ;if(rdi==0)
  40120b:	74 2b                	je     401238 <fun7+0x34>             ;goto 401238: eax=-1,准备return
  40120d:	8b 17                	mov    (%rdi),%edx                    ;else edx=(rdi)
  40120f:	39 f2                	cmp    %esi,%edx                      ;if(edx<=esi)
  401211:	7e 0d                	jle    401220 <fun7+0x1c>             ;goto 401220
  401213:	48 8b 7f 08          	mov    0x8(%rdi),%rdi                 ;else rdi=(rdi+8)
  401217:	e8 e8 ff ff ff       	callq  401204 <fun7>                  ;开始递归
  40121c:	01 c0                	add    %eax,%eax                      ;eax=2*eax
  40121e:	eb 1d                	jmp    40123d <fun7+0x39>             ;return 
  401220:	b8 00 00 00 00       	mov    $0x0,%eax                      ;eax=0
  401225:	39 f2                	cmp    %esi,%edx                      ;if(edx==esi)
  401227:	74 14                	je     40123d <fun7+0x39>             ;goto 40123d:return
  401229:	48 8b 7f 10          	mov    0x10(%rdi),%rdi                ;else rdi=(rdi+16)    ;又是节点的结构是吗
  40122d:	e8 d2 ff ff ff       	callq  401204 <fun7>                  ;开始递归
  401232:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax          ;eax=rax*2+1
  401236:	eb 05                	jmp    40123d <fun7+0x39>             ;return 
  401238:	b8 ff ff ff ff       	mov    $0xffffffff,%eax               ;eax=-1
  40123d:	48 83 c4 08          	add    $0x8,%rsp                    
  401241:	c3                   	retq   

0000000000401242 <secret_phase>:
  401242:	53                   	push   %rbx
  401243:	e8 56 02 00 00       	callq  40149e <read_line>
  401248:	ba 0a 00 00 00       	mov    $0xa,%edx                      ;edx=10
  40124d:	be 00 00 00 00       	mov    $0x0,%esi                      ;esi=0
  401252:	48 89 c7             	mov    %rax,%rdi                      ;rdi=rax
  401255:	e8 76 f9 ff ff       	callq  400bd0 <strtol@plt>
  40125a:	48 89 c3             	mov    %rax,%rbx                      ;rbx=rax
  40125d:	8d 40 ff             	lea    -0x1(%rax),%eax                ;eax=rax-1
  401260:	3d e8 03 00 00       	cmp    $0x3e8,%eax                    ;if(eax<=1000)
  401265:	76 05                	jbe    40126c <secret_phase+0x2a>     ;goto 40126c
  401267:	e8 ce 01 00 00       	callq  40143a <explode_bomb>          ;else boom!
  40126c:	89 de                	mov    %ebx,%esi                      ;esi=ebx
  40126e:	bf f0 30 60 00       	mov    $0x6030f0,%edi                 ;(edi)=<n1>"$",edi=$0x6030f0 反应出这是个连续存储的链表,用x/140 0x6030f0查看所以节点
  401273:	e8 8c ff ff ff       	callq  401204 <fun7>                  ;edi=$0x6030f0
  401278:	83 f8 02             	cmp    $0x2,%eax                      ;if(eax==2)   所以func返回的eax=2
  40127b:	74 05                	je     401282 <secret_phase+0x40>     ;goto 40127d
  40127d:	e8 b8 01 00 00       	callq  40143a <explode_bomb>          ;else boom!
  401282:	bf 38 24 40 00       	mov    $0x402438,%edi                 ;(edi)="Wow! You've defused the secret stage!"
  401287:	e8 84 f8 ff ff       	callq  400b10 <puts@plt>
  40128c:	e8 33 03 00 00       	callq  4015c4 <phase_defused>
  401291:	5b                   	pop    %rbx
  401292:	c3                   	retq   
  401293:	90                   	nop
  401294:	90                   	nop
  401295:	90                   	nop
  401296:	90                   	nop
  401297:	90                   	nop
  401298:	90                   	nop
  401299:	90                   	nop
  40129a:	90                   	nop
  40129b:	90                   	nop
  40129c:	90                   	nop
  40129d:	90                   	nop
  40129e:	90                   	nop
  40129f:	90                   	nop
00000000004015c4 <phase_defused>:
  4015c4:	48 83 ec 78          	sub    $0x78,%rsp                 ;rdp-=120
  4015c8:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  4015cf:	00 00 
  4015d1:	48 89 44 24 68       	mov    %rax,0x68(%rsp)            ;rsp[104]=rax
  4015d6:	31 c0                	xor    %eax,%eax                  ;eax=0
  4015d8:	83 3d 81 21 20 00 06 	cmpl   $0x6,0x202181(%rip)        # 603760 <num_input_strings>
  4015df:	75 5e                	jne    40163f <phase_defused+0x7b>  ;如果输入数据的个数不等于6 return
  4015e1:	4c 8d 44 24 10       	lea    0x10(%rsp),%r8               ;r8=rsp[16]
  4015e6:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx               ;rcx=rsp[12]
  4015eb:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx               ;rdx=rsp[8]
  4015f0:	be 19 26 40 00       	mov    $0x402619,%esi               ;"%d %d %s",输入的格式为“整形 整形 字符串”
  4015f5:	bf 70 38 60 00       	mov    $0x603870,%edi               ;input_strings+240> ""
  4015fa:	e8 f1 f5 ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  4015ff:	83 f8 03             	cmp    $0x3,%eax                    ;if(eax!=3) 如果输入数据个数不为3
  401602:	75 31                	jne    401635 <phase_defused+0x71>  ;goto 401635 结束
  401604:	be 22 26 40 00       	mov    $0x402622,%esi               ;else esi="DrEvil" 
  401609:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi              ;rdi=rsp[16]
  40160e:	e8 25 fd ff ff       	callq  401338 <strings_not_equal>
  401613:	85 c0                	test   %eax,%eax                    ;if(eax!=0)
  401615:	75 1e                	jne    401635 <phase_defused+0x71>  ;goto 401635 结束
  401617:	bf f8 24 40 00       	mov    $0x4024f8,%edi               ;else (edi)="Curses, you've found the secret phase!"
  40161c:	e8 ef f4 ff ff       	callq  400b10 <puts@plt>
  401621:	bf 20 25 40 00       	mov    $0x402520,%edi               ;(edi)="But finding it and solving it are quite different..."
  401626:	e8 e5 f4 ff ff       	callq  400b10 <puts@plt>
  40162b:	b8 00 00 00 00       	mov    $0x0,%eax                    ;eax=0
  401630:	e8 0d fc ff ff       	callq  401242 <secret_phase>        ;goto secret_phase
  401635:	bf 58 25 40 00       	mov    $0x402558,%edi               ;"Congratulations! You've defused the bomb!"
  40163a:	e8 d1 f4 ff ff       	callq  400b10 <puts@plt>
  40163f:	48 8b 44 24 68       	mov    0x68(%rsp),%rax              ;准备return了
  401644:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  40164b:	00 00 
  40164d:	74 05                	je     401654 <phase_defused+0x90>
  40164f:	e8 dc f4 ff ff       	callq  400b30 <__stack_chk_fail@plt>
  401654:	48 83 c4 78          	add    $0x78,%rsp
  401658:	c3                   	retq   
  401659:	90                   	nop
  40165a:	90                   	nop
  40165b:	90                   	nop
  40165c:	90                   	nop
  40165d:	90                   	nop
  40165e:	90                   	nop
  40165f:	90                   	nop

标签:CSAPP,Bomb,48,00,Lab,mov,eax,rsp,rax
From: https://blog.csdn.net/John_Snowww/article/details/139456301

相关文章