CSAPP: bomblab

最近真没写什么其他好玩的东西,所以拿课堂作业报告水一水Blog。
因为写这个的时间很有限,可能不像datalab那样精致,是边做边记的,逻辑有点混乱,也有不少错误。

效果截图

各phase答案:

sudo hostnamectl set-hostname ubuntu

Brownie, you are doing a heck of a job.
0 1 1 2 3 5
0 u 81
5 15 DrEvil
5>:?70
5 1 2 3 6 4
50

secret_phase: 二叉树搜索

读入整数,整数,字符串
b secret_phase
edx: a
esi: 0
rdi: &input_str
rbx: rax=int(input_str)=input_int
rax: input_int - 1
u<=3e8, so 1 ≤ input_int ≤ 3e9
esi: input_int
x/512x 0x604110



24
8 32 (50)
6 16 2d 6b
1 7 14 23 28 2f(47) 63 3e9(1001)

要求fun7的返回值为1
fun7
参数1: edi=edx,开始时是树的根节点
参数2: esi,input_int

if (input_int >= 根节点的值):
+28
eax = 0
edx = input_int时直接跳出返回
否则参数1: edi根节点变右节点,嵌套一层,但注意这里是call而不是jump,意味着过去完还要回来的,而且保存返回值
走右边: 返回值翻倍再加一
< :
+15
去左节点: 返回值翻倍

但注意上述返回值的变化是从叶到根生效,是倒序的
所以大于就往右走,小于就往左走
既然返回值为1,而返回值初始为0,往右走一步就跳出就可以,意味着被调用函数返回值为0,之后向右执行一步得到1,返回即可。
注意读到的0x32等于50

phase_6: 链表排序

读入六个整数
rsi: %rsp,其实是&num1

循环1开头:
r12: &num1
r13d: 0
rbp: &num1
eax: num1 - 1 unsigned <= 5
要求num1 - 1 >= 0,1 <= num1 ≤ 6
r13d: 1
执行满六次之后跳+134

循环2:
+73到+98
ebx: 1
eax: num2
要求num1 != num2
ebx++,小于等于5时继续循环这一块,如上执行五次,
要求num1 != num2, num2 != num3, num3 != num4, num4 != num5, num5 != num6

循环1结尾:
+100
r12: &num1→&num2
所以意味着以上类似要求执行了6次,
即要求 1 <= num1, ..., num6 <= 6
之后跳134

+134
esi = 0
+139开始是第三循环体
ecx = num1
eax = 1
edx = node1
if (num1>1):
goto 106
else (num1 == 1):
goto 117

+117
rsp+20 = node1
再执行5次,也就是挑好了合适的,numx的值为y则对应nodey

+159跳出第三循环体,进入第四循环(其实还有第五循环套在这一层外面)
rbx=node1,不是->val

第四循环
rax=node1
rsi=node6
rcx=node1->val?还是node1? node1吧
循环体:+177
rdx=node2
node1->next=node2?
rax=node2
rcx=node2
总之第四循环就是把node1到6按顺序连起来

+197跳出第四循环,回到第五循环
node6->next = null
ebp=5
rax=node2->val
要求node1->val > node2->val
rbx变node2,ebp--,循环五次意味着节点值从node1到node6递减
因此只要找合适的num值使前述节点排列成递减即可,即值顺序为 5 1 2 3 6 4

综上,这次phase没有a[i] = 7 - a[i],应该相对还行。

phase_5

版本1: 数组对应

输入字符串长度为6
%rax: &字符串,之后右移,属于循环变量
%rbx: &字符串
%rdi: 字符串结尾,属于循环条件
%ecx: 0
%edx: 第一个字符与0xf,即取低四位作为数组下标,对应出另一个整数,加到%ecx中
观察x/16 0x402780得到如下关系:

