在数据区域执行代码需要做什么,(段保护)

use*_*779 2 c c++ assembly linux-kernel

我在linux平台上工作,我使用g ++和上面的程序将函数从代码区域复制到数据区域.如何更改数据段的保护以允许我执行复制的功能?

代码如下:

#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define Return asm volatile("pop %rbp; retq; retq; retq; retq; retq;")
int64_t funcEnd=0xc35dc3c3c3c3c35d;
constexpr int maxCode=0x800;
int8_t code[maxCode];

void testCode(void){
    int a=8,b=7;
    a+=b*a;
    Return;
}

typedef void (*action)(void);

int main(int argc, char **argv)
{
    action a=&testCode;
    testCode();

    int8_t *p0=(int8_t*)a,*p=p0,*p1=p0+maxCode;
    for(;p!=p1;p++)
        if ( (*(int64_t*)p)==funcEnd ) break;

    if(p!=p1){
            p+=sizeof(int64_t);
            printf("found\n");
        memcpy(&code,(void*)a,p-(int8_t*)a);
        ((action)&code)();
    }

  printf("returning 0\n");
    return 0;

}
Run Code Online (Sandbox Code Playgroud)

Jon*_*art 5

这取决于您是否尝试静态(在构建时)或动态(在运行时)执行此操作.

建造时间

你需要告诉GCC将你的blob放在一个可执行的部分.我们使用__attribute__((section)),并且这个技巧在我们创建它时指定该部分的属性.

运行

TL; DR:跳到我的答案的末尾,我在哪里使用mmap.

虽然其他人可能会质疑你为什么要在运行时允许这样的东西,但请记住,这正是带有JIT编译器的VM(例如Java VM,.NET CLR等)在发出本机时所做的事情.码.

您需要更改您尝试执行的内存的内存保护.我们这样做mprotect(addr, PROT_EXEC).请注意,addr必须与平台的页面大小保持一致.在x86上,页面大小为4K.我们aligned_alloc用来保证这种对齐.

示例(两者):

#define _ISOC11_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>   /* mprotect() */

__attribute__((section(".my_executable_blob,\"awx\",@progbits#")))
static uint8_t code[] = {
    0xB8,0x2A,0x00,0x00,0x00,   /* mov  eax,0x2a    */
    0xC3,                       /* ret              */
};

int main(void)
{
    int (*func)(void);

    /* Execute a static blob of data */
    func = (void*)code;
    printf("(static) code returned %d\n", func());

    /* Execute a dynamically-allocated blob of data */
    void *p = aligned_alloc(0x1000, sizeof(code));
    if (!p) {
        fprintf(stderr, "aligned_alloc() failed\n");
        return 2;
    }
    memcpy(p, code, sizeof(code));
    if (mprotect(p, sizeof(code), PROT_EXEC) < 0) {
        perror("mprotect");
        return 2;
    }
    func = p;
    printf("(dynamic) code returned %d\n", func());

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

$ ./a.out 
(static) code returned 42
(dynamic) code returned 42
Run Code Online (Sandbox Code Playgroud)

SELinux影响力

请注意,这会将您的可执行代码放在堆上,这可能有点危险.我的CentOS 7机器上的SELinux实际上拒绝了这个mprotect电话:

SELinux is preventing /home/jreinhart/so/a.out from using the execheap access on a process.

*****  Plugin allow_execheap (53.1 confidence) suggests   ********************

If you do not think /home/jreinhart/so/a.out should need to map heap memory that is both writable and executable.
Then you need to report a bug. This is a potentially dangerous access.
Run Code Online (Sandbox Code Playgroud)

所以我不得不暂时sudo setenforce 0让这个工作.

但是,我不确定为什么,因为查看/proc/[pid]/maps,页面明确标记为可执行文件,而不是SELinux指示的"可写和可执行".如果我移动memcpy mprotect,我的过程内存设计缺陷,因为我试图写不可写内存.所以看来SELinux在这里过于热心了.

mmap改用

它不是mprotect堆的区域(分配aligned_alloc),而是使用起来更直接mmap.这也避免了SELinux的任何问题,因为我们没有尝试在堆上执行.

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>   /* mmap() */

static uint8_t code[] = { 
    0xB8,0x2A,0x00,0x00,0x00,   /* mov  eax,0x2a    */
    0xC3,                       /* ret              */
};

int main(void)
{
    void *p = mmap(NULL, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC,
            MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 
    if (p==MAP_FAILED) {
        fprintf(stderr, "mmap() failed\n");
        return 2;
    }   
    memcpy(p, code, sizeof(code));

    int (*func)(void) = p;
    printf("(dynamic) code returned %d\n", func());

    pause();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

最终的解决方案

mmap解决方案是好的,但它并没有提供我们的任何安全; 我们的mmaped代码区域是可读,可写和可执行的.最好只允许内存在我们将代码放到适当的位置时可写,然后只使其可执行.以下代码就是这样做的:

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>   /* mmap(), mprotect() */

static uint8_t code[] = {
    0xB8,0x2A,0x00,0x00,0x00,   /* mov  eax,0x2a    */
    0xC3,                       /* ret              */
};

int main(void)
{
    const size_t len = sizeof(code);

    /* mmap a region for our code */
    void *p = mmap(NULL, len, PROT_READ|PROT_WRITE,  /* No PROT_EXEC */
            MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 
    if (p==MAP_FAILED) {
        fprintf(stderr, "mmap() failed\n");
        return 2;
    }   

    /* Copy it in (still not executable) */
    memcpy(p, code, len);

    /* Now make it execute-only */
    if (mprotect(p, len, PROT_EXEC) < 0) {
        fprintf(stderr, "mprotect failed to mark exec-only\n");
        return 2;
    } 

    /* Go! */
    int (*func)(void) = p;
    printf("(dynamic) code returned %d\n", func());

    pause();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)