├── .gitignore ├── CMakeLists.txt ├── README.md ├── m.bat └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-*/ 3 | build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.28) 2 | 3 | if (NOT DEFINED CMAKE_ANDROID_NDK) 4 | set(CMAKE_ANDROID_NDK D:/Android/SDK/ndk/26.1.10909125) 5 | set(CMAKE_TOOLCHAIN_FILE ${CMAKE_ANDROID_NDK}/build/cmake/android.toolchain.cmake) 6 | set(CMAKE_SYSTEM_NAME Android) 7 | set(CMAKE_SYSTEM_VERSION 24) 8 | set(CMAKE_ANDROID_ARCH_ABI arm64-v8a) 9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 10 | set(ANDROID_ABI arm64-v8a) 11 | set(ANDROID_PLATFORM android-26) 12 | set(ANDROID_NDK ${CMAKE_ANDROID_NDK}) 13 | else () 14 | SET(ANDROID_STUDIO_BUILD true) 15 | endif () 16 | 17 | project(AndroidJNICreateJavaVM) 18 | 19 | set(CMAKE_CXX_STANDARD 20) 20 | 21 | 22 | include(FetchContent) 23 | 24 | 25 | macro(SET_OPTION option value) 26 | set(${option} ${value} CACHE INTERNAL "" FORCE) 27 | endmacro() 28 | SET_OPTION(DOBBY_DEBUG OFF) 29 | SET_OPTION(DOBBY_GENERATE_SHARED OFF) 30 | 31 | FetchContent_Declare( 32 | Dobby 33 | GIT_REPOSITORY https://github.com/jmpews/Dobby.git 34 | GIT_TAG b0176de 35 | ) 36 | FetchContent_MakeAvailable(Dobby) 37 | 38 | add_executable(AndroidJNICreateJavaVM main.cpp) 39 | 40 | target_link_libraries(AndroidJNICreateJavaVM dobby_static log android) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidJNICreateJavaVM 2 | Android JNI Create Java VM, tested on Android 9~14 3 | -------------------------------------------------------------------------------- /m.bat: -------------------------------------------------------------------------------- 1 | REM m.bat | $CMakeCurrentTargetName$ | $ProjectFileDir$ 2 | REM adb connect ip:port 3 | adb push cmake-build-debug\%1 /data/local/tmp 4 | adb shell su -c chmod +x /data/local/tmp/%1 5 | adb shell su -c /data/local/tmp/%1 6 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "dobby.h" 11 | 12 | /*https://blog.quarkslab.com/android-greybox-fuzzing-with-afl-frida-mode.html*/ 13 | 14 | /* JniInvocationImpl struct (from libnativehelper/JniInvocation.c) */ 15 | typedef struct JniInvocation { 16 | const char* jni_provider_library_name; 17 | void* jni_provider_library; 18 | 19 | jint (*JNI_GetDefaultJavaVMInitArgs)(void*); 20 | 21 | jint (*JNI_CreateJavaVM)(JavaVM**, JNIEnv**, void*); 22 | 23 | jint (*JNI_GetCreatedJavaVMs)(JavaVM**, jsize, jsize*); 24 | } JniInvocationImpl; 25 | 26 | /* CTX */ 27 | typedef struct JavaContext { 28 | JavaVM* vm; 29 | JNIEnv* env; 30 | JniInvocationImpl* invoc; 31 | } JavaCTX; 32 | 33 | static JavaCTX ctx; 34 | 35 | #define ANDROID_RUNTIME_DSO "libandroid_runtime.so" 36 | 37 | typedef jint (*JNI_CreateJavaVM_t)(JavaVM** p_vm, JNIEnv** p_env, void* vm_args); 38 | 39 | typedef void (*JniInvocation_ctor_t)(void*); 40 | 41 | typedef void (*JniInvocation_dtor_t)(void*); 42 | 43 | typedef void (*JniInvocation_Init_t)(void*, const char*); 44 | 45 | static void modify_function(void* func) { 46 | unsigned long page_start = (unsigned long)func & ~(PAGE_SIZE - 1); 47 | if (mprotect((void*)page_start, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) { 48 | return; 49 | } 50 | unsigned char* p = (unsigned char*)func; 51 | //RET 52 | p[0] = 0xC0; 53 | p[1] = 0x03; 54 | p[2] = 0x5F; 55 | p[3] = 0xD6; 56 | // mprotect((void *) page_start, PAGE_SIZE, PROT_READ | PROT_EXEC); 57 | } 58 | 59 | 60 | struct startup { 61 | void*(*start_routine)(void*); 62 | 63 | void* arg; 64 | }; 65 | 66 | void* my_start_routine(void* arg) { 67 | startup* up = (startup*)arg; 68 | 69 | // char thread_name[16]{0}; 70 | // pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name)); 71 | // LOGD("thread name %s %d", thread_name,gettid()); 72 | //if (strstr(thread_name, "binder:")|| strstr(thread_name,"main")){ 73 | while (!ctx.vm) { 74 | usleep(1000); 75 | } 76 | JNIEnv* Env; 77 | for (int i = 0; i < 10; ++i) { 78 | if (ctx.vm->AttachCurrentThread(&Env, nullptr) == JNI_OK) { 79 | break; 80 | } 81 | } 82 | // LOGD("hooked thread %s %d", thread_name,gettid()); 83 | // } 84 | return up->start_routine(up->arg); 85 | } 86 | 87 | static int (*real_pthread_create)(pthread_t*, const pthread_attr_t*, void*(*)(void*), void*) = NULL; 88 | 89 | 90 | int pthread_create_hook(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg) { 91 | startup* up = (startup*)malloc(sizeof(*up)); 92 | up->start_routine = start_routine; 93 | up->arg = arg; 94 | 95 | return real_pthread_create(thread, attr, my_start_routine, up); 96 | } 97 | 98 | /* API */ 99 | JavaCTX* init_java_env(char** jvm_options, uint8_t jvm_nb_options) { 100 | JNI_CreateJavaVM_t JNI_CreateJVM; 101 | JniInvocationImpl*(*JniInvocationCreate)(); 102 | bool (*JniInvocationInit)(JniInvocationImpl*, const char*); 103 | jint (*registerFrameworkNatives)(JNIEnv*); 104 | void* runtime_dso; 105 | 106 | JniInvocation_ctor_t JniInvocation_ctor; 107 | //JniInvocation_dtor_t JniInvocation_dtor; 108 | JniInvocation_Init_t JniInvocation_Init; 109 | 110 | if ((runtime_dso = dlopen(ANDROID_RUNTIME_DSO, RTLD_NOW)) == NULL) { 111 | printf("[!] %s\n", dlerror()); 112 | return nullptr; 113 | } 114 | 115 | JNI_CreateJVM = (JNI_CreateJavaVM_t)dlsym(runtime_dso, "JNI_CreateJavaVM"); 116 | if (!JNI_CreateJVM) { 117 | printf("Cannot find JNI_CreateJavaVM\n"); 118 | return nullptr; 119 | } 120 | 121 | registerFrameworkNatives = (jint (*)(JNIEnv*))dlsym(runtime_dso, "registerFrameworkNatives"); 122 | if (!registerFrameworkNatives) { 123 | registerFrameworkNatives = (jint (*)(JNIEnv*))dlsym(runtime_dso, 124 | "Java_com_android_internal_util_WithFramework_registerNatives"); 125 | if (!registerFrameworkNatives) { 126 | printf("Cannot find registerFrameworkNatives\n"); 127 | return nullptr; 128 | } 129 | } 130 | 131 | JniInvocationCreate = (JniInvocationImpl *(*)())dlsym(runtime_dso, "JniInvocationCreate"); 132 | JniInvocationInit = (bool (*)(JniInvocationImpl*, const char*))dlsym(runtime_dso, "JniInvocationInit"); 133 | if (JniInvocationCreate && JniInvocationInit) { 134 | ctx.invoc = JniInvocationCreate(); 135 | JniInvocationInit(ctx.invoc, ANDROID_RUNTIME_DSO); 136 | } else { 137 | JniInvocation_ctor = (JniInvocation_ctor_t)dlsym(runtime_dso, "_ZN13JniInvocationC1Ev"); 138 | JniInvocation_Init = (JniInvocation_Init_t)dlsym(runtime_dso, "_ZN13JniInvocation4InitEPKc"); 139 | 140 | if (!JniInvocation_ctor || !JniInvocation_Init) { 141 | printf("Cannot find JniInvocationImpl\n"); 142 | return nullptr; 143 | } 144 | 145 | ctx.invoc = (JniInvocationImpl*)calloc(1, 256); 146 | JniInvocation_ctor(ctx.invoc); 147 | JniInvocation_Init(ctx.invoc, NULL); 148 | } 149 | 150 | JavaVMOption options[jvm_nb_options]; 151 | JavaVMInitArgs args; 152 | args.version = JNI_VERSION_1_6; 153 | if (jvm_nb_options > 0) { 154 | for (int i = 0; i < jvm_nb_options; ++i) 155 | options[i].optionString = jvm_options[i]; 156 | 157 | args.nOptions = jvm_nb_options; 158 | args.options = options; 159 | } else { 160 | args.nOptions = 0; 161 | args.options = NULL; 162 | } 163 | 164 | args.ignoreUnrecognized = JNI_TRUE; 165 | 166 | int api_level = android_get_device_api_level(); 167 | if (api_level < 31) { 168 | const char* symbols[] = { 169 | "InitializeSignalChain", 170 | "ClaimSignalChain", 171 | "UnclaimSignalChain", 172 | "InvokeUserSignalHandler", 173 | "EnsureFrontOfChain", 174 | "AddSpecialSignalHandlerFn", 175 | "RemoveSpecialSignalHandlerFn", 176 | NULL 177 | }; 178 | for (const char** sym = symbols; *sym; ++sym) { 179 | void* func = DobbySymbolResolver("libsigchain.so", *sym); 180 | if (!func) { 181 | // fprintf(stderr, "%s\n", dlerror()); 182 | continue; 183 | } 184 | modify_function(func); 185 | } 186 | } 187 | 188 | jint status = JNI_CreateJVM(&ctx.vm, &ctx.env, &args); 189 | if (status == JNI_ERR) return nullptr; 190 | 191 | printf("[d] vm: %p, env: %p\n", ctx.vm, ctx.env); 192 | 193 | status = registerFrameworkNatives(ctx.env); 194 | if (status == JNI_ERR) return nullptr; 195 | 196 | DobbyHook((void*)pthread_create, (void*)pthread_create_hook, (void**)&real_pthread_create); 197 | return &ctx; 198 | } 199 | 200 | static void JavaEnvDestructor(void*) { 201 | ctx.vm->DetachCurrentThread(); 202 | } 203 | 204 | static JNIEnv* GetJavaEnv() { 205 | static uint32_t TlsSlot = 0; 206 | if (TlsSlot == 0) { 207 | pthread_key_create((pthread_key_t*)&TlsSlot, &JavaEnvDestructor); 208 | } 209 | auto* Env = (JNIEnv*)pthread_getspecific(TlsSlot); 210 | if (Env == nullptr) { 211 | ctx.vm->GetEnv((void**)&Env, JNI_VERSION_1_6); 212 | jint AttachResult = ctx.vm->AttachCurrentThread(&Env, nullptr); 213 | if (AttachResult == JNI_ERR) { 214 | return nullptr; 215 | } 216 | pthread_setspecific(TlsSlot, Env); 217 | } 218 | return Env; 219 | } 220 | 221 | int main() { 222 | init_java_env(nullptr, 0); 223 | if (!ctx.vm) { 224 | printf("Failed to initialize Java environment\n"); 225 | return -1; 226 | } 227 | 228 | //System.out.println("Hello, World!"); 229 | 230 | auto env = GetJavaEnv(); 231 | if (env == nullptr) { 232 | printf("Failed to get JNIEnv\n"); 233 | return -1; 234 | } 235 | 236 | // Find the System class 237 | jclass systemClass = env->FindClass("java/lang/System"); 238 | if (systemClass == nullptr) { 239 | printf("Failed to find the System class\n"); 240 | return -1; 241 | } 242 | 243 | // Find the PrintStream class 244 | jclass printStreamClass = env->FindClass("java/io/PrintStream"); 245 | if (printStreamClass == nullptr) { 246 | printf("Failed to find the PrintStream class\n"); 247 | return -1; 248 | } 249 | 250 | // Get the System.out field, which is a static field 251 | jfieldID outField = env->GetStaticFieldID(systemClass, "out", "Ljava/io/PrintStream;"); 252 | if (outField == nullptr) { 253 | printf("Failed to get the out field\n"); 254 | return -1; 255 | } 256 | 257 | // Get the PrintStream.println method 258 | jmethodID printlnMethod = env->GetMethodID(printStreamClass, "println", "(Ljava/lang/String;)V"); 259 | if (printlnMethod == nullptr) { 260 | printf("Failed to get the println method\n"); 261 | return -1; 262 | } 263 | 264 | // Get the System.out object 265 | jobject outObject = env->GetStaticObjectField(systemClass, outField); 266 | if (outObject == nullptr) { 267 | printf("Failed to get the out object\n"); 268 | return -1; 269 | } 270 | 271 | // Create a string 272 | jstring helloWorldString = env->NewStringUTF("Hello, World!"); 273 | 274 | // Call the println method 275 | env->CallVoidMethod(outObject, printlnMethod, helloWorldString); 276 | 277 | return 0; 278 | } 279 | --------------------------------------------------------------------------------