之把某個 android app 產品進行反組譯的動作,只是單純地想要驗證該 app 的實作流程,但在 Java 層只看到一些 UI 介面的操作,算是驗證了半個想法,但總覺得缺少什麼,所幸在原本的 apk 檔案內發現了 xxx.so 檔案,透過很基本的 strings 指令可以查看一些敏感字眼,就這樣我找到了鐵證。心中有股莫名踏實感,雖然 xxx.so 檔只能瞧見一些關鍵字,但因為撰寫程式的把 function name 取得很容易猜測功能,所以加加減減就成了一種驗證。
前陣子上過幾個小時的 android 課程,恰好稍微提到 NDK 的部份,此部份主要是設計給遊戲類的使用,例如之前寫 OpenGL ES 時,必須透過 Android Java API 來呼叫,現在則可以直接用 NDK 寫 C 語言達成,除了解決繁瑣的 C to Java 語法,更可以避開 JVM 來提昇效能。除此之外,我覺得 NDK 也是可以用拿來保護關鍵程式碼囉。此筆記就當作簡單的測試 NDK 的用法,在此不提及安裝 Android 開發環境,有興趣可以查看Android & Eclipse 開發環境- 第一次安裝筆記。在此使用 Ubuntu 10.04 64-bit 環境。
首先,從 Android NDK | Android Developers 下載 Android NDK (我在 Ubuntu 所以下載 android-ndk-r5c-linux-x86.tar.bz2) 並挑選一個地方解壓縮(此例為 ~/android-ndk-r5c),除此之外要把 Android SDK 更新至最新版,如果已經安裝好 Android 開發環境,更新方式透過 Eclipse -> [Window]-> [Android SDK and AVD Manager] -> [Installed packages] -> [Update All...]
接著要進行 NDK 環境設置,可查看 android-ndk-r5c/docs/INSTALL.html,可以得知 Android NDK 至少需使用 Android 1.5 系統,而編譯環境至少是 GNU Make 3.8.1,除此之外還需要 Nawk 或 GNU Awk 工具,而常見的 awk 則不適用。
在 Ubuntu 10.04 64-bit desktop 環境上:
$ awk -W version
GNU Awk 3.1.6
Copyright (C) 1989, 1991-2007 Free Software Foundation.
~$ make -version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc
透過 Eclipse 建立一個 android 2.3.3 project,例如 MyNDK:
Project name: MyNDK
Build Target: Android 2.3.3/Android Open Source Project/2.3.3/10
Application name: MyNDK
Package name: com.example.ndk
Create Activity: MyNDK
Min SDK Version: 3
接著,在 Eclipse 左邊 Project Explorer 建立 jni 和 libs 兩個目錄,位置擺在剛剛的 Project 裡(點一下 MyNDK,接著按右鍵選 New -> Folder 輸入 jni,之後在建立 libs 目錄):
MyNDK/jni
MyNDK/libs
在 MyNDK/jni 裡頭新增兩個檔案,分別是 JNI 程式碼和 Android.mk,結構如下:
MyNDK/jni
MyNDK/jni/Android.mk
MyNDK/jni/my-jni.c
其中 Android.mk 和 *.c 可以從 android-ndk-r5c/samples/hello-jni/jni 裡頭尋得範例,簡單筆記:
MyNDK/jni/Android.mk (參考 android-ndk-r5c/samples/hello-jni/jni/Android.mk)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := my-jni
LOCAL_SRC_FILES := my-jni.c
include $(BUILD_SHARED_LIBRARY)
MyNDK/jni/my-jni.c (參考 android-ndk-r5c/samples/hello-jni/jni/hello-jni.c)
#include <string.h>
#include <jni.h>
jstring Java_com_example_ndk_MyNDK_stringFromJNI( JNIEnv* env, jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from My JNI !");
}
其中要留意 function name,裡頭包括 packages name 和 Activity name 等資訊,此例是 com_example_ndk 和 MyNDK。
接著要透過 android ndk 去編譯 my-jni.c,除了在 Command line 可以切換到 MyNDK 目錄,用 android-ndk-r5c/ndk-build 進行編譯外,還可以透過 Eclipse 設定好,讓你每次自動編譯好:
在 Eclipse 左邊 Project Explorer 點選 MyNDK,選右鍵 -> Properties -> Builders -> New -> Program
在 Main 分頁:
Name: NDK_Builder
Location(ndb-build位置): /home/user/android-ndk-r5c/ndk-build
Working Directory:(project位置): ${workspace_loc:${project_path}}
在 Refresh 分頁:
將 Refresh resources upon completion 打勾,並且在 Specific resources 選擇 MyNDK/libs 目錄
在 Build Options 分頁:
勾選 Allocate Console(necessary for input)、Launch in backgroud、After a "Clean"、During manual builds、During auto builds 和 Specify working set of relevant resources
在 Specific resources 選擇 MyNDK/jni
如此一來,只要編譯或清除時,自動會編譯 jni 的部份,可以使用 Build all(快速鍵Ctrl+B) 試試,可以看到 Console 的輸出:
Compile thumb : my-jni <= my-jni.c
SharedLibrary : libmy-jni.so
Install : libmy-jni.so => libs/armeabi/libmy-jni.so
接著,才正式要寫 Java 程式部份,切換到 MyNDK.java:
package com.example.ndk;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MyNDK extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main);
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
}
public native String stringFromJNI();
static {
System.loadLibrary("my-jni");
}
}
其中特別的是要宣告 jni 使用到的函數,這跟 my-jni.c 裡頭的宣告有關,以及 System.loadLibrary 的部份,跟 jni/Android.mk 設定的編譯結果也有關係,如果都不相符,執行時會出現 exception。
最後跑起來的成果:
整個 Project 結構:
相關文章:
- Android 開發教學筆記 - 使用 NDK / JNI 實作從底層呼叫上層 (C call Java)
- Android 開發教學筆記 - 設定 Windows 之 Android NDK (Native Development Kit) 開發環境
感謝大大分享~獲益良多
回覆刪除你好,我的ndk_build並沒有產生出libmy-jni.so檔
回覆刪除Eclipse的builder已確認有設定好,但是build project的console沒有半點動靜(已確認ndk加入Path環境變數)
請問是什麼問題?
勞煩之處,請見諒,謝謝。