使用 Intel x86 程序集在 Unix 上手动空终止(地址与内存操作数中的值)?
Manual Null-Termination on Unix using Intel x86 Assembly (address vs value in memory operands)?
我一定是遗漏了一些明显的东西,但我似乎找不到字符串的结尾。
我的代码从几个调用开始,如下所示:
; read user input
;
mov eax, SYSCALL_READ ; read function
mov ebx, STDIN ; Arg 1: file descriptor
mov ecx, buf ; Arg 2: address of buffer (buffer is input)
mov edx, BUFLEN ; Arg 3: buffer length (defined as 256)
int 080h
mov [rlen], eax ; save length of string read
教授给了我们一个 shell 程序来工作,但我对其中的大部分都掌握得很好。让我失望的是,我的印象是 rlen 现在应该包含我正在使用的字符串的长度,但是当我键入以下内容时:
mov byte[esi + rlen], 92 ; add a zero
我遇到段错误。同样,如果我使用 [buf + rlen]。 buf 和 ESI 本身都不会导致段错误,所以在我看来 rlen 没有按照我的想法去做。
谁能帮我弄清楚这是怎么回事?
您的代码有两个问题:
mov byte[esi + rlen], 92
92 != 0
。终止零字节 '[=13=]'
是一个值为零的整数。
rlen 是一个地址,不是那个地址的值。
所以测试读取 returned >= 0,然后使用仍在寄存器中的 return 值。
; read(2) return value still in eax
test eax, eax
jl read_error ; handle errors on eax less than zero.
mov esi, buf ; mov imm32 to get the address in a register
mov [rlen], eax ; store return value to the rlen global
mov byte ptr[esi + eax], 0
;or: mov byte ptr [buf + eax], 0 ; works because the buffer is statically allocated.
jz handle_EOF ; flags still set from test
或者,如果您将 ecx 复制到未被读取系统调用破坏的寄存器,则可以使用它而不是重新加载。
在一个函数中,将局部变量视为存在于寄存器中,并且内存位置仅在您 运行 寄存器不足时可以溢出它们的地方。不要像非优化编译器和 store/reload 你不需要的变量。这在像 x86-64 这样有 16 个寄存器的架构上更容易; 32 位 x86 非常有限,并且有一个过时的 args-on-the-stack ABI。
如果您的缓冲区已经清零,您可以只向 read(2) 传递一个比缓冲区大小小 1 的计数。不过,在读取 returns 后将最后一个字节归零更好。
我一定是遗漏了一些明显的东西,但我似乎找不到字符串的结尾。
我的代码从几个调用开始,如下所示:
; read user input
;
mov eax, SYSCALL_READ ; read function
mov ebx, STDIN ; Arg 1: file descriptor
mov ecx, buf ; Arg 2: address of buffer (buffer is input)
mov edx, BUFLEN ; Arg 3: buffer length (defined as 256)
int 080h
mov [rlen], eax ; save length of string read
教授给了我们一个 shell 程序来工作,但我对其中的大部分都掌握得很好。让我失望的是,我的印象是 rlen 现在应该包含我正在使用的字符串的长度,但是当我键入以下内容时:
mov byte[esi + rlen], 92 ; add a zero
我遇到段错误。同样,如果我使用 [buf + rlen]。 buf 和 ESI 本身都不会导致段错误,所以在我看来 rlen 没有按照我的想法去做。
谁能帮我弄清楚这是怎么回事?
您的代码有两个问题:
mov byte[esi + rlen], 92
92 != 0
。终止零字节'[=13=]'
是一个值为零的整数。rlen 是一个地址,不是那个地址的值。
所以测试读取 returned >= 0,然后使用仍在寄存器中的 return 值。
; read(2) return value still in eax
test eax, eax
jl read_error ; handle errors on eax less than zero.
mov esi, buf ; mov imm32 to get the address in a register
mov [rlen], eax ; store return value to the rlen global
mov byte ptr[esi + eax], 0
;or: mov byte ptr [buf + eax], 0 ; works because the buffer is statically allocated.
jz handle_EOF ; flags still set from test
或者,如果您将 ecx 复制到未被读取系统调用破坏的寄存器,则可以使用它而不是重新加载。
在一个函数中,将局部变量视为存在于寄存器中,并且内存位置仅在您 运行 寄存器不足时可以溢出它们的地方。不要像非优化编译器和 store/reload 你不需要的变量。这在像 x86-64 这样有 16 个寄存器的架构上更容易; 32 位 x86 非常有限,并且有一个过时的 args-on-the-stack ABI。
如果您的缓冲区已经清零,您可以只向 read(2) 传递一个比缓冲区大小小 1 的计数。不过,在读取 returns 后将最后一个字节归零更好。