在跳回之前我们观察结束条件,%ecx=0x3f=63,也就是六个字符的下标对应出的数字之和为63.
可取16+15+14+13+3+2,即下标为5, 14, 10, 15, 7, 0, 比如可以取48+这些数:5>:?70
注意不能用32+这些数,因为空格会在类scanf的函数中读值时自动被忽略掉。

版本2: 数组跳转

数组下标 数组下标
0 a 8 0
1 2 9 4
2 e a 1
3 7 b d
4 8 c 3
5 c d 9
6 f e 6
7 b f 5

第一个数只取最低四位做入口,x/不能等于0xf
第一个数edx加完后 = 0xf,执行15次
第二个数等于rcx

读出该程序干的事情是加15次数组元素,其中最后一次一定加的是f,我们需要输入的是第一个下标以及加和。
不妨倒推,f+6+e+2+1 +a+0+8+4+9 +d+b+7+3+c = 115
那么我们第一个下标对应值c,即入口是5,加和是115。

phase_4: 二分查找

读入整数:
rcx(arg4) = rsp + 4 = &num2
rdx(arg3) = rsp = &num1
esi(arg2) = "%d %d"

需要我们输入两个整数,盲猜可以进secret phase。
要求0 <= num1 <= 0xe,
rdx = 0xe(arg3) 上界
esi = 0(arg2) 下界
edi = num1(arg1)
call func4, 要求返回值等于0xf
要求num2=0xf,此时phase_4全部结束

func4
传入了三个参数: num1(edi), 0(下界, esi),0xe(上界, rdx)
eax = 0xe - 0 = 0xe
ebx = 0xe 逻辑右移 0x1f=31位,变0了吧,目测是看符号位
eax = 0xe + 0 = 0xe,算术右移变0x111=7,也就是做了作差除以2下取整
ebx = 0x(7 + 0)=0x7,中点值
跳+33
eax=0x7
ebx>=num1时直接返回
...
是二分查找,差值是符号判断,如果上面的差值为负值,则ebx得1,挪一挪eax,防止死循环的,所以不去考虑它也完全没关系

可以暴力从0x0到0xe直接试一遍,虽然容易手误
如果要仔细推理:在num为0到14,low初始为0,high初始为14的情况下怎样返回15呢?
尤其是,为什么num == 5时会返回15?因为我们每次都加了每次计算出的中间值,
所以:
左右端点为0和14时返回值+7,
左右端点为0和6时返回值+3;
左右端点为4和6时返回值+5。
返回值初始为0,完成上述查找得到返回15。

phase_3: 跳转表

注意输入是 %d %c %d
分别传到 %rsp+10, %rsp+f, %rsp+14,换回了数字 数字 字符的顺序
num1 unsigned > 7时,炸
也就是说0 <= num1 ≤ 6,
根据num1的值跳到跳转表相应位置,从而执行相应switch的结构。
既然switch的default执行+319,我们先不看跳转表,直接看+319。
传入的%c等于%rax的最低位字符即可。
接下来看跳转表。
我们用 x/a 0x402740发现为+77,意味着%rax=num1=0时,执行+77,此时:
%eax=0x75,num2 = 0x51=81。所以c = 0x75=u即可,这个字符可通过 Alt + 小键盘117 得到。
答案为 0 u 81

phase_2: 斐波那契数列

%rsi参数2的值是%rsp,给read_six_numbers
利用read_six_numbers将参数3-8读入%rsi, %rsi+4, ..., %rsi+14共六处,对应于%rsp, %rsp+4, ..., %rsp+14共六处
num1=0
num2=1
%rbx=&num1
%rbp=&num5

%eax = num2 = 1
num2 += num1 = 1
num3 = %eax = 1
%rbx = &num2

%eax = num3 = 1
%eax += num2 = 1+1=2
num4 = %eax = 2

0 1 1 2 3 5

phase_1: 比较字符串

strings_not_equal的返回值(%eax)为0就跳出去,意味着要求All strings are created equal. 识得唔识得啊。

从相应位置读入所需字符串即可。