从C执行二进制机器代码

按照这个说明,我设法只产生528字节的大小a.out(当gcc main.c最初给我8539字节的大文件时)。

main.c是:

int main(int argc, char** argv) { return 42; } 

但我已经从这个汇编文件中构建了一个a.out:

电源:

 ; tiny.asm BITS 64 GLOBAL _start SECTION .text _start: mov eax, 1 mov ebx, 42 int 0x80 

有:

 me@comp# nasm -f elf64 tiny.s me@comp# gcc -Wall -s -nostartfiles -nostdlib tiny.o me@comp# ./a.out ; echo $? 42 me@comp# wc -c a.out 528 a.out 

因为我需要机器码:

 objdump -d a.out a.out: file format elf64-x86-64 Disassembly of section .text: 00000000004000e0 : 4000e0: b8 01 00 00 00 mov $0x1,%eax 4000e5: bb 2a 00 00 00 mov $0x2a,%ebx 4000ea: cd 80 int $0x80 ># objdump -hrt a.out a.out: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .note.gnu.build-id 00000024 00000000004000b0 00000000004000b0 000000b0 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .text 0000000c 00000000004000e0 00000000004000e0 000000e0 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE SYMBOL TABLE: no symbols 

文件是小端约定:

 me@comp# readelf -a a.out ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x4000e0 Start of program headers: 64 (bytes into file) Start of section headers: 272 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 2 Size of section headers: 64 (bytes) Number of section headers: 4 Section header string table index: 3 

现在我想这样执行:

 #include  // which version is (more) correct? // this might be related to endiannes (???) char code[] = "\x01\xb8\x00\x00\xbb\x00\x00\x2a\x00\x00\x80\xcd\x00"; char code_v1[] = "\xb8\x01\x00\x00\x00\xbb\x2a\x00\x00\x00\xcd\x80\x00"; int main(int argc, char **argv) { /*creating a function pointer*/ int (*func)(); func = (int (*)()) code; (int)(*func)(); return 0; } 

但是我得到了分段错误。 我的问题是:是这部分文字

  4000e0: b8 01 00 00 00 mov $0x1,%eax 4000e5: bb 2a 00 00 00 mov $0x2a,%ebx 4000ea: cd 80 int $0x80 

(这个机器代码)我真的需要什么? 我做错了什么(endiannes ??),也许我只需要从SIGSEGV以不同的方式调用它?

我完成了这件事。 代码必须标记为可执行代码。 一种方法是将此二进制机器代码复制到可执行缓冲区中。

 #include  #include  #include  char code[] = {0x55,0x48,0x89,0xe5,0x89,0x7d,0xfc,0x48, 0x89,0x75,0xf0,0xb8,0x2a,0x00,0x00,0x00,0xc9,0xc3,0x00}; /* * 00000000004004b4 
55 push %rbp 00000000004004b5 48 89 e5 mov %rsp,%rbp 00000000004004b8 89 7d fc mov %edi,-0x4(%rbp) 00000000004004bb 48 89 75 f0 mov %rsi,-0x10(%rbp) /NetBeansProjects/examples/tiny_c/tiny.c:15 return 42; 00000000004004bf b8 2a 00 00 00 mov $0x2a,%eax /NetBeansProjects/examples/tiny_c/tiny.c:16 } 00000000004004c4 c9 leaveq 00000000004004c5 c3 retq */ int main(int argc, char **argv) { void *buf; /* copy code to executable buffer */ buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0); memcpy (buf, code, sizeof(code)); /* run code */ int i = ((int (*) (void))buf)(); printf("get this done. returned: %d", i); return 0; }

输出:

完成这件事。 返回:42

RUN SUCCESSFUL(总时间:57ms)