HTML5技术

如何实现在Windows上运行Linux程序,附示例代码 - q303248153(4)

字号+ 作者:H5之家 来源:H5之家 2017-05-16 14:00 我要评论( )

需要注意的是0x601018一开始并不会指向实际的printf函数,而是会指向0x400406, 为什么会这样? 因为Linux的可执行程序为了考虑性能,不会在一开始就解决所有动态连接的函数,而是选择了延迟解决. 在上面第一次jmpq *

需要注意的是0x601018一开始并不会指向实际的printf函数,而是会指向0x400406, 为什么会这样? 因为Linux的可执行程序为了考虑性能,不会在一开始就解决所有动态连接的函数,而是选择了延迟解决.
在上面第一次jmpq *0x200c12(%rip)会跳转到下一条指令0x400406, 又会继续跳转到0x4003f0, 再跳转到0x601010指向的地址, 0x601010指向的地址就是延迟解决的实现, 第一次延迟解决成功后, 0x601018就会指向实际的printf, 以后调用就会直接跳转到实际的printf上.

程序入口点

Linux程序运行首先会从_start函数开始, 上面readelf中的入口点地址0x400430就是_start函数的地址,

0000000000400430 <_start>: d1 e2 e4 f0 ,%%%c7 c0 ,%c7 c1 b0 ,%c7 c7 3c ,%rdi 400454: e8 b7 ff ff ff callq 400410 <__libc_start_main@plt> 0f 1f nopw 0x0(%rax,%rax,1)

接下来_start函数会调用__libc_start_main函数, __libc_start_main是libc库中定义的初始化函数, 负责初始化全局变量和调用main函数等工作.

__libc_start_main函数还负责设置返回值和退出进程, 可以看到上面调用__libc_start_main后的指令是hlt, 这个指令永远不会被执行.

实现Linux程序运行器

在拥有以上的知识后我们可以先构想以下的运行器需要做什么.

因为x64的Windows和Linux程序使用的cpu指令集都是一样的,我们可以直接执行汇编而不需要一个指令模拟器,
而且这次我打算在用户层实现, 所以不能像Bash On Windows一样模拟syscall, 这个运行器会像下图一样模拟libc库的函数

这样运行器需要做的事情有:

这些工作会在以下的示例程序中一一实现, 完整的源代码可以看文章顶部的链接

首先我们需要把ELF文件格式对应的代码从binutils中复制过来, 它包含了ELF头, 程序头和相关的数据结构, 里面用unsigned char[]是为了防止alignment, 这样结构体可以直接从文件内容中转换过来

ELFDefine.h:

#pragma once namespace HelloElfLoader { EI_MAG0 = 0; const int EI_MAG1 = 1; const int EI_MAG2 = 2; const int EI_MAG3 = 3; const int EI_CLASS = 4; const int EI_DATA = 5; const int EI_VERSION = 6; const int EI_OSABI = 7; const int EI_ABIVERSION = 8; const int EI_PAD = 9; const int EI_NIDENT = 16; // ELF文件类型 enum { ELFCLASSNONE = 0, ELFCLASS32 = 1, ELFCLASS64 = 2 }; // ByteOrder enum { ELFDATANONE = 0, ELFDATA2LSB = 1, ELFDATA2MSB = 2 }; // 程序头类型 enum PT { PT_NULL = 0, PT_LOAD = 1, PT_DYNAMIC = 2, PT_INTERP = 3, PT_NOTE = 4, PT_SHLIB = 5, PT_PHDR = 6, PT_TLS = 7, PT_LOOS = 0x60000000, PT_HIOS = 0x6fffffff, PT_LOPROC = 0x70000000, PT_HIPROC = 0x7fffffff, // The remaining values are not in the standard. // Frame unwind information. PT_GNU_EH_FRAME = 0x6474e550, PT_SUNW_EH_FRAME = 0x6474e550, // Stack flags. PT_GNU_STACK = 0x6474e551, // Read only after relocation. PT_GNU_RELRO = 0x6474e552, // Platform architecture compatibility information PT_ARM_ARCHEXT = 0x70000000, // Exception unwind tables PT_ARM_EXIDX = 0x70000001 }; // 动态节类型 enum DT { DT_NULL = 0, DT_NEEDED = 1, DT_PLTRELSZ = 2, DT_PLTGOT = 3, DT_HASH = 4, DT_STRTAB = 5, DT_SYMTAB = 6, DT_RELA = 7, DT_RELASZ = 8, DT_RELAENT = 9, DT_STRSZ = 10, DT_SYMENT = 11, DT_INIT = 12, DT_FINI = 13, DT_SONAME = 14, DT_RPATH = 15, DT_SYMBOLIC = 16, DT_REL = 17, DT_RELSZ = 18, DT_RELENT = 19, DT_PLTREL = 20, DT_DEBUG = 21, DT_TEXTREL = 22, DT_JMPREL = 23, DT_BIND_NOW = 24, DT_INIT_ARRAY = 25, DT_FINI_ARRAY = 26, DT_INIT_ARRAYSZ = 27, DT_FINI_ARRAYSZ = 28, DT_RUNPATH = 29, DT_FLAGS = 30, // This is used to mark a range of dynamic tags. It is not really // a tag value. DT_ENCODING = 32, DT_PREINIT_ARRAY = 32, DT_PREINIT_ARRAYSZ = 33, DT_LOOS = 0x6000000d, DT_HIOS = 0x6ffff000, DT_LOPROC = 0x70000000, DT_HIPROC = 0x7fffffff, // The remaining values are extensions used by GNU or Solaris. DT_VALRNGLO = 0x6ffffd00, DT_GNU_PRELINKED = 0x6ffffdf5, DT_GNU_CONFLICTSZ = 0x6ffffdf6, DT_GNU_LIBLISTSZ = 0x6ffffdf7, DT_CHECKSUM = 0x6ffffdf8, DT_PLTPADSZ = 0x6ffffdf9, DT_MOVEENT = 0x6ffffdfa, DT_MOVESZ = 0x6ffffdfb, DT_FEATURE = 0x6ffffdfc, DT_POSFLAG_1 = 0x6ffffdfd, DT_SYMINSZ = 0x6ffffdfe, DT_SYMINENT = 0x6ffffdff, DT_VALRNGHI = 0x6ffffdff, DT_ADDRRNGLO = 0x6ffffe00, DT_GNU_HASH = 0x6ffffef5, DT_TLSDESC_PLT = 0x6ffffef6, DT_TLSDESC_GOT = 0x6ffffef7, DT_GNU_CONFLICT = 0x6ffffef8, DT_GNU_LIBLIST = 0x6ffffef9, DT_CONFIG = 0x6ffffefa, DT_DEPAUDIT = 0x6ffffefb, DT_AUDIT = 0x6ffffefc, DT_PLTPAD = 0x6ffffefd, DT_MOVETAB = 0x6ffffefe, DT_SYMINFO = 0x6ffffeff, DT_ADDRRNGHI = 0x6ffffeff, DT_RELACOUNT = 0x6ffffff9, DT_RELCOUNT = 0x6ffffffa, DT_FLAGS_1 = 0x6ffffffb, DT_VERDEF = 0x6ffffffc, DT_VERDEFNUM = 0x6ffffffd, DT_VERNEED = 0x6ffffffe, DT_VERNEEDNUM = 0x6fffffff, DT_VERSYM = 0x6ffffff0, // Specify the value of _GLOBAL_OFFSET_TABLE_. DT_PPC_GOT = 0x70000000, // Specify the start of the .glink section. DT_PPC64_GLINK = 0x70000000, // Specify the start and size of the .opd section. DT_PPC64_OPD = 0x70000001, DT_PPC64_OPDSZ = 0x70000002, DT_SPARC_REGISTER = 0x70000001, DT_AUXILIARY = 0x7ffffffd, DT_USED = 0x7ffffffe, DT_FILTER = 0x7fffffff };; { e_type[e_machine[e_version[e_entry[e_phoff[e_shoff[e_flags[e_ehsize[e_phentsize[e_phnum[e_shentsize[e_shnum[e_shstrndx[2]; /* Section header string table index */ } Elf64_External_Ehdr; { p_flags[p_offset[p_vaddr[p_paddr[p_filesz[p_memsz[p_align[8]; /* Segment alignment, file & memory */ } Elf64_External_Phdr; { { unsigned char d_val[8]; unsigned char d_ptr[8]; } d_un; } Elf64_External_Dyn; { r_info[r_addend[8]; /* Constant addend used to compute value */ } Elf64_External_Rela; { st_info[st_other[st_shndx[st_value[st_size[8]; /* Associated symbol size */ } Elf64_External_Sym; }

接下来我们定义一个读取和执行ELF文件的类, 这个类会在初始化时把文件加载到fileStream_, execute函数会负责执行

HelloElfLoader.h:

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 通用网页调用本地应用程序方案(windows平台) - 小龙女先生

    通用网页调用本地应用程序方案(windows平台) - 小龙女先生

    2017-05-16 13:00

  • H5到底如何做视频直播 - 郭东生blog

    H5到底如何做视频直播 - 郭东生blog

    2017-05-13 11:01

  • cordova 基本命令 以及如何添加,删除插件 - huangenai

    cordova 基本命令 以及如何添加,删除插件 - huangenai

    2017-05-13 09:00

  • 如何快速处理线上故障 - 倒骑的驴

    如何快速处理线上故障 - 倒骑的驴

    2017-05-02 12:01

网友点评
e