13

我找到了一篇关于自我修改代码的文章并尝试做一些示例,但我总是遇到分段错误。据我所知,内存权限存在违规行为。代码段是 (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建议的修改之后,这里是一个工作代码。(实际上,如果你组装&链接&运行它会崩溃,但如果你使用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:
4

3 回答 3

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 */
}
于 2010-11-12T21:59:18.787 回答
5

现代 CPU 有一个称为 DEP 的功能,它可以防止在堆栈上执行代码。以前,这是可能的;现在,它不是。默认情况下,二进制文件被加载到只读内存中。

有了这个,您可以使用 mprotect 系统调用将二进制文件在内存中的位置标记为可执行文件 - 只要您的代码不受 DEP 保护。所以不要试图把代码和堆栈放在一起然后跳进去。

于 2010-11-12T21:58:54.340 回答
3

-N您还可以通过将开关传递给链接器来禁用整个程序的写保护。如果您从 gcc 调用链接器,请传递Wl,-N. 如果ld直接调用,则传递-N.

于 2015-07-08T23:33:55.300 回答