飘遥的Blog

C/C++/.NET
posts - 126, comments - 184, trackbacks - 9, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理
很久之前整理了一篇《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中运行,需修改该代码!
标签: VirtualAlloc, DEP

Feedback

#1楼  回复 引用 查看   

2010-06-22 17:28 by ocean      
有问题吧?在Win7下,如果关闭DEP,那么数据区的代码仍然可以执行。你不能执行的原因是因为你的编译器里面设置了DEP开启。你可以看看你的Visual Studio2008的c++编译器中有一个专门的DEP选项,只有关掉这个选项才可以。

我没有用C#测试过,因为C#不能执行数据区代码,但是我用C++测试过,在VC++ 2008下,只要编译选项的DEP关掉,并且操作系统的DEP关掉,那么执行数据区代码是可以的。

#2楼[楼主]  回复 引用 查看   

2010-06-22 17:51 by 飘遥(Zhenxing Zhou)      
@ocean
多谢指出,确实没有考虑到这一点。刚查了一下,PE文件中有个IMAGE_DLLCHARACTERISTICS_NX_COMPAT标记位,如果设置了该位,则禁止执行数据区代码,而.NET 3.5编译器编译出的PE文件默认设置了该位。
参考:http://blogs.msdn.com/b/ed_maurer/archive/2007/12/14/nxcompat-and-the-c-compiler.aspx

是我的错,但解决方法应该没有错。
回去再在XP上测一下,看是否有影响,稍后会更新一下。
Thanks.

#3楼  回复 引用 查看   

2010-06-22 18:09 by +-+      
专业用语请叫它:shellcode

#4楼  回复 引用 查看   

2010-06-22 18:11 by ocean      
XP上不受影响,无论编译器是否设置了DEP,只要XP的DEP关闭掉了,就万事大吉了。VS2008默认是设置了DEP编译的。

#5楼  回复 引用 查看   

2010-06-22 19:10 by pandaren      
-_-! 新家新气象啊

#6楼  回复 引用 查看   

2010-06-22 21:55 by 玄魂      
shellCode,很好很强大

#7楼  回复 引用 查看   

2010-06-22 22:09 by 曹宗颖      
是一种思路
牛B人还真多啊,呵呵。

#9楼  回复 引用 查看   

2010-06-23 00:54 by 大石头      
要是有个汇编引擎就好了,即时解析汇编代码

#10楼  回复 引用 查看   

2010-06-23 09:26 by +-+      
DASM库就可以的。

#11楼[楼主]  回复 引用 查看   

2010-06-23 10:07 by 飘遥(Zhenxing Zhou)      
@ocean
经测试,http://www.xianfen.net/Article90.aspx 最后一种方法,在关闭DEP时,XP(SP2)和Win7表现不一致!
究其原因,应该是XP会忽略掉PE文件中IMAGE_DLLCHARACTERISTICS_NX_COMPAT标识,也就是Win7对应用程序提供双重约束:DEP系统策略和应用本身PE文件的IMAGE_DLLCHARACTERISTICS_NX_COMPAT标记;而XP中,应用程序只受DEP系统策略约束。

#12楼  回复 引用 查看   

2010-06-23 12:11 by 周银辉      
关于C#嵌汇编,弱弱地一问,我试了这个 http://www.atrevido.net/blog/PermaLink.aspx?guid=ac03f447-d487-45a6-8119-dc4fa1e932e1
但运行时为啥报错“试图访问受保护的内存”,如何避免呢?谢谢咯

#13楼  回复 引用 查看   

2010-06-23 12:15 by IT鸟      
@大石头
可以搞cpu了。

#14楼[楼主]  回复 引用 查看   

2010-06-23 12:56 by 飘遥(Zhenxing Zhou)      
@周银辉
Windows会阻止执行数据区的代码,而http://www.atrevido.net/blog/PermaLink.aspx?guid=ac03f447-d487-45a6-8119-dc4fa1e932e1 中,asmBytes存储在数据区中,所以运行失败。
本文中的代码就是一种解决方法;另外的一种解决方法是关闭DEP,并且用VS带的Editbin.exe修改PE文件的IMAGE_DLLCHARACTERISTICS_NX_COMPAT标识(这种方法不太好)。
原因见文章和楼主回复。
不好意思,分析的比较乱,不知大侠能否看懂^_^

#15楼  回复 引用 查看   

2010-06-23 13:17 by 夜闻香      
此文太给力了,先收藏一下。

#16楼  回复 引用 查看   

2010-06-23 13:27 by +-+      
引用周银辉:
关于C#嵌汇编,弱弱地一问,我试了这个 http://www.atrevido.net/blog/PermaLink.aspx?guid=ac03f447-d487-45a6-8119-dc4fa1e932e1
但运行时为啥报错“试图访问受保护的内存”,如何避免呢?谢谢咯


改变为分配SHELLCODE的字节码的CPU的页面属性为可执行就可以了-》NtVirtualProtectEx