简单来说,栈
是一种 LIFO(Last In Frist Out,后进先出)
形式的数据结构。栈一般是从高地址向低地址增长,并且栈支持 push(入栈)
和 pop(出栈)
两个操作。如下图所示:
push
操作先将 栈顶(sp指针)
向下移动一个位置,然后将数据写入到新的栈顶;而 pop
操作会从 栈顶
读取数据,并且将 栈顶(sp指针)
向上移动一个位置。
例如,将 0x100 压入栈,过程如下图所示:
我们再来看看 出栈
操作,如下图所示:
栈帧
,也就是 Sack Frame
,其本质就是一种栈,只是这种栈专门用于保存函数调用过程中的各种信息(参数,返回地址,本地变量等)。
栈帧
有 栈顶
和 栈底
之分,其中栈顶的地址最低,栈底的地址最高。SP(栈指针) 就是一直指向栈顶的。在 x86 的 32 位 CPU 中,我们用 %ebp
寄存器指向栈底,也就是基址指针;用 %esp
寄存器指向栈顶,也就是栈指针。下面是一个栈帧的示意图:
一般来说,我们将 %ebp
到 %esp
之间区域当做栈帧。并不是整个栈空间只有一个栈帧,每调用一个函数,就会生成一个新的栈帧。
在函数调用过程中,我们将调用函数的函数称为:调用者(caller)
,将被调用的函数称为:被调用者(callee)
。在这个过程中:
调用者
需要知道在哪里获取 被调用者
返回的值(一般存放到 %eax
寄存器)。被调用者
需要知道传入的参数在哪里和调用完后的返回地址在哪里。被调用者
返回后,%ebp
和 %esp
寄存器的值应该和调用前一致。现在,我们来看看函数调用时,栈帧是如何变化的。
我们以一个函数调用的实例来解说,代码如下:
// stack.c
int add_func(int a, int b)
{
int c, d;
c = a;
d = b;
return c + d;
}
int main(int argc, char *argv[])
{
int total;
total = add_func(1, 2);
return 0;
}
我们使用命令 gcc -S -m32 stack.c
来编译上面的代码,获取的汇编代码如下所示(去掉一些无关紧要的信息):
add_func:
pushl %ebp // 保存ebp寄存器到栈
movl %esp, %ebp // 把ebp进程设置为esp的值
subl $16, %esp // 为局部变量申请空间
movl 8(%ebp), %eax // 把参数a保存到eax寄存器中
movl %eax, -8(%ebp) // 把eax寄存器的值保存到局部变量c中(c = a)
movl 12(%ebp), %eax // 把参数b保存到eax寄存器中
movl %eax, -4(%ebp) // 把eax寄存器到值保存到局部变量d中(d = b)
movl -8(%ebp), %edx // 把d的值保存到edx寄存器中
movl -4(%ebp), %eax // 把c的值保存到eax寄存器中
addl %edx, %eax // 将eax寄存器与edx寄存器的值相加,保存到eax中(返回值)
leave
ret // 函数返回
...
可能汇编代码比较难看懂,我们用下面的插图来说明这个调用过程:
如上图所示,调用过程如下:
main()
函数调用 add_func()
函数前,先将调用 add_func()
函数的参数压栈。add_func()
函数时,会将 返回地址
压栈,接着进入 add_func()
函数。add_func()
函数执行时,会将原来的 ebp寄存器
的值压栈,然后把 ebp寄存器
的设置为 esp寄存器
的值。add_func()
函数会为局部变量申请空间,也就是将 esp寄存器
向下移动。c
设置为参数 a
的值,局部变量 d
设置为 参数 b
的值。eax寄存器
中(C语言规定以 eax寄存器
传递返回值),然后调用 ret
指令返回到 main()
函数。上面介绍了 函数调用
的过程,现在我们来介绍一下函数调用完毕后,从被调用函数返回到原来的函数过程是如何处理的。
从 add_func()
函数的汇编代码可以看到,当被调用函数执行完毕返回到调用函数前,会执行 leave
指令,这条指令等价于:
movl %ebp, %esp
popl %ebp
这两条汇编指令的意思是,将 esp寄存器
和 ebp寄存器
恢复到调用函数前的值。
然后,调用 ret
指令返回到原来的函数。ret
指令会从栈顶获取 返回地址
,然后跳转到(jmp指令
)此地址继续执行。这时的 栈帧
的结构如下图所示:
前面说了那么,都是为了 栈溢出攻击
这节作铺垫的。通过前面的学习,我们知道调用函数的 参数
、执行完函数后的 返回地址
和被调用函数的 局部变量
都是存放在栈中的。
如果在调用函数时,不小心将 返回地址
覆盖了,那么调用完函数后,将不会跳转到原来的函数继续执行,而是跳转到覆盖后的地址执行。如下图所示:
那么,怎样才能把 返回地址
覆盖呢?我们可以通过下面的例子来说明:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#define PTR_SIZE 8 // 指针的大小
#define EBP_SIZE 8 // ebp寄存器的大小
void inject_callback()
{
printf("inject_callback called...\n");
exit(0);
}
void func_call(char *addr, int len)
{
char tmpBuf[16] = {0xff};
memcpy(tmpBuf + 16 + EBP_SIZE, addr, len);
printf("func_call called...\n");
}
int main(int argc, char** argv)
{
uint64_t injectPtr = (uint64_t)&inject_callback;
func_call(&injectPtr, PTR_SIZE);
printf("main exited...\n");
return 0;
}
我们使用以下命令编译上面代码,并且执行:
$ gcc stack-overflow.c -fno-stack-protector -o stack-overflow
$ ./stack-overflow
func_call called...
inject_callback called...
在编译上面程序时,一定要加上
-fno-stack-protector
参数,否则将会触发栈溢出保护,导致执行失败。
在上面的代码中,我们并没有直接调用 inject_callback()
函数,而是通过把 inject_callback()
函数的地址复制到 func_call()
函数的局部变量 tmpBuf
中。
由于局部变量 tmpBuf
的类型为字符串数组,而且大小为 16 个字节。但我们复制数据是从 24(16 + 8)处开始复制,已经超出了局部变量 tmpBuf
的大小,如下图所示:
从上图可以看出,func_call()
函数在调用 memcpy()
函数复制数据时,由于不小心用 inject_callback()
函数的地址覆盖了返回地址,导致 func_call()
函数执行完毕后,跳转到 inject_callback()
函数处执行。
这就是 栈溢出攻击
的原理,而导致 栈溢出攻击
的原因就是:调用 memcpy()
、strcpy()
等函数复制数据时,没有对数据的长度进行验证,从而 返回地址
被复制的数据覆盖了。
黑客可以利用 栈溢出攻击
来把函数的返回地址修改成入侵代码的地址,从而实现攻击的目的。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/cmLp5aHfqy1-wYiNiqdq-Q
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。