2012-07-15 49 views
1

我遇到了一个非常特殊的问题。 对于VM我需要从该指令的功能拷贝代码到一个UBYTE数组,然后执行该阵列(该TECHNIC类同于GCC内嵌宏VM),基本上它的工作原理是这样的:D内联汇编程序:函数调用出错

__gshared void * sp = null, sb = null; //stack pointer and stack base 

__gshared void add() //the function is just there to access the instruction code 
{ 
    asm{db "INSTRUCTIONCODESTART";} //this is a key to know where the instruction code starts 

    //instruction code here (sample instruction add, pops 2 values from the stack and pushes its result) 
    sp += 4; 
    *cast(uint*)sp += *cast(uint*)(sp - 4); 

    asm{db "INSTRUCTIONCODEEND";} //this is a key to know where instruction code ends 
} 

在Init方法,每个指令代码都有自己的缓冲区,缓冲区中的每个字节都是INSTRUCTIONCODESTART和INSTRUCTIONCODEEND键之间的字节。我通过windows VirtualProtect调用使该数组可执行。到目前为止,一切都按预期工作,但是当我试图做一个函数调用作为指令时,我会得到一个错误。

__gshared void testcall(){} 

__gshared void call() 
{ 
    asm{db "INSTRUCTIONCODESTART";} //this is a key to know where the instruction code starts 

    //instruction code here (just calls a D function) 
    testcall(); //this somehow throws an error 

    asm{db "INSTRUCTIONCODEEND";} //this is a key to know where instruction code ends 
} 

顺便说一句我用下面的代码测试说明

void instructiontest() 
{ 
    uint dummy; 
    ubyte[] buf = getFunctionCode(&add) ~ 0xC3; //gets code of instruction, appends 0xC3 at it ("ret" instruction, for test purposes only to see if it returns to the D code without errors) 
    VirtualProtect(cast(void*)buf, buf.length, PAGE_EXECUTE_READWRITE, &dummy); //makes it executeable 
    dummy = cast(uint)&buf[0]; 
    asm 
    { 
     call dummy[EBP]; 
    } 
    print("instruction worked without errors!"); 
} 

到目前为止,每一个简单的指令(添加,MUL子,push0,push1,...)的作品,但如果我尝试通过函数调用来获取指令的代码,它会抛出一个错误

我会很高兴,非常感谢任何帮助。 (顺便说一句,我需要在指令函数调用为了让脚本语言与沟通d)

+0

仅供参考:'__gshared'对功能,只有变量没有影响。 – 2012-07-15 12:14:12

回答

2

您应该真正反汇编代码,以便清楚地了解它在做什么以及为什么代码被破坏。您call功能的拆装:

0000000000414db8 <_D4test4callFZv>: 
    414db8: 55      push rbp 
    414db9: 48 8b ec    mov rbp,rsp 
    414dbc: 48 83 ec 08    sub rsp,0x8 
    414dc0: 53      push rbx 
    414dc1: 41 54     push r12 
    414dc3: 41 55     push r13 
    414dc5: 41 56     push r14 
    414dc7: 41 57     push r15 
    414dc9: 49      rex.WB 
    414dca: 4e 53     rex.WRX push rbx 
    414dcc: 54      push rsp 
    414dcd: 52      push rdx 
    414dce: 55      push rbp 
    414dcf: 43 54     rex.XB push r12 
    414dd1: 49      rex.WB 
    414dd2: 4f      rex.WRXB 
    414dd3: 4e      rex.WRX 
    414dd4: 43      rex.XB 
    414dd5: 4f      rex.WRXB 
    414dd6: 44      rex.R 
    414dd7: 45 53     rex.RB push r11 
    414dd9: 54      push rsp 
    414dda: 41 52     push r10 
    414ddc: 54      push rsp 
    414ddd: e8 ce ff ff ff   call 414db0 <_D4test8testcallFZv> 
    414de2: 49      rex.WB 
    414de3: 4e 53     rex.WRX push rbx 
    414de5: 54      push rsp 
    414de6: 52      push rdx 
    414de7: 55      push rbp 
    414de8: 43 54     rex.XB push r12 
    414dea: 49      rex.WB 
    414deb: 4f      rex.WRXB 
    414dec: 4e      rex.WRX 
    414ded: 43      rex.XB 
    414dee: 4f      rex.WRXB 
    414def: 44      rex.R 
    414df0: 45      rex.RB 
    414df1: 45      rex.RB 
    414df2: 4e      rex.WRX 
    414df3: 44      rex.R 
    414df4: 41 5f     pop r15 
    414df6: 41 5e     pop r14 
    414df8: 41 5d     pop r13 
    414dfa: 41 5c     pop r12 
    414dfc: 5b      pop rbx 
    414dfd: c9      leave 
    414dfe: c3      ret  
    414dff: 90      nop 

