很久之前整理了一篇《
C# 调用非托管程序》文章,在
博客园zhongzf同学《
在.net程序中嵌入asm汇编代码》进行了简单的讨论,现在才有时间整理。
《C# 调用非托管程序》最后一种方法通俗的讲是构造符合汇编代码(机器代码)格式的数据,把该数据当作可执行代码执行。Windows提供了
DEP(Data Execution Prevention 数据执行保护)机制,也就是Windows会试图阻止程序运行非可执行内存区域的可执行代码。如果开启DEP,《C# 调用非托管程序》一文中最后一种方法执行会失败;如果关闭DEP,Windows XP SP2中执行该代码会成功,但Vista/Win7由于安全性增强的原因,该代码执行会失败。
是不是这用嵌入汇编代码的方式在开启DEP及Vista/Win7中一定不能使用呢?答案在下面分析中得出。
如果我们把保存汇编代码的内存区域标记为可执行,上面的方法也许可用。
Win API
VirtualAlloc中有参数,可以指定新分配内存的权限。
使用完内存后,调用
VirtualFree释放。
沿着这样的思路,将《C# 调用非托管程序》一文中最后一种方法修改如下(篇幅原因简化了注释):
/*
修改记录
2008-5-11 8:07 曲滨
>> 基本实现预期功能
[!] 明天进行优化
2008-5-12 15:54 曲滨
[E] 优化完成
[N] 加入 NativeCodeHelper 类便于使用
2010-6-17 周振兴
修改兼容性,可在开启DEP及Vista/Win7中运行。
*/
namespace
NShellNativeCode
{
using
System;
using
System.Runtime.InteropServices;
delegate
int
AddProc(
int
p1,
int
p2);
class
Program
{
static
void
Main(
string
[] args)
{
byte
[] codeBytes
=
{
0x8B
,
0x44
,
0x24
,
0x08
//
mov eax,[esp+08h]
,
0x8B
,
0x4C
,
0x24
,
0x04
//
mov ecx,[esp+04h]
,
0x03
,
0xC1
//
add eax,ecx
,
0xC3
//
ret
};
/*
上面的字节数组,就是下面函数的本机代码;
int add(int x,int y) {
return x+y;
}
*/
IntPtr handle
=
IntPtr.Zero;
handle
=
VirtualAlloc(
IntPtr.Zero,
codeBytes.Length,
MEM_COMMIT
|
MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
try
{
Marshal.Copy(codeBytes,
0
, handle, codeBytes.Length);
AddProc add
=
Marshal.GetDelegateForFunctionPointer(handle,
typeof
(AddProc))
as
AddProc;
int
r
=
add(
1976
,
1
);
Console.WriteLine(
"
本机代码返回:{0}
"
, r);
}
finally
{
VirtualFree(handle,
0
, MEM_RELEASE);
}
Console.ReadLine();
}
//
Windows API
[DllImport(
"
Kernel32.dll
"
, EntryPoint
=
"
VirtualAlloc
"
)]
public
static
extern
IntPtr VirtualAlloc(IntPtr address,
int
size,
uint
allocType,
uint
protect);
[DllImport(
"
Kernel32.dll
"
, EntryPoint
=
"
VirtualFree
"
)]
public
static
extern
bool
VirtualFree(IntPtr address,
int
size,
uint
freeType);
//
flags
const
uint
MEM_COMMIT
=
0x1000
;
const
uint
MEM_RESERVE
=
0x2000
;
const
uint
PAGE_EXECUTE_READWRITE
=
0x40
;
const
uint
MEM_RELEASE
=
0x8000
;
}
}
事实证明,这种嵌入汇编代码的方式在开启DEP及Vista/Win7可运行。
Win7 (X86)开启DEP环境下测试通过。
注:codeBytes数组中是X86汇编代码,如果要在X64中运行,需修改该代码!