MSVC平台下X64处理器函数调用规则——底层机制

新手福利——x64逆向基础 此时的寻址方式并不是以rsp为基地址来传递局部变量和参数,那么如果我们想知道一个rbp+xxxx是局部变量还是参数,就需要到头部去进行一个相对复杂的运算,比如图中的rcx来源rbp-59,虽然我们明知他是一个局部变量(因为前面是lea),但是我们也要到头部去算一算,-59-5F= -B8,在头部的地址是rsp-B8,也就是说他是一个局部变量。虽然在x64程序中,我们默认的前4个参数是rcx-r9,但是也有例外,如果我们传递的参数是浮点型的话,那么传入浮点数的参数则会使用xmm0-xmm3来代替。 阅读详情

目录

1. 默认的调用规则

2. 内存边界对齐问题

3. 解开性(Unwindability)

4. 参数传递

5. 可变数量参数的传递

6. 非原型函数

7. 函数返回值

8. 调用函数或被调函数存储寄存器

9. 函数指针

10. 对较旧代码的浮点支持

11. 浮点状态和控制寄存器(Float Point Status and Control Register,简记为FPSCR)

12. 多媒体扩展控制和状态寄存器(Multimedia Extensions Control and Status Register,简记为MXCSR,注:因E在计算机单词缩写中已留作他用,因此E开始的单词,缩写时往往采用单词第2个字母大写)

13. 跳转指令(setjmp,longjmp)


这里介绍的是在X86代码中,一个函数(调用函数(caller))对另一个函数(被调函数(callee))调用的标准处理方式和有关约定。

1. 默认的调用规则

按照默认规则,X64应用二制接口(Application Binary Interface,简记为ABI)采用一个四寄存器快速调用(a four-register fast-call)的调用规则。系统在调用栈分配一块影子内存(shadow)来存储这四个寄存器的值。

在函数传递的参数与四个寄存器之间,规定了严格的一一对应关系。任何不适合按8字节,或者按1,2,4,或8字节传递的参数,必须通过引用方式进行传递(即,通过内存栈进行传递)。单个参数永远不会跨越多个寄存器进行传递。

X87浮点寄存器栈(指的是寄存器和调用中使用到的内存栈,是两个不同的东西,统称“寄存器栈”)没有使用。可以被被调函数使用,但应考虑到它在函数调用的过程中是易失性的(即,用完不用管恢复其值)。所有的浮点操作都是通过这16个XMM寄存器完成。整型参数通过将参数放在RCX,RDX,R8和R9这四个寄存器中进行传递。浮点参数通过将参数放在XMM0L, XMM1L, XMM2L,和XMM3L这四个寄存器中进行传递。参数传递的详细过程稍后介绍。这些用于参数传递的寄存器,和RAX, R10, R11, XMM4, 和XMM5寄存器一起,统称为易失性寄存器(volatile)(即,使用后不管恢复其值,也不会造成任何影响)。寄存器用法,在参见文档的寄存器用法页(register usage)和调用/被调用存储寄存器页(caller/calleesavedregister)。

对于原型函数(prototyped function,即,显式定义了参数),所有参数在被传递前,都会传化成符合被调用函数参数的类型。调用函数负责为被调函数的参数分配存储空间。调用函数必须为被调函数分配存储四个寄存器参数的足够容量的内存空间,即使被调函数用不了四个参数(即,不管被调函数有几个参数,那怕是0个参数,也需要分配这些内存空间,正所谓“影子内存”,这一点对编程很重要)。这个调用规则使得同时支持原型C语言函数和可变参数的C/C++函数得到简化。对于可变参数函数或非原型函数,任何浮点值必须在相应的通用寄存器中复制。超过四个参数的任何参数在调用之前必须将其存储在影子内存之后的内存栈中以便进行传递(即,从影子内存之后开始存储,不能硬破影响内存部分;编程时程序员需要处理这一点)。可变参数函数细节参见可变参数函数文档部分,非原型函数信息细节参见非原型函数文档部分。

2. 内存边界对齐问题

大部分结构在它们的自然边界对齐。主要的例外是栈指针和利用malloc和alloca动态分配的堆内存,为便于性能提升,它们都按16字节的内存地址边界对齐(即,内存地址的边界能被16整除)。16字节以上的边界对齐必须手动处理。因为,对于XMM寄存器的操作而言,16字节是一个通用的对齐大小,这个值对于大部分的代码都有效。更多关于结构布局和对齐的信息,参见类型和存储部分的文档。关于栈分布的信息,参见X64栈用法部分的文档。

3. 解开性(Unwindability)

