首页
社区
课程
招聘
[原创]android JNI静态注册和动态注册
发表于: 2021-9-28 20:46 9452

[原创]android JNI静态注册和动态注册

2021-9-28 20:46
9452

这几天在摸android的题目, 在XCTF里面做了一个ONLOAD来动态注册native的题目, 但是本身ida的export能够找到对应native函数名. 想找找ONLOAD注册方法和直接调用的区别, 就仔细去翻了一些相关的帖子.
这个OnLoad注册了一个名为CheckFlag的方法.

native方法分为静态注册和动态注册两种.(我的环境是java16)
因为注册的过程不太好调, 可能会有问题, 如果有错误请师傅们提出来, 我好及时改进.

java的每个方法都会有一个Method对象, 而这个Method对象有两个成员:nativeFunc和insns.

当java构建这个native方法时, 会将该方法对应的Method对象的nativeFunc成员设置为dvmResolveNativeMethod, dvmResolveNativeMethod方法会按照方法的名称去查找对应的C方法.

lookupSharedLibMethod函数用来查找SO层里面的方法, 再看下lookupSharedLibMethod代码

dvmHashForeach的注释是

findMethodInLib:

最终通过调用dlsym来查找so文件里面的方法, 这和windows的GetProAddress()很类似了.
在dvmResolveNativeMethod找到对应的方法后, 会对native方法对应的方法对象进行再一次赋值,
nativeFunc = dvmCallJNIMethod, insns = 找到的so中的方法地址.
dvmCallJNIMethod方法是一个bridge方法, 这个方法的作用就是调用insns对应的方法
当native方法对象构建好了之后, nativeFunc中已经有值, 这样就会直接执行nativeFunc方法

动态注册是将RegisterNatives放在OnLoad方法里面, 在加载库的时候, 会通过dlsym调用Onload方法.

System.java:

Runtime.class:

ClassLoader.loadLibrary:

libs.loadLibrary():

findFromPaths():

loadLibrary():

然后发现居然最后调用了一个native :

NativeLibraries.c:

findJniFunction:

buildJniFunctionName:

JVM_FindLibraryEntry中调用了dlsym()

然后在Onload里面写RegisterNatives方法就可以进行方法注册:

RegisterNatives的第二个参数为JNINativeMethod类型, 这个类型的定义为:

最后对native方法对应的方法对象进行赋值, 和静态注册类似

nativeFunc = dvmCallJNIMethod, insns = 找到的so中的方法地址.

最后可以总结一些, 在静态注册的时候,感觉和windows的iat加载有点类似, 一个是通过GetProAddress()查找DLL的EAT来实现,一个是通过通过dlsym寻找动态符号表.动态注册的时候, 是事先对结构体中的函数指针进行赋值, 通过RegisterNatives进行注册.当注册好了之后, native方法对应的方法对象的成员nativeFunc==dvmCallJNIMethod, 就不会在再设置为dvmResolveNativeMethod, 而是直接执行vmCallJNIMethod, 调用insns的方法.

 
 
