MENU
大家如果做boomlab还碰到了什么有意思的错误可以留言或者发我邮箱(hwk2019ucb@163.com)哈
boomlab还有30s到达
嘎嘎,今天有机会做了系统级程序设计课程布置的作业!
韩波老师真的非常nice!!!助教也很nice!!!
下面开始本次实验的介绍:
首先我是在虚拟机中使用!
配置的是VMware 15和Ubuntu18(好像是的,不确定,不重要)
实验1
首先我们会获得一个可执行文件和一个.c的文件,readme被我删掉了
肯定有人想和我一样想直接跑一跑可执行文件!然后你可能就会碰到问题!
那么有事问百度,我找到了解决办法
果不其然我找到了解决办法,好像是如此就可以给予权限还是啥的,但不管了,可以执行!
然后我随便输入了我的名字缩写!
果然炸了!
那么开始我们的下一步正式研究了!
Step1–反汇编
我们先把可执行文件反汇编成一个.s文件
vim大法
然后我们可以装模作用的来看看这里都是些啥
但肯定像我这种性格,没有耐心看完
想看的可以
vim bomb.s
问题来了,记得安装vim…
这里有两种很酷炫的操作,其实也很normal haha
第一种是垂直显示两个,第二个是竖直的并列两个!
我个人喜欢第二个
检查boom原因
我们可以发现mov了一个值进$esi然后比较了string not equal
这肯定是string不一致就boomb,然后我们可以用gdb调试
gdb调试出结果
先安装gdb!
然后输入指令
gdb bomb
就可以快乐的调试辣
我们在phase_1处加断点!
(gdb) r 表示运行代码哦
随便输入一个字符串
disass 用于反汇编
examine
可以使用examine命令(简写是x)来查看内存地址中的值
那么我们查看0x402400
答案出来啦!!!!
quit
输入quit退出gdb调试哦
实验1至此结束
撒花撒花
Border relations with Canada have never been better.
实验二
分析汇编语言
push %rbx 保存rbx的值
sub $0x28,%rsp 将栈的长度增加0x28以便字符串存储
callq <read_six_numbers> 调用函数读取六个字符(应该会把字符串首地址给rsi 也就是放在上一帧栈顶rsp)
通过callq调用函数read_six_numbers,从函数名就可以知道是从我们的输入中获取6个数字。那么就可以大胆的猜测,刚才开辟栈空间的操作是在栈上分配了一个具有6个元素的数组,至于数组元素是什么类型,目前不得而知。并且可以判断函数read_six_numbers具有两个参数,第一个参数使我们的输入input,第二个参数就是数组第一个元素的首地址,即函数read_six_numbers的原型为**void read_six_numbers(const char input, TYPE p)
这里我研究了很久最后看了半天没看出个所以然,我就用gdb调试了下
当当当!!!我发现了!!!!
PS:
- MOV指令的功能是传送数据,例如MOV AX,[1000H],作用是将1000H作为偏移地址,寻址找到内存单元,将该内存单元中的数据送至AX;
- LEA指令的功能是取偏移地址,例如LEA AX,[1000H],作用是将源操作数[1000H]的偏移地址1000H送至AX。理解时,可直接将[ ]去掉,等同于MOV AX,1000H。
原来函数read_six_numbers是通过函数sscanf从我们的输入中获得6个数字的。这就好办了,根据函数sscanf的原型,我们来仔细看看调用函数sscanf之前,它需要的参数是如何传递进去的。
第一个参数肯定是我们的输入input的首地址,它目前储存在寄存器%rdi中,那么第二个参数应该是sscanf需要的格式化字符串,从mov $0x4025c3,%esi中知道这个格式化字符串存储在地址0x4025c3处,用x/s命令查看,原来是"%d %d %d %d %d %d",这下我们可以断定,在函数phase_2中的数组元素类型是int型,即int a[6]。
既然格式化字符串中有个6个%d,自然的sscanf函数还需要6个参数,应该分别是数组a每个元素的地址,即分别是**&a[0],&a[1],&a[2],&a[3],&a[4],&a[5]。
根据调用约定,前4个元素的地址应该用寄存器传递,分别是%rdx、%rcx、%r8、%r9**
最后两个通过栈来传递,通过lea 0x10(%rsi),%rax,mov %rax,(%rsp)和lea 0x14(%rsi),%rax,**mov %rax,0x8(%rsp)**分别将&a[4]和&a[5]压栈,这就完成了sscanf所有参数的传递任务。
如有不懂请反复看上面那张图
&a[0]
&a[1]
&a[2]
&a[3]
&a[4]
一定要注意!!!这里tmd是16进制!!!!!!我看了好久为什么是10!!!!!!!!
&a[5]
写成这样了如果还看不懂的话,建议原地爆炸
如果没仔细看的话,你下棋必被祖安人民指指点点
让我们回到phase_2函数
mov %rsp,%rsi 将栈顶的的地址送给指针寄存器,rsp为字符串首地址
cmpl $0x1,(%rsp) 把第一个数字和1比较!
je 400f30 <phase_2+0x34> 如果相等跳到400f30,让我们思考下这里写了什么东西
艹,这里写到了函数一开始就压入栈的两个寄存器,使之分别指向&a[1]和&a[6]。读者可能会问,数组a只有6个元素啊,那么最后一个元素不是应该是a[5]吗? 注意,这里取的是a[6]的地址,这在标准C中是允许的,只要不取这个地址处的值都是OK的,而标准允许这样做是有原因的,这样就可以很方便在循环中来遍历数组了。
jmp 0x400f17 <phase_2+27>,jmp到地址0x0000000000400f17处继续执行。
如果仔细观察地址0x0000000000400f2c处也有个jmp到地址0x0000000000400f17处的指令,我们可以判定这里应该是个循环语句,那么循环体的内容可以根据地址0x0000000000400f17 ~ 地址0x0000000000400f29处的指令来获知。
假设第一次进入循环体,现在寄存器**%rbx指向&a[1],那么mov -0x4(%rbx),%eax**就是把a[0]的值存储寄存器%eax,add %eax,%eax将寄存器%eax的值乘以2,cmp %eax,(%rbx)就是将现在%eax的值与a[1]比较,也就是a[0]乘以2后的值与a[1]比较。如果不相等, 则触发炸弹,相等则%rbx指向下一个元素的地址,在这里就是&a[3]。好了我懂了就是乘以2从1开始
1 2 4 8 16 32
ENDING
tql,柯少是真的智慧
实验三
个人感觉这个实验有点脑瘫,其实韩老师上课基本都说了,还蛮简单的…、
依旧断点找到phase_3的函数来看干了些啥
有了分析phase_2的经验,很容易从寄存器%esi中获得格式化字符串,x/s显示的结果是"%d %d",即从我们的输入中获得两个int型整数。我们不妨将第一个整数命令为num1,第二个为num2。
紧接着判断sscanf的返回值,如果小于等于1,则触发炸弹。否则继续,这里都还蛮简单,后面有意思了!
cmpl $0x7,0x8(%rsp),判断num1是否大于7,如果大于,则调整到地址0x0000000000400fad处,触发炸弹。否则继续。
下一步就是核心了
熟悉不熟悉!!!!!韩老师牛逼!!!
第一条指令,mov 0x8(%rsp),%eax,将num1的值存储寄存器%eax中,第二条指令jmpq *0x402470(,%rax,8),这条指令什么意思呢?
可能AT&T的汇编指令不太容易看懂,那我们通过set disassembly-flavor intel来查看intel形式的这条指令为jmp QWORD PTR [rax*8+0x402470],这下就容易多了 – 取出地址rax*8+0x402470处的值,并调转到这个值指示的内存地址处继续执行。
起飞,原地起飞,快来看看这个地址放了些啥
因为上面判断num1小于8,因此可知跳转表中应该存储有8个地址。x表明以十六进制的形式显示地址,g表示每8个字节的内存,因为这是x64平台,所以地址占8个字节
这些地址都是在phase_3中存在的!!!!!
答案
当num1等于0时,跳转到0x0000000000400f7c处执行。如果num2不等于0xcf,则触发炸弹。
当num1等于1时,跳转到0x0000000000400fb9处执行。如果num2不等于0x137,则触发炸弹。
当num1等于2时,跳转到0x0000000000400f83处执行。如果num2不等于0x2c3,则触发炸弹。
当num1等于3时,跳转到0x0000000000400f8a处执行。如果num2不等于0x100,则触发炸弹。
当num1等于4时,跳转到0x0000000000400f91处执行。如果num2不等于0x185,则触发炸弹。
当num1等于5时,跳转到0x0000000000400f98处执行。如果num2不等于0xce,则触发炸弹。
当num1等于6时,跳转到0x0000000000400f9f处执行。如果num2不等于0x2aa,则触发炸弹。
当num1等于7时,跳转到0x0000000000400fa6处执行。如果num2不等于0x147,则触发炸弹。
所以答案这不就好起来了?????
随便拿一个去测试就完事了,快快乐乐
答案如下
(0,207)、(1,311)、(2,707)、(3,256)、(4,389)、(5,206)、(6,682)、(7,327)
实验四
这个实验还是老办法gdb调试调试
我们首先分析是输入的是什么:
又是两个数字,那么我们可以暂时把他们假设成num1,num2
在代码中有一句话我们可以看到:
这里比较的是num1和14的大小关系,当低于或等于时候就跳转,否则就炸了
所以我们可以知道num是要小于等于14的
这里我们可以知道num2的值不为0也会炸,所以两个参数,第二个参数一定是0
之后我们需要分析func4
func4
大致意思就是传了三个参数进去
% edx 是
% esi 是
% edi 中是第一个参数
mov %edx,%eax,将参数c存储在寄存器%eax中。
sub %esi,%eax,用%eax中的值减去%esi,结果存在寄存器%eax中,即c - b的值存储在寄存器%eax中。(14)
为了直观,我们将%eax定义为一个局部变量int x,即int x = c - b;
这几句整合起来就是x = (x>>31 + x) >> 1,大家可以自己推下,蛮简单的
lea (%rax,%rsi,1),%ecx,这句意思是%ecx = %rax + %rsi * 1,其中%rax就是我们刚刚求得的x,%rsi是参数b。因此%ecx = (x>>31 + x) >> 1 + b;
我们将寄存器%ecx定义一个局部变量,名为tmp,即int tmp = (x>>31 + x) >> 1 + b;将x带进去就是int tmp = ((c - b) >> 31 + (c -b)) >> 1 + b;
这里就是比较这个tmp和num1的关系,如果小于等于就跳到0x400ff2
处
这里的意思是比较tmp和num1如果大于等于就去0x401007,原来如此通过小于等于和大于等于,即如果等于就ok!那么最基本的情况就结束了
我们可以继续分析代码得到如下C语言代码
// %edi %esi %edx
static int func4(int a, int b, int c)
{
int tmp = (((c - b) + ((c - b) >> 31)) >> 1) + b;
if (tmp <= a) {
if (tmp == a) {
return (0);
} else {
return func4(a, tmp + 1, c) * 2 + 1;
}
} else {
return func4(a, b, tmp - 1) * 2;
}
}
然后我们写一个测试程序就知道答案有哪些了
#include <stdio.h>
#include <stdlib.h>
// %edi %esi %edx
static int func4(int a, int b, int c)
{
int tmp = (((c - b) + ((c - b) >> 31)) >> 1) + b;
if (tmp <= a) {
if (tmp == a) {
return (0);
} else {
return func4(a, tmp + 1, c) * 2 + 1;
}
} else {
return func4(a, b, tmp - 1) * 2;
}
}
int main(int argc, const char *argv[])
{
int i, result;
for (i = 0; i < 14; ++i) {
result = func4(i, 0, 14);
if (result == 0) {
printf("%d\n", i);
}
}
return 0;
}
程序输出0 1 3 7,因此本阶段的答案有4组,分别为(0,0)、(1,0)、(3,0)、(7,0)。
实验五
先看看phase_5的汇编语言
看着这么多有点压迫感,但是应该不难,一开始的push,submov其实都还好我感觉然后一直到callq语句核心来了
他比较了返回值%eax和6那么这里返回的是字符串的长度,如果不是6就boom,好了这里解决了,不是很难,那么我们看下je语句跳到了什么地方,4010d2
在这里设置寄存器%eax值为0,再跳转到0x000000000040108b指定位置。
然后我们发现这里有个循环体,那么这个是干什么的呢?
mov %cl,(%rsp)
mov (%rsp),%rdx
and $0xf,%edx
这三行只需关注最后一行,即只取字符数值的低4位。例如字符’A’,ascii码值为0x41,取低4位后得到0x01。
movzbl 0x4024b0(%rdx),%edx,又是访问数组,并且是用刚刚才算得的字符低4位数值作为索引来访问这个值。从movzbl得到这是个单字节数组,数组首地址是0x4024b0。我们使用x命令查看下这个数组的内容是什么。
mov %dl,0x10(%rsp,%rax,1),这句又是在访问数组,而且是个局部单字节数组,数组首地址在%rsp + 0x10处,目前猜测这个数组至少6个字节大小。
循环结束后,movb $0x0,0x16(%rsp),将局部单字节数组第7个元素复制为0,所以我们可以得出这个局部数组至少7个字节大小。
最后又调用函数strings_not_equal来比较字符串,这个函数在phase_1中就已经分析过了。
待比较的两个字符串一个是刚刚的局部数组,一个是存储在地址0x40245e处的字符串,我们使用x/s查看是"flyers"。
…然后事情就变得简单了。记得之前那个莫名奇妙的数组么
看来我们输入的6个字符是array数组的下标值。得到的6个字符必须是"flyers",得出这个结论就好办了,接下来就是数数游戏了。要得到字符"flyers",下标值必须依次为9、15、14、5、6和7。用16进制来表示的话,则为0x09、0x0f,0x0e,0x05,0x06和0x07。
也就是说我们输入的
- 第一个字符的低4位的值必须是0x09,查表得出字符’)’、‘9’、‘I’、‘Y’、‘i’、‘y’符合条件。
- 第二字字符的低4位的值必须是0x0f,查表得出字符‘/’、‘?’、‘O’、‘_’、‘o’符合条件。
- 第三个字符的低4位的值必须是0x0e,查表得出字符‘.’、‘>’、‘N’、‘^’、n‘、‘~’符合条件。
- 第四个字符的低4位的值必须是0x05,查表得出字符’%’、‘5’、‘E’、‘U’、‘e’、‘u‘符合条件。
- 第五个字符的低4位的值必须是0x06,查表得出字符‘&’、‘6’、‘F’、‘V’、‘f’、‘v’符合条件。
- 第六个字符的低4位的值必须是0x07,查表得出字符‘’’、‘7’、‘G’、‘W’、‘g’、‘w’符合条件。
因此phase_5的答案就多了,这6组字符随便按顺序组合即可。
然后是我对于实验五的一些更细的思考,与答案无关,答案呢很好做,但是我想弄明白经历每一步:
先列个总结怕自己忘记
- %rax 作为函数返回值使用。
- %rsp 栈指针寄存器,指向栈顶
- %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
- %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防他被修改
- %r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
push %rbx 把rbx寄存器压栈
sub %0x20 %rsp 开辟一个长度是20的空间出来,这些之后应该用得到
mov %rdi % rax 把接收到的第一个参数传递给%rax
mov %fs:0x28,%rax 不知道在干啥,有知道的可以告诉我下,据说是gcc做的一个栈保护检测机制
**mov %rax,0x18(rsp)**这里我怀疑是把%rax的值给到了rsp的24位置偏移的地方,但是这个是在干啥我没想明白
xor %eax %eax 这里在干啥我也没搞清楚,但不妨碍我做题…
callq 4014b <string_length> 这里调用了字符串长度的函数那么这里会有一个返回值给到了eax
cmp %0x6 %eax 这里应该就是看是否是六个字符吧,如果不是就直接炸了
然后就会看到这个函数函数跳走了
mov %0x0 %eax 设置寄存器%eax值为0,再跳转到0x000000000040108b指定…有一说一不干扰我做题,但是这是在干什么我还是不知道,暂且当作置零
之后跳走了
之后的分析就是上面对循环分析了,easy了,做题可以做,细究我炸了
开始最后一个实验!!!!!!
实验六
这个phase_6也太长了…我裂开…甚至有点想打人…如果暴力求解的话会不会有答案…
gdb调试
前6行很老套,分别将寄存器%r14、%r13、%r12、%rbp和%rbx入栈,然后开辟栈空间,这次开辟的栈空间可不小哦,足足0x50个字节
接下来调用函数read_six_numbers,这个函数我们可不是第一次遇到了哦!在phase_2中详细分析过,这个函数需要两个参数,第一个是我们输入的字符串,目前存储在寄存器%rdi中;第二个参数是一个6个int型元素数组的首地址,这里通过寄存器**%rsi**传递。我们断定phase_6内定义了int a[6];它的首地址就是当前栈顶,这点要牢记,因为后续有很多以%rsp为基址的内存访问,看到就会意识到是在访问数组a。
- 使寄存器指向栈顶,也就是数组a的首地址。
- 寄存器%r12d初始化为0,从后续的add r12d, 1来看,它应该是充当一个计数器的作用
- %r13是指向数组a首地址的,因此这里是使寄存器%rbp指向数组a首地址。到目前为止,指向数组a首地址的寄存器有不少,分别为%r13、%r14、%rsi、%rbp。
这5条汇编指令很清晰,取出当前**%rbp指向的数组a元素值与6比较**,如果大于6则触发炸弹,否则跳转到0x401128继续执行。
跳转到地址0x0000000000401128处,寄存器%r12d这个计数器增加1,接着判断是否等于6了,如果等于6了,则跳转到地址0x401153处,观察到这里已经跳出了最外层循环,结束了。所以可以判断%12d是否等于6是结束循环的条件。
我的脑袋要炸了…
如果%12d小于6,继续执行。将%12d的值赋给寄存器%ebx后,后面的一些列操作又是一个循环,从地址0x0040114b处的jmp指令可以判断出来。这个循环做了什么呢?很简单,就是将后续的数组a元素值与a[%12d]比较,如果相等则触发炸弹。这意味着什么? 意思是说a[0]和a[%12d]不能相等。
然后他把ebx加一,之后把ebx和5比较大小,如果小于或等于则跳转了,这里其实是个越界符号判断?然后他又跳回去了,那么这里循环了六个元素并且他们都和第一个不一样,这就是核心了,因为ebx某种程度上是数组的index,六个元素 01 2 3 4 5 大循环结束,到了这里,我们得出结论:数组a的6个元素值不能大于,并且它们彼此不相等。
这段比较简单,寄存器%rax指向数值a首地址,而寄存器%rsi指向a[6],即数组a的最后一个元素的末端,以此为循环条件,分别用7减去数组a的元素,结果再存回数组a中。
首先这里有两层循环,外层循环以寄存器%esi为计数器,初始值为0; 里层循环以寄存器%eax为计数器,并且初值为1。
最为关键的是这里牵扯到两个内存地址,一个是以%rsp + 0x20为基址的内存区域,步长为8,这个肯定是函数phase_6内的局部变量,就跟数组a一样。
另一个地址为0x6032d0,通过调试,发现这个地址是位于数据段.data内的地址,用x命令观察如下:
卧槽,这tm竟然是个链表,我裂开
目前可以猜测nodeX的类型是个结构体,最后一个成员是指向同类型的指针,其他成员类型不得而知
可以看到这里在比较结构体struct node的前4个字节,因此可以初步判断出struct node的第一个成员是int型的整数。
struct node {
int value;
struct node *next;
};
继续观察, 后续代码中访问next时,为什么都是结构体首地址加上偏移值8呢?
原因在于这是x64平台,指针大小占8个字节,而int型占4个字节,因此struct node中的next成员为了内存对齐的需要,会在value和next之间填充4个字节的padding,不过仔细观察上面的截图,这里所谓的padding分别是1、2、3、4、5和6,这些数值好像不是内存里的随意值,更像是特意填充的,因此我们可以假设struct node的完整类型如下:
struct node {
int value;
int seq;
struct node *next;
};
当然了,成员seq在后续代码中并没有什么作用,最后关键部分用到的是成员value;
好了,因此我们可以得出链表在内存中的初始状态如下:
回到代码,从—> **0x00401188 *,可以猜测以%rsp + 0x20为基址的局部变量是个数组,并且数组元素类型为struct node,即指向struct node的指针。因此可以判定函数内定义了一个struct node *nodes[6];数组。
这段代码是在给数组nodes的每个元素赋值,根据对应数组a的元素分别指向不同的nodeX。如果a[%esi]的值小于等于1则nodes[%esi]直接指向node1。否则指向对应的nodeX,其中X的值与a[%esi]的值相等。
循环遍历数组nodes,调整各自的next值。
最后的这段代码最为关键,它告诉我们链表最终的内存状态,得出的结论是:从nodes[0]开始遍历链表,nodeX.i的值是从大到小的顺序排列的,即如下这样:
CNM 我做出来了!!!!!!!!!
因此得到最终的输入应该是: 4 3 2 1 6 5。 Bingo!!!
我从网上找到了牛逼的程序员复原的程序:
#include <stdio.h>
#include <stdlib.h>
static void read_six_numbers(const char *input, int *a)
{
// %rdi %rsi %rdx %rcx %r8 %r9 (%rsp) *(%rsp)
int result = sscanf(input, "%d %d %d %d %d %d", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]);
if (result <= 5) {
explode_bomb();
}
}
struct node {
int value;
int seq;
struct node *next;
};
struct node node6 = {
0x000001bb, 6, NULL
};
struct node node5 = {
0x000001dd, 5, &node6
};
struct node node4 = {
0x000002b3, 4, &node5
};
struct node node3 = {
0x0000039c, 3, &node4
};
struct node node2 = {
0x000000a8, 2, &node3
};
struct node node1 = {
0x0000014c, 1, &node2
};
void phase_6(const char *input)
{
int a[6];
struct node *nodes[6];
read_six_numbers(input, a);
// %r13
int *pa = a;
// %r12d
int i = 0;
for ( ;; ) {
if (*pa > 6) {
explode_bomb();
}
++i;
if (i == 6) break;
int j = i;
do {
if (*pa == a[j]) {
explode_bomb();
}
j++;
} while (j <= 5);
++pa;
}
int *begin = &a[0];
int *end = &a[6];
do {
*begin = 7 - *begin;
++begin;
} while (begin != end);
i = 0;
do {
if (a[i] <= 1) {
nodes[i] = &node1;
} else {
int j = 1;
struct node *pnode = &node1;
do {
pnode = pnode->next;
++j;
} while (j != a[i]);
nodes[i] = pnode;
}
++i;
} while (i != 6);
struct node *head = nodes[0]; // %rbx
struct node **begin_node = &nodes[1]; // %rax
struct node **end_node = &nodes[6]; // %rsi
struct node *tmp; // %rdx
for ( ;; ) {
// %rdx
tmp = *begin_node;
head->next = tmp;
++begin_node;
if (begin_node == end_node) break;
head = tmp;
}
tmp->next = NULL;
i = 5;
head = nodes[0];
do {
tmp = head->next;
if (head->value < tmp->value) {
explode_bomb();
}
head = tmp;
--i;
} while (i != 0);
printf("sucess\n");
}
#if 0
int main(int argc, const char *argv[])
{
phase_6(argv[1]);
return 0;
}
#endif
这个代码都把我看晕了,让我们快去试试对不对!
答案汇总
- Border relations with Canada have never been better.
- 1 2 4 8 16 32
- (0,207)、(1,311)、(2,707)、(3,256)、(4,389)、(5,206)、(6,682)、(7,327)
- (0,0)、(1,0)、(3,0)、(7,0)
- 第一个字符的低4位的值必须是0x09,查表得出字符’)’、‘9’、‘I’、‘Y’、‘i’、‘y’符合条件。
- 第二字字符的低4位的值必须是0x0f,查表得出字符‘/’、‘?’、‘O’、‘_’、‘o’符合条件。
- 第三个字符的低4位的值必须是0x0e,查表得出字符‘.’、‘>’、‘N’、‘^’、n‘、‘~’符合条件。
- 第四个字符的低4位的值必须是0x05,查表得出字符’%’、‘5’、‘E’、‘U’、‘e’、‘u‘符合条件。
- 第五个字符的低4位的值必须是0x06,查表得出字符‘&’、‘6’、‘F’、‘V’、‘f’、‘v’符合条件。
- 第六个字符的低4位的值必须是0x07,查表得出字符‘’’、‘7’、‘G’、‘W’、‘g’、‘w’符合条件。
- 4 3 2 1 6 5
ENDING
问题解决(By hjq)
Question:
mov %fs:0x28,%rax 不知道在干啥,有知道的可以告诉我下,据说是gcc做的一个栈保护检测机制
**mov %rax,0x18(rsp)**这里我怀疑是把%rax的值给到了rsp的24位置偏移的地方,但是这个是在干啥我没想明白
xor %eax %eax 这里在干啥我也没搞清楚,但不妨碍我做题…
Answer:
这段话的意思大概是说 FS: %0x28 存储了一个特殊的标记堆栈的保护值,执行堆栈的保护检查,后面常常有 FS: %0x28 处原始值的XOR操作。若两值相等,则返回零,因为异或操作在两相同值之间的操作结果为0;相反,若是XOR操作不为0,则跳到一个特殊的处理堆栈错误的函数,因为两值不相同表明寄存器损坏了,导致存储在堆栈上的标记值发生了变化。
彩蛋
听闻这个Boomlab实验还有隐藏实验…大家可以试试…