【问题标题】:load ELF file into memory将 ELF 文件加载到内存中
【发布时间】:2017-01-22 00:01:59
【问题描述】:

我正在尝试将一个elf文件放入内存然后执行它,这些是步骤:

1-要放入内存的文件

int main()
{
   printf("Hello world! \n");
   return 0;
}

2- 编译gcc -o hello hello.c -static

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x8120
  Start of program headers:          52 (bytes into file)
  Start of section headers:          119864 (bytes into file)
  Flags:                             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         4
  Size of section headers:           40 (bytes)
  Number of section headers:         18
  Section header string table index: 17

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00008000 0x00008000 0x16828 0x16828 R E 0x1000
  LOAD           0x016840 0x0001f840 0x0001f840 0x00250 0x02660 RW  0x1000
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0
  EXIDX          0x015f40 0x0001df40 0x0001df40 0x008e8 0x008e8 R   0x4

3- 我写了一个加载器(为 ARM 编译)

mmap2(0x8000, 92200, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x8000
mmap2(0x1f000, 65536, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x1f000

3.1- 然后我将所有的精灵字节复制到分配中

3.2- jmp 到主函数

ldr r0, =0x008160
blx r0

.text:00008160 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00008160                 EXPORT main
.text:00008160 main                                    ; DATA XREF: _start+8o
.text:00008160                                         ; .text:off_8150o
.text:00008160                 STMFD           SP!, {R11,LR}
.text:00008164                 ADD             R11, SP, #4
.text:00008168                 LDR             R3, =(aHelloWorld - 0x8174)
.text:0000816C                 ADD             R3, PC, R3 ; "Hello world! "
.text:00008170                 MOV             R0, R3
.text:00008174                 BLX             puts
.text:00008178                 MOV             R3, #0
.text:0000817C                 MOV             R0, R3
.text:00008180                 LDMFD           SP!, {R11,PC}
.text:00008180 ; End of function main

问题是每次我到达 0x8174 行,然后跳转到,经过一些指令后,我总是有一个随机位置的 SIGSEGV,更多的时间崩溃指令是 => 0x9cc0: ldr r0, [r0, #4]r0=0x70a34

00008000-0002f000 rwxp 00000000 00:00 0 
80000000-80001000 r-xp 00000000 b3:18 129754     /data/local/tmp/main
80001000-8001a000 rwxp 00001000 b3:18 129754     /data/local/tmp/main
becdf000-bed00000 rwxp 00000000 00:00 0 
ffff0000-ffff1000 r-xp 00000000 00:00 0          [vectors]

这些是崩溃后的更多说明:

.text:00009CB4 loc_9CB4                                ; CODE XREF: pthread_mutex_lock_impl+18j
.text:00009CB4                 MOV             R3, #0xFFFF0FE0
.text:00009CBC                 BLX             R3
.text:00009CC0                 LDR             R0, [R0,#4]

0x9CB4这个位置,r0的值为0x1f96c(没关系),blx后r0的值为0x70a34

(gdb) x/10x 0xffff0fe0
0xffff0fe0: 0xee1d0f70  0xe12fff1e  0xee1d0f70  0x00000000
0xffff0ff0: 0x00000000  0x00000000  0x00000000  0x00000005
0xffff1000: Cannot access memory at address 0xffff1000

谢谢!

【问题讨论】:

  • 您至少应该解决所有动态依赖关系,然后才能将控制权传递给加载的代码。 main() 也不是您 ELF 的真正入口点。有所谓的启动代码——它的目的是初始化 C 运行时。实际入口点地址放置在 ELF 标头的某处。
  • 在将可执行文件映射到内存并跳转到内存中执行之前,操作系统的加载器会做很多事情。
  • 嗯,是的,puts 的电话到底要去哪里?您是否已加载并初始化 C 库?修复了所有的搬迁?加载除了一个简单的、静态链接的、与位置无关的二进制文件以外的任何东西都将需要两个以上的mmaps 和一个memcpy
  • @Serhio 是的,对不起,我错过了这一点,如果我设置入口点 ldr r0, =0x008120,这是我的输出:mmap2(0x8000, 92200, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED| MAP_ANONYMOUS, -1, 0) = 0x8000 mmap2(0x1f000, 65536, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x1f000 gettid() = 3582 set_tls(0x201a00, 0x40,xa 02 02 ) = 0 futex(0x202ec, FUTEX_WAIT_PRIVATE, 2, NULL 并且永远等待
  • 所以?如果你将几个 ELF 部分转储到内存中,忽略入口点,然后盲目地直接跳到其中一个部分的中间,那么你几乎肯定会在某个地方崩溃。 那次崩溃的本质一点也不有趣。如果你的车着火了,你会打电话给汽车电工诊断为什么你的车不能启动吗?

标签: c assembly static arm elf


【解决方案1】:

我正在尝试将一个elf文件放入内存然后执行它,

对于完全静态链接的可执行文件,您的步骤将起作用(除非您需要跳转到 _start == 入口点 0x8120不是 main)。

然后我将所有的精灵字节复制到分配中

另一个可能的问题是没有注意.p_offset。您的 memcpyies 应该如下所示:

unsigned char buf1[0x16828];  // read 0x16828 bytes from start of file
memcpy(0x8000, buf1, 0x16828);

unsigned char buf2[0x250];  // read 0x250 bytes from offset 0x016840 into the file
memcpy(0x0001f840, buf2, 0x250);

【讨论】:

  • 它看起来不像动态链接的可执行文件,因为没有 INTERP 程序头。如果它是动态链接的,它将包含动态加载器的名称,而他所要做的就是加载并正确执行它。他的问题是他没有使用正确的入口点。
  • @RossRidge 你是对的。他在链接线上有-static。对不起,我错过了。
  • 是此答案的先前版本:在动态二进制文件上,您可以像内核一样将 ELF 解释器加载到内存中,然后跳转到它的入口点,对吗?如果您只是想避免调用 execve(),那不应该阻止您仍然使用现有的动态链接器。
  • @EmployedRussian 感谢您的回复,问题是我在加载程序中忽略了偏移量 0x016840。然后,如果我跳到主 0x008160,而不是入口点,它就可以工作!
【解决方案2】:

您的问题是您需要使用正确的入口点,并且您需要以与操作系统相同的方式初始化程序的堆栈(可能还有寄存器)。您需要使用正确的入口点来初始化 C 运行时库,否则您对 printf(或 puts 视情况而定)的调用几乎肯定会崩溃。您需要正确设置堆栈,因为 C 运行时库的初始化代码将在此处查找程序的参数和环境(可能还有其他内容)。

您没有说您使用的是什么操作系统,但如果您使用的是 Linux,您可能希望查看 CesarB describing the initial state of the stack on ARM Linux 给出的不同问题的答案。

【讨论】:

    猜你喜欢
    • 2012-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-31
    • 2013-11-09
    • 2015-12-24
    • 2011-09-07
    • 1970-01-01
    相关资源
    最近更新 更多