编译阶段:编译器ccl将预处理后的文件翻译成.s结尾的文本文件,里面包含一个汇编程序。...其中push指令相当于这两条指令subq $8 %rsp,mov %rbp %rsp; 算数和逻辑运算 ?...x86-64的过程实现包括特殊的指令和一些对机器资源使用的约束。 1.3.1运行时的栈 当x86-64过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间(栈帧)。...下图给出了运行是栈的通用结构,包括划分“栈帧”。当前正在执行的过程的栈帧总是在栈顶。(数据段、代码段、堆栈段、BBS段的区别) ? ?...程序重置了%esx寄存器的值,把栈指针加24,释放了栈帧。 寄存器的局部存储空间 寄存器是唯一在所有过程中共享的资源。
A调用完B后还需要继续执行,继续执行的位置需要保存起来。 ---- 下面分析x86的具体实现。 (资料汇编) 速查: 对于栈帧来说:栈帧顶部用bp指针(高地址),栈帧底部(低地址)用sp指针。...ebp:帧指针,保存当前栈帧顶部地址(高地址)。 esp:堆栈指针,保存当前堆栈底部地址(低地址)。...image.png step11:从堆栈中删除参数 继续讲堆栈上的参数弹出到寄存器,然后删除esp栈顶以下的元素。栈顶以下的元素已经不在栈中,没有意义。...# 栈帧顶部入栈 0x0000000000401123 : 48 89 e5 mov %rsp,%rbp # 栈帧顶部指针rbp指向新栈帧顶部 4...# 帧顶位置 入栈 0x000000000040113d : 48 89 e5 mov %rsp,%rbp # rbp帧顶指针,指向新帧顶
调用约定指定他们其中的一些寄存器有特殊的用途,例如: r0-r3:用于存放传递给函数的参数; r4-r11:用于存放函数的本地参数; r11:通常用作桢指针fp(frame pointer寄存器),栈帧基址寄存器...在这个过程中用到了上面说的寄存器,fp帧指针,它总是指向当前帧的底部;sp栈指针,它总是指向当前帧的顶部。这两个寄存器用来定位当前帧中的所有空间。...而从func的代码可以看到,首先使用push rbp将帧指针保存起来,而由于刚跳转到func函数,此时rbp其实是上一个栈帧的帧指针,即它的值其实还是上一个栈帧的底部地址,所以此步骤其实是将上一个帧底部地址保存了下来...而栈顶部又正好是刚刚push进去的存储上一个帧指针地址的地址,所以rbp指向的时当前栈帧的底部,但其中保存的值是上一个栈帧底部的地址。...至此,也就解释了为什么fp指向的地址存储的内容是上一个栈帧的fp的地址,也解释了为什么fp向前一个地址就正好是lr。
A调用完B后还需要继续执行,继续执行的位置需要保存起来。 ---- 下面分析x86的具体实现。 (资料汇编) 速查: 对于栈帧来说:栈帧顶部用bp指针(高地址),栈帧底部(低地址)用sp指针。...ebp:帧指针,保存当前栈帧顶部地址(高地址)。 esp:堆栈指针,保存当前堆栈底部地址(低地址)。...step10:弹出eip 继续使用esp弹出old eip的值赋给eip。 step11:从堆栈中删除参数 继续讲堆栈上的参数弹出到寄存器,然后删除esp栈顶以下的元素。...# 栈帧顶部入栈 0x0000000000401123 : 48 89 e5 mov %rsp,%rbp # 栈帧顶部指针rbp指向新栈帧顶部 4...# 帧顶位置 入栈 0x000000000040113d : 48 89 e5 mov %rsp,%rbp # rbp帧顶指针,指向新帧顶
防止代码重用攻击 利用内核的常用方法是使用错误来覆盖存储在内存中的函数指针,例如存储了回调函数的指针,或已被推送到堆栈的返回地址。...虽然我们继续将 GNU 汇编程序用于独立的汇编代码,但 LTO 要求我们切换到 LLVM 的集成汇编程序以进行内联汇编,并将 GNU gold 或 LLVM 自己的 lld 作为链接器。...如前一节所述,我们在 Pixel 3 上启用 CFI 时遇到的最常见问题是由函数指针类型不匹配引起的良性违规。...当内核遇到这种违规时,它会打印出一个运行时警告,其中包含失败时的调用堆栈,以及未通过 CFI 检查的目标调用。更改代码以使用正确的函数指针类型可以解决问题。...虽然我们已经修复了 Android 内核中所有已知的间接分支类型不匹配的问题,但在设备特定的驱动程序中仍然可能发现类似的问题,例如。
在堆栈的外部(在 x86 和 x86_64 上向下增长,这意味着随着内存地址变大,内存地址会下降),程序的其他部分被存储和操作。通常,我们进行黑客攻击的想法是按照我们认为合适的方式重定向程序流。...这是通过写入越过缓冲区的末尾并任意覆盖堆栈来完成的。...随机键,您就会覆盖堆栈。除非仔细挑选输入的数据,否则这通常只会导致崩溃,更常见的是所谓的分段错误。 [marshall@jerkon]{11:14 PM}: [~/Hack/bof_wt] $ ....下一部分需要一些反复试验,您需要弄清楚可以在缓冲区 u 末尾插入多少个 A(十六进制 0x41),直到完全覆盖 RIP 地址(返回指令指针)。...我们将要覆盖返回指针, 0x55555555519b以便跳过 p 条件。 您需要重新计算 A 的数量作为要使用的填充,通常是您使用的数字 - 6。
完成这一步后,就完成了保留上一帧的基地址,初始化本帧的栈顶地址。 这里以我debug的地址为例,此时rbp 的值为 0x730,rsp值也为0x730 2....看到这里便有一个疑问,其实做一个传递立即数的操作,为什么需要先传递到内存,再传递到寄存器用于函数调用呢?...(%rip), %rdi ; "result=%d\n" 6.子函数调用 将前一个堆栈的栈基地址寄存器rbp入栈。...(%rbp), %eax 8 子程序跳出函数,跳转回到main函数 执行前的堆栈 最后便是回到main函数的步骤。...函数如无返回值时,显式声明void类型的返回 听起来其实非常简单,日常编程中也不容易遗漏。这里提及一下C的早期版本中,支持不填返回值。且默认的返回值为int。
帧指针 R12 ip 指令指针 R13 sp 栈指针 R14 lr 连接寄存器 2、堆栈使用规则: ATPCS规定堆栈为FD类型,即满递减堆栈。...而对于汇编程序来说,如果目标文件中包含了外部调用,则必须满足以下条件: 外部接口的数据栈一定是8位对齐的,也就是要保证在进入该汇编代码后,直到该汇编程序调用外部代码之间,数据栈的栈指针变化为偶数个字;.... 1.参数个数可变的子程序参数传递规则 对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数....举例: 使用r0 接收返回值 int func1(int m, int n) m -- r0 n -- r1 返回值给 r0 「为什么有的编程规范要求自定义函数的参数不要超过4个?」...;第5个参数通过堆栈传递 ADD R3, R1, R1 ;计算4*i(第4个参数) BL fcn ;调用C程序 ADD sp, sp, #4 ;从堆栈中删除第五个参数 .end 假设程序进入
ThreadStackSpoofer是线程堆栈欺骗技术的一个示例实现,旨在规避恶意软件分析、反病毒产品和EDR在检查的线程调用堆栈中查找Shellcode帧的引用。...其思想是隐藏对线程调用堆栈上针对Shellcode的引用,从而伪装包含了恶意代码的内存分配行为。...函数的返回地址会分散在线程的堆栈内存区域周围,由RBP/EBP寄存器存储其指向。...为了在堆栈上找到它们,我们需要首先收集帧指针,然后取消对它们的引用以进行覆盖: *(PULONG_PTR)(frameAddr + sizeof(void*)) = Fake_Return_Address...工具使用演示 下面的例子中,演示了没有执行欺骗技术时的堆栈调用情况: 开启线程堆栈欺骗之后的堆栈调用情况如下图所示: 上述例子中,我们可以看到调用栈中最新的帧为MySleep回调。
3.3 栈的对齐问题 System V AMD64要求栈必须按照16字节对齐,就是说在通过call指令调用目标函数之前栈顶指针即rsp指针必须是16的倍数。...subq $16, %rsp # 栈顶地址-16,栈扩容,这里没搞懂为什么要扩容,有懂的同学欢迎评论区指点下 这三条指令是用来分配栈帧的,执行完成后栈变成下方的样子: 继续往下看: movabsq...开头仍然是建立栈帧的指令,执行完成后,此时栈帧的样子如下: 继续往下看: movq %rdi, -24(%rbp) movq %rsi, -32(%rbp) movq -32(%rbp...寄存器 movq %rax, -8(%rbp) movq -8(%rbp), %rax # 将返回值保存到rax寄存器 这里没搞懂为什么需要先挪到内存中再保存到rax寄存器上,可能是编译器实现起来比较方便吧...,有懂的同学欢迎评论区指点下 此时栈情况: foo函数最后执行了以下两条指令: popq %rbp # 将栈顶值pop出来保存到rbp寄存器,即修改栈基底地址为当前栈顶值,同时栈顶指针-8
在分析上面的汇编程序之前,需要了解rbp、rsp为栈基址寄存器、栈顶寄存器,分别指向栈底和栈顶;edx、eax、esi、edi均为x86CPU上的通用寄存器,可以存放数据(虽然它们还有别的作用,但是本文章不涉及...对上面汇编代码的分析: 进入main函数,保护现场,将rbp压入堆栈; 然后为main函数开拓新的堆栈框架,rbp与当前rsp相同,rsp再向上扩充16个字节(0x10);(以前的C程序只能在函数前面声明变量...; 再次将main的rbp压栈,保护; 新的rbp与当前rsp相同,把通用寄存器中的数据赋给栈底上方偏移地址为4和8的单元(此为函数参数传递的关键); 将传入新栈的参数赋给通用寄存器,进行加法操作,结果存入...eax; pop出rbp,回到main函数; 将eax中的运算结果赋给栈底上方偏移地址为4的单元; 然后调用printf函数显示结果。...这段代码的解析与x86类似,只不过需要了解几个arm汇编指令和寄存器名称。fp为帧寄存器,起“标签”作用。
ASLR和DEP配合使用,能有效阻止攻击者在堆栈上运行恶意代码。...,尤其是存储函数指针的区域。...为什么不传递 “/bin/sh”的字符串地址到最后调用的system(“/bin/sh”),而是将”/bin/sh”写入 bss段 因为这里rdi=r15d=param1 r15d 32-bit 所以不能传递给...漏洞点 在delete操作上发现调用free指针函数释放结构后没有置结构指针为NULL,这样就能实现UAF, 如下图 ?...这样就可以恶意构造结构数据,利用uaf覆盖旧数据结果的函数指针,打印出函数地址,泄露出二进制base基址,主要逻辑如下: create(4 创建old_chunk0 但是程序占位 old_chunk0_
在上图中,堆栈指针的顺序如下: 栈指针当前指向第 3 帧。 指令指针寄存器指向的代码调用一个新函数。...堆栈指针将更新为指向 Frame 4,该 feame 可能负责指令指针中此新调用函数中的暂存空间和数据。 函数的具体执行在第 4 帧中完成,执行完之后指针从第四帧弹出,并继续指向第三帧....当一个函数序言完成设置时,RBP 的内容将指向堆栈帧下面的前一个 RBP 注意:当您通过单击 Xcode 中的帧或使用 LLDB 跳到另一个堆栈帧时,RBP 和 RSP 寄存器都将更改值以对应于新的帧!...您可以通过选择不同的帧并在 LLDB 控制台中键入 cpx rbp 或 cpx rsp 在 LLDB 中对此进行验证。 那么,为什么这两个寄存器很重要?...rdx = 0x0040000000000000 在本节中,dumpreg 的输出将覆盖在每个汇编指令上,以准确显示每个指令期间每个寄存器发生的情况。
内存为什么叫堆栈 因为它的存储方式是堆叠的,水位线是指的栈顶,它也是一个内存地址,保存才rsp寄存器里。...堆栈不仅能存放函数返回地址,还能存放参数、栈变量和其他的数据,这也是每次函数调用都要存储恢复rbp寄存器的原因 堆栈溢出例子:无穷递归 手动回溯函数调用轨迹: 从CPU视角认识函数指针 两个函数的汇编指令完全相同...普通变量因为用法、字节长度的不同需要定义不同的变量类型,函数也不例外,参数返回值的不同也需要事先定义(typedef)相应类型的函数指针,从而帮助主调函数正确的给函数指针传递参数和获取返回值。...红色水位线是寄存器rsp的值,用来表示栈顶的内存地址,蓝色基准线是寄存器rbp 的值,用来表示main函数的栈帧基地址。...func() 把rbp寄存器的值压入栈顶,栈顶水位线也随之升高,至此main函数的栈帧保护工作完成。
(一)一个简单的汇编例子 上面讲的有些空洞,来看一个实际的例子: ; 将寄存器rsp的值存储到寄存器rbp中mov rbp, rsp ; 将四个字节的4存储到地址为rbp-4的栈上mov...%esp,%ebp总被分别用着指向当前栈帧的顶部和底部,主要用于在当前函数推出时,将他们还原为原始值;往往会在栈帧开始处保存上一个栈帧的ebp,而esp是全栈的栈顶指针,一直指向栈的顶部。...rsp是堆栈指针寄存器,通常会指向栈顶位置,堆栈的pop和push操作就是通过改变rsp的值即移动堆栈指针的位置来实现的。 rbp是栈帧指针,用于标识当前栈帧的起始位置。...保存返回地址和保存上一栈帧的%rbp都是为了函数返回时,恢复父函数的栈帧结构(保存函数调用上下文)。...所以传递数据的时候,要知道传递的数据大小: Intel格式的汇编会在数据前面说明数据大小:比如 mov DWORD PTR [rbp-4],4,意思是将一个4字节的4存储到栈上(地址为rbp-4)。
in print at gdb_example.c:4 显示栈帧 backtrace命令可以在遇到断点而暂停执行时显示栈帧。...add %al,(%rax) 显示反汇编 格式 disassemble disassemble 程序计数器 disassemble 开始地址 结束地址 格式1为反汇编当前整个函数,2为反汇编程序计数器所在函数的整个函数...gcore 'pidof gdb_example' 该命令无需停止正在运行的程序,可以直接从命令行直接生成转储文件。当需要在其他机器上单独分析问题原因时,或者是分析客户现场问题时十分有用。...反复执行 ignore 断点编号 次数 在编号指定的断点,监视点忽略指定的次数 continue与ignore一样,也可以指定次数,达到指定次数前,执行到断点时不暂停。...最后向大家推荐一个github上的.gdbinit文件:https://github.com/gdbinit/Gdbinit,把这个弄懂,相信gdb脚本文件就不在话下了。
指令sub rsp, 0x10在栈上为局部变量开辟了存储空间,即从rbp所指地址到rsp所指地址的内存区域(16个字节),足以存储int型变量a。这块内存空间被称为栈帧。...五、对栈的进一步探究 1、局部变量为什么要初始化 当栈上的局部变量被释放的时候,他们并未被完全清理。他们所占内存区域,填充的仍然是原来的数值,而这一区域在接下来可能被其他函数用到。...a位于rbp - 0xc所指的内存区域 b位于rbp - 0x8所指的内存区域 c位于rbp - 0x4所指的内存区域 注意,这几个变量在栈上的排列顺序和代码中定义的顺序并不一致。...前面解释过,该指令会导致栈帧收缩,如下图所示: ? 当我们调用函数func2时,它的栈帧如下图所示,局部变量的值就是当前栈上残留的值。这就是func2中变量a、b、c的值和func1一致的原因了。...所以,我们只需要将变量rsp转换为unsigned long int类型的指针,并解析该指针指向内存区域的值即可:*(unsigned long int *)rbp。
该函数的开头的push rbp; mov rbp, rsp; sub rsp, 0x40,先保存rbp的数值,再令rbp等于rsp,然后将栈顶指针rsp减小0x40(也就是64),相当于在栈上分配长度为...再结合函数结尾的leave; ret,同时类比一下32位汇编中的函数栈帧布局,可以画出本程序中main函数的栈帧布局如下(请注意下图是按栈顶在上、栈底在下的方式画的): ...rbp即函数的栈帧基指针,在main函数中,name数组保存在rbp-0x40~rbp+0x00之间,rbp+0x00处保存的是上一个函数的rbp数值,rbp+0x08处保存了main函数的返回地址...当main函数执行完leave命令,执行到ret命令时:上一个函数的rbp数值已重新取回至rbp寄存器,栈顶指针rsp已经指向了保存这个返回地址的单元。...但是,如果用户输入了精心挑选的字符后,覆盖在这里的数值是一个合法的地址呢?如果这个地址上恰好保存了用户想要执行的恶意的指令呢?会发生什么事情?
有的文档将ESI,EDI也称为通用寄存器, 因为他们也是程序可自由读写的, 不过他们不支持部分引用. EBP/ESP分别称为栈基指针和栈指针, 分别指向 当前栈帧的栈底和栈顶...., 更多的是体现在调用约定(Calling Convention)上....rip指针并没有被我们的序列覆盖到....其实也很简单, 只要在溢出后打上断点, 并查看$rbp+8就是我们将要覆盖的rip值了....() (gdb) p $rip $1 = (void (*)()) 0x42424242 确实是BBBB覆盖了返回的指针. 所以栈的布局和32位下应该是类似的.
基于堆栈的缓冲区溢出类似于前面的堆示例,因此,当程序向缓冲区写入的数据超过堆栈分配的处理量时,可能会导致覆盖现有堆栈数据,并在覆盖指令指针时导致拒绝服务或任意代码执行。...如果输入值大于其长度,它将覆盖金丝雀值,导致程序抛出分段错误(segfault),因为输入值的内容试图覆盖内存的受限区域。过去,Linux允许在堆栈上执行指令。...RSP(堆栈指针)、RBP(基指针)和RIP(指令指针)是帮助促进程序执行的重要寄存器。...基指针用于记住堆栈的底部(即end)所在的位置,指令指针保存CPU正在执行的指令的地址。对于缓冲区溢出,如果可以控制RBP,就可以控制RIP并获得对执行位置的控制。...正如我在步骤4中提到的,RSP和RBP很重要,因为我们需要使用这些位置来识别偏移量并执行恶意负载。
领取专属 10元无门槛券
手把手带您无忧上云