Android开发积累Android JNI

Android安全---AndroidStudio3.0JNI编

2019-03-12  本文已影响44人  善笃有余劫

JNI介绍

官方介绍:

在编程领域,JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。

总的来说呢,就是一个框架,可以使得java调用c/c++代码。在Android中谷歌提供了NDK工具,用于移动下的JNI开发。

Android NDK 是一套允许您使用 C 和 C++ 等语言,以原生代码实现部分应用的工具集。在开发某些类型的应用时,这有助于您重复使用以这些语言编写的代码库

NDK下载和教程地址:https://developer.android.com/ndk/?hl=zh-cn

JNI对于Android安全的意义

做过逆向工程的小伙伴都知道。所谓的java层代码是非常容易被解开的。即使加上混淆,加上签名校验什么的,依然十分脆弱。而真正的大厂都会把真正的加密安全相关的信息放在so文件里面。

使用jni开发的so文件是一个二进制文件,无法轻易被读取。极大的增加破解难度。再配合加固等技术一般都能做到软件的安全防护。

使用AndroidStudio3.0进行JNI开发

这里强调AS3.0呢,主要是在3.0之后的JNI编程和之前有很大的区别。

众所周知,混合编译需要一个配置文件来说明编译的过程。在3.0之前使用的配置文件是makefile。而在3.0之后官方直接默认了CMake.txt。所以我们要习惯CMake文件的使用。

CMake介绍

CMake文件官网https://cmake.org/

官网首页的介绍如下(谷歌翻译):

CMake是一个开源的跨平台工具系列,旨在构建,测试和打包软件。CMake用于使用简单的平台和独立于编译器的配置文件来控制软件编译过程,并生成可在您选择的编译器环境中使用的本机makefile和工作空间。CMake工具套件由Kitware创建,以满足对ITK和VTK等开源项目的强大跨平台构建环境的需求。

总之就是一个混合编译配置文件,它有自己的语法包括版本。如果你想知道更多可以仔细的学习他的文档。

新建工程

新建安卓工程,点上Include C++ support

image.png

一路next到finish,系统就会自动配置一个带JNI的工程。

查看工程

实际上这个工程已经是一个标准的JNI工程了,直接运行就可以了。
不过在运行钱可以查看下工程目录,看看和平常的安卓工程有什么区别。

区别1:MainActivity.java的so文件导入和本地方法

image.png

引用so文件native-lib
调用本地方法stringFromJNI()
本地方法public native String stringFromJNI();

区别2:app build.gradle 文件配置
可以看到和平常的build.gradle有两处不同

image.png

可以看到有两个externalNativeBuild节点

defaultConfig内部的externalNativeBuild节点用于配置cmake的配置
可选配置有下面这些文档地址:

android {
    // Similar to other properties in the defaultConfig block, you can override
    // these properties for each product flavor you configure.
    defaultConfig {
        // This block is different from the one you use to link Gradle
        // to your CMake or ndk-build script.
        externalNativeBuild {
            // For ndk-build, instead use the ndkBuild block.
            cmake {
                // Passes optional arguments to CMake.
                arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

                // Sets a flag to enable format macro constants for the C compiler.
                cFlags "-D__STDC_FORMAT_MACROS"

                // Sets optional flags for the C++ compiler.
                cppFlags "-fexceptions", "-frtti"

                // Specifies the library and executable targets from your CMake project
                // that Gradle should build.
                targets "libexample-one", "my-executible-demo"
            }
        }
    }
}

defaultConfig外部的externalNativeBuild节点用于配置cmake的文件地址,如果CMake.txt放在根目录,配置如下:

android {
    externalNativeBuild {
        // Encapsulates your CMake build configurations.
        // For ndk-build, instead use the ndkBuild block.
        cmake {
            // Specifies a path to your CMake build script that's
            // relative to the build.gradle file.
            path "CMakeLists.txt"
        }
    }
}

区别3:main目录下的cpp文件夹以及里面的native-lib.cpp文件
这个不解释了,就是c层文件目录了
代码如下:

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring

JNICALL
Java_com_huruwo_as3_1jni_1demo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

这里就是return 了一个字符串 ,具体这里怎么生成的我下一节讲。

区别4:CMakeLists.txt文件

非常有意思这个文件,我把内容复制出来分析:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        src/main/cpp/native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

通过里面的英文说明我们可以知道每一段文字的意义:

设置最小的CMake版本

# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)

设置生成so文件信息 源代码地址

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        native-lib  # 设置so文件名称 也就是我们在mainactivity里面引用的名称

        # Sets the library as a shared library.
        SHARED #设置为分享库

        # Provides a relative path to your source file(s).
        src/main/cpp/native-lib.cpp) # 设置代码的地址 有多少写多少

导入一些而外的库,比如这里的log库(系统库)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

把前面的所有库连接起来 包括三方库 so库 系统库

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

如何生成native-lib.cpp文件

步骤如下:

1.新建myJNI.java文件

代码如下:

public class myJNI {

   //加载so文件
    static {
        System.loadLibrary("JniTest");
    }
    
    //调用c层方法
    public static native System sayHello();
}

2.利用javac 命令生成.class文件

转到myJNI.java目录下,右键当前打开命令行:

image.png

会生成一个myJNI.class文件

image.png

3.javah命令生成.h头文件

转到java目录下(即com同层目录下)

命令如下: javah -jni 包名+文件名

image.png

生成新的.h文件

image.png

打开里面的内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_huruwo_hellosoworld_myJNI */

#ifndef _Included_com_huruwo_hellosoworld_myJNI
#define _Included_com_huruwo_hellosoworld_myJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_huruwo_hellosoworld_myJNI
 * Method:    sayHello
 * Signature: ()Ljava/lang/System;
 */
JNIEXPORT jobject JNICALL Java_com_huruwo_hellosoworld_myJNI_sayHello
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

4.编写c层代码

新建文件夹jni,在main下与java同层级

image.png

新建main.c文件(这个命名可以随意) 把之前生成的.h文件内容拷贝过来

然后改动JNIEXPORT jobject JNICALL Java_com_huruwo_hellosoworld_myJNI_sayHello这个方法,让其返回一段文字。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_huruwo_hellosoworld_myJNI */

#ifndef _Included_com_huruwo_hellosoworld_myJNI
#define _Included_com_huruwo_hellosoworld_myJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_huruwo_hellosoworld_myJNI
 * Method:    sayHello
 * Signature: ()Ljava/lang/System;
 */
JNIEXPORT jobject JNICALL Java_com_huruwo_hellosoworld_myJNI_sayHello
  (JNIEnv *, jclass){
  return (*env)->NewStringUTF(env,"hello 52pojie!");
  }

#ifdef __cplusplus
}
#endif
#endif

编译工程 其他配置

为了节省空间,避免包太大。通常我们配置NDK 支持的CPU架构。
百度一搜可能就会出现以下代码:

ndk{
            moduleName "helloJni"//*生成的so文件名,必填
            abiFilters "armeabi", "armeabi-v7a", "x86" //配置输出的abi体系结构下的so库,
        }

你兴奋的复制上去,shit 报错了。

image.png

显然这些配置需要出现在CMake节点里面 就想这样

externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
            }
        }

运行起来:

image.png

没毛病哦老铁,数据请求成功。

我们可以看到生成的so文件,build文件后解压apk:

image.png

总结

本次了解了AS3.0的JNI基础编程,可以提高安全性。

上一篇 下一篇

猜你喜欢

热点阅读