热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何在AssemblyWorks中调用带有大量args的函数

如何解决《如何在AssemblyWorks中调用带有大量args的函数》经验,为你挑选了1个好方法。

这是一组示例函数,第一个是20个args,第二个是2:

int a(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11, int n12, int n13, int n14, int n15, int n16, int n17, int n18, int n19, int n20) {
    return n1 * n2 * n3 * n4 * n5 * n6 * n7 * n8 * n9 * n10 * n11 * n12 * n13 * n14 * n15 * n16 * n17 * n18 * n19 * n20;
}

int b(int n1, int n2) {
    return a(n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1)
      + a(n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1)
      + a(n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1, n1, n2, n1, n2, n1);
}

它被编译到这个程序集:

a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int):
  push    rbp
  mov     rbp, rsp
  mov     DWORD PTR [rbp-4], edi
  mov     DWORD PTR [rbp-8], esi
  mov     DWORD PTR [rbp-12], edx
  mov     DWORD PTR [rbp-16], ecx
  mov     DWORD PTR [rbp-20], r8d
  mov     DWORD PTR [rbp-24], r9d
  mov     eax, DWORD PTR [rbp-4]
  imul    eax, DWORD PTR [rbp-8]
  imul    eax, DWORD PTR [rbp-12]
  imul    eax, DWORD PTR [rbp-16]
  imul    eax, DWORD PTR [rbp-20]
  imul    eax, DWORD PTR [rbp-24]
  imul    eax, DWORD PTR [rbp+16]
  imul    eax, DWORD PTR [rbp+24]
  imul    eax, DWORD PTR [rbp+32]
  imul    eax, DWORD PTR [rbp+40]
  imul    eax, DWORD PTR [rbp+48]
  imul    eax, DWORD PTR [rbp+56]
  imul    eax, DWORD PTR [rbp+64]
  imul    eax, DWORD PTR [rbp+72]
  imul    eax, DWORD PTR [rbp+80]
  imul    eax, DWORD PTR [rbp+88]
  imul    eax, DWORD PTR [rbp+96]
  imul    eax, DWORD PTR [rbp+104]
  imul    eax, DWORD PTR [rbp+112]
  imul    eax, DWORD PTR [rbp+120]
  pop     rbp
  ret
