LAB2 二进制炸弹实验是CSAPP配套实验中最有名的一个,实验一共分为6关,每关难度依次递增。独立完成还是需要很大一块时间的。
做完这个实验能够让我们理解汇编语言的原理,GDB调试器的使用,以及进一步了解一个HELLO WORLD程序背后到底发生了什么。
【必要的知识储备】
objdump -t bomb > name.txt 列出可执行文件bomb包含的所有函数名字和全局变量名等等信息。>重定向到name.txt文件
objdump -d bomb > bomb.txt反汇编bomb文件,将生成的汇编文件重定向到bomb.txt文件中
GDB相关的几个常用的操作:
r 1 2 3:进入gdb并导入程序文件后,以命令行参数1 2 3运行程序
b *0x40046b :在地址0x40046b处设置断点
d :删除所有断点
stepi :执行一条汇编指令
s:执行一条C语句,跳转到相应函数
n:执行一条C语句,但是直接跳过跳转函数,到达下一跳C语句
p /d $edx:以十进制方式打印出%edx内容,/x就对应16进制
p *(int *) ($ebp-0x32) :这对于查看寄存器对应地址所对应内容很有帮助
p (char*)0xbfff890:检查以该地址开头的字符串
x/3w $esp:从寄存器%esp地址开始,所对应内容打印2个字
bt:打印当前堆栈信息
info registers:查看寄存器信息
info frame:查看帧,栈指针信息
【实验分析】
实验材料只有一个可执行文件,先把他用objdump -d反汇编得到汇编文件。打开文件发现对应6个phase,对应着6关。
一:phase_1(字符串比较)
这一关是最容易的,先按照逻辑过一边第一段的代码,其中比较重要的几句是:
<span style="font-size:14px;"> 8048b29: 83 c4 f8 add $0xfffffff8,%esp
8048b2c: 68 c0 97 04 08 push $0x80497c0
8048b31: 50 push %eax
8048b32: e8 f9 04 00 00 call 8049030 <strings_not_equal>
8048b37: 83 c4 10 add $0x10,%esp
8048b3a: 85 c0 test %eax,%eax
8048b3c: 74 05 je 8048b43 <phase_1+0x23>
8048b3e: e8 b9 09 00 00 call 80494fc <explode_bomb>
</span>
我们看到push了两个东西后,调用了一个<strings_not_equal>函数,根据名字判断极有可能是我判断两个字符串是否相等。
我们跳转到这个函数来验证我们的猜想是否正确:
首先在<strings_not_equal>中调用了<string_length>,很明显,稍加分析就可以得知这是返回字符串长度的函数。
<span style="font-size:14px;"> 8049039: 8b 75 08 mov 0x8(%ebp),%esi
804903c: 8b 7d 0c mov 0xc(%ebp),%edi
804903f: 83 c4 f4 add $0xfffffff4,%esp
8049042: 56 push %esi
8049043: e8 d0 ff ff ff call 8049018 <string_length>
8049048: 89 c3 mov %eax,%ebx
804904a: 83 c4 f4 add $0xfffffff4,%esp
804904d: 57 push %edi
804904e: e8 c5 ff ff ff call 8049018 <string_length>
8049053: 39 c3 cmp %eax,%ebx
8049055: 74 09 je 8049060 <strings_not_equal+0x30></span>
因此我们可以在*0x804903f处设置一个断点,再使用命令:p (char*)($esi)可以看出,esi储存我们输入的字符,同理显示%edi寄存器的内容:“Public speaking is very easy.”这个字符串属于系统的硬编码。
接下来的几条指令分别计算了二个字符串的长度,返回值存放在%eax和%ebx寄存器中,再cmp比较两者长度,不等就爆炸。
<strings_not_equal>的剩余语句其实不用看我们基本可以猜测出答案了,当该函数%eax返回1表示不相等就爆炸,返回0表示相等,因此这一关我们的答案:“Public speaking is very easy.”
二: phase_2(循环语句)
第二关我们先看调用的名为<read_six_numbers>函数:
<span style="font-size:14px;"><span style="font-size:12px;"> 8048fe7: 50 push %eax
8048fe8: 8d 42 10 lea 0x10(%edx),%eax
8048feb: 50 push %eax
8048fec: 8d 42 0c lea 0xc(%edx),%eax
8048fef: 50 push %eax
8048ff0: 8d 42 08 lea 0x8(%edx),%eax
8048ff3: 50 push %eax
8048ff4: 8d 42 04 lea 0x4(%edx),%eax
8048ff7: 50 push %eax
8048ff8: 52 push %edx
8048ff9: 68 1b 9b 04 08 push $0x8049b1b //格式
8048ffe: 51 push %ecx
8048fff: e8 5c f8 ff ff call 8048860 <sscanf@plt></span></span>
我们先看看sscanf函数的原型:int sscanf(const char *buffer,const char *format,[argument ]…);
sscanf会从buffer里读进数据,依照format的格式将数据写入到argument里。因此我们很容易联想到,前面push的一系列数据正式调用sscanf的参数,从中我们也可以发现,调用函数时,参数是从右向左入栈的。
在gdb中输入命令:p (char*)(0x8048ff9),我们得到输出“%d %d %d %d %d”。因此我们确定了输入6个数据的格式。
从<read_six_numbers>回来后,首先比较了第一个数:
<span style="font-size:14px;"> 8048b63: 83 7d e8 01 cmpl $0x1,-0x18(%ebp) </span>
因此输入6个数据的第一个数就是1。
接着进入了一个循环,关键语句是
<span style="font-size:14px;"> 8048b7e: 39 04 9e cmp %eax,(%esi,%ebx,4)</span>
%eax根据前一条语句计算出来,值为1*2=2,而(%esi,%ebx,4)储存的是sscanf格式化输出后的6个数中的其中一个(也就是我们的输入),比较两者,如果不相等就爆炸。%ebx寄存器的值用来计数,inc之后表示当前验证第几个数,当其大于5时就退出循环。依次比较完6个数都相等后本关才通过。
根据规律我们很容易得到:第n个数的值=第n-1个数的值*n
因此本关的答案1 2 6 24 120 720
三:phase_3(switch语句)
这个片段代码很长,我们先分析一下前面一小段,直接看代码后面的注释
<span style="font-size:14px;">08048b98 <phase_3>:
8048b98: 55 push %ebp
8048b99: 89 e5 mov %esp,%ebp
8048b9b: 83 ec 14 sub $0x14,%esp
8048b9e: 53 push %ebx
8048b9f: 8b 55 08 mov 0x8(%ebp),%edx
8048ba2: 83 c4 f4 add $0xfffffff4,%esp
8048ba5: 8d 45 fc lea -0x4(%ebp),%eax
8048ba8: 50 push %eax //压入第三个参数c
8048ba9: 8d 45 fb lea -0x5(%ebp),%eax
8048bac: 50 push %eax //压入第二个参数b
8048bad: 8d 45 f4 lea -0xc(%ebp),%eax
8048bb0: 50 push %eax //压入第一个参数a
8048bb1: 68 de 97 04 08 push $0x80497de//得出输入格式“"%d %c %d" (a b c)
8048bb6: 52 push %edx
8048bb7: e8 a4 fc ff ff call 8048860 <sscanf@plt>
8048bbc: 83 c4 20 add $0x20,%esp
8048bbf: 83 f8 02 cmp $0x2,%eax //%eax返回元素的个数
8048bc2: 7f 05 jg 8048bc9 <phase_3+0x31>
8048bc4: e8 33 09 00 00 call 80494fc <explode_bomb>//小于2个爆炸
8048bc9: 83 7d f4 07 cmpl $0x7,-0xc(%ebp)
8048bcd: 0f 87 b5 00 00 00 ja 8048c88 <phase_3+0xf0>// a>7爆炸
</span>
上面这段代码和第二关有点类似,我们可以得出输入数据的格式,紧接着就是本题的核心语句了:
<span style="font-size:14px;"> 8048bd3: 8b 45 f4 mov -0xc(%ebp),%eax /*开始switch分支*/
8048bd6: ff 24 85 e8 97 04 08 jmp *0x80497e8(,%eax,4)//选择跳转点</span>
前一条表明switch分支的开始,而后一条的意思是根据我们输入的参数a(必须小于等于7)选择跳转点,之后对应不同的跳转点有不同的cmp语句,如果不相等就爆炸。
因此可以看出本题的答案不止1个。在这里假设输入的第一个参数为1,之后可以按照顺序推导出结果。
eax为1跳转到如下位置:
<span style="font-size:14px;"> 8048c00: b3 62 mov $0x62,%bl
8048c02: 81 7d fc d6 00 00 00 cmpl $0xd6,-0x4(%ebp) //c=0xd6,%bl=0x62
8048c09: 0f 84 80 00 00 00 je 8048c8f <phase_3+0xf7>
</span>
得出c的值为0xd6,接着继续跳转:
<span style="font-size:14px;"> 8048c8f: 3a 5d fb cmp -0x5(%ebp),%bl //b=0x62,即b
8048c92: 74 05 je 8048c99 <phase_3+0x101>
</span>
我们得出了b=0x62,即字母b。值得注意的一点是:当想查看偏移ebp一定两的内存对应的数据时比如-0x4(%ebp),但是他并没有对应的数据寄存器。我们可以在gdb中用命令: p *(int*)($ebp-0x4)查看。
因此本关的一个答案:1 b 214
四:phase_4(递归)
这一关考察的主要是一个递归的知识,我们只要找到了模式,能够逆推出c代码这道题就解决了。
<span style="font-size:14px;">08048ce0 <phase_4>:
8048cef: 50 push %eax
8048cf0: 68 08 98 04 08 push $0x8049808 //字符格式"%d"
8048cf5: 52 push %edx
8048cf6: e8 65 fb ff ff call 8048860 <sscanf@plt>
8048cfb: 83 c4 10 add $0x10,%esp
8048cfe: 83 f8 01 cmp $0x1,%eax //是1个参数就继续否则爆炸
8048d01: 75 06 jne 8048d09 <phase_4+0x29>
8048d03: 83 7d fc 00 cmpl $0x0,-0x4(%ebp)
8048d07: 7f 05 jg 8048d0e <phase_4+0x2e>//且参数大于0
8048d09: e8 ee 07 00 00 call 80494fc <explode_bomb>
8048d0e: 83 c4 f4 add $0xfffffff4,%esp
8048d11: 8b 45 fc mov -0x4(%ebp),%eax
8048d14: 50 push %eax
8048d15: e8 86 ff ff ff call 8048ca0 <func4>
8048d1a: 83 c4 10 add $0x10,%esp
8048d1d: 83 f8 37 cmp $0x37,%eax //返回值必须等于0x37即55
8048d20: 74 05 je 8048d27 <phase_4+0x47>
8048d22: e8 d5 07 00 00 call 80494fc <explode_bomb>
8048d27: 89 ec mov %ebp,%esp
8048d29: 5d pop %ebp
8048d2a: c3 ret
8048d2b: 90 nop</span>
同理,得出输入字符格式为“%d“,要求一个参数且大于0,之后进入一个<func4>,后一句看出要求返回值%eax必须等于0x37,我们进入func4看看究竟发生了什么:
<span style="font-size:14px;">08048ca0 <func4>:
8048ca0: 55 push %ebp
8048ca1: 89 e5 mov %esp,%ebp
8048ca3: 83 ec 10 sub $0x10,%esp
8048ca6: 56 push %esi
8048ca7: 53 push %ebx //$ebx储存输入参数假设为n
8048ca8: 8b 5d 08 mov 0x8(%ebp),%ebx
8048cab: 83 fb 01 cmp $0x1,%ebx
8048cae: 7e 20 jle 8048cd0 <func4+0x30> //n<=1,返回1
8048cb0: 83 c4 f4 add $0xfffffff4,%esp
8048cb3: 8d 43 ff lea -0x1(%ebx),%eax //n>0,先n-1
8048cb6: 50 push %eax
8048cb7: e8 e4 ff ff ff call 8048ca0 <func4>//调用func4(n-1)
8048cbc: 89 c6 mov %eax,%esi //%esi保存返回值
8048cbe: 83 c4 f4 add $0xfffffff4,%esp
8048cc1: 8d 43 fe lea -0x2(%ebx),%eax //fun4(n-1)调用完后,n变成n-2
8048cc4: 50 push %eax
8048cc5: e8 d6 ff ff ff call 8048ca0 <func4>//调用func4(n-1)
8048cca: 01 f0 add %esi,%eax //相加两次调用的结果并返回
8048ccc: eb 07 jmp 8048cd5 <func4+0x35>
8048cce: 89 f6 mov %esi,%esi
8048cd0: b8 01 00 00 00 mov $0x1,%eax
8048cd5: 8d 65 e8 lea -0x18(%ebp),%esp
8048cd8: 5b pop %ebx
8048cd9: 5e pop %esi
8048cda: 89 ec mov %ebp,%esp
8048cdc: 5d pop %ebp
8048cdd: c3 ret
8048cde: 89 f6 mov %esi,%esi</span>
因此不难得出c实现:
<span style="font-size:14px;">inf func4(int n) {
if (n <= 1)
return 1;
else
return func4(n-1) + func4(n-2);
} </span>
因此经过计算后得出n=9时满足返回值为0x37即55
五:phase_5(字符串操作)
本题主要的分析都在注释中:
<span style="font-size:14px;"> 8048d34: 8b 5d 08 mov 0x8(%ebp),%ebx //储存输入字符串
8048d37: 83 c4 f4 add $0xfffffff4,%esp
8048d3a: 53 push %ebx
8048d3b: e8 d8 02 00 00 call 8049018 <string_length>
8048d40: 83 c4 10 add $0x10,%esp
8048d43: 83 f8 06 cmp $0x6,%eax
8048d46: 74 05 je 8048d4d <phase_5+0x21>
8048d48: e8 af 07 00 00 call 80494fc <explode_bomb>
8048d4d: 31 d2 xor %edx,%edx
8048d4f: 8d 4d f8 lea -0x8(%ebp),%ecx
8048d52: be 20 b2 04 08 mov $0x804b220,%esi //作为模式串:"isrveawhobpnutfg\260\001"
8048d57: 8a 04 1a mov (%edx,%ebx,1),%al //循环开始位置,依次取输入字符串的第1个到第5个字符(%edx:0-5)存入%al
8048d5a: 24 0f and $0xf,%al //取%al最后四位
8048d5c: 0f be c0 movsbl %al,%eax //作符号扩展
8048d5f: 8a 04 30 mov (%eax,%esi,1),%al //根据符号扩展后的索引选取模式串中的字符
8048d62: 88 04 0a mov %al,(%edx,%ecx,1) //按照0-5的顺序存入一个新数组%ecx
8048d65: 42 inc %edx
8048d66: 83 fa 05 cmp $0x5,%edx
8048d69: 7e ec jle 8048d57 <phase_5+0x2b>
8048d6b: c6 45 fe 00 movb $0x0,-0x2(%ebp)
8048d6f: 83 c4 f8 add $0xfffffff8,%esp
8048d72: 68 0b 98 04 08 push $0x804980b //字符串:“giants”
8048d77: 8d 45 f8 lea -0x8(%ebp),%eax //得到的新数组
8048d7a: 50 push %eax
8048d7b: e8 b0 02 00 00 call 8049030 <strings_not_equal>//比较,两者要一致,逆推即可(低四位作索引:15 0 5 11 13 1,高四位任意)
8048d80: 83 c4 10 add $0x10,%esp //任意取一组对应的字符串为?@EKMA
8048d83: 85 c0 test %eax,%eax
8048d85: 74 05 je 8048d8c <phase_5+0x60></span>
总之,本关分为2大块:
第一块:取输入字符串的每一个字符的低四位符号扩展后,作为索引选取模式串中的字符,形成一个新数组。
第二块:调用<string_not_equeal>比较得到的新数组和本身硬编码的”giants”,二者要一致。
本关需要逆推,先根据“giants”逆推出在模式串中的索引:15 0 11 13 1 。索引作为低四位,高四位任取。
因此本关的其中一个答案是:?@EKMA
六:phase_6(链表)
这一关难度很大,研究了半天,最后参考了以下别人的才弄清楚。
总的来说可以分成四部分来解读:
1).读入6个数并检验输入数据是否满足要求
2).根据输入重新排列链表并存入另一块内存。
3).重新连接重排后的链表。
4).检测链表值是否满足要求。
(假设我们已经)
首先是部分一:检验输入是否合法。
汇编和C代码解释一起看更容易懂。
<span style="font-size:14px;"> 8048da1: 8b 55 08 mov 0x8(%ebp),%edx
8048da4: c7 45 cc 6c b2 04 08 movl $0x804b26c,-0x34(%ebp)
8048dab: 83 c4 f8 add $0xfffffff8,%esp
8048dae: 8d 45 e8 lea -0x18(%ebp),%eax //数组arr首地址,gdb中通过p *(int*)($ebp-0x18)可得出
8048db1: 50 push %eax
8048db2: 52 push %edx
8048db3: e8 20 02 00 00 call 8048fd8 <read_six_numbers> //读入6个数字存入数组arr
8048db8: 31 ff xor %edi,%edi //%edi清零,表示i=0
8048dba: 83 c4 10 add $0x10,%esp
8048dbd: 8d 76 00 lea 0x0(%esi),%esi
8048dc0: 8d 45 e8 lea -0x18(%ebp),%eax //得到数组的首地址
8048dc3: 8b 04 b8 mov (%eax,%edi,4),%eax //edi范围从0-5,改变数组下标
8048dc6: 48 dec %eax
8048dc7: 83 f8 05 cmp $0x5,%eax //每个元素必须小于等于6,否则爆炸
8048dca: 76 05 jbe 8048dd1 <phase_6+0x39>
8048dcc: e8 2b 07 00 00 call 80494fc <explode_bomb>
8048dd1: 8d 5f 01 lea 0x1(%edi),%ebx //ebx=edi+1,ebx表示为j,相当于j=i+1
8048dd4: 83 fb 05 cmp $0x5,%ebx //j <= 5进行內循环,如果大于就进行外循环
8048dd7: 7f 23 jg 8048dfc <phase_6+0x64>
8048dd9: 8d 04 bd 00 00 00 00 lea 0x0(,%edi,4),%eax //%eax表示距离数组首地址的偏移量
8048de0: 89 45 c8 mov %eax,-0x38(%ebp) //暂存在此地址
8048de3: 8d 75 e8 lea -0x18(%ebp),%esi //数组首地址
8048de6: 8b 55 c8 mov -0x38(%ebp),%edx //偏移量,用于得到对应下标的数组元素地址
8048de9: 8b 04 32 mov (%edx,%esi,1),%eax //首地址+偏移量=arr[i]
8048dec: 3b 04 9e cmp (%esi,%ebx,4),%eax //同理,得到arr[j]
8048def: 75 05 jne 8048df6 <phase_6+0x5e> //要求arr[i] != arr[j],否则爆炸
8048df1: e8 06 07 00 00 call 80494fc <explode_bomb>
8048df6: 43 inc %ebx //j++
8048df7: 83 fb 05 cmp $0x5,%ebx // j <= 5 继续內循环
8048dfa: 7e ea jle 8048de6 <phase_6+0x4e>
8048dfc: 47 inc %edi //i++
8048dfd: 83 ff 05 cmp $0x5,%edi //i <= 5 继续外循环
8048e00: 7e be jle 8048dc0 <phase_6+0x28></span>
这是这段汇编代码对应的C代码:
<span style="font-size:14px;"> for (int i =0; i<=5; i++){
if (arr[i]>= 6)
explode_bomb();
for (int j = i+1; j<=5; j++){
if (arr[i]== arr[j])
explode_bomb();
}
} </span>
然后是部分二:根据输入的6个数字,重新排列链表并存入新的地址。
<span style="font-size:14px;"> 8048e02: 31 ff xor %edi,%edi // i = 0
8048e04: 8d4d e8 lea -0x18(%ebp),%ecx //数组arr首地址存入%ecx
8048e07: 8d45 d0 lea -0x30(%ebp),%eax
8048e0a: 8945 c4 mov %eax,-0x3c(%ebp)
8048e0d: 8d76 00 lea 0x0(%esi),%esi
8048e10: 8b75 cc mov -0x34(%ebp),%esi //%esi存放链表首地址(稍后解释为什么)
8048e13: bb01 00 00 00 mov $0x1,%ebx // j = 1
8048e18: 8d04 bd 00 00 00 00 lea 0x0(,%edi,4),%eax //%eax 存放偏移地址
8048e1f: 89c2 mov %eax,%edx
8048e21: 3b1c 08 cmp (%eax,%ecx,1),%ebx
8048e24: 7d12 jge 8048e38 <phase_6+0xa0> // j - arr[i] <= 0继续循环
8048e26: 8b04 0a mov (%edx,%ecx,1),%eax //%eax = arr[i]
8048e29: 8db4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
8048e30: 8b76 08 mov 0x8(%esi),%esi //相当于node=node->next
8048e33: 43 inc %ebx //j++
8048e34: 39c3 cmp %eax,%ebx // j < arr[i] 继续內循环
8048e36: 7cf8 jl 8048e30 <phase_6+0x98>
8048e38: 8b55 c4 mov -0x3c(%ebp),%edx //%edx存放新数组a首地址
8048e3b: 8934 ba mov %esi,(%edx,%edi,4) //esi存入新数组对应下标中
8048e3e: 47 inc %edi
8048e3f: 83ff 05 cmp $0x5,%edi
8048e42: 7ecc jle 8048e10 <phase_6+0x78> //i <= 5 继续外循环</span>
首先关注一条核心语句(也告诉了我们如何推到出硬编码的地址表示一个链表):
<span style="font-size:14px;"> 8048da4: c7 45 cc 6c b2 04 08 movl $0x804b26c,-0x34(%ebp) </span>
看起来似乎是一个字符串,尝试着:
打印处理了node1,可以猜测很有可能是个结构体,再看这一条语句:
<span style="font-size:14px;">048e30: 8b 76 08 mov 0x8(%esi),%esi //esi值为0x804b26c,这是一个结构体的操作
</span>
因此,我们可以尝试着打印:
这是一个首位相接的链表。且结构体含有3个值。为2个整型和1个指针。
C代码如下:
<span style="font-size:14px;"> for (int i =0; i<=5; i++){
for (int j =0; j<arr[i]; j++){
node = node->next;
}
a[i]= node; //a[i]是一个新数组
} </span>
假设为们输入的是1 2 3 4 5 6 ,经过这段代码之后就变成了2 3 4 5 6 1。
这段代码的功能就是:假设arr[i]=x,找出arr中第x个数存入新数组a[i](x从0开始,到6后变为1)。
部分三:重新连接链表
<span style="font-size:14px;"> 8048e44: 8b 75 d0 mov -0x30(%ebp),%esi //取数组a首地址
8048e47: 8975 cc mov %esi,-0x34(%ebp) //更新原链表首地址
8048e4a: bf01 00 00 00 mov $0x1,%edi // i = 1
8048e4f: 8d55 d0 lea -0x30(%ebp),%edx //数组a首地址
8048e52: 8b04 ba mov (%edx,%edi,4),%eax //ptr[i] => eax
8048e55: 8946 08 mov %eax,0x8(%esi) //ptr[i-1]->next = ptr[i]
8048e58: 89c6 mov %eax,%esi //esi = ptr[i]
8048e5a: 47 inc %edi
8048e5b: 83ff 05 cmp $0x5,%edi
8048e5e: 7ef2 jle 8048e52 <phase_6+0xba>
8048e60: c746 08 00 00 00 00 movl $0x0,0x8(%esi)
8048e67: 8b75 cc mov -0x34(%ebp),%esi //最后一个结点的next赋0
</span>
部分四:检验链表值
<span style="font-size:14px;"> 8048e6a: 31 ff xor %edi,%edi // i = 0
8048e6c: 8d74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048e70: 8b56 08 mov 0x8(%esi),%edx //esi+8 的值为下一个结点地址
8048e73: 8b06 mov (%esi),%eax //取当前结点第一个元素值
8048e75: 3b02 cmp (%edx),%eax
8048e77: 7d05 jge 8048e7e <phase_6+0xe6> //取当前结点第一个元素值和下一个结点第一个元素值比较,必须大于,否则爆炸
8048e79: e87e 06 00 00 call 80494fc<explode_bomb>
8048e7e: 8b76 08 mov 0x8(%esi),%esi
8048e81: 47 inc %edi
8048e82: 83ff 04 cmp $0x4,%edi
8048e85: 7ee9 jle 8048e70 <phase_6+0xd8> </span>
从这部分看出,要求重建后的链表每个结点的第一个元素都要大于后面一个结点的第一个元素。
第一个元素的大小分别为:fd 2d5 12d 3e5 d4 1b0
从大到小排列后:3e5 2d5 1b0 12d fd d4
参考第二个元素对应的输入值为:4 2 6 3 1 5
参考资料:spch2008大神的博客,最后一关帮助很大!http://blog.csdn.net/spch2008/article/details/29393915
今天的文章【CSAPP】二进制炸弹 实验分析分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/33281.html