• boom lab分析


    单步调试:

    (gdb) bt
    #1  0x0000000000401347 in strings_not_equal ()
    #2  0x0000000000400eee in phase_1 ()
    #3  0x0000000000400e3f in main (argc=, argv=) at bomb.c:74
    

    这里的执行流程为:

        /* Hmm...  Six phases must be more secure than one phase! */
        input = read_line();             /* Get input                   */
        phase_1(input);                  /* Run the phase               */
        phase_defused();                 /* Drat!  They figured it out!
    				      * Let me know how they did it. */
        printf("Phase 1 defused. How about the next one?\n");
    

    可以发现只有phase_1正常返回的时候,才会执行下面的phase_defused()函数将阶段1的炸弹拆除。现在就想让phase_1正常执行完,那就需要让此函数中所有不正常的跳转都不执行。

    首先查看phase_1函数的汇编代码:

    (gdb) disassemble phase_1
    Dump of assembler code for function phase_1:
       0x0000000000400ee0 <+0>:     sub    $0x8,%rsp
       0x0000000000400ee4 <+4>:     mov    $0x402400,%esi               #0x402400 --> %esi
       0x0000000000400ee9 <+9>:     call   0x401338  #调用strings_not_equal函数
       0x0000000000400eee <+14>:    test   %eax,%eax                    
       0x0000000000400ef0 <+16>:    je     0x400ef7 23>		#如果%eax值为0,则正常返回
       0x0000000000400ef2 <+18>:    call   0x40143a 		#如果%eax值不为0,则触发炸弹
       0x0000000000400ef7 <+23>:    add    $0x8,%rsp
       0x0000000000400efb <+27>:    ret
    

    test 指令将 %eax 寄存器的值与自身进行逻辑与操作,并设置标志位,但不修改 %eax 的值。如果 %eax 的值为零,则标志位会被置为零;如果 %eax 的值不为零,则标志位会被置为非零。

    如果想让程序正常执行,那么就需要让获得%esi寄存器值的函数strings_not_equal返回值为0。

    下面查看函数strings_not_equal的代码,并且分析下主要的控制代码,看看如何让返回值为0:

    (gdb) disassemble strings_not_equal 
    Dump of assembler code for function strings_not_equal:
       0x0000000000401338 <+0>:     push   %r12
       0x000000000040133a <+2>:     push   %rbp
       0x000000000040133b <+3>:     push   %rbx
       0x000000000040133c <+4>:     mov    %rdi,%rbx						
       0x000000000040133f <+7>:     mov    %rsi,%rbp
       0x0000000000401342 <+10>:    call   0x40131b 			# 将%rbx和 %rbp传入函数string_length
       0x0000000000401347 <+15>:    mov    %eax,%r12d						# 第一次返回值%eax赋给 %r12d
       0x000000000040134a <+18>:    mov    %rbp,%rdi						# %rbp —> %rdi
       0x000000000040134d <+21>:    call   0x40131b 			# %rdi的值作为入参	
       0x0000000000401352 <+26>:    mov    $0x1,%edx
       0x0000000000401357 <+31>:    cmp    %eax,%r12d 						# 对比两次函数长度调用的结果
       0x000000000040135a <+34>:    jne    0x40139b 99>  # 若长度不同,返回1
       
       # 以上代码均为判断两个字符串的长度是否相同,现在的%r12d、%eax分别保存了第一次和第二次的函数调用结果,即两个字符串的长度
       # 寄存器rbxrbp 分别保存着函数的两个参数
       
       # 35c-41:若函数第一个参数低8位为0,则返回0
       0x000000000040135c <+36>:    movzbl (%rbx),%eax						# 参数1的低1位字节赋值给eax
       0x000000000040135f <+39>:    test   %al,%al							# %al & %al
       0x0000000000401361 <+41>:    je     0x401388 80>  # Jump condition:ZF---(Equal / zero)
       # 35c-37f:对比参数1和参数2的每个字节,均相同的话遍
       0x0000000000401363 <+43>:    cmp    0x0(%rbp),%al					# 参数2的低1位字节与数1的低1位字节对比
       0x0000000000401366 <+46>:    je     0x401372 58>	# 如果相等,则对比更高1位的字节
       0x0000000000401368 <+48>:    jmp    0x40138f 87>  # 不相等,返回错误1
       0x000000000040136a <+50>:    cmp    0x0(%rbp),%al					
       0x000000000040136d <+53>:    nopl   (%rax)
       0x0000000000401370 <+56>:    jne    0x401396 94> #若参数12的第2个字节不同,则返回错误1
       0x0000000000401372 <+58>:    add    $0x1,%rbx
       0x0000000000401376 <+62>:    add    $0x1,%rbp
       0x000000000040137a <+66>:    movzbl (%rbx),%eax
       0x000000000040137d <+69>:    test   %al,%al
       0x000000000040137f <+71>:    jne    0x40136a 50>
       
       
       0x0000000000401381 <+73>:    mov    $0x0,%edx
       0x0000000000401386 <+78>:    jmp    0x40139b 99>
       0x0000000000401388 <+80>:    mov    $0x0,%edx
       0x000000000040138d <+85>:    jmp    0x40139b 99>
       0x000000000040138f <+87>:    mov    $0x1,%edx
       0x0000000000401394 <+92>:    jmp    0x40139b 99>
       0x0000000000401396 <+94>:    mov    $0x1,%edx
       0x000000000040139b <+99>:    mov    %edx,%eax
       0x000000000040139d <+101>:   pop    %rbx
       0x000000000040139e <+102>:   pop    %rbp
       0x000000000040139f <+103>:   pop    %r12
       0x00000000004013a1 <+105>:   ret    
    End of assembler dump.
    

    这里的几个比较重要的跳转代码:

    0x40139b :函数将以%edx作为返回值返回。

    0x401388 :返回0。

    0x40136a :比较 %al 寄存器中的值与 %rbp 寄存器偏移 0 的内存地址处的值,如果不相等,则跳转到地址 0x401396 处执行相应的代码(函数会返回1),否则继续执行后续指令。

    重点理解的指令有:

    • test S1, S2:相当于S1&S2,若结果为0,则设置ZF为1
    • jmp的所有指令。重点有je,jump condition为ZF = 1
    • movzbl:0拓展数据。Move zero-extended byte to double word(movz S,R: R ← ZeroExtend(S))

    综上,可以得知如果想拆除阶段1的炸弹,则需要输入合理的字符串,而这个字符串可以通过以下分析得出:

    1. 查看phase_1函数的汇编代码,发现入参%rdi需要跟另一个参数0x402400 --> %rsi进入函数strings_not_equal 进行计算,若结果为0,则可以拆除炸弹,否则爆炸。
    2. 查看strings_not_equal 函数的汇编代码,可以分析出此函数是将入参寄存器(%rdi%rsi)所指地址处的字符串进行比对,如果长度相同并且每个字都相同(按照字节对比),则返回0

    通过上面两个函数的分析,就可以得知我们所输入的字符串,需要跟地址为0x402400处的字符串进行比对,比对成功则炸弹拆除。

    那这样,通过gdb的命令x/30s 0x402400,以 ASCII 格式打印出从地址 0x402400 开始的 10 个字节的数据(假设这些数据是以 0 结尾的字符串),并且找出第一个字符串即可,这里打印的值为:

    (gdb) x/30s 0x402400                                                                                                                                           
    0x402400:       "Border relations with Canada have never been better."
    				#translation:边境与加拿大的关系从未如此良好。
    
  • 相关阅读:
    kudu集群数据节点(tserver)扩容(缩容)
    第二章 JAVA基础语法
    2023游戏开发制作如何选择合适的游戏引擎?看完本文就知道
    【ESP32】22.智能家居-DHT11温湿度采集(WiFi)
    机器学习知识点:模型加权集成7种方法
    2024年孝感市建筑类中级职称申报资料私企VS国企
    C#virtual关键
    【Mysql】第8篇--数据库元数据
    后仿真中的 《specify/endspecify block》之(5)使用specify进行时序仿真
    liunx标准输入与输出
  • 原文地址:https://www.cnblogs.com/shangshankandashu/p/18156497