【问题标题】:How to declare a char buffer in inline assembly?如何在内联汇编中声明一个字符缓冲区?
【发布时间】:2020-03-17 17:03:00
【问题描述】:

到目前为止,我有以下代码:

char* s;
s = new char[10];

__asm
{       
    mov ebx, s
    mov byte ptr[ebx], 0x48       ;'H'
    mov byte ptr[ebx + 1], 0x65   ;'e'
    mov byte ptr[ebx + 2], 0x6C   ;'l'
    mov byte ptr[ebx + 3], 0x6C   ;'l'
    mov byte ptr[ebx + 4], 0x6F   ;'o'
    mov byte ptr[ebx + 5], 0x0A   ; 'new line'
    mov byte ptr[ebx + 6], 0;
    mov eax, ebx
    push eax
    call DWORD ptr printf
    pop ebx  
}

我想在 asm 中声明一个不使用 DB 的 char 数组,看起来如何?

【问题讨论】:

  • 您没有为您的问题提供足够的细节。你在说什么内存分配?您想要汇编语言和printf() 的内存分配?这真的没有意义,因为一个是语言,一个是标准的 C 函数。
  • 不,对不起,我措辞不好,我想在 asm 中为 char 数组分配内存,然后调用 printf
  • 你问的是如何用汇编语言声明一个字符数组?还是你问如何在运行时动态分配数组?
  • 如果您询问如何声明字符数组,请参阅this question
  • 为什么?它是 inline 程序集,与独立程序相反,它更容易返回到 C 中进行打印。此外,NASM 与它有什么关系?内联汇编通常由编译器进行汇编。你的情况下的 C 编译器。

标签: c arrays assembly visual-c++ inline-assembly


【解决方案1】:

也可以这样:

call do_print
db "Hola...Hello", 0Ah,0
do_print:
call DWORD ptr printf
pop rcx

在调用 do_print 时,我们将实际上是缓冲区“Hello..”的起始地址放入堆栈返回地址,在汇编程序中如此简单,改为 pop rcx 而不是 pop [esp]

【讨论】:

  • 虽然此代码 sn-p 可能是解决方案,但包含解释确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
  • 这个坏了;在离开内联 asm 语句之前,您忘记将字符串指针(由第一个 call 推送)从堆栈中弹出。最后你需要一个虚拟弹出(如pop ecx),或add esp,4。这也使返回地址预测器不平衡,因此包含函数末尾的ret 将错误地预测为转到db,并且将来备份调用树的rets 也会错误地预测。
  • call DWORD ptr printf 是关于问题的stdcall,call do_printf 将字符串的地址放入堆栈,对不起,你是__cdecl,最后弹出一些东西
  • 可变函数永远不是标准调用。使用错误数量或类型的参数调用 printf 是 C UB,所以理论上它可能是这样,但实际上,制作一个弹出可变大小作为返回值的函数并不方便。 ret imm16 只接受和立即,并且您不想要 pop ecx / add esp, edx /jmp ecx 因为这会使返回地址预测器堆栈失衡。此外,太多的代码依赖于 printf 默默地忽略额外的参数。也许使用puts,这样你也可以省略\n
  • pop dword ptr [esp] 的效率低于add esp,4。只有虚拟弹出到 register 才有意义。无论如何,您的回答仍然没有解释为什么这样做(call 将指针推送到字符串)或性能下降。
【解决方案2】:

我就是这样做的:

  push 0x6F
  push 0x6c6c6548
  push esp
  call DWORD ptr printf
  add esp, 12 

【讨论】:

  • 为堆栈上的缓冲区保留匿名空间。在这种情况下,这正是您真正想要的,但它不是任何类型的 声明。您可以说“创建”但不声明。仍然是 Defining a variable inside c++ inline assembly 的副本,它显示堆栈上的保留空间。
  • 哦,在 inline asm 中修改 EBP 会破坏任何编译器生成的本地寻址模式。如果你写了mov ecx, my_local_var,你会加载错误的东西。相反,add esp, whatever 在您调用清理堆栈之后。或者使用任何其他保留呼叫的 reg,例如 EDI,而不是 EBP。
  • 另外,你不需要push eaxpush 'o' 已经是符号扩展立即数的 4 字节推送,因此它存储 0x0000006F。您也不需要sub esp, 16 来在您推送的文字字符串数据上方留下空白:推送已经为您移动了 ESP,这就是为什么它被称为 push 而不是 mov [esp], 0x6c6c6548
  • 这样可以吗? push 0x6F push 0x6c6c6548 push esp call DWORD ptr printf add esp, 12
  • 是的,在我看来很正常。 printf 是可变参数,所以它绝对不会从堆栈中删除自己的参数,所以是的 add esp,12 删除了指针和字符串数据。
猜你喜欢
  • 1970-01-01
  • 2013-10-06
  • 2022-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-01
  • 1970-01-01
  • 2012-03-03
相关资源
最近更新 更多