b(int, int):
  push    rbp
  mov     rbp, rsp
  push    rbx
  sub     rsp, 8
  mov     DWORD PTR [rbp-12], edi
  mov     DWORD PTR [rbp-16], esi
  mov     r9d, DWORD PTR [rbp-12]
  mov     r8d, DWORD PTR [rbp-12]
  mov     ecx, DWORD PTR [rbp-16]
  mov     edx, DWORD PTR [rbp-12]
  mov     esi, DWORD PTR [rbp-16]
  mov     eax, DWORD PTR [rbp-12]
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, eax
  call    a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
  add     rsp, 112
  mov     ebx, eax
  mov     r9d, DWORD PTR [rbp-12]
  mov     r8d, DWORD PTR [rbp-12]
  mov     ecx, DWORD PTR [rbp-16]
  mov     edx, DWORD PTR [rbp-12]
  mov     esi, DWORD PTR [rbp-16]
  mov     eax, DWORD PTR [rbp-12]
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, eax
  call    a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
  add     rsp, 112
  add     ebx, eax
  mov     r9d, DWORD PTR [rbp-12]
  mov     r8d, DWORD PTR [rbp-12]
  mov     ecx, DWORD PTR [rbp-16]
  mov     edx, DWORD PTR [rbp-12]
  mov     esi, DWORD PTR [rbp-16]
  mov     eax, DWORD PTR [rbp-12]
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, DWORD PTR [rbp-12]
  push    rdi
  mov     edi, DWORD PTR [rbp-16]
  push    rdi
  mov     edi, eax
  call    a(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
  add     rsp, 112
  add     eax, ebx
  mov     rbx, QWORD PTR [rbp-8]
  leave
  ret

我有几个问题.首先,我注意到它似乎随着数量的增加而切换它如何处理args:

push    rbp
mov     rbp, rsp
mov     DWORD PTR [rbp-4], edi
...
imul    eax, DWORD PTR [rbp-8]
...

想知道那里发生了什么,为什么会这样做.它似乎对待第一个arg push,然后接下来的8个左右mov,然后剩下的imul仅与eax寄存器有关.想知道你可以拥有多少个args是否有限制.

我想知道的第二件事是以下内容.比如说a(),该函数b()调用了一些"系统函数"或其他一些外部库调用.想知道它是如何解开args的.嗯,我想,没关系,我假设C编译器将编译成汇编/机器代码所有链接的外部库.所以,我猜是的.

最后一件事是,如果系统调用都有一定数量的参数,类似于x86中最大操作数为3的方式.或者系统调用可能有任意数量的参数.看来他们想限制它的性能,所以他们只使用那些早期指示像pushmov的,而不是imul.

感谢您的帮助,只需查看在汇编时调用函数时如何处理参数的说明,尤其是在存在大量参数时.



1> 小智..:

您需要了解有关堆栈帧和应用程序二进制接口(ABI或调用约定)的更多信息.ABI定义调用者将参数传递给被调用者的方式,寄存器是易失性的,以及如何清理堆栈.

存在许多ABI,因为只要呼叫者和被呼叫者同意,任何人都可以设计他们自己的ABI.但是,只有少数ABI被广泛使用.在Windows上,大多数32位程序使用stdcall,cdecl,Microsoft的fastcall或Borland的fastcall,而64位程序大多使用Microsoft x64调用约定.在Unix 64位程序上总是使用System V AMD64 ABI,它也是编译器使用的.

让我们看一下您的代码,并附上评论:

push    rbp                     ; save the old stack frame
mov     rbp, rsp                ; establish new stack frame
mov     DWORD PTR [rbp-4], edi  ; save the first six arguments
mov     DWORD PTR [rbp-8], esi
mov     DWORD PTR [rbp-12], edx
mov     DWORD PTR [rbp-16], ecx
mov     DWORD PTR [rbp-20], r8d
mov     DWORD PTR [rbp-24], r9d
mov     eax, DWORD PTR [rbp-4]  ; load n1
imul    eax, DWORD PTR [rbp-8]  ; eax = eax * n2
imul    eax, DWORD PTR [rbp-12] ; eax = eax * n3
imul    eax, DWORD PTR [rbp-16]
imul    eax, DWORD PTR [rbp-20]
imul    eax, DWORD PTR [rbp-24]
imul    eax, DWORD PTR [rbp+16] ; eax = eax * n7
imul    eax, DWORD PTR [rbp+24] ; eax = eax * n8
imul    eax, DWORD PTR [rbp+32]
imul    eax, DWORD PTR [rbp+40]
imul    eax, DWORD PTR [rbp+48]
imul    eax, DWORD PTR [rbp+56]
imul    eax, DWORD PTR [rbp+64]
imul    eax, DWORD PTR [rbp+72]
imul    eax, DWORD PTR [rbp+80]
imul    eax, DWORD PTR [rbp+88]
imul    eax, DWORD PTR [rbp+96]
imul    eax, DWORD PTR [rbp+104]
imul    eax, DWORD PTR [rbp+112]
imul    eax, DWORD PTR [rbp+120]
pop     rbp                      ; restore old stack frame
ret                              ; exit

注意:前两行与参数无关; 它们是创建一个堆栈框架,以便您可以轻松访问局部变量和参数.如果没有堆栈帧你仍然可以用[RSP +*]访问它们,但偏移根据需要任意调整PUSHPOP您使用.

接下来是将参数存储到局部变量的指令.寄存器经常被更改,并且需要存储寄存器中传递的参数,以防您以后需要使用它们.但是,在这种情况下,没有必要.所以优化的代码可以

push    rbp                     ; save the old stack frame
mov     rbp, rsp                ; establish new stack frame
mov     eax, edi                ; eax = n1
imul    eax, esi                ; eax = eax * n2
imul    eax, edx                ; eax = eax * n3
imul    eax, ecx                ; eax = eax * n4
imul    eax, e8d                ; eax = eax * n5
imul    eax, e9d                ; eax = eax * n6
imul    eax, DWORD PTR [rbp+16] ; eax = eax * n7
imul    eax, DWORD PTR [rbp+24] ; eax = eax * n8
imul    eax, DWORD PTR [rbp+32] ; eax = eax * n9
imul    eax, DWORD PTR [rbp+40]
imul    eax, DWORD PTR [rbp+48]
imul    eax, DWORD PTR [rbp+56]
imul    eax, DWORD PTR [rbp+64]
imul    eax, DWORD PTR [rbp+72]
imul    eax, DWORD PTR [rbp+80]
imul    eax, DWORD PTR [rbp+88]
imul    eax, DWORD PTR [rbp+96]
imul    eax, DWORD PTR [rbp+104]
imul    eax, DWORD PTR [rbp+112]
imul    eax, DWORD PTR [rbp+120]
pop     rbp                      ; restore old stack frame
ret                              ; exit

从上面的例子,你应该能够猜出的第一个参数是在传递edi(或rdi,di,dil取决于大小),第二个是在esi,然后edx,ecx,r8dr9d(整数只,浮标在矢量寄存器中传递) .当你有超过6个参数时,另一个被推到堆栈并且可以使用[rbp + 16],[rbp + 24],...来访问([rbp + 8]是旧的rbp; [rbp]正在返回地址).

对于来电者

mov     r9d, DWORD PTR [rbp-12]  ; r9d = n6
mov     r8d, DWORD PTR [rbp-12]  ; r8d = n5
mov     ecx, DWORD PTR [rbp-16]  ; ecx = n4
mov     edx, DWORD PTR [rbp-12]  ; edx = n3
mov     esi, DWORD PTR [rbp-16]  ; esi = n2
mov     eax, DWORD PTR [rbp-12]  ; eax = n1  ; will assign to edi
mov     edi, DWORD PTR [rbp-12]  ; push n20
push    rdi
mov     edi, DWORD PTR [rbp-16]  ; push n19
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]
push    rdi
mov     edi, DWORD PTR [rbp-12]
push    rdi
mov     edi, DWORD PTR [rbp-16]  ; push n7
push    rdi
mov     edi, eax                 ; edi = n1
call    a()                      ; call the function
add     rsp, 112                 ; clean up the stack, 14 * 8 = 112 bytes
mov     ebx, eax                 ; result is in eax

一个更直接的版本是

mov     r9d, DWORD PTR [rbp-12]  ; r9d = n6
mov     r8d, DWORD PTR [rbp-12]  ; r8d = n5
mov     ecx, DWORD PTR [rbp-16]  ; ecx = n4
mov     edx, DWORD PTR [rbp-12]  ; edx = n3
mov     esi, DWORD PTR [rbp-16]  ; esi = n2
mov     edi, DWORD PTR [rbp-12]  ; edi = n1
push    [rbp-12]                 ; push n20
push    [rbp-16]                 ; push n19
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]
push    [rbp-16]
push    [rbp-12]                 ; push n8
push    [rbp-16]                 ; push n7
call    a()                      ; call the function
add     rsp, 112                 ; clean up the stack, 14 * 8 = 112 bytes
mov     ebx, eax                 ; result is in eax

请注意,参数按相反顺序推送.

由于您可以在调用函数之前将任何数字推送到堆栈(在它溢出之前),因此对参数的数量没有限制.


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文介绍了PHP常量的定义和使用方法,包括常量的命名规则、大小写敏感性、全局范围和标量数据的限制。同时还提到了应尽量避免定义resource常量,并给出了使用define()函数定义常量的示例。 ... [详细]
  • 本文介绍了在Linux中执行.sh脚本时出现/bin/sh^M: bad interpreter: No such file or directory异常的原因分析,并提供了两种解决方法:在Windows下进行编码格式转换,或在Linux中修改文件格式和执行权限。具体操作步骤也在摘要中给出。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了PE文件结构中的导出表的解析方法,包括获取区段头表、遍历查找所在的区段等步骤。通过该方法可以准确地解析PE文件中的导出表信息。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • Linux下安装免费杀毒软件ClamAV及使用方法
    本文介绍了在Linux系统下安装免费杀毒软件ClamAV的方法,并提供了使用该软件更新病毒库和进行病毒扫描的指令参数。同时还提供了官方安装文档和下载地址。 ... [详细]
author-avatar
翻版的袁城
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有