展开

关键词

x64汇编第三讲,64位调用约定与函数传参.

目录 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.调用函数之前,会申请参数预留空间.

2K20

深入iOS系统底层之CPU寄存器介绍

解决的办法是CPU内部开辟一小块临时存储区域,并在进行运算时先将数据从内存复制这一小块临时存储区域中,运算时就在这一小快临时存储区域内进行。我们称这一小块临时存储区域为寄存器。 正是通过高速缓存的引入,当程序在运行时,就可以预先将部分在内存中要执行的指令代码以及数据复制高速缓存中去,而CPU则不再每次都从内存中读取指令而是直接从高速缓存依次读取指令来执行,从而加快了整体的速度 我们高级语言里面看到的只是变量,但是低级语言里面看到的就是内存地址寄存器,你可以将内存地址寄存器也理解为定义的变量,带着这样的思路去阅读汇编代码时你就会发现其实汇编语言也不是那么的困难。 XCODE中可以很方便的代码执行断点时查看当前线程中的所有寄存器中内容(请选择最左下角处的all表示显示所有变量)。我们可以通过下面两张图来查看所有的寄存的信息。 ? 模拟器下的寄存器信息 ? 但是如果是机器指令则不一样了,因为运算时总是要将内存数据移动寄存器中去,但是寄存器只有一份。

