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,我们输入的内容作为参数1400ee9:	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)=1400f10:	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*=2400f1c:	39 03                	cmp    %eax,(%rbx)                    ;if((rbx)==eax)400f1e:	74 05                	je     400f25 <phase_2+0x29>          ;goto 400f25400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb>          ;else bomb!400f25:	48 83 c3 04          	add    $0x4,%rbx                      ;rbx=rbx+0x4400f29:	48 39 eb             	cmp    %rbp,%rbx                      ;if(rbx!=rbp)400f2c:	75 e9                	jne    400f17 <phase_2+0x1b>          ;goto 400f17:mov -0x4(%rbx),%eax400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40>          ;goto 400f3c return400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx                 ;rbx=rsp+0x4400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp                ;rbp=rsp+0x18400f3a:	eb db                	jmp    400f17 <phase_2+0x1b>          ;goto 400f17400f3c:	48 83 c4 28          	add    $0x28,%rsp400f40:	5b                   	pop    %rbx400f41:	5d                   	pop    %rbp400f42:	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;elsereturn ;}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=0400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt> ;读入输入400f60:	83 f8 01             	cmp    $0x1,%eax                    ;if(eax>1)吧   所以eax必为1400f63:	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=0x400f7c400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax                   ;eax=0xcf400f81:	eb 3b                	jmp    400fbe <phase_3+0x7b>        ;跳转到400fbe400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax                  ;eax=0x2c3400f88:	eb 34                	jmp    400fbe <phase_3+0x7b>         400f8a:	b8 00 01 00 00       	mov    $0x100,%eax400f8f:	eb 2d                	jmp    400fbe <phase_3+0x7b>400f91:	b8 85 01 00 00       	mov    $0x185,%eax400f96:	eb 26                	jmp    400fbe <phase_3+0x7b>400f98:	b8 ce 00 00 00       	mov    $0xce,%eax400f9d:	eb 1f                	jmp    400fbe <phase_3+0x7b>400f9f:	b8 aa 02 00 00       	mov    $0x2aa,%eax400fa4:	eb 18                	jmp    400fbe <phase_3+0x7b>400fa6:	b8 47 01 00 00       	mov    $0x147,%eax400fab:	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,%eax400fb7:	eb 05                	jmp    400fbe <phase_3+0x7b>400fb9:	b8 37 01 00 00       	mov    $0x137,%eax400fbe:	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                     ;栈指针自减8400fd2:	89 d0                	mov    %edx,%eax                     ;eax=edx=14400fd4:	29 f0                	sub    %esi,%eax                     ;eax=eax-esi  esi=0400fd6:	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+eax400fdd:	d1 f8                	sar    %eax                          ;把eax算数右移一位400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx            ;ecx=rax+rsi400fe2:	39 f9                	cmp    %edi,%ecx                     ;if(ecx<=edi)400fe4:	7e 0c                	jle    400ff2 <func4+0x24>           ;goto 400ff2400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx               ;else edx=rcx-1400fe9:	e8 e0 ff ff ff       	callq  400fce <func4>                ;goto 400fce 开始递归了  为了跳出递归,就有ecx=edi400fee:	01 c0                	add    %eax,%eax                     ;eax=2*eax400ff0:	eb 15                	jmp    401007 <func4+0x39>           ;goto 401007 返回400ff2:	b8 00 00 00 00       	mov    $0x0,%eax                     ;eax=0400ff7:	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+1400ffe:	e8 cb ff ff ff       	callq  400fce <func4>                ;goto 400fce 又开始递归了他奶奶滴401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax         ;eax=2*rax+1401007:	48 83 c4 08          	add    $0x8,%rsp                     ;恢复栈指针40100b:	c3                   	retq                                 ;返回0才行

