Применение к DLL-файлам Global Hook - PullRequest
0 голосов
/ 12 июля 2010

Моя цель концептуально проста: я хочу установить функцию глобального подключения GetMessage, которая использует дескриптор общего файла.Проблема возникает потому, что, насколько я понимаю, DLL, содержащая функцию ловушки, загружается несколько раз для каждого процесса, каждый со своим «адресным пространством».По этой причине меня убеждают, что я не могу просто обработать DLL_PROCESS_ATTACH DllMain для создания желаемого файла, так как несколько файлов будут создаваться с разными дескрипторами.

Решение, на которое я обратил внимание, - это Named Pipes,В основном приложение будет действовать как серверная часть;он будет один раз создавать файл, а затем предоставлять дескриптор файла клиентам DLL, поэтому каждый глобальный хук будет использовать один и тот же файл.

Кажется, я не могу заставить его работать из кода, который я собрал.В приложении я создаю файл, устанавливаю глобальную функцию перехвата, затем пропускаю этот цикл:

while(1)
{
  HANDLE hPipe = CreateNamedPipe("\\\\.\\pipe\\pipename", PIPE_ACCESS_OUTBOUND, 
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 32, 32, 5000, NULL);
  if(hPipe == INVALID_HANDLE_VALUE)
    return 42;
  if(!ConnectNamedPipe(hPipe, NULL))
    return 43;
  DWORD dwWritten;
  WriteFile(hPipe, logFile, sizeof(logFile), &dwWritten, NULL);
  FlushFileBuffers(hPipe);
  DisconnectNamedPipe(hPipe);
  CloseHandle(hPipe);
}

Затем я обрабатываю DLL_PROCESS_ATTACH DllMain следующим образом:

case DLL_PROCESS_ATTACH:
{
  HANDLE hPipe;
  while(1)
  {
    hPipe = CreateFile("\\\\.\\pipe\\pipename", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
    if(hPipe != INVALID_HANDLE_VALUE)
      break;
    WaitNamedPipe("\\\\.\\pipe\\pipename", NMPWAIT_USE_DEFAULT_WAIT);
  }
  DWORD dwRead;
  ReadFile(hPipe, logFile, sizeof(logFile), &dwRead, NULL);
  CloseHandle(hPipe);
  break;
}

Проще говоря, это не работает, и я не могу понять, почему.Есть ли что-то, что я пропускаю или делаю неправильно в своем коде?

Еще одна проблема, которую я не могу понять, заключается в том, что приложение застряло в бесконечном цикле постоянного обслуживания.Я хочу установить Событие, которое DLL установит при определенных обстоятельствах и заставит основное приложение отцепить глобальный хук, закрыть файл и выйти, однако ConnectNamedPipe является функцией блокировки.Как определить, когда все клиенты были обслужены, чтобы цикл обслуживания мог прерваться?

Спасибо за любую помощь.

Ответы [ 2 ]

1 голос
/ 12 июля 2010

Мне кажется, что вашей основной проблемой может быть последний параметр функции CreateNamedPipe (SECURITY_ATTRIBUTES) или другие проблемы безопасности (см. Ниже).

Я не совсем понял, какую информациювы планируете записать в logFile, что может быть не более 32 байтов (16 WCHAR).Использование sizeof() в CreateNamedPipe также будет немного лучше (подумайте также о 64-битных операционных системах).Вы хотите отправить дескриптор файла журнала, открытого в одном процессе, другим процессам?Если вы сделаете это, вы должны использовать такие функции, как DuplicateHandle (см. http://msdn.microsoft.com/en-us/library/ms724251.aspx).. В общем, пример кода для связи с именованным каналом, который вы разместили, я считаю не совсем удачным.отладка связи по именованному каналу с внешней стороны подключаемой DLL (по крайней мере, с двумя отдельными клиентскими процессами, которые лучше работают под разными учетными данными пользователя и серверным процессом, создавшим канал).

Существуют ограничения в использованииWindows API внутри DLL_THREAD_ATTACH, но при использовании Kernel32.dll в вашем случае мне кажется безопасным.

Я не знаю, какой тип связи вы хотите реализовать, но в целом использование неблокированного режиманапример, использование подпрограмм завершения или другой асинхронной операции (см. http://msdn.microsoft.com/en-us/library/aa365788.aspxhttp://msdn.microsoft.com/en-us/library/aa365788.aspx и http://msdn.microsoft.com/en-us/library/aa365601.aspx) внутри DllMain, может быть лучше.

Еще одна небольшая рекомендация: вам следует использовать DisableThreadLibraryCalls() в случае DLL_PROCESS_ATTACH и попытайтесь выбрать разумный базовый адрес DLL (переключатель компоновщика)что уменьшит перемещение DLL при ее загрузке в разные процессы.Это немного ускорит вашу программу и сэкономит память.

1 голос
/ 12 июля 2010

Существуют строгие ограничения в отношении того, какие системные API вы можете вызывать во время DLL_PROCESS_ATTACH или DLL_THREAD_ATTACH.Из документации MSDN.

Функция точки входа должна выполнять только простые задачи инициализации или завершения.Он не должен вызывать функцию LoadLibrary или LoadLibraryEx (или функцию, которая вызывает эти функции), поскольку это может создать циклы зависимости в порядке загрузки DLL.Это может привести к использованию DLL до того, как система выполнит свой код инициализации.Аналогично, функция точки входа не должна вызывать функцию FreeLibrary (или функцию, которая вызывает FreeLibrary) во время завершения процесса, поскольку это может привести к использованию DLL после того, как система выполнила свой код завершения.

Потому чтоKernel32.dll гарантированно загружается в адресное пространство процесса при вызове функции точки входа, вызов функций в Kernel32.dll не приводит к использованию библиотеки DLL до выполнения ее кода инициализации.Следовательно, функция точки входа может вызывать функции в Kernel32.dll, которые не загружают другие библиотеки DLL.Например, DllMain может создавать объекты синхронизации, такие как критические секции и мьютексы, и использовать TLS.К сожалению, в Kernel32.dll нет исчерпывающего списка безопасных функций.

Windows 2000: Не создавайте именованный объект синхронизации в DllMain, поскольку система затем загрузит дополнительную DLL.

Вызов функций, которым требуются библиотеки DLL, отличные от Kernel32.dll, может привести к проблемам, которые трудно диагностировать.Например, вызов функций User, Shell и COM может вызвать ошибки нарушения доступа, поскольку некоторые функции загружают другие компоненты системы.И наоборот, вызов таких функций во время завершения может привести к ошибкам нарушения доступа, поскольку соответствующий компонент уже может быть выгружен или неинициализирован.

Для работы в классе эксперимента рассмотрите возможность использования события присоединения потока, чтобы увидеть "что просходит."Для производственной работы вам понадобится полностью пересмотренный подход, который не требует тяжелой работы в DllMain.Вы можете видеть выше, что в будущей истории будет больше ошибок в этом средстве ОС.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...