Самомодифицирующий код всегда приводит к ошибкам сегментации в Linux
я нашел статью о самомодифицирующем коде и попытался сделать некоторые примеры, но у меня всегда есть ошибки сегментации. Как я понимаю, есть нарушения в разрешениях памяти. Сегмент кода (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 & запускаете его сбой, но если вы смотрите с помощью 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:
Ответы
Ответ 1
Вы должны изменить разрешения доступа к памяти во время выполнения.
#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 */
}
Ответ 2
Современные процессоры имеют функцию DEP, которая предотвращает выполнение кода в стеке. Раньше это было возможно; теперь это не так. По умолчанию двоичный файл загружается в постоянную память.
С помощью этого способа вы можете использовать системный вызов mprotect, чтобы пометить ваше двоичное местоположение в памяти как исполняемое. ТАКОЕ ДЛИННО, КАК ВАШ КОД НЕ ЗАЩИЩЕН. Поэтому не пытайтесь поместить код и стек, а затем прыгать в него.
Ответ 3
Вы также можете отключить защиту от записи для всей программы, передав переключатель -N
в компоновщик. Если вы вызываете компоновщик из gcc, пройдите Wl,-N
. Если вы вызываете ld
напрямую, пройдите -N
.