叶函数(leaf functions)是一类不会改变任何非易失性寄存器值的函数。而一个非叶函数(anon-leaf function)可以改变非易失性寄存器的值,例如,通过调用函数,修改RSP寄存器的值。或者,通过为局部变量分配额外的栈空间,它也会改变RSP寄存器的值。为了在处理异常时恢复非易失性寄存器的值,非叶函数使用静态数据进行标注(annotated)。静态数据说明了如何使用任意指令恰当地解开函数。静态数据存储为“pdata”、或者存储为例程数据(procedure data),这反过来又指的是“xdata”,即异常处理数据。

序言(prologs)和结语(epilogs)受到高度限制,因此可以在xdata中正确地描述它们(指在xdata中描述序言和尾声)。除了叶函数以外,栈指针在不是序言和结语代码块的任何代码区域中必须保持16字节边界对齐。叶函数可以通过简单地模拟一个返回的方式解开函数,因此,不要求使用pdata和xdata,关于函数序言和结语的恰当结构,请参见X64序言和结语这部分文档。关于异常处理,以及异常处理和利用pdata和xdata解开函数问题,参见X64异常处理这部分文档。

4. 参数传递

按照默认约定,函数的前四个参数通过寄存器传递。用寄存器传递参数取决于参数的位置和类型。超过四个的函数参数按从右向左的顺序压入调用函数的内存栈中进行传递。

最左边的四个整型参数按从左到右的顺序分别存入RCX,RDX,R8和R9这四个寄存器中。第5个及以后的参数通过前述的内存栈进行传递。寄存器中的所有整数参数都是右对齐的,因此被调函数可以忽略寄存器的高位并仅访问寄存器的必要部分。任何前四个单精度和双精度类型参数都是通过从XMM0到XMM3这四个寄存器进行传递,具体取决于其位置。当它们是可变参数的时候,单精度浮点数只是放在RCX,RDX,R8和R9这四个寄存器中。具体细节参见可变参数的文档。同样地,当对应的参数是一个整型(整数或指针类型)时,系统忽略XMM0到XMM3这四个寄存器。

__m128类型(__m128 数据类型用于流式 SIMD 扩展和流式 SIMD 扩展-2指令内在函数,定义在<xmmintrin.h>中),数组和字符串从不使用立即值传递。代之的是,一个指针被传递给调用函数分配的内存(即,调用者分配栈内存存放的是数组或字符串的内存指针,这个指针存储的是数组或字符串在内存中的位置)。大小为 8、16、32 或 64 位以及 __m64 类型的结构和联合,当成相同大小的整数传递。其它大小的结构或联合通过调用函数分配的内存传递(即栈内存存放参数的指针)。对于这些作为指针传递的聚合类型(Aggregates types),包括__m128类型,调用者分配的临时性内存必须按16字节边界对齐。

内置函数不分配栈空间,并且不调用其它函数,有时候,它们会使用其它的易失性寄存器传递额外的寄存器参数。通过编译器与内置函数之间的紧绑定来实现这种优化。

如果有需要,被调函数负责将寄存器参数转储到它们的影子存储空间中。

下面的例表总结了参数传递过程,按类型和位置从左到右:

参数类型

第5个及以上参数

第4个

第3个

第2个

第1个

单精度浮点数

栈(指调用函数分配的栈,下同)

XMM3

XMM2

XMM1

XMM0

整型(整数和指针)

栈

R9

R8

RDX

RCX

聚合类型(8、16、32 或 64 位)和 __m64

栈

R9

R8

RDX

RCX

其它聚合类型,例如指针

栈

R9

R8

RDX

RCX

_m128,例如指针

栈

R9

R8

RDX

RCX

以下均以C++函数为例展示函数参数传递过程。

示例1:所有参数为有符号整数的参数传递:

func1(int a, int b, int c, int d, int e, int f);

// a 放入RCX, b 放入 RDX, c 放入 R8, d 放入 R9, f 和e 按先后顺序压入栈中传递

示例2:所有参数为浮点数的参数传递(单精度,又精度):

func2(float a, double b, float c, double d, float e, float f);

// a 放入XMM0, b 放入 XMM1, c 放入 XMM2, d 放入 XMM3, f 和e 按先后顺序压入栈中传递

示例3:混合有有符号整型和浮点型的参数传递(单精度,又精度):

func3(int a, double b, int c, float d, int e, float f);

// a 放入RCX, b 放入 XMM1, c 放入 R8, d 放入 XMM3, f 和e 按先后顺序压入栈中传递

(注意到,以上例子中,按从左向右的顺序,而不管是浮点型还是整型。)

示例4:__m64 、__m128和聚合类型参数传递:

func4(__m64 a, __m128 b, struct c, float d, __m128 e, __m128 f);

a 放入RCX, b 放入 RDX, 指向c的指针放入 R8, d 放入 XMM3, 指向f 和e 的指针按先后顺序压入栈中传递

