首页 生活指南 正文内容

如何编写入门级入门教程模块和Frida入门系列课程?|青藤实验室

阿立指南 生活指南 2022-09-08 23:09:13 393 0

常春藤实验室|文字

入坑真是让人上瘾。我之前在 AS3.0 下学习过如何编写入门模块和 Frida 入门课程。我原本想添加更多高级课程。可惜时间有限,只能先挖坑,再补。这段时间,我也在补充开发的相关知识,辅助移动安全学习。随着学习的进展,发现现在的apk有点安全开发意识,会用NDK。开发方面,赵大四的《应用安全替换与逆向分析》甚至在第二章用了很大篇幅介绍了NDK开发的相关内容,并在章末暗示“学NDK开发就是为了学”后面几章的内容基本”,所以只能开始到处找资料尝试学习NDK开发了。

不过赵的书是写IDE相关的NDK入门教程。网上很多资料也是基于AS2.2及以下的教程,但AS3.0及以上版本由于IDE版本的演进,NDK相关项目的开发简化了很多。之前的教程好像已经过时了,所以今天写一篇适合AS3.0及以上的NDK入门教程,应该也是记录新版本NDK开发的过程

基础知识

这里需要补充一些入门的基础知识,不然你做的时间长了都不知道自己在做什么。这里可以直接参考CSDN上童鞋整理的一篇教程,非常全面。

什么是 NDK?

定义:Kit,是一个工具开发包。

NDK是属于 Android 的,与Java并无直接关系

功能:快速开发C和C++的动态库,并自动将so和应用程序一起打包成APK,让您可以在NDK中使用JNI与代码(如C、C++)进行交互。

应用场景:在.的场景中使用JNI。

即 Android开发的功能需要本地代码(C/C++)实现

上面提到了一个叫做JNI的概念。如果你入过移动安全的坑,肯定对这个词很熟悉,那么它是什么意思呢?

什么是 JNI?

定义:Java,Java 原生接口。

作用:允许Java与其他原生语言(如C、C++)交互。

即在 Java代码 里调用 C、C++等语言的代码 或 C、C++代码调用 Java 代码

特别注意:

JNI 是 Java 调用语言的一个特性。 JNI属于Java,与Java没有直接关系

JNI的含义

背景:在实践中,Java 需要与原生代码交互

问题:由于Java是跨平台的,Java与原生代码交互的能力很弱

解决方案:使用 JNI 功能增强 Java 与本机代码交互的能力

JNI 实现步骤

1. 在Java中声明Native方法(即需要调用的本地方法)
2. 编译上述 Java源文件javac(得到 .class文件)
3. 通过 javah 命令导出JNI的头文件(.h文件)
4. 使用 Java需要交互的本地代码 实现在 Java中声明的Native方法 
 如 Java 需要与 C++ 交互,那么就用C++实现 Java的Native方法
5. 编译.so库文件
6. 通过Java命令执行 Java程序,最终实现Java调用本地代码

NDK 实施步骤

1. 配置 Android NDK环境
2. 创建 Android 项目,并与 NDK进行关联
3. 在 Android 项目中声明所需要调用的 Native方法
4. 使用 Android需要交互的本地代码 实现在Android中声明的Native方法 
 比如 Android 需要与 C++ 交互,那么就用C++ 实现 Java的Native方法

jni c++调用java_jni 调用java方法_java调用其他类方法

5. 通过 ndk - bulid 命令编译产生.so库文件 6. 编译 Android Studio 工程,从而实现 Android 调用本地代码

NDK和JNI的关系

JNI就是我们需要在JAVA中实现方法,最终实现Java调用原生代码的目的。

NDK是我们用来实现JNI的工具包,帮助我们编译相关文件,是手段。

好的,简单总结一下:

Java作为跨平台语言,兼容性强,但与C、C++等其他本地语言的交互性较弱。

为了实现Java与其他本地语言的交互,JNI应运而生,实现了在Java中使用C/C++代码,在C/​​C++中使用Java。

为了实现JNI,引入了NDK,帮助开发者在自己的应用中实现Java源代码与其他原生语言的交互。

那么我们为什么要学习 NDK?

因为很多应用程序通过JNI调用C/C++来防止一些核心代码被反编译,这些代码在Java源码中是看不到的,所以作为安全测试人员,掌握NDK的相关知识可以帮助我们打下​​良好的基础为比较难的apk反编译打下基础。

说了这么多,是时候开始实战了~~

测试环境

