zoukankan      html  css  js  c++  java
  • DLL/OCX文件的注册与数据执行保护DEP

    注册/反注册dll或ocx文件时,无论是用regsvr32还是DllRegisterServer/DllUnregisterServer,可能会遇到【内存位置访问无效】的问题:

    此时把操作系统的数据执行保护(Data Execution Prevention,下称DEP)彻底关掉,重启,应该就能解决问题。操作:

    • NT6.x系统:运行  bcdedit /set nx alwaysoff 
    • NT5.x系统:修改 %systemdrive%oot.ini 文件,将当前操作系统条目的/noexecute参数的值改为AlwaysOff,若没有则添加。若是多系统,要注意修改到正确的条目

    本文主要是讨论,作为开发者,当需要在自己的程序中注册dll时(反注册的情况一样,下文只拿注册说事,其实适用于所有受DEP影响的问题),如何避免改动系统DEP,避免重启地把问题解决掉。其实这个问题的关键是,执行注册的进程是否启用了DEP,启用就不能注册,关闭就能,跟系统DEP没有直接关系,但进程DEP受系统DEP的影响。

    来自系统DEP的原因

    系统DEP策略有4种,每种策略下对进程DEP的影响如下(注意,64位程序总是启用DEP,且不可禁用,不论系统DEP如何设置。所以下表和接下来说的都是32位程序的情况):

    系统DEP策略 进程默认DEP 能否更改进程DEP
    OptIn (仅为基本的Windows程序和服务启用DEP;默认策略) 关闭 允许更改
    OptOut (除指定的程序外,全部启用DEP) 开启 允许更改
    AlwaysOn (全部启用DEP) 开启 不允许更改
    AlwaysOff (全部禁用DEP) 关闭 不允许更改

    我们的目的是关闭进程DEP。可以看到,系统DEP为OptIn和AlwaysOff时,进程DEP已经是默认关闭的。其余两种情况中,OptOut虽然是默认开启DEP,但它允许被关闭,所以这是一种可以抢救的情况,唯独AlwaysOn不可抢救,必须修改系统DEP策略并重启。

    插播一下,系统默认的策略是OptIn,这种策略下普通程序的DEP是关闭的,但是系统程序例外,所以在程序中调用regsvr32进行注册会失败,因为实际执行注册的进程是regsvr32而非自己的程序,而regsvr32是系统程序,它在OptIn下是会被开启DEP的。可以在任务管理器进程页面中添加【数据执行保护】列,以呈现进程DEP的开闭情况。

    小结:

    • 系统DEP有4种,除了AlwaysOn,其余3种都可以让(或已经是)自己的程序处于DEP关闭状态;
    • 系统DEP可以用GetSystemDEPPolicy API获取,没有发现用于设置的API;
    • 进程DEP可以用GetProcessDEPPolicy获取,同时该函数还返回可否修改DEP的信息,如果是可修改,则可以用SetProcessDEPPolicy更改自身DEP状态;
    • 一个简易的DepHelper分享给大家:
       1 using System;
       2 using System.ComponentModel;
       3 using System.Runtime.InteropServices;
       4 
       5 namespace AhDung.Win32
       6 {
       7     /// <summary>
       8     /// 数据执行保护(DEP)辅助类
       9     /// </summary>
      10     public static class DepHelper
      11     {
      12         /// <summary>
      13         /// 获取进程DEP状态
      14         /// </summary>
      15         /// <param name="hProcess">进程句柄</param>
      16         /// <param name="permanently">是否永久。该参数同时指示了是否可以更改进程DEP,为true表示不可修改</param>
      17         public static ProcessDepFlag GetProcessDepPolicy(IntPtr hProcess, out bool permanently)
      18         {
      19             if (!GetProcessDEPPolicy(hProcess, out var lpFlags, out permanently))
      20             {
      21                 throw new Win32Exception();
      22             }
      23 
      24             return (ProcessDepFlag)lpFlags;
      25         }
      26 
      27         /// <summary>
      28         /// 设置进程DEP状态
      29         /// </summary>
      30         public static void SetProcessDepPolicy(ProcessDepFlag flag)
      31         {
      32             if (!Enum.IsDefined(typeof(ProcessDepFlag), flag))
      33             {
      34                 throw new InvalidEnumArgumentException();
      35             }
      36 
      37             if (!SetProcessDEPPolicy((int)flag))
      38             {
      39                 throw new Win32Exception();
      40             }
      41         }
      42 
      43         /// <summary>
      44         /// 获取系统DEP状态
      45         /// </summary>
      46         [DllImport("Kernel32.dll", EntryPoint = "GetSystemDEPPolicy")]
      47         public static extern SystemDepPolicyType GetSystemDepPolicy();
      48 
      49         [DllImport("kernel32.dll", SetLastError = true)]
      50         static extern bool SetProcessDEPPolicy(int dwFlags);
      51 
      52         [DllImport("kernel32.dll", SetLastError = true)]
      53         static extern bool GetProcessDEPPolicy(IntPtr hProcess, out int lpFlags, out bool lpPermanent);
      54     }
      55 
      56     public enum SystemDepPolicyType
      57     {
      58         AlwaysOff,
      59         AlwaysOn,
      60         OptIn,
      61         OptOut
      62     }
      63 
      64     public enum ProcessDepFlag
      65     {
      66         Disabled,
      67         PROCESS_DEP_ENABLE,
      68         PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
      69     }
      70 }
      DepHelper.cs

    基本上,由于系统默认策略是OptIn,我们的32程序按说不太会遇到DEP的问题,但事情并没有这么简单,尤其作为.Neter,我们往往会遇到来自另一方面的原因。

    来自PE标记的原因

    如果系统DEP已经是OptIn或OptOut,程序也是32位,根据上面的表格,按说程序DEP应该是处于关闭或可以关闭的状态,但它偏偏处于开启状态,并且不可更改。这是因为,程序DEP除了会受系统DEP影响外,还会受程序文件PE头中的一个标记影响。

    这个标记叫IMAGE_DLLCHARACTERISTICS_NX_COMPAT,有些地方叫NX Compatible。

    标记的意思大概是标识该PE文件是否是DEP兼容,这里不必深究该标记的明确意义及其所在位置,只要知道拥有该标记的exe程序,会在除AlwaysOff外的情况下开启DEP且不可关闭,而据称在.net framework 2.0 SP1后,.net的编译器就会为生成的PE文件打上该标记,导致今天的.net程序,相信大多都是携带该标记的。

    VC构建工具中有个叫editbin.exe的命令行工具,可以简单移除该标记:

    editbin.exe /NXCOMPAT:NO xxx.exe

    如果安装VS时没装c++相关的东西,那你的电脑可能没有这个工具,可以选择把c++组件装上,或搜单独的vc build tools装上。

    说回DLL注册

    上面说过如果是调用regsvr32来注册的,那就算搞掂程序的DEP也无济于事,需要考虑用regsvr32以外的办法进行注册,比如直接调用dll的DllRegisterServer函数,事实上regsvr32也应该是这么搞。下面是c#版的实现:

     1 using System;
     2 using System.ComponentModel;
     3 using System.Runtime.InteropServices;
     4 
     5 namespace AhDung.Win32
     6 {
     7     /// <summary>
     8     /// COM组件注册辅助类
     9     /// </summary>
    10     public static class ComRegHelper
    11     {
    12         /// <summary>
    13         /// 反注册
    14         /// </summary>
    15         public static void UnRegister(string file)
    16         {
    17             Register(file, false);
    18         }
    19 
    20         /// <summary>
    21         /// 注册或反注册
    22         /// </summary>
    23         /// <param name="file">文件路径</param>
    24         /// <param name="isRegister">true为注册,false为反注册</param>
    25         /// <remarks>抄于https://limbioliong.wordpress.com/2011/08/11/programmatically-register-com-dlls-in-c/</remarks>
    26         public static void Register(string file, bool isRegister = true)
    27         {
    28             file = Environment.ExpandEnvironmentVariables(file);
    29 
    30             var hModuleDLL = LoadLibrary(file);
    31 
    32             if (hModuleDLL == IntPtr.Zero)
    33             {
    34                 throw new Win32Exception();
    35             }
    36 
    37             try
    38             {
    39                 // Obtain the required exported API.
    40                 var pExportedFunction = GetProcAddress(hModuleDLL, isRegister
    41                     ? "DllRegisterServer"
    42                     : "DllUnregisterServer");
    43 
    44                 if (pExportedFunction == IntPtr.Zero)
    45                 {
    46                     throw new Win32Exception();
    47                 }
    48 
    49                 // Obtain the delegate from the exported function, whether it be
    50                 // DllRegisterServer() or DllUnregisterServer().
    51                 var pDelegateRegUnReg =
    52                     (DllRegUnRegAPI)Marshal.GetDelegateForFunctionPointer(pExportedFunction, typeof(DllRegUnRegAPI));
    53 
    54                 // Invoke the delegate.
    55                 var hResult = pDelegateRegUnReg();
    56 
    57                 if (hResult != 0)
    58                 {
    59                     throw new Win32Exception();
    60                 }
    61             }
    62             finally
    63             {
    64                 FreeLibrary(hModuleDLL);
    65             }
    66         }
    67 
    68         // All COM DLLs must export the DllRegisterServer()
    69         // and the DllUnregisterServer() APIs for self-registration/unregistration.
    70         // They both have the same signature and so only one
    71         // delegate is required.
    72         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    73         delegate uint DllRegUnRegAPI();
    74 
    75         [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    76         static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string strLibraryName);
    77 
    78         [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    79         static extern int FreeLibrary(IntPtr hModule);
    80 
    81         [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    82         static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
    83     }
    84 }
    ComRegHelper.cs

    如果不关DEP,LoadLibrary就会遇到问题。

    -文毕-

  • 相关阅读:
    PBRT笔记(3)——KD树
    PBRT笔记(2)——BVH
    PBRT笔记(1)——主循环、浮点误差
    《Ray Tracing in One Weekend》、《Ray Tracing from the Ground Up》读后感以及光线追踪学习推荐
    在Node.js中使用ffi调用dll
    Node.js c++ 扩展之HelloWorld
    在Qt中配置TBB以及简单实用
    对《将Unreal4打包后的工程嵌入到Qt或者桌面中》一文的补充
    QtQuick大坑笔记之Http的Get与Post操作(带cookie)
    QtQuick自定义主题以及控件样式指引
  • 原文地址:https://www.cnblogs.com/ahdung/p/dllreganddep.html
Copyright © 2011-2022 走看看