5. 可变数量参数的传递

    假如参数通过可变参数数量方式传递(例如,省略号形式的函数参数),则按照规范参数传递约定执行。包括超过五个及以上参数采用栈空间传递的约定。被调函数负责转储已取得地址的参数。仅对于浮点参数,整型寄存器和浮点寄存器都必须包含该值,以防被调函数期望从整数寄存器取得该值(即,可变参数方式时,仅对于浮点参数,对应浮点寄存器和整型寄存器都必须存储这个浮点值,以便于某些计算)。

6. 非原型函数

    对于未完全原型化(Unprototyped functions,即,没有显式定义参数)的函数,调用函数以整型传递整型值,以双精度传递浮点值。仅对于浮点参数,整型寄存器和浮点寄存器都必须包含该值,以防被调函数期望从整数寄存器取得该值(即,可变参数方式时,仅对于浮点参数,对应浮点寄存器和整型寄存器都必须存储这个浮点值,以便于某些计算)。

例如,以下C++代码:

func1();

func2() {   // RCX = 2, RDX = XMM1 = 1.0, 以及R8 = 7

   func1(2, 1.0, 7);

}

7. 函数返回值

    标量(scalar)返回值适用于64位(包括__m64类型),通过放在RAX寄存器中返回。非标量(Non-scalar)返回值(包括单精度、双精度和向量类型,例如__m128、__m128i、__m128d)通过放在XMM0中返回。RAX或XMM0 返回的值中未使用位的状态未定义。

    用户定义类型可以通过全局函数和静态成员函数的值返回。要使用RAX中的值返回用户定义数据,要求数据必须具有1、2、4、8、16、32或64位的长度。同时,也必须满足没有用户定义的构造函数,析构函数,或者拷贝赋值运算符。它不能有私有或保护类非静态数据成员,也不能有非静态的引用类型数据成员。不能有基类或虚函数。以及,只能有满足这些要求的数据成员。(这种定义在本质上与 C++03 POD 类型一样。因为这种定义在C++11标准中已经改变,我们不推荐使用std::is_pod来测试这个定义。) 不然,调用函数必须为返回类型分配内存,并将指向它的指针作为第一个参数传递。然后将剩余的参数向右移动一个参数,同一个指针必须由被调函数放在RAX中返回。

    下面这些示例展示了如何为具有指定声明的函数传递参数和返回值(C++函数):

返回值示例1——64位结果:

__int64 func1(int a, float b, int c, int d, int e);

// 调用函数将a放入RCX, b 放入 XMM1, c 放入 R8, d 放入 R9, e压入函数栈,

// 被调函数用RAX返回__int64的结果。

返回值示例2——128位结果:

__m128 func2(float a, double b, int c, __m64 d);

// 调用函数将a放入XMM0, b 放入 XMM1, c 放入 R8, d 放入 R9,

// 被调函数用XMM0返回__m128的结果。

返回值示例3——通过指针返回用户定义类型的结果:

struct Struct1 {

   int j, k, l;    // Struct1 exceeds 64 bits.

};

Struct1 func3(int a, double b, int c, float d);

//调用函数为用户定义的返回类型Struct1分配内存,并通过RCX传递分配内存的指针,

// a放入RDX,b放入XMM2,c放入R9,d压入函数栈,

// 被调函数用RAX返回指向结构Struct1的指针结果。

返回值示例4——通过值返回用户定义类型的结果:

struct Struct2 {

   int j, k;    // Struct2 适合64位, 并 满足按值返回的要求。

};

Struct2 func4(int a, double b, int c, float d);

// 调用函数将 a 放入RCX, b 放入 XMM1, c 放入 R8, 以及将d 放入XMM3;

// 被调函数按值以RAX返回Struct2类型的结果。

8. 调用函数或被调函数存储寄存器

X64 ABI认为寄存器 RAX、RCX、RDX、R8、R9、R10、R11 和 XMM0-XMM5易失性的。当存在时(指后面这些寄存器存在时),YMM0-YMM15 和 ZMM0-ZMM15 的上部分也是易失性的。 在 AVX512VL 上,ZMM、YMM 和 XMM 寄存器 16-31 也是易失性的。 应考虑易失性寄存器的值在函数调用时被销毁,除非通过分析(例如整个程序优化)可以证明安全(指利用易失性寄存器的值安全)。

X64 ABI认为寄存器RBX、RBP、RDI、RSI、RSP、R12、R13、R14、R15 和 XMM6-XMM15是非易失性寄存器。它们必须由使用它们的函数负责存储和恢复(即,使用前要将其值保存起来,用完后使用保存的值去恢复其原来的值,否则可以导致灾难后果)。

9. 函数指针

函数指针只是指向相应函数标签的指针。函数指针没有目录 (Table Of Contents,简记为TOC)要求。

10. 对较旧代码的浮点支持

MMX和浮点栈寄存器(MM0-MM7/ST0-ST7)预留作上下文切换。对这些寄存器的访问没有显式的调用规则。在内核模式的代码中,严格禁用这些寄存器。

11. 浮点状态和控制寄存器(Float Point Status and Control Register,简记为FPSCR)

