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

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

  • NT6.x系统:运行  bcdedit /set nx alwaysoff
  • NT5.x系统:修改 %systemdrive%\boot.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分享给大家:
     using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices; namespace AhDung.Win32
    {
    /// <summary>
    /// 数据执行保护(DEP)辅助类
    /// </summary>
    public static class DepHelper
    {
    /// <summary>
    /// 获取进程DEP状态
    /// </summary>
    /// <param name="hProcess">进程句柄</param>
    /// <param name="permanently">是否永久。该参数同时指示了是否可以更改进程DEP,为true表示不可修改</param>
    public static ProcessDepFlag GetProcessDepPolicy(IntPtr hProcess, out bool permanently)
    {
    if (!GetProcessDEPPolicy(hProcess, out var lpFlags, out permanently))
    {
    throw new Win32Exception();
    } return (ProcessDepFlag)lpFlags;
    } /// <summary>
    /// 设置进程DEP状态
    /// </summary>
    public static void SetProcessDepPolicy(ProcessDepFlag flag)
    {
    if (!Enum.IsDefined(typeof(ProcessDepFlag), flag))
    {
    throw new InvalidEnumArgumentException();
    } if (!SetProcessDEPPolicy((int)flag))
    {
    throw new Win32Exception();
    }
    } /// <summary>
    /// 获取系统DEP状态
    /// </summary>
    [DllImport("Kernel32.dll", EntryPoint = "GetSystemDEPPolicy")]
    public static extern SystemDepPolicyType GetSystemDepPolicy(); [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool SetProcessDEPPolicy(int dwFlags); [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool GetProcessDEPPolicy(IntPtr hProcess, out int lpFlags, out bool lpPermanent);
    } public enum SystemDepPolicyType
    {
    AlwaysOff,
    AlwaysOn,
    OptIn,
    OptOut
    } public enum ProcessDepFlag
    {
    Disabled,
    PROCESS_DEP_ENABLE,
    PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION
    }
    }

    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#版的实现:

 using System;
using System.ComponentModel;
using System.Runtime.InteropServices; namespace AhDung.Win32
{
/// <summary>
/// COM组件注册辅助类
/// </summary>
public static class ComRegHelper
{
/// <summary>
/// 反注册
/// </summary>
public static void UnRegister(string file)
{
Register(file, false);
} /// <summary>
/// 注册或反注册
/// </summary>
/// <param name="file">文件路径</param>
/// <param name="isRegister">true为注册,false为反注册</param>
/// <remarks>抄于https://limbioliong.wordpress.com/2011/08/11/programmatically-register-com-dlls-in-c/</remarks>
public static void Register(string file, bool isRegister = true)
{
file = Environment.ExpandEnvironmentVariables(file); var hModuleDLL = LoadLibrary(file); if (hModuleDLL == IntPtr.Zero)
{
throw new Win32Exception();
} try
{
// Obtain the required exported API.
var pExportedFunction = GetProcAddress(hModuleDLL, isRegister
? "DllRegisterServer"
: "DllUnregisterServer"); if (pExportedFunction == IntPtr.Zero)
{
throw new Win32Exception();
} // Obtain the delegate from the exported function, whether it be
// DllRegisterServer() or DllUnregisterServer().
var pDelegateRegUnReg =
(DllRegUnRegAPI)Marshal.GetDelegateForFunctionPointer(pExportedFunction, typeof(DllRegUnRegAPI)); // Invoke the delegate.
var hResult = pDelegateRegUnReg(); if (hResult != )
{
throw new Win32Exception();
}
}
finally
{
FreeLibrary(hModuleDLL);
}
} // All COM DLLs must export the DllRegisterServer()
// and the DllUnregisterServer() APIs for self-registration/unregistration.
// They both have the same signature and so only one
// delegate is required.
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate uint DllRegUnRegAPI(); [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string strLibraryName); [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
static extern int FreeLibrary(IntPtr hModule); [DllImport("Kernel32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
}
}

ComRegHelper.cs

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

-文毕-

DLL/OCX文件的注册与数据执行保护DEP的更多相关文章

  1. 关闭Wind XP/Vista/Win7的DEP数据执行保护汇总(转)

    数据执行保护 (DEP) 是一种Windows安全机制,从Windows版本顺序上看是从Windows XP SP2开始引入,通过监视程序以确保它们使用的系统内存是安全的,帮助防止操作系统受到病毒和其 ...

  2. DEP(Data Execution Prevention) 数据执行保护

    1.原理 数据执行保护,简称“DEP”,英文全称为“Data Execution Prevention”,是一组在存储器上运行额外检查的硬件和软件技术,有助于防止恶意程序码在系统上运行. 此技术由Mi ...

  3. C#自动实现Dll(OCX)控件注册的两种方法

    尽管MS为我们提供了丰富的.net framework库,我们的程序C#开发带来了极大的便利,但是有时候,一些特定功能的控件库还是需要由第三方提供或是自己编写.当需要用到Dll引用的时候,我们通常会通 ...

  4. 数据执行保护呈灰色无法开启 用命令BCEDIT无效 请问怎么解决?

    1.默认的管理员账号是关闭开不了的. 2.你重新创建个管理员账户. 3.然后登陆这个账号. 4.然后打开cmd 输入 bcdedit /set nx optin alwaysoff 这行指令.然后回车 ...

  5. Dll,OCX控件的注册(转)

    原文地址:http://home.51.com/heiyijiushi/diary/item/10053693.html C#自动实现Dll(OCX)控件注册的两种方法2012-07-31 21:20 ...

  6. C#实现Dll(OCX)控件自动注册的两种方法 网上找的 然后 自己试了试 还是可以用的

    尽管MS为我们提供了丰富的.net framework库,我们的程序C#开发带来了极大的便利,但是有时候,一些特定功能的控件库还是需要由第三方提供或是自己编写.当需要用到Dll引用的时候,我们通常会通 ...

  7. C#实现Dll(OCX)控件自动注册的两种方法

    尽管MS为我们提供了丰富的.net framework库,我们的程序C#开发带来了极大的便利,但是有时候,一些特定功能的控件库还是需要由第三方提供或是自己编写.当需要用到Dll引用的时候,我们通常会通 ...

  8. ocx文件转换成C#程序引用的DLL

    将ocx文件转换成C#程序引用的DLL文件的办法  将ocx文件转换成C#程序引用的DLL文件的办法,需要的朋友可以参考一下  1.打开VS2008或VS2010命令提示符(此例用VS2008) 将o ...

  9. 将DLL文件直接封装进exe执行文件中(C#)

    前言:由于项目需要,需制作一个注册机,将个人PC的MAC值和硬盘序列号与软件进行绑定,由于笔者的C++不是很好,所以采用C#进行开发.但在采用C#的时候,获取硬盘的MAC值和序列号的时候又不是很准确, ...

随机推荐

  1. Java 中,如何对日期进行加减操作

    今天在做项目时,遇到了对时间的加减进行操作的需求,根据传入的日期字符串,操作参数("+","-"),加数(要操作的天数),对日期进行加减操作,经查询资料,自己写 ...

  2. STL vector容器 和deque容器

    前言 STL是C++的框架,然后vector容器和deque容器又是STL的一部分... 这块的内容都是理解.概念为主,没什么捷径,希望读者能静下来记. 先来讲vector容器(单端动态数组) 1.v ...

  3. Mysql性能优化:什么是索引下推?

    导读 索引下推(index condition pushdown )简称ICP,在Mysql5.6的版本上推出,用于优化查询. 在不使用ICP的情况下,在使用非主键索引(又叫普通索引或者二级索引)进行 ...

  4. C++实现秒表

    完整代码下载 思路概括:如果有键按下,判断按下的是什么键并处理.没有键按下,计时.传统的Sleep无法满足秒表精确到百毫秒的需求,这里使用更精确的clock,clock的作用是统计从程序开始运行到现在 ...

  5. POJ旅行商问题——解题报告

    旅行商问题 总时间限制: 1000ms 内存限制: 65536kB 描述 某国家有n(1<=n<=10)座城市,给定任意两座城市间距离(不超过1000的非负整数).一个旅行商人希望访问每座 ...

  6. [洛谷1437&Codevs1257]敲砖块<恶心的dp>

    题目链接:https://www.luogu.org/problem/show?pid=1437#sub http://codevs.cn/problem/1257/ 不得不说,这个题非常的恶心,在初 ...

  7. Javascript/JQuery遇到的bug

    这次遇到一个js内部文件报错的,找了下不是我们的代码问题.我突然想到了,我使用的Umeditor有个内置的jquery,然后我也把它加到我们的项目里面,但是我查看了我们的项目,是引用过一个jquery ...

  8. js定时器及定时器叠加问题

    回武汉隔离的第二天打卡,武汉加油,逆战必胜!今天想和大家简单聊一下js定时器的问题. 1.setTimeout 延时器 在指定时间后执行一次,注意只会执行一次 当然有的时候我们想用延时器做出定时器的效 ...

  9. B. Food Buying Round #617(递归 + 模拟)

    B. Food Buying time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  10. Java程序员拥有这些工具,还怕干不出好活

    老话说,工欲善其事,必先利其器. 那么,作为编程人员,你都用过哪些“兵器”呢?你的”兵器“够”锋利”吗? 近期,有很多新朋友问,都有什么画流程图的工具,给推荐推荐? 索性,就静下来,好好梳理一下,从事 ...