void dvmResolveNativeMethod(const u4* args, JValue* pResult,
    const Method* method, Thread* self)
{
    ClassObject* clazz = method->clazz;
 
    /*
     * If this is a static method, it could be called before the class
     * has been initialized.
     */
    if (dvmIsStaticMethod(method)) {
        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
            assert(dvmCheckException(dvmThreadSelf()));
            return;
        }
    } else {
        assert(dvmIsClassInitialized(clazz) ||
               dvmIsClassInitializing(clazz));
    }
 
    /* start with our internal-native methods */
    DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
    if (infunc != NULL) {
        /* resolution always gets the same answer, so no race here */
        IF_LOGVV() {
            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
            LOGVV("+++ resolved native %s.%s %s, invoking",
                clazz->descriptor, method->name, desc);
            free(desc);
        }
        if (dvmIsSynchronizedMethod(method)) {
            ALOGE("ERROR: internal-native can't be declared 'synchronized'");
            ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
            dvmAbort();     // harsh, but this is VM-internal problem
        }
        DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
        dvmSetNativeFunc((Method*) method, dfunc, NULL);
        dfunc(args, pResult, method, self);
        return;
    }
 
    /* now scan any DLLs we have loaded for JNI signatures */
    void* func = lookupSharedLibMethod(method);
    if (func != NULL) {
        /* found it, point it at the JNI bridge and then call it */
        dvmUseJNIBridge((Method*) method, func);
        (*method->nativeFunc)(args, pResult, method, self);
        return;
    }
 
    IF_ALOGW() {
        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
        ALOGW("No implementation found for native %s.%s:%s",
            clazz->descriptor, method->name, desc);
        free(desc);
    }
 
    dvmThrowUnsatisfiedLinkError("Native method not found", method);
}
void dvmResolveNativeMethod(const u4* args, JValue* pResult,
    const Method* method, Thread* self)
{
    ClassObject* clazz = method->clazz;
 
    /*
     * If this is a static method, it could be called before the class
     * has been initialized.
     */
    if (dvmIsStaticMethod(method)) {
        if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
            assert(dvmCheckException(dvmThreadSelf()));
            return;
        }
    } else {
        assert(dvmIsClassInitialized(clazz) ||
               dvmIsClassInitializing(clazz));
    }
 
    /* start with our internal-native methods */
    DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);
    if (infunc != NULL) {
        /* resolution always gets the same answer, so no race here */
        IF_LOGVV() {
            char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
            LOGVV("+++ resolved native %s.%s %s, invoking",
                clazz->descriptor, method->name, desc);
            free(desc);
        }
        if (dvmIsSynchronizedMethod(method)) {
            ALOGE("ERROR: internal-native can't be declared 'synchronized'");
            ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);
            dvmAbort();     // harsh, but this is VM-internal problem
        }
        DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;
        dvmSetNativeFunc((Method*) method, dfunc, NULL);
        dfunc(args, pResult, method, self);
        return;
    }
 
    /* now scan any DLLs we have loaded for JNI signatures */
    void* func = lookupSharedLibMethod(method);
    if (func != NULL) {
        /* found it, point it at the JNI bridge and then call it */
        dvmUseJNIBridge((Method*) method, func);
        (*method->nativeFunc)(args, pResult, method, self);
        return;
    }
 
    IF_ALOGW() {
        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
        ALOGW("No implementation found for native %s.%s:%s",
            clazz->descriptor, method->name, desc);
        free(desc);
    }
 
    dvmThrowUnsatisfiedLinkError("Native method not found", method);
}
static void* lookupSharedLibMethod(const Method* method)
{
    if (gDvm.nativeLibs == NULL) {
        ALOGE("Unexpected init state: nativeLibs not ready");
        dvmAbort();
    }
    return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
        (void*) method);
}
static void* lookupSharedLibMethod(const Method* method)
{
    if (gDvm.nativeLibs == NULL) {
        ALOGE("Unexpected init state: nativeLibs not ready");
        dvmAbort();
    }
    return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib,
        (void*) method);
}
int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
/*
 * Execute "func" on every entry in the hash table.
 *
 * If "func" returns 1 detach the entry from the hash table. Does not invoke
 * the "free" function.
 *
 * Returning values other than 0 or 1 from "func" will abort the routine.
 */
int dvmHashForeach(HashTable* pHashTable, HashForeachFunc func, void* arg);
/*
 * Execute "func" on every entry in the hash table.
 *
 * If "func" returns 1 detach the entry from the hash table. Does not invoke
 * the "free" function.
 *
 * Returning values other than 0 or 1 from "func" will abort the routine.
 */
