2017-06-14 172 views
0

我们试图将二进制/ elf文件复制到我们系统的共享内存区域,然后执行它。我们不想直接调用我们的“客户端”程序,因为我们需要从内存本身执行它来达到我们的目的。直接从(共享)内存中执行二进制/ elf文件C

虽然我们知道,我们的做法(以下描述)将没有真正的工作,我们是(显然)设法得到它的工作。如何复制二进制/精灵/等等。直接将文件存入(共享)内存并在其后执行?也许我们是以错误的方式编译它的?还是别的什么做错了?

我们也不想将它转换成十六进制/ shell代码,我们已经做到了。我们正在寻找更简单,更实用的解决方案。

有人能帮忙吗?将不胜感激!

两个方案:

  1. “主机” - 程序(复制&共享内存执行客户端程序)
  2. “客户” - 程序(基本上是一个hello世界回声

“客户端” - 程序:

#include <stdio.h> 
int main() 
{ 
    printf("Hello, World!\n"); 
    return 0; 
} 

编译gcc -o binfile clientprogram.c -static

“主机” - 程序:

#include <string.h> 
#include <sys/mman.h> 
#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char **argv) 
{ 
    FILE *fp; //filepointer 
    size_t size; //filesize 
    unsigned char *buffer; //buffer 

    fp = fopen("binfile","rb"); 
    fseek(fp, 0, SEEK_END); 
    size = ftell(fp); 
    fseek(fp, 0, SEEK_SET); 
    buffer = (unsigned char *) malloc(size); 
    if (fp == NULL){ //file empty? 
     printf("Error: There was an Error reading the file %s \n", "binfile");   
     exit(1); 
    } 
    else if (fread(buffer, sizeof *buffer, size, fp) != size){ 
     printf("Error: There was an Error reading the file %s\n", "binfile"); 
     exit(1); 
    }else{ 
    int i; 
    // for(i=0; i<size;i++){  
    // printf("%02x", buffer[i]); 
    // } 
    } 


    void *mem = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); 
    memcpy(mem, buffer, size); 
    mprotect(mem, size, PROT_READ|PROT_WRITE|PROT_EXEC); 

    void (*func)(); 
    func = (void (*)()) buffer; 
    func(); 

    munmap(mem, size); 

    fclose(fp); 
    free(buffer); 
    return 0; 
} 

编译时gcc hostprogram.c

+1

这种方法很幼稚,当然会失败。加载和执行二进制文件比将其读入内存并调用加载地址要复杂得多。这可能是[XY问题](http://xyproblem.info/)。你想达到什么样的目标? –

+0

虽然main函数是程序的正式入口点,但在main函数被调用之前,有很多代码正在执行。例如,全局变量的初始化是在'main'函数之前处理的,但是其他的东西比如设置标准文件流'stdout','stdin'和'stderr'。这样做只应该发生一次。更不用说[ELF文件](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format)不能以可调用的* code *开头。 –

+2

至于你的问题,*为什么*你想这样做?它应该解决什么问题? –

回答

3

将客户端构建为PIE,其中-rdynamic。然后,你就可以dlopen()dlsym()main符号(dlopen()会做mmap ING和荷兰国际集团mprotect你,你就可以看到,如果你strace程序),之后你就可以在主机的地址空间内运行main

例子:

#!/bin/sh 
cat > client.c <<EOF 
#include <stdio.h> 
#include <unistd.h> 
int main() 
{ 
    printf("Hello, World!: from %ld\n", (long)getpid()); 
    return 0; 
} 
EOF 

gcc -fpic -c client.c 
gcc -pie -rdynamic -o client client.o 

cat > host.c <<EOF 
#include <dlfcn.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
int main() 
{ 
    printf("Hello, I'm your host: %ld\n", (long)getpid()); ; 

    void *client_hndl; 
    typedef int main_t(int, char**); 
    main_t *client_main; 
    client_hndl = dlopen("./client", RTLD_LAZY); 
    if (!client_hndl){ 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(1); 
    } 
    client_main = (main_t*)dlsym(client_hndl, "main"); 
    if (!client_main){ 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(2); 
    } 
    return client_main(1, (char*[]){"client", 0}); 
} 
EOF 
gcc host.c -ldl 
./client 
echo ============= 
./a.out 

输出示例:

Hello, World!: from 14520 
============= 
Hello, I'm your host: 14521 
Hello, World!: from 14521 
+0

谢谢@PSkocik!这种有用!但是我们需要使用共享内存区域。所以'client_hndl指针'正好指向程序?我们可以将其复制到我们创建的共享内存区域吗? – Marvin

0

您正在寻找解决这一GLIBC feature request

此功能的要求是7岁,和它有点很少会与它的任何时间很快发生。

您最好的选择可能是做粗略你已经在做(建立一个完全静态二进制)什么。

你的方法是行不通的,因为你建的可执行文件要求它在(可见readelf -l binfile链接为第一PT_LOAD段的地址的地址被加载。您将需要mmapbinfile那里MAP_FIXED,没有其他的地址就可以了。

您还需要读取和解码是在文件的开头中找到切入点跳转到你目前是跳跃到ELF头本身的Elf{32,64}_Ehdr,但是这个头并不是执行应该开始的地方,