运行时嘲笑C?
这在我的列表中已经有很长时间了.简而言之 – 我需要运行mocked_dummy()代替dummy()ON RUN-TIME,而不需要修改factorial().我不在乎软件的入门点.我可以添加任何数量的附加功能(但是不能修改/ *中的代码—-不要修改—- * /).
为什么我需要这个? 平台/环境? 接近我正在尝试吗? >我知道GDB使用陷阱/非法指令来添加断点(gdb internals). 这里有问题.我也发现这个方法是繁琐的,需要很多的编码,一些也是组装的. 我也发现,在gcc下,每个函数调用可以是hooked / instrumented,但是再次不是很有用,因为该函数的意图是要嘲笑将无论如何都被执行. 还有其他方法可以使用吗? #include <stdio.h> #include <stdlib.h> void mocked_dummy(void) { printf("__%s__()n",__func__); } /*---- do not modify ----*/ void dummy(void) { printf("__%s__()n",__func__); } int factorial(int num) { int fact = 1; printf("__%s__()n",__func__); while (num > 1) { fact *= num; num--; } dummy(); return fact; } /*---- do not modify ----*/ int main(int argc,char * argv[]) { int (*fp)(int) = atoi(argv[1]); printf("fp = %xn",fp); printf("factorial of 5 is = %dn",fp(5)); printf("factorial of 5 is = %dn",factorial(5)); return 1; } 解决方法
这是一个我一直在试图回答自己的问题.我也有要求我想使用与我的应用程序相同的语言完成嘲弄的方法/工具.不幸的是,这不能在C中以便携式的方式完成,所以我已经诉诸于你可能称之为蹦床或绕行.这属于“使代码自我修改”.你上面提到的方法这是我们在运行时更改函数的实际字节,以跳转到我们的模拟函数.
#include <stdio.h> #include <stdlib.h> // Additional headers #include <stdint.h> // for uint32_t #include <sys/mman.h> // for mprotect #include <errno.h> // for errno void mocked_dummy(void) { printf("__%s__()n",__func__); while (num > 1) { fact *= num; num--; } dummy(); return fact; } /*---- do not modify ----*/ typedef void (*dummy_fun)(void); void set_run_mock() { dummy_fun run_ptr,mock_ptr; uint32_t off; unsigned char * ptr,* pg; run_ptr = dummy; mock_ptr = mocked_dummy; if (run_ptr > mock_ptr) { off = run_ptr - mock_ptr; off = -off - 5; } else { off = mock_ptr - run_ptr - 5; } ptr = (unsigned char *)run_ptr; pg = (unsigned char *)(ptr - ((size_t)ptr % 4096)); if (mprotect(pg,5,PROT_READ | PROT_WRITE | PROT_EXEC)) { perror("Couldn't mprotect"); exit(errno); } ptr[0] = 0xE9; //x86 JMP rel32 ptr[1] = off & 0x000000FF; ptr[2] = (off & 0x0000FF00) >> 8; ptr[3] = (off & 0x00FF0000) >> 16; ptr[4] = (off & 0xFF000000) >> 24; } int main(int argc,char * argv[]) { // Run for realz factorial(5); // Set jmp set_run_mock(); // Run the mock dummy factorial(5); return 0; } 便携解释… mprotect() – 这会更改内存页访问权限,以便我们可以实际写入保存功能代码的内存.这不是很便携,在WINAPI环境中,您可能需要使用VirtualProtect(). mprotect的内存参数与前一个4k页面对齐,这也可以从系统更改为系统,4k适用于香草linux内核. 我们用于jmp到mock函数的方法是实际放下自己的操作码,这可能是可移植性最大的问题,因为我使用的操作码只能在一个小的endian x86(大多数桌面)上工作.因此,您需要针对您计划运行的每个arch(可能会在CPP宏中处理半容易)进行更新. 该函数本身必须至少为5个字节.通常情况是这样,因为每个函数的序言和结尾通常都至少有5个字节. 潜在改进… set_mock_run()调用可以很容易地设置为接受参数以供重用.另外,您可以从原始函数保存五个覆盖的字节,以便稍后在代码中恢复. 我无法测试,但是我已经在ARM看过了,你会做类似的,但是你可以用分支操作码跳转到一个地址(而不是一个偏移量)…对于一个无条件的分支,你可以第一个字节为0xEA,接下来的3个字节为地址. Chenz (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |