考虑以下带有函数f()的代码,将函数本身完整地复制到缓冲区,修改其代码并运行更改的函数。 实际上,克隆并修改返回编号22的原始函数以返回编号42。
#include #include #include #define ENOUGH 1000 #define MAGICNUMBER 22 #define OTHERMAGICNUMBER 42 int f(void) { return MAGICNUMBER; } int main(void) { int i,k; char buffer[ENOUGH]; /* Pointer to original function f */ int (*srcfptr)(void) = f; /* Pointer to hold the manipulated function */ int (*dstfptr)(void) = (void*)buffer; char* byte; memcpy(dstfptr, srcfptr, ENOUGH); /* Replace magic number inside the function with another */ for (i=0; i < ENOUGH; i++) { byte = ((char*)dstfptr)+i; if (*byte == MAGICNUMBER) { *byte = OTHERMAGICNUMBER; } } k = dstfptr(); /* Prints the other magic number */ printf("Hello %d!n", k); return 0; }
代码现在依赖于猜测函数将适合1000字节缓冲区。 它还通过向缓冲区复制过多来违反规则,因为函数f()很可能比1000字节短很多。
这就引出了一个问题 :是否有一种方法可以计算出C中任何给定函数的大小? 一些方法包括查看中间链接器输出,并根据函数中的指令进行猜测,但这还不够。 有什么方法可以肯定吗?
请注意:它在我的系统上编译和工作但不完全遵守标准,因为函数指针和void *之间的转换不是完全允许的:
$ gcc -Wall -ansi -pedantic fptr.c -o fptr fptr.c: In function 'main': fptr.c:21: warning: ISO C forbids initialization between function pointer and 'void *' fptr.c:23: warning: ISO C forbids passing argument 1 of 'memcpy' between function pointer and 'void *' /usr/include/string.h:44: note: expected 'void * __restrict__' but argument is of type 'int (*)(void)' fptr.c:23: warning: ISO C forbids passing argument 2 of 'memcpy' between function pointer and 'void *' /usr/include/string.h:44: note: expected 'const void * __restrict__' but argument is of type 'int (*)(void)' fptr.c:26: warning: ISO C forbids conversion of function pointer to object pointer type $ ./fptr Hello 42! $
请注意:在一些从可写内存执行的系统上是不可能的,这段代码会崩溃。 它已经在运行x86_64架构的Linux上使用gcc 4.4.4进行了测试。
你不能在C中做到这一点。即使你知道长度,函数的地址也很重要,因为函数调用和对某些类型数据的访问将使用程序计数器相对寻址。 因此,位于不同地址的函数的副本将不会与原始函数执行相同的操作。 当然还有很多其他问题。
在C标准中,没有内省或反思的概念,因此你需要自己设计一个方法,就像你所做的那样,然而存在一些其他更安全的方法。
有两种方法:
注意:这两种方法都要求目标代码具有读取权限。
@R ..提出了一个很好的观点,你的代码将无法重新定位,除非它的PIC或你就地重新调整它以调整地址等。
以下是符合标准的实现所需结果的方法:
int f(int magicNumber) { return magicNumber; } int main(void) { k = f(OTHERMAGICNUMBER); /* Prints the other magic number */ printf("Hello %d!n", k); return 0; }
现在,你可能在没有参数的地方有很多f()
的使用,并且不想通过你的代码改变每一个,所以你可以改为
int f() { return newf(MAGICNUMBER); } int newf(int magicNumber) { return magicNumber; } int main(void) { k = newf(OTHERMAGICNUMBER); /* Prints the other magic number */ printf("Hello %d!n", k); return 0; }
我并不是说这是对你的问题的直接回答,但你所做的是如此可怕,你需要重新考虑你的设计。
那么,您可以使用标签在运行时获取函数的长度:
int f() { int length; start: length = &&end - &&start + 11; // 11 is the length of function prologue // and epilogue, got with gdb printf("Magic number: %dn", MagicNumber); end: return length; }
执行此函数后,我们知道它的长度,因此我们可以将malloc
为正确的长度,复制和编辑代码,然后执行它。
int main() { int (*pointerToF)(), (*newFunc)(), length, i; char *buffer, *byte; length = f(); buffer = malloc(length); if(!buffer) { printf("can't mallocn"); return 0; } pointerToF = f; newFunc = (void*)buffer; memcpy(newFunc, pointerToF, length); for (i=0; i < length; i++) { byte = ((char*)newFunc)+i; if (*byte == MagicNumber) { *byte = CrackedNumber; } } newFunc(); }
现在还有另一个更大的问题,就是@R。 提及。 一旦修改(正确),使用此函数会在调用printf
时导致分段错误,因为call
指令必须指定一个错误的偏移量 。 您可以使用gdb
查看此内容,使用disassemble f
查看原始代码,使用x/15i buffer
查看编辑过的代码。
顺便说一下,我的代码和你的代码在没有警告的情况下编译,但在调用编辑过的函数时崩溃在我的机器上( gcc 4.4.3
)。
以上就是c/c++开发分享如何确定函数的长度?相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/544840.html