为什么在 shellcode 中使用 Push/Pop 而不是 Mov 将数字放入寄存器中?

2024-04-02

我有一些来自 shell 代码有效负载的示例代码,显示了 for 循环并使用 push/pop 来设置计数器:

push 9
pop ecx

为什么不能直接使用mov呢?

mov ecx, 9

是的,通常你应该总是使用mov ecx, 9出于性能原因。它的运行效率比push/pop,作为可以在任何端口上运行的单微操作指令。 (Agner Fog 测试过的所有现有 CPU 都是如此:https://agner.org/optimize/ https://agner.org/optimize/)


正常的原因是push imm8 / pop r32是机器代码没有零字节。这对于外壳代码必须通过以下方式溢出缓冲区strcpy或任何其他将其视为隐式长度 C 字符串的一部分的方法,该字符串以 a 结尾0 byte.

mov ecx, immediate仅适用于 32 位立即数,因此机器代码如下所示B9 09 00 00 00. vs. 6a 09按9;59流行 ecx.

(ECX是寄存器号1,这就是哪里B9 and 59来自:指令的低3位=001)


另一个用例纯粹是代码大小: mov r32, imm32是 5 个字节(使用 no ModRM 编码,将寄存器编号放在操作码的低 3 位中),因为不幸的是 x86 缺少符号扩展的 imm8 操作码mov(没有mov r/m32, imm8)。几乎所有可追溯到 8086 的 ALU 指令都存在这种情况。

在 16 位 8086 中,该编码不会节省任何空间:3 字节短格式mov r16, imm16和假设一样好mov r/m16, imm8对于几乎所有事情,除了将立即转移到记忆中mov r/m16, imm16需要表单(带有 ModRM 字节)。

由于 386 的 32 位模式没有添加特定于该模式的新操作码,只是更改了默认操作数大小和立即数宽度,因此 32 位模式下 ISA 中的这种“错过的优化”从 386 开始。长 2 个字节,add r32,imm32现在比add r/m32, imm8. See x86 汇编 16 位与 8 位立即操作数编码 https://stackoverflow.com/questions/56524046/x86-assembly-16-bit-vs-8-bit-immediate-operand-encoding。但我们没有这个选项mov因为没有 MOV 操作码对其立即数进行符号扩展(或零扩展)。

有趣的事实:clang -Oz(即使以牺牲速度为代价来优化大小)将编译 https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:1.2899450879999999,j:1,lang:c%2B%2B,source:%27int+foo()%7Breturn+9%3B%7D%27),l:%275%27,n:%270%27,o:%27C%2B%2B+source+%231%27,t:%270%27)),k:46.68013929135108,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((g:!((h:compiler,i:(compiler:clang800,filters:(b:%270%27,binary:%271%27,commentOnly:%270%27,demangle:%270%27,directives:%270%27,execute:%271%27,intel:%270%27,libraryCode:%271%27,trim:%271%27),lang:c%2B%2B,libs:!(),options:%27-Oz%27,source:1),l:%275%27,n:%270%27,o:%27x86-64+clang+8.0.0+(Editor+%231,+Compiler+%231)+C%2B%2B%27,t:%270%27)),header:(),k:42.71263230348839,l:%274%27,m:50,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((h:compiler,i:(compiler:g91,filters:(b:%270%27,binary:%271%27,commentOnly:%270%27,demangle:%270%27,directives:%270%27,execute:%271%27,intel:%270%27,libraryCode:%271%27,trim:%271%27),lang:c%2B%2B,libs:!(),options:%27-Os%27,source:1),l:%275%27,n:%270%27,o:%27x86-64+gcc+9.1+(Editor+%231,+Compiler+%232)+C%2B%2B%27,t:%270%27)),header:(),l:%274%27,m:50,n:%270%27,o:%27%27,s:0,t:%270%27)),k:53.31986070864893,l:%273%27,n:%270%27,o:%27%27,t:%270%27)),l:%272%27,n:%270%27,o:%27%27,t:%270%27)),version:4 int foo(){return 9;} to push 9 ; pop rax。 GCC12也支持类似的-Oz.

也可以看看使用 x86/x64 机器代码打高尔夫球的技巧 https://codegolf.stackexchange.com/questions/132981/tips-for-golfing-in-x86-x64-machine-code/132985#132985Codegolf.SE(一个关于大小优化的网站,通常是为了好玩,而不是为了将代码放入小的 ROM 或引导扇区中。但是对于机器代码,大小优化有时确实有实际应用,即使是以牺牲性能为代价。)

