我目前正在开发一个简单的应用程序来捕获OutputDebugString消息(类似于Windows Sysinternals DbgView或DBMon.NET)。从本地会话访问OutputDebugString消息时,一切都按预期工作(即Local \ DBWIN_BUFFER_READY,Local \ DBWIN_DATA_READY和Local \ DBWIN_BUFFER)。
但是,当我尝试访问会话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();
SecurityAttributes and Events
目前在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");
我正在使用Log4Net和配置了OutputDebugString appender的简单Web应用程序进行测试。当我通过Visual Studio运行应用程序时,我按预期捕获所有本地输出。当我将应用程序移动到IIS并配置代码以捕获全局会话中的任何内容时;我一无所获。我已经确认DbgView正如我所期望的那样捕获IIS的输出(所以这肯定是我做错了)。
希望这是足够的背景,但如果需要更多的信息或细节;让我知道。
注意:如果有所不同,请在Windows 7 Professional上进行开发。
编辑
正如Tyranid(和Luke)所指出的,所有应该要求的是管理员权限和SE_CREATE_GLOBAL_NAME。我运行了一些测试,上面的代码设置实际上是捕获一些全局消息(例如,在IISRESET期间);但是,当应用程序在IIS内运行时(VS会话期间通过Local \ **路由),上面的代码不会从Log4Net OutputDebugString appender捕获任何数据。所有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());
}
对此的任何进一步见解将不胜感激。
答案 0 :(得分:2)
您确定要设置创建节/文件映射对象吗?是的,您需要成为管理员才能这样做,因为在全局命名空间中创建文件映射需要在令牌中使用SeCreateGlobalPrivilege。现在你可能需要在调用CreateFileMapping之前启用它,但是我已经玩了一段时间,因为我没有使用我的代码。
哦,你真的不应该指定一个NULL DACL,它在许多级别上都是错误的,我知道dbgview会这样做,但它只是懒惰,加上它可以在完整性级别涉及vista +时中断。