寄存器状态也包括X87浮点处理单元控制字(Float Process Unit,简记为FPU;控制字(Control Word))。调用约定表明这个寄存器是非易失性的。X87浮点处理单元控制字在程序执行的开始通过下面的标准值获得设置:

寄存(位)

设置

FPCSR[0:6]

异常掩码7个位全部置1(屏蔽所有异常)

FPCSR[7]

保留,置0

FPCSR[8:9]

精度控制-二进制10(双精度)

FPCSR[10:11]

舍入控制,0——舍入到最接近的值

FPCSR[12]

无限性控制(Infinity control),未使用,置0

被调函数如果修改了FPSCR寄存器内的任何域,在它返回调用函数之前,都必须恢复它们以前的值(非易失性寄存器)。另外,如果调用函数修改了这些域中的任何域的值,在它调用别的函数之前,都必须将其修改的域恢复成标准值,除非经过协商,修改值是新的被调函数所期望的值。

关于控制标识的非易失性规则,存在两种例外:

(1) 在这类函数中——给定函数的记录目的是为了修改非易失性寄存器FPSCR的标志(即,这类函数的存在目的就是为了修改FPSCR的标志)。

(2) 当它可以正确地证明,一个程序违背这些规则导致的运行结果与一个没有违背这些规则的程序运行结果一样,例如,通过整个程序的分析。

12. 多媒体扩展控制和状态寄存器(Multimedia Extensions Control and Status Register,简记为MXCSR,注:因E在计算机单词缩写中已留作他用,因此E开始的单词,缩写时往往采用单词第2个字母大写)

寄存器的状态也包括MXCSR。调用规则将寄存器分为易失性和非易失性两部分。易失性部分包括6个状态标识位,即MXCSR[0:5],而寄存器的其它部分,即MXCSR[6:15],被认为是非易失性的。

非易失性部分在程序开始执行时设置成下面的标准值:

寄存(位)

设置

MXCSR[6]

非正式是0,默认置0

MXCSR [7:12]

扩展掩码全部置1(屏蔽所有异常)

MXCSR [13:14]

舍入控制,0——舍得到最近的值

MXCSR [15]

刷新为0屏蔽下溢,0——关闭

被调函数如果修改了MXCSR寄存器内的任何易失性域,在它返回调用函数之前,都必须恢复它们以前的值(非易失性寄存器域)。另外,如果调用函数修改了这些域中的任何域的值,在它调用别的函数之前,都必须将其修改的域恢复成标准值,除非经过协商,修改值是新的被调函数所期望的值。

关于控制标识的非易失性规则,存在两种例外:

(1) 在这类函数中——给定函数的记录目的是为了修改寄存大MXCSR的非易失性域的标志(即,这类函数的存在目的就是为了修改MXCSR的非易失性域的标志)。

(2) 当它可以正确地证明,一个程序违背这些规则导致的运行结果与一个没有违背这些规则的程序运行结果一样,例如,通过整个程序的分析。

若非函数的文档明确说明,不要假设MXCSR寄存器在函数边界上的易失性部分状态。

13. 跳转指令(setjmp,longjmp)

当你在代码中包含了setjmpex.h或setjmp.h头文件,所有对setjmp或longjmp的调用都会导致一个解开函数的动作(unwind),它会触发系统调用析构函数和__finally标识的代码块。这个功能有别于X86调用规则,在X86环境下,包含了setjmp.h头文件会导致系统不调用__finally子句和析构函数。

对 setjmp 的调用会保留当前栈指针、非易失性寄存器和 MXCSR寄存器。对 longjmp 的调用返回到最近的 setjmp 调用站点,并将栈指针、非易失性寄存器和 MXCSR 寄存器重置为最近的 setjmp 调用所保留的状态。

内容来源:

x64 calling convention | Microsoft DocsDetails of the default x64 calling convention.https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170

函数栈结构与内存对齐 函数栈 下图是x86-64的函数栈的结构, 函数P调用函数Q的过程,Q正在执行。 这里仅对两个地方解释,其他的很容易理解,就不细说了 其中返回地址用来实现函数的返回。当Q要返回到P时,就要调用此地址获取返回位置。 参数构造区是对调用函数P传递的参数的拷贝,因为当P传递参数过多时,无法通过寄存器传递,只好在P的栈帧中保存;调用Q时,将其拷贝到参数构造区 x86-64过程只分配自己所需... 阅读详情

相关推荐

解决Qt/MSVC项目LNK2019链接错误:从编译原理到第三方库配置实战

在C++项目开发中,编译与链接是构建可执行程序的核心环节。编译器将源代码转换为目标文件,而链接器则负责将这些目标文件与库文件合并,解析所有外部符号引用。当链接器在指定路径中找不到某个函数或变量的具体实现时,就会抛出LNK2019“无法解析的外部符号”错误,这直接影响程序的最终生成。理解这一原理对于解决依赖管理、第三方库集成等工程问题至关重要,尤其在混合开发环境(如Qt Creator配合MSVC编译器)中。本文以典型的硬件SDK函数(如`bSpcMInitCardByIdx`)链接失败为例,深入剖析从工程配

