如何在 x64 中创建 thunk? [英] How to create thunk in x64?

查看:23
本文介绍了如何在 x64 中创建 thunk?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了很好的示例如何为闭包创建 thunk,但它是 32 位版本:

I've found nice example how to create thunk for closure, but it's 32-bit version:

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

struct env {
  int x;
};

struct __attribute__((packed)) thunk {
  unsigned char push;
  struct env * env_addr;
  unsigned char call;
  signed long call_offset;
  unsigned char add_esp[3];
  unsigned char ret;
};

struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3};

typedef void (* cfunc)();

struct thunk * make_thunk(struct env * env, void * code)
{
  struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk), PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  *thunk = default_thunk;
  thunk->env_addr = env;
  thunk->call_offset = code - (void *)&thunk->add_esp[0]; // Pretty!                                                                               
  mprotect(thunk,sizeof(struct thunk), PROT_EXEC);
  return thunk;
}


void block(struct env * env) {
  env->x += 1;
  printf ("block: x is %d
", env->x);
}

cfunc foo (int x)
{
  struct env * env = (struct env *)malloc(sizeof(struct env));
  env->x = x;

  printf ("x is %d
",env->x);

  return (cfunc)make_thunk(env,(void *)&block);
}

int main() {
  cfunc c = foo(5);

  c();
  c();
}

如何为 64 位版本重写它?

How can I rewrite it for 64-bit version?

我使用的是 Linux x86_64.我已经能够使用 gcc -m32 交叉编译它,效果很好.

I'm using Linux x86_64. I've been able to cross-compile it with gcc -m32, which worked perfectly.

推荐答案

以下代码设计用于 Linux 上的 GCC,应支持 32 位和 64 位编译.

The code below is designed to be used with GCC on Linux and should support 32 and 64 bit compilation.

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

struct env {
    int x;
};

#if __x86_64__
struct __attribute__((packed)) thunk {
    unsigned char mov[2];
    struct env * env_addr;
    unsigned char movrax[2];
    void (*call_address)();
    unsigned char jmp[2];
};

struct thunk default_thunk = {{0x48, 0xbf}, 0x0, {0x48, 0xb8}, 0x0, {0xff, 0xe0} };
#elif __i386__
struct __attribute__((packed)) thunk {
    unsigned char push;
    struct env * env_addr;
    unsigned char call;
    signed long call_offset;
    unsigned char add_esp[3];
    unsigned char ret;
};
struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3};
#else
#error Architecture unsupported
#endif


typedef void (* cfunc)();

struct thunk * make_thunk(struct env * env, void * code)
{
    struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk), 
                            PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    *thunk = default_thunk;
#if __x86_64__
    thunk->env_addr = env;
    thunk->call_address = code; /* Pretty! */
#else
    thunk->env_addr = env;
    thunk->call_offset = code - (void *)&thunk->add_esp[0]; /* Pretty! */
#endif
    mprotect(thunk,sizeof(struct thunk), PROT_EXEC);
    return thunk;
}


void block(struct env * env) {
    env->x += 1;
    printf ("block: x is %d
", env->x);
}

cfunc foo (int x)
{
    struct env * env = (struct env *)malloc(sizeof(struct env));
    env->x = x;

    printf ("x is %d
",env->x);

    return (cfunc)make_thunk(env,(void *)&block);
}

int main() {
    cfunc c = foo(5);
    c();
    c();

    return 0;
}

假设操作系统正在使用 系统V 64 位 ABI(Linux 使用的)调用约定然后将传递给函数的第一个参数将在寄存器 %rdi 中.然后我们只需要mov环境地址(env_addr)到%rdi,然后做一个call.该调用使用通过 %rax 间接跳转到绝对位置.所以指令序列看起来像(at&t 语法):

Assuming that the OS is using System V 64bit ABI (Which Linux uses) calling convention then the first parameter that will be passed to the function will be in register %rdi. Then we just have to mov the environment address (env_addr) to %rdi and then do a call. The call uses an indirect jump to an absolute location through %rax. So the instruction sequence looks like (at&t syntax):

mov    $env_addr, %rdi
movabs $call_pointer, %rax
jmpq  *%rax                   # Tail call instead of call/retq

这篇关于如何在 x64 中创建 thunk?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