未采用到弹跳床的手动编码分支,而是使用附加的调试器进行

问题描述 投票:0回答:1

大约一个月前,我尝试用 C 语言实现一个类似弹床的结构,它封装了一小段汇编代码,将一个函数与另一个函数挂钩。

这个想法很简单:每当创建

bounce_bed
时,我都会将挂钩函数的第一条指令替换为到
bounce_bed
的分支,并在
bounce_bed
中编码一个执行堆栈操作的短汇编函数,然后调用钩子并返回,最后跳回该函数。

这是我的代码:

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

typedef uint32_t u32;

static inline u32
encode_b (unsigned long target_addr)
{
  return 0x14000000 | (0x03ffffff & (target_addr >> 2));
}

static inline u32
encode_bl (unsigned long target_addr)
{
  return 0x94000000 | (0x03ffffff & (target_addr >> 2));
}

/**
 * a bounce_bed is an entry to a hook function
 * use trace(func, hook) to set it up.
 */
struct __attribute__ ((aligned (4))) bounce_bed
{
  u32 first_inst;  // 0xa9bc7bfd
  u32 second_inst; // 0x910003fd
  u32 stp_x0_x1;   // 0xa9be07e0
  u32 stp_x2_x3;   // 0xa9be0fe2
  u32 bl_trace_hook;
  u32 ldp_x2_x3; // 0xa8c20fe2
  u32 ldp_x0_x1; // 0xa8c207e0
  u32 b_trace_point;
};

struct bounce_bed *
trace (void *func_addr, void *hook_addr)
{
  u32 *ptr = func_addr;
  size_t page_size = getpagesize ();
  void *page_aligned_addr;
  struct bounce_bed *tnode = malloc (sizeof (struct bounce_bed));

  tnode->first_inst = ptr[0];    // normally stp fp, lr, [sp, #-32]!
  tnode->second_inst = ptr[1];   // normally mov fp, sp
  tnode->stp_x0_x1 = 0xa9be07e0; // stp x0, x1, [sp, #-32]!
  tnode->stp_x2_x3 = 0xa9be0fe2; // stp x2, x3, [sp, #-32]!
  tnode->bl_trace_hook
      = encode_bl ((void *)hook_addr - (void *)&tnode->bl_trace_hook);
  tnode->ldp_x2_x3 = 0xa8c20fe2; // ldp x2, x3. [sp], #32
  tnode->ldp_x0_x1 = 0xa8c207e0; // ldp x0, x1, [sp], #32
  tnode->b_trace_point
      = encode_b (((void *)func_addr + 8) - (void *)&tnode->b_trace_point);

  page_aligned_addr = (void *)((uintptr_t)tnode & ~(page_size - 1));
  mprotect (page_aligned_addr, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);

  page_aligned_addr = (void *)((uintptr_t)func_addr & ~(page_size - 1));
  mprotect (page_aligned_addr, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);

  ptr[0] = encode_b ((void *)tnode - (void *)func_addr); // b tnode
  ptr[1] = 0xd5033fdf;                                   // isb

  return tnode;
}

/** computes x power y */
int
mypow (int x, int y)
{
  if (y > 0)
    return x * mypow (x, y - 1);
  else
    return 1;
}

/** this is called before hooked function */
void
myhook (void)
{
  unsigned long x1;

  asm ("mov %0, x1" : "=r"(x1));
  printf ("%s: x1 = %#lx\n", __func__, x1);
  return;
}

int
main (int argc, char *argv[])
{
  if (argc != 3)
    return -1;

  /* read arguments */
  int x = strtod (argv[1], NULL);
  int y = strtod (argv[2], NULL);

  /* attach myhook to mypow */
  struct bounce_bed *tp = trace (mypow, myhook);
  /* test */
  int c = mypow (x, y);
  printf ("%d\n", c);
  free (tp);
  return 0;
}

所有测试都是在arm64板子(rk3588 with linux bsp kernel 5.10)上执行的,但后来我也在arm64服务器(ampere,kernel 5.10,gcc-12.0)上测试了它,答案是一样的。

我使用 gcc-13.1 使用

-g
选项编译它,并尝试使用参数
2 4
运行它,它正常返回,但没有显示打印。但是,预计会从
myhook
打印出来。这意味着,
myhook
没有被调用,实际上,没有跳转到弹跳床。

但是当我用gdb一步步运行它时,似乎每一步都是正确的,

myhook
的打印显示如预期,最后它正常返回......我检查了地址,发现一切都是正确的。

更重要的是,我还尝试用

valgrind
运行它来检测是否存在内存泄漏,我发现
myhook
的打印也显示,没有检测到内存泄漏。

我尝试禁用 ASLR(地址空间布局随机化),但没有任何反应。

这太奇怪了,我在stackoverflow上检查了相关问题,但没有发现对此有帮助的想法。

c assembly arm gdb arm64
1个回答
0
投票

@Siguza 给了你答案,但它被嵌入到关于

u32
的争论中,所以它可能被错过了。

各种 ARM 芯片具有独立的数据和指令缓存。您可能已经用另一条指令覆盖了该指令,这将反映在数据缓存中,但指令缓存仍将具有旧指令。

gcc 提供了

__builtin__clear_cache
可以做你想做的事。

© www.soinside.com 2019 - 2024. All rights reserved.