我觉得我的问题看起来有点奇怪,但是在这里呢。 我试图在C ++中dynamic地创build一个程序(主要是为了它的乐趣,但也是一个编程的原因),并没有听起来那么难。 要做到这一点,你必须像这样在运行时使用汇编:
byte * buffer = new byte[5]; *buffer = '0xE9'; // Code for 'jmp' *(uint*)(buffer + 1) = 'address destination'; // Address to jump to
这比看起来要容易得多,因为我只针对一个平台和编译器。 与Linux 32位的GCC(也只有一个调用约定,cdecl)。 所以我试图创build一个dynamic的组装函数来redirect来自触发器的调用,所以我可以使用类方法作为callback(即使与C API库(当然与cdecl))。 我只需要这个来支持指针和本地types(char,int,short等)。
ANYTHING MyRedirect(ANY AMOUNT ARGUMENTS) { return MyClassFunc('this', ANY AMOUNT ARGUMENTS); }
上面的函数是我想要在纯汇编中创build的(在C ++的内存中)。 由于函数非常简单,所以它的ASM也很简单(取决于参数)。
55 push %ebp 89 e5 mov %esp,%ebp 83 ec 04 sub $0x4,%esp 8b 45 08 mov 0x8(%ebp),%eax 89 04 24 mov %eax,(%esp) e8 00 00 00 00 call <address> c9 leave c3 ret
所以在我的程序中,我创build了一个ASM模式生成器(因为我不太了解ASM,所以我search模式)。 这个函数可以通过指定函数所需的参数数量来生成汇编代码(以字节为单位,对于上面的确切情况,即redirect和返回的函数)。 这是我的C ++代码片段。
std::vector<byte> detourFunc(10 + stackSize, 0x90); // Base is 10 bytes + argument size // This becomes 'push %ebp; move %esp, %ebp' detourFunc.push_back(0x55); // push %ebp detourFunc.push_back(0x89); // mov detourFunc.push_back(0xE5); // %esp, %ebp // Check for arguments if(stackSize != 0) { detourFunc.push_back(0x83); // sub detourFunc.push_back(0xEC); // %esp detourFunc.push_back(stackSize); // stack size required // If there are arguments, we want to push them // in the opposite direction (cdecl convention) for(int i = (argumentCount - 1); i >= 0; i--) { // This is what I'm trying to implement // ... } // Check if we need to add 'this' if(m_callbackClassPtr) { } } // This is our call operator detourFunc.push_back(0xE8); // call // All nop, this will be replaced by an address detourFunc.push_back(0x90); // nop detourFunc.push_back(0x90); // nop detourFunc.push_back(0x90); // nop detourFunc.push_back(0x90); // nop if(stackSize == 0) { // In case of no arguments, just 'pop' detourFunc.push_back(0x5D); // pop %ebp } else { // Use 'leave' if we have arguments detourFunc.push_back(0xC9); // leave } // Return function detourFunc.push_back(0xC3); // ret
如果我指定零作为stackSize
这将是输出:
55 push %ebp 89 e5 mov %esp,%ebp e8 90 90 90 90 call <address> 5d pop %ebp c3 ret
正如你所看到的,这是完全有效的32位ASM,如果它没有参数而且不需要“this”指针,它将作为“MyRedirect”。 问题是,我想实现它生成ASM代码的部分,这取决于我指定'redirect'函数将接收的参数的数量。 我已经成功地在我的小C ++程序中完成了这个(破解模式)。
#include <stdio.h> #include <stdlib.h> int main(int argc, char * argv[]) { int val = atoi(argv[1]); printf("\tpush %%ebp\n"); printf("\tmov %%esp,%%ebp\n"); if(val == 0) { printf("\tcall <address>\n"); printf("\tpop %%ebp\n"); } else { printf("\tsub $0x%x,%%esp\n", val * sizeof(int)); for(int i = val; i > 0; i--) { printf("\tmov 0x%x(%%ebp),%%eax\n", i * sizeof(int) + sizeof(int)); printf("\tmov %%eax,0x%x(%%esp)\n", i * sizeof(int) - sizeof(int)); } printf("\tcall <address>\n"); printf("\tleave\n"); } printf("\tret\n"); return 0; }
这个函数打印出与'objdump'生成的ASM代码完全相同的模式。 所以我的问题是, 如果我只想要一个上面的redirect函数,无论这个参数是什么,如果只是在Linux下32位,还是有什么缺陷我都需要知道的话,这在所有情况下都是有效的吗? 例如; 生成的ASM会与“shorts”还是“chars”不同?或者这个工作(我只用整数testing过),以及如果我调用返回“void”的函数(这将如何影响ASM)?
我可能已经解释了一切有点模糊,所以请问,而不是任何误解:)
注意:我不想知道替代scheme,我喜欢当前的实施,并认为这是一个非常有趣的方法,我只是非常感谢您对这个主题的帮助。
编辑:在感兴趣的情况下,这里是一些上述C代码转储: 链接
如丹建议,您需要将内存标记为可执行文件。 我写了一些你可以使用的代码 。 (它可以在GNU / Linux和Windows上运行)。如果你打算永远不支持ARM,x86-64或者其他平台,那么我认为你的代码没有任何问题(可执行部分被添加)应该“总是工作”。 (当然假设一切正常。)
#include <sys/mman.h> ... n = <size of code buffer>; p = mmap(0, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0);
'鱼'建议你使用asmjit 。 我必须同意这一点; 它比你的方法更便携。 但是,你说你对替代品不感兴趣。
您可能对“ Thunking ”(某种)感兴趣。 它基本上试图完成“用C ++方法替换C回调”。 这实际上非常有用,但对于您的应用程序来说并不是一个很好的设计。
希望有所帮助。