Assembly x86_64程序集-尝试在x64程序集中编辑数组中的字节时出现SEGFULT
我下面的教程是针对x86的,并且是使用32位汇编编写的,在学习x64汇编的过程中,我将尝试遵循这一教程。这一直都很顺利,直到本课,我有下面的简单程序,它只是试图修改字符串中的单个字符;它编译的很好,但在运行时会出错Assembly x86_64程序集-尝试在x64程序集中编辑数组中的字节时出现SEGFULT,assembly,segmentation-fault,x86-64,Assembly,Segmentation Fault,X86 64,我下面的教程是针对x86的,并且是使用32位汇编编写的,在学习x64汇编的过程中,我将尝试遵循这一教程。这一直都很顺利,直到本课,我有下面的简单程序,它只是试图修改字符串中的单个字符;它编译的很好,但在运行时会出错 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
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
罪魁祸首是这样的:
mov [rsi], al; Why segfault?
如果我将其注释掉,那么程序运行正常,输出消息“aaaabbbnbaaaaaaaabbbbbb”,为什么我不能修改字符串
作者代码如下:
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
我已经用以下方法编译了:
nasm -f elf <infile> -o <outfile>
ld -m elf_i386 <infile> -o <outfile>
nasm-f elf-o
ld-m elf_i386-o
但即使这样也会导致segfault,页面上的图片显示它工作正常,并将N改为一个空格,但我似乎陷入了segfault的境地:(谷歌在这种情况下没有真正的帮助,所以我转向你stackoverflow,任何指针(没有双关语!)非常感谢我认为这是因为您试图访问
.text
部分中的数据。通常,出于安全考虑,您不允许写入代码段。可修改的数据应位于.data
部分。(或者.bss
如果初始化为零。)
对于不希望使用单独部分的实际外壳代码,请参阅以了解其他解决方法
此外,我决不会建议使用
call
的副作用将其后面的地址推送到堆栈中,以获得指向其后面数据的指针,外壳代码除外
这是外壳代码中的常见技巧(必须独立于位置);32位模式需要调用才能以某种方式获取EIP。调用
必须具有向后位移,以避免机器代码中出现00
字节,因此将调用放在创建“返回”的位置您特别想要的地址保存一个add
或lea
即使在可以进行RIP相对寻址的64位代码中,jmp/call/pop的紧凑性也相当于跳过带有负位移的RIP相对LEA的字符串
在外壳代码/受限机器代码用例之外,这是一个糟糕的想法,你应该像普通人一样使用.data
中的数据和.text
中的代码(或.rodata
中的只读数据)这样,您就不会试图在数据旁边执行代码,或者将数据放在代码旁边
(允许外壳代码的代码注入漏洞已经暗示存在具有写入和执行权限的页面,但现代工具链中的正常进程没有任何W+X页面,除非您采取措施使其发生。因此,这是一个很好的安全功能,因此必须击败正常的工具链安全功能/默认设置。)测试外壳代码。)他没有试图写入.text节内存地址,而是试图写入一个不存在的内存地址。他想要rsi中的堆栈地址,但他做了一个pop rsi,没有mov rsi,rsp@sinkmanu是的,他们是。阅读代码:call将地址放在堆栈中,然后他们将其放入rsi。他们不希望rsp进入rsi。是的,我没有我看不出他没有将消息保存在.data部分。这种技术被称为“跳转调用pop”@MykelStone:i386 Linux历史上有R+X文本和R+X+W数据,因为直到NX位(和AMD64),无法使页面可读但不可执行。不过,始终存在写保护,Linux将其用于可执行文件的文本段。我非常确定,将作者的代码构建为正常的32位可执行文件,并以正常方式运行它时,始终会出现segfaulted。
调用
将指针推送到following数据是我以前在外壳代码示例中见过的东西,但是在外壳代码利用中,您没有把数据放在不同的部分的奢侈。而且代码+数据通常只在RWX页面中工作,通常在堆栈上。TL:DR:一些外壳代码黑客在具有只读文本段的正常可执行文件中不工作。Disablingrandomize\u va\u space
仅在您将其注入某个内容时才起作用。call
推送以下地址是PIC,代码的其余部分也是PIC@MykelStone。因为您的.text段标记为readonly。