Explore the power of NDK and reverse engineering in Android development

What is NDK?

NDK (Native Development Kit) is a set of tools for developing and building applications written in C++ or other native languages ​​on the Android platform. The NDK provides libraries and tools that enable developers to use native code in their applications and achieve interoperability with Java code. The following is a detailed analysis of NDK:

1. Why use NDK?

  • Performance optimization: Some tasks can provide higher performance using native code, such as graphics rendering, signal processing, etc. NDK allows developers to use native code in performance-critical parts, thereby optimizing the execution speed and efficiency of applications.
  • Reuse of existing code: Sometimes, developers may already have some existing code written in C++ or other native languages ​​that can be integrated into Android applications via NDK for better reuse and integration of existing solutions.
  • Platform Compatibility: Some functions or libraries may only be available in native language versions. By using the NDK, applications can take advantage of these functions and libraries for better compatibility with specific platforms.

2. The components of NDK:

  • Toolchain (Toolchain): including tools such as cross compilers and debuggers, used to compile C/C++ source codes into executable files for specific Android target platforms.
  • Native Libraries: Contains a series of native libraries that can be used by applications, such as libc (C standard library), libm (mathematical library), etc. In addition, developers can also build their own native libraries for applications to call.
  • Header Files: Contains header file declarations for interoperability with Java code. These header files define the interface of functions and data structures, so that the functions of the native library can be called in Java code.
  • Build System (Build System): Android's build system (such as Gradle) can integrate the NDK tool set for the construction and management of local code.

3. Steps to use NDK:

  • Writing native code: Developers use C++ or other native languages ​​to write specific parts or functions of an application, such as computationally intensive tasks, image processing, audio processing, etc.
  • Configure build scripts: In Android application projects, you need to configure build scripts (such as CMake or ndk-build) to specify how to compile and link native code.
  • Build and generate native libraries: use build scripts and NDK toolchains to compile native code and generate native library files (usually .so files).
  • Call the native library: Call the functions and data structures of the native library through the JNI (Java Native Interface) mechanism in the Java code.
  • Build and run the application: Build and run the application on a device or emulator using Gradle or other build tools.

The principle of dynamic registration under Dalvik

Under the Dalvik virtual machine (DVM), dynamic registration refers to binding the functions of the native code (C/C++) and the Java code at runtime, so that the Java layer can call the native function. This is often used to integrate Native code into Android applications to provide higher performance features or to use existing native libraries.

