You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

x86-64 NASM Intel风格汇编中字符串反转时的数值篡改Bug排查问询

无栈x86-64 NASM整数转字符串的数值篡改Bug分析

我写了一个无栈的x86-64 NASM Intel风格子程序,用来把整数转成字符串。汇编链接都没问题,但运行时遇到了奇怪的Bug:输入1691时,预期输出是"1691",实际却得到"1432"。我已经定位到问题出在_reverse_string里的mov [str_digits + rcx], bl这一行——如果去掉反转逻辑,输出是正确的逆序"1961",说明确实需要反转,但我搞不懂为什么当前逻辑会篡改数值。相关代码如下:

section .data
section .bss
str_digits resb 100
section .text
global _start
_start:
mov rax, 1691
call _int_to_str
call _exit
_int_to_str:
mov rbx, 10
mov rcx, 0
_conversion_loop:
mov rdx, 0
div rbx
add rdx, 48
mov [str_digits + rcx], dl
inc rcx
cmp rax, 0
jne _conversion_loop
mov rbx, [str_digits]
mov rdx, rcx
_reverse_string:
mov [str_digits + rcx], bl
inc rbx
dec rcx
cmp rcx, 0
jne _reverse_string
_output_string:
mov rax, 1
mov rdi, 1
mov rsi, str_digits
syscall
ret
_exit:
mov rax, 60
mov rdi, 0
syscall

我做了这些调试工作:

  • 确认mov [str_digits + rcx], bl语法合法;
  • 用GDB调试,得到以下关键信息:
    (gdb) disass _reverse_string
    => 0x000000000040103f <+0>: mov BYTE PTR [rcx+0x402000],bl
    0x0000000000401045 <+6>: inc rbx
    0x0000000000401048 <+9>: dec rcx
    0x000000000040104b <+12>: cmp rcx,0x0
    0x000000000040104f <+16>: jne 0x40103f <_reverse_string>
    (gdb) info reg rcx
    rcx            0x4                 4
    (gdb) info reg rbx
    rbx            0x31363931          825637169
    (gdb) info reg bl
    bl             0x31                49
    # 单步执行后
    (gdb) info reg rcx
    rcx            0x3                 3
    (gdb) info reg rbx
    rbx            0x31363932          825637170
    (gdb) info reg bl
    bl             0x32                50
    
    这里发现每次inc rbx后,bl的值都会加1,完全不是我想要的原字符串的下一个字符;
  • 用strace跟踪,发现系统调用本身没问题,只是输出内容错误:
    execve("./test3", ["./test3"], 0x7ffc5651b270 /* 91 vars */) = 0
    write(1, "1432", 41432)                  = 4
    exit(0)                                 = ?
    +++ exited with 0 +++
    
  • 尝试在mov [str_digits + rcx], bl前加mov bl, [rbx],结果触发段错误。

注:我不是要问“怎么反转字符串”或“怎么实现整数转字符串”(这些我知道有解决方案),而是专门想搞懂这个数值篡改Bug的原因。


Bug原因分析

问题的核心出在你初始化rbx的方式上:

mov rbx, [str_digits]

在x86-64汇编里,[str_digits]表示加载内存地址str_digits处的内容到rbx中,而不是把str_digits这个地址本身加载到rbx(这才是你需要的指针)。

具体来说,_conversion_loop结束后,str_digits里存储的是逆序的字符:'1'(0x31)、'9'(0x39)、'6'(0x36)、'1'(0x31)(因为1691除以10的余数依次是1、9、6、1)。由于x86是小端模式,mov rbx, [str_digits]会把这四个字节(加上后面四个0字节)拼成一个64位数值0x0000000031363931,也就是十进制的825637169。

接下来的inc rbx是对这个数值加1,而不是对指针移动。所以每次执行inc rbx,rbx的值就会加1,对应的bl(rbx的低8位)也会加1:

  • 初始bl是0x31('1')
  • 第一次inc后,bl变成0x32('2')
  • 第二次inc后,bl变成0x33('3')
  • 第三次inc后,bl变成0x34('4')

然后看你的反转循环逻辑:

_reverse_string:
mov [str_digits + rcx], bl  ; 把bl的值写到str_digits+rcx的位置
inc rbx                     ; 数值rbx加1
dec rcx                     ; 索引rcx减1
cmp rcx, 0
jne _reverse_string

第一次循环时rcx是4,你把'1'写到str_digits+4的位置;第二次rcx是3,把'2'写到str_digits+3;第三次rcx是2,把'3'写到str_digits+2;第四次rcx是1,把'4'写到str_digits+1。循环结束后,str_digits里的内容变成:
str_digits[0] = '1'(原内容没被修改)、str_digits[1]='4'、str_digits[2]='3'、str_digits[3]='2',所以输出就是"1432",完全符合你看到的错误结果。

至于你尝试加mov bl, [rbx]触发段错误,是因为rbx里存储的是0x31363931这样的数值,这个地址不属于你的程序内存空间,访问它自然会触发段错误。

关键修正点

如果你要继续用指针方式反转,正确的初始化应该是把str_digits的地址加载到rbx,而不是加载内容:

mov rbx, str_digits  ; 把字符串的起始地址(指针)加载到rbx,而不是内容

之后的inc rbx才是移动指针,指向字符串的下一个字符,这样你才能通过mov bl, [rbx]读取到原字符串的下一个字符。

内容的提问来源于stack exchange,提问作者maybe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 06:39:28