文章目录

  • 前言
  • 前期准备
  • 直接创建支持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

Android Studio 编译bat打包apk android studio编译so_cmake


然后点击sdk tool 选项卡,选择安装 ndk(side by side) 和CMAKE。

Android Studio 编译bat打包apk android studio编译so_android_02


安装完成后,就可以尝试编译.so了,下面分两种情况进行介绍,一是还没有开始工程,那么可以选择直接创建一个支持c/c++的工程。另一种情况是,项目本身已经创建好,需要添加c/c++代码。

直接创建支持c/c++的安卓项目

选择file-> new-> project, 在select a project template界面,选择 native c++

Android Studio 编译bat打包apk android studio编译so_安卓_03


选择完成后点击next, 完成剩余配置。

Android Studio 编译bat打包apk android studio编译so_android studio_04


配置完成后,等待片刻,android studio便创建好了一个支持c/c++的项目。

Android Studio 编译bat打包apk android studio编译so_android_05


可以看到,项目默认生成了一个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的文件:

Android Studio 编译bat打包apk android studio编译so_ndk_06


Android Studio 编译bat打包apk android studio编译so_ndk_07


然后打开cpp目录下的CMAKEList.txt文件,将add_library中的native-lib.cpp改为native-lib.c

Android Studio 编译bat打包apk android studio编译so_ndk_08


然后删除项目中的native-lib.cpp文件,并点击右上角工具栏的sync。(删除native-lib.cpp后,可能会看到cpp文件夹被清空,不用担心,同步完成后文件就又会出现。)

Android Studio 编译bat打包apk android studio编译so_android_09


同步完成后,在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语言打印的字符串:


Android Studio 编译bat打包apk android studio编译so_android_10

现有项目中导入c/c++的支持

首先创建一个新的空的项目:

Android Studio 编译bat打包apk android studio编译so_安卓_11


项目目录切换为project试图

Android Studio 编译bat打包apk android studio编译so_android studio_12


在app/src/main/目录下,新建一个cpp文件夹,用来存放c语言代码。

Android Studio 编译bat打包apk android studio编译so_cmake_13


并在cpp文件夹下新建一个c/c++源代码文件,我起的名字是main.c

Android Studio 编译bat打包apk android studio编译so_android_14


然后再在这个目录下新建一个文件,名为CMakeLists.txt:

Android Studio 编译bat打包apk android studio编译so_安卓_15


创建好后的目录结构:

Android Studio 编译bat打包apk android studio编译so_android_16


然后,右键app文件夹,选择link c++ project with gradle。

Android Studio 编译bat打包apk android studio编译so_android studio_17


并选择刚刚创建CMakeLists.txt文件的位置。

Android Studio 编译bat打包apk android studio编译so_ndk_18


点击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

Android Studio 编译bat打包apk android studio编译so_安卓_19


回到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语言代码是否有问题,可以跳过):

Android Studio 编译bat打包apk android studio编译so_android_20


出现这个界面,编译一切正常。

回到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被成功调用。

Android Studio 编译bat打包apk android studio编译so_ndk_21