<分区>
是否可以编写执行以下操作的 C 函数?
- 在堆中分配一堆内存
- 在里面写机器码
- 执行那些机器指令
当然,我必须将堆栈的状态恢复到手动执行这些机器指令之前的状态,但我首先想知道这是否可行。
<分区>
是否可以编写执行以下操作的 C 函数?
当然,我必须将堆栈的状态恢复到手动执行这些机器指令之前的状态,但我首先想知道这是否可行。
最佳答案
这当然可能。出于各种原因,在过去的 30 到 40 年里,我们付出了很多努力,试图让它变得尽可能困难,但这是可能的。现在在大多数系统中,都有试图保护数据空间不被执行的硬件和软件机制。
不过,基础知识相当简单:您构造一段代码,然后手动或通过编译器对其进行汇编。然后你需要一段代码空间,所以你将代码插入到你的程序中
unsigned int prgm[] = { 0x0F, 0xAB, 0x9A ... }; // Random numbers, just as an example
因为你想使用堆你需要分配空间
void * myspace ;
if((myspace= malloc(sizeof(prgm))) != NULL) {
memcpy(myspace, pgrm, sizeof(pgrm));
} else { // allocation error
}
现在,您需要一种方法让程序计数器 指向也是您的代码块的数据 block 。这是你需要一点技巧的地方。设置程序计数器没什么大不了的;这只是您的底层机器的 JUMP 指令。但是该怎么做呢?
最简单的方法之一是有目的地弄乱堆栈。从概念上讲,堆栈看起来像这样(详细信息取决于您的操作系统和编译器对,以及您的硬件):
| subroutine return addr | | parameters ... | | automatic variables |
The basic trick here is to sneakily get the address of your code into the return address; when a routine returns, it basically jumps to that return addrfess. If you can fake it out, the PC will be set to where you like.
So, what you need is a routine, let's call it "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 进入堆。出于安全目的,这很有用,因为我们不会让您进行这种完全控制。
关于c - C中的一种自修改程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/408592/