CSAPP实验2:Bomb Lab笔记

本文记录了CSAPP实验2 - Bomb Lab的详细过程,包括汇编复习、反汇编和GDB使用。实验涉及6个解密阶段,通过分析汇编代码和利用GDB调试,逐个破解每个阶段的密码,加深了对汇编和程序执行的理解。
摘要由CSDN通过智能技术生成

实验简介

​ Bomb LAB 目的是熟悉汇编。

​ 一共有7关,六个常规关卡和一个隐藏关卡,每次我们需要输入正确的拆弹密码才能进入下一关,而具体的拆弹密码藏在汇编代码中。实验中的bomb实际上是一个程序的二进制文件,该程序由一系列phase组成,每个phase需要我们输入一个字符串,然后该程序会进行校验,如果输入的字符串不满足拆弹要求,那么就会打印BOOM!!!

​ 完成整个实验的思路是通过objdumpbomb进行反编译(objdump -d bomb > bomb.txt),获取所有的汇编代码。提取每个阶段对应的代码并借助gdb进行分析,逐一拆弹。

Github地址:Bomb Lab

准备

汇编复习

类型 语法 例子 备注
常量 符号$ 开头 $-42, $0x15213 一定要注意十进制还是十六进制
寄存器 符号 % 开头 %esi, %rax 可能存的是值或者地址
内存地址 括号括起来 (%rbx), 0x1c(%rax), 0x4(%rcx, %rdi, 0x1) 括号实际上是去寻址的意思

一些汇编语句与实际命令的转换:

指令 效果
mov %rbx, %rdx rdx = rbx
add (%rdx), %r8 r8 += value at rdx
mul $3, %r8 r8 *= 3
sub $1, %r8 r8--
lea (%rdx, %rbx, 2), %rdx rdx = rdx + rbx*2

比较与跳转是拆弹的关键,基本所有的字符判断就是通过比较来实现的,比方说 cmp b,a 会计算 a-b 的值,test b, a 会计算 a&b,注意运算符的顺序。例如

cmpl %r9, %r10
jg   8675309

等同于 if %r10 > %r9, jump to 8675309

各种不同的跳转:

指令 效果 指令 效果
jmp Always jump ja Jump if above(unsigned >)
je/jz Jump if eq / zero jae Jump if above / equal
jne/jnz Jump if !eq / !zero jb Jump if below(unsigned <)
jg Jump if greater jbe Jump if below / equal
jge Jump if greater / eq js Jump if sign bits is 1(neg)
jl Jump if less jns Jump if sign bit is 0 (pos)
jle Jump if less / eq x x

举几个例子

cmp $0x15213, %r12
jge deadbeef

%r12 >= 0x15213,则跳转到 0xdeadeef

cmp %rax, %rdi
jae 15213b

如果 %rdi 的无符号值大于等于 %rax,则跳转到 0x15213b

test %r8, %r8
jnz (%rsi)

如果 %r8 & %r8 不为零,那么跳转到 %rsi 存着的地址中。

x86-64寄存器规则: 默认函数的第一个参数是%rdi 第二个参数%rsi 第三个参数%rdx

反汇编

# 检查符号表
# 然后可以寻找跟 bomb 有关的内容
objdump -t bomb | less 

# 反编译
# 搜索 explode_bomb
objdump -d bomb > bomb.txt

# 显示所有字符
strings bomb | less

GDB

gdb bomb
help    # 获取帮助

break explode_bomb  # 设置断点
break phase_1

run # 开始运行

disas # 反汇编

info registers  # 查看寄存器内容

print $rsp # 打印指定寄存器

stepi   # (单步跟踪进入)执行一行代码,如果函数调用,则进入该函数 可以使用s简化

n   # (单步跟踪) 执行一行代码,如果函数调用,则一并执行

x/4wd $rsp # 检查寄存器或某个地址,查看内存地址里面的内容(常用)

ctl+c 可以退出,每次进入都要设置断点(保险起见),炸弹会用 sscanf 来读取字符串,了解清楚到底需要输入什么。

Phase 1

Dump of assembler code for function phase_1:
   0x0000000000400ee0 <+0>:    sub    $0x8,%rsp
   0x0000000000400ee4 <+4>:    mov    $0x402400,%esi
   0x0000000000400ee9 <+9>:    callq  0x401338 <strings_not_equal>
   0x0000000000400eee <+14>:    test   %eax,%eax
   0x0000000000400ef0 <+16>:    je     0x400ef7 <phase_1+23>
   0x0000000000400ef2 <+18>:    callq  0x40143a <explode_bomb>
   0x0000000000400ef7 <+23>:    add    $0x8,%rsp
   0x0000000000400efb <+27>:    retq   