000000000040100c <phase_4>:40100c:	48 83 ec 18          	sub    $0x18,%rsp                      ;栈指针自减24401010:	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=0401024:	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必然等于2401033:	76 05                	jbe    40103a <phase_4+0x2e>           ;goto 40403a 所以rdx<=0xe401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>           ;else bomb!40103a:	ba 0e 00 00 00       	mov    $0xe,%edx                       ;edx=0xe40103f:	be 00 00 00 00       	mov    $0x0,%esi                       ;esi=0401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi                  ;edi=edx=0xe rdx不是刚刚被赋值了吗? mov指令和lea指令的区别401048:	e8 81 ff ff ff       	callq  400fce <func4>                  ;goto func440104d:	85 c0                	test   %eax,%eax                       ;if(eax!=0)        所以eax=040104f:	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=num2401056:	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,%rsp401061:	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  把输入的参数地址传递到rbx40106a:	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)=rax401078:	31 c0                	xor    %eax,%eax                      ;将eax清040107a:	e8 9c 02 00 00       	callq  40131b <string_length>40107f:	83 f8 06             	cmp    $0x6,%eax                      ;if(eax==6)  所以字符串长度为6=eax401082:	74 4e                	je     4010d2 <phase_5+0x70>          ;goto 4010d2401084:	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 40108b4010ae:	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>          ;bomb4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)               ;什么意思?4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>          ;goto 4010d94010d2:	b8 00 00 00 00       	mov    $0x0,%eax                      ;eax=04010d7:	eb b2                	jmp    40108b <phase_5+0x29>          ;goto 40108b4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax4010e5:	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,%rsp4010f2:	5b                   	pop    %rbx4010f3:	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;}		elseboom!}elsegoto 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  把栈的起始地址传递给r13401103:	48 89 e6             	mov    %rsp,%rsi                      ;rsi=rsp  把栈的起始地址传递给rsi401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>      ;读入六个数,两个函数通过rsi进行参数传递40110b:	49 89 e6             	mov    %rsp,%r14                      ;r14=rsp40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d                     ;r12d=0401114:	4c 89 ed             	mov    %r13,%rbp                      ;rbp=r13  把栈的起始地址传递给rbp401117:	41 8b 45 00          	mov    0x0(%r13),%eax                 ;eax=(r13)  获取首地址的数据40111b:	83 e8 01             	sub    $0x1,%eax                      ;eax-=0x140111e:	83 f8 05             	cmp    $0x5,%eax                      ;if(eax<=0x5)401121:	76 05                	jbe    401128 <phase_6+0x34>          ;goto 401128:add  $0x1,%r12d401123:	e8 12 03 00 00       	callq  40143a <explode_bomb>          ;else boom!401128:	41 83 c4 01          	add    $0x1,%r12d                     ;r12d+=0x140112c:	41 83 fc 06          	cmp    $0x6,%r12d                     ;if(r12d==0x6)401130:	74 21                	je     401153 <phase_6+0x5f>          ;goto 401153:lea    0x18(%rsp),%rsi401132:	44 89 e3             	mov    %r12d,%ebx                     ;else ebx=r12d401135:	48 63 c3             	movslq %ebx,%rax                      ;rax=ebx401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax             ;eax=rsp+rax*440113b:	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+=0x1401148:	83 fb 05             	cmp    $0x5,%ebx                      ;if(ebx<=0x5)40114b:	7e e8                	jle    401135 <phase_6+0x41>          ;goto 401135:movslq %ebx,%rax40114d:	49 83 c5 04          	add    $0x4,%r13                      ;else r13+=0x4401151:	eb c1                	jmp    401114 <phase_6+0x20>          ;goto 401114:	mov    %r13,%rbp401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi                ;rsi=(rsp+0x18)401158:	4c 89 f0             	mov    %r14,%rax                      ;rax=r1440115b:	b9 07 00 00 00       	mov    $0x7,%ecx                      ;ecx=0x7401160:	89 ca                	mov    %ecx,%edx                      ;edx=ecx401162:	2b 10                	sub    (%rax),%edx                    ;edx-=(rax)401164:	89 10                	mov    %edx,(%rax)                    ;(rax)=edx401166:	48 83 c0 04          	add    $0x4,%rax                      ;rax+=0x440116a:	48 39 f0             	cmp    %rsi,%rax                      ;if(rax!=rsi)40116d:	75 f1                	jne    401160 <phase_6+0x6c>          ;goto 401160:mov    %ecx,%edx40116f:	be 00 00 00 00       	mov    $0x0,%esi                      ;else esi=0401174:	eb 21                	jmp    401197 <phase_6+0xa3>          ;goto 401197:ecx=rsp+rsi*1401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx                 ;rdx=(rdx+0x8)40117a:	83 c0 01             	add    $0x1,%eax                      ;eax+=0x140117d:	39 c8                	cmp    %ecx,%eax                      ;if(eax!=ecx)40117f:	75 f5                	jne    401176 <phase_6+0x82>          ;goto 401176401181:	eb 05                	jmp    401188 <phase_6+0x94>          ;else goto 101188401183:	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*240118d:	48 83 c6 04          	add    $0x4,%rsi                      ;rsi+=0x4401191:	48 83 fe 18          	cmp    $0x18,%rsi                     ;if(rsi==0x18)401195:	74 14                	je     4011ab <phase_6+0xb7>          ;goto 4011ab:0x20(%rsp),%rbx401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx             ;这是数组的形式吧  ecx=rsp+rsi*140119a:	83 f9 01             	cmp    $0x1,%ecx                      ;if(ecx<=0x1)40119d:	7e e4                	jle    401183 <phase_6+0x8f>          ;goto 401183:mov    $0x6032d0,%edx40119f:	b8 01 00 00 00       	mov    $0x1,%eax                      ;eax=0x14011a4:	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),%rdx4011ab:	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=rbx4011bd:	48 8b 10             	mov    (%rax),%rdx                    ;rdx=(rax)    rax里面存的是地址而不是普通数据了4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)                 ;(rcx+0x8)=rdx4011c4:	48 83 c0 08          	add    $0x8,%rax                      ;rax+=0x84011c8:	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=rdx4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>          ;goto 4011bd:	mov    (%rax),%rdx4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)                 ;(rdx+8)=04011d9:	00 4011da:	bd 05 00 00 00       	mov    $0x5,%ebp                      ;ebp=0x54011df:	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)>=eax4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb>          ;else boom4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx                 ;rbx=(rbx+0x8)4011f2:	83 ed 01             	sub    $0x1,%ebp                      ;ebp-=0x14011f5:	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    %rbx4011fc:	5d                   	pop    %rbp4011fd:	41 5c                	pop    %r124011ff:	41 5d                	pop    %r13401201:	41 5e                	pop    %r14401203:	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+0x4401467:	48 8d 46 14          	lea    0x14(%rsi),%rax                  ;rax=rsi+0x1440146b:	48 89 44 24 08       	mov    %rax,0x8(%rsp)                   ;(rsp+0x8)=rax401470:	48 8d 46 10          	lea    0x10(%rsi),%rax                  ;rax=rsi+0x10401474:	48 89 04 24          	mov    %rax,(%rsp)                      ;(rsp)=rax401478:	4c 8d 4e 0c          	lea    0xc(%rsi),%r9                    ;(rsi+0xc)=%r940147c:	4c 8d 46 08          	lea    0x8(%rsi),%r8                    ;(rsi+0x8)=%r8401480:	be c3 25 40 00       	mov    $0x4025c3,%esi                   ;esi=0x4025c3401485:	b8 00 00 00 00       	mov    $0x0,%eax                        ;eax=040148a:	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=6401494:	e8 a1 ff ff ff       	callq  40143a <explode_bomb>            ;else bomb!401499:	48 83 c4 18          	add    $0x18,%rsp40149d:	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);elsegoto 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,%rsp401208:	48 85 ff             	test   %rdi,%rdi                      ;if(rdi==0)40120b:	74 2b                	je     401238 <fun7+0x34>             ;goto 401238: eax=-1,准备return40120d:	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 401220401213:	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*eax40121e:	eb 1d                	jmp    40123d <fun7+0x39>             ;return 401220:	b8 00 00 00 00       	mov    $0x0,%eax                      ;eax=0401225:	39 f2                	cmp    %esi,%edx                      ;if(edx==esi)401227:	74 14                	je     40123d <fun7+0x39>             ;goto 40123d:return401229:	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+1401236:	eb 05                	jmp    40123d <fun7+0x39>             ;return 401238:	b8 ff ff ff ff       	mov    $0xffffffff,%eax               ;eax=-140123d:	48 83 c4 08          	add    $0x8,%rsp                    401241:	c3                   	retq   

