我遇到了一个非常特殊的问题。 对于VM,我需要将代码从指令函数复制到ubyte数组,然后执行此数组(技术类似于gcc中的内联宏vm),基本上它的工作原理如下:
__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!");
}
到目前为止,每个简单的指令(add,mul,sub,push0,push1,...)都有效,但如果我尝试使用函数调用获取指令代码,则会抛出错误
我会很高兴并且非常感谢任何帮助。 (顺便说一下,我需要在指令中进行函数调用才能让脚本语言与D进行通信)
答案 0 :(得分: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()
{
}
它对我有用。坦率地说,我不知道你的问题在哪里。您粘贴的大部分代码都无法复制到源文件中并进行编译,因此很难分清哪些内容出错。我希望这里的一些信息可以帮到你。您最有可能必须附加调试器并找出错误的原因;不要吝啬像这样的低级别的东西,而不要弄脏你的手。 ;)
BTW,我在Linux上测试了64位x86。
无论哪种方式,您正在做的事情都是高度不可移植,未定义等等。请注意。它可能会有用,但你正在零保证工作。
答案 1 :(得分:0)
如果没有看到所有相关代码的完全反汇编,很难说出所有的错误,但是......
x86代码通常与位置无关,这意味着将其复制到其他地方并在那里执行它可能并且通常会失败。
您最有可能复制用于删除寄存器(包括ebp
和esp
)以及堆栈内容的代码。