我的问题主要是专注于教授,并且是以“奇怪”的方式使用C.在C中,指向变量的指针和指向函数的指针之间没有太大区别.我们可以做一些像这样无用的事情:
char* buff = new char[32]; void (*func)() = (void (*)())buff;
但我们所有人都创造了一个从未存在的功能,对吧?如果我们进一步用文件中的x86命令stord填充buff怎么办?操作系统永远不会知道创建了一个函数.
#include <iostream> using namespace std; // no stack push'ing or pop'ing,nothing to return void func(void){cout << "Hello?";} int main() { char* x86_code = new char[6]; x86_code[0] = 0x9A; // call (far) *((__int32*)(x86_code + 1)) = (__int32)func; // load 32-bit address x86_code[5] = 0xC3; // ret void (*x86_func)(void) = (void (*)(void))x86_code; x86_func(); return 0; }
调用x86_func()会产生运行时错误(违反读取位置0xFFFFFFFF).如果不以这种方式操作系统如何在RAM中加载它的二进制文件或模块?非常感谢.
解决方法
实际上,您可以使用x86机器代码填充数组并尝试执行它.它被称为shellcode并且管理使应用程序或库在不打算被称为“漏洞利用”时执行这样的代码.
不幸的是,它并不那么容易,因为现代硬件和操作系统通常会阻止您从可写区域执行代码,例如非const char数组.这称为W ^ X(写入或执行权限,但不是两者)但是可以通过mprotect()函数请求符合POSIX的操作系统禁用此类保护.这是一个有效的示例,因为它在相关的机器代码字节数组上启用读取,写入和执行权限:
#include <stdio.h> #include <stdint.h> #include <sys/mman.h> typedef int(*FUNKY_POINTER)(void); char shellcode[] = { 0xb8,0x2a,0x00,//mov $0x2a,%eax 0xc3 //retq }; int main(void){ uintptr_t pageSize = 4096; uintptr_t shellcodeAddr = (uintptr_t)shellcode; uintptr_t pageAlignedAddr = shellcodeAddr & ~(pageSize-1); FUNKY_POINTER shellcodeFn = (FUNKY_POINTER)shellcode; /* Magic */ mprotect((void*)pageAlignedAddr,(shellcodeAddr - pageAlignedAddr) + sizeof(shellcode),PROT_EXEC|PROT_WRITE|PROT_READ); printf("The answer to the ultimate question of life," "the universe and everything is %d\n",shellcodeFn()); return 0; }