1

我目前正在开发一个简单的应用程序来捕获 OutputDebugString 消息(类似于Windows Sysinternals DbgViewDBMon.NET)。从本地会话(即 Local\DBWIN_BUFFER_READY、Local\DBWIN_DATA_READY 和 Local\DBWIN_BUFFER)访问 OutputDebugString 消息时,一切都按预期工作。

但是,当我尝试访问会话 0 的任何输出(即 Global\DBWIN_BUFFER_READY 等)时,我没有收到任何输出。基于 DbgView 的行为,我假设应用程序必须以某种级别的管理权限运行。我在想我错误地配置了 SecurityDescriptor,或者我完全丢失了一些东西来访问 Global OutputDebugString 消息(读作......我现在对此事有些迷茫)。

我在下面突出显示了代码片段,但可以在CodePlex上找到完整的源代码

任何有关此事的帮助或见解将不胜感激。提前致谢!

安全描述符配置

我尝试了几种不同的配置,但提交的代码目前如下所示。

[DllImport("advapi32.dll", SetLastError = true)]
private static extern Boolean InitializeSecurityDescriptor(ref SecurityDescriptor sd, UInt32 dwRevision);

[DllImport("advapi32.dll", SetLastError = true)]
private static extern Boolean SetSecurityDescriptorDacl(ref SecurityDescriptor sd, Boolean daclPresent, IntPtr dacl, Boolean daclDefaulted);

public SecurityDescriptor InitializeSecurityDescriptor()
{
  const Int32 securityDescriptorRevision = 1;
  var securityDescriptor = new SecurityDescriptor();

  // Initialize the security descriptor.
  if (!InitializeSecurityDescriptor(ref securityDescriptor, securityDescriptorRevision))
    throw new Win32Exception(Marshal.GetLastWin32Error());

  // Set information in a discretionary access control list
  if (!SetSecurityDescriptorDacl(ref securityDescriptor, true, IntPtr.Zero, false))
    throw new Win32Exception(Marshal.GetLastWin32Error());

  return securityDescriptor;
}

如您所料,此代码最终在我的 DbWinMessageSource 类的设置中被调用...

_windowsApi.Advanced.InitializeSecurityDescriptor();

安全属性和事件

目前在 CodePlex 上提交的代码使用 Local\** 前缀,但据我所知,唯一的区别应该是 Local\** 替换为 Global\** ?然而,这似乎并没有按预期捕获输出。同样,相关的代码片段......

  public const Int32 ErrorAlreadyExists = 183;

  [DllImport("kernel32.dll", SetLastError = true)]
  private static extern IntPtr CreateEvent(ref SecurityAttributes sa, Boolean bManualReset, Boolean bInitialState, String lpName);

  public Handle CreateLocalEvent(ref SecurityAttributes securityAttributes, String objectName)
    {
      Verify.NotWhitespace(objectName);
      return CreateEvent(ref securityAttributes, "Local", objectName);
    }

    public Handle CreateGlobalEvent(ref SecurityAttributes securityAttributes, String objectName)
    {
      Verify.NotWhitespace(objectName);
      return CreateEvent(ref securityAttributes, "Global", objectName);
    }

    private static Handle CreateEvent(ref SecurityAttributes securityAttributes, String objectNamePrefix, String objectName)
    {
      IntPtr handle = CreateEvent(ref securityAttributes, false, false, String.Format(@"{0}\{1}", objectNamePrefix, objectName));

      if(Marshal.GetLastWin32Error() == ErrorAlreadyExists)
        throw new Win32Exception(ErrorAlreadyExists);

      if (handle == IntPtr.Zero)
        throw new Win32Exception(Marshal.GetLastWin32Error());

      return new Handle(handle, CloseHandle);
    }

同样,最终在 DbWinMessageSource 的设置中调用如下:

  _dbwinBufferReadyEvent = _windowsApi.Basic.CreateGlobalEvent(ref securityAttributes, "DBWIN_BUFFER_READY");
  _dbwinDataReadyEvent = _windowsApi.Basic.CreateGlobalEvent(ref securityAttributes, "DBWIN_DATA_READY");
  _dbwinBufferFile = _windowsApi.Basic.CreateGlobalFileMapping(ref securityAttributes, "DBWIN_BUFFER");

我正在使用配置了 OutputDebugString 附加程序的 Log4Net 使用简单的 Web 应用程序进行测试。当我通过 Visual Studio 运行应用程序时,我会按预期捕获所有本地输出。当我将应用程序移入 IIS 并配置代码以捕获全局会话中的任何内容时;我什么都得不到。我已经确认 DbgView 正在捕获 IIS 的输出,正如我所期望的那样(所以这绝对是我做错的事情)。

希望这是足够的上下文,但如果需要更多信息或细节;让我知道。

注意:在 Windows 7 Professional 上进行开发,如果这有影响的话。

编辑

正如 Tyranid(和 Luke)所指出的,所需要的只是管理员权限和 SE_CREATE_GLOBAL_NAME。我又进行了一些测试,上面的代码设置实际上是在捕获一些全局消息(例如,在 IISRESET 期间);但是,当应用程序在 IIS 内部运行时(在 VS 会话期间通过 Local\** 路由),上面的代码不会从 Log4Net OutputDebugString 附加程序捕获任何数据。所有 Win32 api 调用都成功返回,调用时也没有返回任何错误。Marshal.GetLastWin32Error().为了更好地衡量,我添加了一些代码以确保当前 Windows 令牌具有 SE_CREATE_GLOBAL_NAME。粗略的代码如下所示:

  using (var identity = WindowsIdentity.GetCurrent())
  {
    if (identity == null)
      return;

    TokenPrivilege tp;

    tp.Count = 1;
    tp.Luid = 0;
    tp.Attr = SE_PRIVILEGE_ENABLED;
    if (!LookupPrivilegeValue(null, SE_CREATE_GLOBAL_NAME, ref tp.Luid))
      throw new Win32Exception(Marshal.GetLastWin32Error());

    if (!AdjustTokenPrivileges(identity.Token, false, ref tp, Marshal.SizeOf(tp), IntPtr.Zero, IntPtr.Zero))
      throw new Win32Exception(Marshal.GetLastWin32Error());
  }

对此的任何进一步见解将不胜感激。

4

1 回答 1

2

您确定您正在设法创建节/文件映射对象吗?是的,您需要成为管理员才能执行此操作,因为在全局命名空间中创建文件映射需要您的令牌中的 SeCreateGlobalPrivilege。现在,您可能需要在调用 CreateFileMapping 之前启用它,但我已经有一段时间没有使用它并且没有我的代码可供使用。

哦,你真的不应该指定一个 NULL DACL,它在很多层面上都是错误的,我知道 dbgview 会这样做,但它只是懒惰,而且当涉及 vista+ 的完整性级别时它可能会中断。

于 2011-02-20T15:58:06.010 回答