Ошибка захвата сообщений Global (Session 0) OutputDebugString через Win32 API? - PullRequest
1 голос
/ 20 февраля 2011

В настоящее время я работаю над простым приложением для захвата сообщений 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 и 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. Когда я запускаю приложение через Visual Studio, я получаю все локальные выходные данные, как и ожидалось. Когда я перемещаю приложение в IIS и настраиваю код для захвата чего-либо в глобальной сессии; Я ничего не получаю. Я подтвердил, что DbgView захватывает выходные данные из IIS, как я и ожидал (так что это определенно то, что я делаю неправильно).

Надеюсь, этого достаточно, но если требуется больше информации или подробностей; дайте мне знать.

Примечание. Разработка в Windows 7 Professional, если это имеет значение.

РЕДАКТИРОВАТЬ

Как указал Тиранид (и Люк), все, что нужно, это привилегии администратора и SE_CREATE_GLOBAL_NAME. Я выполнил еще несколько тестов, и приведенная выше настройка кода фактически захватывает некоторые глобальные сообщения (например, во время IISRESET); однако приведенный выше код не собирает никаких данных от приложения Log4Net OutputDebugString, когда приложение выполняется внутри IIS (маршрутизируется через Local \ ** во время сеансов VS). Все вызовы API Win32 возвращаются успешно, и при вызове 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 Ответ

2 голосов
/ 20 февраля 2011

Вы уверены, что вам удается создать объект отображения раздела / файла?Да, для этого вам нужно быть администратором, поскольку для создания сопоставления файлов в глобальном пространстве имен в вашем токене требуется SeCreateGlobalPrivilege.Теперь возможно, что вам может потребоваться включить это перед вызовом CreateFileMapping, но прошло много времени с тех пор, как я поиграл с этим, и у меня нет кода для раздачи.

О, и вы действительно не должны указывать NULLDACL, это просто неправильно на многих уровнях, я знаю, что dbgview делает это, но это просто лениво, плюс оно может сломаться, когда уровни целостности включаются в Vista +.

...