-
-
[原创]带你走进我的世界 热注入黑客(一)
-
发表于: 5天前 629
-
做我的世界这类的外挂其实是最好做的一类 毕竟没有客户端的反作弊 而且源码还全部能看到 所以想做一个好外挂的正真难点其实还是功能的实现 但在这里我就主要讲讲底层一点的内容。
写我的世界外挂和其他外挂的最大不同其实就是系统 我的世界是运行在jvm上的软件 而其他游戏是运行在windows上的
因此我们先要了解几个工具
JVMTI的作用更多的是JVM的底层调试比如像字节码修改(后续用来hook)
JNI的功能就是调用java里面的类
这两个工具的功能是互补的 所以都很重要
注入我的世界和注入其他的程序一样 就是写一个dllmain文件具体咋写可以去看微软的文档
这里要注意 在dllmain里面要创建一个新线程去跑我们自己的代码不然会有问题
大概就是这么写
因为这两个工具是由JVM提供的 因此如果我们想要获取并初始化这两个工具 我们要先获取jvm.dll
这一步很简单 因为我们注入的就是一个jvm所以这个进程里天生就加载了jvm.dll 我们只要获取一下就行了
但是获取到jvm.dll还不够 因为一个程序可以有很多jvm的实例 虽然大多数只有一个jvm 但我们还要获取我们需要的那个jvm
因此在这里我们还要使用到一个函数 JNI_GetCreatedJavaVMs
他的功能就是帮我们做这件事的
然后就能获取到我的世界的jvm了
获取到jvm后就能获取到jni
这段代码就是把之前创建的那个线程也变成jvm的一部分
同时我们也获得了使用jni的权利 也就是我上面的jvmEnv变量
获取jvmti也差不多
和获取jni最大的不同就是 jvmti要设置它的能力
因为我们要做的我的世界的外挂所以就添加了一个获取字节码的能力 也可以加其他的能力
这里顺带提一嘴怎么使用imgui
我的世界底层使用的opengl渲染所以我们只要使用minhook之类的库hook一下SwapBuffers然后就可以在里面写imgui的代码了
先给不熟悉java的同学科普一下java里的classloader
classloader顾名思义就是类加载器 功能就是用来加载类 这个东西非常重要 你大致可以把它看成C++里的namespace 但还是有很多不同的 具体可以去网上搜
接下来我们会大量的使用jni和我的世界的类交互 没有classloader我们就没办法获取到我的世界定义的class
先看一段代码
看懂这四行代码基本上就可以入门JNI了
想要在C++里调用java里的函数 我们首先要找获取要调用的类 也就是第一行干的事
然后我们要在class里获取我们要获取要调用的函数 也就是第二行(注意:通过jvmEnv->FindClass获取的class是一个定义 而不是实例 所以使用这个方法获取的class只能调用其静态函数)
有人可能会问 ()Ljava/util/Map; 是什么意思 这个就是用来描述一个java函数定义的表达式 具体规则可以直接问AI非常方便
第三行就是调用我们刚刚获取的函数
最后就是释放我们刚刚获取的class
接下来放一段使用我的世界classloader获取类的函数(网上找来的)
这段代码的作用就是通过我的世界的classloader来找我的世界游戏的class
不难看出这段复杂的代码就是翻来覆去的使用我上面那四行代码
首先我要明确一下 接下来以及上面提到的的classloader都是模组加载器提供的 如果是纯原版是不会有定制的classloader(应该吧?)
上面那个用classloader获取class的函数如果你直接放到程序里用 大概率是可以编译但是运行的时候会出问题
因为我的世界是被混淆过的 虽然我的世界过段时间要取消混淆 但目前绝大多数的版本都是被混淆过的 所以反混淆就诞生了 比较出名的反混淆有yarn和MCP 而上面那段代码的运行就必须在1.7.10FORGE的环境下才能执行
主要是由于这段代码
上面那整一段代码的具体逻辑我就不细讲了 这一行代码在试着用当前线程的classloader寻找net/minecraft/launchwrapper/Launch这个游戏中的类 但这个类只存在于我的世界1.7.10FORGE里面
在往后更新一点的版本里由于FORGE引入了SRG(中间名)因此无法直接寻找到net/minecraft/launchwrapper/Launch这个类
此外Fabric和Forge使用的反混淆也是截然不同 所以上面这个classloader查询class的函数还有改进的空间
接下来的开发想要使用到我的世界的源码 可以是fabric的也可以是forge的 关键看你是给哪个版本写的
写到这里有点累了 还有很多没讲 等到哪天心情不错了再继续写(如果大家喜欢看的话我就尽快更新)
以上的内容都是自己的经验之谈 如果有错误或者更好的方案一定要告诉我
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ lclt::windows::g_hModule = hModule; CloseHandle(CreateThread(nullptr, 0, main, nullptr, 0, nullptr)); return TRUE;}BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){ lclt::windows::g_hModule = hModule; CloseHandle(CreateThread(nullptr, 0, main, nullptr, 0, nullptr)); return TRUE;}auto jvm_handle = GetModuleHandleA("jvm.dll");auto jvm_handle = GetModuleHandleA("jvm.dll");typedef jint(*t_JNI_GetCreatedJavaVMs)(JavaVM** vmBuf, jsize bufLen, jsize* nVMs);auto o_JNI_GetCreatedJavaVMs = reinterpret_cast<t_JNI_GetCreatedJavaVMs(GetProcAddress(jvm_handle,"JNI_GetCreatedJavaVMs"));o_JNI_GetCreatedJavaVMs(&g_Jvm, 1, nullptr);typedef jint(*t_JNI_GetCreatedJavaVMs)(JavaVM** vmBuf, jsize bufLen, jsize* nVMs);auto o_JNI_GetCreatedJavaVMs = reinterpret_cast<t_JNI_GetCreatedJavaVMs(GetProcAddress(jvm_handle,"JNI_GetCreatedJavaVMs"));o_JNI_GetCreatedJavaVMs(&g_Jvm, 1, nullptr);g_Jvm->AttachCurrentThread(reinterpret_cast<void**>(&jvmEnv), nullptr)g_Jvm->AttachCurrentThread(reinterpret_cast<void**>(&jvmEnv), nullptr)if (g_Jvm->GetEnv((void**)&jvm::jvmtiEnv, JVMTI_VERSION_1) == JNI_OK) { LOG_INFO("[init] JVMTI environment obtained successfully"); jvmtiCapabilities capabilities{}; capabilities.can_get_bytecodes = 1; if (jvm::jvmtiEnv->AddCapabilities(&capabilities)!=jvmtiError::JVMTI_ERROR_NONE) { LOG_ERROR("[init] add jvmti capabilities failed"); }}if (g_Jvm->GetEnv((void**)&jvm::jvmtiEnv, JVMTI_VERSION_1) == JNI_OK) { LOG_INFO("[init] JVMTI environment obtained successfully"); jvmtiCapabilities capabilities{}; capabilities.can_get_bytecodes = 1; if (jvm::jvmtiEnv->AddCapabilities(&capabilities)!=jvmtiError::JVMTI_ERROR_NONE) { LOG_ERROR("[init] add jvmti capabilities failed"); }}jclass c_Thread = jvmEnv->FindClass("java/lang/Thread");jmethodID m_Thread_getAllStackTraces = jvmEnv->GetStaticMethodID(c_Thread, "getAllStackTraces","()Ljava/util/Map;");jobject obj_stackTracesMap = jvm::jvmEnv->CallStaticObjectMethod(c_Thread, m_Thread_getAllStackTraces);jvm::jvmEnv->DeleteLocalRef(threads);jclass c_Thread = jvmEnv->FindClass("java/lang/Thread");jmethodID m_Thread_getAllStackTraces = jvmEnv->GetStaticMethodID(c_Thread, "getAllStackTraces","()Ljava/util/Map;");jobject obj_stackTracesMap = jvm::jvmEnv->CallStaticObjectMethod(c_Thread, m_Thread_getAllStackTraces);jvm::jvmEnv->DeleteLocalRef(threads);jclass findclass(const char* clsName, JNIEnv* env){ jclass thread_clazz = env->FindClass("java/lang/Thread"); static jmethodID curthread_mid = env->GetStaticMethodID(thread_clazz, "currentThread", "()Ljava/lang/Thread;"); jobject thread = env->CallStaticObjectMethod(thread_clazz, curthread_mid); jmethodID threadgroup_mid = env->GetMethodID(thread_clazz, "getThreadGroup", "()Ljava/lang/ThreadGroup;"); jclass threadgroup_clazz = env->FindClass("java/lang/ThreadGroup"); jobject threadgroup_obj = env->CallObjectMethod(thread, threadgroup_mid); jmethodID groupactivecount_mid = env->GetMethodID(threadgroup_clazz, "activeCount", "()I"); jfieldID count_fid = env->GetFieldID(threadgroup_clazz, "nthreads", "I"); jint activeCount = env->GetIntField(threadgroup_obj, count_fid); jobjectArray arrayD = env->NewObjectArray(activeCount, thread_clazz, NULL); jmethodID enumerate_mid = env->GetMethodID(threadgroup_clazz, "enumerate", "([Ljava/lang/Thread;)I"); jint enumerate = env->CallIntMethod(threadgroup_obj, enumerate_mid, arrayD); jmethodID mid_getname = env->GetMethodID(thread_clazz, "getName", "()Ljava/lang/String;"); jobject array_elements = env->GetObjectArrayElement(arrayD, 0); jmethodID threadclassloader = env->GetMethodID(thread_clazz, "getContextClassLoader", "()Ljava/lang/ClassLoader;"); if (threadclassloader != 0) { auto class_loader = env->CallObjectMethod(array_elements, threadclassloader); jclass launch_clazz = env->FindClass("net/minecraft/launchwrapper/Launch"); jclass class_loader_class = env->GetObjectClass(class_loader); jmethodID find_class_id = env->GetMethodID(class_loader_class, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); env->DeleteLocalRef(launch_clazz); jstring name = env->NewStringUTF(clsName); jclass res = (jclass)env->CallObjectMethod(class_loader, find_class_id, name); env->DeleteLocalRef(name); env->DeleteLocalRef(class_loader_class); env->DeleteLocalRef(array_elements); env->DeleteLocalRef(thread_clazz); env->DeleteLocalRef(thread); env->DeleteLocalRef(threadgroup_clazz); env->DeleteLocalRef(threadgroup_obj); env->DeleteLocalRef(arrayD); return res; } env->DeleteLocalRef(array_elements); env->DeleteLocalRef(thread_clazz); env->DeleteLocalRef(thread); env->DeleteLocalRef(threadgroup_clazz); env->DeleteLocalRef(arrayD); env->DeleteLocalRef(threadgroup_obj); return env->FindClass(clsName);}jclass findclass(const char* clsName, JNIEnv* env){ jclass thread_clazz = env->FindClass("java/lang/Thread");赞赏
- [原创]带你走进我的世界 热注入黑客(一) 630
- [原创]金盾播放器研究 905
- [求助] 逆向遇到错误怎么解决 690