289

x86-64 下函数调用及栈帧原理

缘起 在 C/C++ 程序中,函数调用是十分常见的操作。那么,这一操作的底层原理是怎样的?编译器帮我们做了哪些操作?CPU 中各寄存器及内存堆栈在函数调用时是如何被使用的?栈帧的创建和恢复是如何完成的?针对上述问题,本本文进行了探索和研究。 通用寄存器使用惯例 函数调用时,在硬件层面我们需要关注的通常是cpu 的通用寄存器。在所有 cpu 体系架构中,每个寄存器通常都是有建议的使用方法的,而编译器也通常依照CPU架构的建议来使用这些寄存器,因而我们可以认为这些建议是强制性的。 对于 x86-64 架

~~ LINUX ~~ 2034

GCC/Clang/MSVC 3 大编译器结构体对齐实测:pragma pack 与默认值差异分析

本文深入分析了GCC、Clang和MSVC三大编译器在结构体对齐方面的差异,通过实测数据揭示了默认对齐值和#pragma pack指令的跨平台陷阱。重点关注字节对齐对性能和兼容性的影响,为跨平台开发提供实用建议和最佳实践,帮助开发者避免常见的内存对齐问题。

weixin_30776545的博客 393

x64位游戏call代码测试注入器

这个神器是专门用来开发分析64位游戏call代码是否可以正常调用的工具 由于有些call的参数是需要自己申请内存地址,所以增加了 内存 这个选项功能 可以在测试x64程序 或有些64位游戏call时候,参数需要分配内存地址,然后写入需要的值,用于x64汇编代码中 其他基本操作说明: 本工具由独立团论坛官方开发,用于测试x64程序执行汇编call功能测试,写x64内存辅助必备工具 可以调用64位网络端游,手游模拟器 工具作用类似于之前的32位的代码注入器,用于写call之前的分析call测试call参数

hzw320的博客 5575

Windows平台下使用MSVC手动编译OpenSSL 1.1.1w的完整指南

在C/C++开发中,加密解密和网络通信是常见需求,OpenSSL作为广泛使用的开源加密库,提供了SSL/TLS协议实现和多种加密算法。其核心原理在于通过模块化设计,为应用程序提供安全的通信和数据保护能力。在Windows平台上,直接使用预编译的二进制包常因编译器不匹配导致链接错误,例如MSVC与MinGW的ABI不兼容问题。因此,掌握手动编译技能对于确保项目依赖可控、环境纯净具有重要技术价值。本文以Visual Studio 2019和OpenSSL 1.1.1w为例,详细讲解从环境配置、编译参数解析到项目

weixin_34414650的博客 319

ZLIB1.3静态库Windows X64构建与LNK2019/LNK2001链接修复指南

zlib 是广泛使用的 C 语言压缩算法库,其静态链接在 Windows 平台常因运行时(/MT vs /MD)、符号导出规则(如 inflateInit2_)、调用约定(__stdcall)及头文件版本不一致等问题引发 LNK2019 和 LNK2001 链接错误,甚至运行时崩溃。根本原因在于 zlib 官方源码不直接提供开箱即用的静态库工程,需手动控制编译定义(ZLIB_STATIC、ZLIB_WINAPI)、CRT 链接模式与 legacy 符号兼容性。本文聚焦 Windows X64 环境下基于 M

weixin_29169899的博客 85

MSVC平台下X64 ABI规则

本节描述X64架构(X86架构的64位扩展)的基本应用程序二进制接口(Application Binary Interface,简记为ABI)。这个主题的内容包括函数调用规则、类型分布、栈和寄存器用法、以及其它内容。 1. X64调用规则概述 X64与X86之间重要的两个区别是64位的寻址能力和一套平坦的16个16位通用寄存器。给定扩展的寄存器集,X64使用 __fastcall调用规则和基于RISC(Reduced Instruction Set Computer)的异常处理模型。__fast...

ComputerInBook的专栏 1368

Windows x64下开箱即用的zlib 1.3静态库(Debug+Release双版本,含头文件)

直接可用的zlib 1.3静态链接库,专为Windows 64位系统编译,包含zlibstatic_debug.lib和zlibstatic_release.lib两个版本,分别对应开发调试和最终发布场景。包内已集成zlib.h和zconf.h头文件,无需额外下载或配置,Visual Studio项目中只需将对应.lib加入链接器输入,并在包含目录中添加头文件路径,即可调用标准zlib接口,支持deflate/inflate压缩解压、gzip格式处理等核心功能。所有文件均为x64原生架构,不兼容x86或AR

tea88的博客 204

Linux/Unix平台X64函数调用约定

