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

看见的看不见的

瞬间的永恒的

青草长啊大雪飘扬

——月亮之上

完整代码见: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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/691270.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

比亚迪正式签约国际皮划艇联合会和中国皮划艇协会,助推龙舟入奥新阶段

6月5日&#xff0c;比亚迪与国际皮划艇联合会、中国皮划艇协会在深圳共同签署合作协议&#xff0c;国际皮划艇联合会主席托马斯科涅茨科&#xff0c;国际皮划艇联合会秘书长理查德派蒂特&#xff0c;中国皮划艇协会秘书长张茵&#xff0c;比亚迪品牌及公关处总经理李云飞&#…

【计算视觉】学习计算机视觉你不得不膜拜的CVPR大神:何凯明

目录 第一章&#xff1a;CVPR——计算机视觉的终极擂台 第二章&#xff1a;何凯明——计算机视觉领域的耀眼星辰 第三章&#xff1a;高引用论文——计算机视觉研究的璀璨星辰 第四章&#xff1a;何凯明的CVPR论文——深度学习的探索之旅 第五章&#xff1a;结语——向何凯…

多样本上下文学习:开拓大模型的新领域

大模型&#xff08;LLMs&#xff09;在少量样本上下文学习&#xff08;ICL&#xff09;中展现出了卓越的能力&#xff0c;即通过在推理过程中提供少量输入输出示例来学习&#xff0c;而无需更新权重。随着上下文窗口的扩展&#xff0c;我们现在可以探索包含数百甚至数千个示例的…

线性表和链表

一&#xff0c;线性结构 1.Array Array文档&#xff1a;可以自行阅读相关文档来了解Array class array.array(typecode[, initializer]) array.append(x)&#xff1a;添加元素到数组末尾 array.count(x)&#xff1a;计算元素出现次数 array.extend(iterable)&#xff1a;将迭代…

数据库(27)——多表查询——自连接

语法 SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件...; 自连接可以是内连接查询也可以是外连接查询。 演示 我新增了字段friend便于演示 查询所有人的名字以及他们的friend的人的名字&#xff1a; select a.name,b.name from user a,user b where a.friendb.id; 其…

LeetCode72编辑距离

题目描述 解析 一般这种给出两个字符串的动态规划问题都是维护一个二维数组&#xff0c;尺寸和这两个字符串的长度相等&#xff0c;用二维做完了后可以尝试优化空间。这一题其实挺类似1143这题的&#xff0c;只不过相比1143的一种方式&#xff0c;变成了三种方式&#xff0c;就…

构建数字社会:Web3时代的社会治理与价值重构

随着数字化技术的飞速发展&#xff0c;我们正逐渐迈入Web3时代&#xff0c;这是一个以去中心化、开放性和透明性为特征的新时代。在这个时代&#xff0c;数字技术将不仅仅改变我们的生活方式和商业模式&#xff0c;还将对社会治理和价值观念产生深远影响。本文将探讨Web3时代下…

今天是放假带娃的一天

端午节放假第一天 早上5点半宝宝就咔咔乱叫了&#xff0c;几乎每天都这个点醒&#xff0c;准时的很&#xff0c;估计他是个勤奋的娃吧&#xff0c;要早起锻炼婴语&#xff0c;哈哈 醒来后做饭、洗锅、洗宝宝的衣服、给他吃D3&#xff0c;喂200ml奶粉、给他洗澡、哄睡&#xff0…

Unity2D游戏制作入门 | 12(之人物受伤和死亡的逻辑动画)

上期链接&#xff1a;Unity2D游戏制作入门 | 11(之人物属性及伤害计算)-CSDN博客 上期我们聊到了人物的自身属性和受伤时的计算&#xff0c;我们先给人物和野猪挂上属性和攻击属性的代码&#xff0c;然后通过触发器触发受伤的事件。物体&#xff08;人物也好敌人也行&#xff…

