memo问题

ARM汇编

ARM汇编中B BL BLX 之间的区别

  • B 跳转指令

跳转指令B使程序跳转到指定的地址执行程序。

B 0x1234 跳转到1234处

  • BL 带链接的跳转

BL指令用于实现子程序调用。首先将当前指令的下一条指令地址保存在LR寄存器,然后跳转的lable。通常用于调用子程序,可通过在子程序的尾部添加mov pc, lr 返回。

BL func 跳转到子程序func处执行,同时将当前pc值保存到LR中。

  • BX 带状态切换的跳转

最低位为1时,切换到Thumb指令执行,为0时,解释为ARM指令执行。

BX R0 跳转到R0中的地址,如果R0[0]=1,则进入Thumb状态。

  • BLX 带链接和状态切换的跳转

结合了BX与BL功能。

BLX 指令从ARM 指令集跳转到指令中所指定的目标地址,并将处理器的工作状态有ARM 状态切换到Thumb 状态,该指令同时将PC 的当前内容保存到寄存器R14 中。因此,当子程序使用Thumb 指令集,而调用者使用ARM 指令集时,可以通过BLX 指令实现子程序的调用和处理器工作状态的切换。

同时,子程序的返回可以通过将寄存器R14 值复制到PC 中来完成。

该指令为无条件执行指令

Arm和Thumb的区别

https://blog.csdn.net/itismine/article/details/4753701?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

在编写Thumb指令时,先要使用伪指令CODE16声明(16位),编写ARM指令时,则可使用CODE32伪指令声明(32位)。

  1. Thumb指令集没有协处理器指令、信号量指令、以及访问CPSR或SPSR的指令,没有乘加指令及64位乘法指令等,且指令的第二操作数受到限制;

  2. 大多数的Thumb数据处理指令采用2地址格式;

  3. 除了跳转指令B有条件执行功能之外,其他指令均为无条件执行,而且分支指令的跳转范围有更多限制;

  4. 数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果放入其中一个操作数寄存器中,而不是放入第3个寄存器中;访问寄存器R8~R15受到一定的限制,除MOV、ADD指令访问R8 ~ R15外,其他数据处理指令总是更新CPSR中ALU状态标志,访问寄存器R8~R15的Thumb数据处理指令不能更新CPSR中的ALU状态指示。

  5. Thumb状态下,单寄存器加载和存储指令只能访问寄存器R0~R7;

  6. LDM、STM指令可以将任何范围为R0~R7的寄存器子集加载或存储;

  7. PUSH、POP指令使用栈寄存器R13作为基址堆栈操作。

    大多数ARM数据处理指令采用的是3地址格式(除了64位乘法指令外)。

所有异常都会使微处理器返回到ARM模式状态,并在ARM的编程模式中处理。由于ARM微处理器字传送地址必须可被4整除(即字对准),半字传送地址必须可被2整除(即半字对准)。而Thumb指令是2个字节长,而不是4个字节,所以,由Thumb执行状态进入异常时其自然偏移与ARM不同。

16位Thumb指令集是从32位ARM指令集提取指令格式的,每条Thumb指令有相同处理器模型所对应的32位ARM指令。

pc、lr的寄存器是什么,对应寄存器的编号

  • LR(链接寄存器) → R14 指向PC下一次要执行的地址

  • PC(程序计数器) → R15 存储将要执行的指令地址(取值地方)

程序是先取值后译码再执行

arm怎么传参

ARM架构下,函数参数是通过 r0~r3寄存器传递的;但是如果参数超过四个,就要借助于栈了。

arm64

https://www.jianshu.com/p/99067af33f14
http://blog.chinaunix.net/uid-29955651-id-4855990.html

注入和Hook

常见注入和hook

注入

ptrace