67930
  • 广告
    关闭

    【玩转 Cloud Studio】有奖调研征文,千元豪礼等你拿!

    想听听你玩转的独门秘籍,更有机械键盘、鹅厂公仔、CODING 定制公仔等你来拿!

  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    基于汇编的 CC++ 协程 - 切换上下文

    该函数协程初始化的时候,保存在了 func_ret_addr 成员变量中。 请注意这个变量结构体中的偏移值:64,下文的 asm_amc_coroutine_enter() 汇编函数就用上了。 参考资料用户态调度要保存些什么中就说明了 GCC 程序中,需要保存的寄存器内容(x86_64 / x64): rsp:栈指针,指向栈顶,也就是下一个可用的栈地址。 这两句的逻辑如下: 首先 asm_amc_coroutine_dump() 将主线程的上下文保存在一个全局变量中 第二句将堆栈指针移动了一个单位,效果上就是忽略了函数 asm_amc_coroutine_dump 而由于协程是单线程运行的,因此我们可以使用全局变量判断出刚刚结束的是哪一个协程。 强制跳转到协程的入口处开始执行。 前文不是说了一大堆需要保存的上下文吗,为什么这里赋值的寄存器那么少? 这个时候汇编中做了以下的事情: 从堆栈中取出函数的返回地址 调用 retq 返回(retq 同时会将返回地址出栈丢掉) 这就是我们前文中将协程返回地址重定向的原理基础。

    1.5K61

    CC++:堆栈面面观

    简单说来,我们函数中声明的任何局部变量(非静态)都是栈中分配的(编译期间完成)。并且函数的参数,以及返回值也是依赖于栈。 为了深入地探讨这些概念,我们需要从汇编角度来展开研究。 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,这个程序确确实实可以成功打印出099这100个数。why? 汇编代码我们就不看了。

    9220

    X64汇编之指令格式解析

    总要有人来完成剩下的工作吧,这里我就把研究一天的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

    2.5K30

    编写Windows x64的shellcode

    但是,由于我已经x64(Windows)博客文章的基于堆栈的缓冲区溢出上写了一些关于Windows 64位的详细信息,我将在这里复制并粘贴它们。 此外,由于它不调用其他函数,因此不需要将堆栈对齐16个字节。我不确定为什么它分配24个字节,看起来堆栈上的“局部变量区域”必须对齐16个字节,其他8个字节可能用于堆栈对齐(如前所述)。 ret - 从函数返回 Windows x64上编写ASM Windows x64上有多种方法可以编写汇编程序。我将使用NASM和Microsoft Visual Studio社区提供的链接器。 这就是为什么在上面的代码中使用了诸如ESI或CX的寄存器。 因此,我们将LoadLibraryA地址存储RSI寄存器中。

    13740

    深入理解计算机系统(3.3)---数据传送(或者说复制)指令详解

    可以看到,指令执行之后,%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指令执行之后,当前帧会恢复调用者的帧上面去,如下所示。 ?

    39630

    深入理解计算机系统(3.3)---数据传送(或者说复制)指令详解

    本文转载地址: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。

    27340

    深入理解计算机系统(3.3)---数据传送(或者说复制)指令详解

    可以看到,指令执行之后,%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指令执行之后,当前帧会恢复调用者的帧上面去,如下所示。 ?

    33350

    一文读懂|栈溢出攻击

    如下图所示: 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() 函数复制数据时,由于不小心用

    19320

    内联汇编很可怕吗?看完这篇文章,终结它!

    ; movl %eax, c // 把 %eax 寄存器中的值复制变量 c 中; ? 思考一个问题:为什么汇编代码中,可以使用变量a, b, c? 复制寄存器 %ebx,变量 data2 复制寄存器 %ecx。 前面的修饰符等号意思是:会写入往 %eax 中写入数据,不会从中读取数据; 通过上面的这种格式,内联汇编代码中,就可以使用指定的寄存器来操作局部变量了,稍后将会看到局部变量是如何从经过栈空间,复制寄存器中的 可以看到,进入手写的内联汇编代码之前: 把数字 1 通过栈空间(-20(%ebp)),复制寄存器 %eax,再复制寄存器 %ebx; 把数字 2 通过栈空间(-16(%ebp)),复制寄存器

    30820

    盘点.NET JITRelease下由循环体优化所产生的不确定性Bug

    上述例子说明了,一定的条件下,编译器会对循环体中进行比较的变量进行特殊的优化,通过避免地址中取值,以提升循环的效率。 第二段中,我已经举例介绍了这种优化,这取决于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的地址不是同一个位置,因此会产生无限轮训。

    21120

    《深入理解计算机系统》阅读笔记--程序的机器级表示(上)

    一、为什么要学习和了解汇编 编译器基于编程语言的规则,目标机器的指令集和操作系统遵循的惯例,经过一系列的阶段生成机器代码。 但是如果是用汇编语言,程序员就必须制定程序用来执行计算的低级指令。 那么为什么我们还要学习和了解汇编呢? 源操作数指定的值是一个立即数,存储寄存器中或者内存中,目的操作数指定一个位置,要么是一个内存地址。而在x86-64中增加一个限制,传送指令的两个操作数不能都指向内存位置。 ? 上图中记录的是两类数据移动指令,将较小的源值赋值较大的目的的时候使用,所有这些指令都把数据从源(寄存器或内存中)复制目的寄存器。 其次像x这样的局部变量通常是保存在寄存器中,而不是内存中,访问寄存器比访问内存要快的多 压入和弹出栈数据 最后两个数据传送操作可以将数据压入程序栈中,以及从程序栈中弹出数据。

    34200

    汇编语言知识总结

    ,硬盘同理 为什么要了解寄存器 因为程序员如果想要操控cpu或者修改内存, 不能直接操控, 需要借助寄存器, 更改寄存器当中的数据间接地操控cpu和内存 寄存器的数量 高级语言中如果要对两个变量进行数据交换 从x0x30 MIPS架构中,, 一共有32个通用寄存器 ,从031 x86架构中,不同精度cpu 通用寄存器名称有所区分: ;x86架构中,不同精度cpu 通用寄存器名称有所区分: 0x1122334455667788 ,这也是为什么函数和函数中的局部变量都存放在栈中的原因 总线 存在的意义, 内存中的数据不能直接运算,必须将其读取到寄存器中进行处理, cpu运算完毕后,将其保存至内存中, 那么这一系列过程中,涉及数据传输 具体到上面的示例代码中,根据上下文,涉及2个操作数变量a、b,这段汇编代码的作用是将a的值赋给b,可见,a是input operand,而b是output operand,那么根据操作数的引用规则,不难推出 d m 使用合法的内存代表参数 g 任意寄存器,内存,立即数 为什么有些汇编语法不一致 C语言外链汇编 新建一个汇编原文件, linux平台.s结尾 ,windows平台.asm结尾 ;

    36220

    JavaScript 是如何工作的:JavaScript 的共享传递和按值传递

    调用函数现在从 EAX 寄存器检索返回值 s 的内存位置。 mov eax, 0x000002 ; // s 变量在内存中的位置 我们已经看到了内存中发生了什么以及如何将参数传递汇编代码的函数。 就像我们汇编代码中看到的那样。最初,num1 引用与 n 相同的内存地址,因为n被推入堆栈。 然后创建对象之后,将 num1 重新分配到对象实例的内存地址 sum 函数实现中,没有新的对象创建,该参数受到直接影响。 ... 000270 sum: 000271 mov (ebp+4), eax ; // 将参数值复制 eax 寄存器。 eax 现在为 0x002233 000271 mov 30, [eax]; // 将 30 移动到 eax 指向的地址 num1 是(ebp+4),包含 n 的地址。 值被复制 eax 中,30 被复制 eax 指向的内存中。任何寄存器上的花括号 [] 都告诉 CPU 不要使用寄存器中找到的值,而是获取与其值对应的内存地址号的值。

    28341

    linux内核启动流程分析 - efi_stub_entry

    为什么是保存在rdx里,且又是怎么保存到rdx里的呢? 这就要说到汇编语言的calling convention了。 所谓calling convention,其实就是一种约定,是说当我调用你写的汇编函数时,我会把该函数所需的参数,放到约定好的寄存器或堆栈里,你获取这些参数时,直接到那里去拿就好了。 但如果我们要直接写汇编代码,这些就是要了解清楚的。 那对于x64的linux内核来说,calling convention具体是怎么约定的呢? 该调用传递了三个指针类型的参数,所以它们使用的寄存器分别是 rdi, rsi, rdx。 现在大家就应该明白了,为什么保存boot_paramsrbx时,要从rdx里取了吧。 为什么这样相加就是startup_64运行时的地址呢? 首先第五行代码中使用的startup_64是编译时(构建时)地址,而并不是运行时地址。 这个可以通过下述方法确认。

    63720

    iOS: ARM64不定函数传参问题调试剖析

    目前,主流移动设备CPU主要采用ARM处理器。在做移动客户端开发时,难免遇到需要分析汇编代码的情况,牵涉到过程调用的部分就必须要了解相应平台的ABI。 ARC复习: id类型的默认所有权修饰符是id strong,超出其变量作用域时会被调用release方法 使用void *或unsafe_unretained修饰符传递参数相当于直接传递对象指针 分析 通过汇编单步调试发现上述crash属于访存错误,objc_retain调用传入了一个堆栈上的地址。这很奇怪,按理说传入的应当是该方法的实参对象——一个堆中的地址,指向一个合法对象。 生成的汇编传递的是参数栈上的地址;调用两个参数时,依次传递了第二个参数和第一个参数的地址。 但是我们注意x86-64架构的模拟器一直是正确的,这是为什么呢? 回到关键的测试代码2部分,查看指令片段Assembly 4-2.2可以注意这三条指令 ?

    1.4K33

    用Rust实现Brainfuck的JIT编译器

    x64汇编简介 Linux x64 汇编/Hello World 我们每天产出大量的垃圾代码,我们每个人都可以像这样简单地编写最简单的代码: #include <stdio.h> int main() 例如,为什么只吃巧克力或简单的坚果,而不是将两者结合起来,成为一块可爱的坚果巧克力呢? 1960 年约翰·麦卡锡偶然发现了此方法。 根据 nasm 规范,函数的第一个参数被存在 rdi 寄存器中,第二个参数被存在 rsi 寄存器中。我们将它们复制 r12 和 r13 这两个寄存器内持久化存储。 同时 rcx 寄存器被用作为纸带的指针 SP,赋予其初始值为纸带起始地址。 let mut ops = dynasmrt::x64::Assembler::new()? 之后是 PUTCHAR 与 GETCHAR,们遵循汇编中函数调用的逻辑,的参数与地址按照规则写入指定寄存器,然后,用 call 指令调用该函数。

    6810

    内核调试技巧-逆向寻踪,揭开 LACP 协议流程的神秘面纱

    NGW 网关产品中,同样的,从物理端口接收的 LACP 报文则通过 kni 注入给内核;内核向外发送的 LACP 报文则通过 kni 处理并从物理口出。 已经被编译器优化,不能通过 systemtap 直接获取$port 入参或者 pointer_arg(1)提取入参,因此从汇编入手获取标识 port 的寄存器的值。 而汇编[890-929]区间为 ad_lacpdu_send 能否执行的判断逻辑,所以选择汇编 890 位置为 probe 位置调用 print_regs 函数来获取寄存器。 同时汇编 890 位置对应的源码位置为 bond_3ad.c:1261: 因此编写如下 systemtap 脚本: 运行结果如下: 至此找到 port 的地址了,为了验证 port 的地址的正确性 2、 当核心数据结构不能直接访问时,则反汇编,查看可通过哪些寄存器间接获取。

    32140

    扫码关注腾讯云开发者

    领取腾讯云代金券