【问题标题】:C: Run machine code from memoryC:从内存中运行机器代码
【发布时间】:2014-09-10 17:05:29
【问题描述】:

我想从内存中执行一些代码;我的长期目标是创建一个自解密应用程序。为了理解这件事,我从根源开始。 我创建了以下代码:

#define UNENCRYPTED true

#define sizeof_function(x) ( (unsigned long) (&(endof_##x)) - (unsigned long) (&x))
#define endof_function(x) void volatile endof_##x() {}
#define DECLARE_END_OF_FUNCTION(x) void endof_##x();

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>

unsigned char *bin;

#ifdef UNENCRYPTED
void hexdump(char *description, unsigned char *toDump, unsigned long length) {
    printf("Hex-dump of \"%s\":\n", description);
    for (int i = 0; i < length; i++) {
        printf("%02x", toDump[i]);
    }
    printf("\n");
}


void hello_world() {
    printf("Hello World!\n");
}
endof_function(hello_world);
#endif

int main (void) {
    errno = 0;
    unsigned long hello_worldSize = sizeof_function(hello_world);
    bin = malloc(hello_worldSize);

    //Compute the start of the page
    size_t pagesize = sysconf(_SC_PAGESIZE);
    uintptr_t start = (uintptr_t) bin;
    uintptr_t end = start + (hello_worldSize);
    uintptr_t pagestart = start & -pagesize;
    bin = (void *)pagestart;

    //Set mprotect for bin to write-only
    if(mprotect(bin, end - pagestart, PROT_WRITE) == -1) {
        printf("\"mprotect\" failed; error: %s\n", strerror(errno));
        return(1);
    }

    //Get size and adresses
    unsigned long hello_worldAdress = (uintptr_t)&hello_world;
    unsigned long binAdress = (uintptr_t)bin;

    printf("Address of hello_world %lu\nSize of hello_world %lu\nAdress of bin:%lu\n", hello_worldAdress, hello_worldSize, binAdress);

    //Check if hello_worldAdress really points to hello_world()
    void (*checkAdress)(void) = (void *)hello_worldAdress;
    checkAdress();

    //Print memory contents of hello_world()
    hexdump("hello_world", (void *)&hello_world, hello_worldSize);

    //Copy hello_world() to bin
    memcpy(bin, (void *)hello_worldAdress, hello_worldSize);

    //Set mprotect for bin to read-execute
    if(mprotect(bin, end - pagestart, PROT_READ|PROT_EXEC) == -1) {
        printf("\"mprotect\" failed; error: %s\n", strerror(errno));
        return(1);
    }

    //Check if the contents at binAdress are the same as of hello_world
    hexdump("bin", (void *)binAdress, hello_worldSize);

    //Execute binAdress
    void (*executeBin)(void) = (void *)binAdress;
    executeBin();

    return(0);
}

但是我得到一个段错误;程序输出如下:

(在 OS X 上;i86-64):

hello_world 4294970639的地址

Size of hello_world 17
Adress of bin:4296028160
Hello World!
Hex-dump of "hello_world":
554889e5488d3d670200005de95a010000
Hex-dump of "bin":
554889e5488d3d670200005de95a010000
Program ended with exit code: 9

在我的 Raspi(带有 32 位 ARM 的 Linux)上:

Adress of hello_world 67688
Size of hello_world 36
Hello World!
Hello World!
Hex-dump of "hello_world":
00482de90db0a0e108d04de20c009fe512ffffeb04008de50bd0a0e10088bde8d20b0100
Hex-dump of "bin":
00482de90db0a0e108d04de20c009fe512ffffeb04008de50bd0a0e10088bde8d20b0100
Speicherzugriffsfehler //This is german for memory access error

我的错误在哪里?


问题是,hello_world 中的 printf 调用是基于相对跳转地址的,这在复制的函数中当然不起作用。 出于测试目的,我将 hello_world 更改为:

int hello_world() {
    //_printf("Hello World!\n");
    return 14;
}

和“//执行binAdress”下的代码:

int (*executeBin)(void) = (void *)binAdress;
int test = executeBin();
printf("Value: %i\n", test);

打印出 14 :D

【问题讨论】:

  • 你的 x64 编译的 sn-p 以 e95a010000 结尾,这是一个跳转到另一个地址。这个跳转的目的地是相对于跳转指令本身的地址计算出来的——当你将汇编代码复制到内存中的另一个位置时,跳转的目的地就乱套了。
  • 好的,如果我做对了,我需要将 printf 替换为对 printf 的绝对引用吗?
  • 是的,这种方法应该有助于解决这个问题。
  • 是的,这就是问题所在:D 我将函数类型更改为 int,并将 printf 调用替换为简单的“return 14”。现在执行代码并返回 14。 @DCoder 如果您创建一个答案,我会接受它:)
  • 接受 nnoneo 的回答 :)

标签: c memory-management out-of-memory self-reference


【解决方案1】:

在 ARM 上,您必须使用 cacheflush 之类的函数刷新指令缓存,否则您的代码可能无法正常运行。这是自修改代码和 JIT 编译器所必需的,但 x86 通常不需要。

此外,如果您将一段代码从一个位置移动到另一个位置,则必须修复任何相对跳转。通常,对库函数的调用被实现为跳转到重定位部分,并且通常是相对的。

为避免必须修复跳转,您可以使用一些链接器技巧来编译代码以从不同的偏移量开始。然后,在解密时,您只需将解密后的代码加载到该偏移量。通常使用两阶段编译过程:编译您的真实代码,将生成的机器代码附加到您的解密存根中,然后编译整个程序。

【讨论】:

    猜你喜欢
    • 2011-01-02
    • 1970-01-01
    • 1970-01-01
    • 2013-08-30
    • 2013-05-13
    • 2017-06-27
    • 1970-01-01
    • 1970-01-01
    • 2013-08-22
    相关资源
    最近更新 更多