Загружаю в процесс ява-машины свою DLL, патчу образ kernel32.dll и делаю классический сплайсинг: в тела функций CreateFileW, ReadFile, WriteFile, CloseHandle внедряю джампы на функции из DLL, а также сохраняю модифицированный код ("трамплины"). У меня нет задачи определить с какими аргументами вызваны перехваченные функции, нужно только отследить факт их вызова, поэтому аргументы оставляю в стеке, отправляю оповещение о вызове (делаю OutputDebugString("CreateFile\n")) и делаю jmp на соответствующий трамплин. По шагам: 1. Процесс вызывает одну из перехваченных функций, например CreateFileW; 2. Через внедренный в kernel32.dll jmp управление попадает на функцию (экспортируется моей dll), которая вызывает OutputDebugStringA; 3. Моя функция делает jmp на соответствующий "трамплин"; 4. "Трамплин" передает управление системе. Все это под XP SP2, пишу на С++, функции из пункта 2 выглядят так: extern "C" __declspec(naked,dllexport) void CreateFileStub() { OutputDebugStringA("CreateFile called\n"); __asm { jmp CreateFileTrampline }; } Это задача глобального перехвата, такие хуки ставятся во всех процессах в системе, никаких падений нет, работает везде кроме процесса java.exe: ява-машина почему-то не может прочитать свой файл конфигурации и выводит кучу ошибок. Если закоментировать вызов OutputDebugStringA и сразу сделать jmp на трамплин ява работает нормально. Стек проверял, OutputDebugString ничего лишнего там не оставляет. В чем может быть проблема?
Не должны по-идее: OutputDebugStringA сама должна следовать соглашению stdcall, и она ему следует - изменяются только ecx, edx. Пробовал даже делать pushad перед вызовом OutputDebugStringA и popad после него - эффекта ноль.
Нашел ошибку. Моего запаса матерных слов не хватило что-бы охарактеризовать разработчиков Microsoft.. Дело в том, что у меня перехвачена CreateFileW, а Java вызывает CreateFileA, которая в свою очередь вызывает мою CreateFileW. Но имя файла CreateFileA передает в CreateFileW не через аргументы функции, а через именованную общую область памяти DBWIN_BUFFER (в MSDN такой способ вызова CreateFileW даже не описан). Получается так, что между вызовом явой CreateFileA и передачей управления на CreateFileW мой обработчик вызывает OutputDebugStringA, которая тоже работает через этот же DBWIN_BUFFER и благополучно затирает имя файла, к которому пытается обратиться Java. Видимо придется сохранять содержимое этого DBWIN_BUFFER в стеке и восстанавливать перед джампом на CreateFileW.
Ни разу такого не видел. Можно поподробнее? (Что же тогда будет если будет работать одновременно несколько потоков?)
А там есть mutex который этого не допускает. Но беда в том, что этот mutex уже захвачен потоком при исполнении CreateFileA и при вызове этим же потоком OutputDebugStringA никакой блокировки нет (рекурсивный захват). Подробнее описано тут: http://unixwiz.net/techtips/outputdebugstring.html
Насколько я понял, такое поведение CreateFileA не во всех версиях винды, возможно, что в семерке этот маразм уже убрали, но у меня в XP SP2 это работает именно так. В качестве имени файла в CreateFileW в этом случае передается строка "DBWIN_BUFFER".
Разобрался подробнее, все немного иначе. CreateFileA использует статический буфер в kernel32 для преобразования Ansi в Unicode, и указатель на этот буфер передается в CreateFileW в качестве имени файла. Но OutputDebugStringA использует тот же самый статический буфер для формирования имени меппинга DBWIN_BUFFER, и если вызвать ее между CreateFileA и CreateFileW имя файла будет затерто. Даже не знаю как гарантированно решить эту проблему, ведь у меня задача не извлекать аргументы из стека (это удобно тем, что не важно какая функция была вызвана, не нужно описывать прототипы). Единственное, что приходит на ум, это проверять в CreateFileW адрес возврата, находящийся на вершине стека при вызове хука, и если он соответствует kernel32.dll - не вызывать OutputDebugStringA, а сразу отдавать управление системе.
У нее нет аналогов, только если самому реализовывать общение с отладчиком через этот DBWIN_BUFFER. DbgPrint из ntdll не работает в режиме пользователя.
Под какую систему это делаешь? Та статья мега древняя, там реверс OutputDebugStringA с winNT. В хр она реализована очень просто через документированую RaiseException, там кода на пару строк. И никакого маппинга там и в помине нет. А DbgPrint помоему очень даже работает в юзер моде, это OutputDebugString нету в кернеле, насколько я помню. Ты сам наблюдал все что написал выше? Я про передачу параметров через DBWIN_BUFFER. Или это догадки на основе древней статьи?)))
Система XP SP2. DbgPrint из ntdll это первое что я попробовал, но его вызов ничего не дает, сообщение не выводится. Проблема в другом, OutputDebugStringA портит статический буфер (пишет в него строку "DBWIN_BUFFER") в котором CreateFileA формирует имя файла в юникоде. Пробовал под Windows7, там действительно уже такой проблемы нет, но нужно что бы работало под XP SP2.
можно просто в начале твоего кода скопировать имя файла в свой буффер и передать уже его в оригинальную функцию тогда результат будет нормальный.
Все равно не верится блин)) На хр3 точно нету такой фигни. Можешь скинуть свою кернел32 плиз, охота глянуть на это)
Да чего там, я порылся в исходниках 2000 винды, там такая хрень в каждой ansi функции, называется так: PUNICODE_STRING Basep8BitStringToStaticUnicodeString( IN LPCSTR lpSourceString ) /*++ Routine Description: Captures and converts a 8-bit (OEM or ANSI) string into the Teb Static Unicode String Arguments: lpSourceString - string in OEM or ANSI Return Value: Pointer to the Teb static string if conversion was successful, NULL otherwise. If a failure occurred, the last error is set. --*/
Так то старая винда. Ты в дизасме OutputDebugStringA на хр у себя глянь. У меня там почти сразу вызывается kernel32.RaiseException->ntdll.RtlRaiseException->ntdll.NtRaiseException, и никаких преобразований не видно.