[英]x86_64 Assembly - Segfault when trying to edit a byte within an array in x64 assembly
The tutorial I am following is for x86 and was written using 32-bit assembly, I'm trying to follow along while learning x64 assembly in the process.我正在关注的教程是针对 x86 并使用 32 位汇编编写的,我在学习 x64 汇编的过程中尝试跟随。 This has been going very well up until this lesson where I have the following simple program which simply tries to modify a single character in a string;
这一直进展顺利,直到本课我有以下简单的程序,它只是尝试修改字符串中的单个字符; it compiles fine but segfaults when ran.
它编译得很好,但运行时会出现段错误。
section .text
global _start ; Declare global entry oint for ld
_start:
jmp short message ; Jump to where or message is at so we can do a call to push the address onto the stack
code:
xor rax, rax ; Clean up the registers
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
; Try to change the N to a space
pop rsi ; Get address from stack
mov al, 0x20 ; Load 0x20 into RAX
mov [rsi], al; Why segfault?
xor rax, rax; Clear again
; write(rdi, rsi, rdx) = write(file_descriptor, buffer, length)
mov al, 0x01 ; write the command for 64bit Syscall Write (0x01) into the lower 8 bits of RAX
mov rdi, rax ; First Paramter, RDI = 0x01 which is STDOUT, we move rax to ensure the upper 56 bits of RDI are zero
;pop rsi ; Second Parameter, RSI = Popped address of message from stack
mov dl, 25 ; Third Parameter, RDX = Length of message
syscall ; Call Write
; exit(rdi) = exit(return value)
xor rax, rax ; write returns # of bytes written in rax, need to clean it up again
add rax, 0x3C ; 64bit syscall exit is 0x3C
xor rdi, rdi ; Return value is in rdi (First parameter), zero it to return 0
syscall ; Call Exit
message:
call code ; Pushes the address of the string onto the stack
db 'AAAABBBNAAAAAAAABBBBBBBB',0x0A
This culprit is this line:罪魁祸首是这一行:
mov [rsi], al; Why segfault?
If I comment it out, then the program runs fine, outputting the message 'AAAABBBNAAAAAAAABBBBBBBB', why can't I modify the string?如果我注释掉它,那么程序运行正常,输出消息'AAAABBBNAAAAAAABBBBBBBB',为什么我不能修改字符串?
The authors code is the following:作者代码如下:
global _start
_start:
jmp short ender
starter:
pop ebx ;get the address of the string
xor eax, eax
mov al, 0x20
mov [ebx+7], al ;put a NULL where the N is in the string
mov al, 4 ;syscall write
mov bl, 1 ;stdout is 1
pop ecx ;get the address of the string from the stack
mov dl, 25 ;length of the string
int 0x80
xor eax, eax
mov al, 1 ;exit the shellcode
xor ebx,ebx
int 0x80
ender:
call starter
db 'AAAABBBNAAAAAAAABBBBBBBB'0x0A
And I've compiled that using:我已经使用以下方法编译了它:
nasm -f elf <infile> -o <outfile>
ld -m elf_i386 <infile> -o <outfile>
But even that causes a segfault, images on the page show it working properly and changing the N into a space, however I seem to be stuck in segfault land:( Google isn't really being helpful in this case, and so I turn to you stackoverflow, any pointers (no pun intended!) would be appreciated但即使这会导致段错误,页面上的图像显示它正常工作并将 N 更改为空格,但是我似乎被困在段错误领域:(谷歌在这种情况下并没有真正提供帮助,所以我转向你stackoverflow,任何指针(没有双关语!)将不胜感激
I would assume it's because you're trying to access data that is in the .text
section.我认为这是因为您正在尝试访问
.text
部分中的数据。 Usually you're not allowed to write to code segment for security.通常,为了安全起见,您不允许写入代码段。 Modifiable data should be in the
.data
section.可修改的数据应该在
.data
部分。 (Or .bss
if zero-initialized.) (或
.bss
如果零初始化。)
For actual shellcode, where you don't want to use a separate section, see Segfault when writing to string allocated by db [assembly] for alternate workarounds.对于不希望使用单独部分的实际 shellcode,请参阅Segfault 当写入由 db [assembly] 分配的字符串时的替代解决方法。
Also I would never suggest using the side effects of call
pushing the address after it to the stack to get a pointer to data following it, except for shellcode.此外,我永远不会建议使用
call
将地址之后的地址推送到堆栈的副作用来获取指向其后数据的指针,除了 shellcode。
This is a common trick in shellcode (which must be position-independent);这是 shellcode 中的一个常见技巧(必须与位置无关); 32-bit mode needs a call to get EIP somehow.
32 位模式需要调用以某种方式获取 EIP。 The
call
must have a backwards displacement to avoid 00
bytes in the machine code, so putting the call somewhere that creates a "return" address you specifically want saves an add
or lea
. call
必须具有向后位移以避免机器代码中的00
字节,因此将调用放置在创建您特别想要的“返回”地址的地方可以节省add
或lea
。
Even in 64-bit code where RIP-relative addressing is possible, jmp / call / pop is about as compact as jumping over the string for a RIP-relative LEA with a negative displacement.即使在可以使用 RIP 相对寻址的 64 位代码中,jmp / call / pop 也与跳过具有负位移的 RIP 相对 LEA 的字符串一样紧凑。
Outside of the shellcode / constrained-machine-code use case, it's a terrible idea and you should just lea reg, [rel buf]
like a normal person with the data in .data
and the code in .text
.在shellcode / 受限机器代码用例之外,这是一个糟糕的主意,您应该像普通人一样使用
.data
的数据和.text
的代码lea reg, [rel buf]
。 (Or read-only data in .rodata
.) This way you're not trying execute code next to data, or put data next to code. (或
.rodata
只读数据。)这样您就不会尝试在数据旁边执行代码,或将数据放在代码旁边。
(Code-injection vulnerabilities that allow shellcode already imply the existence of a page with write and exec permission, but normal processes from modern toolchains don't have any W+X pages unless you do something to make that happen. W^X is a good security feature for this reason, so normal toolchain security features / defaults must be defeated to test shellcode.) (允许 shellcode 的代码注入漏洞已经暗示存在具有 write 和 exec 权限的页面,但是现代工具链中的正常进程没有任何 W+X 页面,除非你做一些事情来实现它。W ^X是一个出于这个原因,良好的安全功能,因此必须击败正常的工具链安全功能/默认值才能测试 shellcode。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.