static int findMethodInLib(void* vlib, void* vmethod)
{
    const SharedLib* pLib = (const SharedLib*) vlib;
    const Method* meth = (const Method*) vmethod;
    char* preMangleCM = NULL;
    char* mangleCM = NULL;
    char* mangleSig = NULL;
    char* mangleCMSig = NULL;
    void* func = NULL;
    int len;
    if (meth->clazz->classLoader != pLib->classLoader) {
        ALOGV("+++ not scanning '%s' for '%s' (wrong CL)",
            pLib->pathName, meth->name);
        return 0;
    } else
        ALOGV("+++ scanning '%s' for '%s'", pLib->pathName, meth->name);
    /*
     * First, we try it without the signature.
     */
    preMangleCM =
        createJniNameString(meth->clazz->descriptor, meth->name, &len);
    if (preMangleCM == NULL)
        goto bail;
    mangleCM = mangleString(preMangleCM, len);
    if (mangleCM == NULL)
        goto bail;
    ALOGV("+++ calling dlsym(%s)", mangleCM);
    func = dlsym(pLib->handle, mangleCM);
    if (func == NULL) {
        mangleSig =
            createMangledSignature(&meth->prototype);
        if (mangleSig == NULL)
            goto bail;
        mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);
        if (mangleCMSig == NULL)
            goto bail;
        sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);
        ALOGV("+++ calling dlsym(%s)", mangleCMSig);
        func = dlsym(pLib->handle, mangleCMSig);
        if (func != NULL) {
            ALOGV("Found '%s' with dlsym", mangleCMSig);
        }
    } else {
        ALOGV("Found '%s' with dlsym", mangleCM);
    }
bail:
    free(preMangleCM);
    free(mangleCM);
    free(mangleSig);
    free(mangleCMSig);
    return (int) func;
}
static int findMethodInLib(void* vlib, void* vmethod)
{
    const SharedLib* pLib = (const SharedLib*) vlib;
    const Method* meth = (const Method*) vmethod;
    char* preMangleCM = NULL;
    char* mangleCM = NULL;
    char* mangleSig = NULL;
    char* mangleCMSig = NULL;
    void* func = NULL;
    int len;
    if (meth->clazz->classLoader != pLib->classLoader) {
        ALOGV("+++ not scanning '%s' for '%s' (wrong CL)",
            pLib->pathName, meth->name);
        return 0;
    } else
        ALOGV("+++ scanning '%s' for '%s'", pLib->pathName, meth->name);
    /*
     * First, we try it without the signature.
     */
    preMangleCM =
        createJniNameString(meth->clazz->descriptor, meth->name, &len);
    if (preMangleCM == NULL)
        goto bail;
    mangleCM = mangleString(preMangleCM, len);
    if (mangleCM == NULL)
        goto bail;
    ALOGV("+++ calling dlsym(%s)", mangleCM);
    func = dlsym(pLib->handle, mangleCM);
    if (func == NULL) {
        mangleSig =
            createMangledSignature(&meth->prototype);
        if (mangleSig == NULL)
            goto bail;
        mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);
        if (mangleCMSig == NULL)
            goto bail;
        sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);
        ALOGV("+++ calling dlsym(%s)", mangleCMSig);
        func = dlsym(pLib->handle, mangleCMSig);
        if (func != NULL) {
            ALOGV("Found '%s' with dlsym", mangleCMSig);
        }
    } else {
        ALOGV("Found '%s' with dlsym", mangleCM);
    }
bail:
    free(preMangleCM);
    free(mangleCM);
    free(mangleSig);
    free(mangleCMSig);
    return (int) func;
}
 