X64调用约定利用寄存器来传递部分函数参数,不规范的函数调用规则逐渐减少。现主要有两种比较通用的函数调用规则。一种是Microsoft X64函数调用约定,参见以下链接:MSVC平台下X64处理器函数调用规则——底层机制_ComputerInBook的博客-优快云博客目录1. 默认的调用规则2. 内存边界对齐问题3. 解开性(Unwindability)4. 参数传递5. 可变数量参数的传递6. 非原型函数7. 函数返回值8. 调用函数或被调函数存储寄存器9. 函数指针10. 对较旧代码的浮点支持11.

ComputerInBook的专栏 1712

eWOW64Ext v1.2 - 加载任意 32/64 模块|动态调用|64 位汇编|64 位进程读写-易语言

模块原理: wow64 是在 64 位操作系统上允许 32 位程序(比如易编译的程序)执行的模拟器子系统;在 64 位操作系统中,不管你的程序是 32 还是 64 位的,其实都存在两个地址空间,正常情况下 32 位程序访问的自然是 32 位的地址空间,而 64 位程序访问其 64 位地址空间。 但是这两个空间是同时存在且可以切换的,本模块就是通过该原理切换到 64 位地址空间获取 ntdll.dll 相关函数进行调用(注:此基址是 64 位的,与平常获取的 32 位模块基址截然不同); 也就是:wow 环境 -> 进入 x64 环境 -> x64 函数调用 或 x64 汇编代码 -> 退出 x64 环境 -> wow 环境,以上必须在一个子程序内完成; 部分实现代码借鉴 c++ 开源代码:wow64ext,在此感谢作者 rewolf。 模块功能: 实现易语言 纯 64 位汇编置入代码; 允许调用易程序 64 位 ntdll.dll 的所有函数,也就是你虽然开发的是 32 位程序,但可以实现很多 64 位函数所能实现的功能; 直接使用 64 位函数自由读写(注入) 64 位进程,与很多模块调用 NtWow64xxx 系列函数实现的方式有本质不同; 部分常用 ntdll.dll 函数已在模块直接提供,或以模拟 kernel32 函数的调用形式提供,v1.1 新增多个函数; 未提供函数获取地址后,可使用 X64Call 这个通用函数调用即可; 大部分提供的 64 位功能也同时提供了 32 位版本,以便兼容不同需求(模块在 32 位系统中不会开启 64 位功能引起异常,但 32 位功能依然可用); 支持加载任意 32/64 位 DLL,从此易语言可以调用外部 64 位 DLL 了(包括加载 kernel32.dll),v1.1 新增功能; 除了动态加载外,还支持 32 位 DLL 的内存加载,但 64 位只能加载本地 DLL 文件,v1.1 新增功能; 如有 BUG,请提供错误重现代码及执行环境,如非不可抗因素我都会及时更新的; 模块部分命令简述: 以下只是适用于 64 位的部分函数,模块中以相同命令形式实现的 32 位命令,这里就不列举了; 辅Zhu函数 fn_WOW64Enabled 如果你在代码中需要使用 64 位汇编或者操作 64 位进程,则初始化时应确保本函数返回真。实际只要是 64 位操作系统,均应返回 真 fn_ProcessIsX64 检测指定进程是否为 64 位进程 fn_CalcModOrFuncHash 使用过动态调用DLL的都清楚取模块基址和函数指针,微软默认使用字符串对比,本模块可使用哈希对比效率和易用上相对提升,本函数用于计算模块或函数哈希 易内部命令 X64Call 调用 64 位函数通用版本 X64CallArr 调用 64 位函数通用版本,数组方式传参,支持无限个数参数;【v1.2新增】 X64MemCopy 同类还有 X64MemCmp 函数;从 64 地址复制数据或 64 位地址与 32 位地址数据对比,但仅限进程内部 X64GetLong64 获取 64 位地址数值,同类函数还有:X64GetLong32、X64GetWord、X64GetByte X64GetTEB 取当前易程序 64 位 TEB,通过 TEB 再取 PEB,则进程和线程信息以及模块等一览无余了 GetNtdll64 ntdll.dll 在 64 位环境下的内存基址 GetModuleHandleEx64 通过模块哈希值获取其 64 位地址空间的内存基址(易进程而不是外部进程哦);同类还有 GetModuleHandle64 GetProcAddressEx64 通过函数哈希值或函数索引序号获取其 64 位调用地址;同类还有 GetProcAddress64 NtQuerySystemInformation64 cha询系统信息,可获取很多类别信息。这个 API 微软已不推荐使用并给出部分替代 API,但其个别功能十分好用且没用可替代品。cha询系统进程也是最全面的 OpenProcess64 打开进程句柄,关闭进程句柄时使用 CloseHandle64;【v1.1新增】 HeapAlloc64 堆管理函数,同类还有 GetDefaultHeap64/HeapReAlloc64/HeapFree64/HeapSize64;【v1.1新增】 malloc64 简化版默认堆管理函数,同类还有 realloc64/free64 RtlUnicodeToAnsi64 内核实现的 Unicode、Ansi 结构(不是数据指针)管理函数,同类还有:RtlInitAnsiString64/RtlFreeAnsiString64、RtlIni

