是否可以编写执行以下操作的C函数?
在堆中分配一堆内存
在其中写入机器代码
执行那些机器指令
当然,我必须将堆栈的状态恢复到手动执行这些机器指令之前的状态,但我想知道这是否可行.
这当然是可能的.出于各种原因,我们在过去的30 - 40年中花了很多精力试图尽可能地让它变得困难,但这是可能的.现在,在大多数系统中,都有硬件和软件机制试图保护数据空间不被执行.
但是,基础知识非常简单:构建一段代码,然后通过编译器手动或4来组装它.然后,您需要一段代码空间,因此您可以将代码插入到程序中
unsigned int prgm[] = { 0x0F, 0xAB, 0x9A ... }; // Random numbers, just as an example
因为你想使用堆,你需要malloc空间
void * myspace ; if((myspace= malloc(sizeof(prgm))) != NULL) { memcpy(myspace, pgrm, sizeof(pgrm)); } else { // allocation error }
现在,您需要的是一种让程序计数器指向同样是您的代码块的数据块的方法.这是你需要一点狡猾的地方.设置程序计数器没什么大不了的; 这只是底层机器的JUMP指令.但是怎么做呢?
最简单的方法之一就是有目的地搞乱堆栈.从概念上讲,堆栈看起来像这样(细节取决于您的操作系统和编译器对,以及您的硬件):
| subroutine return addr | | parameters ... | | automatic variables |
这里的基本技巧是偷偷将代码的地址放入返回地址; 当例程返回时,它基本上跳转到返回addrfess.如果你可以伪造它,PC将被设置到你喜欢的地方.
所以,你需要的是一个例程,我们称之为"goThere()"
void goThere(void * addr){ int a ; // observe above; this is the first space // on the stack following the parameters int * pa; // so we use it's address pa = (&a - (sizeof(int)+(2*sizeof(void*))) ; // so use the address // but back up by the size of an int, the pointer on the // stack, and the return address // Now 'pa' points to the routine's return add on the stack. *pa = addr; // sneak the address of the new code into return addr return ; // and return, tricking it into "returning" // to the address of your special code block }
它会起作用吗?好吧,也许,取决于硬件和操作系统.大多数现代操作系统将保护堆(通过内存映射或类似)从进入它的PC.出于安全目的,这是一个有用的东西,因为我们也不能让你采取那种完全控制.