操作系统:Windows 10
Android Studio版本:3.1.2
测试机:Google Nexus 5X(已root)
测试机版本:Android 4.4.4
Java版本:1.8.0_60

初始状态下,我这里没有安装NDK相关内容。我稍后会介绍它。目前的状态是一个apk可以正常编译并在手机上运行。

基本的准备工作到此完成,是时候开始了。

使用 NDK 编写您的第一个应用程序

首先确认我们的目标

目标:在AS 3.0版本中使用NDK编写一个在屏幕上输出指定字符串的apk

嗯,一点也不……

不过没关系。大家可以参考一些教程互相交流。看了很多教程,终于弄明白了相关的套路,所以我们正式开始吧~~~

第一步:新建项目

记得上下勾选“C++”,然后一路Next,但是第二步卡住了。原因是AS需要去外网下载一些libs来支持我们的C++,所以AS需要翻越长城,天梯自成一体。准备好了。

如果你的梯子不错,应该很快就好了。

继续下一步直到下一步,这是加载 C++ 后的附加内容之一。

选择C++版本,默认是“”,下面什么都不需要勾选,然后是“”,如果你对这些配置项感兴趣,我这里也简单补充一下。

C++ Standard : 希望使用哪种C++标准,一般情况下,选择默认即可。 
Exceptions Support(-fexceptions): 是否启用对 C++ 异常处理的支持,如果启用,Android Studio 会将 

-fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。 
Runtime Type Infomation Support (-frtti): 如果您希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将
 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。

好的,等待项目创建完成。

如果你是个细心的小伙子,你可能会发现有些地方不太对劲……

我们的教程是开发 NDK 应用程序,我们没有安装 NDK。创建这样的项目可以吗? “C++”能帮我们搞定NDK吗?

第二步:安装 NDK 及其插件

当然你不能没有 NDK,你在创建一个新项目的那一刻就知道了。

左下角直接报错“NDK not”。好吧,它不起作用。安装NDK的方式其实并不难。可以通过AS自带的SDK安装。

在右上角或“文件”->“”->“&”->“”->“SDK”打开。

在 SDK Tools 中勾选三项:

CMake
LLDB
NDK

其中需要NDK。前两个在一些辅助编译调试的教程中提到过,这里也补充一下。

当然你还是需要一个梯子,不然你打开SDK Tools会发现没有这个东西可以选择,而且左下角还在加载信息:

如果网速足够快,通常需要几分钟。如果是第一次安装,多试几次就OK了。前提是你的梯子很结实。安装完成后,SDK Tools自然会显示“”会显示出来。

好的,回到项目,在右上角同步,就完全OK了。

好,我们来简单的看一下目前的文件情况:

视图可能看起来有点粗略,让我们切换回视图:

可以看出,主文件夹中除了java文件夹外,还多了一个cpp文件夹。里面的文件是-lib.cpp。让我们来看看里面有什么。先来看看我们熟悉的.java的源码吧。 :

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 = (TextView) 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();
}

与常规项目的.java相比,静态方法块加载和()调用更多。

再看-lib.cpp文件jni 调用java方法,后缀名表示这是一个典型的C++类:

#include 
#include 
extern "C" JNIEXPORT jstring
JNICALL
Java_com_example_xfn_myndktest_MainActivity_stringFromJNI(
 JNIEnv *env,
 jobject /* this */) {
 std::string hello = "Hello from C++";
 return env->NewStringUTF(hello.c_str());
}

C++的知识已经尘封多年了,但是大致的意思还是可以理解的。一开始就引入了jni和两个头文件。

这里和都是JNI关键字,表示这个函数要被JNI调用。

然后定义一个方法返回字符串“Hello from C++”,这个方法是上面.java调用的最后一个方法!

说实话,如果你只是想看jni的java实现,可以编译运行项目,使用NDK编译apk。此时屏幕上显示“Hello from C++”。

当然,我们是想自己实现JNI的人。自然,我们不能止步于此。项目中有.java和-lib.cpp来实现JNI。我们也可以编写一对java和c/c++文件来实现类似的实现。效果。

Step3:自己实现JNI

首先我们右键点击cpp文件夹,新建一个“C/C++文件”。

命名为“”,记得检查后缀名是.c而不是.cpp,否则后面会报错

这个c文件主要实现返回自定义字符串,比如返回“Hello NDK From C”。

#include 
JNIEXPORT jstring

jni c++调用java_jni 调用java方法_java调用其他类方法

JNICALL Java_com_example_xfn_myndktest_HelloNDK_sayhello(JNIEnv *env, jclass jclass) { return (*env)->NewStringUTF(env,"Hello NDK From C"); }

