Android逆向-Android基础逆向9(so相关学习干货)

ARM 82浏览


0X00 前言

导航

博客导航戳这里
练习资源戳这里

IDA 是反汇编工具,Android Killer 是反编译工具

内容

1.ARM基础知识
2.so文件基础知识

0x01 ARM基础知识

SO Helper工具 介绍

这里写图片描述
可以直接对ARM汇编进行直接修改
这里写图片描述

ARM介绍

ARM是ARM公司的32位处理器。ARM汇编指令的机器码就是32位。

ARM的作用

学会了ARM,就会了主流的嵌入式开发。
还可以进行硬件编程,可以从事机器人或机械制造。

ARM 和Thumb指令

Thumb 是16位的ARM汇编。
如同样的beq,bne这两个汇编指令,用ARM的4个HEX数表示时,
其HEX值为0A,1A,而当用2个HEX数表示时,其HEX值为D0,D1。
在动态调试的时候,IDA无法分辨ARM和Thumb指令。所以需要人工去进行纠正和调整。

ARM的机器码

ARM机器码的一般格式。
举一个实例:
movne r2,r1
0001 00 0 1101 0 0000 0010 000000000001
这里写图片描述

这里写图片描述

首先来说说 31-28字段,cond是条件码,就是表明这条语句里是否有大于、等于、非零等的条件判断。
这里写图片描述

再来一张图片

这里写图片描述

这张图片就能更好的说明了。

指令与条件码可以有多重组合,比如MOV指令可以有MOVEQ、MOBLT等多种形式。

27-26位为保留位,恒为00

25位,shifter_operand段存放的是立即数还是寄存器,若为寄存器则为0,如果是立即数则为1

24-21 位为opcode

这里写图片描述

20位:表明指令是否会影响程序状态寄存器,如果是就是1,否则为0。

19-16位,表示第一个源操作数寄存器。

11-0 目的寄存器

ARM寄存器

R0-R7: 通用寄存器
R8-R10:不常用的通用寄存器
R11:基质寄存器(FP)
R12:暂时寄存器(IP)
R13:堆栈制作(SP)
R14:链接寄存器(LR)
CPSR:状态寄存器

ARM指令集

B 无条件跳转
BL 带链接的无条件跳转
BLX 带状态的无条件跳转

BNE 不相等跳转
BEQ 相等跳转

寄存器交互指令

LDR 从存储器中加载数据到寄存器。
LDR R1,[R2],把R2指向的位置的数据给R1

STR:把寄存器的数据存储到存储器
STR R1,[R2],在R2指向的地址,存储R1

LDM :将存储器的数据加载到一个寄存器列表。
LDM R0,{R1,R2,R3},把R0中的数据一次加载到R1,R2,R3

SDM: 将一个寄存器列表的数据存储到指定的存储器
SDM R0,{R1,R2,R3},把R1,R2,R3加载到R0单元

PUSH:入栈
POP:出栈

数据传送指令

MOV 将立即数或寄存器的数据传送到目标寄存器

数据算数运算指令

ADD : 加法
(Addition)
ADD{条件}{S} , , dest = op_1 + op_2
ADD 将把两个操作数加起来,把结果放置到目的寄存器中。操作数 1 是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即值:
ADD R0, R1, R2 ; R0 = R1 + R2 ADD R0, R1, #256 ; R0 = R1 + 256 ADD R0, R2, R3,LSL#1 ; R0 = R2 + (R3 << 1)
加法可以在有符号和无符号数上进行。
ps:带进位的加法ADC

SUB : 减法
(Subtraction)
SUB{条件}{S} , , dest = op_1 - op_2
SUB 用操作数 one 减去操作数 two,把结果放置到目的寄存器中。操作数 1 是一个寄存器,操作数 2 可以是一个寄存器,被移位的寄存器,或一个立即值:
SUB R0, R1, R2 ; R0 = R1 - R2 SUB R0, R1, #256 ; R0 = R1 - 256 SUB R0, R2, R3,LSL#1 ; R0 = R2 - (R3 << 1)
减法可以在有符号和无符号数上进行。
ps:带进位的减法SBC

