搭建相关
SDK(Software Development Kit)
https://developer.android.com/studio
studio使用
常用linux shell命令
#列出文件目录
ls /dir
#输出文件内容
cat ./file
#搜索匹配文本内容
grep -r "xxx" ./dir #在dir目录下搜索所有包含xxx字符串的文件并列出
grep -r "xxx" ./dir -A 10 #输出前10条
grep -r "xxx" ./ | grep "aaa" #当前目录下同时包含xxx和aaa的文件
#创建多级目录 -p
mkdir -p x/xx
#export导出环境变量
export AS="……"
AS x.s -o x.o
Windows+Docker编译Android源码
下载Kitematic:https://github.com/docker/kitematic/releases
搜索aosp,第一个点击CREATE
adb
adb start-server /*启动*/
adb kill-server /*关闭*/
adb devices /*查看设备*/
/*假设我们要安装一个ebook.apk文件,可以使用如下的命令。*/
adb install ebook.apk
/*假设ebook.apk中的package是net.blogjava.mobile.ebook,可以使用如下的命令卸载这个应用程序。*/
adb uninstall net.blogjava.mobile.ebook
/*重新安装。*/
adb install -r ebook.apk
/*加上-k命令行参数保留数据和缓冲目录,只卸载应用程序。*/
adb uninstall -k net.blogjava.mobile.ebook
https://blog.csdn.net/shengerjianku/article/details/54927530
简述Android知识相关
Android系统架构
系统静态架构图
系统启动架构图
图解:Android系统启动过程由上图从下往上的一个过程是由Boot Loader引导开机,然后依次进入 -> Kernel -> Native -> Framework -> App
Loader层
Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在 ROM里的预设代码开始执行,然后加载引导程序到 RAM;
Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能。
Linux内核层
Android平台的基础是Linux内核,比如ART虚拟机最终调用底层Linux内核来执行功能。Linux内核的安全机制为Android提供相应的保障,也允许设备制造商为内核开发硬件驱动程序。
启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作;
启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。 kthreadd进程是所有内核进程的鼻祖。
硬件抽象层(HAL)
硬件抽象层 (HAL) 提供标准接口,HAL包含多个库模块,其中每个模块都为特定类型的硬件组件实现一组接口,比如WIFI/蓝牙模块,当框架API请求访问设备硬件时,Android系统将为该硬件加载相应的库模块。
Android Runtime&系统库
每个应用都在其自己的进程中运行,都有自己的虚拟机实例。ART通过执行DEX文件可在设备运行多个虚拟机,DEX文件是一种专为Android设计的字节码格式文件,经过优化,使用内存很少。ART主要功能包括:预先(AOT)和即时(JIT)编译,优化的垃圾回收(GC),以及调试相关的支持。
这里的Native系统库主要包括init孵化来的用户空间的守护进程、HAL层以及开机动画等。启动init进程(pid=1),是Linux系统的用户进程, init进程是所有用户进程的鼻祖。
init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程;
init进程还启动 servicemanager(binder服务管家)、 bootanim(开机动画)等重要服务;
init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程), Zygote是所有Java进程的父进程,Zygote进程本身是由init进程孵化而来的。
Framework层
Zygote进程,是由init进程通过解析init.rc文件后fork生成的,Zygote进程主要包含:
- 加载ZygoteInit类,注册Zygote Socket服务端套接字
- 加载虚拟机
- 提前加载类preloadClasses
- 提前加载资源preloadResouces
- System Server进程,是由Zygote进程fork而来, SystemServer是Zygote孵化的第一个进程,System Server负责启动和管理整个Java framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服务。
- Media Server进程,是由init进程fork而来,负责启动和管理整个C++framework,包含AudioFlinger,Camera Service等服务。
App层
Zygote进程孵化出的第一个App进程是Launcher,这是用户看到的桌面App;
Zygote进程还会创建Browser,Phone,Email等App进程,每个App至少运行在一个进程上。
所有的App进程都是由Zygote进程fork生成的。
Syscall && JNI
- Native与Kernel之间有一层系统调用(SysCall)层(Linux系统调用(Syscall)原理);
- Java层与Native(C/C++)层之间的纽带JNI。
通信方式
无论是Android系统,还是各种Linux衍生系统,各个组件、模块往往运行在各种不同的进程和线程内,这里就必然涉及进程/线程之间的通信。对于IPC(Inter-Process Communication, 进程间通信),Linux现有管道、消息队列、共享内存、套接字、信号量、信号这些IPC机制,Android额外还有Binder IPC机制。Android OS中的Zygote进程的IPC采用的是Socket机制,在上层system server、media server以及上层App之间更多的是采用Binder IPC方式来完成跨进程间的通信。对于Android上层架构中,很多时候是在同一个进程的线程之间需要相互通信,例如同一个进程的主线程与工作线程之间的通信,往往采用的Handler消息机制。
想深入理解Android内核层架构,必须先深入理解Linux现有的IPC机制;对于Android上层架构,则最常用的通信方式是Binder、Socket、Handler,当然也有少量其他的IPC方式,比如杀进程Process.killProcess()采用的是signal方式。
关于linuxIPC和Binder采用原因详情:https://www.zhihu.com/question/39440766/answer/89210950
Binder
Socket
Socket通信方式也是C/S架构,比Binder简单很多。在Android系统中采用Socket通信方式的主要有:
- zygote:用于孵化进程,system_server创建进程是通过socket向zygote进程发起请求;
- installd:用于安装App的守护进程,上层PackageManagerService很多实现最终都是交给它来完成;
- lmkd:lowmemorykiller的守护进程,Java层的LowMemoryKiller最终都是由lmkd来完成;
- adbd:这个也不用说,用于服务adb;
- logcatd:这个不用说,用于服务logcat;
- vold:即volume Daemon,是存储类的守护进程,用于负责如USB、Sdcard等存储设备的事件处理。
等等。
Socket方式更多的用于Android framework层与native层之间的通信。
Handler
Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信,Handler消息机制是由一组MessageQueue、Message、Looper、Handler共同组成的,为了方便且称之为Handler消息机制。
总结
详情:https://cloud.tencent.com/developer/article/1415759
Dalvik虚拟机
https://zh.wikipedia.org/wiki/Dalvik%E8%99%9A%E6%8B%9F%E6%9C%BA
Android可执行文件
可执行文件格式DEX(Dalvik VM executes)
一般java编写后的脚本文件是.java,.class是字节码文件,.dex是android平台可执行文件类型,一般java文件打包成jar包后里面的jar资源是.class,如果需要运行于android,那必须jar里面资源为.dex。
程序的生成步骤
APK(Android Package),实际上为一个zip压缩包
每个APK文件中包含一个classes.dex,即为Dalvik虚拟机的可执行文件
APK打包过程:
①打包资源文件,生成R.java文件
检查AndroidManifest.xml的合法性
对res目录下的资源子目录进行处理(处理内容:资源文件名的合法性检查,向资源表table添加条目等)
编译res与asserts目录下的资源并生成resources.arsc,调用函数生成R.java文件
对res目录的子目录下的xml文件分别进行编译(xml文件被加密)
将所有的资源与编译生成的 resources.arsc文件以及”加密“过的AndroidManifest.xml文件打包压缩为resources.ap_文件
②处理aidl文件,生成相应的java文件。
aidl(android interface definition language,Android接口描述语言)
③编译工程源代码,生成相应的class文件。
调用javac编译工程src目录下所有的java源文件(如果有native代码,也会使用NDK编译C/C++代码。
④转换所有的class文件,生成classes.dex文件。
⑤打包生成APK文件。
以包含resources.arsc的文件为基础生成apk文件,文件以.ap_结尾。
添加资源,写入依赖库。
⑥对APK文件进行签名。
⑦对签名后的APK文件进行对齐处理。
使apk包中的所有资源文件距离文件起始偏移为4字节整数倍(通过内存映射访问apk文件时的速度会更快)。
(Java源代码首先被编译成.class文件,然后Android SDK自带的dx工具会将这些.class文件转换成classes.dex。所以我们只需要想办法反编译classes.dex即可得到java源代码。)
dex文件格式
class文件显然有很多可以优化的地方,比如每一个class文件都有一个常量池,如果有重复字符串就造成了资源浪费,所以Dalvik的dex文件对其进行了优化。
数据结构
dex文件结构
一个dex文件由7个部分组成。
dex header为dex文件头,指定一些属性和其他6部分数据结构在dex文件中的物理偏移。
string_ids和class_def结构为”索引结构区“。
真实的数据存放在data数据区。
link_data为静态连接数据区。(目前生成的dex文件,始终为空)
详情:https://juejin.cn/post/6844903847647772686#heading-2
源码:http://androidxref.com/9.0.0_r3/xref/dalvik/libdex/DexFile.h

odex文件格式
odex(OptimizedDEX)
采用odex方式优化的dex文件,包含了加载dex必须的依赖库文件列表,Dalvik虚拟机只需检测并加载所需的依赖库即可执行相应的dex文件,大大的缩短了读取dex文件所需时间。
Apk在安装(installer)时,就会进行验证和优化,目的是为了校验代码合法性及优化代码执行速度,验证和优化后,会产生ODEX文件,运行Apk的时候,直接加载ODEX,避免重复验证和优化,加快了Apk的响应时间。
odex文件结构

dexopt验证与优化
ART虚拟机
详细看这个:https://paul.pub/android-art-vm/
Android程序文件
Activity类
android程序每个应用界面对应一个Activity类
AndroidManifest.xml配置文件
android程序中是使用配置文件来配置入口的Activity界面
uses-permission :表示用户需要授权该程序的权限,上面xml中配置的需要用户授予访问网络的权限。
application : 这个配置节点很重要,它的子节点 activity
就是配置android程序的入口,
android:name=".MainActivity"
配置了程序的初始视图界面为MainActivity
(这个MainActivity类在src文件夹中)。
action节点中的android.intent.action.MAIN
表明这个Activity是整个应用程序的入口点;
而category中的android.intent.category.LAUNCHER
意思是把这个Activity归属到加载器类,即把这个Activity标注为自动会加载和启动的Activity,这样程序启动时候就先加载这个Activity了。
项目文件夹
-
src:存放项目的源代码。
-
gen:该文件是创建项目时候自动生成的,里面包了一个R.java的静态类,它里面包括很多静态类(内部类),每个静态类中的静态成员名称都对应res文件夹中的一个资源名称,保存着该资源的索引,方便在代码中进行获取资源。
-
assets:assets文件夹里面都保存原始的文件格式,在代码中通过AssetManager来进行访问。
assets文件夹主要保存原始的文件格式,比如我需要在代码中访问加载一个html文件,或者一个txt文档,那么就需要把html文件和txt文档保存到assets文件下。
-
bin:存放编译后的apk和资源文件。
-
res:存放项目需要的资源文件,比如字符串,布局,皮肤等,这里面每个资源的索引都保存在R.java类中。
Gradle
wrapper就用来解决解决兼容性问题。wrapper里定义了gradle的版本。若今后构建这个工程时,它都会用我所绑定的gradle版本(避免版本兼容性问题)所以wrapper的作用就是会来检查在你构建这个工程的机器上有没有5.4.1这个版本。如果有就开始构建,发现没有的话就会去到所提供的url下载这个版本。
https://zhuanlan.zhihu.com/p/139685763
smali——未完成
.local指定局部变量的个数
.parameter指定方法的参数(一对一)
语句
ndk
JNI
JNI (Java Native Interface英文缩写),译为Java本地接口。是Java众多开发技术中的一门技术,意在利用本地代码,为Java程序提供更高效、更灵活的拓展。尽管Java一贯以其良好的跨平台性而著称,但真正的跨平台非C/C++莫属,因为当前世上90%的系统都是基于C/C++编写的。同时,Java的跨平台是以牺牲效率换来对多种平台的兼容性,因而JNI就是这种跨平台的主流实现方式之一。
总之,JNI是一门技术,是Java 与C/C++ 沟通的一门技术。
其他
注解
https://www.liaoxuefeng.com/wiki/1252599548343744/1265102413966176
Android开发相关
SDK+NDK
默认创建Android NDK工程时,Android提供了一个简单的JNI交互示例,返回一个字符串给Java层,方法名的格式为:Java_包名_类名_方法名
。
//.java文件中load lib
static {
System.loadLibrary("native-lib");
}
//声明本地的方法
public native String stringFromJNI();
//.cpp文件
//jstring对应返回类型string
//注意方法命名
//JNIEnv*是定义任意native函数的第一个参数,表示指向JNI环境的指针,可以通过它来访问JNI提供的接口方法。
//jobject表示Java对象中的this,如果是静态方法则表示jclass。
//JNIEXPORT和JNICALL: 它们是JNI中所定义的宏,可以在jni.h这个头文件中查找到。
extern "C" JNIEXPORT jstring JNICALL
Java_com_drioider_testndk_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
Java 类型 | Native类型 |
---|---|
java.lang.Class | jclass |
java.lang.Throwable | jthrowable |
java.lang.String | jstring |
jjava.lang.Object[] | jobjectArray |
Byte[] | jbyteArray |
Char[] | jcharArray |
Short[] | jshortArray |
int[] | jintArray |
long[] | jlongArray |
float[] | jfloatArray |
double[] | jdoubleArray |
例子:
//调用
String result = operateString("待操作的字符串");
Log.d("xfhy", result);
//定义
public native String operateString(String str);
//native访问java.lang.String 对应的JNI类型jstring时,不能像访问基本数据类型那样使用,因为它是一个Java的引用类型,所以在本地代码中只能通过类似GetStringUTFChars这样的JNI函数来访问字符串的内容。
//参数string放在了第三个,前两个固定
//调用了env->GetStringUTFChars(jstring)
extern "C"
JNIEXPORT jstring JNICALL
Java_com_xfhy_jnifirst_MainActivity_operateString(JNIEnv *env, jobject thiz, jstring str) {
//从java的内存中把字符串拷贝出来 在native使用
const char *strFromJava = (char *) env->GetStringUTFChars(str, NULL);
if (strFromJava == NULL) {
//必须空检查
return NULL;
}
//将strFromJava拷贝到buff中,待会儿好拿去生成字符串
char buff[128] = {0};
strcpy(buff, strFromJava);
strcat(buff, " 在字符串后面加点东西");
//释放资源
env->ReleaseStringUTFChars(str, strFromJava);
//调用env->NewStringUTF(string)
//自动转为Unicode
return env->NewStringUTF(buff);
}
//java使用Unicode编码,C/C++默认使用UTF编码,所以在native层与java层进行字符串交流的时候需要进行编码转换。GetStringUTFChars就刚好可以把jstring指针(指向JVM内部的Unicode字符序列)的字符串转换成一个UTF-8格式的C字符串。
详情:https://segmentfault.com/a/1190000037594523
Native调用Java静态方法
public class MyJNIClass {
public int age = 30;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static String getDes(String text) {
if (text == null) {
text = "";
}
return "传入的字符串长度是 :" + text.length() + " 内容是 : " + text;
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_xzh_allinone_jni_CallMethodActivity_callJavaStaticMethod(JNIEnv *env, jobject thiz) {
//调用某个类的static方法
//1. 从classpath路径下搜索MyJNIClass这个类,并返回该类的Class对象
jclass clazz = env->FindClass("com/xzh/jni/jni/MyJNIClass");
//2. 从clazz类中查找getDes方法 得到这个静态方法的方法id
jmethodID mid_get_des = env->GetStaticMethodID(clazz, "getDes", "(Ljava/lang/String;)Ljava/lang/String;");
//3. 构建入参,调用static方法,获取返回值
jstring str_arg = env->NewStringUTF("我是xzh");
jstring result = (jstring) env->CallStaticObjectMethod(clazz, mid_get_des, str_arg);
const char *result_str = env->GetStringUTFChars(result, NULL);
LOGI("获取到Java层返回的数据 : %s", result_str);
//4. 移除局部引用
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(str_arg);
env->DeleteLocalRef(result);
}
- Native调用Java实例方法。
- 获取构造方法的id,获取需要调用方法的id。其中获取构造方法时,方法名称固定写法就是
<init>
,然后后面是方法签名。 - 使用NewObject()函数构建一个Java对象。
- 调用Java对象的setAge和getAge方法,获取返回值,打印结果。
- 删除引用。
NDK原生程序——未完成
破解相关
基础
寻找字符串
http://yocosqamaq.top/android%e9%80%86%e5%90%91/
反编译apk文件成功后,会在当前的outdir目录下生成一系列目录与文件。
smali目录下存放了程序所有的反汇编代码,res目录则是程序中所有的资源文件,这些目录的子目录和文件与开发时的源码目录组织结构是一致的。
字符串可能硬编码到源码中,也可能引用自“res\values”目录下的strings.xml文件,apk文件在打包时,strings.xml中的字符串被加密存储为resources.arsc文件保存到apk程序包中。
开发Android程序时,String.xml文件中的所有字符串资源都在“gen/
修改smali,用signapk.jar工具对apk文件进行签名。
ida
apk解压缩后找到classes.dex文件拖入ida
通过ida的hex View修改二进制文件。
修改完需要更改二进制文件的checksum。
将修改后的classes.dex文件覆盖原文件,删除META-INF文件夹,再进行签名之后即可安装。
静态分析
每个Activity都是Android程序的一个显示”页面“,主要负责数据的处理及展示工作。
每个Android程序有且只有一个主Activity,它是程序启动的第一个Activity。
程序的代码入口处:OnCreate()方法。(检查是否有Application类,如果有,先看其OnCreate())
动态分析——未完成
NDK分析
用as初始包含NDK源码进行生成APK
对得到的apk进行解压
与无NDK编写的APK对比:
将lib下的.so文件拖入ida
待解决
Android系统架构需更底层了解
linuxIPC机制
BInder等一些专有机制原理不清