Ошибка захвата сообщений Global (Session 0) OutputDebugString через Win32 API?
В настоящее время я работаю над простым приложением для захвата сообщений 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 и события
Код, принятый в настоящее время в 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. Когда я запускаю приложение через Visual Studio, я получаю все локальные выходные данные, как и ожидалось. Когда я перемещаю приложение в IIS и настраиваю код для захвата чего-либо в глобальном сеансе; Я ничего не получаю. Я подтвердил, что DbgView захватывает выходные данные из IIS, как я и ожидал (так что это определенно то, что я делаю неправильно).
Надеюсь, этого достаточно, но если требуется больше информации или подробностей; дай мне знать.
Примечание: Разработка на Windows 7 Professional, если это имеет значение.
РЕДАКТИРОВАТЬ
Как указывает Тиранид (и Люк), все, что нужно, это привилегии администратора и SE_CREATE_GLOBAL_NAME. Я выполнил еще несколько тестов, и приведенная выше настройка кода фактически захватывает некоторые глобальные сообщения (например, во время IISRESET); однако приведенный выше код не собирает никаких данных из приложения Log4Net OutputDebugString, когда приложение выполняется внутри IIS (маршрутизируется через Local\** во время сеансов VS). Все вызовы 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());
}
Любое дальнейшее понимание этого будет с благодарностью.
1 ответ
Вы уверены, что вам удается создать объект отображения раздела / файла? Да, для этого вам нужно быть администратором, поскольку для создания сопоставления файлов в глобальном пространстве имен в вашем токене требуется SeCreateGlobalPrivilege. Теперь возможно, что вам может понадобиться включить это перед вызовом CreateFileMapping, но прошло уже много времени с тех пор, как я поиграл с этим, и у меня нет своего кода под рукой.
О, и вы действительно не должны указывать NULL DACL, это просто неправильно на многих уровнях, я знаю, что dbgview делает это, но это просто лениво, плюс оно может сломаться, когда уровни целостности включаются в vista+.