告别底噪与失真:手把手教你调校WM8988音频Codec的输入输出增益链

本文详细介绍了如何调校WM8988音频Codec的输入输出增益链,解决底噪与失真问题。从模拟输入增益、数字转换增益到输出放大增益,提供了具体的寄存器配置和调试策略,帮助开发者优化音频信号处理链,提升音质表现。

weixin_30485379的博客 363

MSVC编译静态库

本文介绍了使用MSVC工具链(cl.exe和link.exe)在Windows系统下编译程序的详细步骤。主要内容包括:1.单文件编译链接过程;2.多文件编译方法;3.静态库的创建与使用(包括lib.exe命令操作和VS2019图形界面操作);4.项目配置文件(.vcxproj和.vcxproj.filters)的作用;5.头文件在静态库制作中的必要性;6.标准库文件的位置说明。

weixin_54946088的博客 1260

Spring Boot 实现多图片上传并回显,涨姿势了~

上传 Controller的代码非常简单,由于用了SpringMVC框架,所以直接用MultipartFile来接即可。由于是多图片上传所以用数组来接。此处应该注意参数名应该和<input>中的name值相对应: @RequestMapping("/pic") @ResponseBody publicResponseEntity<String>pic(MultipartFile[]pictures)throwsException{ ResponseEnti...

weixin_54556126的博客 894

x64(一)

在 VS2019 的 x64 项目中,写汇编只能使用“独立汇编文件 + MASM”从 Intel SDM 的角度来看,x64 虚拟地址并不是完整使用 64 位。在反汇编工具中,x64 下对全局变量的访问表面上看起来和 x86 几乎一致,默认情况下,VS2019 的 C/C++ 项目并不会编译 .asm 文件,x64 处理器真的使用了完整的 64 位虚拟地址空间。x64 不只是“加寄存器”,还补全了子寄存器体系。x64 的通用寄存器已经全部扩展到了 64 位。事实上,x64 的地址是“受限制的 64 位”。

取个名字太难了a的博客 690

C#串口通信从入门到精通(27)——高速通信下解决数据处理慢的问题(20ms以内)

本文介绍如何在高速通信情况下解决串口接收的数据来不及处理的问题,由高级工程师多年经验总结而成,绝对是串口领域的实战经验!

qq_34059233的博客 6809

MyBatis-Plus 实战:敏感字段自动加解密支持模糊查询

本文基于MyBatis-Plus框架提出了一种全链路敏感字段加密方案。通过自定义TypeHandler实现数据入库自动加密和查询自动解密,结合重写LambdaQueryWrapper处理查询条件加密,并适配多种查询方式。方案核心包括:1)AESUtil工具类提供加解密基础能力;2)EncryptTypeHandler实现字段类型转换;3)实体类通过注解配置加密字段。该方案在保证数据安全的同时,对业务代码侵入性小,支持姓名、手机号等敏感信息的全流程加密处理,满足系统开发的安全需求。

蔡定努 596

MSVC C4996 警告处理:strncpy 安全函数与工程化配置

C/C++ 字符串操作中的缓冲区安全是内存安全的基础问题。传统 strncpy 在源串长度不小于目标容量时不会自动补结束符,可能引发越界读取或崩溃,这类风险正是编译器弃用警告要提示的内容。MSVC 以 C4996 标记部分 CRT 函数,Visual Studio 又可通过 SDL 检查把警告提升为错误;应对时,_CRT_SECURE_NO_WARNINGS 宏、strncpy_s 安全函数、属性表与 CMake 编译定义各有取舍。技术价值在于既消除编译噪音,又不牺牲边界校验与跨平台兼容。工业上位机、遗留

weixin_34326429的博客 368

银行业数据科学家的 6 条经验教训

原本希望在计算机科学、统计学和机器学习领域工作。运用新方法获得独特见解,实现一切自动化。简而言之,最终成了这个职业炒作的牺牲品。我想和你们分享一下。希望我们能够摆脱炒作,提高你对数据科学家工作的理解。

茶桁专栏 246

【C++/Qt桌面开发实战01】Qt Creator找不到MSVC?Qt 6.11.1 + MSVC v143环境搭建避坑实录

振动信号文件导入;时域波形与频谱显示;MSSTFrFT 时频图显示;CNN-Mamba 五类故障分类;ONNX Runtime 模型推理;诊断结果保存与导出。Qt Creator+ CMake+ Ninja安装过程中主要遇到了 Qt Creator 无法自动识别 MSVC v143、Kit 出现黄色警告、CMake 编译器路径不匹配等问题。本文记录完整安装与排查过程,并通过 Qt Widgets 测试项目验证环境是否真正可用。CDB检查 cl.exe↓。