过程
  1. 将当前的hook进程attach到目标进程(通过目标apk的包名,在/proc中一个一个找;如果当前/proc/pid/cmdline中有目标包名名字,那这就是目标进程),然后保存当前远程进程的上下文寄存器环境(有个pt_regs可以 存放寄存器环境)
  2. 获取远程进程中的mmap函数,申请一块内存空间,获得远程进程中申请的内存空间的首地址
    【远程call函数的时候
    ①先存函数参数
    ②再设置ARM_pc寄存器为需要调用的函数地址(要记得判断跳转地址是不是arm!如果地址末位是1,CPST寄存器的标志T置位,解释为Thumb代码;否则复位,解释为arm)
    ③设置寄存器环境,并使目标进程继续运行
    ④判断是否成功执行函数(看waitpid函数返回的stat值是否为0xb7f),如果成功,获取返回值

    (在调用远程函数的时候arm_lr会设为0,这样远程进程在实行要调用的远程函数时就会因为返回地址有问题而暂停,这时候就可以保存他的寄存器上下文地址;返回值可以由arm_r0中获得)
  3. 获取远程进程中的dlopen、dlsym和dlclose地址(根据本进程的相对地址不变),加载链接我们注入模块so文件(其中,传递参数的时候就需要将加载so库的路径和目标函数名写进远程进程内存中,方便远程函数调用)
  • dlopen返回so文件在链接后的内存地址
  • dlsym返回目标函数在内存里的地址
  1. 根据dlsym的返回值,远程callptrace_call远程进程中的目标函数
  2. 恢复最初的上下文寄存器环境
  3. detach远程进程
如何保存寄存器状态的
ptrace(PTRACE_GETREGS, pid, NULL, regs)
在高版本进行ptrace的过程中是否有遇到问题

Xposed原理(源码)

Android Hook框架Xposed原理与源代码分析

通过替换/system/bin/app_process程序控制zygote进程,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。

hook

lnlinehook

  • 基本原理:直接修改需要Hook的位置的指令代码,并让其跳转到桩函数中。在桩函数里会处理寄存器信息,并调用自定义的桩函数。处理完桩函数后,跳转到存有原指令的地方并执行,然后再跳转回原来的函数。

  • 【推测】一般来说是写一个so文件,attach到目标进程,然后远程call我们的这个hook so文件,然后执行lnlinehook。在so文件的入口函数中进行hook操作,然后我们的这个钩子函数也写在那个文件里