0000000000401242 <secret_phase>:401242:	53                   	push   %rbx401243:	e8 56 02 00 00       	callq  40149e <read_line>401248:	ba 0a 00 00 00       	mov    $0xa,%edx                      ;edx=1040124d:	be 00 00 00 00       	mov    $0x0,%esi                      ;esi=0401252:	48 89 c7             	mov    %rax,%rdi                      ;rdi=rax401255:	e8 76 f9 ff ff       	callq  400bd0 <strtol@plt>40125a:	48 89 c3             	mov    %rax,%rbx                      ;rbx=rax40125d:	8d 40 ff             	lea    -0x1(%rax),%eax                ;eax=rax-1401260:	3d e8 03 00 00       	cmp    $0x3e8,%eax                    ;if(eax<=1000)401265:	76 05                	jbe    40126c <secret_phase+0x2a>     ;goto 40126c401267:	e8 ce 01 00 00       	callq  40143a <explode_bomb>          ;else boom!40126c:	89 de                	mov    %ebx,%esi                      ;esi=ebx40126e:	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=$0x6030f0401278:	83 f8 02             	cmp    $0x2,%eax                      ;if(eax==2)   所以func返回的eax=240127b:	74 05                	je     401282 <secret_phase+0x40>     ;goto 40127d40127d:	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    %rbx401292:	c3                   	retq   401293:	90                   	nop401294:	90                   	nop401295:	90                   	nop401296:	90                   	nop401297:	90                   	nop401298:	90                   	nop401299:	90                   	nop40129a:	90                   	nop40129b:	90                   	nop40129c:	90                   	nop40129d:	90                   	nop40129e:	90                   	nop40129f:	90                   	nop
00000000004015c4 <phase_defused>:4015c4:	48 83 ec 78          	sub    $0x78,%rsp                 ;rdp-=1204015c8:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax4015cf:	00 00 4015d1:	48 89 44 24 68       	mov    %rax,0x68(%rsp)            ;rsp[104]=rax4015d6:	31 c0                	xor    %eax,%eax                  ;eax=04015d8:	83 3d 81 21 20 00 06 	cmpl   $0x6,0x202181(%rip)        # 603760 <num_input_strings>4015df:	75 5e                	jne    40163f <phase_defused+0x7b>  ;如果输入数据的个数不等于6 return4015e1:	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) 如果输入数据个数不为3401602:	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=0401630:	e8 0d fc ff ff       	callq  401242 <secret_phase>        ;goto secret_phase401635:	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,%rax40164b:	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,%rsp401658:	c3                   	retq   401659:	90                   	nop40165a:	90                   	nop40165b:	90                   	nop40165c:	90                   	nop40165d:	90                   	nop40165e:	90                   	nop40165f:	90                   	nop

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Git】如何不管本地文件,强制git pull

