【问题标题】:How to use printf without storing data into a variable nasm?如何在不将数据存储到变量 nasm 的情况下使用 printf?
【发布时间】:2021-01-15 00:27:20
【问题描述】:

我试图在汇编中打印一个字符串,而不必将我的数据存储到一个变量中。

什么有效

global  _main
    extern  _printf

section .data:
    message: db  'AAAA'

section .text
    _main:
    push    message
    call    _printf
    add     esp, 4
    ret

现在,我想要的是直接将AAAA 推入堆栈并调用printf 进行打印,而无需将其存储在message 中。

我花了好几个小时用不同的方法尝试自己做,但到目前为止都失败了。

试过了:

方法#1:

push 41414141h
call _printf

方法#2:

mov esi, 41414141h
push esi
call _printf

方法#3:

mov   esi, 41414141h
mov   edi, 7325      ; '%s'
call  _printf
; or storing 'AAAA' in EDI and storing '%s' in ESI

方法#4:

mov   esi, 41414141h
mov   edi, 7325      ; '%s'
push  esi
push  edi
call  _printf
; or storing 'AAAA' in EDI and storing '%s' in ESI

方法#5:

mov   esi, 41414141h
mov   edi, 7325      ; '%s'
push  esi
xor   eax, eax
push  eax            ; just push a null byte to the stack
push  edi
call  _printf
; or storing 'AAAA' in EDI and storing '%s' in ESI

还有很多我都不记得了。

那么,打印存储在寄存器中的值的正确方法是什么。

如果也可以在下面回答,我将不胜感激。

在调查了第一次尝试的工作原理之后。我意识到,数据或AAAA 存储在程序的堆栈中,然后将数据的地址推送到ESP。

enter image description here

不幸的是,我不知道如何将数据推送到程序的堆栈\.data 部分,或者如果这甚至可能,以及如何将该部分的地址推送到 ESP。

【问题讨论】:

  • push 0; push 41414141h; push esp; call _printf 请注意,您需要将字符串归零。您还需要删除放在堆栈上的东西。
  • @Jester,你做得更好,效果很好:)
  • 您的数据部分版本仅适用,因为它后面恰好有一些 0 填充。您应该使用 db 'AAAA', 0 来制作 C 字符串,或者使用 fwrite 和指针,长度,这样您就不需要终止符。 (另外,通常你会将只读常量数据放入section .rodata

标签: windows assembly x86 nasm


【解决方案1】:

我不习惯使用_printf 函数,但我可以展示如何使用系统调用来做到这一点:

mov dword [esp-4], 'AAAA' ; This is the red-zone, try googling it
    
; The actual printing
mov eax, 4
mov ebx, 1
lea ecx, [esp-4]
mov edx, 4
int 80h

(顺便说一下,这里使用的是 Linux 32 位系统调用,不知道 windows 系统调用是什么)

也许试试pushing [esp-4]

[编辑] 忘了提到这个的一个主要限制(至少对于系统调用,仍然不确定 _printf),它最多只能打印 4 个字符,不能再多了。 [ANOTHER EDIT] 正如其他评论者指出的那样,这种方法可能不安全(检查 cmets),因此您可以决定避免使用它。

【讨论】:

  • 请注意,依靠 ESP 下的数据在您的 mov 存储和系统调用读取它时保持未修改在技术上是不安全的。信号处理程序(或评估print foo() 的调试器)可以使用该堆栈空间。只有 x86-64 System V ABI 才能保证低于 RSP 的红色区域可以安全使用。在这种情况下,push 'AAAA' / mov ecx, esp 会是更好的选择。
  • 回复:您对这个问题的实际回答:mov dword [esp-4], 'AAAA' / push [esp-4]不是是个好主意。这相当于push 'AAAA'; push [esp-4] 基本上是一个 NOP(从同一位置加载并重新存储一个 dword:从旧 ESP 下方加载,然后在递减后存储在新 ESP 中)。 libc printf 期望 pointer 指向一个以 0 结尾的 C 字符串(隐式长度,这就是没有 4 arg 的原因),而不是值。 Windows cdecl 调用约定或多或少与 Linux i386 Sys V 调用约定相匹配,因此您可以在 Linux 上使用call printf
  • 无论如何,正如问题中的 cmets 所述,您可以推送终止的 0然后 推送一些字符串数据。 ESP 然后指向 C 字符串,所以你可以push esp / call printf。顺便说一句,与 Linux 不同,Windows 的原始系统调用接口没有文档或支持,因此标准方法是使用 WinAPI DLL 函数调用。 (或对 DLL 进行逆向工程以直接在特定版本的 Windows 上进行原始系统调用。)我认为 32 位 Windows DLL 最终会进行远调用或跳转到 64 位用户空间以使用 syscall,这与 Linux sysenter 相比,开销似乎很大。
  • 我建议修复您的答案以显示可以在堆栈上构造一个以 0 结尾的 C 字符串的内容。我投了反对票,因为这对调用 printf 没有任何用处。 Linux sys_write 作为 Windows 问题的答案并不是特别有用,并且已经有 Linux Q&A 表明了这一点。如果/当这个答案有所改善时,我很乐意删除或撤销我的反对意见,特别是修复push [esp-4] 可能有用的建议。如果您的意思是 LEA / push,请记住 push 将覆盖 [esp-4] 处的任何内容,因为它执行 ESP-=4 和 [esp]=src。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
  • 2012-10-22
  • 2018-06-23
相关资源
最近更新 更多