├── client ├── build.bat └── Client.java ├── .gitignore ├── .github ├── workflows │ └── build.yml ├── ISSUE_TEMPLATE │ ├── feature-request.md │ └── bug_report.md └── PULL_REQUEST_TEMPLATE.md ├── CMakeLists.txt ├── loader ├── include │ ├── utils.h │ ├── jni_natives.h │ ├── class_loader.h │ ├── main_natives.h │ └── mapper.h ├── src │ ├── utils.cpp │ ├── class_loader.cpp │ ├── jni_natives.cpp │ ├── main_natives.cpp │ └── mapper.cpp └── dllmain.cpp ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── mappings └── download.py ├── injector ├── inject.cpp └── instr.h └── CODE_OF_CONDUCT.md /client/build.bat: -------------------------------------------------------------------------------- 1 | mkdir build & javac -d ./build Client.java & pause -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /client/build 3 | /mappings/* 4 | !/mappings/download.py 5 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - run: | 13 | mkdir build && cd build 14 | cmake .. 15 | cmake --build . 16 | cd ../client 17 | cmd /c "build.bat < nul" 18 | 19 | - uses: actions/upload-artifact@v3 20 | with: 21 | path: | 22 | build/*.exe 23 | build/*.dll 24 | client/build 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature Request]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report 4 | title: "[Bug]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Windows 10] 28 | - Version [e.g. 1.0.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | 3 | project(ClientBase VERSION 1.0.0 LANGUAGES CXX) 4 | 5 | set(CMAKE_BUILD_TYPE Release) 6 | set(BUILD_SHARED_LIBS ON) 7 | set(CMAKE_DISABLE_TESTING ON) 8 | set(CMAKE_CXX_STANDARD 17) 9 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_BINARY_DIR}>) 10 | 11 | find_package(JNI REQUIRED) 12 | 13 | include_directories(${JNI_INCLUDE_DIRS}) 14 | 15 | include_directories(loader/include) 16 | 17 | add_library(loader 18 | loader/dllmain.cpp 19 | loader/src/class_loader.cpp 20 | loader/src/mapper.cpp 21 | loader/src/jni_natives.cpp 22 | loader/src/main_natives.cpp 23 | loader/src/utils.cpp 24 | ) 25 | 26 | set_target_properties(loader PROPERTIES 27 | PREFIX "" 28 | ) 29 | 30 | target_include_directories(loader PUBLIC 31 | ${JAVA_HOME}/include 32 | ) 33 | 34 | target_link_libraries(loader PUBLIC -static 35 | ${JNI_LIBRARIES} 36 | ) 37 | 38 | add_executable(inject 39 | injector/inject.cpp 40 | ) 41 | 42 | target_link_libraries(inject PUBLIC -static 43 | ) 44 | -------------------------------------------------------------------------------- /loader/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | struct JVM_CTX { 10 | JNIEnv *env; 11 | JavaVM *jvm; 12 | JavaVMInitArgs vm_args; 13 | jvmtiEnv *jvmti; 14 | }; 15 | 16 | extern JVM_CTX ctx; 17 | 18 | #define JNI_EXCEPTION_CHECK \ 19 | { \ 20 | if (ctx.env->ExceptionCheck()) { \ 21 | ctx.env->ExceptionDescribe(); \ 22 | ctx.env->ExceptionClear(); \ 23 | } \ 24 | } 25 | 26 | // Converts a jstring to std::string 27 | // str: The jstring to convert. 28 | std::string jstringToString(JNIEnv *jniEnv, jstring str); 29 | std::string jstringToString(jstring str); 30 | 31 | // Sets the current Minecraft client by checking if certain classes exist 32 | void setClient(); 33 | 34 | extern std::string _dllPath; 35 | std::string getDllPath(); 36 | 37 | extern std::string _parentPath; 38 | std::string getParentPath(); 39 | 40 | #endif // UTILS_H 41 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Thank you for taking the time to contribute to this project! 4 | 5 | Read our [Code of Conduct](CODE_OF_CONDUCT.md) to understand which actions are and aren’t tolerated. 6 | 7 | ## Issues 8 | 9 | Before opening an issue, please check to make sure someone hasn't already opened an issue about it. 10 | 11 | ## Pull Requests 12 | 13 | Once you've made changes, you can submit a Pull Request. 14 | 15 | Make sure your Pull Request adheres to the following guidelines: 16 | 17 | - Use the PR template provided. 18 | - Keep your Pull Request title succinct, detailed and to the point. 19 | - Keep commits atomic. One commit should contain one change. If you want to make multiple changes, submit multiple Pull Requests. 20 | - Commits should be clear, concise and easy to understand. 21 | - References to the Issue number in the Pull Request and/or Commit message. 22 | 23 | ### Your PR is merged! 24 | 25 | Congratulations :tada::tada: 26 | 27 | Thanks for taking the time and effort to contribute to this project! :sparkles: 28 | 29 | We are excited to see your code in this project! :star2: 30 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | # How Has This Been Tested? 17 | 18 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. 19 | 20 | # Checklist: 21 | 22 | - [ ] I have read the [Contribution Guidelines](https://github.com/sbplat/Minecraft-Client-Base/blob/main/CONTRIBUTING.md) and [Code of Conduct](https://github.com/sbplat/Minecraft-Client-Base/blob/main/CODE_OF_CONDUCT.md) 23 | - [ ] My code follows the style guidelines of this project 24 | - [ ] I have performed a self-review of my own code 25 | - [ ] I have commented my code, particularly in hard-to-understand areas 26 | - [ ] My changes generate no new warnings 27 | -------------------------------------------------------------------------------- /loader/include/jni_natives.h: -------------------------------------------------------------------------------- 1 | #ifndef NATIVES_H 2 | #define NATIVES_H 3 | 4 | #include 5 | 6 | namespace JniNatives { 7 | 8 | // Get the mapped (obfuscated) class name 9 | // seargeClassName: The Searge name of the class to get the mapped name of. 10 | jstring GetClassMapped(JNIEnv *jniEnv, jobject, jstring seargeClassName); 11 | 12 | // Finds and returns the class with the given Searge name 13 | // seargeClassName: The Searge name of the class to find. 14 | jclass FindClass(JNIEnv *jniEnv, jobject, jstring seargeClassName); 15 | 16 | // Get the mapped (obfuscated) method name 17 | // seargeClassName: The Searge name of the class the method is in. 18 | // seargeMethodName: The Searge name of the method to get the mapped name of. 19 | // seargeMethodDescriptor: The Searge descriptor of the method to get the mapped name of. 20 | jstring GetMethodMapped(JNIEnv *jniEnv, jobject, jstring seargeClassName, jstring seargeMethodName, jstring seargeMethodDescriptor); 21 | 22 | // Get the mapped (obfuscated) field name 23 | // seargeClassName: The Searge name of the class the field is in. 24 | // seargeFieldName: The Searge name of the field to get the mapped name of. 25 | jstring GetFieldMapped(JNIEnv *jniEnv, jobject, jstring seargeClassName, jstring seargeFieldName); 26 | 27 | // Register the native functions 28 | // clazz: The class to register the native functions to. 29 | void RegisterJniNatives(JNIEnv *jniEnv, jclass clazz); 30 | 31 | } // namespace JniNatives 32 | 33 | #endif // NATIVES_H 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, sbplat 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /loader/include/class_loader.h: -------------------------------------------------------------------------------- 1 | #ifndef CLASS_LOADER_H 2 | #define CLASS_LOADER_H 3 | 4 | #include "mapper.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | class ClassLoader { 12 | private: 13 | CLIENT Client; 14 | 15 | jobject classLoaderObject = NULL; 16 | 17 | jmethodID findClassMethod = NULL; 18 | 19 | void InitializeClassLoaderObject(); 20 | void InitializeFindClassMethod(); 21 | 22 | public: 23 | ClassLoader(); 24 | ~ClassLoader(); 25 | 26 | // Set the Minecraft client for automatic mapping 27 | // client: The Minecraft client to set. 28 | void SetMinecraftClient(CLIENT client); 29 | 30 | // Finds a class given the Searge class name 31 | // seargeClassName: The Searge class name of the class to find. 32 | jclass FindClass(JNIEnv *jniEnv, std::string seargeClassName); 33 | 34 | // Finds a class given the Searge class name 35 | // seargeClassName: The Searge class name of the class to find. 36 | jclass FindClass(std::string seargeClassName); 37 | 38 | // Loads a class given the class name and the class path 39 | // fullClassName: The full class name. 40 | // classPath: The class path relative to the dll parent directory. 41 | jclass LoadClass(std::string fullClassName, std::string classPath); 42 | 43 | // Loads the client classes given the class paths 44 | // classPaths: A map of the class names to the class paths. 45 | std::map LoadClient(std::map classPaths); 46 | 47 | // Loads a jar given the jar path 48 | // jarPath: The path to the jar. 49 | // TODO: Finish implementation 50 | std::map LoadJar(std::string jarPath); 51 | }; 52 | 53 | extern ClassLoader classLoader; 54 | 55 | #endif // CLASS_LOADER_H 56 | -------------------------------------------------------------------------------- /loader/include/main_natives.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_NATIVES_H 2 | #define MAIN_NATIVES_H 3 | 4 | #include 5 | #include 6 | 7 | namespace MainNatives { 8 | 9 | struct ClassData { 10 | jint length; 11 | unsigned char *bytes; 12 | 13 | ~ClassData(); 14 | }; 15 | 16 | // Enables the ClassFileLoadHook callback 17 | // targetClassSig: The signature of the class to hook. 18 | // classData: This will be filled with the class data. 19 | jvmtiError EnableClassFileLoadHook(JNIEnv *jniEnv, char *targetClassSig, ClassData *classData); 20 | 21 | // Disables the ClassFileLoadHook callback 22 | jvmtiError DisableClassFileLoadHook(JNIEnv *jniEnv); 23 | 24 | // Gets the bytes of the class 25 | // clazz: The class to get the bytes of. 26 | jbyteArray GetClassBytes(JNIEnv *jniEnv, jobject, jclass clazz); 27 | 28 | // Sets the bytes of the class 29 | // clazz: The class to set the bytes of. 30 | // classBytes: The bytes to set. 31 | jvmtiError SetClassBytes(JNIEnv *jniEnv, jobject, jclass clazz, jbyteArray classBytes); 32 | 33 | // Get the class field as an array of string objects 34 | // clazz: The class to get the fields of. 35 | // fieldNames: If fieldNames is true, the names of the fields will be returned. 36 | // Otherwise, the descriptors will be returned. 37 | jobjectArray GetClassFields(JNIEnv *jniEnv, jobject, jclass clazz, jboolean fieldNames); 38 | 39 | // Get the class method as an array of string objects 40 | // clazz: The class to get the methods of. 41 | // methodNames: If methodNames is true, the names of the methods will be returned. 42 | // Otherwise, the descriptors will be returned. 43 | jobjectArray GetClassMethods(JNIEnv *jniEnv, jobject, jclass clazz, jboolean methodNames); 44 | 45 | // Register the native functions 46 | // clazz: The class to register the native functions to. 47 | void RegisterMainNatives(JNIEnv *jniEnv, jclass clazz); 48 | 49 | } // namespace MainNatives 50 | 51 | #endif // MAIN_NATIVES_H 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | Minecraft Client Base 4 |
5 |

6 | 7 |

External client base for Minecraft using JNI.

8 | 9 |

10 | Features • 11 | Building/Usage • 12 | Crashes • 13 | License 14 |

15 | 16 | ## Features 17 | 18 | * External injector and loader 19 | * Currently supports vanilla and forge 20 | * Open source 21 | 22 | ## Building/Usage 23 | 24 | 1. Clone this project. 25 | 2. Build the loader and injector using CMake. 26 | ``` 27 | mkdir build && cd build 28 | cmake .. 29 | cmake --build . 30 | ``` 31 | 3. Build the client using the batch file. 32 | ``` 33 | cd ../client 34 | build.bat 35 | ``` 36 | 4. Download the mappings for your desired version. 37 | ``` 38 | cd ../mappings 39 | py download.py 40 | ``` 41 | 5. Start Minecraft and run `build/inject.exe` to inject the loader. The loader will automatically load the client into the game. 42 | 43 | ## Crashes 44 | 45 | If Minecraft crashes when the loader is injected, try the following: 46 | 47 | * Injecting **two or more times** will cause the game to crash. This is not a bug, and is caused by the "client" not being detached properly, which redefines the classes. 48 | * Run Minecraft with **logging enabled**. If it crashes without any logging, then there is most likely a problem with your JNI version. Set the full JNI header and library paths in the `CMakelists.txt` file. 49 | * Make sure you are using the **correct mappings** for your version. For example, if you are using the latest version of 1.8.9, choose "1.8.9 stable 22" in the download script. 50 | * The "client" must be built using the **same version of java** as the version running the game. You can check your javac version by running `javac -version`. If the version is different, you can change the version by specifying the full path to the javac executable in the `client/build.bat` file. 51 | 52 | ## License 53 | 54 | This project is licensed under the [BSD 3 license](LICENSE). 55 | -------------------------------------------------------------------------------- /loader/src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include "class_loader.h" 4 | #include "mapper.h" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | JVM_CTX ctx; 12 | 13 | std::string jstringToString(JNIEnv *jniEnv, jstring str) { 14 | if (!str) { 15 | return std::string(); 16 | } 17 | 18 | int length = jniEnv->GetStringUTFLength(str); 19 | const char *data = jniEnv->GetStringUTFChars(str, 0); 20 | std::string string(data, length); 21 | jniEnv->ReleaseStringUTFChars(str, data); 22 | return string; 23 | } 24 | 25 | std::string jstringToString(jstring str) { 26 | return jstringToString(ctx.env, str); 27 | } 28 | 29 | void setClient() { 30 | jclass MinecraftForgeClass = ctx.env->FindClass("net/minecraftforge/common/MinecraftForge"); 31 | JNI_EXCEPTION_CHECK 32 | 33 | if (MinecraftForgeClass != NULL) { 34 | jclass EssentialModClass = ctx.env->FindClass("gg/essential/loader/stage2/relaunch/RelaunchClassLoader"); 35 | JNI_EXCEPTION_CHECK 36 | 37 | if (EssentialModClass != NULL) { 38 | std::cout << "Client is: " 39 | << "FORGE_ESSENTIAL" << std::endl; 40 | mapper.SetClient(CLIENT::FORGE_ESSENTIAL); 41 | 42 | } else { 43 | std::cout << "Client is: " 44 | << "FORGE" << std::endl; 45 | mapper.SetClient(CLIENT::FORGE); 46 | } 47 | 48 | } else { 49 | std::cout << "Client is: " 50 | << "VANILLA" << std::endl; 51 | mapper.SetClient(CLIENT::VANILLA); 52 | } 53 | 54 | classLoader.SetMinecraftClient(mapper.GetClient()); 55 | 56 | ctx.env->DeleteLocalRef(MinecraftForgeClass); 57 | } 58 | 59 | std::string _dllPath; 60 | 61 | std::string getDllPath() { 62 | if (_dllPath.size() != 0) { 63 | return _dllPath; 64 | } 65 | 66 | char dllPathBuffer[MAX_PATH]; 67 | HMODULE hModule = NULL; 68 | 69 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)&getDllPath, &hModule); 70 | GetModuleFileName(hModule, dllPathBuffer, MAX_PATH); 71 | 72 | return _dllPath = std::string(dllPathBuffer); 73 | } 74 | 75 | std::string _parentPath; 76 | 77 | std::string getParentPath() { 78 | if (_parentPath.size() != 0) { 79 | return _parentPath; 80 | } 81 | 82 | std::filesystem::path dllPath(getDllPath()); // ./build/loader.dll 83 | 84 | return _parentPath = dllPath 85 | .parent_path() // ./build 86 | .parent_path() // ./ 87 | .string(); 88 | } 89 | -------------------------------------------------------------------------------- /mappings/download.py: -------------------------------------------------------------------------------- 1 | from packaging import version 2 | import requests 3 | import zipfile 4 | import io 5 | import tkinter as tk 6 | from tkinter import ttk, messagebox 7 | 8 | # https://github.com/bspkrs/MCPMappingViewer/tree/master 9 | JSON_VERSIONS_URL = "https://maven.minecraftforge.net/de/oceanlabs/mcp/versions.json" 10 | BASE_MAPPING_URL = "https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_{channel}/{map_ver}-{mc_ver}/mcp_{channel}-{map_ver}-{mc_ver}.zip" 11 | NEW_BASE_SRG_URL = "https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp_config/{mc_ver}/mcp_config-{mc_ver}.zip" 12 | OLD_BASE_SRG_URL = "https://maven.minecraftforge.net/de/oceanlabs/mcp/mcp/{mc_ver}/mcp-{mc_ver}-srg.zip" 13 | 14 | def get_versions(): 15 | r = requests.get(JSON_VERSIONS_URL).json() 16 | versions = [] 17 | 18 | for mc_ver in r: 19 | for channel in r[mc_ver]: 20 | for ver in r[mc_ver][channel]: 21 | versions.append([mc_ver, channel, ver]) 22 | 23 | return versions 24 | 25 | def select_version(): 26 | mc_versions = get_versions() 27 | mc_versions.sort(key=lambda x: version.parse(x[0])) 28 | 29 | root = tk.Tk() 30 | root.title("Select a version") 31 | root.geometry("250x150") 32 | 33 | selected = tk.StringVar() 34 | selected.set(mc_versions[0]) 35 | 36 | combo_box = ttk.Combobox(root, state="readonly", values=mc_versions, textvariable=selected, width=100) 37 | button = ttk.Button(root, text="Select", command=root.destroy) 38 | 39 | combo_box.pack() 40 | button.pack() 41 | 42 | root.mainloop() 43 | 44 | return selected.get().split(" ") 45 | 46 | def confirm_download(mc_ver, channel, ver): 47 | msg = f"Download mappings for {mc_ver} {channel} {ver}?" 48 | msg_box = messagebox.askquestion("Confirm download", msg) 49 | return msg_box == "yes" 50 | 51 | def download_and_extract(zip_url, extract_path): 52 | r = requests.get(zip_url) 53 | z = zipfile.ZipFile(io.BytesIO(r.content)) 54 | z.extractall(extract_path) 55 | 56 | mc_version, channel, ver = select_version() 57 | is_new = version.parse(mc_version) >= version.parse("1.13") 58 | print(f"Selected version: {mc_version} {channel} {ver} (new: {is_new})") 59 | 60 | map_url = BASE_MAPPING_URL.format(mc_ver=mc_version, channel=channel, map_ver=ver) 61 | srg_url = (NEW_BASE_SRG_URL if is_new else OLD_BASE_SRG_URL).format(mc_ver=mc_version) 62 | 63 | if not confirm_download(mc_version, channel, ver): 64 | print("Aborting...") 65 | exit(0) 66 | 67 | print(f"Downloading mappings from {map_url}") 68 | download_and_extract(map_url, ".") 69 | print(f"Downloading SRG from {srg_url}") 70 | download_and_extract(srg_url, ".") 71 | print("Done!") 72 | -------------------------------------------------------------------------------- /injector/inject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "instr.h" 8 | 9 | std::string window_class = "LWJGL", window_name = "Minecraft 1.8.9"; 10 | 11 | int getProcessID() { 12 | OBF_BEGIN 13 | HWND hWnd = FindWindowExA(NULL, NULL, window_class.c_str(), window_name.c_str()); 14 | DWORD processID; 15 | GetWindowThreadProcessId(hWnd, &processID); 16 | std::cout << "Process ID: " << processID << std::endl; 17 | RETURN(processID) 18 | OBF_END 19 | } 20 | 21 | bool inject(DWORD processID, std::string dllPath) { 22 | OBF_BEGIN 23 | HANDLE handle = OpenProcess(N(PROCESS_ALL_ACCESS), false, processID); 24 | 25 | IF (handle == NULL) { 26 | std::cout << "OpenProcess failed" << std::endl; 27 | RETURN(false) 28 | } ELSE { 29 | std::cout << "Opened process" << std::endl; 30 | } 31 | ENDIF 32 | 33 | LPVOID alloc = VirtualAllocEx(handle, NULL, dllPath.size(), N((MEM_COMMIT | MEM_RESERVE)), N(PAGE_EXECUTE_READWRITE)); 34 | 35 | IF (alloc == NULL) { 36 | std::cout << "VirtualAllocEx failed" << std::endl; 37 | RETURN(false) 38 | } ELSE { 39 | std::cout << "Allocated memory" << std::endl; 40 | } 41 | ENDIF 42 | 43 | bool writeMemory = WriteProcessMemory(handle, alloc, dllPath.c_str(), dllPath.size(), NULL); 44 | 45 | IF (V(writeMemory) == false) { 46 | std::cout << "WriteProcessMemory failed" << std::endl; 47 | RETURN(false) 48 | } ELSE { 49 | std::cout << "Wrote to process memory" << std::endl; 50 | } 51 | ENDIF 52 | 53 | LPTHREAD_START_ROUTINE loadLibraryAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"); 54 | 55 | IF (loadLibraryAddress == NULL) { 56 | std::cout << "loadLibraryAddress not found" << std::endl; 57 | RETURN(false) 58 | } ELSE { 59 | std::cout << "Found loadLibraryAddress" << std::endl; 60 | } 61 | ENDIF 62 | 63 | HANDLE threadHandle = CreateRemoteThread(handle, NULL, N(0), loadLibraryAddress, alloc, N(0), NULL); 64 | 65 | IF (threadHandle == NULL) { 66 | std::cout << "CreateRemoteThread failed" << std::endl; 67 | RETURN(false) 68 | } ELSE { 69 | std::cout << "Created remote thread" << std::endl; 70 | } 71 | ENDIF 72 | 73 | std::cout << "DLL injected" << std::endl; 74 | 75 | RETURN(true) 76 | OBF_END 77 | } 78 | 79 | int main(int argc, char const *argv[]) { 80 | OBF_BEGIN 81 | int processID = getProcessID(); 82 | 83 | IF (std::filesystem::exists("./loader.dll")) { 84 | std::cout << "Found loader.dll" << std::endl; 85 | inject(processID, std::filesystem::absolute("./loader.dll").string()); 86 | 87 | } ELSE { 88 | std::cout << "Could not find loader.dll" << std::endl; 89 | } 90 | ENDIF 91 | 92 | system("pause"); 93 | RETURN(0) 94 | OBF_END 95 | } 96 | -------------------------------------------------------------------------------- /client/Client.java: -------------------------------------------------------------------------------- 1 | package inject.client; 2 | 3 | public class Client { 4 | 5 | // Main native methods 6 | public static native byte[] GetClassBytes(Class clazz); 7 | public static native int SetClassBytes(Class clazz, byte[] classBytes); 8 | public static native String[] GetClassFields(Class clazz, boolean fieldNames); 9 | public static native String[] GetClassMethods(Class clazz, boolean methodNames); 10 | 11 | // JNI native methods 12 | public static native String GetClassMapped(String seargeClassName); 13 | public static native Class FindClass(String seargeClassName); 14 | public static native String GetMethodMapped(String seargeClassName, String seargeMethodName, String seargeMethodDescriptor); 15 | public static native String GetFieldMapped(String seargeClassName, String seargeFieldName); 16 | 17 | public static void test(int times) { 18 | for (int i = 0; i < times; ++i) { 19 | System.out.println("Hello world " + i); 20 | } 21 | 22 | String className = "net/minecraft/client/Minecraft"; 23 | Class mcClass = FindClass(className); 24 | String mappedClass = GetClassMapped(className); 25 | System.out.println(className + " -> " + mappedClass); 26 | 27 | byte[] classBytes = GetClassBytes(mcClass); 28 | System.out.println("Class bytes length: " + classBytes.length); 29 | 30 | int result = SetClassBytes(mcClass, classBytes); 31 | System.out.println("Set class bytes"); 32 | 33 | System.out.println("Class fields: "); 34 | String[] fieldNames = GetClassFields(mcClass, true); 35 | String[] fieldDescriptors = GetClassFields(mcClass, false); 36 | for (int i = 0; i < fieldNames.length; ++i) { 37 | String fieldName = fieldNames[i]; 38 | String fieldDescriptor = fieldDescriptors[i]; 39 | System.out.print(fieldName + " " + fieldDescriptor + (i == fieldNames.length - 1 ? "" : ", ")); 40 | } 41 | System.out.println(); 42 | 43 | System.out.println("Class methods: "); 44 | String[] methodNames = GetClassMethods(mcClass, true); 45 | String[] methodDescriptors = GetClassMethods(mcClass, false); 46 | for (int i = 0; i < methodNames.length; ++i) { 47 | String methodName = methodNames[i]; 48 | String methodDescriptor = methodDescriptors[i]; 49 | System.out.print(methodName + " " + methodDescriptor + (i == methodNames.length - 1 ? "" : ", ")); 50 | } 51 | System.out.println(); 52 | 53 | String fieldName = "theMinecraft"; 54 | String mappedField = GetFieldMapped(className, fieldName); 55 | System.out.println(fieldName + " -> " + mappedField); 56 | 57 | String methodName = "getMinecraft"; 58 | String methodDescriptor = "()Lnet/minecraft/client/Minecraft;"; 59 | String mappedMethod = GetMethodMapped(className, methodName, methodDescriptor); 60 | System.out.println(methodName + " " + methodDescriptor + " -> " + mappedMethod); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /loader/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include "class_loader.h" 2 | #include "jni_natives.h" 3 | #include "main_natives.h" 4 | #include "utils.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | void init_thread(HMODULE hModule) { 15 | jsize jvmCount; 16 | 17 | if (JNI_GetCreatedJavaVMs(&ctx.jvm, 1, &jvmCount) != JNI_OK || jvmCount == 0) { 18 | std::cout << "JVM not found (JNI)" << std::endl; 19 | return; 20 | } 21 | 22 | jint res = ctx.jvm->GetEnv((void **)&ctx.env, JNI_VERSION_1_6); 23 | 24 | if (res == JNI_EDETACHED) { 25 | res = ctx.jvm->AttachCurrentThread((void **)&ctx.env, nullptr); 26 | } 27 | 28 | if (res != JNI_OK) { 29 | std::cout << "Couldn't attach thread (JNI)" << std::endl; 30 | return; 31 | } 32 | 33 | res = ctx.jvm->GetEnv((void **)&ctx.jvmti, JVMTI_VERSION_1_2); 34 | 35 | if (res == JNI_EDETACHED) { 36 | res = ctx.jvm->AttachCurrentThread((void **)&ctx.jvmti, nullptr); 37 | } 38 | 39 | if (res != JNI_OK) { 40 | std::cout << "Couldn't attach thread (JVMTI)" << std::endl; 41 | return; 42 | } 43 | 44 | jvmtiCapabilities tiCapabilities = {0}; 45 | tiCapabilities.can_tag_objects = 1; 46 | tiCapabilities.can_retransform_classes = 1; 47 | tiCapabilities.can_retransform_any_class = 1; 48 | tiCapabilities.can_redefine_classes = 1; 49 | tiCapabilities.can_redefine_any_class = 1; 50 | tiCapabilities.can_generate_all_class_hook_events = 1; 51 | ctx.jvmti->AddCapabilities(&tiCapabilities); 52 | 53 | std::cout << "Loader initialized!" << std::endl; 54 | 55 | setClient(); // Set the client and initialize the mapper 56 | 57 | std::map classPaths = { 58 | {"inject/client/Client", "/client/build/inject/client/Client.class"} 59 | }; 60 | 61 | std::map loadedClasses = classLoader.LoadClient(classPaths); 62 | 63 | jclass clientClass = loadedClasses["Client"]; 64 | 65 | JniNatives::RegisterJniNatives(ctx.env, clientClass); 66 | MainNatives::RegisterMainNatives(ctx.env, clientClass); 67 | 68 | jmethodID testMethod = ctx.env->GetStaticMethodID(clientClass, "test", "(I)V"); 69 | ctx.env->CallStaticObjectMethod(clientClass, testMethod, 3); 70 | 71 | Sleep(5000); // Wait a little bit before detaching the loader (native methods are still in use) 72 | 73 | std::cout << "Detaching loader" << std::endl; 74 | ctx.jvm->DetachCurrentThread(); 75 | FreeLibraryAndExitThread(hModule, EXIT_SUCCESS); 76 | return; 77 | } 78 | 79 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { 80 | switch (dwReason) { 81 | case DLL_PROCESS_ATTACH: 82 | CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)init_thread, hModule, 0, nullptr)); 83 | 84 | case DLL_THREAD_ATTACH: 85 | case DLL_THREAD_DETACH: 86 | case DLL_PROCESS_DETACH: 87 | break; 88 | } 89 | 90 | return TRUE; 91 | } 92 | -------------------------------------------------------------------------------- /loader/include/mapper.h: -------------------------------------------------------------------------------- 1 | #ifndef MAPPER_H 2 | #define MAPPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum class CLIENT { 10 | VANILLA, 11 | FORGE, 12 | FORGE_ESSENTIAL, 13 | LUNAR, // TODO: Implement 14 | BADLION // TODO: Implement 15 | }; 16 | 17 | class Mapper { 18 | private: 19 | CLIENT Client; 20 | 21 | // File string buffers 22 | std::string joined, fields, methods, params, static_methods; 23 | 24 | // searge: obfuscated name (getMinecraft) 25 | // retroguard: mcp obfuscation (method_12345, field_12345) 26 | // proguard: vanilla obfuscation (ave) 27 | 28 | // joined.srg 29 | std::map searge_to_proguard_class_name /*, proguard_to_searge_class_name*/; 30 | std::map> /*proguard_to_retroguard_field_name,*/ searge_to_proguard_field_name, 31 | searge_to_retroguard_field_name; 32 | std::map>> /*proguard_to_retroguard_method_name,*/ 33 | searge_to_proguard_method_name, searge_to_retroguard_method_name; 34 | std::map searge_to_proguard_method_descriptor; 35 | 36 | // fields.csv 37 | std::map retroguard_field_to_searge /*, searge_to_retroguard_field*/; 38 | 39 | // methods.csv 40 | std::map retroguard_method_to_searge /*, searge_to_retroguard_method*/; 41 | 42 | // static_methods.txt 43 | std::set static_methods_searge /*, static_methods_retroguard*/; 44 | 45 | public: 46 | Mapper(); 47 | ~Mapper(); 48 | 49 | // Set the Minecraft client for automatic mapping 50 | // client: The Minecraft client to set. 51 | void SetClient(CLIENT client); 52 | 53 | // Get the Minecraft client 54 | CLIENT GetClient(); 55 | 56 | // Reads a file into a string 57 | // filePath: The path of the file to read. 58 | // out: The string to read the file into. 59 | // binaryFile: Whether or not to open the file using std::ios::binary. 60 | void Load(std::string filePath, std::string &out, bool binaryFile = false); 61 | 62 | // Splits a string into a vector of strings by a delimiter 63 | // str: The string to split. 64 | // delim: The delimiter to split by. 65 | std::vector Split(std::string &str, char delim); 66 | 67 | // Parses the data from the file string buffers into maps 68 | void ParseData(); 69 | 70 | std::string SeargeToProguardClass(std::string seargeClassName); 71 | 72 | std::string SeargeToProguardField(std::string seargeClassName, std::string seargeFieldName); 73 | std::string SeargeToRetroguardField(std::string seargeClassName, std::string seargeFieldName); 74 | 75 | std::string SeargeToProguardMethod(std::string seargeClassName, std::string seargeMethodName, std::string seargeMethodDescriptor); 76 | std::string SeargeToRetroguardMethod(std::string seargeClassName, std::string seargeMethodName, std::string seargeMethodDescriptor); 77 | 78 | bool IsSeargeStaticMethod(std::string seargeMethodName); 79 | 80 | std::string SeargeToFieldDescriptor(std::string seargeClassName); 81 | std::string SeargeToMethodDescriptor(std::string seargeMethodDescriptor); 82 | std::string SeargeToClientClass(std::string seargeClassName); 83 | std::string SeargeToClientField(std::string seargeClassName, std::string seargeFieldName); 84 | std::string SeargeToClientMethod(std::string seargeClassName, std::string seargeMethodName, std::string seargeMethodDescriptor); 85 | }; 86 | 87 | extern Mapper mapper; 88 | 89 | #endif // MAPPER_H 90 | -------------------------------------------------------------------------------- /loader/src/class_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "class_loader.h" 2 | 3 | #include "utils.h" 4 | 5 | ClassLoader::ClassLoader() { 6 | } 7 | 8 | ClassLoader::~ClassLoader() { 9 | ctx.env->DeleteGlobalRef(classLoaderObject); 10 | } 11 | 12 | void ClassLoader::SetMinecraftClient(CLIENT client) { 13 | Client = client; 14 | } 15 | 16 | void ClassLoader::InitializeClassLoaderObject() { 17 | if (classLoaderObject != NULL) { 18 | return; 19 | } 20 | 21 | if (Client == CLIENT::VANILLA) { 22 | // Only used for DefineClass 23 | jclass classLoaderClass = ctx.env->FindClass("java/lang/ClassLoader"); 24 | jmethodID getSystemClassLoaderMethod = ctx.env->GetStaticMethodID(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); 25 | 26 | jobject classLoaderObjectLocal = ctx.env->CallStaticObjectMethod(classLoaderClass, getSystemClassLoaderMethod); 27 | classLoaderObject = ctx.env->NewGlobalRef(classLoaderObjectLocal); 28 | ctx.env->DeleteLocalRef(classLoaderObjectLocal); 29 | 30 | } else if (Client == CLIENT::FORGE) { 31 | jclass launchClass = ctx.env->FindClass("net/minecraft/launchwrapper/Launch"); 32 | 33 | // Get the LaunchClassLoader object 34 | jfieldID classLoaderField = ctx.env->GetStaticFieldID(launchClass, "classLoader", "Lnet/minecraft/launchwrapper/LaunchClassLoader;"); 35 | 36 | jobject classLoaderObjectLocal = ctx.env->GetStaticObjectField(launchClass, classLoaderField); 37 | classLoaderObject = ctx.env->NewGlobalRef(classLoaderObjectLocal); 38 | ctx.env->DeleteLocalRef(classLoaderObjectLocal); 39 | 40 | } else if (Client == CLIENT::FORGE_ESSENTIAL) { 41 | // Find an instance of the RelaunchClassLoader class 42 | jclass relaunchClassLoaderClass = ctx.env->FindClass("gg/essential/loader/stage2/relaunch/RelaunchClassLoader"); 43 | 44 | auto heap_object_callback = [](jlong class_tag, jlong size, jlong *tag_ptr, void *user_data) { 45 | *tag_ptr = 1337; 46 | return JVMTI_ITERATION_ABORT; // We only need one instance 47 | }; 48 | 49 | ctx.jvmti->IterateOverInstancesOfClass(relaunchClassLoaderClass, JVMTI_HEAP_OBJECT_EITHER, heap_object_callback, NULL); 50 | 51 | jlong tag = 1337; 52 | jint count; 53 | jobject *instances; 54 | ctx.jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL); 55 | 56 | // Get the instance 57 | jobject relaunchClassLoaderObject = instances[0]; 58 | ctx.jvmti->Deallocate((unsigned char *)instances); 59 | 60 | // Get the RelaunchClassLoader findClass method 61 | jmethodID relaunchFindClassMethod = ctx.env->GetMethodID(relaunchClassLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); 62 | 63 | jstring launchClassString = ctx.env->NewStringUTF("net.minecraft.launchwrapper.Launch"); 64 | jclass launchClass = (jclass)ctx.env->CallObjectMethod(relaunchClassLoaderObject, relaunchFindClassMethod, launchClassString); 65 | 66 | // Get the LaunchClassLoader object 67 | jfieldID classLoaderField = ctx.env->GetStaticFieldID(launchClass, "classLoader", "Lnet/minecraft/launchwrapper/LaunchClassLoader;"); 68 | 69 | jobject classLoaderObjectLocal = ctx.env->GetStaticObjectField(launchClass, classLoaderField); 70 | classLoaderObject = ctx.env->NewGlobalRef(classLoaderObjectLocal); 71 | ctx.env->DeleteLocalRef(classLoaderObjectLocal); 72 | } 73 | } 74 | 75 | void ClassLoader::InitializeFindClassMethod() { 76 | if (findClassMethod != NULL) { 77 | return; 78 | } 79 | 80 | if (Client == CLIENT::VANILLA) { 81 | return; 82 | } 83 | 84 | jclass launchClassLoaderClass = ctx.env->GetObjectClass(classLoaderObject); 85 | findClassMethod = ctx.env->GetMethodID(launchClassLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); 86 | } 87 | 88 | jclass ClassLoader::FindClass(JNIEnv *jniEnv, std::string seargeClassName) { 89 | std::string clientClassName = mapper.SeargeToClientClass(seargeClassName); 90 | 91 | if (Client == CLIENT::VANILLA) { 92 | return jniEnv->FindClass(clientClassName.c_str()); 93 | } 94 | 95 | InitializeClassLoaderObject(); 96 | InitializeFindClassMethod(); 97 | 98 | jstring seargeClassNameJStr = jniEnv->NewStringUTF(clientClassName.c_str()); 99 | 100 | return (jclass)jniEnv->CallObjectMethod(classLoaderObject, findClassMethod, seargeClassNameJStr); 101 | } 102 | 103 | jclass ClassLoader::FindClass(std::string seargeClassName) { 104 | return FindClass(ctx.env, seargeClassName); 105 | } 106 | 107 | jclass ClassLoader::LoadClass(std::string fullClassName, std::string classPath) { 108 | InitializeClassLoaderObject(); 109 | 110 | std::string classData; 111 | mapper.Load(getParentPath() + classPath, classData, true); 112 | return ctx.env->DefineClass(fullClassName.c_str(), classLoaderObject, (jbyte *)classData.c_str(), classData.size()); 113 | } 114 | 115 | std::map ClassLoader::LoadClient(std::map classPaths) { 116 | std::map loadedClasses; 117 | 118 | for (auto [fullClassName, classPath] : classPaths) { 119 | size_t lastSlash = fullClassName.find_last_of('/'); 120 | std::string className = fullClassName.substr(lastSlash + 1); 121 | loadedClasses[className] = LoadClass(fullClassName, classPath); 122 | } 123 | 124 | return loadedClasses; 125 | } 126 | 127 | std::map ClassLoader::LoadJar(std::string jarPath) { 128 | std::map classPaths; 129 | 130 | // TODO: implement method to get all classes in a jar 131 | 132 | return LoadClient(classPaths); 133 | } 134 | 135 | ClassLoader classLoader; 136 | -------------------------------------------------------------------------------- /loader/src/jni_natives.cpp: -------------------------------------------------------------------------------- 1 | #include "jni_natives.h" 2 | 3 | #include "class_loader.h" 4 | #include "mapper.h" 5 | #include "utils.h" 6 | 7 | #include 8 | 9 | namespace JniNatives { 10 | 11 | /* 12 | GetVersion, 13 | */ 14 | 15 | /* 16 | FindClass, 17 | */ 18 | jstring GetClassMapped(JNIEnv *jniEnv, jobject, jstring seargeClassName) { 19 | std::string mappedClass = mapper.SeargeToClientClass(jstringToString(jniEnv, seargeClassName)); 20 | return jniEnv->NewStringUTF(mappedClass.c_str()); 21 | } 22 | 23 | jclass FindClass(JNIEnv *jniEnv, jobject, jstring seargeClassName) { 24 | return classLoader.FindClass(jniEnv, jstringToString(jniEnv, seargeClassName)); 25 | } 26 | /* 27 | GetMethodID, 28 | 29 | CallObjectMethod, 30 | CallObjectMethodV, 31 | CallObjectMethodA, 32 | CallBooleanMethod, 33 | CallBooleanMethodV, 34 | CallBooleanMethodA, 35 | CallByteMethod, 36 | CallByteMethodV, 37 | CallByteMethodA, 38 | CallCharMethod, 39 | CallCharMethodV, 40 | CallCharMethodA, 41 | CallShortMethod, 42 | CallShortMethodV, 43 | CallShortMethodA, 44 | CallIntMethod, 45 | CallIntMethodV, 46 | CallIntMethodA, 47 | CallLongMethod, 48 | CallLongMethodV, 49 | CallLongMethodA, 50 | CallFloatMethod, 51 | CallFloatMethodV, 52 | CallFloatMethodA, 53 | CallDoubleMethod, 54 | CallDoubleMethodV, 55 | CallDoubleMethodA, 56 | CallVoidMethod, 57 | CallVoidMethodV, 58 | CallVoidMethodA, 59 | */ 60 | 61 | jstring GetMethodMapped(JNIEnv *jniEnv, jobject, jstring seargeClassName, jstring seargeMethodName, jstring seargeMethodDescriptor) { 62 | std::string mappedMethod = mapper.SeargeToClientMethod(jstringToString(jniEnv, seargeClassName), jstringToString(jniEnv, seargeMethodName), 63 | jstringToString(jniEnv, seargeMethodDescriptor)); 64 | return jniEnv->NewStringUTF(mappedMethod.c_str()); 65 | } 66 | 67 | /* 68 | GetStaticMethodID, 69 | 70 | CallStaticObjectMethod, 71 | CallStaticObjectMethodV, 72 | CallStaticObjectMethodA, 73 | CallStaticBooleanMethod, 74 | CallStaticBooleanMethodV, 75 | CallStaticBooleanMethodA, 76 | CallStaticByteMethod, 77 | CallStaticByteMethodV, 78 | CallStaticByteMethodA, 79 | CallStaticCharMethod, 80 | CallStaticCharMethodV, 81 | CallStaticCharMethodA, 82 | CallStaticShortMethod, 83 | CallStaticShortMethodV, 84 | CallStaticShortMethodA, 85 | CallStaticIntMethod, 86 | CallStaticIntMethodV, 87 | CallStaticIntMethodA, 88 | CallStaticLongMethod, 89 | CallStaticLongMethodV, 90 | CallStaticLongMethodA, 91 | CallStaticFloatMethod, 92 | CallStaticFloatMethodV, 93 | CallStaticFloatMethodA, 94 | CallStaticDoubleMethod, 95 | CallStaticDoubleMethodV, 96 | CallStaticDoubleMethodA, 97 | CallStaticVoidMethod, 98 | CallStaticVoidMethodV, 99 | CallStaticVoidMethodA, 100 | */ 101 | 102 | /* 103 | CallNonvirtualObjectMethod, 104 | CallNonvirtualObjectMethodV, 105 | CallNonvirtualObjectMethodA, 106 | CallNonvirtualBooleanMethod, 107 | CallNonvirtualBooleanMethodV, 108 | CallNonvirtualBooleanMethodA, 109 | CallNonvirtualByteMethod, 110 | CallNonvirtualByteMethodV, 111 | CallNonvirtualByteMethodA, 112 | CallNonvirtualCharMethod, 113 | CallNonvirtualCharMethodV, 114 | CallNonvirtualCharMethodA, 115 | CallNonvirtualShortMethod, 116 | CallNonvirtualShortMethodV, 117 | CallNonvirtualShortMethodA, 118 | CallNonvirtualIntMethod, 119 | CallNonvirtualIntMethodV, 120 | CallNonvirtualIntMethodA, 121 | CallNonvirtualLongMethod, 122 | CallNonvirtualLongMethodV, 123 | CallNonvirtualLongMethodA, 124 | CallNonvirtualFloatMethod, 125 | CallNonvirtualFloatMethodV, 126 | CallNonvirtualFloatMethodA, 127 | CallNonvirtualDoubleMethod, 128 | CallNonvirtualDoubleMethodV, 129 | CallNonvirtualDoubleMethodA, 130 | CallNonvirtualVoidMethod, 131 | CallNonvirtualVoidMethodV, 132 | CallNonvirtualVoidMethodA, 133 | */ 134 | 135 | /* 136 | GetFieldID, 137 | 138 | GetObjectField, 139 | GetBooleanField, 140 | GetByteField, 141 | GetCharField, 142 | GetShortField, 143 | GetIntField, 144 | GetLongField, 145 | GetFloatField, 146 | GetDoubleField, 147 | SetObjectField, 148 | SetBooleanField, 149 | SetByteField, 150 | SetCharField, 151 | SetShortField, 152 | SetIntField, 153 | SetLongField, 154 | SetFloatField, 155 | SetDoubleField, 156 | */ 157 | 158 | jstring GetFieldMapped(JNIEnv *jniEnv, jobject, jstring seargeClassName, jstring seargeFieldName) { 159 | std::string mappedField = mapper.SeargeToClientField(jstringToString(jniEnv, seargeClassName), jstringToString(jniEnv, seargeFieldName)); 160 | return jniEnv->NewStringUTF(mappedField.c_str()); 161 | } 162 | 163 | 164 | /* 165 | GetStaticFieldID, 166 | 167 | GetStaticObjectField, 168 | GetStaticBooleanField, 169 | GetStaticByteField, 170 | GetStaticCharField, 171 | GetStaticShortField, 172 | GetStaticIntField, 173 | GetStaticLongField, 174 | GetStaticFloatField, 175 | GetStaticDoubleField, 176 | 177 | SetStaticObjectField, 178 | SetStaticBooleanField, 179 | SetStaticByteField, 180 | SetStaticCharField, 181 | SetStaticShortField, 182 | SetStaticIntField, 183 | SetStaticLongField, 184 | SetStaticFloatField, 185 | SetStaticDoubleField, 186 | */ 187 | 188 | void RegisterJniNatives(JNIEnv *jniEnv, jclass clazz) { 189 | #define DEFINE_NATIVE_METHOD(METHOD_NAME, METHOD_SIGNATURE, NATIVE_FUNCTION) {(char *)METHOD_NAME, (char *)METHOD_SIGNATURE, (void *)NATIVE_FUNCTION} 190 | 191 | JNINativeMethod natives[] = { 192 | DEFINE_NATIVE_METHOD("GetClassMapped", "(Ljava/lang/String;)Ljava/lang/String;", GetClassMapped), 193 | DEFINE_NATIVE_METHOD("FindClass", "(Ljava/lang/String;)Ljava/lang/Class;", FindClass), 194 | DEFINE_NATIVE_METHOD("GetMethodMapped", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", GetMethodMapped), 195 | DEFINE_NATIVE_METHOD("GetFieldMapped", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", GetFieldMapped)}; 196 | 197 | #undef DEFINE_NATIVE_METHOD 198 | 199 | jniEnv->RegisterNatives(clazz, natives, sizeof(natives) / sizeof(natives[0])); 200 | } 201 | 202 | } // namespace JniNatives 203 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series of 85 | actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within the 112 | community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.1, available at 118 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 119 | 120 | Community Impact Guidelines were inspired by 121 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 122 | 123 | For answers to common questions about this code of conduct, see the FAQ at 124 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 125 | [https://www.contributor-covenant.org/translations][translations]. 126 | 127 | [homepage]: https://www.contributor-covenant.org 128 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 129 | [Mozilla CoC]: https://github.com/mozilla/diversity 130 | [FAQ]: https://www.contributor-covenant.org/faq 131 | [translations]: https://www.contributor-covenant.org/translations 132 | -------------------------------------------------------------------------------- /loader/src/main_natives.cpp: -------------------------------------------------------------------------------- 1 | #include "main_natives.h" 2 | 3 | #include "utils.h" 4 | 5 | //#include // For debugging 6 | #include 7 | #include 8 | #include 9 | 10 | namespace MainNatives { 11 | 12 | ClassData::~ClassData() { 13 | delete[] bytes; 14 | bytes = nullptr; 15 | } 16 | 17 | thread_local char *targetClassSigGlobal = nullptr; 18 | thread_local ClassData *classDataGlobal = nullptr; 19 | 20 | jvmtiError EnableClassFileLoadHook(JNIEnv *jniEnv, char *targetClassSig, ClassData *classData) { 21 | targetClassSigGlobal = targetClassSig; 22 | classDataGlobal = classData; 23 | 24 | auto ClassFileLoadHookCallback = [](jvmtiEnv *jvmti_env, JNIEnv *jni_env, jclass class_being_redefined, jobject loader, const char *name, 25 | jobject protection_domain, jint class_data_len, const unsigned char *class_data, jint *new_class_data_len, 26 | unsigned char **new_class_data) { 27 | char *classSignature; 28 | ctx.jvmti->GetClassSignature(class_being_redefined, &classSignature, nullptr); 29 | 30 | if (strcmp(classSignature, targetClassSigGlobal) != 0) { 31 | return; 32 | } 33 | 34 | ctx.jvmti->Deallocate((unsigned char *)classSignature); 35 | 36 | classDataGlobal->length = class_data_len; 37 | classDataGlobal->bytes = new unsigned char[class_data_len]; 38 | 39 | const unsigned char **actual_class_data = (const unsigned char **)class_data; 40 | 41 | memcpy(classDataGlobal->bytes, actual_class_data, class_data_len); 42 | 43 | /* 44 | std::ofstream file; 45 | file.open((getParentPath() + "/classbytes.txt").c_str(), std::ios::out | std::ios::binary); 46 | file.write((const char*)classDataGlobal->bytes, classDataGlobal->length); 47 | file.close(); 48 | std::cout << "Wrote to file" << std::endl; 49 | */ 50 | }; 51 | 52 | // Set the callback 53 | jvmtiEventCallbacks eventCallbacks = {0}; 54 | eventCallbacks.ClassFileLoadHook = ClassFileLoadHookCallback; 55 | jvmtiError res = ctx.jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks)); 56 | 57 | if (res != JVMTI_ERROR_NONE) { 58 | std::cout << "Couldn't set ClassFileLoadHookCallback" << std::endl; 59 | return res; 60 | } 61 | 62 | // Enable the notification for our class file load hook 63 | res = ctx.jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); 64 | 65 | if (res != JVMTI_ERROR_NONE) { 66 | std::cout << "Couldn't set SetEventNotificationMode" << std::endl; 67 | return res; 68 | } 69 | 70 | return res; 71 | } 72 | 73 | jvmtiError DisableClassFileLoadHook(JNIEnv *jniEnv) { 74 | ctx.jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); 75 | return ctx.jvmti->SetEventCallbacks(nullptr, 0); 76 | } 77 | 78 | jbyteArray GetClassBytes(JNIEnv *jniEnv, jobject, jclass clazz) { 79 | char *classSignature; 80 | ctx.jvmti->GetClassSignature(clazz, &classSignature, nullptr); 81 | 82 | ClassData classData = {0}; 83 | 84 | EnableClassFileLoadHook(jniEnv, classSignature, &classData); 85 | jclass classes[] = {clazz}; 86 | jvmtiError res = ctx.jvmti->RetransformClasses(1, classes); 87 | DisableClassFileLoadHook(jniEnv); 88 | 89 | ctx.jvmti->Deallocate((unsigned char *)classSignature); 90 | 91 | // TODO: Check if res is an error 92 | 93 | jbyteArray classBytes = jniEnv->NewByteArray(classData.length); 94 | jniEnv->SetByteArrayRegion(classBytes, 0, classData.length, (jbyte *)classData.bytes); 95 | 96 | return classBytes; 97 | } 98 | 99 | jvmtiError SetClassBytes(JNIEnv *jniEnv, jobject, jclass clazz, jbyteArray classBytes) { 100 | jsize classBytesLength = jniEnv->GetArrayLength(classBytes); 101 | jbyte *classBytesJByte = jniEnv->GetByteArrayElements(classBytes, nullptr); 102 | 103 | jvmtiClassDefinition classDefinitions[] = {{clazz, classBytesLength, (unsigned char *)classBytesJByte}}; 104 | 105 | jvmtiError res = ctx.jvmti->RedefineClasses(1, classDefinitions); 106 | 107 | return res; 108 | } 109 | 110 | jobjectArray GetClassFields(JNIEnv *jniEnv, jobject, jclass clazz, jboolean fieldNames) { 111 | jint fieldCount; 112 | jfieldID *fieldIDs; 113 | 114 | jvmtiError res = ctx.jvmti->GetClassFields(clazz, &fieldCount, &fieldIDs); 115 | if (res != JVMTI_ERROR_NONE) { 116 | std::cout << "jvmti->GetClassFields error: " << res << std::endl; 117 | return nullptr; 118 | } 119 | 120 | jclass stringClass = jniEnv->FindClass("java/lang/String"); 121 | jobjectArray fields = jniEnv->NewObjectArray(fieldCount, stringClass, nullptr); 122 | 123 | for (int i = 0; i < fieldCount; ++i) { 124 | char *fieldString; 125 | 126 | if (fieldNames) { 127 | res = ctx.jvmti->GetFieldName(clazz, fieldIDs[i], &fieldString, nullptr, nullptr); 128 | } else { 129 | res = ctx.jvmti->GetFieldName(clazz, fieldIDs[i], nullptr, &fieldString, nullptr); 130 | } 131 | 132 | jstring fieldJString = jniEnv->NewStringUTF(fieldString); 133 | 134 | jniEnv->SetObjectArrayElement(fields, i, fieldJString); 135 | 136 | ctx.jvmti->Deallocate((unsigned char *)fieldString); 137 | ctx.env->DeleteLocalRef(fieldJString); 138 | } 139 | 140 | ctx.jvmti->Deallocate((unsigned char *)fieldIDs); 141 | 142 | return fields; 143 | } 144 | 145 | jobjectArray GetClassMethods(JNIEnv *jniEnv, jobject, jclass clazz, jboolean methodNames) { 146 | jint methodCount; 147 | jmethodID *methodIDs; 148 | 149 | jvmtiError res = ctx.jvmti->GetClassMethods(clazz, &methodCount, &methodIDs); 150 | if (res != JVMTI_ERROR_NONE) { 151 | std::cout << "jvmti->GetClassMethods error: " << res << std::endl; 152 | return nullptr; 153 | } 154 | 155 | jclass stringClass = jniEnv->FindClass("java/lang/String"); 156 | jobjectArray methods = jniEnv->NewObjectArray(methodCount, stringClass, nullptr); 157 | 158 | for (int i = 0; i < methodCount; ++i) { 159 | char *methodString; 160 | 161 | if (methodNames) { 162 | res = ctx.jvmti->GetMethodName(methodIDs[i], &methodString, nullptr, nullptr); 163 | } else { 164 | res = ctx.jvmti->GetMethodName(methodIDs[i], nullptr, &methodString, nullptr); 165 | } 166 | 167 | jstring methodJString = jniEnv->NewStringUTF(methodString); 168 | 169 | jniEnv->SetObjectArrayElement(methods, i, methodJString); 170 | 171 | ctx.jvmti->Deallocate((unsigned char *)methodString); 172 | ctx.env->DeleteLocalRef(methodJString); 173 | } 174 | 175 | ctx.jvmti->Deallocate((unsigned char *)methodIDs); 176 | 177 | return methods; 178 | } 179 | 180 | void RegisterMainNatives(JNIEnv *jniEnv, jclass clazz) { 181 | #define DEFINE_NATIVE_METHOD(METHOD_NAME, METHOD_SIGNATURE, NATIVE_FUNCTION) {(char *)METHOD_NAME, (char *)METHOD_SIGNATURE, (void *)NATIVE_FUNCTION} 182 | 183 | JNINativeMethod natives[] = { 184 | DEFINE_NATIVE_METHOD("GetClassBytes", "(Ljava/lang/Class;)[B", GetClassBytes), 185 | DEFINE_NATIVE_METHOD("SetClassBytes", "(Ljava/lang/Class;[B)I", SetClassBytes), 186 | DEFINE_NATIVE_METHOD("GetClassFields", "(Ljava/lang/Class;Z)[Ljava/lang/String;", GetClassFields), 187 | DEFINE_NATIVE_METHOD("GetClassMethods", "(Ljava/lang/Class;Z)[Ljava/lang/String;", GetClassMethods), 188 | }; 189 | 190 | #undef DEFINE_NATIVE_METHOD 191 | 192 | jniEnv->RegisterNatives(clazz, natives, sizeof(natives) / sizeof(natives[0])); 193 | } 194 | 195 | } // namespace MainNatives 196 | -------------------------------------------------------------------------------- /loader/src/mapper.cpp: -------------------------------------------------------------------------------- 1 | #include "mapper.h" 2 | 3 | #include "utils.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | Mapper::Mapper() { 11 | Load(getParentPath() + "/mappings/joined.srg", joined); 12 | Load(getParentPath() + "/mappings/fields.csv", fields); 13 | Load(getParentPath() + "/mappings/methods.csv", methods); 14 | // Load(getParentPath() + "/mappings/params.csv", params); 15 | Load(getParentPath() + "/mappings/static_methods.txt", static_methods); 16 | 17 | ParseData(); 18 | 19 | // Free memory from the buffers 20 | std::string().swap(joined); 21 | std::string().swap(fields); 22 | std::string().swap(methods); 23 | // std::string().swap(params); 24 | std::string().swap(static_methods); 25 | } 26 | 27 | Mapper::~Mapper() { 28 | } 29 | 30 | void Mapper::SetClient(CLIENT client) { 31 | Client = client; 32 | } 33 | 34 | CLIENT Mapper::GetClient() { 35 | return Client; 36 | } 37 | 38 | void Mapper::Load(std::string filePath, std::string &out, bool binaryFile) { 39 | std::ifstream inFile(filePath, (binaryFile ? std::ios::in | std::ios::binary : std::ios::in)); 40 | inFile.seekg(0, std::ios::end); 41 | out.resize(inFile.tellg()); 42 | inFile.seekg(0, std::ios::beg); 43 | inFile.read(&out[0], out.size()); 44 | inFile.close(); 45 | } 46 | 47 | std::vector Mapper::Split(std::string &str, char delim) { 48 | std::istringstream iss(str); 49 | std::string element; 50 | std::vector elements; 51 | 52 | while (std::getline(iss, element, delim)) { 53 | elements.push_back(element); 54 | } 55 | 56 | return elements; 57 | } 58 | 59 | void Mapper::ParseData() { 60 | // fields.csv 61 | std::istringstream iss(fields); 62 | std::string line; 63 | 64 | iss.ignore(std::numeric_limits::max(), '\n'); // ignore first line (searge,name,side,desc) 65 | 66 | while (std::getline(iss, line)) { 67 | std::vector splitted = Split(line, ','); 68 | 69 | retroguard_field_to_searge[splitted[0]] = splitted[1]; 70 | // searge_to_retroguard_field[splitted[1]] = splitted[0]; 71 | } 72 | 73 | iss.clear(); 74 | 75 | // methods.csv 76 | iss.str(methods); 77 | 78 | iss.ignore(std::numeric_limits::max(), '\n'); // ignore first line (searge,name,side,desc) 79 | 80 | while (std::getline(iss, line)) { 81 | std::vector splitted = Split(line, ','); 82 | 83 | retroguard_method_to_searge[splitted[0]] = splitted[1]; 84 | // searge_to_retroguard_method[splitted[1]] = splitted[0]; 85 | } 86 | 87 | iss.clear(); 88 | 89 | // static_methods.txt 90 | iss.str(static_methods); 91 | 92 | while (std::getline(iss, line)) { 93 | // static_methods_retroguard.insert(line); 94 | static_methods_searge.insert(retroguard_method_to_searge[line]); 95 | } 96 | 97 | iss.clear(); 98 | 99 | // joined.retroguard 100 | iss.str(joined); 101 | 102 | while (std::getline(iss, line)) { 103 | std::vector splitted = Split(line, ' '); 104 | 105 | if (splitted[0] == "PK:") { 106 | // Package 107 | continue; 108 | 109 | } else if (splitted[0] == "CL:") { 110 | // Class 111 | // splitted[1] = proguard class name 112 | // splitted[2] = searge class name 113 | // CL: ave net/minecraft/client/Minecraft 114 | // ave = net/minecraft/client/Minecraft 115 | std::string proguard_class_name = splitted[1], searge_class_name = splitted[2]; 116 | searge_to_proguard_class_name[searge_class_name] = proguard_class_name; 117 | // proguard_to_searge_class_name[splitted[1]] = splitted[2]; 118 | 119 | } else if (splitted[0] == "FD:") { 120 | // Field 121 | // splitted[1] = proguard class name/proguard field name 122 | // splitted[2] = searge class name/retroguard field name 123 | // FD: ave/f net/minecraft/client/Minecraft/field_71441_e 124 | // ave = net/minecraft/client/Minecraft, f = theWorld, field_71441_e = theWorld 125 | size_t lastSlash = splitted[1].find_last_of('/'); 126 | std::string proguard_class_name = splitted[1].substr(0, lastSlash), proguard_field_name = splitted[1].substr(lastSlash + 1); 127 | 128 | lastSlash = splitted[2].find_last_of('/'); 129 | std::string searge_class_name = splitted[2].substr(0, lastSlash), retroguard_field_name = splitted[2].substr(lastSlash + 1); 130 | 131 | // proguard_to_retroguard_field_name[proguard_class_name][proguard_field_name] = retroguard_field_name; 132 | 133 | std::string searge_field_name = retroguard_field_to_searge[retroguard_field_name]; 134 | 135 | searge_to_proguard_field_name[searge_class_name][searge_field_name] = proguard_field_name; 136 | searge_to_retroguard_field_name[searge_class_name][searge_field_name] = retroguard_field_name; 137 | 138 | } else if (splitted[0] == "MD:") { 139 | // Method 140 | // splitted[1] = proguard class name/proguard method name 141 | // splitted[2] = proguard method descriptor 142 | // splitted[3] = searge class name/retroguard method name 143 | // splitted[4] = searge method descriptor 144 | // MD: ave/A ()Lave; net/minecraft/client/Minecraft/func_71410_x ()Lnet/minecraft/client/Minecraft; 145 | // ave = net/minecraft/client/Minecraft, A = getMinecraft(), ()Lave; = descriptor, func_71410_x = getMinecraft() 146 | size_t lastSlash = splitted[1].find_last_of('/'); 147 | std::string proguard_class_name = splitted[1].substr(0, lastSlash), proguard_method_name = splitted[1].substr(lastSlash + 1); 148 | 149 | lastSlash = splitted[3].find_last_of('/'); 150 | std::string searge_class_name = splitted[3].substr(0, lastSlash), retroguard_method_name = splitted[3].substr(lastSlash + 1); 151 | 152 | std::string proguard_method_descriptor = splitted[2]; 153 | // proguard_to_retroguard_method_name[proguard_class_name][proguard_method_name][proguard_method_descriptor] = retroguard_method_name; 154 | 155 | std::string searge_method_descriptor = splitted[4]; 156 | searge_to_proguard_method_descriptor[searge_method_descriptor] = proguard_method_descriptor; 157 | 158 | std::string searge_method_name = retroguard_method_to_searge[retroguard_method_name]; 159 | searge_to_proguard_method_name[searge_class_name][searge_method_name][searge_method_descriptor] = proguard_method_name; 160 | searge_to_retroguard_method_name[searge_class_name][searge_method_name][searge_method_descriptor] = retroguard_method_name; 161 | } 162 | } 163 | } 164 | 165 | std::string Mapper::SeargeToProguardClass(std::string seargeClassName) { 166 | return searge_to_proguard_class_name.at(seargeClassName); 167 | } 168 | 169 | 170 | std::string Mapper::SeargeToProguardField(std::string seargeClassName, std::string seargeFieldName) { 171 | return searge_to_proguard_field_name.at(seargeClassName).at(seargeFieldName); 172 | } 173 | 174 | std::string Mapper::SeargeToRetroguardField(std::string seargeClassName, std::string seargeFieldName) { 175 | return searge_to_retroguard_field_name.at(seargeClassName).at(seargeFieldName); 176 | } 177 | 178 | 179 | std::string Mapper::SeargeToProguardMethod(std::string seargeClassName, std::string seargeMethodName, std::string seargeMethodDescriptor) { 180 | return searge_to_proguard_method_name.at(seargeClassName).at(seargeMethodName).at(seargeMethodDescriptor); 181 | } 182 | 183 | std::string Mapper::SeargeToRetroguardMethod(std::string seargeClassName, std::string seargeMethodName, std::string seargeMethodDescriptor) { 184 | return searge_to_retroguard_method_name.at(seargeClassName).at(seargeMethodName).at(seargeMethodDescriptor); 185 | } 186 | 187 | 188 | bool Mapper::IsSeargeStaticMethod(std::string seargeMethodName) { 189 | return static_methods_searge.count(seargeMethodName); 190 | } 191 | 192 | 193 | std::string Mapper::SeargeToFieldDescriptor(std::string seargeClassName) { 194 | return "L" + SeargeToClientClass(seargeClassName) + ";"; 195 | } 196 | 197 | std::string Mapper::SeargeToMethodDescriptor(std::string seargeMethodDescriptor) { 198 | if (Client == CLIENT::VANILLA) { 199 | return searge_to_proguard_method_descriptor.at(seargeMethodDescriptor); 200 | 201 | } else if (Client == CLIENT::FORGE || Client == CLIENT::FORGE_ESSENTIAL) { 202 | return seargeMethodDescriptor; 203 | 204 | } else { 205 | return std::string(); 206 | } 207 | } 208 | 209 | std::string Mapper::SeargeToClientClass(std::string seargeClassName) { 210 | if (Client == CLIENT::VANILLA) { 211 | return SeargeToProguardClass(seargeClassName); 212 | 213 | } else if (Client == CLIENT::FORGE || Client == CLIENT::FORGE_ESSENTIAL) { 214 | return seargeClassName; 215 | 216 | } else { 217 | return std::string(); 218 | } 219 | } 220 | 221 | std::string Mapper::SeargeToClientField(std::string seargeClassName, std::string seargeFieldName) { 222 | if (Client == CLIENT::VANILLA) { 223 | return SeargeToProguardField(seargeClassName, seargeFieldName); 224 | 225 | } else if (Client == CLIENT::FORGE || Client == CLIENT::FORGE_ESSENTIAL) { 226 | return SeargeToRetroguardField(seargeClassName, seargeFieldName); 227 | 228 | } else { 229 | return std::string(); 230 | } 231 | } 232 | 233 | std::string Mapper::SeargeToClientMethod(std::string seargeClassName, std::string seargeMethodName, std::string seargeMethodDescriptor) { 234 | if (Client == CLIENT::VANILLA) { 235 | return SeargeToProguardMethod(seargeClassName, seargeMethodName, seargeMethodDescriptor); 236 | 237 | } else if (Client == CLIENT::FORGE || Client == CLIENT::FORGE_ESSENTIAL) { 238 | return SeargeToRetroguardMethod(seargeClassName, seargeMethodName, seargeMethodDescriptor); 239 | 240 | } else { 241 | return std::string(); 242 | } 243 | } 244 | 245 | Mapper mapper; 246 | -------------------------------------------------------------------------------- /injector/instr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, Ferenc Deak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | **/ 21 | 22 | #ifndef INSTR_H 23 | #define INSTR_H 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifndef MAX_BOGUS_IMPLEMENTATIONS 35 | #define MAX_BOGUS_IMPLEMENTATIONS 3 36 | #endif 37 | 38 | namespace obf 39 | { 40 | 41 | // Compile time random number generator from https://github.com/andrivet/ADVobfuscator 42 | /* 43 | * Written by Sebastien Andrivet 44 | * Copyright (c) 2010-2015 - Sebastien Andrivet All rights reserved. 45 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 46 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 47 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 48 | * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 49 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 50 | */ 51 | constexpr char time[] = __TIME__; 52 | constexpr int DigitToInt(char c) { return c - '0'; } 53 | const int seed = DigitToInt(time[7]) + 54 | DigitToInt(time[6]) * 10 + 55 | DigitToInt(time[4]) * 60 + 56 | DigitToInt(time[3]) * 600 + 57 | DigitToInt(time[1]) * 3600 + 58 | DigitToInt(time[0]) * 36000; 59 | 60 | template 61 | struct MetaRandomGenerator final 62 | { 63 | private: 64 | static constexpr unsigned a = 16807; 65 | static constexpr unsigned m = 2147483647; 66 | static constexpr unsigned s = MetaRandomGenerator::value; 67 | static constexpr unsigned lo = a * (s & 0xFFFF); 68 | static constexpr unsigned hi = a * (s >> 16); 69 | static constexpr unsigned lo2 = lo + ((hi & 0x7FFF) << 16); 70 | static constexpr unsigned hi2 = hi >> 15; 71 | static constexpr unsigned lo3 = lo2 + hi; 72 | 73 | public: 74 | static constexpr unsigned max = m; 75 | static constexpr unsigned value = lo3 > m ? lo3 - m : lo3; 76 | }; 77 | 78 | template<> 79 | struct MetaRandomGenerator<0> final 80 | { 81 | static constexpr unsigned value = seed; 82 | }; 83 | 84 | template 85 | struct MetaRandom final 86 | { 87 | static const int value = MetaRandomGenerator::value % M; 88 | }; 89 | 90 | #define COMP_ASSIGNMENT_OPERATOR(x) \ 91 | refholder& operator x##= (const refholder& ov) { v x##= ov.v; return *this;} \ 92 | refholder& operator x##= (const refholder&& ov) { v x##= ov.v; return *this;} \ 93 | refholder& operator x##= (const T& ov) { v x##= ov; return *this;} \ 94 | refholder& operator x##= (const T&& ov) { v x##= ov; return *this;} \ 95 | refholder& operator x##= (T& ov) { v x##= ov; return *this;} 96 | 97 | 98 | #define COMPARISON_OPERATOR(x) \ 99 | bool operator x (const T& ov) { return (v x ov); } 100 | 101 | /* simple reference holder class, mostly for dealing with holding variables */ 102 | template 103 | class refholder final 104 | { 105 | public: 106 | /* Construction, destruction */ 107 | refholder() = delete; 108 | refholder(T& pv) : v(pv) {} 109 | refholder(const refholder& ov) : v(ov.v) {} 110 | refholder(T&&) = delete; 111 | 112 | ~refholder() = default; 113 | 114 | /* Assignment */ 115 | refholder& operator = (const T& ov) { v = ov; return *this;} 116 | refholder& operator = (const refholder& ov ) { v = ov.v; return *this; } 117 | 118 | /* Comparison */ 119 | bool operator == (const T& ov) { return !(v ^ ov); } 120 | bool operator != (const T& ov) { return !operator ==(ov); } 121 | COMPARISON_OPERATOR(>=) 122 | COMPARISON_OPERATOR(<=) 123 | COMPARISON_OPERATOR(>) 124 | COMPARISON_OPERATOR(<) 125 | 126 | /* Conversion to the real type */ 127 | operator T() {return v;} 128 | 129 | /* Pre increment/decrement operators */ 130 | refholder& operator++() { ++ v; return *this; } 131 | refholder& operator--() { -- v; return *this; } 132 | 133 | /* post increment/decrement */ 134 | refholder operator++(int) { refholder rv(*this); operator ++(); return rv; } 135 | refholder operator--(int) { refholder rv(*this); operator --(); return rv; } 136 | 137 | /* Compound assignments */ 138 | COMP_ASSIGNMENT_OPERATOR(+) 139 | COMP_ASSIGNMENT_OPERATOR(-) 140 | COMP_ASSIGNMENT_OPERATOR(*) 141 | COMP_ASSIGNMENT_OPERATOR(/) 142 | COMP_ASSIGNMENT_OPERATOR(%) 143 | COMP_ASSIGNMENT_OPERATOR(<<) 144 | COMP_ASSIGNMENT_OPERATOR(>>) 145 | COMP_ASSIGNMENT_OPERATOR(&) 146 | COMP_ASSIGNMENT_OPERATOR(|) 147 | COMP_ASSIGNMENT_OPERATOR(^) 148 | 149 | private: 150 | 151 | /* The root of all evil */ 152 | volatile T& v; 153 | }; 154 | 155 | /* simple reference holder class, for holding reference of constant variables */ 156 | template 157 | class refholder final 158 | { 159 | public: 160 | /* Construction, destruction */ 161 | refholder() = delete; 162 | refholder(const T& pv) : v(pv) {} 163 | refholder(T&&) = delete; 164 | 165 | ~refholder() = default; 166 | 167 | /* Conversion to the real type */ 168 | operator const T() {return v;} 169 | 170 | private: 171 | 172 | /* The root of all evil */ 173 | const T& v; 174 | }; 175 | 176 | /* Binary operators for the value wrappers */ 177 | #define DEFINE_BINARY_OPERATOR(x) \ 178 | template refholder operator x (refholder& ls, const T& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 179 | template refholder operator x (refholder& ls, T& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 180 | template refholder operator x (refholder& ls, T&& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 181 | template refholder operator x (refholder& ls, const T&& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 182 | template refholder operator x (refholder& ls, const refholder&& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 183 | template refholder operator x (refholder& ls, refholder&& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 184 | template refholder operator x (refholder& ls, refholder& rs) {refholder rv = ls; ls x##= rs; return rv; } \ 185 | template refholder operator x (refholder& ls, const refholder& rs) {refholder rv = ls; ls x##= rs; return rv; } 186 | 187 | DEFINE_BINARY_OPERATOR(+) 188 | DEFINE_BINARY_OPERATOR(-) 189 | DEFINE_BINARY_OPERATOR(*) 190 | DEFINE_BINARY_OPERATOR(/) 191 | DEFINE_BINARY_OPERATOR(%) 192 | DEFINE_BINARY_OPERATOR(&) 193 | DEFINE_BINARY_OPERATOR(|) 194 | DEFINE_BINARY_OPERATOR(<<) 195 | DEFINE_BINARY_OPERATOR(>>) 196 | DEFINE_BINARY_OPERATOR(^) 197 | 198 | 199 | /* Helping stuff */ 200 | 201 | struct base_rvholder 202 | { 203 | virtual ~base_rvholder() = default; 204 | 205 | template 206 | operator T () const 207 | { 208 | return *reinterpret_cast(get()); 209 | } 210 | 211 | template 212 | bool operator == (const T& o) const 213 | { 214 | return o == operator T (); 215 | } 216 | 217 | template 218 | bool equals(const T& o) const 219 | { 220 | return o == *reinterpret_cast(get()); 221 | } 222 | 223 | virtual const void* get() const = 0; 224 | }; 225 | 226 | template 227 | class rvholder : public base_rvholder 228 | { 229 | public: 230 | rvholder(T t, T c) :base_rvholder(), v(t), check(c) {} 231 | ~rvholder() = default; 232 | virtual const void* get() const override {return reinterpret_cast(&v);} 233 | private: 234 | T v; 235 | T check; 236 | }; 237 | 238 | 239 | /* what the RETURN/BREAK/CONTINUE will return while running from inside a loop block*/ 240 | enum class next_step 241 | { 242 | ns_break, 243 | ns_continue, 244 | ns_return, 245 | ns_done, 246 | ns_unused 247 | }; 248 | 249 | struct next_step_functor_base 250 | { 251 | virtual ~next_step_functor_base() = default; 252 | virtual next_step run() = 0; 253 | }; 254 | 255 | template 256 | struct next_step_functor final : public next_step_functor_base 257 | { 258 | next_step_functor(T r) : runner(r) {} 259 | virtual next_step run() {return runner();} 260 | 261 | private: 262 | T runner; 263 | }; 264 | 265 | struct bool_functor_base 266 | { 267 | virtual ~bool_functor_base() = default; 268 | virtual bool run() = 0; 269 | }; 270 | 271 | template 272 | struct bool_functor final : public bool_functor_base 273 | { 274 | bool_functor(T r) : runner(r) {} 275 | virtual bool run() {return runner();} 276 | 277 | private: 278 | T runner; 279 | }; 280 | 281 | struct any_functor_base 282 | { 283 | virtual ~any_functor_base() = default; 284 | virtual void run(void*) const = 0; 285 | }; 286 | 287 | template 288 | struct any_functor final : public any_functor_base 289 | { 290 | any_functor(T r) : runner(r) {} 291 | 292 | virtual void run(void* retv) const override 293 | { 294 | auto r = runner(); 295 | *reinterpret_cast(retv) = r; 296 | } 297 | 298 | 299 | private: 300 | T runner; 301 | 302 | }; 303 | 304 | /* c++ control structures implementation */ 305 | 306 | /* supporting implementation for the REPEAT/AS_LONG_AS macros*/ 307 | 308 | class repeat_wrapper final 309 | { 310 | public: 311 | repeat_wrapper():body(nullptr), condition(nullptr) {} 312 | 313 | void run() 314 | { 315 | do 316 | { 317 | try 318 | { 319 | body->run(); 320 | } 321 | catch(next_step& c) 322 | { 323 | if(c == next_step::ns_break) break; 324 | if(c == next_step::ns_continue) continue; 325 | } 326 | } 327 | while( condition->run() ); 328 | } 329 | 330 | template 331 | repeat_wrapper& set_body(T lambda) { body.reset(new next_step_functor(lambda)); return *this; } 332 | 333 | 334 | template 335 | repeat_wrapper& set_condition(T lambda) { condition.reset(new bool_functor(lambda)); return *this; } 336 | 337 | private: 338 | 339 | std::unique_ptr body; 340 | std::unique_ptr condition; 341 | }; 342 | 343 | /* supporting implementation for the FOR macro */ 344 | 345 | class for_wrapper final 346 | { 347 | public: 348 | 349 | template 350 | explicit for_wrapper(INIT lambda_init, COND lambda_cond, INCR lambda_incr) 351 | { 352 | condition.reset(new bool_functor(lambda_cond)); 353 | initializer.reset(new next_step_functor(lambda_init)); 354 | increment.reset(new next_step_functor(lambda_incr)); 355 | } 356 | 357 | void run() 358 | { 359 | for( initializer->run(); condition->run(); increment->run()) 360 | { 361 | try 362 | { 363 | body->run(); 364 | } 365 | catch(next_step& c) 366 | { 367 | if(c == next_step::ns_break) break; 368 | if(c == next_step::ns_continue) continue; 369 | } 370 | } 371 | } 372 | 373 | ~for_wrapper() noexcept = default; 374 | 375 | template 376 | for_wrapper& set_body(T lambda) { body.reset(new next_step_functor(lambda)); return *this; } 377 | private: 378 | std::unique_ptr initializer; 379 | std::unique_ptr condition; 380 | std::unique_ptr increment; 381 | std::unique_ptr body; 382 | 383 | }; 384 | 385 | /* supporting implementation for the WHILE macro */ 386 | 387 | class while_wrapper final 388 | { 389 | public: 390 | template 391 | explicit while_wrapper(T lambda) :body(nullptr), condition(nullptr) 392 | {condition.reset(new bool_functor(lambda));} 393 | 394 | void run() 395 | { 396 | while( condition->run() ) 397 | { 398 | try 399 | { 400 | body->run(); 401 | } 402 | catch(next_step& c) 403 | { 404 | if(c == next_step::ns_break) break; 405 | if(c == next_step::ns_continue) continue; 406 | } 407 | } 408 | } 409 | 410 | ~while_wrapper() noexcept = default; 411 | 412 | template 413 | while_wrapper& set_body(T lambda) { body.reset(new next_step_functor(lambda)); return *this; } 414 | 415 | private: 416 | std::unique_ptr body; 417 | std::unique_ptr condition; 418 | }; 419 | 420 | /* supporting implementation for the IF macro */ 421 | 422 | class if_wrapper final 423 | { 424 | public: 425 | template 426 | if_wrapper(T lambda) {condition.reset(new bool_functor(lambda));} 427 | 428 | void run() 429 | { 430 | if(condition->run()) { if(thens) { 431 | thens->run(); 432 | }} 433 | else { if(elses) { 434 | elses->run(); 435 | }} 436 | 437 | } 438 | 439 | ~if_wrapper() noexcept = default; 440 | 441 | template 442 | if_wrapper& set_then(T lambda) { thens.reset(new next_step_functor(lambda)); return *this; } 443 | template 444 | if_wrapper& set_else(T lambda) { elses.reset(new next_step_functor(lambda)); return *this; } 445 | 446 | private: 447 | std::unique_ptr condition; 448 | std::unique_ptr thens; 449 | std::unique_ptr elses; 450 | }; 451 | 452 | /* select ... case helper */ 453 | 454 | class case_instruction 455 | { 456 | public: 457 | case_instruction() = default; 458 | virtual ~case_instruction() = default; 459 | virtual next_step execute(const base_rvholder&) const = 0; 460 | }; 461 | 462 | template 463 | class branch final : public case_instruction 464 | { 465 | public: 466 | template 467 | branch(T lambda) {condition.reset(new any_functor(lambda));} 468 | 469 | bool equals(const base_rvholder& rv, CT lv) const 470 | { 471 | return rv.equals(lv); 472 | } 473 | 474 | virtual next_step execute(const base_rvholder& against) const override 475 | { 476 | CT retv; 477 | condition->run( const_cast(reinterpret_cast(&retv) ) ); 478 | // this looks funny, however we cannot use the operator == 479 | // due to a visual C++ 2015 compiler bug, which fails to compile and crashes 480 | return equals(against,retv) ? next_step::ns_done : next_step::ns_continue; 481 | } 482 | 483 | private: 484 | std::unique_ptr condition; 485 | }; 486 | 487 | class body final : public case_instruction 488 | { 489 | public: 490 | template 491 | body(T lambda) {instructions.reset(new next_step_functor(lambda));} 492 | 493 | 494 | virtual next_step execute(const base_rvholder&) const override 495 | { 496 | return instructions->run(); 497 | } 498 | private: 499 | std::unique_ptr instructions; 500 | }; 501 | 502 | template 503 | class case_wrapper_base 504 | { 505 | public: 506 | explicit case_wrapper_base(const CT& v) : check(v), default_step(nullptr) {} 507 | 508 | 509 | case_wrapper_base& add_entry(const case_instruction& lambda_holder) 510 | { 511 | steps.push_back(&lambda_holder); 512 | return *this; 513 | } 514 | 515 | case_wrapper_base& add_default(const case_instruction& lambda_holder) 516 | { 517 | default_step = &lambda_holder; 518 | return *this; 519 | } 520 | 521 | case_wrapper_base& join() 522 | { 523 | return *this; 524 | } 525 | 526 | void run() const 527 | { 528 | auto it = steps.begin(); 529 | while(it != steps.end()) 530 | { 531 | bool increased = false; 532 | // see if this is a branch 533 | if(dynamic_cast*>(*it) || dynamic_cast*>(*it)) 534 | { 535 | // branch. Execute it, see if it returns true or false 536 | next_step enter = (*it)->execute(rvholder(check,check)); 537 | if(enter == next_step::ns_continue) 538 | { 539 | // step to the next branch 540 | ++it; 541 | increased = true; 542 | } 543 | else 544 | { 545 | // now fast forward and find the first body, 546 | // and from that point on run all the bodies, unless one of them breaks 547 | while(! dynamic_cast(*it) && it != steps.end() ) 548 | { 549 | ++it; 550 | increased = true; 551 | } 552 | 553 | // found the first body. 554 | while(it != steps.end()) 555 | { 556 | if(dynamic_cast(*it)) 557 | { 558 | (*it)->execute(rvholder(check,check)); 559 | } 560 | increased = true; 561 | ++it; 562 | } 563 | } 564 | } 565 | else if(dynamic_cast(*it)) 566 | { 567 | // skip body 568 | } 569 | else 570 | { 571 | throw(std::string("invalid type:") + typeid(*it).name()); 572 | } 573 | 574 | // just for safety 575 | if(!increased) 576 | { 577 | ++it; 578 | } 579 | } 580 | 581 | if(default_step) 582 | { 583 | default_step->execute(rvholder(check,check)); 584 | } 585 | } 586 | 587 | private: 588 | 589 | std::vector steps; 590 | const CT check; 591 | const case_instruction* default_step; 592 | }; 593 | 594 | template 595 | class case_wrapper final : public case_wrapper_base 596 | { 597 | public: 598 | explicit case_wrapper(const CT& v) : case_wrapper_base(v) {} 599 | }; 600 | 601 | 602 | template 603 | class case_wrapper final : public case_wrapper_base 604 | { 605 | public: 606 | explicit case_wrapper(const CT& v) : case_wrapper_base(v) {} 607 | }; 608 | 609 | /* syntactic sugar */ 610 | 611 | class stream_helper {}; 612 | template 613 | refholder operator << (stream_helper, T& a) 614 | { 615 | return refholder(a); 616 | } 617 | 618 | /* The bogus operations allowed */ 619 | class basic_extra 620 | { 621 | public: 622 | basic_extra() = default; 623 | ~basic_extra() = default; 624 | basic_extra(const basic_extra&) = delete; 625 | basic_extra& operator = (const basic_extra&) = delete; 626 | basic_extra(const basic_extra&&) = delete; 627 | basic_extra& operator = (const basic_extra&&) = delete; 628 | }; 629 | 630 | template 631 | class extra_xor final : public basic_extra 632 | { 633 | public: 634 | extra_xor(T& a) : v(a) 635 | { 636 | volatile T lv = MetaRandom<__COUNTER__, 4096>::value; 637 | v ^= lv; 638 | } 639 | virtual ~extra_xor() 640 | { 641 | volatile T lv = MetaRandom<__COUNTER__ - 1, 4096>::value; 642 | v ^= lv; 643 | } 644 | 645 | private: 646 | volatile T& v; 647 | }; 648 | 649 | template 650 | class extra_xor final : public basic_extra 651 | { 652 | public: 653 | extra_xor(const T&) {} 654 | }; 655 | 656 | template 657 | class extra_addition final : public basic_extra 658 | { 659 | public: 660 | extra_addition(T& a) : v(a) 661 | { 662 | volatile T lv = MetaRandom<__COUNTER__, 4096>::value; 663 | v += lv; 664 | } 665 | virtual ~extra_addition() 666 | { 667 | volatile T lv = MetaRandom<__COUNTER__ - 1, 4096>::value; 668 | v -= lv; 669 | } 670 | private: 671 | volatile T& v; 672 | }; 673 | 674 | template 675 | class extra_addition final: public basic_extra 676 | { 677 | public: 678 | extra_addition(const T&) {} 679 | }; 680 | 681 | template 682 | class extra_substraction final : public basic_extra 683 | { 684 | public: 685 | extra_substraction(T& a) : v(a) {v -= MetaRandom<__COUNTER__, 4096>::value; } 686 | virtual ~extra_substraction() { v += MetaRandom<__COUNTER__ - 1, 4096>::value; } 687 | 688 | private: 689 | volatile T& v; 690 | }; 691 | 692 | template 693 | class extra_substraction final : public basic_extra 694 | { 695 | public: 696 | extra_substraction(const T&) {} 697 | }; 698 | 699 | template 700 | class extra_chooser final 701 | { 702 | using type=basic_extra; // intentionally private 703 | }; 704 | 705 | /* Constant obfuscation implementer */ 706 | 707 | template class Num final 708 | { 709 | public: 710 | enum { value = ( (n & 0x01) | ( Num < T , (n >> 1)>::value << 1) ), int64_forcer = 0xffffffffffffffffULL }; 711 | Num() : v(0) 712 | { 713 | v = value ^ MetaRandom<32, 4096>::value; 714 | } 715 | T get() const { T x = v ^ MetaRandom<32, 4096>::value; return x;} 716 | private: 717 | volatile T v; 718 | }; 719 | 720 | struct ObfZero { enum {value = 0}; }; 721 | struct ObfOne { enum {value = 1}; }; 722 | #define OBF_ZERO(t) template <> struct Num final : public ObfZero { t v = value; t get() const { return v;}; }; 723 | #define OBF_ONE(t) template <> struct Num final : public ObfOne { t v = value; t get() const { return v;}; }; 724 | #define OBF_TYPE(t) OBF_ZERO(t) OBF_ONE(t) 725 | 726 | OBF_TYPE(bool) 727 | 728 | OBF_TYPE(char) 729 | OBF_TYPE(signed char) 730 | OBF_TYPE(unsigned char) 731 | OBF_TYPE(char16_t) 732 | OBF_TYPE(char32_t) 733 | OBF_TYPE(wchar_t) 734 | 735 | OBF_TYPE(short int) 736 | OBF_TYPE(unsigned short int) 737 | 738 | OBF_TYPE(int) 739 | OBF_TYPE(unsigned int) 740 | 741 | OBF_TYPE(long int) 742 | OBF_TYPE(unsigned long int) 743 | 744 | OBF_TYPE(long long int) 745 | OBF_TYPE(unsigned long long int) 746 | 747 | #if defined OBF_DEBUG 748 | 749 | #define OBF_BEGIN 750 | #define OBF_END 751 | 752 | #define V(x) x 753 | #define N(x) x 754 | 755 | #define RETURN(x) return x; 756 | 757 | #define BREAK break; 758 | #define CONTINUE continue; 759 | 760 | #define IF(x) if(x) { 761 | #define ELSE } else { 762 | #define ENDIF } 763 | 764 | #define FOR(init,cond,inc) for(init;cond;inc) { 765 | #define ENDFOR } 766 | 767 | #define WHILE(x) while(x) { 768 | #define ENDWHILE } 769 | 770 | #define REPEAT do { 771 | #define AS_LONG_AS(x) } while ((x)); 772 | 773 | #define CASE(a) switch (a) { 774 | #define ENDCASE } 775 | #define WHEN(c) case c: 776 | #define DO { 777 | #define DONE } 778 | #define OR 779 | #define DEFAULT default: 780 | 781 | 782 | 783 | #else 784 | #define _JOIN(a,b) a##b 785 | #define N(a) (obf::Num::value ^ a>().get() ^ obf::MetaRandom<__COUNTER__ - 1, 4096>::value) 786 | #define DEFINE_EXTRA(N,implementer) template struct extra_chooser { using type = implementer; } 787 | DEFINE_EXTRA(0, extra_xor); 788 | DEFINE_EXTRA(1, extra_substraction); 789 | DEFINE_EXTRA(2, extra_addition); 790 | #define V(a) ([&](){obf::extra_chooser::type, obf::MetaRandom<__COUNTER__, \ 791 | MAX_BOGUS_IMPLEMENTATIONS>::value >::type _JOIN(_ec_,__COUNTER__)(a);\ 792 | return obf::stream_helper();}() << a) 793 | 794 | #define FOR(init,cond,inc) { std::shared_ptr __rvlocal; obf::for_wrapper( [&](){(init); return __crv; },\ 795 | [&]()->bool{return (cond); }, \ 796 | [&](){inc;return __crv;}).set_body( [&]() { 797 | #define ENDFOR return __crv;}).run(); } 798 | 799 | #define END return __crv;}).run(); } 800 | 801 | #define IF(x) {std::shared_ptr __rvlocal; obf::if_wrapper(( [&]()->bool{ return (x); })).set_then( [&]() { 802 | #define ELSE return __crv;}).set_else( [&]() { 803 | #define ENDIF END 804 | 805 | #define WHILE(x) {std::shared_ptr __rvlocal; obf::while_wrapper([&]()->bool{ return (x); }).set_body( [&]() { 806 | #define ENDWHILE END 807 | 808 | #define BREAK __crv = obf::next_step::ns_break; throw __crv; 809 | #define CONTINUE __crv = obf::next_step::ns_continue; throw __crv; 810 | 811 | #define RETURN(x) __rvlocal.reset(new obf::rvholder::type>(x,x)); throw __rvlocal; 812 | 813 | #define REPEAT { std::shared_ptr __rvlocal; obf::repeat_wrapper().set_body( [&]() { 814 | #define AS_LONG_AS(x) return __crv;}).set_condition([&]()->bool{ return ( (x) ); }).run(); } 815 | 816 | #define OBF_BEGIN try { obf::next_step __crv = obf::next_step::ns_done; std::shared_ptr __rvlocal; (void)__crv; 817 | #define OBF_END } catch(std::shared_ptr& r) { return *r; } catch (...) {throw;} 818 | 819 | #define CASE(a) try { std::shared_ptr __rvlocal;\ 820 | auto __avholder = a; obf::case_wrapper::type>(a). 821 | #define ENDCASE run(); } catch(obf::next_step&) {} 822 | #define WHEN(c) add_entry(obf::branch::type>\ 823 | ( [&,__avholder]() -> std::remove_reference::type \ 824 | { std::remove_reference::type __c = (c); return __c;} )). 825 | #define DO add_entry( obf::body([&](){ 826 | #define DONE return obf::next_step::ns_continue;})). 827 | #define OR join(). 828 | #define DEFAULT add_default(obf::body([&](){ 829 | 830 | #endif 831 | 832 | } // namespace obf 833 | 834 | #endif // INSTR_H 835 | --------------------------------------------------------------------------------