【计算机系统基础bomb lab】CSAPP实验:Bomb Lab

【计算机系统基础bomb lab】CSAPP实验:Bomb Lab

  • CSAPP 实验:Bomb Lab
    • 实验内容简述
    • 实验环境
    • 实验过程:phase 1
      • phase 1 调试过程
    • 实验过程:phase 2
      • phase 2 调试过程
    • 实验过程:phase 3
      • phase 3 调试过程
    • 实验过程:phase 4
      • phase 4 调试过程
    • 实验过程:phase 5
      • phase 5 调试过程
    • 实验过程:phase 6
    • 一些其他收获

CSAPP 实验:Bomb Lab

实验内容简述

  • 作为实验目标的二进制炸弹 “Bomb Lab” Linux可执行程序包含了多个阶段(或关卡),在每个阶段程序要求输入一-个特定字符串,如果输入满足程序代码所定义的要求,该阶段的炸弹就被拆除了,否则程序输出 “炸弹爆炸BOOM!!!” 的提示并转到下一阶段再次等待对应的输入-实验的目标是设法得出解除尽可能多阶段的字符串。
  • 为完成二进制炸弹拆除任务,需要通过反汇编并分析可执行炸弹文件程序的机器代码或使用 gdb 调试器跟踪机器代码的执行,从中理解关键机器指令的行为和作用,进而设法推断拆除炸弹所需的目标字符串。

实验环境

64 位 linux 操作系统

实验过程:phase 1

  • 将可执行文件bomb 反汇编

    objdump -d bomb > bomb.asm
    
  • 查看源代码

    image-20220727104850796

    可以了解到 phase_1 是调用函数名

  • bomb.asm 里搜索 phase_1,阅读源码含义

    image-20220727105926060

phase 1 调试过程

  • 使用 gdb 调试,将断点设置在 phase_1 ,即 0x400ee0。运行后随便输入后查看 0x402400 的内容

    (gdb) b *0x400ee0
    Breakpoint 1 at 0x400ee0
    (gdb) r
    Starting program: /mnt/d/OneDrive/csapp/bomb/bomb 
    Welcome to my fiendish little bomb. You have 6 phases with
    which to blow yourself up. Have a nice day!
    dsasd 
    
    Breakpoint 1, 0x0000000000400ee0 in phase_1 ()
    (gdb) i r rip
    rip            0x400ee0            0x400ee0 <phase_1>
    (gdb) si
    0x0000000000400ee4 in phase_1 ()
    (gdb) si
    0x0000000000400ee9 in phase_1 ()
    (gdb) x/s 0x402400
    0x402400:       "Border relations with Canada have never been better."
    

    image-20220727111507095

  • 可以从上面了解到字符串的答案就是: Border relations with Canada have never been better.

实验过程:phase 2

  • 查看 phase 2 汇编代码

    0000000000400efc <phase_2>:
      400efc:	55                   	push   %rbp
      400efd:	53                   	push   %rbx
      400efe:	48 83 ec 28          	sub    $0x28,%rsp
      400f02:	48 89 e6             	mov    %rsp,%rsi
      ----------------------分割线----------------------------------------;上面是函数的初始化
      400f05:	e8 52 05 00 00       	callq  40145c <read_six_numbers>;读取六个数字
      400f0a:	83 3c 24 01          	cmpl   $0x1,(%rsp);拿第一个数字与 1 进行比较
      400f0e:	74 20                	je     400f30 <phase_2+0x34>;如果相等就跳转到 400f30
      400f10:	e8 25 05 00 00       	callq  40143a <explode_bomb>;否则,爆炸
      400f15:	eb 19                	jmp    400f30 <phase_2+0x34>
      400f17:	8b 43 fc             	mov    -0x4(%rbx),%eax ;将第一个数字赋给 eax
      400f1a:	01 c0                	add    %eax,%eax;将第一个数字*2
      400f1c:	39 03                	cmp    %eax,(%rbx);将当前数字(eax) 与后一个数字 (rbx) 比较
      400f1e:	74 05                	je     400f25 <phase_2+0x29>;如果相等,就跳转到400f25
      400f20:	e8 15 05 00 00       	callq  40143a <explode_bomb>;否则爆炸
      400f25:	48 83 c3 04          	add    $0x4,%rbx;循环迭代条件
      400f29:	48 39 eb             	cmp    %rbp,%rbx;rbp 是栈底,也就是数组最后一个元素,作为循环判断的最后一个条件
      400f2c:	75 e9                	jne    400f17 <phase_2+0x1b>;如果没遍历到最后一个数字,就跳转到循环内400f17
      400f2e:	eb 0c                	jmp    400f3c <phase_2+0x40>;循环结束,跳转到最后函数结束后的处理400f3c
      400f30:	48 8d 5c 24 04       	lea    0x4(%rsp),%rbx ;将第二个数赋值给 rbx
      400f35:	48 8d 6c 24 18       	lea    0x18(%rsp),%rbp ;设置栈底,就是最后一个数字(0x18 == 24,输入的是6个数,所以是最后一个数)
      400f3a:	eb db                	jmp    400f17 <phase_2+0x1b>;跳到 400f17 进入循环开始的地方
      ----------------------分割线----------------------------------------;之后都是函数结束后的处理
      400f3c:	48 83 c4 28          	add    $0x28,%rsp
      400f40:	5b                   	pop    %rbx
      400f41:	5d                   	pop    %rbp
      400f42:	c3                   	retq  
    

