这听起来超级hackish但是有没有人知道在C ++中运行时组合方法体的方法?我目前正在抓住函数的地址,然后将memcopy写入可执行内存,但它存在不需要的prolog / epilog的问题。
基本上我有几十个简单的操作,它们采用相同的参数并且什么都不返回,我想在这些简单的操作中在运行时构建一个函数。
答案 0 :(得分:4)
实现所需功能的简单解决方案是将函数指针存储在数组中 然后,您可以在运行时创建函数指针列表(您的脚本) 您可以通过迭代列表并调用函数来执行脚本。
答案 1 :(得分:3)
为什么不使用汇编?
答案 2 :(得分:3)
除了它之外,我看不出任何实际用途: - )
首先,你应该决定一个平台。 你不可能以跨平台的方式做到这一点。 实际上,以一种适用于多个编译器的方式实现它可能非常困难,即使在同一平台上也是如此。 那么处理器类型当然是: - )
然后你应该以某种方式检查你正在复制的代码是否可以重新定位。并非所有东西都可以随心所欲地移动。
你必须非常了解调用约定,这样才能确保不会弄乱堆栈。
了解编译器生成的prolog / epilog。您可以通过在函数的开头和结尾添加一些无效的代码序列来作弊,但是您可以使用签名然后查找(即.nop; nop; nop; xor ax,ax; nop; push ax; pop ax; nop; nop; nop;)。确保编译器未对其进行优化: - )
确保您可以编写/执行该代码。现代CPU和操作系统通常不允许用户在代码段中写入或执行非代码段。因此,您必须找出更改权限的方法(100%特定于操作系统)。
然后玩“地址空间布局随机化”,“堆栈随机化”,“数据执行预防”,“堆随机化”之类的乐趣。
无论如何,做了很多工作。毫无意义,除了享受一个好的挑战,并在此过程中学习一些程序集和操作系统内部。 O用于证明自己是“1337”,但是,然后,询问stackoverflow如何做到这一点并不完全是“1337”,如果你问我: - )
无论如何,祝你好运。
答案 3 :(得分:2)
它不起作用,但我告诉你我所知道的。您可以使用setjmp()获取当前进程计数器位置,并使用您获得的填充结构。
如果我不得不像你要问的那样,我会在例程的开头和结尾放置两个setjmp,分配可执行内存(甚至可能吗?我认为文本部分是readonly,但是是...缓冲区溢出漏洞通过执行堆栈来工作,所以是的,我想你可以,除非你有NX技术,然后从四个setjmp获得的括号中复制块。在这个过程中,你会发现一堆乱码不可重定位的操作码,我想,就像它们引用汇编代码中其他操作码的绝对地址一样。当然,如果你复制,这个地址必须相应地改变。
祝你好运。答案 4 :(得分:2)
模板?
如果你有这个:
template <int Operations>
struct Foo
{
static void Do(int a, int b)
{
if (Operations & 1)
{
/// op 1
}
if (Operations & 2)
{
/// op 2
}
if (Operations & 4)
{
/// op 3
}
//...
}
};
优化器会丢弃与您的专业化无关的块,例如:
Foo<6>::Do(77,88)
只会处理第2步和第3步,而不会为第1步生成代码。
(您应该检查编译器的输出,但大多数应该能够。我正在使用它来检查所选的数组属性,在VC6和后来的编译器中工作,如魅力)
答案 5 :(得分:1)
对于一个动态的解决方案......将它们物理地组合在一起:
现在您已准备好构建指令向量:
struct Op {
char code[MAX_OP_SIZE];
public Op(const void (*op)()) {
/* fill code with no-ops */
const char* opCode = (char*)op;
char* opCodeCopy = &code[0];
while (*opCode != GUARD_INSTRUCTION) {
*opCodeCopy++ = *opCode++;
}
}
}
std::vector<Op> combinedOperation;
combinedOperation.push_back(op1);
// ...
void (*combinedOpFunc)(params) = void *()(params)&combinedOperation[0]; // not sure about syntax here
combinedOpFunc(data);
// start praying
另一种选择是根本不复制代码。这是使用setjmp / longjmp的Stano Borini答案的变体。我们没有在每种方法中进行跳转,而是将它们设置一次。
如果你的操作如下:
void op1(paramtype param) { ... }
成功 typedef void(* OPTYPE)(paramtype);
void op1(paramtype param, OPTYPE nextop, jmp_buf returnFrame)
{
// ...
if (nextop) {
// place (paramtype+1) into argument slot 2 (assume +1 does pointer arithmetic)
// update program counter to value of paramtype
} else {
longjmp(returnFrame, 0);
}
}
然后列出一系列操作:
std::vector<OPTYPE> ops; ops.push_back(&op1); // ...
和辅助函数
void callOps(const std::vector<OPTYPE>& opList, paramtype param)
{
OPTYPE firstOp = (OPTYPE)&opList[0];
jmp_buf frame;
int ret = setjmp(&frame);
if (ret == 0) firstOp(param, firstOp + 1, frame);
}
并执行它
callOps(ops, opParameter);
答案 6 :(得分:0)