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汇编我也没整太明白,就赶紧结束以免露馅,本篇文章到此为止吧。