2012-07-15 51 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