phase 2 调试过程

  • 设置断点

    (gdb) b phase_2
    Breakpoint 1 at 0x400efc
    
  • 运行并输入phase 1 的答案与 phase 2 推测出的答案

    image-20220730105854670

  • 一直单步汇编运行,执行到 400f0a: cmpl $0x1,(%rsp); 此步说明第一个数字必须为1

    image-20220730111649035

  • 比较第一个数字是否为 1

    (gdb) x/wd $rsp
    0x7ffffffedd70: 1
    

    可以看到,rsp 的值为1 ,就是我们输入的第一个值:1

    由此,通过 je 400f30 可知,两数相等,故跳转到 400f30

    image-20220730111959643

  • 通过单步汇编调试,查看rsp 的值

    (gdb) si
    0x0000000000400f30 in phase_2 ()
    (gdb) si
    0x0000000000400f35 in phase_2 ()
    

    image-20220730112217810

    由此可以得出,rbx 此时存放的是第二个数字 2rbp 作为栈底,是最后一个数字的结尾,存放的是 4199473 这个数字。

  • 继续单步汇编调试,进入循环400f17

    image-20220730112624184

    此时执行完 400f17:mov -0x4(%rbx),%eax eax` 的值为 1。如下图

    image-20220730113150925

    继续单步调试,执行到 add %eax,%eax ,对 eax进行 乘 2 的操作。 此时 eax 为2 ,与我们输入的第二个数( rbx 存放) 相等

    image-20220730113939792

    image-20220730114119156

  • 继续单步汇编调试,跳转到 400f25:add $0x4,%rbx

    image-20220730114459317

    image-20220730114515282

    执行完 add $0x4,%rbx ,则选择了数组的下一个数字,判断循环是否是否结束。

  • 之后一直执行到结束。

    image-20220730135934202

  • 所以答案为:1 2 4 8 16 32

实验过程:phase 3

  • 查看 phase_3 汇编代码:
0000000000400f43 <phase_3>:
  400f43:	48 83 ec 18          	sub    $0x18,%rsp
  400f47:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx ;第二个参数
  400f4c:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx ;第一个参数
  400f51:	be cf 25 40 00       	mov    $0x4025cf,%esi ;系统自带字符串"%d %d",2个参数
  400f56:	b8 00 00 00 00       	mov    $0x0,%eax
  ;----------------------------------------------------------------
  400f5b:	e8 90 fc ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  400f60:	83 f8 01             	cmp    $0x1,%eax ;eax 与 1 比较。返回的是输入的参数个数
  400f63:	7f 05                	jg     400f6a <phase_3+0x27> ;如果大于 1,跳转400f6a
  400f65:	e8 d0 04 00 00       	callq  40143a <explode_bomb>
  400f6a:	83 7c 24 08 07       	cmpl   $0x7,0x8(%rsp) ;比较 7 与 第一个参数大小
  400f6f:	77 3c                	ja     400fad <phase_3+0x6a> ;如果第一个参数大于7,爆炸
  400f71:	8b 44 24 08          	mov    0x8(%rsp),%eax ;把第一个参数赋给 eax
  400f75:	ff 24 c5 70 24 40 00 	jmpq   *0x402470(,%rax,8) ;跳转到 rax*8 + *0x402470
  400f7c:	b8 cf 00 00 00       	mov    $0xcf,%eax
  400f81:	eb 3b                	jmp    400fbe <phase_3+0x7b>
  400f83:	b8 c3 02 00 00       	mov    $0x2c3,%eax
  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 ;eax 为311(十进制)
  400fbe:	3b 44 24 0c          	cmp    0xc(%rsp),%eax ;比较第二个参数与 eax
  400fc2:	74 05                	je     400fc9 <phase_3+0x86>;如果成功,拆弹成功
  400fc4:	e8 71 04 00 00       	callq  40143a <explode_bomb>
  ;----------------------------------------------------------------
  400fc9:	48 83 c4 18          	add    $0x18,%rsp
  400fcd:	c3                   	retq   

分析:

我这个都是自己调试出来看到参数的,最开始第一个参数与第二个参数理解有误。结果最开始输入的竟然是第二个参数

首先看到了调用函数是 __isoc99_sscanf 可以从中得知,返回的 eax 为输入格式的个数,而最开始 0x4025cf 的字符串就是 %d %d,可以了解到参数个数为 2 。

之后让第一个参数与 7 比较,可以猜到第一个参数是一个选择做一个选择,选择是下面7个跳转(类似于 switch)。

最后可以在 400f75:jmpq *0x402470(,%rax,8) 观测,程序的跳转地址情况。

phase 3 调试过程

  • 设置断点并运行

    此时的 ans.txt如下:

    Border relations with Canada have never been better.
    1 2 4 8 16 32
    1 311
    
    

    image-20220730172753721

  • 单步汇编调试,直到执行完 400f5b:callq 400bf0 <__isoc99_sscanf@plt>

    image-20220730203508519

  • 单步汇编调试,执行到 400f75:jmpq *0x402470(,%rax,8),观察对应 0x402470 开始的 8 个单元对应的值。

    image-20220731104055183

    rax 保存的是第一个参数的值,我们输入的是 1,所以计算机结果是 0x402470 + 1 * 80x402478 单元对应的值 0x400fb9

  • 单步汇编调试,验证进入的单元。

    image-20220731104919753

  • 要使第一个第二个参数与 0x137相等,即十进制 311,最后拆除炸弹。

    image-20220731105331630

实验过程:phase 4

0000000000400fce <func4>:
  400fce:	48 83 ec 08          	sub    $0x8,%rsp
  400fd2:	89 d0                	mov    %edx,%eax ;eax = 14 (phase_4 输入的第三个参数)
  400fd4:	29 f0                	sub    %esi,%eax ;eax = 14 - 0 = 14 (第二个参数减去第三个参数)
  400fd6:	89 c1                	mov    %eax,%ecx ;ecx = 14
  400fd8:	c1 e9 1f             	shr    $0x1f,%ecx;逻辑右移31 位,ecx 为0
  400fdb:	01 c8                	add    %ecx,%eax ;eax = 14
  400fdd:	d1 f8                	sar    %eax ;算术右移1位,即÷2  eax = 7
  400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx ;ecx = 7
  400fe2:	39 f9                	cmp    %edi,%ecx ;比较第一传入参数与 ecx = 7
  400fe4:	7e 0c                	jle    400ff2 <func4+0x24> ;如果小于等于7,跳转400ff2
  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx ;
  400fe9:	e8 e0 ff ff ff       	callq  400fce <func4>
  400fee:	01 c0                	add    %eax,%eax
  400ff0:	eb 15                	jmp    401007 <func4+0x39>
  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax ;eax = 0
  400ff7:	39 f9                	cmp    %edi,%ecx ;比较 7 与第一个传入参数的大小
  400ff9:	7d 0c                	jge    401007 <func4+0x39>;如果第一个传入参数的大小大于等于7就正常返回
  400ffb:	8d 71 01             	lea    0x1(%rcx),%esi
  400ffe:	e8 cb ff ff ff       	callq  400fce <func4>
  401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401007:	48 83 c4 08          	add    $0x8,%rsp
  40100b:	c3                   	retq    
  
000000000040100c <phase_4>:
  40100c:	48 83 ec 18          	sub    $0x18,%rsp
  401010:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx;第二个参数
  401015:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx;第一个参数
  40101a:	be cf 25 40 00       	mov    $0x4025cf,%esi;"%d %d"
  40101f:	b8 00 00 00 00       	mov    $0x0,%eax
  ;------------------------------------------------------------------------------------------
  401024:	e8 c7 fb ff ff       	callq  400bf0 <__isoc99_sscanf@plt>
  401029:	83 f8 02             	cmp    $0x2,%eax;输入的参数个数是不是两个
  40102c:	75 07                	jne    401035 <phase_4+0x29>;不是,爆炸
  40102e:	83 7c 24 08 0e       	cmpl   $0xe,0x8(%rsp);将第一个参数和 0xe 比较
  401033:	76 05                	jbe    40103a <phase_4+0x2e>;如果小于等于,就继续
  401035:	e8 00 04 00 00       	callq  40143a <explode_bomb>;否则爆炸
  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx;调用函数第三个传入的实参,14
  40103f:	be 00 00 00 00       	mov    $0x0,%esi;调用函数第二个传入实参,0
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi;调用函数第一个传入实参,也是输入的第一个参数
  401048:	e8 81 ff ff ff       	callq  400fce <func4>
  40104d:	85 c0                	test   %eax,%eax ;递归返回值为0
  40104f:	75 07                	jne    401058 <phase_4+0x4c>;否则爆炸
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp) ;经过func4 递归后,输入的第二个参数应为0 
  401056:	74 05                	je     40105d <phase_4+0x51>;第二个参数不为0就爆炸
  401058:	e8 dd 03 00 00       	callq  40143a <explode_bomb>
  ;------------------------------------------------------------------------------------------
  40105d:	48 83 c4 18          	add    $0x18,%rsp
  401061:	c3                   	retq   

分析:

首先从callq 400bf0 <__isoc99_sscanf@plt> 可以知道又是调用sscanf,上面的内容就是输入的参数。 401010:lea 0xc(%rsp),%rcx401015:lea 0x8(%rsp),%rdx 可以得知,这是传入的第二个和第一个参数,传入的是两个参数。

之后从 40103a:mov $0xe,%edx 可以得知,接下来这三行都是传递实参,为调用 func4 做准备。 func4 被调用的过程可以分析出第一个数字小于等于7,之后完成以后回到 phase_4401051:cmpl $0x0,0xc(%rsp) 可以得知第二个参数为 0。故测试以下 7 0

phase 4 调试过程

  • 设置断点,并运行至 phase_4

    此时的 ans.txt如下:

    Border relations with Canada have never been better.
    1 2 4 8 16 32
    1 311
    7 0
    
    

    image-20220802151932815

  • 执行到调用 func4 的位置,中间确认输入参数

    image-20220803145124535

  • 重点是 func4 的实现,这个过程需要的确定各个寄存器的值是否符合预期

    image-20220803150218152

  • 最后拆弹成功

    image-20220803150307235

实验过程:phase 5

0000000000401062 <phase_5>:
  401062:	53                   	push   %rbx
  401063:	48 83 ec 20          	sub    $0x20,%rsp
  401067:	48 89 fb             	mov    %rdi,%rbx ;第一个参数 -> rbx
  40106a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax ;接下来4行汇编是栈哨兵的检查(栈的保护机制,在编译时加上 -fno-stack-protector 就会被取消)
  401071:	00 00 
  401073:	48 89 44 24 18       	mov    %rax,0x18(%rsp) ;将哨兵的值赋值到栈里
  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 ;字符串长度必须为6
  401082:	74 4e                	je     4010d2 <phase_5+0x70>
  401084:	e8 b1 03 00 00       	callq  40143a <explode_bomb>
  401089:	eb 47                	jmp    4010d2 <phase_5+0x70>
  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx ;ecx = Mem[rbx + rax(0) * 1] 此时ecx 指向字符串第一个字符a
  40108f:	88 0c 24             	mov    %cl,(%rsp) ;取字符串的低8位值入栈
  
  401092:	48 8b 14 24          	mov    (%rsp),%rdx ;输入字符串作为值(具体的ascii),之后在一长串字符串将输入字符串的值作为偏移量
  401096:	83 e2 0f             	and    $0xf,%edx ;与1111 取与运算后作为偏移量,也就是取低4位
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx ;edx = 'a' 0x4024b0 = "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"
  ;分别取第10位 f(I:74->0100 1001), 第 16 位 l(O:79->0100 1111), 第 15 位 y(N:78->0100 1110),第 6 位 e(E:69->0100 0101),第 7 位 r(F:70->0100 0110),第 8 位 s(G:71->0100 0111)
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1) ;rax = 0
  4010a4:	48 83 c0 01          	add    $0x1,%rax ;rax = 1
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax ;循环终止的判断条件
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>
  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi ;第二个传入参数 0x40245e = "flyers"
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi ;第一个传入参数,rdi 0x10(%rsp)是保存的我们输入的地址,上面的操作就是将每个字符串赋值到0x10(%rsp)起始的地址单元里面
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal> ;要使得二者相等
  4010c2:	85 c0                	test   %eax,%eax
  4010c4:	74 13                	je     4010d9 <phase_5+0x77> ;相等就结束
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>
  4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)
  4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>
  4010d2:	b8 00 00 00 00       	mov    $0x0,%eax ;初始化 eax 为0 
  4010d7:	eb b2                	jmp    40108b <phase_5+0x29>
  ;---------------------------------------------------------------------------------
  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   

分析:

首先能够判断是要输入 6 个字符串,然后随便输入6个字符进行调试,建议使用规律不同的字符串,这样能够知道传递的参数位置。我使用的是 abcdef

接着当代码运行到 40108b:movzbl (%rbx,%rax,1),%ecx 以后,表示程序进入到循环体内,最后退出的条件是 rax == 6 ,而 rax 的初始值为 0, 这个意思就是遍历完我们输入的字符串,循环体内的意思是取我们输入字符的值的低四位的值,我们输入的是字符,也就是该字符对应 ascii 码的低四位。然后利用这个值作为一个索引,去 0x4024b0 这个长的字符串中寻找 flyers 对应的下标,注意我注释里面添加说明的是第几位,但是具体下标要-1操作。

当代码运行到 4010b3:mov $0x40245e,%esi 的时候,可以看到这个就是在传递参数了,作为第二个实参,是系统赋予的,要将这个字符串flyers0x10(%rsp) 的值(也就是上面提及到的低四位ascii码作为索引取出来的字符)进行比较,必须要二者相等。

综上,得出更新的ans.txt

Border relations with Canada have never been better.
1 2 4 8 16 32
1 311
7 0
IONEFG

phase 5 调试过程

  • 设置断点在 phase_5 并运行

    image-20220804164357374

  • 运行到进入循环体 40108b:movzbl (%rbx,%rax,1),%ecx

    image-20220804165103007

    可以看到movzbl (%rbx,%rax,1),%ecx,注意 rbx 最开始就用存储输入的字符串,此时rax作为循环体初始变量 为 0。

  • 执行到 401096 以后,完成了取第一个值的低四位的目的,此时再查看取到值是否为 I 的低四位 ascii 码的值 9

    image-20220804165345097

    可以看到值为 9,证明计算没错,之后可以直接运行 n,直到结束。

  • 运行 n,直接验证是否正确

    image-20220804165447748

实验过程:phase 6

实在是太长了,暂时放弃😞

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
  401100:	49 89 e5             	mov    %rsp,%r13
  401103:	48 89 e6             	mov    %rsp,%rsi
  401106:	e8 51 03 00 00       	callq  40145c <read_six_numbers>;读取六个数
  ;------------------------------------------------------------------------------------
  40110b:	49 89 e6             	mov    %rsp,%r14 ;r14 最后一个数地址
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d 
  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax ;第一个数字
  40111b:	83 e8 01             	sub    $0x1,%eax ; eax是返回的值的大小
  40111e:	83 f8 05             	cmp    $0x5,%eax ; 第一个数字小于等于5就继续
  401121:	76 05                	jbe    401128 <phase_6+0x34> 
  401123:	e8 12 03 00 00       	callq  40143a <explode_bomb> 
  401128:	41 83 c4 01          	add    $0x1,%r12d ; 循环迭代器 i
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d ; 循环体,条件为6
  401130:	74 21                	je     401153 <phase_6+0x5f> 
  401132:	44 89 e3             	mov    %r12d,%ebx
  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 <phase_6+0x51>
  401140:	e8 f5 02 00 00       	callq  40143a <explode_bomb>
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 <phase_6+0x41>
  40114d:	49 83 c5 04          	add    $0x4,%r13
  401151:	eb c1                	jmp    401114 <phase_6+0x20>
  ; ---------------------------------------------------------------------
  ; 循环分析比较麻烦,401151会跳转到401114这个是最外层,完成了%r13 + 0x4。即arr++
  ; %13初始的时候是数组首地址。
  ;  按照思路逻辑如下
  ;  for( i=0;i<6;i++ ){
  ;      if(arr[i] - 1 > 5) bomb()
  ;      for(j=i+1;j<=5;j++){
  ;          if(arr[j] == arr[i]) bomb()
  ;      }
  ;  }
  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi 
  401158:	4c 89 f0             	mov    %r14,%rax
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx
  401160:	89 ca                	mov    %ecx,%edx
  401162:	2b 10                	sub    (%rax),%edx
  401164:	89 10                	mov    %edx,(%rax)
  401166:	48 83 c0 04          	add    $0x4,%rax
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 <phase_6+0x6c>
; -----------------------------------------------------------------
; rsi的值为rsp+0x18 = rsp+0x4*6即数组的结束位置。
; %rax = %r14往前看,是输入的数组首地址。
; 
; for(int i=0;i<6;i++){
;     arr[i] = 7 - arr[i];
; }
  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 <phase_6+0xa3>
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 <phase_6+0x82>
  401181:	eb 05                	jmp    401188 <phase_6+0x94>

  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx

  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab <phase_6+0xb7>

  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 <phase_6+0x8f>
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 <phase_6+0x82>
  ; --------------------------------------------------------------------------
  ; 借助流程图分析我们得到如下逻辑伪代码
  ; for(int i=0;i<6;i++){
  ;     int cnt = arr[i];
  ;     int add = 0x6032d0;
  ;     for(int j=1;j<arr[i];j++){
  ;         add = *(add+8);
  ;     *(rsp + 2*i + 0x20) = add;  // 8 字节 
  ; }
  ; 查看下*(add+8)中的数据
  ; (gdb) x 0x6032d0+0x8
  ; 0x6032d8 <node1+8>:   0x006032e0
  ; (gdb) x 0x6032e0+0x8 
  ; 0x6032e8 <node2+8>:   0x006032f0
  ; (gdb) x 0x6032f0+0x8
  ; 0x6032f8 <node3+8>:   0x00603300
  ; (gdb) x 0x603300+0x8
  ; 0x603308 <node4+8>:   0x00603310
  ; (gdb) x 0x603310+0x8
  ; 0x603318 <node5+8>:   0x00603320
  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
  4011ba:	48 89 d9             	mov    %rbx,%rcx
  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 <phase_6+0xde>
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd <phase_6+0xc9>
  ;   --------------------------------------------------------------- 
  ; narr 数组存放数据范围上面分析为 0x6032d0 - 0x603320 
  ; int *narr = {};
  ; for(int i=1;i<6;i++){
  ;     *(narr[i-1]+0x8) = narr[i]
  ; }
  ; 将narr[i-1]+0x8内存地址数据存放为narr[i]
  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
  4011d9:	00 
  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax
  4011e3:	8b 00                	mov    (%rax),%eax
  4011e5:	39 03                	cmp    %eax,(%rbx)
  4011e7:	7d 05                	jge    4011ee <phase_6+0xfa>
  4011e9:	e8 4c 02 00 00       	callq  40143a <explode_bomb> 
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx 
  4011f2:	83 ed 01             	sub    $0x1,%ebp
  4011f5:	75 e8                	jne    4011df <phase_6+0xeb>
  ;----------------------------------------------------------------------------------------
; eax    :  *(narr[0]+0x8) => narr[1] => *(narr[1])
; (%rbx) :  *(narr[0])
; for(int i=1;i<=5;i++){
;     if( *(narr[i-1]) < *(narr[i]) ) bomb()        
; }
  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   

一些其他收获

  • 下载一些 cmucode

    wget -r -np -nH --cut-dirs=3 -R index.html 网站地址
    

    e.g.

    wget -r -np -nH --cut-dirs=3 -R index.html https://www.cs.cmu.edu/~213/code/06-machine-control/
    

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

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

相关文章

Java字符串知多少:String、StringBuffer、StringBuilder

一、String 1、简介 String 是 Java 中使用得最频繁的一个类了&#xff0c;不管是作为开发者的业务使用&#xff0c;还是一些系统级别的字符使用&#xff0c; String 都发挥着重要的作用。String 是不可变的、final的&#xff0c;不能被继承&#xff0c;且 Java 在运行时也保…

C++的cout详解

2023年5月20日&#xff0c;周六早上&#xff1a; 我发现我找不到非常详细的cout类的成员函数&#xff0c;只好自己写了。 不定期更新。 cout的继承关系 cout类继承自ostream类&#xff0c;ostream类继承自ios类&#xff0c;ios类继承自ios_base类 cout类拥有的所有成员函数 …

pg事务:事务的处理

事务的处理 事务块 从事务形态划分可分为隐式事务和显示事务。隐式事务是一个独立的SQL语句&#xff0c;执行完成后默认提交。显示事务需要显示声明一个事务&#xff0c;多个sql语句组合到一起称为一个事务块。 事务块通过begin&#xff0c;begin transaction&#xff0c;st…

QT学习记录(三)绘图

按照下面两个教程学习 QT学习教程&#xff08;全面&#xff09;_Strive--顾的博客-CSDN博客_qt学习 天山老妖S的博客_QT开发(3)_51CTO博客 1、绘图 VC项目右键增加QT GUI Class&#xff0c;在QT Designer中编辑DlgDraw.ui 在DlgDraw中重载函数 void DlgDraw::paintEvent(Q…

C++之堆排

堆排的原理和结构&#xff1a; 堆排序是一种常见的排序算法&#xff0c;基于堆这种数据结构实现。堆是一种特殊的树形数据结构&#xff0c;它满足以下两个条件&#xff1a; 堆是一棵完全二叉树。 堆的任意节点的值&#xff0c;都必须大于等于&#xff08;或小于等于&#xff0…

基于ROS2的costmap中Obstacle Layer中对障碍物信息的增加与删除机制的方案调研。

文章目录 1.背景2.目标3. 障碍物信息添加方式发送数据的数据结构与接收数据的数据结构 4. 障碍物清理机制4.1 可调参数4.2 优化光追算法4.3 障碍物跟踪 1.背景 基于costmap地图&#xff0c;使用navigation导航时&#xff0c;会出现由于激光雷达/图像测距的局限性&#xff0c; …

由浅入深Netty粘包与半包解决方案

目录 1 粘包现象2 半包现象3 现象分析4 解决方案4.1 方法1&#xff1a;短链接4.2&#xff1a;方法2&#xff1a;固定长度4.3 方法3&#xff1a;固定分隔符4.4 方法4&#xff1a;预设长度 1 粘包现象 服务端代码 public class HelloWorldServer {static final Logger log Logg…

【libcurl 】win32 构建 Release版本 修改cmakelist 链接openssl1.1.*

以下库均已MD的构建以vs2019 V142构建MD构建 直接换用了一个openssl库,libcurl连接报错 $(ProjectDir)..\..\..\3rdparty\openssl\xdw_openssl1_1_1\lib\win32\libcrypto.lib

【Unity】 UI自适应案例

UI自适应案例 案例一:背包自动布局1. 创建背包面板2. 背包子项自动布局3. C#代码:动态添加子项到背包中案例二:文字自适应高度1. 创建文字面板2. 组件基本设置3. C#代码:动态更新文字并自适应高度案例一:背包自动布局 需求:动态添加背包组件,设定每行特定个数并自动匹配…

C++学习之路-变量和基本内置类型

变量和基本内置类型 一、基本内置类型1.1 算数类型1.2 带符号类型和无符号类型1.3 类型转换含有无符号类型的表达式 1.4 字面值常量整形和浮点型字面值字符和字符串字面值转义序列指定字面值的类型 二、变量2.1 变量的定义初始化列表初始化默认初始化 2.2 变量声明和定义的关系…

彻底理解粘性定位 - position: sticky(IT枫斗者)

彻底理解粘性定位 - position: sticky 介绍 粘性定位可以被认为是相对定位(position: relative)和固定定位(position: fixed)的混合。元素在跨越特定阈值前为相对定位&#xff0c;之后为固定定位。例如: .sticky-header { position: sticky; top: 10px; }在 视口滚动到元素…

python处理字符串、文本实例及注释

1、多个界定符切割字符串 代码 line = asdf fjdk; afed, fjek,asdf, foo import re re.split(r[;,\s]\s*, line) 结果 在上面的例子中,分隔符可以是逗号,分号或者是空格,并且后面紧跟着任意个的空格。只要这个模式被找到,那么匹配的分隔符两边的实体都会被当成是结果中…

【数据结构与算法】- 期末考试

课程链接: 清华大学驭风计划 代码仓库&#xff1a;Victor94-king/MachineLearning: MachineLearning basic introduction (github.com) 驭风计划是由清华大学老师教授的&#xff0c;其分为四门课&#xff0c;包括: 机器学习(张敏教授) &#xff0c; 深度学习(胡晓林教授), 计算…

可算是熬出头了,测试6年,费时8个月,入职阿里,涨薪14K

前言 你的努力&#xff0c;终将成就无可替代的自己。 本科毕业后就一直从事测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事点点点的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要的状态。 一年半…

在 Android 手机上恢复出厂设置后恢复照片的 4 种简单方法(新方法)

“嗨&#xff0c;谁能帮我恢复我的照片&#xff0c;因为我不小心恢复了出厂设置&#xff0c;而且我没有做备份&#xff1f;几个月来我一直试图通过使用恢复软件来恢复我的照片&#xff0c;root 了一个深扫描&#xff0c;但没用……” 恢复出厂设置可以清除电子设备的所有信息并…

Linux安装Redis数据库,无需公网IP实现远程连接

文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 转发自cpolar内网穿透的文章&#xff1a;公网远程连接…

连续签到积分兑换试用流量主小程序开发

每日签到积分兑换试用流量主小程序开发 打卡兑奖小程序。用户签到活得积分。积分可以兑换商品。观看激励视频广告可以积分翻倍。 用户可以参加试用商品活动参加试用需要提交信息。可以通过分享方式直接获取试用资格。 以下是流量主小程序的功能列表&#xff1a; 广告位管理&a…

Java流程控制(一)

⭐ 控制语句⭐ 条件判断结构(选择结构)⭐ switch 语句 做任何事情事情都要遵循一定的原则&#xff0c;毕竟不以规矩&#xff0c;不成方圆&#xff0c;例如&#xff0c;到图书馆去借书&#xff0c;就必须要有借书证&#xff0c;并且借书证不能过期&#xff0c;这两个条件缺一不可…

Spring Boot 日志处理

Spring Boot 日志处理 Spring Boot 是一个非常流行的 Java 开发框架&#xff0c;它提供了简洁的配置和强大的开发工具。日志是应用程序中必不可少的一部分&#xff0c;因为它可以帮助开发人员进行调试和故障排除。Spring Boot 提供了多种日志框架&#xff0c;本文将重点介绍如…

Java泛型基本知识附面试题

一次平平无奇的面试 为什么要写这篇文档&#xff0c;主要就是在字节二面的时候&#xff0c;面试官提了这么一个问题 面试官&#xff1a;Java中的List<Integer>里有可能存String类型元素吗&#xff1f; 当时的我&#xff1a;应该…不可以吧&#xff0c;好像编译器会报错…