C# inline-asm / 嵌入x86匯編,
C#可不可以嵌入匯編 可以 在我眼中C#作為一個介於中上層語言是不可能不可以
置入匯編代碼的 為什麼會被我認為中上層語言呢 從C#保留指針就可以看出 我知
道有很多人一定不會相信C#可以使用匯編代碼 不過C#會比較麻煩C#不可以直接
內聯匯編(inline-asm)准確的說C#只可以使用(auto-asm)動態匯編 這種技術不是
C#獨有的 易語言、VB、C++ 三種語言都可以 不過動態匯編我見過最多的是被應
用在外掛方面 及遠程匯編注入 實際上是屬於動態匯編技術的一種擴展 不過很難
說JIT在編譯代碼後是通過在遠程把匯編代碼寫入托管進程執行的 又或者說是一種
寄生在外殼程序中運行的技術及“內存運行” 懶得討論這些一想到就頭大。
從上圖中你可以看見一份簡單的x86 / call匯編在C#中內嵌並被調用
執行一看你會發現並不是太難 我的一篇博文 寫了一大堆廢話就是說
這個東西不過是易語言的 http://blog.csdn.net/u012395622/article/details/46400569
我們知道軟件運行時所有代碼會放在虛擬內存中 而可執行的代碼在內存中
內存保護一般是PAGE_EXECUTE_READ及32不過經過我研究.NET上的
可執行代碼應該是PAGE_EXECUTE_READWIRTE及64 如果是P/invoke
上執行DLL中的保護是32 就可以我們在內嵌匯編時不可以使用只讀保護
如果我們需要使用由.NET去委托去Call那麼必須是可讀可寫 如果通過Win32
API去Call那麼使用32就可以 有些區別 、我曾研究過易語言上字節集在內存
中的內存保護到底是多少結果與C#是一致 4 / PAGE_READWRITE 不過為什
麼易語言可以CALL而C#不可以CALL一直是讓我感到較為迷惑的事情 可能是
托管堆與非托管堆之間不同造成的 不過我更希望有大神出來幫忙指點一下下。
由於是X86匯編 首先需要把目標平台切換為x86 這樣才不會造成C#調用匯編
代碼時出錯 一定不要省略這個步驟
首先你需要定義一個有參數的委托 重點在於在匯編中有這樣一句話
call dword ptr[ebp+8] // call 參數一
[csharp] view plaincopy
- [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- public delegate IntPtr CallMethod(IntPtr ptr);
由於是在VC下內聯的匯編 最後移植到C# 一般在VC下函數的調用方式是cdcel
何況下面的是按照cdcel導出函數格式進行的 所以不可以使用__stdcall的方式
[csharp] view plaincopy
- [STAThread]
- static void Main(string[] args)
- {
- byte[] buf_asm = {
- // push ebp
- // mov ebp,esp
- // sub esp,0C0h
- // push ebx
- // push esi
- // push edi
- // lea edi,[ebp-0C0h]
- // mov ecx,30h
- // mov eax,0CCCCCCCCh
- // rep stos dword ptr es:[edi]
- 85, 139, 236, 129, 236, 192, 0, 0, 0, 83, 86, 87, 141, 189, 64,
- 255, 255, 255, 185, 48, 0, 0, 0, 184, 204, 204, 204, 204, 243, 171,
- // call dword ptr[ebp+8]
- 255, 85, 8,
- // pop edi
- // pop esi
- // pop ebx
- // mov esp,ebp
- // pop ebp
- // ret
- 95, 94, 91, 139, 229, 93, 195
- };
- IntPtr ptr_asm = SetHandleCount(buf_asm);
- VirtualProtect(ptr_asm, buf_asm.Length);
- CallMethod call_method = Marshal.GetDelegateForFunctionPointer(ptr_asm, typeof(CallMethod)) as CallMethod;
- call_method(Marshal.GetFunctionPointerForDelegate(new Action(Hello_x86)));
- }
首先把你需要嵌入的匯編以字節數組的格式寫出來 然後通過
SetHandleCount函數是用於取地址指針的
[csharp] view plaincopy
- static void VirtualProtect(IntPtr ptr, int size)
- {
- int outMemProtect;
- if (!VirtualProtect(ptr, size, 64, out outMemProtect))
- throw new Exception("Unable to modify memory protection.");
- }
上面的函數用於修改內存保護 不過是為了讓委托可以進行交互 包括匯編代碼可以被互調用
[csharp] view plaincopy
- static void Hello_x86()
- {
- Console.Title = ((new StackFrame()).GetMethod()).Name;
- Console.WriteLine("I was x86 assembly call a test function.");
- Console.ReadKey(false);
- }
上面的函數是一個測試函數 這個函數沒有太大意義 只是表現利用了匯編調用
本函數 然後本函數輸出一個回應的信息 用於提示該函數被寫入內存匯編調用
依賴的外部函數
[csharp] view plaincopy
- [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
- public static extern IntPtr SetHandleCount(byte[] value);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool VirtualProtect(IntPtr lpAddress, int dwSize, int flNewProtect, out int lpflOldProtect);
依賴的命名空間
[csharp] view plaincopy
- using System;
- using System.Runtime.InteropServices;
- using System.Diagnostics;
版權聲明:本文為博主原創文章,未經博主允許不得轉載。