我发现了一篇关于自修改代码的文章,并试图做一些例子,但我总是得到分段错误。就像我能理解的那样,内存权限存在违规行为。代码段是(r)ead / e(x)ecute,因此写入的尝试导致此错误。有没有办法通过在运行时或之前更改内存权限来测试程序?我正在使用linux,这个例子是用GAS汇编编写的。
.extern memcpy
.section .data
string:
.asciz "whatever"
string_end:
.section .bss
.lcomm buf, string_end-string
.section .text
.globl main
main:
call changer
mov $string, %edx
label:
push string_end-string
push $buf
push $string
call memcpy
changer:
mov $offset_to_write, %esi
mov $label, %edi
mov $0xb, %ecx
loop1:
lodsb
stosb
loop loop1
ret
offset_to_write:
push 0
call exit
end:
所以osgx建议修改之后这是一个正常工作的代码。(实际上,如果你组装& link& run it crashes但是如果你观看使用gdb它会修改它的代码!)
.extern memcpy
.section .data
string:
.asciz "Giorgos"
string_end:
.section .bss
.lcomm buf, string_end-string
.section .text
.globl main
main:
lea (main), %esi # get the start of memory region to
# change its permissions (smc-enabled)
andl $0xFFFFF000, %esi # align to start of a pagesize
pushl $7 # permissions==r|w|x
pushl $4096 # page size
pushl %esi # computed start address
call mprotect
call changer # function that does smc
mov $string, %edx
label:
push string_end-string # this code will be overridden
push $buf # and never be executed!
push $string
call memcpy
changer:
mov $offset_to_write, %esi # simple copy bytes algorithm
mov $label, %edi
mov $0xb, %ecx
loop1:
lodsb
stosb
loop loop1
ret
offset_to_write: # these instructions will be
push $0 # executed eventually
call exit
end:
答案 0 :(得分:14)
您应该在运行时更改内存访问权限。
#include <sys/mman.h>
void *addr = get_address_of_instruction_pointer();
int length = 4096; /* size of a page */
if (mprotect(addr, length, PROT_READ | PROT_WRITE | PROT_EXEC) == 0) {
/* current code page is now writable and code from it is allowed for execution */
}
答案 1 :(得分:5)
现代CPU具有一个名为DEP的功能,可防止在堆栈上执行代码。以前,这是可能的;现在,事实并非如此。默认情况下,二进制文件将加载到只读内存中。
通过这种方式,您可以使用mprotect系统调用将内存中的二进制文件位置标记为可执行文件 - 因为您的代码不受保护。所以不要试图放入代码和堆栈,然后跳进去。
答案 2 :(得分:2)
您还可以通过将开关-N
传递给链接器来禁用整个程序的写保护。如果要从gcc调用链接器,请传递Wl,-N
。如果您直接调用ld
,请传递-N
。