我们自定义方法的名字是(),全名以包名为前缀。

源码和-lib.cpp很相似,但是如果保存为.cpp格式,会在*env处报错,这自然是C和C++的区别造成的。

好了,有了C文件,我们就可以写对应的java文件来调用了。我们在java文件夹中新建一个java类,命名为“”,内容如下:

package com.example.xfn.myndktest;
public class HelloNDK {
 static {
 System.loadLibrary("HelloNDK");
 }
 public native String sayhello();
}

静态块声明加载,然后调用方法(),自然就是我们上面c中定义的()方法。

当然我们的目标是在主屏幕上输入我们的字符串“Hello NDK From C”,所以我们还需要修改.java和.xml。

首先在.xml中注册一个来显示我们的字符串。


然后回到.java,调用方法中的方法,通过新类的()方法,使用方法显示其返回值作为方法的参数。

TextView tv2 = (TextView)findViewById(R.id.tv_hellondk);
tv2.setText(new HelloNDK().sayhello());

当然,这并没有结束。如果你看过相关的教程,编译头文件和修改配置的操作还是很多的。当然,上面的AS3.0不需要这么麻烦,我们离成功还有很长的路要走。

Step4:修改.txt

文件在app目录下

.txt 实际上源自 CMake 工具。 CMake 是一个跨平台的编译工具,比 make 更先进,使用也更方便。 CMake主要编写.txt文件,然后使用cmake命令将.txt文件转换成make需要的文件,最后使用make命令编译源码生成可执行程序或共享库(so())。

在此处添加以下内容:

add_library( # Sets the name of the library.
 HelloNDK
 # Sets the library as a shared library.

jni c++调用java_java调用其他类方法_jni 调用java方法

SHARED # Provides a relative path to your source file(s). src/main/cpp/HelloNDK.c )

主要目的是在上面加上.c的信息,让编译器知道我们自定义了一个叫 的库,这样就可以通过JNI调用java源代码了。

好的,如果你看到这个,我很高兴的告诉你,你马上就能看到结果了jni 调用java方法,哈哈~

Step5:编译并运行第一个 NDK 应用程序

我们下一步其实是直接编译运行,但是如果你一路跟着我,你会发现,不,源代码有错误

Can't parse() 方法...这个其实不影响,AS作为一系列编译器,java中的源代码在apk没有运行的时候不能和C文件中的方法动态交互,我们编译直接运行就可以了。

很不错,系统的Hello from C++输出在屏幕中央,我们定义的“Hello NDK From c”在屏幕左上角。这实际上是由 .xml 控制的。别担心,我们可以说我们的目标已经实现了!恭喜~

NDK编译区别

如果你只是想编译一个NDK应用程序,那么上面其实已经完成了。下面我简单介绍一下NDK编译出来的apk和普通apk的区别。

我们在AS build中生成我们项目的apk,然后进入相关目录。我们知道apk本质上是一个zip,我们把后缀改成zip,然后解压到文件夹中。

和普通的apk没什么区别,我们进入lib目录。

里面有4个文件夹,分别对应不同的CPU系统架构。我们的手机是arm-v7架构,进入-v7a文件夹即可。

如您所见,此时我们cpp文件夹中的两个文件就是这个文件夹中的so文件的形式,那么我们的.c文件也是在这里编译生成的吗?

我们用IDA打开直接拖进去,注意选择类型为arm。

本次不关注其他内容,请自行调整:

您可以清楚地看到我们在.c中定义的字符串“Hello NDK From C”。

看来so文件是编译时生成的c文件。

总结

AS3.0:

中NDK编程的一般步骤小结

1. 创建C++ support项目;
2. 配置NDK环境;
3. 创建Java文件,在该类中调用native方法;
4. 创建c/cpp文件并实现头文件里面的方法;
5. Java文件里面加入静态方法块;
6. 配置CmakeLists.txt文件;

当然,我的一些步骤可能会被简化,一些不必要的步骤在我的教程中没有提到,也有一些其他教程中提到的步骤,比如修改构建。我这里就不介绍了,有兴趣的可以自行了解。

本文只是介绍NDK入门相关知识。如果你学过 NDK 编程,如果你想练手,还是可以找 OWASP 的 -.apk 和 -.apk 来配合 Frida 工作,或者找几个基于 NDK 开发的 apk 进行实战。为了加深对相关知识的理解,本教程就先到这里吧~~

参考链接

欢迎 发表评论:

文章目录
    搜索