ARM(advanced RISC machines)
RISC(Reduced Instruction Set Computer)精简指令集计算机。特点是所有指令的格式都是一致的。所有格式的指令周期也是相同的,并且采用流水线技术。
ARM微处理器的工作状态一般有两种:并可在两种状态之间切换。ARM状态—此时处理器执行32位的字对齐的ARM指令;Thumb指令—此时处理器执行16位的半字对齐的Thumb指令。处理器工作状态的转变并不影响处理器的工作模式和相应寄存器中的内容。
Arm微处理器在开始执行代码时,应该处于ARM状态
进入Thumb状态:当操作数寄存器的状态位(位0)为1时,可以采用执行BX指令的方法,从ARM切换至Thumb。此外,处理器处于Thumb状态时发生异常(如IRQ\FIQ\Undfef、Abort、SWI),则异常处理返回时,自动切换至Thumb状态
进入ARM状态:当操作数寄存器的状态为0的时候,,执行BX指令时,可以从Thumb切换至ARM。此外,在进行异常处理时,把PC指针放入异常模式链接寄存器中,并从异常向量地址开始执行程序,也可以切换至ARM状态
ARM体系结构将存储器看作是从零地址开始的字节的线性组合。从零字节到三字节防止一个存储的字数据,,,依次排列。作为32位微处理器,ARM体系结构所支持的最大寻址空间为4GB(232字节)。ARM体系结构可用两种方式存储字数据,称之为大端格式和小端格式:大端(字数据的高字节存储在低地址中,字数据的低字节则存放在高地址中)
ARM支持字节(8位)、半字(16位)、字(32位)这三种数据类型。其中,字需要4字节对齐(地址低两位为0),半字需要二字节对齐(地址的最低位为0)
ARM支持7种运行模式,分别为:
用户模式(usr):ARM处理器正常的程序执行状态
快速中断模式(fiq):用于高速数据传输或通道处理、
外部中断模式(irq):用于通用的中断处理
管理模式(svc):操作系统使用的保护模式
数据访问终止模式(abt):当数据或指令预终止时进入该模式,可用于虚拟存储及存储保护
系统模式(sys):运行具有特权的操作系统任务
未定义指令中止模式(und):当未定义的指令执行时进入该模式,可用于支持硬件协处理器的软件仿真
ARM运行模式可通过软件改变,也可通过外部中断或异常处理改变
除用户模式之外,其余六种模式称之为非用户模式,或特权模式(privilegedmodes)。除用户模式、系统模式之外的5种模式称之为异常模式(exception modes)。常用语处理中断和异常(exeception modes),以及需要访问受保护的系统资源等。
ARM共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。不能同时访问,具体哪些可编程访问,取决于工作状态及运行模式。但通用寄存器R14~R0,程序计数器PC。一个或两个状态寄存器都是可访问的
通用寄存器包括R0~R15,可分为3类:
未分组寄存器R0~R7
分组寄存器R8~R14
程序计数器PC(R15)
寄存器R13在ARM指令中常用作堆栈指针
R14也成称作子程序连接寄存器(subruntimelink regieter),或连接寄存器LR
由于ARM体系结构采用了多级流水线技术,对于ARM指令集而言,PC总是指向当前指令的下两条指令的地址,即PC的值为当前指令的地址值+8个字节
寄存器R16用做当前程序状态寄存器(currentregieter status register,CPSR),CPSR可在任何运行模式下被访问,它包括条件标识位,中断禁止位,当前处理器模式标识位,以及其他一些相关的控制和状态位
每一种运行模式下又都有一个专用的物理状态寄存器,称为备份的程序状态寄存器(savedprogram status register,SPSR),当异常发生时,SPSR用于保存CPSR的当前值,从异常退出时则可由SPSR表来恢复CPSR
ARM的指令集是加载/存储型的,也即指令集仅能处理寄存器中的数据,而且处理结果都要放回寄存器中,而对系统存储器的访问则需要通过专门的加载/存储指令来完成。ARM的指令集可分为跳转指令,数据处理指令,程序状态寄存器(PSR)处理指令,加载/存储指令,协处理器指令和异常产生指令六大类。
ARM体系结构包含一个当前程序状态寄存器(CPSR)和5个备份的程序状态寄存器(SPSRs),备份的程序状态寄存器用来进行异常处理,其功能包括:
保存ALU中的当前操作信息;控制允许和禁止中断;设置处理器的运行模式
在ARM状态下,绝大多数的指令都是有条件执行的;在Thumb状态下,仅有分支指令是有条件执行的
寻址方式:处理器根据指令中给出的地址信息来寻找物理地址的方式
ARM支持的寻址方式:立即寻址,即立即数寻址,操作数本身就在指令中给出,只要取出指令也就取到了操作数。这个操作数被称为立即数,对应的寻址方式也就叫做立即寻址
寄存器寻址:利用寄存器中的数值作为操作数,这种寻址方式时各类微处理器经常采用的一种方式(执行效率较高)
寄存器间接寻址:以寄存器中的值作为操作数的地址,而操作数本身存放在寄存器中
基址变址寻址,多寄存器寻址,相对寻址,堆栈寻址
根据堆栈的生成方式,当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈
Thumb指令集时ARM指令集的一个子集,允许指令编码为16位的长度。Thumb指令集在保留32位代码优势的同时,大大节省了系统的存储空间
只要遵循一定的调用规则,Thumb指令和ARM指令可以互相调用
大多数Thumb的指令都是无条件执行的,Thumb中数据处理指令的操作数处理32位,指令地址也为32位
大多数Thumb数据处理指令的目的寄存器与其中一个源寄存器相同
Thumb指令集是ARM指令集压缩格式的子集,所有Thumb指令均有对应的ARM指令。执行Thumb指令时,先进行动态解压缩,然后作为标准的ARM指令执行。其主要特点有:
采用16位二进制编码,代码密度小
如何区分指令流取决于CPSR的第三位(T)
大多数Thumb数据处理指令采用2地址格式
由于16位的限制,移位指令变成单独指令
Thumb指令集没有协处理器指令、单寄存器交换指令,乘加指令,64位乘法指令以及程序状态寄存器处理指令,而且指令的第二操作数受到限制
除了分支指令B有条件执行功能外,其他指令均为无条件执行
另外,Thumb时一个不完整的体系结构,不可能只执行Thumb代码而不支持ARM指令集
DDMS(Dalvik DebugMonitor Service)Dalvik调试监视服务的缩写,由Android软件开发包(software development kit)SDK提供的调试工具
DDMS提供了链接logger查看的logcat功能。开发者开发时一般会使用android.util.Log打印调试自己的信息,可从log上看出一部分程序的逻辑走向。整个系统的log都会显示在Logcat中,我们可通过如TAG,PID,Application,Loglevel过滤
拥有了root权限后,进入系统的“设置”后(包名为com.android.settings)应用的私有目录下,我们就能看到它的所有私有数据:database(数据库),lib,shared,prefs(sharedpreference),cache(缓存),files(自定义存储文件)
Android的内核中有一个函数叫trace,它能够动态地attach(跟踪一个目标进程),detach(结束跟踪一个目标进程),peektext(获取内存地址)等。另一个内核函数dlopen,能够以指定的动态链接库文件。
调用Ptrace让PC指向LR堆栈,调用dlopen将希望注入的动态库注入至目标进程中
对于代码块的注入(hook api),可以使用mmap函数分配一段临时的内存来完成代码的存放
目标进程中的mmap函数地址的寻找与HOOKAPI函数地址的寻找都需要通过目标进程的虚拟地址空间解析与ELF文件解析来完成,算法如下:
读取/proc/pid/maps文件找到链接库的基地址
读取动态库,解析ELF文件,找到符号
计算目标函数的绝对地址=函数地址+动态基地址
总结向目标进程中注入代码:
用ptrace函数attach上目标进程-》发现装载共享库so函数-》装载指定的.so-》让目标进程的执行流程跳转到注入的代码执行-》使用ptrace函数的detach函数释放目标进程
工作原理:逆向分析应用程序找到hook点-》调试宿主进程ptrace(PTRACE_ATTACH)->宿主进程中注入代码块hack.so(加载.so流程,获取mmap,dlopen,dlsym地址->调用mmap写入字符串hack.so->修改PC寄存器,指向dlopen,加载hack.so->调用dlsym,获取hack.so调用执行)->宿主进程释放ptrace(PTRACE_DETACH)
根据API_HOOK对应的API不一样,我们可以分为使用androidSDK开发环境的JavaAPIHook与使用android NDK开发环境的Native APIHook。对于Android中so库文件的函数Hook,根据ELF文件的特性能分为Got表Hook,sym表Hook,以及inlineHook
Java API hook:通过对android平台的虚拟机注入与java反射的方式,来改变android虚拟机调用函数的方式(classloader),从而达到java函数重定向的目的。(由于采集java中的反射函数来重定向函数,所以java中反射出现的问题也会在此出现,如无法调用关键字的native方法函数(JNI实现的函数),基本类型的静态常量无法反射修改等)
对应的app.process正是Zygote进程启动一个应用程序的入口,常见的hook框架Xposed与Cydia substrate是通过替换app_process来完成全局hook的
DEX文件Android应用程序的逻辑所在
全磁盘加密(full disk encryption,FDE)FDE只针对用户数据分区的存储系统配置文件和应用程序数据,引用和系统分区用来存储系统内核文件,是不进行加密的
Android的磁盘加密使用dm-crypt,是一个加密的物理块设备到目标虚拟设备映射的设备
在Android中的加密机制使用随机生成AES CBC模式的128位密钥。CBC模式需要一个初始化向量(IV),且是随机和不可预测的,以便安全地进行加密
Android对每一个扇区加密密钥使用加盐向量进行初始化(ESSIV),使用SHA256算法进行HASH,最后生成每个扇区的IV,ESSIV。利用散列算法将加密的二次密钥s和密钥K从磁盘弄出,称为盐。然后使用盐,作为加密密钥,加密的每个扇区号SN以产生每个扇区IV。(ESSIV不改变CBC的延展性,也不能确保加密块的完整性)
磁盘加密密钥(Android源代码称之为master key,)用一个128位AES密钥(KEK)加密,从用户提供的密码衍生而来。Android之前使用一个2000次迭代的PBKDF2和128位随机盐值,作为密钥导出方式。然后将得到的master key 和盐值存储在磁盘上 ,占整个加密 分区的最后16KB,所以称为加密页脚。
ADB由三部分组成,分别是ADBServer,ADB Daemon,ADBCommandLine
ADBserver运行在host设别的后台进程中,主要是为了客户端和设备间的解耦和,控制设备的各种连接状态。
ADBDaemon即ADB的守护进程,运行在Android设备或者模拟器中,它提供客户端实际有效的服务,接收和处理一些来自USB、tcp\ip发送来的命令。ADBCommandline,即一些常用的ADB命令,开发者能够通过发送此类命令,在设备上完成一些特定的操作
ADB私钥文件(adbkey)存储在主机上,是标准的OpenSSL PEM格式。公共密钥文件(adbkey.pub)包含公钥基础64位编码,标准RSA PUBLIC KEY结构。这些密钥都存储在/data/misc/adb/adb-keys/文件中
SELinux主要有4个组成部分:对象管理器(Object Manager,OM),对象权限缓存(
Access Vector Cache,AVC)安全服务和策略
SELinux的特点:MAC(mamdatoryaccess control)对访问的控制强制化,TE(Type enforcement)对于进程只赋予最小权限,。Domain迁移,防止权限升级,RBAC(role based accesscontrol)对于用户只赋予最小的权限
Linux内核主要由5个子系统组成:进程调度(process management)内存管理(memory m,anagement),虚拟文件系统(virtual files system ),进程通信(inter-processcommunication)进程调度是核心,其他依赖于进程调度
Linux采用虚拟内存机制,为每个进程提供高达4GB的虚拟内存空间
Linux还采用交换机制对内存数据的换入换出进行控制,保留物理内存中的有效页面,保存经常访问的页面而将不常访问的页面淘汰。同时内存管理还要负责交换内外存空间的程序块。Linux内存管理分别与硬件相关、无关。相关的为硬件提供了虚拟的内存接口,无关的提供了内存映射和逻辑内存的对换
Android内核采用与linux不同的LMK(low memory killer)低内存管理机制
Android还采用了一种内存共享机制:匿名共享内存(asynymous shared memory,ashmem),其源代码文件为mm/ashmem.c
Android操作系统使用不属于Linux标准内核的YAFFS2(yet another flash file system2)文件系统。其适用于使用闪存作为存储介质。YAFFS2比较适用于采用NAND Flash芯片的移动终端
YAFFS2文件系统采用日志结构机制会先分新再删旧。意外掉电时,只丢失当前修改数据的最小写入单位,从而实现了掉电保护,保证了文件数据的完整性。YAFFS2文件系统中包括文件管理接口,内部实现层和NAND,这种层次化的结构级使得YAFFS2文件系统能够很好地被集成到内核中
Linux内核采用单内核机制。Linux1.2以后引入可加载的内核模块(loadable kernel module,LKM),为内核提供动态、可伸缩的内核扩展功能
LKM机制广泛应用在类unix系统中,如FreeBSD的KLD(kernel loadable module)。Macos的KExt(kernelextension)等,用来处理系统硬件、文件系统以及系统调用等功能的扩展
单内核和微内核是操作系统内核设计的两大阵营。
微内核:所在的服务器(微内核的功能被划分为独立的过程,称服务器)运行在各自的地址空间,互相独立,只有强烈要求特权服务的服务器才运行在特权模式下(比如进程间通信、调度,基本的输入、输出,内存管理),其他服务器运行在用户空间,各个服务器之间
单内核:单内核作为一个大过程实现,同时运行在一个单独的地址空间(优点:简单,高性能(内核间通信微不足道,内核可以直接调用函数),缺点:可维护性、可扩展性差)
Linux引入动态可加载内核模块,模块可以在系统运行期间加载或从内核卸载。模块是具有独立功能的程序,它可以被单独编译但不能独立运行。它在运行时被链接到内核作为内核的一部分在内核空间运行(属于内核编程,不能访问C库等)模块通常由一组函数和数据结构组成,用来实现一种文件系统,一个驱动程序或其他内核上层的功能
LKM其实是一个特殊的可执行可链接格式(executable and linkable format,ELF)对象文件爱你。内核对象为后缀是“.bo“的文件
系统调用的执行需要进程以用户态进入内核态,基于ARM架构的系统通过其软件中断(software interrupt,)指令来实现该操作。
Linux内核调用主要分为旧应用二进制接口(old application binary inetrface,OABI)和嵌入式应用二进制接口(embedded application binary interface,EABI)两种规范。Linux内核的系统调用表(sys-call table)是存储系统调用地址的跳转表,系统调用根据系统调用号(表索引)指到内核调用函数,然后执行相应内核函数。在android系统中,系统不兼容OABI规范,仅支持EABI系统调用规范
用户程序使用系统调用必须先由软中断进入内核,再由中断处理程序进入系统调用表(sys_call_table),最后通过系统调用表找到系统调用函数
Android电话子系统实现的机制基本功能是呼叫(calling),短信(SMS)数据连接(data connection),SIM卡服务和电话本等
Android电话子系统分为4个部分:电话和短信等应用,电话服务框架,电话接口层(radio interface layer,RIL),modum驱动
AT命令是一种调制解调器命令语言,每条命令以AT开头,后面接字母和数字表示具体功能。AT命令被用来交换通信双方的modem驱动,提供电话服务,一般使用USB或UART方式
AT命令是终端设备(terminalequipment,TE)向终端适配器(terminal adapter ,TA)发送的。PA,TE通过发送AT指令来控制移动台(mobilestation)的功能,与GSM网络服务进行交互
Inotify是Linux内核提供的一种高效实时的文件系统时间监控框架。在执行inotify.init函数时,就能创建监控实例,返回一个文件描述符。通过这个文件描述符接口可以使用通常的文件I/O操作,select和pull来监视文件系统的变化。
Proc文件系统是系统中内核空间和用户空间通信的接口