应该怎样把值传给一段被编译后的机器码?

软件工程师、主攻高级编程语言虚拟机的设计与实现

56 👍 / 5 💬

问题描述

#include <stdio.h>
 
/* 函数声明 */
int max(int num1, int num2);
 
int main ()
{
   /* 局部变量定义 */
   int a = 100;
   int b = 200;
   int ret;
 
   /* 调用函数来获取最大值 */
   ret = max(a, b);
 
   printf( "Max value is : %d\n", ret );
 
   return 0;
}
 
/* 函数返回两个数中较大的那个数 */
int max(int num1, int num2) 
{
   /* 局部变量声明 */
   int result;
 
   if (num1 > num2)
      result = num1;
   else
      result = num2;
 
   return result; 
}

那在这我把max() 函数用编译成机器代码
然后用强制转换为指针函数
对代码段进行VirtualProtect操作

之后再来调用这段原max()函数
那么这段函数 是如何得到 传过来的 值的?
或者是怎么被调用的?

请问题主的意思是类似这样的场景么?

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

typedef int (*binary_func_t)(int, int);

int main() {
  char code[] = { 0x48, 0x89, 0xF8, 0x48, 0x39, 0xF7, 0x48, 0x0F, 0x4C, 0xC6, 0xC3 };
  char* code_buf = mmap(NULL, sizeof(code), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
  memcpy(code_buf, code, sizeof(code));
  mprotect(code_buf, sizeof(code), PROT_READ|PROT_EXEC);

  binary_func_t max = (binary_func_t)code_buf;
  int n = max(3, 42);
  printf("max(3, 42) = %d\n", n);

  munmap(code_buf, sizeof(code));

  return 0;
}

// Use MAP_ANON instead of MAP_ANONYMOUS on Mac OS X

这是我在Mac OS X / x86-64上写的个小测试。对应Windows上的话 mmap -> VirtualAlloc,mprotect -> VirtualProtect。

如果是这样的场景的话,题主想问的是在我的例子中 max(3, 42) 那里参数是如何传递过去的么?其实没啥特别的,就是按照给定的calling convention来啊。

我的例子里就是用默认的System V ABI的calling convention,两个参数分别从RDI、RSI传递,返回值从RAX传递。

题主在Windows上实验的话,那么就参考您的编程环境里的默认calling convention来搞就好了。

如果题主没留意过的话,其实函数指针类型上是可以声明calling convention修饰符的,不写就是用默认的calling convention。例如说:

typedef int (__stdcall *binary_func_t)(int, int);
typedef int (__cdecl *binary_func_t)(int, int);
typedef int (__fastcall *binary_func_t)(int, int);

等等。这样就可以根据目标函数的实现状况来在caller一侧选择合适的calling convention与之匹配了。