【问题标题】:How to write to shared library code segment loaded into RAM memory?如何写入加载到 RAM 内存中的共享库代码段?
【发布时间】:2017-05-25 08:45:43
【问题描述】:

我想问一下为什么我不能在 MIPS CPU 平台(LG TV)上的 Linux 2.6.28.9 的 RAM 内存中写入已加载的共享库代码段。我能够读取字节但不能写任何东西。在下面的示例源代码中(在 gcc 中交叉编译),当调用 write() 函数时,我得到 ERROR 22: Invalid argument (EINVAL)

// this app tries to replace 4 bytes in code segment memory of loaded shared library

#include <stdio.h>  // printf
#include <stdlib.h> // off_t
#include <dlfcn.h>  // dlopen, dlclose
#include <fcntl.h>  // open, O_RDWR
#include <unistd.h> // lseek, close, read
#include <errno.h>  // errno
#include <string.h> // strerror
#include <sys/mman.h>   // mprotect, PROT_READ, PROT_WRITE, PROT_EXEC

#define BYTES_TO_REPLACE    4

int main (int argc, char *argv[])
{
   int fd, pid;
   unsigned *handle;
   unsigned long pagesize;
   off_t fun_addr, pa_fun_addr;
   unsigned char buf[BYTES_TO_REPLACE];
   char s[100];

   // initialize
   pagesize = sysconf(_SC_PAGESIZE);    // memory page size from system
   pid = getpid();          // PID of current process

   // open shared library file, OK
   handle = dlopen("/path_to_library_files/shared_library.so", RTLD_LAZY | RTLD_GLOBAL);

   // get function address, OK
   fun_addr = (off_t)dlsym(handle, "function_name_in_lib");

   // open memory device (pseudo-file), OK
   sprintf(s, "/proc/%d/mem", pid); // memory space of our process (/proc/self/mem)
   //strcpy(s, "/dev/mem");     // in that case when reading from memory ==> ERROR 14: Bad address
   fd = open(s, O_RDWR);        // open for reading and writing

   // go to starting address of the library function loaded earlier, OK
   lseek(fd, fun_addr, SEEK_SET);

   // read from memory, OK
   read(fd, buf, BYTES_TO_REPLACE);
   printf("old replaced bytes = [%02X %02X %02X %02X]\n", buf[0], buf[1], buf[2], buf[3]);

   // move back, OK
   lseek(fd, fun_addr, SEEK_SET);

   // unprotect memory page - no error, but does not help
   pa_fun_addr = (fun_addr / pagesize) * pagesize;  // page-aligned address
   mprotect((void *)pa_fun_addr, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC);

   // write new data to memory: ERROR 22: Invalid argument
   buf[0] = 0x08; buf[1] = 0x00; buf[2] = 0xE0; buf[3] = 0x03;  // replacing 4-byte command: jr $ra (MIPS CPU)
   if (write(fd, buf, BYTES_TO_REPLACE) != BYTES_TO_REPLACE) printf("ERROR %d: %s!\n", errno, strerror(errno));

   // close memory device and shared library
   close(fd);
   dlclose(handle);
   return 0;
}

【问题讨论】:

    标签: memory c mmap


    【解决方案1】:

    这是因为内存中的进程代码默认没有写权限。要查看进程内存的权限,请使用pmap

    例如下面的共享库最多只有rx权限:

    sudo pmap 5869                                                                                                                    
    5869:   vim supervisor_meeting-2017-05-22.txt
    000055b391f62000   2604K r-x-- vim
    000055b3923ed000     56K r---- vim
    000055b3923fb000    100K rw--- vim
    000055b392414000     60K rw---   [ anon ]
    000055b393377000   2868K rw---   [ anon ]
    00007fc59ef5a000     40K r-x-- libnss_files-2.24.so
    00007fc59ef64000   2048K ----- libnss_files-2.24.so
    00007fc59f164000      4K r---- libnss_files-2.24.so
    00007fc59f165000      4K rw--- libnss_files-2.24.so
    00007fc59f166000     24K rw---   [ anon ]
    
    <..snip..>
    

    我了解到您正在尝试使用 mprotect 更改此设置 - 但您也没有检查 mprotect() 调用的返回值 - 这可能由于某种原因而失败。

    另外,顺便说一句 - write() 不能保证写入给它的所有字节,并且完全符合它的设计返回不写入或不写入部分字节数 - 我建议你也更改代码来反映这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-28
      • 2010-11-28
      • 2014-04-11
      相关资源
      最近更新 更多