文章目录
- 前言
- 前期准备
- 直接创建支持c/c++的安卓项目
- 现有项目中导入c/c++的支持
前言
本文是对52pojie《教我兄弟学Android逆向系列课程》按照自己思路的实践,不完全按照原文思路。
原文使用的工具已经过时,现在有更简单方便的方法编写.so文件
本文所使用的android studio版本为v4.1.1
前期准备
使用ndk编译安卓使用的.so文件需要两个东西,第一个就是ndk(工具链),第二个就是编译配置。安卓的编译配置可以使用ndk-build或CMake两种模式,目前安卓官方更推荐cmake的模式。
当前最新版本的android studio 安装ndk十分简单,只需要从SDK manager进行安装。
首先选中工具栏的sdk manager
然后点击sdk tool 选项卡,选择安装 ndk(side by side) 和CMAKE。
安装完成后,就可以尝试编译.so了,下面分两种情况进行介绍,一是还没有开始工程,那么可以选择直接创建一个支持c/c++的工程。另一种情况是,项目本身已经创建好,需要添加c/c++代码。
直接创建支持c/c++的安卓项目
选择file-> new-> project, 在select a project template界面,选择 native c++
选择完成后点击next, 完成剩余配置。
配置完成后,等待片刻,android studio便创建好了一个支持c/c++的项目。
可以看到,项目默认生成了一个c++文件,并在其中实现了一个stringFromJNI的方法,返回一串字符串,为:“Hello from C++”
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
我们尝试把这段代码改为用c语言实现。
首先,在cpp目录下新建一个native-lib.c的文件:
然后打开cpp目录下的CMAKEList.txt文件,将add_library中的native-lib.cpp改为native-lib.c
然后删除项目中的native-lib.cpp文件,并点击右上角工具栏的sync。(删除native-lib.cpp后,可能会看到cpp文件夹被清空,不用担心,同步完成后文件就又会出现。)
同步完成后,在native-lib.c 文件中写下如下代码:
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
// TODO: implement stringFromJNI()
return (*env)->NewStringUTF(env, "hello from c language!");
}
之后点击运行,可以看到app被成功运行,屏幕上打印了由C语言打印的字符串:
现有项目中导入c/c++的支持
首先创建一个新的空的项目:
项目目录切换为project试图
在app/src/main/目录下,新建一个cpp文件夹,用来存放c语言代码。
并在cpp文件夹下新建一个c/c++源代码文件,我起的名字是main.c
然后再在这个目录下新建一个文件,名为CMakeLists.txt:
创建好后的目录结构:
然后,右键app文件夹,选择link c++ project with gradle。
并选择刚刚创建CMakeLists.txt文件的位置。
点击OK后,项目会自动同步。之后回到刚刚创建的CMakeLists.txt文件,并添加如下配置:
cmake_minimum_required(VERSION 3.4.1)
add_library(
# Specifies the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
main.c)
其中,cmake_minimum_required是指cmake的最小版本,add_library中分别声明了库名,静态/动态,编译所需的文件。如果需要编译多个库,那么直接添加多个add_library配置段即可。
完成后点击sync
回到main.c,添加如下代码:
#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
return (*env)->NewStringUTF(env, "hello from c language");
}
JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_sum(JNIEnv *env, jobject thiz, jint num1, jint num2){
return num1+num2;
}
第一个函数返回一个string类型的文本,第二个函数即为原文的课后作业,计算两个参数之和。
编写完成后,可以尝试点击工具栏的小锤子尝试编译(这一步仅为验证C语言代码是否有问题,可以跳过):
出现这个界面,编译一切正常。
回到MainActivity中,在代码中加入
static {
System.loadLibrary("native-lib");
}
此段代码的含义为加载我们编译出的native-lib.so
然后:
public native String stringFromJNI();
public native int sum(int num1, int num2);
此段代码是声明so中实现的两个函数。然后在onCreate函数中添加对两个函数的调用。
Log.i("from native-lib", stringFromJNI());
Log.i("from native-lib", "sum result: " + sum(10, 15));
修改完成的代码如下:
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("from native-lib", stringFromJNI());
Log.i("from native-lib", "sum result: " + sum(10, 15));
}
public native String stringFromJNI();
public native int sum(int num1, int num2);
}
点击运行,在LogCat中即可看到我们编译的.so被成功调用。