gdb bomb,然后设置断点 break explode_bombbreak phase_1

这段代码还是挺好理解的,保存Stack pointer,将$0x402400传给%esi,调用位于0x401338strings_not_equal函数,比较%eax是否为0,不为零则调用explode_bomb函数,为零则返回设置断点 phase_1explode_bomb,输入命令r运行会在断点处停下,此时随便输入一个字符串用于测试“abcd”,然后disas查看反汇编代码:=>箭号为当前运行的位置

mark

查看寄存器内容 info registereax就是rax的低位用print $eax 打印出来,是一个地址用x/s $eax,查看出地址里的内容,发现是输入字符串

mark

stepi 逐步执行,执行完 mov 之后,把地址中的内容传到%esi中,用print查看!得到字符串,这就是第一关的答案。退出后新建一个文本 touch sol.txt,方便之后输入

mark

mark

Phase 2

这次我们有了第一关的答案,进入gdb后设置好断点,和命令参数。

mark

试运行,在phase_1停住,然后continue,答案正确,触发 phase_2的断点,这次输入abc

mark

反汇编Phase2部分的代码

(gdb) disas
Dump of assembler code for function phase_2:
=> 0x0000000000400efc <+0>:    push   %rbp
   0x0000000000400efd <+1>:    push   %rbx
   0x0000000000400efe <+2>:    sub    $0x28,%rsp
   0x0000000000400f02 <+6>:    mov    %rsp,%rsi
   0x0000000000400f05 <+9>:    callq  0x40145c <read_six_numbers>   #读取6个数字
   0x0000000000400f0a <+14>:    cmpl   $0x1,(%rsp)                  #第一个数字和1比较,不相等则爆炸
   0x0000000000400f0e <+18>:    je     0x400f30 <phase_2+52>        #相等,跳转到<52>
   0x0000000000400f10 <+20>:    callq  0x40143a <explode_bomb>
   0x0000000000400f15 <+25>:    jmp    0x400f30 <phase_2+52>
   0x0000000000400f17 <+27>:    mov    -0x4(%rbx),%eax               #将(%rbx)前一个数字存到%eax
   0x0000000000400f1a <+30>:    add    %eax,%eax                     #%eax数字加倍
   0x0000000000400f1c <+32>:    cmp    %eax,(%rbx)                   #%eax和(%rbx)比较,=(%rbx)则跳过爆炸
   0x0000000000400f1e <+34>:    je     0x400f25 <phase_2+41>
   0x0000000000400f20 <+36>:    callq  0x40143a <explode_bomb>
   0x0000000000400f25 <+41>:    add    $0x4,%rbx                     #(%rbx)地址+4,下一个数字
   0x0000000000400f29 <+45>:    cmp    %rbp,%rbx                     #比较%rbp%rbx,循环是否结束 
   0x0000000000400f2c <+48>:    jne    0x400f17 <phase_2+27>
   0x0000000000400f2e <+50>:    jmp    0x400f3c <phase_2+64>
   0x0000000000400f30 <+52>:    lea    0x4(%rsp),%rbx                #指向第2个数字,%rbx保存第2个数字地址
   0x0000000000400f35 <+57>:    lea    0x18(%rsp),%rbp               #0x18 = 0x0 + 4 bit * 6 个数字 
   0x0000000000400f3a <+62>:    jmp    0x400f17 <phase_2+27>
   0x0000000000400f3c <+64>:    add    $0x28,%rsp
   0x0000000000400f40 <+68>:    pop    %rbx
   0x0000000000400f41 <+69>:    pop    %rbp
   0x0000000000400f42 <+70>:    retq   
End of assembler dump.

根据Phase1,很敏感的会发现movl $0x4025c3, %esi这行。通过之前一样的方法,得到0x4025c3内存里的字符串

(gdb) x/s $esi
0x4025c3:    "%d %d %d %d %d %d

再根据bomb[0x40148a] <+46>: callq 0x400bf0 ; symbol stub for: __isoc99_sscanf这句,猜一下,立马就能联想到scanf("%d %d %d %d %d %d",a,b,c,d,e,f);,也就是说,输入的格式已经确定了。

解读出循环中,从1开始,是一个等比数列,公比为2。1 2 4 8 16 32

mark

Phase 3


                
  • 14
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值