在Visual C++中,通过将shellcode转换为函数指针来执行它。

7
在gcc中,这个很好用。代码大致如下:
unsigned char b[50] = "\xda\xd1 ... \x0"; //some shellcode with terminating \x0
( (void(*)())b )(); //cast b to function pointer from void to void, then run it

但是当将此代码放入Visual C++中时,会提示以下错误信息:
1>..\test.cpp(132): error C2440: 'type cast' : cannot convert from 'unsigned char [50]' to 'void (__cdecl *)(void)'
1>          There is no context in which this conversion is possible

有人知道为什么会这样吗?

1
即使你设法让它工作,这仍然是一个非常糟糕的想法。 - Mark Ransom
或者:reinterpret_cast<void(*)()>(static_cast<char*>(b))() - Kerrek SB
@NiklasB. (*(void(*)())&b[0])()在运行时崩溃。 首先,&b[0]与b相同。 其次,开头的额外*对函数指针进行了解引用; 我认为它然后尝试将shellcode解释为地址。 - jcai
奇迹不在于它在Visual C++中崩溃,而在于它在gcc中运行正常。你所说的"shellcode"究竟是什么意思? - Mark Ransom
@user49164 解除引用函数指针是无害的。它会迅速退化为一个函数指针。这就是经典的 ***********************************fun_ptr 示例的来源。 - R. Martinho Fernandes
显示剩余2条评论
2个回答

13
一个正常的调试器可以告诉你出了什么问题。我只能猜测,你的代码导致了访问冲突,因为你想跳转到的缓冲区不可执行。
可能你正在使用像Vista或7这样默认启用DEP的系统,所以你必须确保你的shellcode是可执行的。为此,首先使用VirtualAlloc来分配一个新的可执行缓冲区,并将你的shellcode复制到其中,然后执行它:
void *exec = VirtualAlloc(0, sizeof b, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(exec, b, sizeof b);
((void(*)())exec)();

顺便说一下,您不需要给shellcode加上空字符(C++会自动为您终止字符串字面量,但这并非必需)。您也不需要指定大小:

unsigned char b[] = "\xcc";

谢谢 - 这个正常工作。不知道有一个特殊的分配函数来执行东西。 - jcai
1
@user49164:为了增加利用基于堆栈和堆的缓冲区溢出的难度,DEP强制执行内存页面上的执行权限。这可以防止在堆栈或堆上立即执行数据。VirtualAlloc不是专门用于执行操作的分配函数,它是通用的分配函数(恰好以所分配内存的期望访问权限作为参数)。 - Niklas B.

1

将数据重新解释为不同类型的典型方式是通过复制二进制表示:

void (*fp)();
unsigned char buf[50];
char const * p = reinterpret_cast<char const *>(&buf);

std::copy(p, p + sizeof(char const *), reinterpret_cast<char*>(&fp));

// now fp contains the same value as &buf

fp();  // call

这可以避免由别名和对齐违规引起的未定义行为。


OP 正在尝试将指向缓冲区的指针重新解释为函数指针,以便执行其内容作为代码。你的版本覆盖了部分缓冲区内容的函数指针——结果肯定不是有效的指针。你需要定义一个指向 buf 的指针,然后复制其值。 - Mike Seymour
确认。这会在运行时崩溃。 - jcai
这似乎应该可以工作,但它会崩溃 - 或许是同样的 DEP 问题? - jcai
即使在编辑后的形式中,这仅会复制 sizeof(void*) 字节。 - Niklas B.
@NiklasB.:好的,不是,它将复制sizeof(char *)个字节,这正好是&buf上有多少个字节。 没有人说过这保证能让人看得懂,而且你需要确保在你的平台上,char指针、void指针和函数指针具有相同的大小。 如果您希望省略第一个检查(即添加void const * q = static_cast <void const *>(p)并将其复制),则可以添加一个(标准)转换为void指针。 - Kerrek SB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接