c++ 回调函数_UnicornVM进程环境的指令级函数虚拟机

3961e85b1e6bc95e01f0708ac2b5fd2a.png这个标题是不是有点绕?咱们来剖析理解一下,请看下表。
进程环境这里指跑在真实系统中的进程,比如iOS、Android系统中的App、Service、Daemon等。
指令级这里指armv7、arm64、arm64e指令集。
函数这里指汇编级C函数。
虚拟机这里指可逐条执行指令的虚拟CPU,基于Unicorn实现。
上述概念合起来就是:UnicornVM是跑在真实物理进程环境的虚拟机,它以汇编级C函数为最小虚拟化单位,以单条指令为最小执行单位。如果还是没理解,可以去看看京东安全团队旗下的麒麟框架,它是全模拟环境的虚拟机。了解了麒麟框架再回过头看看UnicornVM的定义,你应该就明白了。UnicornVM导出的接口非常非常的简单,就俩,分别是:
// run function 'fn' on our VCPU with 'ctx'// return value is x0VCAPI long vc_run_interp(const void *fn,                          const vc_context_t *ctx);
// make a wrapper for function 'fn' with 'usrctx','callback'// return value is a new function pointer which will run under our VCPU// you can replace this pointer to target's function pointer// like C++-Vtable/Script-Native-Bridge// if return null, you should check errnoVCAPI const void *vc_make_callee(const void *fn,                                  void *usrctx,                                 fn_vc_callback_t callback);
其中vc_run_interp是指你丢一个函数给它,然后在虚拟机环境真实执行这个函数。vc_make_callee是指你丢一个函数给它,然后返回一个新的功能等价的函数给你,但是这个函数的执行是在虚拟机里面的,你可以把这个返回的函数指针拿去替换C++-Vtable,也可以拿去给fishhook当参数使用。两个函数的fn参数都是原始函数指针,下面我们详细介绍一下ctx这个参数。vc_context_t的定义如下:
// interpreter contexttypedef struct vc_context_t {  // 用户自己的context,用在下述callback参数中  void *usrctx;   //这里是指arm64虚拟机初始上下文,32位则为vc_armctx_t  vc_arm64ctx_t regctx;   // 用户丢给框架的回调函数,比如内存访问、函数调用之类的事件  fn_vc_callback_t callback; } vc_context_t;
它的作用是告诉vc_run_interp函数如何初始化虚拟机解释器。比如下面的测试代码:
// run interpretee directlyint vrun_print_message(const char *reason,   const char **argv, FILE *cblogfp, fn_vc_callback_t cb) {  vc_context_t ctx;  memset(&ctx, 0, sizeof(ctx));  ctx.usrctx = cblogfp; // 用户自己的回调参数  ctx.callback = cb; // 用户自定义的回调函数  ctx.regctx.r[0].p = reason; // x0为arm64函数的第一个参数  ctx.regctx.r[1].p = (void *)argv; // x1为arm64函数的第二个参数  //将print_message函数以ctx的初始值跑在虚拟机里面  return (int)(long)vc_run_interp((void *)print_message, &ctx);}
我们继续看看fn_vc_callback_t这个框架回调函数的定义:
// callback prototypetypedef vc_callback_return_t (*fn_vc_callback_t)(vc_callback_args_t *args);
这个回调函数是UnicornVM虚拟机在执行到特殊的事件时调用的,以供用户自己决定如何处理,比如改写内存、函数重定向、修改系统调用参数等高级操作。回调事件定义如下:
// opcode type for callback argstypedef enum vc_optype_t {  vcop_read,    // memory read  vcop_write,   // memory write  vcop_call,    // function call  vcop_return,  // function return  vcop_svc,     // arm64 syscall  vcop_ifetch,  // interpreter fetch instruction} vc_optype_t;
它包含在 vc_callback_args_t回调参数里面,定义如下:
// callback argstypedef struct vc_callback_args_t {  // your own context passed for vc_run_interp/vc_make_callee  const void *usrctx;  // arm64 execution context  vc_arm64ctx_t *arm64ctx;  // current opcode  vc_optype_t op;  union {    // for vcop_read/vcop_write/vcop_ifetch    struct {      const void *src;      void *dst;      int byte;    } rw;    // for vcop_call    struct {      const void *callee;    } call;    // for vcop_return    struct {      const void *hitaddr;  // which address hit return    } ret;    // for vcop_svc    struct {      int sysno;  // syscall number    } svc;  } info;} vc_callback_args_t;
这里面详细记录了回调事件的具体参数,比如 vcop_read事件发生时,rw字段里面的src、dst、byte就依次说明了内存源地址、写入地址、字节大小。再比如当vcop_svc事件发生时,svc字段里面的sysno就给出了当前系统调用的调用号,然后再通过虚拟机上下文arm64ctx就可以取得系统调用的具体参数。一个具体的测试回调函数如下:
// log runtime informationstatic vc_callback_return_t interp_callback_log(vc_callback_args_t *args) {  FILE *logfp = (FILE *)args->usrctx;  switch (args->op) {    case vcop_read:    case vcop_write: {      break;    }    case vcop_call: {      fprintf(logfp, "vcop call : func %p.\n", args->info.call.callee);      break;    }    case vcop_return: {      fprintf(logfp, "vcop return : hit address %p.\n", args->info.ret.hitaddr);      break;    }    case vcop_svc: {      fprintf(logfp, "vcop syscall : syscall number %d.\n", args->info.svc.sysno);      break;    }    default: {      fprintf(logfp, "unknown vcop %d.\n", args->op);      break;    }  }  return cbret_directcall;  // return cbret_recursive;}
回调函数的返回值用于指示虚拟机解释器如何作出反应,它的定义如下:
// callback return typetypedef enum vc_callback_return_t {  cbret_continue,    // let interp continue  cbret_processed,   // already processed by callback implementation  cbret_recursive,   // interp this function recursively  cbret_directcall,  // call this function directly} vc_callback_return_t;
cbret_continue是告诉解释器继续解释执行当前指令。cbret_processed是告诉解释器回调函数已经处理过了,忽略执行当前指令。cbret_recursive是指递归解释执行当前调用的函数。cbret_directcall是指切换到host环境调用当前调用的函数。针对不同架构,有不同的虚拟机上下定义,比如arm64的虚拟机上下文为:
// arm64 common register like x0 x1 ... lr sp pctypedef union vc_cmmreg_t {  unsigned int w;  unsigned long long x;  int sw;  long long sx;  const void *p;  const char *s;  unsigned int ws[2];  int sws[2];} vc_cmmreg_t;// arm64 neon register like s0 d0 q0 ...typedef union vc_neonreg_t {  unsigned int i[4];  unsigned long long l[2];  int si[4];  long long sl[2];} vc_neonreg_t;// special register wrapper#define ARM64_FP(c) (c).r[29]#define ARM64_LR(c) (c).r[30]#define ARM64_SP(c) (c).r[31]#define ARM64_PC(c) (c).pc// arm64 execution contexttypedef struct vc_arm64ctx_t {  vc_cmmreg_t r[32];  // 0-28,29-fp,30-lr,31-sp  vc_neonreg_t v[32];  vc_cmmreg_t pc;} vc_arm64ctx_t;

armv7的上下文定义如下:

// arm common register like r0 r1 ... lr sp pctypedef union vc_cmmreg_t {  unsigned int w;  int sw;  const void *p;  const char *s;} vc_cmmreg_t;// arm neon register like s0 d0 ...typedef union vc_neonreg_t {  unsigned int i[2];  unsigned long long l;  int si[2];  long long sl;  float f[2];  double d;} vc_neonreg_t;// special register wrapper#define ARM_FP(c) (c).r[12]#define ARM_SP(c) (c).r[13]#define ARM_LR(c) (c).r[14]#define ARM_PC(c) (c).pc// arm execution contexttypedef struct vc_armctx_t {  vc_cmmreg_t r[16];  // 0-12, 13-sp, 14-lr, 15-reserved  vc_neonreg_t v[32];  vc_cmmreg_t pc;} vc_armctx_t;
完整的测试代码请参见https://gitee.com/geekneo/VirtualCode里面UnicornVM目录下面的sample。UnicornVM既简单又不简单,你学会了吗?如果你有任何建议,欢迎通过私信、头条、抖音、视频号、微信的‘刘柏江’、‘刘柏江VM’、‘GeekNeo’等渠道提交给我们,谢谢各位朋友的支持,一如既往的么么哒。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值