如果您已经有另一个包含已知内容的寄存器,则可以使用 3 字节在另一个寄存器中创建 9lea ecx, [eax-0 + 9](如果 EAX 保持0)。只需 Opcode + ModRM + disp8。因此,如果您已经要对任何其他寄存器进行异或清零,则可以避免推送/弹出黑客攻击。lea效率几乎不低于mov,并且在优化速度时您可以考虑它,因为较小的代码大小在大规模中具有较小的速度优势:L1i 缓存命中,有时在 uop 缓存尚未热时进行解码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么在 shellcode 中使用 Push/Pop 而不是 Mov 将数字放入寄存器中? 的相关文章

  • gcc 如何知道内联汇编中使用的寄存器大小?

    我有内联汇编代码 define read msr index buf asm volatile rdmsr d buf 1 a buf 0 c index 使用该宏的代码 u32 buf 2 read msr 0x173 buf 我发现反汇
  • 推送 64 位 intel osx

    我想将 64 位地址压入堆栈 如下所示 asm pushq 0x1122334455667788 但我得到编译错误 我只能按以下方式推送 asm pushq 0x11223344 有人可以帮助我理解我的错误吗 我是装配新手 所以如果我的问题
  • C/C++ 中的简单“Hello World”内联汇编语言程序

    我使用 devcpp 和 borland c 编译器 asm mov ax 4 I O Func mov bx 1 Output func mov cx name address of the string mov dx 6 length
  • DASM 汇编器中的 ASCII 到 C64 屏幕代码

    我正在通过 C64 模拟器学习 6502 micro 的汇编 目前正在尝试将字符串输出到屏幕 这是我的代码 processor 6502 org 1000 ldx 00 using x register as column counter
  • 为什么这个“std::atomic_thread_fence”起作用

    首先我想谈一下我对此的一些理解 如有错误请指正 a MFENCE在x86中可以保证全屏障 顺序一致性可防止 STORE STORE STORE LOAD LOAD STORE 和 LOAD LOAD 重新排序 这是根据维基百科 https
  • 在 REP MOVSW 之前 PUSH CS / POP DS 的目的是什么?

    为什么在下面的代码中我们压入代码段 PUSH CS 然后将其弹出到数据段 POP DS 我将这些行明确指定为 line1 和 line2 请告诉我 MOVSW 在这里是如何工作的 IF HIGHMEMORY PUSH DS MOV BX D
  • 预取双类成员需要转换为 char*?

    我有一个正在使用的课程 mm prefetch 预先请求包含 double 类型的类成员的缓存行 class MyClass double getDouble return dbl other members double dbl othe
  • 什么时候汇编比C更快? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 Locked 这个问题及其答案是locked help locked posts因为这个问题是题外话 但却具有历史意义 目前不接受新的
  • 如何让c代码执行hex机器代码?

    我想要一个简单的 C 方法能够在 Linux 64 位机器上运行十六进制字节码 这是我的 C 程序 char code x48 x31 xc0 include
  • orpd等SSE2指令有什么意义?

    The orpd指令是 压缩双精度浮点值的按位逻辑或 这不是做完 全相同的事情吗por 按位逻辑或 如果是这样 拥有它还有什么意义呢 请记住 SSE1orps https www felixcloutier com x86 orps首先 实
  • 在 x86 程序集中将整数打印到控制台

    当我在 16 位汇编中添加两个值时 将结果打印到控制台的最佳方法是什么 目前我有这个代码 CODE START mov ax 1 put 1 into ax add ax 2 add 2 to ax current value mov ah
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • 为什么 Solaris 汇编器生成的机器代码与 GNU 汇编器在这里不同?

    我为 amd64 编写了这个小汇编文件 对于这个问题来说 代码的作用并不重要 globl fib fib mov edi ecx xor eax eax jrcxz 1f lea 1 rax ebx 0 add rbx rax xchg r
  • 在 x86 Intel VT-X 非根模式下,是否可以在每个指令边界传递中断?

    除了不将中断传送到虚拟处理器的某些正常指定条件 cli if 0 等 之外 客户机中的所有指令实际上都是可中断的吗 也就是说 当传入的硬件中断先传递给 LAPIC 然后传递给处理器时 据说会发生一些内部魔法 将其转换为虚拟中断给来宾 使用虚
  • NASM 轮班操作员

    您将如何在寄存器上进行 NASM 中的位移位 我读了手册 它似乎只提到了这些操作员 gt gt lt lt 当我尝试使用它们时 NASM 抱怨移位运算符处理标量值 您能解释什么是标量值并举例说明如何使用 gt gt and lt lt 另外
  • 汇编基础知识:输出寄存器值

    我刚刚开始学习汇编语言 我已经陷入了 在屏幕上显示存储在寄存器中的十进制值 的部分 我使用 emu8086 任何帮助将不胜感激 model small Specifies the memory model used for program
  • 为什么 Visual Studio 使用 xchg ax,ax

    我正在查看程序的反汇编 因为它崩溃了 并注意到很多 xchg ax ax 我用谷歌搜索了一下 发现它本质上是一个 nop 但是为什么 Visual Studio 会执行 xchg 而不是 noop 该应用程序是一个C NET3 5 64位应
  • 近调用/跳转表并不总是在引导加载程序中工作

    一般问题 我一直在开发一个简单的引导加载程序 并在某些环境中偶然发现了一个问题 在这些环境中 此类指令不起作用 mov si call tbl SI Call table pointer call call tbl Call print c
  • 减法进位标志

    我正在使用 MASM32 有了这个代码 mov eax 5 sub eax 10 CF 状态标志将被设置 但使用我的铅笔和纸 我实际上看到 MSB 没有任何进位 是的 我知道从较少的数字中减去大的数字集CF 但我想知道为什么 因为使用这段代
  • Nasm 打印到下一行

    我用 nasm Assembly 编写了以下程序 section text global start start Input variables mov edx inLen mov ecx inMsg mov ebx 1 mov eax 4

随机推荐