MUL : 乘法
这两个指令与普通算术指令在对操作数的限制上有所不同:
给出的所有操作数、和目的寄存器必须为简单的寄存器。
你不能对操作数 2 使用立即值或被移位的寄存器。
目的寄存器和操作数 1 必须是不同的寄存器。
最后,你不能指定 R15 为目的寄存器
(Multiplication)
ps:带累加的乘法MLA
MUL{条件}{S} , , dest = op_1 * op_2
MUL 提供 32 位整数乘法。如果操作数是有符号的,可以假定结果也是有符号的。

DIV除法
SDIV 带符号除法
UDIV 不带符号位除法

数据逻辑运算指令

与:AND
或:ORR
异或:EOR

LSL:逻辑左移
LSR:逻辑右移

比较指令

CMP:比较指令

其他指令

SWT:切换用户模式
伪指令:DCB

寻址方式

恩,其实和8086挺像的

7种,但是是分享常用的

立即寻址:MOV R0,#1234 R0=0X1234
寄存器寻址:MOV R0,R1 R0=R1
寄存器移位寻址:MOV R0,R1,LSL #2 R0=R1*4
寄存器间接寻址:LDR R0,[R1] 将R1寄存器中的值作为地址,取出地址中的值赋予R0
寄存器间接基址偏移寻址:LDR R0,[R1,#-4]将R1寄存器的值-0x4的值作为地址,取出地址中的值给R0

汇编难以分析的原因

1.IDA自身的缺陷
2.函数库与类有时无法识别
3.自身对ARM汇编的熟练度

0x02 so简要说明

和Linux的关系

.so文件实际上是Linux文件里的动态链接库。

还有一些其他文件,比如说静态可执行文件,,没有后缀名。

如果在linux下编写用过gcc文件。那么就要生成可执行文件。

so文件的来源

so文件主要是通过NDK编程来源。因为是程序猿,所有通过Android Studio就可以编译了。具体请看之前的文章。

so文件的文件格式

so文件的文件格式是ELF文件。有自己的文件格式。一般的文件都分为静态下的状态和动态下的状态,当然之后要对ELF文件进行一个分析,之前姜大佬分析过了,不过这个是人家分析的,只有自己过一遍才能对ELF进行一个更深的了解。当然这个会在10中进行说明。

readelf指令

readelf命令用来显示一个或者多个elf格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。这里的elf-file(s)就表示那些被检查的文件。可以支持32位,64位的elf格式文件,也支持包含elf文件的文档(这里一般指的是使用ar命令将一些elf文件打包之后生成的示例lib*.a之类的“静态库”文件)。

部分指令:

-a
--all 显示全部信息,等价于 -h -l -S -s -r -d -V -A -I.

-h
--file-header 显示elf文件开始的文件头信息.

-l
--program-headers
--segments 显示程序头(段头)信息(如果有的话)。

-S
--section-headers
--sections 显示节头信息(如果有的话)。

-s
--syms
--symbols 显示符号表段中的项(如果有的话)。

-d
--dynamic 显示动态段的信息。

-V
--version-info 显示版本段的信息。

-A
--arch-specific 显示CPU构架信息。

-I
--histogram 显示符号的时候,显示bucket list长度的柱状图。 

-a演示:

这里写图片描述

-h演示

这里写图片描述

-l演示

这里写图片描述

其他的自己回去玩

objdump指令

objdump命令是用查看目标文件或者可执行的目标文件的构成的gcc工具。

一些常用命令

-a
显示档案库的成员信息,类似ls -l将lib*.a的信息列出。

-V
--version
版本信息

--debugging
-g
显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。有些其他的格式被readelf -w支持。

--disassemble
-d
从objfile中反汇编那些特定指令机器码的section。

-f
--file-headers
显示objfile中每个文件的整体头部摘要信息。

-h
--section-headers
--headers
显示目标文件各个section的头部摘要信息。

-H
--help
简短的帮助信息。 

-V演示

这里写图片描述

windows下的Linux命令

开虚拟是可以的,但是呢却非常占用内容,那么我们想在Windows下使用Linux的命令应该怎么办呢。

使用cygwin即可。安装或者其他,自行Google or 百度。

当然还有很多其他的方法。

以上