C语言调用机器码令实现
-
编写被调用C代码
#include <stdio.h> int add(int a,int b){ return a+b; }
-
编译,注意添加*-c*参数,不然会因为没有main函数报错
gcc -c add.c
-
获得编译后文件的机器码
objdump -j .text -d add.o
执行命令结果如下:
add.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <add>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 89 7d fc mov %edi,-0x4(%rbp) 7: 89 75 f8 mov %esi,-0x8(%rbp) a: 8b 55 fc mov -0x4(%rbp),%edx d: 8b 45 f8 mov -0x8(%rbp),%eax 10: 01 d0 add %edx,%eax 12: 5d pop %rbp 13: c3 retq
其中左侧从0x55到0xc3这些16进制的数即是add方法对应的机器码
-
编写C程序调用得到的机器码
#include <stdio.h> #include <stdlib.h> #include <sys/mman.h> // 定义数组存储机器码,也可以使用int类型 // \x表示后面的字符是十六进制数 const unsigned char code[]="\x55\x48\x89\xe5\x89\x7d\xfc\x89\x75\xf8\x8b\x55\xfc\x8b\x45\xf8\x01\xd0\x5d\xc3"; int main(int argc,char* argv[]){ /** 这里和《揭秘Java虚拟虚拟机》一书中所写存在差异,按照书中的写法调用C语言会报段错误 经过一番查找原因是机器码必须存在可执行内存页 */ void *buf; // mman.h头文件为Linux下内存管理函数库 // mmap用来开辟新的内存空间,第二个参数分别表示可读|可写|可执行 buf = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0); // 内存拷贝,将code数组中内容拷贝到前面开辟的存存储空间中 memcpy (buf, code, sizeof(code)); // 标记内存区域为已读(具体用处不明,不敢乱说,貌似是个gcc内置函数) __builtin___clear_cache(buf, buf+sizeof(code)-1); // 定义函数指针 int (*fun)(int,int); // 指向机器码内存空间 fun=(void*)buf; // 调用方式 int result=fun(8,1); printf("invoked result:%d\n",result); }
上述调用过程基于Linux 5.10.6-arch1-1,不同的平台必定不兼容,不同的系统版本可能不兼容。