//hook点信息
typedef struct tagINLINEHOOKINFO{
    void *pHookAddr;                //hook的地址
    void *pStubShellCodeAddr;            //shellcode stub的地址
    void (*onCallBack)(struct pt_regs *);       //回调函数,跳转过去的函数地址(这边参数不一定只是寄存器的值,见机行事)
    void ** ppOldFuncAddr;             //shellcode 中存放old function的地址
    BYTE szbyBackupOpcodes[OPCODEMAXLEN];    //原来的opcodes
} INLINE_HOOK_INFO;
  1. 先通过静态分析,确定要hook的地址,对INLINE_HOOK_INFO结构体中的pHookAddronCallBack进行初始化,然后对目标地址进行hook(hook的时候要注意区分arm和thumb)

  2. InitArmHookInfo()备份原opcode到szbyBackupOpcodes

  3. 通过BuildStub()构造stub函数,以及回调老函数(这边老函数指的是被hook替换的原opcode)

    在代码里,是借助shellcode来完成这一环的。

    ① 先malloc() 分配空间并使用memcpy()把shellcode写进去,还要更改他为可读可写可执行属性PROT_READ | PROT_WRITE | PROT_EXEC ( mprotect() )

    ② 将shellcode中的_hookstub_function_addr_s这个跳到用户自定义函数的地址进行修改和填充。并且在INLINE_HOOK_INFO结构体中的pStubShellCodeAddr进行内存shellcode的代码地址备份。

  4. 通过 BuildOldFunction() 构造16比特的原opcode的指令块,并将其改为可读可写可执行属性PROT_READ | PROT_WRITE | PROT_EXEC ( mprotect() ) 。

    先将pstInlineHook->szbyBackupOpcodes中的备份原opcode写进指令块(占8个比特),然后再填充跳转指令,要跳转的地址为 pstInlineHook->pHookAddr + 8
    最后填充pstInlineHook->ppOldFuncAddr回调地址。

    填充完跳转地址后要记得用cacheflush进行刷新,防止崩溃

      LDR PC, [PC, #-4]      @ LDR PC, [PC, #-4]对应的机器码为:0xE51FF004
      addr

http://ele7enxxh.com/Android-Arm-Inline-Hook.html

由于tx给的示例没有进行指令修复这个东西,因为如果涉及到跳转指令,pc可能会有变化。指令修复等看了ele7enxxh的分析再填充。

  1. 通过RebuildHookTarget()修改目标hook地址的操作权限,然后将pstInlineHook->pStubShellCodeAddr中的地址作为跳转指令的目标地址进行填充

记得要cacheflush,防止崩溃

MSHookFunction和lnlinehook的区别

lnlinkhook哪儿都能hook,但是MSHookFuction是只能hook函数开头

.got hook,即对导入函数进行hook

https://www.cnblogs.com/mmmmar/p/8228391.html

  • 基本原理:以so文件的导入作为目标进行函数指针替换
  1. 打开要寻找.got表的so文件(就普通的文件读取'rb'

  2. 找到.got表的起始位置和大小(起始位置是相对于模块基址的偏移量)

    ① 在elf header中找到存放所有节的节名的.shstrtab区(我们是在节头表里找的)

    iShstrtabOffset = 
        Elf32_ElfHeader.e_shoff +    //节区偏移
        Elf32_ElfHeader.e_shstrndx * // 节区名字符串表的节区内的偏移(第几个)
        Elf32_ElfHeader.e_shentsize;//节区每个项目的大小
    

    ② 在.shstrtab节里找到.got

    • Elf32_SectionHeader.sh_name中指明了在.shstrtab区里本节名的存储偏移量(是指在.shstrtab中的)
    • Elf32_SectionHeader.sh_type == SHT_PROGBITS
    • 节中的sh_addrsh_size分别存储了节的地址和大小
  3. 获得目标模块基址后,在.got节里根据目标函数的地址,找到存放目标函数的地址(函数地址与函数地址之间+4)。修改地址权限,将新地址写进去。
    (如果是系统库中的函数,我们可以直接获取到函数地址;如果是第三方库中的函数,可以借助dlsym函数,其中可以通过读取/proc/pod/maps目录下相关文件的内容获得目标so文件的绝对路径和加载到内存中的基址信息)

其他

遇见反调试怎么办,反调试对抗

https://blog.csdn.net/feibabeibei_beibei/article/details/60956307?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://blog.csdn.net/feibabeibei_beibei/article/details/88722407

  1. 检测Tracerpid的值
    解决方法:一是以debug模式启动,在JNI_Onload处下断点,找到那个调用方法NOP掉,二是直接静态分析JNI_Onload,直接去掉方法的调用。
  • 如果遇到so文件加密怎么办
    一般在native代码去做检测的话,都是用fopen系统函数打开status文件(/proc/pid/status),然后用fgets函数读取一行的内容,所以可以对这两个函数进行Hook;或者对mmap进行hook
  1. 检测android_server端口号23946 0x5D8A
    解决办法:换那个端口,很简单。
  2. 通过检测父进程里是否有android_server这些关键字以及/data/local/tmp/等文件目录
    解决办法:换个文件名,换个地方放android_server。
  3. 检测在调试状态下的软件断点
    解决办法:这般函数可以在JNI_Onload、com_java_XX这类的函数进行调用,但是对于有经验的逆向者来说,经过几次动态调试就可以找出问题的原因,然后对关键函数进行NOP掉。
  4. 调试时候代码执行时间差异检测

具体看自己下载的”反调试(爱吃菠菜).pdf“

如果代码混淆、字符串加密、函数名不显示,咋办

so相关

so文件的elf结构

结构纵览

在这里插入图片描述
在这里插入图片描述

名字 类型 属性 含义
.rodata SHT_PROGBITS SHF_ALLOC 存放常量数据,只读 ②字符串会被编译器自动放在rodata中,加 const 关键字的常量数据会被放在 rodata 中
.data SHT_PROGBITS (无) 已被初始化为非零的全局变量
.text SHT_PROGBITS 存放代码(如:函数)和部分整数常量(应该指的是一些立即数),这个段是可执行的。
.bss SHT_NOBITS SHF_ALLOC SHF_WRITE 未初始化的全局变量。根据定义,当程序开始执行,系统将把这些数据初始化为 0。此节区不占用文件空间。
.got SHT_PROGBITS 全局偏移表
.shstrtab SHT_STRTAB 节区名称,段表字符串表(Section Header String Table),针对段表
.symtab SHT_SYMTAB 符号表,一般是变量、函数。
.strtab SHT_STRTAB 字符串表(STRING TABLE)
.dynamic SHT_DYNAMIC 存放每个函数的结构描述。此节区包含动态链接信息。节区的属性将包含 SHF_ALLOC 位。是否 SHF_WRITE 位被设置取决于处理器。
.dynstr SHT_STRTAB SHF_ALLOC 保存每个函数的名称以及so中的所有符号名称。此节区包含用于动态链接的字符串,大多数情况下这些字符串代表了与符号表项相关的名称。
.data1 SHT_PROGBITS SHF_ALLOC SHF_WRITE 这些节区包含初始化了的数据,将出现在程序的内存映像中。
.debug SHT_PROGBITS (无) 此节区包含用于符号调试的信息。
.dynsym SHT_DYNSYM SHF_ALLOC 此节区包含了动态链接符号表。
.fini SHT_PROGBITS SHF_ALLOCSHF_EXECINSTR 此节区包含了可执行的指令,是进程终止代码的一部分。程序正常退出时,系统将安排执行这里的代码。
.hash SHT_HASH SHF_ALLOC 此节区包含了一个符号哈希表.
.init SHT_PROGBITS SHF_ALLOCSHF_EXECINSTR 此节区包含了可执行指令,是进程初始化代码的一部分。当程序开始执行时,系统要在SHF_EXECINSTR 开始调用主程序入口之前(通常指 C 语言的 main 函数)执行这些代码。
.interp SHT_PROGBITS 此节区包含程序解释器的路径名。如果程序包含一个可加载的段,段中包含此节区,那么节区的属性将包含 SHF_ALLOC 位,否则该位为 0。
.line SHT_PROGBITS 此节区包含符号调试的行号信息,其中描述了源程序与机器指令之间的对应关系。其内容是未定义的。
.note SHT_NOTE 此节区中包含注释信息,有独立的格式。
.plt SHT_PROGBITS 此节区包含过程链接表(procedure linkage table)。
.relname SHT_REL 这些节区中包含了重定位信息。如果文件中包含可加载的段,段中有重定位内容,节区的属性将包含 SHF_ALLOC 位,否则该位 置 0。传统上 name 根据重定位所适用的节区给定。例如 .text 节区的重定位节区名字将是:.rel.text 或者 .rela.text。
.rela name SHT_RELA
.rodata1 SHT_PROGBITS SHF_ALLOC

.rodata是干啥的

存放常量数据,只读

.bss是干啥的

未初始化的全局变量

so加载原理

  1. 使用System.loadLibrary方法,加载jniLibs目录下的so 文件, 加载libhello.so 文件
System.loadLibrary("hello");

2)使用System.load(), 加载任意路径下的so 文件,它需要一个参数,这个参数就是so文件所在的完整路径。 可以将so 放到服务器上,App 下载so 到手机后,再加载so, 这就是so的动态加载技术。

加固

常见加固

壳史

https://www.jb51.net/article/133834.htm

  • 第一代壳 Dex加密

Dex字符串加密
资源加密
对抗反编译
反调试
自定义DexClassLoader

脱壳方法
内存Dump法
文件监视法
Hook法
定制系统
动态调试法

  • 第二代壳 Dex抽取与So加固

对抗第一代壳常见的脱壳法
Dex Method代码抽取到外部(通常企业版)
Dex动态加载
So加密

脱壳方法
内存重组法
Hook法
动态调试
定制系统
静态脱壳机

  • 第三代壳 Dex动态解密与So混淆

Dex Method代码动态解密
So代码膨胀混淆
对抗之前出现的所有脱壳法

脱壳方法
dex2oat法
定制系统

  • 第四代壳 arm vmp(未来)

vmp

常见SO加固

https://blog.csdn.net/jltxgcy/article/details/52205210

DEX加载原理

Android中dex文件的加载与优化流程

https://www.jianshu.com/p/b89d0b03e82c
Dex优化验证

Dex的类加载

其他

断点原理

  • 实现原理,就是在指定的位置插入断点指令,当被调试的程序运行到断点的时候,产生SIGTRAP信号。该信号被gdb捕获并进行断点命中判定,当调试工具判断出这次SIGTRAP是断点命中之后就会转入等待用户输入进行下一步处理,否则继续。
  • 设置原理: 在程序中设置断点,就是先将该位置的原来的指令保存,然后向该位置写入int 3。当执行到int 3的时候,发生软中断,内核会给子进程发出SIGTRAP信号,当然这个信号会被转发给父进程。然后用保存的指令替换int3,等待恢复运行。
  • 断点命中判定:gdb把所有的断点位置都存放在一个链表中,命中判定即把被调试程序当前停止的位置和链表中的断点位置进行比较,看是断点产生的信号,还是无关信号。
发布了4 篇原创文章 · 获赞 0 · 访问量 98

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览