• 目的
  • 编译环境配置
  • 配置环境变量
  • 测试简单的例子
  • 为什么非要有jni那一层目录
  • 编译一个可以允许的文件


目的

能快速,简单(主要是指脱离AndroidStudio)的把c/c++ 源码进行交叉编译成Android上的动态库,静态库,或者可执行文件。从而为逆向分析构建demo节省时间。

编译环境配置

电脑:Mac OS 10.13.4
NDK:官网下载,目前最新的好像android-ndk-r17 我用的是 android-ndk-r14b,ndk不需要安装官网下载后解压就行。

配置环境变量

为了方便使用ndk的命令行工具,我们配置环境变量
~/.bash_profile 文件中添加如下配置

export ANDROID_NDK_HOME=/Users/user01/Development/android-ndk-r14b
export PATH=${PATH}:${ANDROID_NDK_HOME}:${ANDROID_NDK_HOME}/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin

然后命令行运行如下命令:

$ source ~/.bash_profile

然后命令行运行:

$ ndk-build -h

如果能打印出 ndk-build 这个工具的帮助文档说明环境变量配置成功

测试简单的例子

创建如下目录结构(注意jni这层目录很重要,名字不要随便取):

~/Desktop/test/
└── jni/
    ├── Android.mk
    ├── Application.mk
    └── test.c

主要有三个文件,其中Android.mk和Application.mk 是辅助ndk-build进行编译的,test.c是我们的c测试用例

  1. Application.mk

主要指定生成哪些cpu架构的动态库,这里只生成armeabi 和armeabi-v7a架构的库

APP_ABI      := armeabi armeabi-v7a
APP_PLATFORM := android-14
  1. Android.mk

具体Android.mk的更详细的配置自行Google

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
# 生成的so库的名字为libdemo.so
LOCAL_MODULE    := demo
# 用于编译的c文件
LOCAL_SRC_FILES := test.c
# 生成库的类型,是动态库,静态库还是可执行文件,这里先来生成一个动态库
include $(BUILD_SHARED_LIBRARY)
# 下面这么写是生成可执行文件
#include $(BUILD_EXECUTABLE)
  1. test.c

很简单就是打印一句 hello world!

#include <stdio.h>

int main(const int argc,const char* args[]){

const char* str  = "hello world!";
printf("%s\n",str);
return 0;

}

然后命令行进入到test目录下面:

$ cd ~/Desktop/test

$ ndk-build -C ./jni

然后会生成如下目录结构,obj目录下是生成动态库的中间文件,libs里面是最终生成的不同CPU架构的动态库:

test
├── jni
│   ├── Android.mk
│   ├── Application.mk
│   └── test.c
├── libs
│   ├── armeabi
│   │   └── libdemo.so
│   └── armeabi-v7a
│       └── libdemo.so
└── obj
    └── local
        ├── armeabi
        │   ├── libdemo.so
        │   └── objs
        │       └── demo
        │           ├── test.o
        │           └── test.o.d
        └── armeabi-v7a
            ├── libdemo.so
            └── objs
                └── demo
                    ├── test.o
                    └── test.o.d

为什么非要有jni那一层目录

约定优于配置,没有不行吗?行,但是要改点东西。去掉jni那层目录再编译一次,当前目录结构如下:

test/
├── Android.mk
├── Application.mk
└── test.c

命令行执行
1. $ cd ~/Desktop/test
2. $ ndk-build

结果报错如下:

Android NDK: Could not find application project directory !
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.

错误日志上说什么路径找不到错误,需要定义一个xx变量,我们只需要把编译命令改成如下就可以了:

ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk

编译一个可以允许的文件

修改Android.mk 文件如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := demo
LOCAL_SRC_FILES := test.c
#include $(BUILD_SHARED_LIBRARY)

# Android4.4 以后 调用的可执行文件不是基于PIE方式编译的,则无法运行
LOCAL_CFLAGS += -pie -fPIE
LOCAL_LDFLAGS += -pie -fPIE
# 编译一个可执行文件
include $(BUILD_EXECUTABLE)

这个可执行文件在电脑上是没法执行的,只能放到手机里面执行,执行如下命令,我们把刚生成的文件放到手机上:

  1. push 文件到手机
    $ adb push libs/armeabi-v7a/demo /data/local/tmp
  2. 为手机中的demo文件添加可执行权限(为了方便直接给予所有权限了)
    $ adb shell 'chmod 777 /data/local/tmp/demo'
  3. 用手机环境运行demo这个文件
    $ adb shell '/data/local/tmp/demo'

运行结果会打印一行
hello world!