So I'm working on a C++ Dll cheat/hack for a game (Minecraft). I created a little sample JNI project just to test things out, but a couple seconds after injecting the dll, Minecraft stops responding with a 'win32 unhandled exception'. I'm not experienced enough in C++ or using the JNI to understand what I'm doing wrong...
Here's my sample code (Not actually a hack, just wanted to try calling the clickMouse function to see if I was on the right track):
DWORD WINAPI Main_Thread(LPVOID lpParam)
{
HMODULE m_hDllInstance = LoadLibraryA("jvm.dll");
JavaVM *jvm;
JNIEnv *env;
typedef jint(JNICALL * GetCreatedJavaVMs)(JavaVM**, jsize, jsize*);
GetCreatedJavaVMs jni_GetCreatedJavaVMs =
(GetCreatedJavaVMs)GetProcAddress(m_hDllInstance, "JNI_GetCreatedJavaVMs");
jint size = 1;
jint vmCount;
jint ret = jni_GetCreatedJavaVMs(&jvm, size, &vmCount);
jint rc = jvm->AttachCurrentThread((void **)& env, NULL);
jclass Minecraft = env->FindClass("net.minecraft.client.Minecraft");
jmethodID constructor = env->GetMethodID(Minecraft, "<init>", "()V");
jobject mc = env->NewObject(Minecraft, constructor);
jmethodID clickMouse = env->GetMethodID(Minecraft, "clickMouse", "()V");
while (!GetAsyncKeyState(VK_END))
{
env->CallVoidMethod(mc, clickMouse);
}
jvm->DestroyJavaVM();
return S_OK;
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID
lpReserved)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
CreateThread(0, 0, Main_Thread, 0, 0, NULL);
}
return TRUE;
}
What's causing this to go wrong and how can I fix it?
P.S: Sorry if code looks a little funny, had a bit of trouble pasting it in here.
Edit: I tried running a debugger upon the crash, and it comes up with this: http://imgur.com/a/Uot9K. I'm still not sure how to fix this...
Related
I'm attempting to create a Java wrapped C implementation of a CAN bus that is to be used in an Android environment. I've used SocketCAN including in the Linux kernel to create a socket and bind it to the CAN interface.
The development environment doesn't have a physical CAN bus, and as a result I'm creating a virtual bus via sudo ip link add dev vcan0 type vcan and sudo ip link set up vcan0.
Running the native C code in this environment works as expected, the socket binds when the interface is present, and returns an error when it is not. However, when running the same native C code via JNI the bind(...) call always returns 0 regardless of the state of the interface, although any subsequent write(...) calls fail as expected.
Is there something I've overlooked that means this is the case?
The JNI code is as follows (lifted directly from my C implementation with additional type casting where necessary):
JNIEXPORT jboolean JNICALL Java_SocketCAN_nativeOpen
(JNIEnv * env, jobject jobj, jint bus_id)
{
if ((int) bus_id < MAX_NUMBER_OF_CAN_BUSES)
{
int s;
if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1)
{
printf("Error while opening socket\n");
return JNI_FALSE;
}
struct sockaddr_can addr;
struct ifreq ifr;
strcpy(ifr.ifr_name, "vcan0");
ioctl(s, SIOCGIFINDEX, &ifr);
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
printf("Error in socket bind\n");
return JNI_FALSE;
}
// Set the socketId in the Java class.
jclass jcls = (*env)->FindClass(env, "SocketCAN");
jfieldID socket_id = (*env)->GetFieldID(env, jcls, "socket", "I");
jint j_socket_id = (*env)->GetIntField(env, jobj, socket_id);
j_socket_id = s;
(*env)->SetIntField(env, jobj, socket_id, j_socket_id);
return JNI_TRUE;
}
return JNI_FALSE;
}
Any help is much appreciated, thanks!
EDIT:
If anyone seems to be experiencing this weird issue and wants a workaround (although it might be the correct way to do this and I've overlooked it), check the return value from the ioctl(...) function call. That returns -1 when "vcan0" isn't set up when running both the C and the JNI.
My updated code after modifying for the suggestions made by #12431234123412341234123 and #AndrewHenle is as follows:
JNIEXPORT jint JNICALL Java_SocketCAN_nativeOpen
(JNIEnv * env, jobject jobj, jint bus_id)
{
if ((int) bus_id < MAX_NUMBER_OF_CAN_BUSES)
{
int s;
if((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) == -1)
{
printf("Error while opening socket\n");
return -1;
}
struct sockaddr_can addr;
struct ifreq ifr;
strcpy(ifr.ifr_name, "vcan0");
if (ioctl(s, SIOCGIFINDEX, &ifr) == -1)
{
printf("Error in ioctl\n");
return -1;
}
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
printf("Error in socket bind\n");
return -1;
}
return (jint) s;
}
return -1;
}
The bind() call needs a proper interface index in can_ifindex. To get this value you can use the ioctl() call with SIOCGIFINDEX, as you do. However, when the ioctl() call fails, the ifreq structure does not necessarily have the correct index, it probably still has the "random" value from the last object that occupied the same memory region before. Because you ignored the return value from ioctl(), you called bind() with a "random" interface index. This also means that bind may or may not fail, depending on the value, because using a uninitialized value is UB in most cases. To avoid this error, check for the return value from ioctl() and handle errors accordingly.
It seems that this "random" value is different for the plain C version as it is for the JNI version. A possibility to avoid such random differences is by setting every new automatic object directly to a value. In your case you could to set everything to 0: struct ifreq ifr={0};, same for addr. This extra step could gain a more consistent behaviour.
I have a C (navive) program and a jar file with the main() method. From my native program I am initializing the JVM, and calling the main() method. I have no problems with this, everything is completely fine. But then I wanted to call back a C function from my java code.
The C function is defined in the native code in the same module as the one, that have created the JVM. The header is auto-generated, and the body is as simple as this:
JNIEXPORT void JNICALL Java_eu_raman_chakhouski_NativeUpdaterBus_connect0(JNIEnv* env, jclass clazz)
{
return;
}
So, from the java code I'm calling NativeUpdaterBus.connect0(), continuosly getting an UnsatisfiedLinkError. I have no System.loadLibrary() calls in my java code, because I thought, that there will be no problems calling the native code back from the java code if the target module is (possibly?) already loaded.
Well, maybe my approach is completely incorrect, but I can't see any obvious defects, maybe you could help?
What possibly could help (but I didn't tried any of these approaches, because I'm still not quite sure)
Use a kind of a "trampoline" dynamic library with these JNI methods, load it from the java code, then marshal native calls through it.
Define a java.lang.Runnable's anonymous inheritor, created with jni_env->DefineClass() but this involves some bytecode trickery.
Use an another, less invasive approach, like sockets, named pipes, etc. But in my case I'm using only one native process, so this might be an overkill.
I'm using OpenJDK 11.0.3 and Windows 10. My C program is compiled with the Microsoft cl.exe 19.16.27031.1 for x64 (Visual Studio 2017).
One possibility, as others have already mentioned, is to create a shared library (.dll) and call it from the native code and from Java to exchange data.
However, if you want to callback to a C function defined in the native code in the same module as the one the JVM originally created, you can use RegisterNatives.
Simple Example
C program creates JVM
it calls a Main of a class
the Java Main calls back a C function named connect0 in the calling C code
to have a test case the native C function constructs a Java string and returns it
the Java side prints the result
Java
package com.software7.test;
public class Main {
private native String connect0() ;
public static void main(String[] args) {
Main m = new Main();
m.makeTest(args);
}
private void makeTest(String[] args) {
System.out.println("Java: main called");
for (String arg : args) {
System.out.println(" -> Java: argument: '" + arg + "'");
}
String res = connect0(); //callback into native code
System.out.println("Java: result of connect0() is '" + res + "'"); //process returned String
}
}
C Program
One can create the Java VM in C as shown here
(works not only with cygwin but still with VS 2019) and then register with RegisterNatives native C callbacks. So using the function invoke_class from the link above it could look like this:
#include <stdio.h>
#include <windows.h>
#include <jni.h>
#include <stdlib.h>
#include <stdbool.h>
...
void invoke_class(JNIEnv* env) {
jclass helloWorldClass;
jmethodID mainMethod;
jobjectArray applicationArgs;
jstring applicationArg0;
helloWorldClass = (*env)->FindClass(env, "com/software7/test/Main");
mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");
applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);
applicationArg0 = (*env)->NewStringUTF(env, "one argument");
(*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);
(*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);
}
jstring connect0(JNIEnv* env, jobject thiz);
static JNINativeMethod native_methods[] = {
{ "connect0", "()Ljava/lang/String;", (void*)connect0 },
};
jstring connect0(JNIEnv* env, jobject thiz) {
printf("C: connect0 called\n");
return (*env)->NewStringUTF(env, "Some Result!!");
}
static bool register_native_methods(JNIEnv* env) {
jclass clazz = (*env)->FindClass(env, "com/software7/test/Main");
if (clazz == NULL) {
return false;
}
int num_methods = sizeof(native_methods) / sizeof(native_methods[0]);
if ((*env)->RegisterNatives(env, clazz, native_methods, num_methods) < 0) {
return false;
}
return true;
}
int main() {
printf("C: Program starts, creating VM...\n");
JNIEnv* env = create_vm();
if (env == NULL) {
printf("C: creating JVM failed\n");
return 1;
}
if (!register_native_methods(env)) {
printf("C: registering native methods failed\n");
return 1;
}
invoke_class(env);
destroy_vm();
getchar();
return 0;
}
Result
Links
Creating a JVM from a C Program: http://www.inonit.com/cygwin/jni/invocationApi/c.html
Registering Native Methods: https://docs.oracle.com/en/java/javase/11/docs/specs/jni/functions.html#registering-native-methods
System.loadLibrary() is essential for the jni lookup to work. You also have a more flexible System.load() alternative.
Make sure that the native method implementation is declared with extern "C" and is not hidden by linker.
My JNI library works flawlessly on Windows, however, on Linux I always get a strange segmentation fault.
siginfo: si_signo: 11 (SIGSEGV), si_code: 1 (SEGV_MAPERR), si_addr: 0x0000000000000000
The stack crace from the crash file is this:
C [libfmodjavaL.so+0xfb8c] JNIEnv_::GetStaticObjectField(_jclass*, _jfieldID*)+0x18
C [libfmodjavaL.so+0xf72b] Logger::sendToSystemOut(bool, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)+0x75
C [libfmodjavaL.so+0xf7c2] Logger::log(char const*)+0x4c
C [libfmodjavaL.so+0xd70d] fmodDebugCallback(unsigned int, char const*, int, char const*, char const*)+0x127
So it appears that it crashed when calling GetStaticObject field in the Logger class. This is that method:
void Logger::sendToSystemOut(bool error, std::string message) {
JNIEnv* jni = FMODWrapper::utils->getJNI();
jobject printStream;
if (error) {
printStream = jni->GetStaticObjectField(this->systemClass, this->errFieldID);
} else {
printStream = jni->GetStaticObjectField(this->systemClass, this->outFieldID);
}
jobject messageString = jni->NewStringUTF(message.c_str());
jni->CallObjectMethod(printStream, this->printlnMethodID, messageString);
}
So I'm guessing something's not right about storing the class and field IDs of these fields. But the weird thing is, I get logging output when my library starts up, even from FMOD, which the fmodDebugCallback gets called by.
Logger::Logger(const char* name) {
this->name = name;
JNIEnv* jni = FMODWrapper::utils->getJNI();
this->systemClass = FMODWrapper::utils->findClass("java/lang/System");
this->outFieldID = jni->GetStaticFieldID(this->systemClass, "out", "Ljava/io/PrintStream;");
this->errFieldID = jni->GetStaticFieldID(this->systemClass, "err", "Ljava/io/PrintStream;");
jclass printStreamClass = FMODWrapper::utils->findClass("java/io/PrintStream");
this->printlnMethodID = jni->GetMethodID(printStreamClass, "println", "(Ljava/lang/String;)V");
}
So, logging works flawlessly on Windows, but after some time crashes on Linux. Compiled with g++ on Fedora 29 64-bit.
Update: my method for getting a JNIEnv*
JNIEnv* Utils::getJNI() {
JNIEnv* jni;
int getEnvResult = FMODWrapper::jvm->GetEnv((void**) &jni, JNI_VERSION_1_6);
if (getEnvResult == JNI_EDETACHED) {
FMODWrapper::jvm->AttachCurrentThread(ANDROID_VOIDPP_CAST &jni, nullptr);
}
return jni;
}
Update 2: the code itself works up to a certain point since I'm getting log messages. Might be something to do with threads?
https://hastebin.com/kuzefuwawu.txt
systemClass, errFieldId, and outFieldID are all obtained from a different JNIEnv.
The JNIEnv cannot be cached:
Keeping a global reference to the JNIEnv environment
Just as it cannot be cached, you cannot store ids that were obtained from the other JNIEnv that you should no longer be using, nor should you be using anything that came from it. You need to get them all from the current valid JNIEnv.
The problem is not with thread affinity of class references or field IDs. The problem is with using a local class reference out of its scope. This is an implementation detail of some JVMs, that local references do not actually expire.
The fix would be to use
Logger::Logger(const char* name) {
this->name = name;
JNIEnv* jni = FMODWrapper::utils->getJNI();
this->systemClass = jni->NewGlobalRef(jni->findClass("java/lang/System"));
…
I'm at a total loss here. I'm trying to get a JVMTI agent library running but it keeps crashing for some reason.
I've narrowed it down this line:
(*jvm)->GetEnv(jvm, (void**)jvmti, JVMTI_VERSION_1_0);
this is the full code of the agent lib (in C):
#include <jvmti.h>
#include <stdlib.h>
jvmtiEnv* jvmti = NULL;
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
printf("Agent started.\n");
_flushall();
jint err = (*jvm)->GetEnv(jvm, (void**)jvmti, JVMTI_VERSION_1_0);
if (err != JNI_OK)
{
printf("Failed to get JVMTI env!\n");
_flushall();
return err;
}
return JNI_OK;
}
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved)
{
return JNI_OK;
}
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
}
As I tried to isolate what the issue was I wrote a very simple java app to test
this with:
public class Test
{
public static void main(String[] args)
{
System.out.println("Hello from java!");
}
}
If I run this from netbeans with the VM arg -agentpath set to my .dllcontaining the code above, the app seems to crash when it tries to call GetEnv().
I've made sure of the following things:
- The JVM and the dll are both 64bit.
- The library is most definitely being found and loaded (the printf output is visible before the crash.)
I don't know what else could probably be causing this, do I have to link against some JVMTI API lib that I don't know about?
Or could this be an issue with the java installation on my PC?
Thanks
You should be passing address of jvmti to GetEnv() as in:
jint err = (*jvm)->GetEnv(jvm, (void**) &jvmti, JVMTI_VERSION_1_0);
I'm using GetStringUTFChars to retrieve a string's value from the java code using JNI and releasing the string using ReleaseStringUTFChars. When the code is running on JRE 1.4 there is no memory leak but if the same code is running with a JRE 1.5 or higher version the memory increases. This is a part of the code
msg_id=(*env)->GetStringUTFChars(env, msgid,NULL);
opcdata_set_str(opc_msg_id, OPCDATA_MSGID, msg_id);
(*env)->ReleaseStringUTFChars(env, msgid,msg_id);
I'm unable to understand the reason for leak.Can someone help?
This one is because if I comment the rest of the code but leave this part the memory leak takes place. This is a part of the code that I'm using
JNIEXPORT jobjectArray JNICALL Java_msiAPI_msiAPI_msgtoescalate( JNIEnv *env,
jobject job,
jstring msgid,
jlong msgseverity,
jstring msgprefixtext,
jint flag )
{
opcdata opc_msg_id; /* data struct to store a mesg ID */
const char *msg_id;
int ret, ret2;
jint val;
val=67;
jstring str=NULL;
jobjectArray array = NULL;
jclass sclass=NULL;
/* create an opc_data structure to store message ids of */
/* messages to escalate */
if ((ret2=opcdata_create(OPCDTYPE_MESSAGE_ID, &opc_msg_id))!= OPC_ERR_OK)
{
fprintf(stderr, "Can't create opc_data structure to store message. opcdata_create()=%d\n", ret2);
cleanup_all();
}
//////////////////////////////////////////////////////////
msg_id=(*env)->GetStringUTFChars(env,msgid,NULL);
opcdata_set_str(opc_msg_id, OPCDATA_MSGID, msg_id);
(*env)->ReleaseStringUTFChars(env, msgid, msg_id);
ret=opcmsg_ack(connection,opc_msg_id);
//////////////////////////////////////////////////////////
if(flag==0 && ret==0)
{
sclass = (*env)->FindClass(env, "java/lang/String");
array = (*env)->NewObjectArray(env, 2, sclass, NULL);
str=(*env)->NewStringUTF(env,"0");
(*env)->SetObjectArrayElement(env,array,0,str);
(*env)->DeleteLocalRef(env, str);
str=(*env)->NewStringUTF(env,"0");
(*env)->SetObjectArrayElement(env,array,1,str);
(*env)->DeleteLocalRef(env, str);
}
opcdata_free(&opc_msg_id);
if(ret!=0)
return NULL;
else
return(array);
}
In the one above is if I comment the sections between ///// I don't see any memory leak.
Release array object.
(*env)->DeleteLocalRef(env, array);