public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
}
void loadLibrary0(Class<?> fromClass, String libname) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkLink(libname);
    }
    if (libname.indexOf((int)File.separatorChar) != -1) {
        throw new UnsatisfiedLinkError(
            "Directory separator should not appear in library name: " + libname);
    }
    ClassLoader.loadLibrary(fromClass, libname);
}
void loadLibrary0(Class<?> fromClass, String libname) {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkLink(libname);
    }
    if (libname.indexOf((int)File.separatorChar) != -1) {
        throw new UnsatisfiedLinkError(
            "Directory separator should not appear in library name: " + libname);
    }
    ClassLoader.loadLibrary(fromClass, libname);
}
static NativeLibrary loadLibrary(Class<?> fromClass, String name) {
    ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
    if (loader == null) {
        NativeLibrary nl = BootLoader.getNativeLibraries().loadLibrary(fromClass, name);
        if (nl != null) {
            return nl;
        }
        throw new UnsatisfiedLinkError("no " + name +
                " in system library path: " + StaticProperty.sunBootLibraryPath());
    }
 
    NativeLibraries libs = loader.libraries;
    // First load from the file returned from ClassLoader::findLibrary, if found.
    String libfilename = loader.findLibrary(name);
    if (libfilename != null) {
        File libfile = new File(libfilename);
        if (!libfile.isAbsolute()) {
            throw new UnsatisfiedLinkError(
                    "ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
        }
        NativeLibrary nl = libs.loadLibrary(fromClass, libfile);
        if (nl != null) {
            return nl;
        }
        throw new UnsatisfiedLinkError("Can't load " + libfilename);
    }
    // Then load from system library path and java library path
    NativeLibrary nl = libs.loadLibrary(fromClass, name);
    if (nl != null) {
        return nl;
    }
 
    // Oops, it failed
    throw new UnsatisfiedLinkError("no " + name +
            " in java.library.path: " + StaticProperty.javaLibraryPath());
}
static NativeLibrary loadLibrary(Class<?> fromClass, String name) {
    ClassLoader loader = (fromClass == null) ? null : fromClass.getClassLoader();
    if (loader == null) {
        NativeLibrary nl = BootLoader.getNativeLibraries().loadLibrary(fromClass, name);
        if (nl != null) {
            return nl;
        }
        throw new UnsatisfiedLinkError("no " + name +
                " in system library path: " + StaticProperty.sunBootLibraryPath());
    }
 
    NativeLibraries libs = loader.libraries;
    // First load from the file returned from ClassLoader::findLibrary, if found.
    String libfilename = loader.findLibrary(name);
    if (libfilename != null) {
        File libfile = new File(libfilename);
        if (!libfile.isAbsolute()) {
            throw new UnsatisfiedLinkError(
                    "ClassLoader.findLibrary failed to return an absolute path: " + libfilename);
        }
        NativeLibrary nl = libs.loadLibrary(fromClass, libfile);
        if (nl != null) {
            return nl;
        }
        throw new UnsatisfiedLinkError("Can't load " + libfilename);
    }
    // Then load from system library path and java library path
    NativeLibrary nl = libs.loadLibrary(fromClass, name);
    if (nl != null) {
        return nl;
    }
 
    // Oops, it failed
    throw new UnsatisfiedLinkError("no " + name +
            " in java.library.path: " + StaticProperty.javaLibraryPath());
}
public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
    assert name.indexOf(File.separatorChar) < 0;
 
    NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
    if (lib == null && searchJavaLibraryPath) {
        lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
    }
    return lib;
}
public NativeLibrary loadLibrary(Class<?> fromClass, String name) {
    assert name.indexOf(File.separatorChar) < 0;
 
    NativeLibrary lib = findFromPaths(LibraryPaths.SYS_PATHS, fromClass, name);
    if (lib == null && searchJavaLibraryPath) {
        lib = findFromPaths(LibraryPaths.USER_PATHS, fromClass, name);
    }
    return lib;
}
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
    for (String path : paths) {
        File libfile = new File(path, System.mapLibraryName(name));
        NativeLibrary nl = loadLibrary(fromClass, libfile);
        if (nl != null) {
            return nl;
        }
        libfile = ClassLoaderHelper.mapAlternativeName(libfile);
        if (libfile != null) {
            nl = loadLibrary(fromClass, libfile);
            if (nl != null) {
                return nl;
            }
        }
    }
    return null;
}
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
    for (String path : paths) {
        File libfile = new File(path, System.mapLibraryName(name));

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-9-28 20:48 被margina1编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//