414dc9为开始标志开始的地方,414ddc是在那里结束(含)。 414de2是你的结束标记开始的地方,414df3是它结束的地方(含)。因此,我们有:

0000000000414db8 <_D4test4callFZv>: 
    414db8: 55      push rbp 
    414db9: 48 8b ec    mov rbp,rsp 
    414dbc: 48 83 ec 08    sub rsp,0x8 
    414dc0: 53      push rbx 
    414dc1: 41 54     push r12 
    414dc3: 41 55     push r13 
    414dc5: 41 56     push r14 
    414dc7: 41 57     push r15 
    ; code start marker here 
    414ddd: e8 ce ff ff ff   call 414db0 <_D4test8testcallFZv> 
    ; code end marker here 
    414df4: 41 5f     pop r15 
    414df6: 41 5e     pop r14 
    414df8: 41 5d     pop r13 
    414dfa: 41 5c     pop r12 
    414dfc: 5b      pop rbx 
    414dfd: c9      leave 
    414dfe: c3      ret  
    414dff: 90      nop 

你显然不在这里复制一些序言和结语代码。但是,这本身不应该是非常有问题的。

我试过此程序:

void main() 
{ 
    foo(); 
} 

void foo() 
{ 
    auto addr = &bar; 

    asm { call addr; } 
} 

void bar() 
{ 
    asm { naked; call baz; ret; } 
} 

void baz() 
{ 
} 

这对我的作品。坦率地说,我不知道你的问题在哪里。你粘贴的大部分代码不能被复制到源文件中并被编译,所以很难说出错。我希望这里的一些信息可以帮助你。你很可能不得不附加一个调试器,找出有什么问题;不要期望像这样的低级别的东西混乱,而不会让你的手变脏。;)

顺便说一句,我在Linux上的64位x86测试。

无论哪种方式,你在做什么是非常不可移植的,未定义等被警告。它可能会解决,但你正在做零保证。

+1

实际上,你的反汇编对我有很大的帮助,如果我将“call testcall”复制到另一个内存位置,相对跳转将不再正常工作,所以我的解决方案不是将代码复制到新位置,而是创建一个类在指令代码周围包装(保存我的密钥后的地址),所以我只是在指令键后跳到地址,现在它可以工作 – 2012-07-15 12:34:34

0

这是很难说所有的错误都没有看到的所有相关代码的全拆装,但是......

  1. x86代码通常不是位置独立的,这意味着将其复制到不同的位置并在其中执行它可能并且通常会失败。

  2. 您很可能会复制破坏寄存器(包括ebpesp)和堆栈内容的代码。

+0

以及我确保我不使用任何局部变量(每个变量都用__gshared声明,这意味着它有一个静态位置,所以ebp寄存器不会被破坏),并且对空函数的函数调用应该解散像“打电话测试”,或者我错了吗? – 2012-07-15 11:31:06

+0

我不能说,因为我没有看到所有相关的代码反汇编。 – 2012-07-15 11:32:49