目录 x64汇编第三讲,64位调用约定与函数传参. 一丶复习X86传参 二丶x64汇编 2.1汇编详解 x64汇编第三讲,64位调用约定与函数传参....二丶x64汇编 2.1汇编详解 在x64下,万变不离其宗.大部分跟x86一样....push压栈 参数的值.相应的栈就会抬高.其实x64下,一样会申请.只不过这个地方在进函数的时候并没有值.进入函数之后才会将寄存器的值在拷贝到这个栈中.其实就相当于你还是push了.只不过我是外边申请空间...通过上面来说.我们应该申请 sub rsp,0x20个字节才对.在CALL的时候 x86 x64都是一样的会将返回地址入栈. 那为什么要rsp,0x28.这样的话会多申请一个参数的值哪....所以x64汇编其实也就搞明白了. 1.在调用函数之前,会申请参数预留空间.
解决的办法是在CPU内部开辟一小块临时存储区域,并在进行运算时先将数据从内存复制到这一小块临时存储区域中,运算时就在这一小快临时存储区域内进行。我们称这一小块临时存储区域为寄存器。...正是通过高速缓存的引入,当程序在运行时,就可以预先将部分在内存中要执行的指令代码以及数据复制到高速缓存中去,而CPU则不再每次都从内存中读取指令而是直接从高速缓存依次读取指令来执行,从而加快了整体的速度...我们在高级语言里面看到的只是变量,但是在低级语言里面看到的就是内存地址和寄存器,你可以将内存地址和寄存器也理解为定义的变量,带着这样的思路去阅读汇编代码时你就会发现其实汇编语言也不是那么的困难。...在XCODE中可以很方便的在代码执行到断点时查看当前线程中的所有寄存器中内容(请选择最左下角处的all表示显示所有变量)。我们可以通过下面两张图来查看所有的寄存的信息。 ? 模拟器下的寄存器信息 ?...但是如果是机器指令则不一样了,因为运算时总是要将内存数据移动到寄存器中去,但是寄存器只有一份。
该函数在协程初始化的时候,保存在了 func_ret_addr 成员变量中。 请注意这个变量在结构体中的偏移值:64,下文的 asm_amc_coroutine_enter() 汇编函数就用上了。...参考资料用户态调度要保存些什么中就说明了在 GCC 程序中,需要保存的寄存器内容(x86_64 / x64): rsp:栈指针,指向栈顶,也就是下一个可用的栈地址。...这两句的逻辑如下: 首先 asm_amc_coroutine_dump() 将主线程的上下文保存在一个全局变量中 第二句将堆栈指针移动了一个单位,效果上就是忽略了在函数 asm_amc_coroutine_dump...而由于协程是单线程运行的,因此我们可以使用全局变量判断出刚刚结束的是哪一个协程。 强制跳转到协程的入口处开始执行。 前文不是说了一大堆需要保存的上下文吗,为什么这里赋值的寄存器那么少?...这个时候在汇编中做了以下的事情: 从堆栈中取出函数的返回地址 调用 retq 返回(retq 同时会将返回地址出栈丢掉) 这就是我们前文中将协程返回地址重定向的原理基础。
简单说来,我们在函数中声明的任何局部变量(非静态)都是在栈中分配的(编译期间完成)。并且函数的参数,以及返回值也是依赖于栈。 为了深入地探讨这些概念,我们需要从汇编角度来展开研究。...x64(x86-64),x86是CPU架构。如果你是x64的CPU装了32位系统,那么也不会使用到x64的寄存器(比如r8d),或者不能完整使用x64CPU的寄存器,比如rax。...栈地址 存储的内容 含义 bp bp - 4 2 c bp - 8 1 b 请注意下面(低地址)是栈顶。可以看到c在高地址,b在低地址。这与变量的初始化顺序相反。...(如果你开优化选项-O来观察的话,你会发现汇编代码里什么都没有做,这是因为声明的变量b,c虽然被初始化了,但是后续并没有被调用,所以编译器在优化的时候,就什么都不做了。...but,这个程序确确实实可以成功打印出0到99这100个数。why? 汇编代码我们就不看了。
# 堆栈相关的操作码 到目前为止,您已经了解了调用约定以及内存的布局方式,但是还没有真正探究许多操作码在 x64 汇编中的实际作用。 现在是时候更详细地介绍几种与堆栈相关的操作码了。...但是请注意,没有使用 push 指令显式推送这些值,这会减少 RSP 寄存器。这是为什么? 嗯,如您所知,在调用指令期间,返回地址被压入堆栈。...观察已创建多少暂存空间: 看看一个变量指向的值…… 它现在肯定不能保持 0x1 的值。为什么一个引用一个看似随机的值? 答案是由嵌入到寄存器应用程序的调试构建中的 DWARF 调试信息存储。...它告诉调试器,始终可以在此内存地址中找到名为 one 的变量。 嗯,并非总是如此,但总是在该变量有效时(即它在范围内)。...如果您已经在使用函数,并且该函数已经完成了函数序言,则以下各项将适用于 x64 程序集: RBP 将指向此功能的堆栈帧的开始地方。 RBP 将包含前一个堆栈帧的起始地址。
总要有人来完成剩下的工作吧,这里我就把研究一天的x64汇编指令格式共享给大家。 一.首先打开Inter手册,看到x64汇编指令格式有多大改动,不多说,看图。 ?...REX prefix 仅存在于 x64 的 64-bit 模式中,在 legacy x86 模式下,REX prefix 是无效的,但是在 x64 的 64-bit 模式下 Legacy prefix...W标识改变默认操作数大小,比如现在x64有个汇编代码mov r8,r10。一般很多指令都是默认32位操作数的,只有在CS.L==1&&CS.D==0的时候才会是64位操作数(我没见过)。...其实很多人不明白0xFF25 为什么后面要加4个0x00.现在工作机上没x64内联汇编环境验证不了,我理解这个其实是一个偏移指示这条指令之后多远的地方存放着一个64位地址,然后再jump到这个64位地址上去...而且是0x48(为什么去看上面),这里注意了,由于开启了寄存器扩展位,所以这得从原来的3位变成4位(从汇编往机器码上推的时候就没多大必要了) r8,r12分别为1000和1100把第四位去掉,再从x86
但是,由于我已经在x64(Windows)博客文章的基于堆栈的缓冲区溢出上写了一些关于Windows 64位的详细信息,我将在这里复制并粘贴它们。...此外,由于它不调用其他函数,因此不需要将堆栈对齐到16个字节。我不确定为什么它分配24个字节,看起来堆栈上的“局部变量区域”必须对齐到16个字节,其他8个字节可能用于堆栈对齐(如前所述)。...ret - 从函数返回 在Windows x64上编写ASM 在Windows x64上有多种方法可以编写汇编程序。我将使用NASM和Microsoft Visual Studio社区提供的链接器。...这就是为什么在上面的代码中使用了诸如ESI或CX的寄存器。...因此,我们将LoadLibraryA地址存储在RSI寄存器中。
本文转载地址:http://www.cnblogs.com/zuoxiaolong/p/computer15.html 引言 上一章我们已经介绍了汇编语言的基础部分,包括数据格式、寄存器以及操作数的标识方式...可以看到,在指令执行之后,%edx寄存器当中的内容会被复制到%eax寄存器。需要一提的是,mov指令可以在后面加上任何数据格式,比如上面这一过程中,数据格式则为四个字节,也就是双字。...接下来执行pop指令时,会先将栈顶的值复制到%edx,然后再将栈指针移动(+4)。我们来看一下它执行后的状态。 ? ...movl 8(%ebp), %edx 这一句将内存地址为%ebp+8的值复制到%edx,很明显,从上面的图中可以看出,%ebp+8这个位置存储着xp变量。...movl 12(%ebp), %ecx 它的作用是将地址为%ebp+12的值复制到寄存器%ecx,从图中可以看出,%ebp+12就是存储的变量y。
可以看到,在指令执行之后,%edx寄存器当中的内容会被复制到%eax寄存器。需要一提的是,mov指令可以在后面加上任何数据格式,比如上面这一过程中,数据格式则为四个字节,也就是双字。...接下来执行pop指令时,会先将栈顶的值复制到%edx,然后再将栈指针移动(+4)。我们来看一下它执行后的状态。 ? ...movl 8(%ebp), %edx 这一句将内存地址为%ebp+8的值复制到%edx,很明显,从上面的图中可以看出,%ebp+8这个位置存储着xp变量。...movl 12(%ebp), %ecx 它的作用是将地址为%ebp+12的值复制到寄存器%ecx,从图中可以看出,%ebp+12就是存储的变量y。...在栈完成之后,也就是pop指令执行之后,当前帧会恢复到调用者的帧上面去,如下所示。 ?
如下图所示: push 操作先将 栈顶(sp指针) 向下移动一个位置,然后将数据写入到新的栈顶;而 pop 操作会从 栈顶 读取数据,并且将 栈顶(sp指针) 向上移动一个位置。...接着 add_func() 函数会为局部变量申请空间,也就是将 esp寄存器 向下移动。 然后把局部变量 c 设置为参数 a 的值,局部变量 d 设置为 参数 b 的值。...最后将局部变量 c 和 d 的值相加,放置到 eax寄存器 中(C语言规定以 eax寄存器 传递返回值),然后调用 ret 指令返回到 main() 函数。...在上面的代码中,我们并没有直接调用 inject_callback() 函数,而是通过把 inject_callback() 函数的地址复制到 func_call() 函数的局部变量 tmpBuf 中。...但我们复制数据是从 24(16 + 8)处开始复制,已经超出了局部变量 tmpBuf 的大小,如下图所示: 从上图可以看出,func_call() 函数在调用 memcpy() 函数复制数据时,由于不小心用
快捷键y 5.变量重命名 点住这个变量 快捷键n 显示设置 可在“Options”-“Gemeral”-"Disassembly"窗口中设置反汇编的显示内容模式 代码定位: 1、交叉引用...IDA安装插件 插件安装:复制相应文件到ida安装目录plugin文件夹 Hex-Rays Decompiler:F5插件 支持x86与x64 6.8支持arm 6.9支持arm64 6.95...OD的窗口 反汇编窗口:显示被调试程序的反汇编代码,标题栏上的地址、HEX 数据、反汇编、注释可以通过在窗口中右击出现的菜单 界面选项->隐藏标题 或 显示标题 来进行切换是否显示。...寄存器窗口:显示当前所选线程的 CPU 寄存器内容。同样点击标签 寄存器 (FPU) 可以切换显示寄存器的方式。 信息窗口:显示反汇编窗口中选中的第一个命令的参数及一些跳转目标地址、字串等。...常用快捷键 断点功能 设置断点 Int3断点:可以有多个,设置在代码上 内存断点:通过设置内存页面属性异常来实现的断点功能,不去修改程序代码 硬件断点:使用调试寄存器设置断点,不会修改程序代码,最多设置
; movl %eax, c // 把 %eax 寄存器中的值复制到变量 c 中; ?...思考一个问题:为什么在汇编代码中,可以使用变量a, b, c?...复制到寄存器 %ebx,变量 data2 复制到寄存器 %ecx。...前面的修饰符等号意思是:会写入往 %eax 中写入数据,不会从中读取数据; 通过上面的这种格式,内联汇编代码中,就可以使用指定的寄存器来操作局部变量了,稍后将会看到局部变量是如何从经过栈空间,复制到寄存器中的...可以看到,在进入手写的内联汇编代码之前: 把数字 1 通过栈空间(-20(%ebp)),复制到寄存器 %eax,再复制到寄存器 %ebx; 把数字 2 通过栈空间(-16(%ebp)),复制到寄存器
上述例子说明了,在一定的条件下,编译器会对循环体中进行比较的变量进行特殊的优化,通过避免在地址中取值,以提升循环的效率。...在第二段中,我已经举例介绍了这种优化,这取决于JIT是否能跟踪到代码对变量i的更改,若JIT通过中间形式解析后能够跟踪到对循环变量的修改,则对循环变量将不会使用寄存器来进行优化。...下面上述例子在DEBUG下的汇编,可以看到,最终对i的比较和赋值的是同一个地址: L007e: cmp dword ptr [eax+4], 0 mov dword ptr [eax+4], 0x80000000...下面上述例子在Release下的汇编,可以看到,最终对i的比较和赋值不是同一个地址: L0037: mov eax, [esi+4] L003a: test eax, eax mov dword...ptr [ecx+4], 0x80000000 在本例中,因为JIT在没能跟踪到委托中的循环变量,最终取i的地址和在委托的闭包中设置的i的地址不是同一个位置,因此会产生无限轮训。
对于C/C++这种语言来说,我们可以尽情操作内存,但没法染指寄存器,所以在它看来,寄存器中的数就跟一个常数值一样,只能感知到它的值而已,不能去操控,不能去改变。...xvalue取址问题与C++引用对于prvalue来说,它是纯「值」或「寄存器值」,因此不能取地址,这件事无可厚非。但对于xvalue来说呢?xvalue有内存实体,但为什么也不能取地址呢?...(这里一种情况是通过寄存器复制过来的,但复制完它已经成为变量了,所以是lvalue;另一种是直接把变量地址传到函数中去接受返回值的,这样它本身也是lvalue)。...当完整使用函数返回值的时候(无论是用变量接收还是用常引用接收),都是相当于在调用方定义了一个局部变量,然后把这个变量的地址传入到被调用的函数中,用于处理返回值(也就是当做出参处理)。...在处理函数返回值的过程中,优先会选择通过寄存器的方式(先复制到寄存器,再复制给外部的变量),如果寄存器长度不够,那么就会选择直接在内存上处理的方式(利用外部传进来的,用于接收返回值的变量的地址来直接处理
# 汇编寄存器的规则 在本章中,您将了解到 CPU 使用的寄存器,并研究和修改传入函数的参数。您还将了解常见的苹果计算机架构,以及如何在函数中使用它们的寄存器。这就是所谓的架构调用约定。...汇编的知识会帮助你来观察这些函数中的参数。 # 汇编 101 等等,所以到底什么是汇编?来看一个场景:您是否曾经打了一个断点,但是中断到没有源代码的地方?然后看到看到大量内存地址和可怕的简短命令?...其中一些值按原样传递,而一个参数存储在局部变量中,然后在函数中作为参数引用。 但是,通过汇编查看代码时,计算机并不关心变量的名称 (name); 它只关心该变量在内存中的位置。...在 x64 汇编中调用函数时,以下寄存器用作参数。...在 LLDB 中,为寄存器加上 $ 字符很重要,因此 LLDB 知道您需要的是寄存器的值,而不是源代码中与范围相关的变量。 是的,这与您在刚刚反汇编视图中看到的汇编不同! 烦人吧?
具体就是将esp(stack pointer)寄存器减去压栈数据的大小,再将数据存储到esp寄存器所指向的地址。 1.2 pop popq 寄存器 popl 寄存器 pop指令将数据出栈并写入寄存器。...x64架构中增加了8个通用寄存器,C语言采用了寄存器来传递参数,如果参数超过。...在x64系统默认有System V AMD64和Microsoft x64两种C语言函数调用约定,System V AMD64实际是System V AMD64 ABI文档的一部分,类UNIX系统多采用...栈顶地址-16,栈扩容,这里没搞懂为什么要扩容,有懂的同学欢迎评论区指点下 这三条指令是用来分配栈帧的,执行完成后栈变成下方的样子: 继续往下看: movabsq $8589934597, %rsi...) movq -8(%rbp), %rax # 将返回值保存到rax寄存器 这里没搞懂为什么需要先挪到内存中再保存到rax寄存器上,可能是编译器实现起来比较方便吧,有懂的同学欢迎评论区指点下
Demo2 中没有刚才那样完整的结构体变量 t,但编译器还是会分配一片用于保存返回值的空间,把这个空间的地址写在 rdi 中,然后拿着这个空间到 Demo1 中来操作。...对于 C/C++ 这种语言来说,我们可以尽情操作内存,但没法染指寄存器,所以在它看来,寄存器中的数就跟一个常数值一样,只能感知到它的值而已,不能去操控,不能去改变。...xvalue 有内存实体,但为什么也不能取地址呢?...当它绑定函数返回值的时候,语义上解释为「一个值的替身(当然也是不可变的)」,实际上是代指一片内存空间,如果函数是通过寄存器返回的,那么就把寄存器的值复制到这片空间,而如果函数是通过内存方式返回的,那么就把这片内存空间传入函数中作为...(这里一种情况是通过寄存器复制过来的,但复制完它已经成为变量了,所以是 lvalue;另一种是直接把变量地址传到函数中去接受返回值的,这样它本身也是 lvalue)。
P相关的状态; 调用Q后,Q在此基础上继续扩展自己的栈帧; 很多过程调用不需要栈帧,只用寄存器足够; ret就是从栈中弹出之前的那返回地址,然后把pc设为那个返回地址; 局部变量放在内存中的情况:寄存器不足...;局部变量使用地址运算符&,必须为他产生一个地址;某些局部变量是数组或结构,必须能够通过数组或结构被引用访问到; 大多栈帧都是定长的,有时也要变长的fram; 通过寄存器过程P最多可传6个整数值(6个指针或者整数...); 如果需要更多参数,P可以在调用Q之前在自己的栈帧存储好这些参数; 在objdump中产生的反汇编callq 和 retq ,q是64位的意思; return返回值默认返回rax的值; 函数调用数据传送示例...解析 参数7位于栈顶; 通过栈传递参数时,所有数据大小都向8的倍数对齐; 参数到位后,就可以开call了; P调用Q时,P的代码首先把参数复制到合适寄存器; P的代码可访问Q返回在rax中的返回值...---**1018乘以16 内存的限制 现在64位机器只用47位地址-------也就是差不多256TB的地址 这就是为什么会出现这个地址: 0x 0000 7FFF FFFF FFFF----
一、为什么要学习和了解汇编 编译器基于编程语言的规则,目标机器的指令集和操作系统遵循的惯例,经过一系列的阶段生成机器代码。...但是如果是用汇编语言,程序员就必须制定程序用来执行计算的低级指令。 那么为什么我们还要学习和了解汇编呢?...源操作数指定的值是一个立即数,存储在寄存器中或者内存中,目的操作数指定一个位置,要么是一个内存地址。而在x86-64中增加一个限制,传送指令的两个操作数不能都指向内存位置。 ?...上图中记录的是两类数据移动指令,在将较小的源值赋值到较大的目的的时候使用,所有这些指令都把数据从源(在寄存器或内存中)复制到目的寄存器。...其次像x这样的局部变量通常是保存在寄存器中,而不是内存中,访问寄存器比访问内存要快的多 压入和弹出栈数据 最后两个数据传送操作可以将数据压入程序栈中,以及从程序栈中弹出数据。
领取专属 10元无门槛券
手把手带您无忧上云