In the Dalvik virtual machine, dynamic registration mainly involves the following steps:

  1. Write local code: Developers use C/C++ to write local code to realize specific functions or operations.
  2. Define native methods: In Java code, native methods corresponding to native code are defined by using the native keyword. For example, to declare a native method: public native void nativeFunction();
  3. Generating the JNI header file: Use the javah command or the automation tool of Android Studio to generate the JNI (Java Native Interface) header file corresponding to the Java class. This header file will contain function prototypes associated with native methods.
  4. Implementing JNI functions: Implementing JNI functions in native code, the function names usually follow specific naming rules. These JNI functions will be bound to native methods in Java. For example, to implement a JNI function: JNIEXPORT void JNICALL Java_com_example_MyClass_nativeFunction(JNIEnv* env, jobject obj) { // Perform native functionality }
  5. Register JNI function: In Java code, load the local library through System.loadLibrary(), and register the JNI function in the static code block. In this way, the DVM knows how to associate native methods in Java code with JNI functions in native code. For example: static { System.loadLibrary("mylibrary"); }
  6. Build and run the application: Using Gradle or other build tools, build and run the Android application. At runtime, the Dalvik virtual machine loads native libraries and handles requests from Java to call native methods.

When Java code calls a native method, the Dalvik virtual machine searches and executes the corresponding JNI function at the JNI layer. In this way, the interaction and interoperability between Java code and native code can be realized.

It should be noted that correct naming and parameter matching are very important during the dynamic registration process. For each local method, the naming rules of the JNI function are fixed, usually "Java package name class name_method name". The type and order of the parameters must also be consistent with the signature of the Java method.

Principle of dynamic registration under ART

Under ART (Android Runtime), the principle of dynamic registration is somewhat different from that under the Dalvik virtual machine. In Dalvik, dynamic registration needs to establish a bridge between Java code and native code through JNI (Java Native Interface). In ART, due to the introduction of AOT (Ahead-of-Time) compilation and a new execution engine, the way of dynamic registration has also changed.

Under ART, dynamic registration mainly involves the following steps:

  1. Write local code: Developers use C/C++ to write local code to realize specific functions or operations.
  2. Define native methods: In Java code, native methods corresponding to native code are defined by using the native keyword. For example, to declare a native method: public native void nativeFunction();
  3. Generating the JNI header file: Use the javah command or the automation tool of Android Studio to generate the JNI header file corresponding to the Java class. This header file will contain function prototypes associated with native methods.
  4. Link local library: In construction tools such as CMake or ndk-build, link the local code with the code of the Java layer to generate a local library (Shared Library) file.
  5. Load the native library:

In Java code, use System.loadLibrary() to load native library files. For example: static { System.loadLibrary("mylibrary"); }

Calling native methods: Calling native methods at the Java layer executes native code.

In ART, unlike Dalvik, ART uses the "Ahead-of-Time" (AOT) compilation strategy, which converts the entire DEX file (Dalvik Executable) into native machine code during application installation, and Not just-in-time compilation (JIT) at runtime. This means that when the application is installed, ART will statically compile all Java code and save the result as native machine code.

Therefore, under ART, the principle of dynamic registration has also changed. Unlike Dalvik's JNI-based dynamic registration, ART uses a new mechanism called "JNI method registration" (JNI method registration). When ART loads an application, it looks up and parses the information of the JNI method in the metadata of the native library, thereby establishing a mapping relationship between Java code and native code.

This new mechanism makes dynamic registration more efficient, because ART does not need to associate Java code and native code through JNI dynamic binding at runtime. Instead, it gets information about all JNI methods at application installation time and associates them with native code addresses, enabling faster method lookup and invocation. This article mainly analyzes some knowledge of NDK and reverse engineering. For more articles, please refer to "Android Core Technology Manual" to view the detailed content catalog.

Summarize

  1. Purpose: NDK aims to provide developers with the ability to write Android applications using native programming languages. It is suitable for application scenarios that require high-performance computing, access to hardware accelerators, implement low-level algorithms, or interact with existing native code.
  2. Development language: NDK supports C, C++ and assembly language development. These languages ​​have higher execution efficiency and lower access rights. Compared with applications developed in Java, they can provide better performance and functionality.
  3. Development process: Development with the NDK typically involves the following steps:
  • Write native code: Use C/C++ or assembly language to write native code that needs to interact with Android applications.
  • Define native methods: Define native methods by using the native keyword in Java code to establish contact with native code.
  • Generate JNI header file: Use the javah command or Android Studio's automation tool to generate a JNI header file corresponding to a Java class, which is used to access the data and methods of the Java layer in the local code.
  • Linking local library: Use construction tools such as CMake or ndk-build to link the local code with the code of the Java layer to generate a local library file (.so file).
  • Load native library: Use System.loadLibrary() in Java code to load native library files to call native methods in the application.
  • Call native methods: Call native methods at the Java layer to execute native code.
  1. Supported functions: NDK provides a wealth of APIs and libraries, which can access the underlying functions and device features of the Android system, such as graphics rendering library (OpenGL ES), media processing library (Media APIs), network library (Sockets), audio library ( OpenSL ES), etc. By using the NDK, developers can better control and optimize the performance and functionality of their applications.
  2. Development environment: In order to use NDK for development, developers need to install and configure the NDK toolset, including setting the NDK path in Android Studio or the command line. Developers also need to master related programming languages ​​and tools, such as C/C++ language, Android Studio, CMake, etc.

Guess you like

Origin blog.csdn.net/m0_71524094/article/details/131331349