2023-03-15 15:12:32 +08:00

123 lines
7.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 第六章 功能定制
## 6.1 如何进行功能定制
在开始动手前必须要明确目标需求规划要实现功能的具体表现。根据预定好的目标方向将能抽取的业务部分隔离开来通过开发一个普通的App先对业务实现流程避免直接在AOSP中修改源码发现问题后反复编译排查这些逻辑的细节导致耗费大量的时间成本在简单问题上。
除此之外尽量使用源码版本管理工具来维护代码避免长期迭代后无法找到自己修改的相关代码导致维护非常困难以及迁移代码的不便利如果不想搭建源码版本管理AOSP则尽量保持代码开发时的风格将自己修改处的代码统一打上标识并且到一定阶段进行备份避免因为自己的修改导致系统异常却又无法回退代码解决问题。
在进行功能定制时需要先对目标的执行流程和实现过程有一定的了解然后找到合适的切入点在实现过程中分析源码时需要多多关注AOSP中提供的各种功能函数有些常见的功能函数不要自己重新写一套实现如果要定制的功能在AOSP源码中有类似的实现则直接参考官方的实现。
功能开发的过程实际就是不断熟悉源码的过程可以通过插桩输出日志来加深对执行流程的认知以及对源码的理解。所以接下来这一章中将围绕着通过插桩来加深印象通过模仿AOSP自身的系统服务来添加一个自己的系统服务接着是通过做一个全局的注入器来了解如何在AOSP中添加自己的源码文件。最后通过修改默认权限来熟悉AOSP中APP执行过程的解析环节。
## 6.2 插桩
在Android逆向中插桩是一项非常常见的手段它可以帮助开发人员检测和诊断代码问题。插桩是指在程序运行时向代码中插入额外的指令或代码段来收集有关程序执行的信息。这些信息可以用于分析程序执行流程、性能瓶颈等问题。常见的插桩方式分为静态插桩和动态插桩。
### 6.2.1 静态插桩
静态插桩是指将插入的代码直接嵌入到源代码中并在编译期间将其转换为二进制形式。静态插桩通常用于收集静态信息例如函数调用图、代码覆盖率等。比如将一个APP反编译后找到要分析的目标函数在smali指令中插入日志输出的指令并且将函数的参数或返回值或全局变量、局部变量等需要观测的相关信息进行输出最后将代码回编成apk后重新签名。
### 6.2.2 动态插桩
动态插桩是指在程序运行时动态地将插入的代码加载到内存中并执行。动态插桩通常用于收集动态信息例如内存使用情况、线程状态等。对函数进行Hook就是一种动态插桩技术在Hook中通过修改函数入口地址将自己的代码"钩"入到目标函数中在函数调用前或调用后执行一些额外的操作例如记录日志、篡改数据、窃听函数调用等。Hook通常用于调试、性能分析和安全审计等方面。它可以帮助开发人员诊断代码问题提高程序的稳定性和性能并增强程序的安全性。同时Hook还可以被黑客用于攻击应用程序因此需要谨慎使用。
### 6.2.3 ROM插桩
ROM插桩是指在预置的ROM固件中进行插桩和前面的两种方式不同直接通过修改系统代码对想要关注的信息进行输出即可过程等于是在开发的APP中添加LOG日志一般虽然插桩非常方便但是这并不是一个小型的APP项目其中的调用流程相当复杂需要具有更高的技术要求和谨慎性所以前提是我们必须熟悉AOSP的源码才能更加优雅的输出日志。
## 6.3 RegisterNative插桩
Native函数是指在Android开发中Java代码调用的由C、C++编写的函数。Native函数通常用来访问底层系统资源或进行高性能的计算操作。和普通Java函数不一样Native函数需要通过JNIJava Native Interface进行调用。而Native函数能被调用到的前提是需要先进行注册有两种方式进行注册分别是静态注册和动态注册。
静态注册是指在编译时就将Native函数与Java方法进行绑定。这种方式需要手动编写C/C++代码并在编译时生成一个共享库文件然后在Java程序中加载该库文件并通过JNI接口调用其中的函数。
动态注册是指在程序运行时将Native函数与Java方法进行绑定。这种方式可以在Java程序中动态地加载Native函数避免了在编译时生成共享库文件的过程。通过JNI接口提供的相关函数可以在Java程序中实现动态注册的功能。
接着开始逐步的通过对RegisterNative的分析最终在系统中插桩将所有App的静态注册和动态注册进行输出打印出进行注册的目标函数名以及注册对应的C++函数的偏移地址。
### 6.3.1 Native函数注册
通过前文的介绍了解到Native函数必须要进行注册才能被找到并调用接下来看两个例子展示了如何对Native函数进行静态注册和动态注册的。
当使用Android Studio创建一个Native C++的项目其中默认使用的就是静态注册在这个例子中Java 函数与 C++ 函数的绑定是通过 Java 和 C++ 函数名的约定来实现的。具体地说,在 Java 代码中声明的 native 方法的命名规则为Java_ + 全限定类名 + _ + 方法名并且将所有的点分隔符替换为下划线。例如在这个例子中Java 类的全限定名为 com.mik.nativecppdemo.MainActivity方法名为 stringFromJNI因此对应的 C++ 函数名为 Java_com_mik_nativecppdemo_MainActivity_stringFromJNI静态注册例子如下。
```java
// java文件
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("nativecppdemo");
}
...
public native String stringFromJNI();
}
// c++文件
extern "C" JNIEXPORT jstring JNICALL
Java_com_mik_nativecppdemo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
```
动态注册一般是写代码手动注册将指定的符号名与对应的函数地址进行关联在AOSP源码中Native函数大部分都是使用动态注册方式的动态注册例子如下。
```java
// java文件
public class NativeClass {
private native int dynamicFunction(int arg);
static {
System.loadLibrary("native-lib");
registerDynamicFunction();
}
private static native void registerDynamicFunction();
}
//c++文件
static JNINativeMethod gMethods[] = {
{"dynamicFunction", "(I)I", (void*) dynamicFunction},
};
JNIEXPORT jint JNICALL
Java_com_example_NativeClass_dynamicFunction(JNIEnv *env, jobject instance, jint arg) {
// 实现Native函数逻辑
return arg + 1;
}
JNIEXPORT void JNICALL
Java_com_example_NativeClass_registerDynamicFunction(JNIEnv *env, jclass clazz) {
// 动态注册Native函数
jclass jclazz = env->FindClass("com/example/NativeClass");
env->RegisterNatives(jclazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
env->DeleteLocalRef(jclazz);
}
```
### 6.3.2 RegisterNative执行流程
### 6.3.3 RegisterNative实现插桩
## 6.4 自定义系统服务
## 6.6 进程注入器
## 6.7 修改APP默认权限
### 6.7.1 APP权限介绍
### 6.7.2 APP权限的源码跟踪
### 6.7.3 AOSP10下的默认权限修改