信息系统项目管理师0148:输出(9项目范围管理—9.3规划范围管理—9.3.3输出)

点击查看专栏目录 文章目录 9.3.3 输出 9.3.3 输出 范围管理计划 范围管理计划是项目管理计划的组成部分&#xff0c;描述将如何定义、制定、监督、控制和确认项 目范围。范围管理计划用于指导如下过程和相关工作&#xff1a; ①制定项目范围说明书&#xff1b;②根据详细项目范…

【树莓派内核版本降级】笔记

【树莓派内核版本降级】笔记 文章目录 【树莓派内核版本降级】笔记一、起因二、降级流程1.降级失败经验&#xff08;使用一体化的降级命令&#xff09;2.手动下载固件&#xff08;降级成功&#xff09; 一、起因 我在学习树莓派内核开发以及驱动开发的时候&#xff0c;树莓派在…

【uni-app】申请高德地图key,封装map.js,实现H5、iOS、Android通过getlocation获取地图定位信息

文章目录 map组件基础使用封装map.js&#xff0c;实现定位1、使用第三方地图&#xff1a;高德&#xff0c;申请对应平台key1、申请H5 key2、申请微信小程序 key3、申请android key查看证书详情&#xff0c;可以看到SHA1查看/设置Android包名 4、申请ios key 2、封装map1、lib/m…

例54:Draw使用

建立一个控制台工程&#xff0c;输入代码&#xff1a; Screen 13 移动到&#xff08;50,50&#xff09;而不绘图 Draw "BM 50,50" B:移动但不绘制,M:移动到指定位置 将绘图颜色设置为2&#xff08;绿色&#xff09; Draw "C2" C将颜色改为n …

2024最新 Jenkins + Docker实战教程(八)- Jenkins实现集群并发构建

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

SEO之关键词分布

初创企业搭建网站的朋友看1号文章&#xff1b;想学习云计算&#xff0c;怎么入门看2号文章谢谢支持&#xff1a; 1、我给不会敲代码又想搭建网站的人建议 2、新手上云 经过核心关键词确定与关键词扩展&#xff0c;应该已经得到一个至少包含几百个相关关键词的大列表。这些关键…

解决 There is no getter for property named ‘null‘ in ‘class 报错

1. 问题 mybatis-plus在更新删除操作时报错 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession750ee72a] 2024-06-08 21:03:07 [http-nio-8080-exec-3] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servl…

人工智能在【肿瘤生物标志物】领域的最新研究进展|顶刊速递·24-06-08

小罗碎碎念 本期文献速递的主题是——人工智能在“肿瘤生物标志物”领域的最新研究进展。 重点关注 今天推荐的6篇文献中&#xff0c;第二篇和第三篇是小罗最喜欢的&#xff0c;因为对于临床来说&#xff0c;比较具有实际意义&#xff0c;也和自己的想法很契合。 尤其是第三篇…

python 多任务之多进程

多任务 优势 多个任务同时执行可以大大提高程序执行效率&#xff0c;可以充分利用CPU资源&#xff0c;提高程序的执行效率 概念 是指在同一时间内执行多个任务 多进程 概念 进程&#xff08;process&#xff09;是资源分配的最小单位&#xff0c;他是操作系统进行资源分配…

Vue3【十二】09Computed计算属性

Vue3【十二】09Computed计算属性 计算属性 获取全名 这种方式是只读的不能修改 这样定义fullName是一个计算属性&#xff0c;可读可写 案例截图 目录结构 代码 Person.vue <template><div class"person"><h1>我是 Person 组件</h1>姓&…

Latex中表格(3)

Latex中的表格 一、多行或多列单元格 这篇主要说Latex中表格出现多行或者多列单元格的形式. 一、多行或多列单元格 可能用到的宏包 \usepackage{booktabs}\usepackage{multirow} 代码&#xff1a; \begin{table}[h!] \centering \caption{Your caption here} \begin{tabul…