要在 Git 中强制执行 git pull 操作&#xff0c;忽略本地文件的更改&#xff0c;可以按照以下步骤操作&#xff1a; 保存当前工作状态&#xff1a;如果你有未提交的更改&#xff0c;可以使用 git stash 将这些更改存储起来。 git stash强制拉取最新代码&#xff1a;使用 git re…

ffmpeg视频编码原理和实战-(2)视频帧的创建和编码packet压缩

源文件&#xff1a; #include <iostream> using namespace std; extern "C" { //指定函数是c语言函数&#xff0c;函数名不包含重载标注 //引用ffmpeg头文件 #include <libavcodec/avcodec.h> } //预处理指令导入库 #pragma comment(lib,"avcodec.…

9.抽象类和接口

抽象类 抽象类概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类 比如&#xff1a; 我…

error /var/lib/jenkins/workspace/*/node_modules/node-sass: Command failed.

原因&#xff1a;node-sass版本不一致 版本图&#xff1a; 解决方案&#xff1a; 进入到jenkins项目目录下&#xff0c;修改package.json文件 将7.0.1改成6.0.1版本

鸿蒙Ability Kit(程序框架服务)【UIExtensionAbility】

UIExtensionAbility 概述 [UIExtensionAbility]是UI类型的ExtensionAbility组件&#xff0c;需要与[UIExtensionComponent]一起配合使用&#xff0c;开发者可以在UIAbility的页面中通过UIExtensionComponent嵌入提供方应用的UIExtensionAbility提供的UI。UIExtensionAbility会…

匈牙利匹配算法

一 什么是匈牙利匹配算法 匈牙利算法是一种解决二分图最大匹配问题的算法。在二分图中&#xff0c;将左边的点称为X集合&#xff0c;将右边的点称为Y集合&#xff0c;我们需要在X集合和Y集合之间建立一个双向边集合&#xff0c;使得所有的边都不相交。如果我们能够找到一个最大…

C++ Thread多线程并发记录(8)生产者-消费者模型与信号量(条件变量)

一.生产者-消费者模型 生产者-消费者模型是一个十分经典的多线程并发协作模式。所谓的生产者-消费者&#xff0c;实际上包含了两类线程&#xff0c;一种是生产者线程用于生产数据&#xff0c;另一种是消费者线程用于消费数据&#xff0c;为了解耦生产者和消费者的关系&#xff…

Opencv 色彩空间

一 核心知识 色彩空间变换&#xff1b; 像素访问&#xff1b; 矩阵的、-、*、、&#xff1b; 基本图形的绘制 二 颜色空间 RGB&#xff1a;人眼的色彩空间&#xff1b; OpenCV默认使用BGR&#xff1b; HSV/HSB/HSL; YUV(视频); 1 RGB 2 BGR 图像的多种属性 1 访问图像(Ma…

人工智能大模型的进化之路:探索如何让它们变得更“聪明”

一、引言 在人工智能&#xff08;AI&#xff09;领域&#xff0c;大模型凭借其强大的处理能力和广泛的应用前景&#xff0c;已经成为研究的热点。然而&#xff0c;尽管这些模型在多个领域展现出了惊人的能力&#xff0c;但它们仍然面临着理解力、泛化能力和适应性等方面的挑战…

学习Java的日子 Day51 数据库,DDL,DML

Day51 MySQL 1.数据库 数据库&#xff08;database&#xff09;就是一个存储数据的仓库。为了方便数据的存储和管理&#xff0c;它将数据按照特定的规律存储在磁盘上。通过数据库管理系统&#xff0c;可以有效地组织和管理存储在数据库中的数据 MySQL就是数据库管理系统&#…

VisionPro界面乱序或字体排版异常,字体不适应界面窗口大小

很多人在安装Visionpro后都遇到了一个界面显示异常的问题&#xff1a; 打开visionpro后字体大小不统一,显示不全或显示在其他窗口之上&#xff0c;如下所示。 解决该问题&#xff0c;首先关闭Visionpro软件&#xff0c;右击软件选择属性->兼容性。勾选兼容模式下面的方框。…

WebStorm 2024.1.1 Mac激活码 前端开发工具集成开发环境(IDE)

WebStorm 2024 Mac激活码 搜索Mac软件之家下载WebStorm 2024 Mac激活版 WebStorm 2024 功能介绍 WebStorm 2024是由JetBrains公司开发的一款专为前端开发设计的集成开发环境&#xff08;IDE&#xff09;。它提供了一整套功能&#xff0c;旨在提高Web开发者的工作效率和代码质…

线性电源运放驱动调整管的方案仿真

群里有人的电路板做出来电压不稳&#xff0c;加负载就掉电压。我对这个运放的工作状态不是很理解&#xff0c;所以仿真了一下。结果却是稳定的。他用12v给运放供电&#xff0c;要求输出10.5. 从仿真看。12运放供电只能输出9v。而且还是到了运放的极限。所以通过仿真后确定怀疑路…

LLM的基础模型4:初识Embeddings

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

软件安全测评之漏洞扫描、代码审计详情介绍

在当今数字化时代&#xff0c;软件已渗透到我们生活的方方面面。然而&#xff0c;与软件的广泛应用相伴随的是各种安全威胁的出现。为了保障用户和企业的信息安全&#xff0c;软件安全测评变得至关重要。而漏洞扫描和代码审计作为安全测评中的重要过程&#xff0c;卓码测评小编…

【线性代数】SVDPCA

用最直观的方式告诉你&#xff1a;什么是主成分分析PCA_哔哩哔哩_bilibili 奇异值分解singular value decomposition&#xff0c;SVD principal component analysis,PCA 降维操作 pca就是降维后使得信息损失最小 投影在坐标轴上的点越分散&#xff0c;信息保留越多 pca的实现…

2-异常-FileNotFoundException(三种不同的报错)

2-异常-FileNotFoundException(三种不同的报错) 更多内容欢迎关注我&#xff08;持续更新中&#xff0c;欢迎Star✨&#xff09; Github&#xff1a;CodeZeng1998/Java-Developer-Work-Note 技术公众号&#xff1a;CodeZeng1998&#xff08;纯纯技术文&#xff09; 生活公众…

关于认证协议

本地用户认证 本地认证的意思就是&#xff0c;我们的电脑上存储着自己的账号密码&#xff0c;无论电脑是否联网&#xff0c;只要能开机&#xff0c;就可以输入账号密码登录到电脑中&#xff0c;工作组就是采用本地认证 本地认证流程 winlogon.exe -> 接收用户输入 -> …

【异常分析:四分位距与3σ原则】

文章目录 前言四分位距&#xff08;IQR&#xff09;3σ原则使用步骤计算四分位距应用3σ原则 代码 前言 异常分析的目标是识别数据中的异常值&#xff0c;这些异常值可能是由于错误的记录、设备故障或者其他未知原因导致的。四分位距&#xff08;interquartile range, IQR&…

H5进度条样式,自定义进度条

进度条样式预览 实现代码&#xff1a; <view class"mainPro"><view class"proBg"><view class"proDetail" :style"{ width: ${schedule}% }"></view></view><view class"proTxt">完成进…