AndroidStudio版本: 4.0.1
使用AndroidStudio進行ndk開發很簡單,我們的目標是生成一個so文件,里面有一個getSign方法,在Java層調用so文件中的getSign方法獲取。
先創建一個NDK項目,創建的時候拉到最下面選擇Native C++,然后Next:
然后輸入項目的名字:
這一步保持默認即可(反正我也不懂C++...):
創建完項目之后糟糕紅色嘆號了,有這么個提示:
這是因為創建的項目沒有配置NDK,NDK需要單獨下載,可以去這個頁面選擇自己平台對應的NDK下載:
https://developer.android.com/ndk/downloads?hl=zh-cn
下載完解壓到本地即可,注意解壓后的路徑名中盡量不要包含中文以免無謂踩坑。
然后回到AndroidStudio,為項目設置上剛剛下載的這個NDK,菜單選擇File-->Project Structure:
切換到SDK Location,然后在Android NDK Location那一項選擇剛剛下載的NDK解壓到的目錄,OK之后項目就會自動開始sync。
sync完之后就不會報錯了,還顯示了個小對號:
切換到Project視圖看下生成的項目結構:
先看MainActivity自動生成的內容:
package cc11001100.android.ndk_study_003; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String stringFromJNI(); }
自動加載了一個native-lab的so文件,然后還自動聲明了一個stringFromJNI的native方法,然后再來看cpp下自動生成的這個native-lib.cpp文件:
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_cc11001100_android_ndk_1study_1003_MainActivity_stringFromJNI( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
看起來跟我們想要的結果已經差不多了,不行,名字不一樣,接下來就是修改名字為getSign,先改Java層的MainActivity,注意修改的時候使用重構重命名,Shift+6,改完之后:
package cc11001100.android.ndk_study_003; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { // Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Example of a call to a native method TextView tv = findViewById(R.id.sample_text); tv.setText(getSign()); } /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */ public native String getSign(); }
會發現native-lib.cpp的名字也自動修改了,都不用我們手動干預,很方便:
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_cc11001100_android_ndk_1study_1003_MainActivity_getSign( JNIEnv* env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
然后Run一下看看安裝效果:
OK,接下來打包成apk,然后反編譯觀察一下這種方式打包的效果是怎樣的:
選擇打包成apk:
key可以創建一個新的也可以用老的:
構建release包,簽名版本為V2,然后“Finish”等一段時間:
如果打包成功的話,在app下會多出一個release文件夾,下面有個app-release.apk文件,這就是本次打包成功的apk文件:
然后啟動jeb,把打包好的apk文件拖到jeb中看下,項目結構是這樣的:
反編譯MainActivity看下:
然后到lib下,把so文件拿出來:
然后拖到IDA64中,在Function name窗口中輸入Java搜索我們的JNI方法,因為我們是靜態注冊的,所以這里是能搜到的,然后雙擊搜索到的函數名,查看其匯編代碼:
然后分析這段匯編的代碼就能還原原本邏輯了,其實arm匯編我也沒整太明白,就趕緊結束以免露餡,本篇文章到此為止吧。