duhong2026的博客 577

1985-2021年30m全国逐年土地覆被数据(含分省数据)

基于CLCD,揭示了1985年至2019年期间中国土地覆被变化的趋势和模式,如不透水表面(+148.71%)和水(+18.39%)的扩大,耕地(-4.85%)和草地(-3.29%)的减少,以及森林(+4.34%)的增加。由于中国的一系列造林政策,如1980年代启动的三北防护林工程和2000年后启动的绿色增益工程,从1985年到 2019年,森林增加了4.34%,灌木减少了259万公顷,与 ESACCI_LC的减少趋势相似。尽管为了养活不断增长的人口,中国东北和西北地区进行了广泛的开垦,但耕地普遍减少。

J_Giser的博客 1471

【一看就会】Autoware.universe的“规划”部分源码梳理【0】——Planning综述(持续更新中)

单独开一篇planning综述,作为整个规划系列的开篇。本篇综述会持续更新,希望能帮助大家更好的整体理解规划部分。这个部分会持续更新。

weixin_48386130的博客 1966

【STL】矢量化 MSVC STL 算法

摘要: MSVC STL通过矢量化技术(SIMD指令)优化算法性能,包括自动和手动两种方式。自动矢量化由编译器完成,适用于连续内存容器(如vector、array)和基础数值运算;手动矢量化针对特定算法(如查找、统计类)预置SIMD实现,仅支持整数类型,通过宏_USE_STD_VECTOR_ALGORITHMS控制。浮点类型因精度问题仅有部分算法支持手动优化,由_USE_STD_VECTOR_FLOATING_ALGORITHMS控制。

呆瓜的博客 621

9、工业数据管理:Historian、ERP、MES与资产模型解析

本文深入解析了工业数据管理中的核心概念与技术,包括Historian数据库、ERP和MES系统以及资产模型。文章详细介绍了Historian数据库的特性、架构分类、数据采集方式及时间序列处理能力,同时探讨了ERP和MES系统的功能与优势,特别是MES在生产执行中的重要作用。资产模型部分结合ISA-95和ISA-88标准,阐述了工业资产的层次化建模方法。此外,文章还分析了各系统之间的协同关系、面临的挑战及应对策略,并展望了未来工业数据管理的发展趋势,如云原生技术、人工智能融合和工业物联网的应用。

pz89012345的博客 144

UE5项目在VS2022下的MSVC编译器兼容性问题全解析与解决方案

在C++项目开发中,编译器工具链的版本兼容性是影响项目构建成功与否的关键因素。MSVC(Microsoft Visual C++)作为Windows平台的主流编译器,其不同版本间的运行时库(MSVCRT)和C++标准库实现可能存在二进制接口(ABI)差异。这种差异在动态链接(/MD或/MDd)模式下尤为突出,当使用新版本编译器(如VS2022的v143工具集)链接针对旧版本编译器(如VS2019的v142工具集)预编译的库时,极易引发“LNK2038: 运行时库不匹配”或“LNK2001: 无法解析的外部符

weixin_34043301的博客 424

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

深入解析Boost自动链接机制:原理、配置与排错指南

在C++项目开发中,库的链接(Linking)是构建过程中的关键环节,它负责将编译后的目标文件与所需的库文件合并生成最终可执行程序。MSVC编译器通过`#pragma comment(lib, ...)`指令提供了源代码级别的库依赖声明机制,这一特性为库的自动化管理奠定了基础。Boost库作为C++社区广泛使用的工具集,在此基础上实现了复杂的自动链接(Auto-Linking)功能,它能根据编译环境动态生成并链接正确的预编译库文件,显著提升了开发效率并减少了手动配置错误。该机制特别适用于Windows平台下

weixin_31714129的博客 353

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Qt与Visual Studio环境配置:MSVC工具链匹配与调试集成指南

Qt 是一个跨平台 C++ 图形用户界面框架,其在 Windows 平台的稳定运行高度依赖 Visual Studio(VS)与 MSVC 工具链的精确匹配。理解 Qt 的预编译二进制包如何绑定特定版本的 cl.exe、链接器行为及运行时库(MD/MT)差异,是避免 LNK2038、LNK1104 等典型链接错误的关键。技术价值在于构建可复现、可交付、支持断点调试的生产级开发环境;典型应用场景包括嵌入式 HMI 开发、工业人机界面项目交接、CI/CD 构建一致性保障。本文聚焦 Qt 安装配置 与 VS St

weixin_29178069的博客 214

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: 汇编语言逻辑“或”指令与应用示例:OR (Logical Inclusive OR)和 XOR (Logical Exclusive OR)
下一篇: MSVC平台下X64 ABI规则
ComputerInBook
博客等级 码龄19年 1168粉丝 464原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值