【问题标题】:Loading a string into a local variable dynamically将字符串动态加载到局部变量中
【发布时间】:2025-12-24 18:40:11
【问题描述】:

假设,我在堆栈中创建了一个局部变量,或者为字符串分配了内存,现在我想在其中放入一个值。

push ebp
mov ebp, esp
sub esp, 0xff ; 255 bytes

并在 C 中将字符串存储在堆栈中:

strcat(my_str1, "something1");
strcat(my_str1, get_var2());
strcat(my_str1, "something3");
strcat(my_str1, get_var4());

我想了解如何在没有 FASM 或 NASM 中的“strcat”的情况下做到这一点:

;1
mov byte ptr [esp - 1], 's'
mov byte ptr [esp - 2], 'o'
mov byte ptr [esp - 3], 'm'
mov byte ptr [esp - 4], 'e'
; and so on

这是怎么做的?关于“get_var2()”和“get_var4()”的部分?

;2
call get_var2
mov ??byte?? ptr [esp - 11], eax
; or lea?

【问题讨论】:

  • 尝试先在 C 中实现strcat(不使用任何库函数)。然后你看看它是如何工作的,你可以在 asm 中做同样的事情。

标签: assembly nasm fasm


【解决方案1】:

1) sub esp, 0xff 保持 esp 对齐,至少每 4 个。如果 C 字符串只需要 255 个字节(254 个字符 + 零终止符),则分配 256。

2) mov [esp-1],'s' 你已经递减了esp,所以局部变量在某个[esp+X] 地址(多少取决于你压入堆栈的其他值的数量)。或者更确切地说,将[ebp-Y]与类C函数序言一起使用,首先在ebp中初始化堆栈帧指针(push ebpmov ebp,espsub esp,local_vars_size..然后是结尾mov esp,ebppop ebpret)。

但永远不要触摸esp 下的值,除非您确切知道自己在做什么以及为什么(例如在 64b 模式下写入红色区域),因为如果您不确定,那么在 99% 中它将以错误结束,并且经常是那些很难找到的,很少影响代码的。

3) 在 NASM 中,这 4 个 mov 可以用单个 dword 常量 mov [esp-4],dword 'emos' 完成(顺便说一句,您可能确实想要设置 'some'?)

是的,这是编译器在本地分配的堆栈空间中设置较短常量的常用方法,对于较大的数据,他们可能会使用来自常量模板数据的memcpy

4) 关于get_var2(),所以get_var2() 返回稳定的(char *) 指针?然后你将不得不重新实现完整的strcat,除非你能知道字符串中已经有多少个字符,以及将添加多少个字符。如果get_var2()返回一些值,你必须先把它转换成一些字符,然后决定多少个等等。然后你可以直接写它们,或者在一些循环中等等。太宽泛了。

此外,在本地堆栈上操作时,我会在变量周围分配一些安全空间,以防您超出边界并覆盖几个字节。您甚至可以先将这些额外字节设置为某个金丝雀值,然后在函数结束时检查它,以查看是否发生了某些堆栈覆盖(至少在调试版本中)。而不是strcat,你需要strncat或类似的,以避免恶意数据溢出堆栈。

否则,正如 Jester 建议的那样,也许首先尝试在固定内存缓冲区上实现 strcat,这样您就可以专注于代码本身,而不是解决本地内存的分配问题。然后你可以在它的基础上进行构建,甚至将它用作函数调用。

【讨论】:

  • 1) sub esp, 0xff keep esp aligned, at least per 4. -- 为什么是 4?
  • @Kevin:在 x86 上,即使内存访问未对齐(但会付出相当大的性能损失),您也基本上会侥幸成功。但是使用某些 SIMD(和 FP?)指令,您将在非法内存访问时崩溃(这通常需要 16B 对齐,并且通常在 64b 调用约定中是强制性的,但在 32b 中不是)。仅仅由于性能原因,它被认为是非常糟糕的做法,永远不要这样做。它甚至没有任何好处,除了节省 1-3 个字节,这在 x86 世界中是不相关的(甚至数据结构也被填充以使其成员对齐,比这浪费更多的字节)。