Самоизменяющийся код всегда вызывает ошибки сегментации в Linux - PullRequest
10 голосов
/ 13 ноября 2010

Я нашел статью о самом изменяющемся коде и попытался привести несколько примеров, но я всегда получаю ошибки сегментации. Насколько я могу понять, есть нарушение прав доступа к памяти. Сегмент кода является (r) ead / e (x) точным, и поэтому попытка записи приводит к этой ошибке. Есть ли способ протестировать программу, изменив разрешения памяти во время выполнения или раньше? Я использую 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, вот рабочий код (на самом деле, если вы собираете и связываете и запускаете его, происходит сбой, но если вы смотрите с помощью 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:

Ответы [ 3 ]

14 голосов
/ 13 ноября 2010

Вы должны изменить права доступа к памяти во время выполнения.

#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 */
}
5 голосов
/ 13 ноября 2010

Современные процессоры имеют функцию DEP, которая предотвращает выполнение кода в стеке. Раньше это было возможно; сейчас это не так. Двоичный файл по умолчанию загружается в постоянную память.

При этом вы можете использовать системный вызов mprotect, чтобы пометить расположение вашего двоичного файла в памяти как исполняемый файл - ТАК, ЧТО ВАШ КОД НЕ ЗАЩИЩЕН. Поэтому не пытайтесь поместить код и стек, а затем перейти к нему.

2 голосов
/ 09 июля 2015

Вы также можете отключить защиту от записи для всей программы, передав переключатель -N компоновщику. Если вы вызываете компоновщик из gcc, передайте Wl,-N. Если вы вызываете ld напрямую, передайте -N.

...