android 软件安全与逆向分析(非虫)读书笔记

前言

这边没有目录 我们只以页码为读书标记
着实 读书是比较系统全面的学习方法,比如学习路线规划,弥补自己在哪些知识点的不足,从而针对性的学习。而不是盲目的CSDN搜索一篇,CSDN固然有全面的文章,有的部分也许只是点到为止,相信大家平时都有些CSDN的习惯,我们常常把他当做笔记来使用,以便以后翻阅,很难做到像教科书那样的全面讲解。
废话结束了。。。。入正题。
下载链接 https://pan.baidu.com/s/1bTO4HuZwOcCchs_esp2PPg

Dalvik基础 指令

文中42页详细介绍了V P两种命名法如下

v p命名法

文中43页详细介绍了Smali中的基本数据类型

基本数据类型

而且这里介绍了三个知识点
  • 关于八个字节的也就是J 和 D 使用的是两个连续的寄存器.
  • L类型为任意类有人就是表示对象类型。
  • [表示数组类型 二多为数组最大维数为255.
文中45页 详细介绍了Dalvik指令的特点
  • 参数采用了destination =》Source的方式
    添加后缀 32位无后缀 64位加-wide 特殊字符如-boolean -byte -class - void 等后缀
  • 每个字母为4位 也就是半个字节
  • VAA为目的寄存器 VBBBB为源寄存器 也就是A在B在后
  • 每个寄存器都是32位 只是取值范围不同
  • move/form16 源寄存器为16位
  • move/16 目的寄存器为16位
    -move-result 将上一个方法的返回值取出赋值给目的寄存器
关于数据指定指令
  • const/hight16 VAA,#+BBBB0000 将源寄存器扩展成32也就是8个0后赋值给VAA.
    const/wide 统一将源寄存器扩展为64位
  • const/wide/hight16 上面的基础上右边加0到16个0。
  • const-string/vAA,string@BBBB 字符串索引并恢复
锁指令
  • monitor-enter-vAA 取锁
  • monitor-exit-vAA 释放锁
书中47页介绍到一个比较重要的 实例操作指令
  • check-cast vAA type@BBBB 将vAA转换成指定类型 可能报出ClassCastException.
  • instance-of vA,vB,type@BBBB 判断vB是否为指定类型,是则vA为1否则为0.
  • new-instance vAA,type@BBBB 构造指定类型的实例赋值给vAA,type不能指定数组类型
数组操作指令
  • array-lenght vA,vB 将数组vB的条目个数赋给vA.
  • array-lenght vA,vB,type@CCCC 前面的基础上添加类型指的都是vB.
  • filled-new-array{vC,vD,vE,vF,vG},type@BBBB 在前面的基础上 其中vA是隐含的负责指定大小和类型.
  • filled-new-array/range{vCCCC..vNNNN},type@BBBB 和前面不同的是range指定了范围.N=A+C-1。
  • fill-array-data vAA,+BBBBBBBB填充指定数组可参考
  • https://blog.csdn.net/qq_20330595/article/details/80995424对数组的操作做了篇总结
跳转指令
  • packde-siwtch vAA,+BBBBBBB 有规则跳转
  • pacrse-siwtch vAA,+BBBBBBB 无规则跳转
关于IF比较指令可以参考我的博客
  • compl-float vAA,vBB vCC 比较B和C > = < 对应-1,0,1
  • compg-float vAA,vBB vCC 比较B和C > = < 对应 1,0,-1
  • compl-double vAA,vBB vCC 比较B和C > = < 对应-1,0,1
  • compg-double vAA,vBB vCC 比较B和C > = < 对应 1,0,-1
  • compg-long vAA,vBB vCC 比较B和C > = < 对应 1,0,-1
字段操作指令
  • 普通字段用i开头 取用iget 赋值用iput
  • 静态使用s开头 取用sget 赋值用sput
文中50页使用到比较重要的方法调用指令
  • invoke-virtual 虚方法
  • invoke-direct 实例方法
  • invoke-super 父类方法
  • invoke-static 实例的静态方法
  • invpoke-interface 实例的的接口方法
数据转化指令

最后文中51页介绍的数据运算指令如下

直接参考这里即可 https://blog.csdn.net/qq_20330595/article/details/80995424

文中54页测试运行
应smali将写好的smali文件转成dex
java -jar smali.jar -o Test.dex Test.smali
使用dex2jar 将Test.dex转成Java的jar包
使用dx将java的Test.jar转成可以dalvikvm虚拟机优化过的target.jar包
dx --dex --output=target.jar origin.jar
//将文件推到手机的指定路径下
adb push D:\下载\J2S2J1.3\work\target.jar /data/local
执行 dalvikvm虚拟机调试命令
adb shell dalvikvm -cp /data/local/target.jar Test

注意两点
- 海马模拟器 的文件上传后后缀有问题
- 需要使用dx工具 将java的jar优化成dalvikvm的jar包。

dx路径参考https://blog.csdn.net/u012398902/article/details/50476580/

我们来梳理一下这些工具的使用流程
//Java2Smali
java <==javac==> class <==apkDB工具(放入新建文件夹Test中)==> jar(java) <==dx.bat==> jar(dalvikvm)
//相比上面这种更快
java <==javac==> class <==dx==> jar(dalvikvm)<==apkDB工具反编译==> jar文件夹<==apkDB工具E回编译==>Smali
//Smali2Java
Smali <==smali.jar==> dex <==baksmali-2.1.3.jar==> dex <==dex2jar==> jar <==jdg_ui==> java

以上涉及到的命令

dx --dex --output=
java -jar smali-2.1.3.jar smali/ -o classes.dex
d2j-dex2jar smaclasses.dex
java -jar smali.jar -o Test.dex Test.smali
java -jar baksmali-2.1.3.jar -o [输出文件夹] dex文件 

APK的文件结构

书中58页详细介绍了一个APK生成过程
书中59页详细介绍了APK安装的4个方式
文中68页介绍如何判断Dex 这个比较复杂
文中69页开始分析dex的具体结构
  • magic 标示dex有效性 值为64 65 78 0a 30 33 35 00 字符串表示为dex.035
  • checksum 校验文件是否损坏篡改
  • fileSize 包含整个dex文件大小 大小0x70
  • endianTag 修饰CPU的字节序
  • 分索引结构区和数据区
文中71页作者将全面讲解dex的文件结构

这公司googl官方文档 不过需要科学上网
https://source.android.com/devices/tech/dalvik/dex-format

文中78页-这8页我们看的一脸懵逼,工欲善其事必先利其器,借助dexdump命令可以更好地帮助我们了解学习dex文件结构。

https://blog.csdn.net/u012889434/article/details/51848749

这边我们动手玩一波
首先连接夜神模拟器
adb connect 127.0.0.1:62001
推送target.jar 文件 
adb -s 127.0.0.1:62001 push D:\下载\J2S2J1.3\work\Test.dex /data/local
获取target.jar的dex头部信息
adb -s 127.0.0.1:62001 shell dexdump -f /data/local/target.jar
将信息导出到指定文件夹下(Windows下)
adb -s 127.0.0.1:62001 shell dexdump -f /data/local/target.jar >> D:\target\target.txt

PS:
- 1.夜神模拟器端口为 62001 且上传的文件后缀不完整。
- 2.adb -s 指定单一设备
- 3.>>D:… 这种方式有时候会找不到文件,需手动创建文件夹

Opened '/data/local/target.jar', DEX version '035'
DEX file header:
magic               : 'dex\n035\0'
checksum            : 7cbf5a00
signature           : 8fc2...8228
file_size           : 696
header_size         : 112
link_size           : 0
link_off            : 0 (0x000000)
string_ids_size     : 13
string_ids_off      : 112 (0x000070)
type_ids_size       : 7
type_ids_off        : 164 (0x0000a4)
proto_ids_size       : 3
proto_ids_off        : 192 (0x0000c0)
field_ids_size      : 1
field_ids_off       : 228 (0x0000e4)
method_ids_size     : 4
method_ids_off      : 236 (0x0000ec)
class_defs_size     : 1
class_defs_off      : 268 (0x00010c)
data_size           : 396
data_off            : 300 (0x00012c)

Class #0            -
  Class descriptor  : 'LTest;'
  Access flags      : 0x0001 (PUBLIC)
  Superclass        : 'Ljava/lang/Object;'
  Interfaces        -
  Static fields     -
  Instance fields   -
  Direct methods    -
    #0              : (in LTest;)
      name          : '<init>'
      type          : '()V'
      access        : 0x10001 (PUBLIC CONSTRUCTOR)
      code          -
      registers     : 1
      ins           : 1
      outs          : 1
      insns size    : 4 16-bit code units
      catches       : (none)
      positions     :
      locals        :
    #1              : (in LTest;)
      name          : 'main'
      type          : '([Ljava/lang/String;)V'
      access        : 0x0009 (PUBLIC STATIC)
      code          -
      registers     : 3
      ins           : 1
      outs          : 2
      insns size    : 8 16-bit code units
      catches       : (none)
      positions     :
      locals        :
  Virtual methods   -
  source_file_idx   : -1 (unknown)
在下确实忘记了偏移的是啥了(一般地址都是16进制的吧?)

https://blog.csdn.net/qq_35874273/article/details/78524323

物理地址=段地址*16(10进制)+偏移地址。(2进制的话,也就是段地址<<4 | 偏移地址)。
当你分析起来很吃力的时候是因为你忘记了另一个工具010Editor

https://blog.csdn.net/sinat_18268881/article/details/55832757

010Editor不仅自动寻找偏移地址 还能显示数据结构 这个是很多博主没有告诉大家的,也是初学者的隐痛

上面的大仙是郭霖推荐的深度好文
经分析 我的helloworld 偏移地址为
0C 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 00

OC为偏移起始位置 大小为12 表示有十二个字符
最后一位为00 表示字符串结尾 没有加入计算。
关于类型序号 貌似是临时排序的

Ps 左边是[角标 10进制的哦

struct type_id_list 7 types
2 Test
3 java.io.PrintStream
4 java.lang.Object
5 java.lang.String
6 java.lang.System
8 void
A java.lang.String[]
struct string_id_list 14strings
0
1 Hello World!
2 LTest;
3 java.io.PrintStream;
4 java.lang.Object;
5 java.lang.String;
6 java.lang.System;
8 V(void;)
9 VL
7 Test.java
A [Ljava/lang/String;
B main
C out
D println
protoIdsSize和protoIdsOff了,它们代表的是dex文件中方法原型的个数和位置偏移

这个不简单 下面看下数据结构可知共介绍了三个参数
shortyIdx 方法内总共的成员变量数目
returnTypeIdx 返回类型
parametersOff 参数列表 记录最终参数typeIdx的偏移地址

struct DexProtoId{
    u4 shortyIdx;           /*指向DexStringId列表的索引*/
    u4 returnTypeIdx;       /*指向DexTypeId列表的索引*/
    u4 parametersOff;       /*指向DexTypeList的位置偏移*/
}
struct DexTypeList{
    u4 size;        /*DexTypeItem的个数*/
    DexTypeItem list[1];    /*DexTypeItem结构*/
}

struct DexTypeItem{
    u2 typeIdx;             /*指向DexTypeId列表的索引*/
}
struct proto_id_list 3 prototypes
0 void()
1 void (java.lang.String)
2 void (java.lang.String[])
书中78页做了详细介绍

最后一个struct class_def_item_list是正真的代码部分 可直接生成smali代码 书中80页曾指出在Android4.0系统源码目录下,dalvik/docs/instruction-formats.html 可查找35c相关指令

mapoff 指明DexMapitem结构文件偏移

其中kDexType是个枚举类型
/* map item type codes */
enum {
    kDexTypeHeaderItem               = 0x0000,
    kDexTypeStringIdItem             = 0x0001,
    kDexTypeTypeIdItem               = 0x0002,
    kDexTypeProtoIdItem              = 0x0003,
    kDexTypeFieldIdItem              = 0x0004,
    kDexTypeMethodIdItem             = 0x0005,
    kDexTypeClassDefItem             = 0x0006,
    kDexTypeMapList                  = 0x1000,
    kDexTypeTypeList                 = 0x1001,
    kDexTypeAnnotationSetRefList     = 0x1002,
    kDexTypeAnnotationSetItem        = 0x1003,
    kDexTypeClassDataItem            = 0x2000,
    kDexTypeCodeItem                 = 0x2001,
    kDexTypeStringDataItem           = 0x2002,
    kDexTypeDebugInfoItem            = 0x2003,
    kDexTypeAnnotationItem           = 0x2004,
    kDexTypeEncodedArrayItem         = 0x2005,
    kDexTypeAnnotationsDirectoryItem = 0x2006,
};

那么现在 我们大概掌握了DEX的基本结构以及分析方法和工具

Odex文件格式

  • odex是优化后的dex文件。
  • 避免每次从apk中提取dex。
  • 两种获取odex方法
    1. Android Rom 系统程序
    2. dalvik-cache 缓存文件
文中81页又一个厉害的工具来了dexopt-wrapper

来我们跟着作者玩一遍

还是照文章看看操作先

这边操作失败了 大概是模拟器的问题 以后有空再看吧

书中91页介绍了IDA pro 和C32asm 以及作者自己开发的DexFixer工具

注意以下几点
- 使用IDA pro 打开的是smali文件 需要有smali基础
- checksum这个需要重新计算

- MATE-INF手动删除,需要自己手动打包

文中115页介绍了Smali中switch的使用

关于Smali的静态语言分析可参考我的博客 smali语言入门 操作流

我们自己练习一下smali

public class Test {
    public  String name;
    public  void main(String[] args) {
        Test test = new Test();
        int i  = 1;
        switch(i){
            case 0:
                System.out.println("Hello World!");
                break;
            case 1:
                test.name = "maqi1";
                break;
            case 2:
                test.name = "maqi2";
                break;
        }

    }
}
    .line 4
    new-instance v0, LTest;

    invoke-direct {v0}, LTest;-><init>()V

    .line 5
    const/4 v1, 0x1

    .line 6
    packed-switch v1, :pswitch_data_1c

    .line 18
    :goto_9
    return-void

    .line 8
    :pswitch_a
    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v1, "Hello World!"

    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    goto :goto_9

    .line 11
    :pswitch_12
    const-string v1, "maqi1"

    iput-object v1, v0, LTest;->name:Ljava/lang/String;

    goto :goto_9

    .line 14
    :pswitch_17
    const-string v1, "maqi2"

    iput-object v1, v0, LTest;->name:Ljava/lang/String;

    goto :goto_9

    .line 6
    :pswitch_data_1c
    .packed-switch 0x0
        :pswitch_a
        :pswitch_12
        :pswitch_17
    .end packed-switch

注意以下几点
- goto :goto_9 这个想当于break;
- packed-switch v1, :pswitch_data_1c
这里packed-switch 是switch开头 v1 为 i pswitch_data_1c规定范围
- :goto_9 即为switch的统一返回地点 然后代码才往下继续执行 比如 我加了String mName=test.name; 将会在:goto_9下面iget-object v0, v0, LTest;->name:Ljava/lang/String;

书中117页对switch做了dex分析
try-catch
  try{
        Test test = new Test();
        }catch(Exception e ){
         e.getMessage();
    }
  • 这里不用看就知道了吧?
  • :try_start_0 try开始
  • :try_end_5 try结束
    .line 5
    :try_start_0
    new-instance v0, LTest;

    invoke-direct {v0}, LTest;-><init>()V
    :try_end_5
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_5} :catch_6

    .line 10
    :goto_5
    return-void

    .line 6
    :catch_6
    move-exception v0

    goto :goto_5

    .line 7
    invoke-virtual {v0}, Ljava/lang/Exception;->getMessage()Ljava/lang/String;

IDA Pro 的使用 127页 着实难以理解

第六章 - 基于Android的ARM汇编语言-老朋友了

这里已经涉及到了汇编语言,所以想继续学习下去就必须先学习汇编语言。
这里分享我的 读书笔记-汇编语言第三版(王爽)给大家,非常全面,可供平时查阅。
ARM架构
- 根据书中所说ARM android从2.2开始支持ARMV4 直到现在的ARMV7-a
- 使用ARM处理器的Android的手机最终生成ARM elf 可执行文件
- ARM核心代码会生成ELF文件 对于ELF的静动态分析都需要具备相应的ARM汇编基础。

书中160页讲解制作一个helloARM

这边的ARM和8086不同 又是新的指令 不过触类旁通

作者写了一行汇编代码 为了试下效果 我专门下了个MASM64
链接:https://pan.baidu.com/s/1C7aSw7zY6bjYY-M3sABi5A 密码:8liz
作者的helloworld
Export main                 ;表明main函数是被程序导出的
main                        ;函数名称
var_C = -0xC            
bar_8 = -8                  ;栈变量
STMFD  = SP!,{R11,LR}       ;堆栈寻址指令  入栈
ADD R11,SP,#4               ;(R11)=(SP)-4
SUB SP,SP,#8                ;SUB:减法指令 SP:堆栈指令 (SP) = (SP)-8 实际作用:分配8个字节的空间
STR R0,{R11,#var_8}         ;写存储器访问指令(内存地址) R0保存到var_8
STR R1,{R11,#var_C}         ;R1保存到var_C
LDR R3,={aHelloArm - 0x830} ;读存储器访问指令(内存地址)
ADD R3,PC,R3                ;"Hello ARM!"
MOV R0,R3                   ;s
BL puts                     ;带链接的跳转指令  调用子程序功能,puts输入输出print
MOV R3,#0
MOV R0,R3
SUB SP,R11,#4
LDMFD SP!,{R11,PC}          ;堆栈寻址指令  出栈

c语言到到可执行exe的过程
- 编译-汇编-链接 而汇编只需要两步

ARM汇编基础

中断模式
  • 用户模式(usr)默认
  • 快速中断模式(fiq)
  • 外部中断模式(irq)
  • 管理模式(svc)
  • 数据访问中自己模式(abt)
  • 系统模式
  • 未定义指令中止模式(und)
用户模式(usr)
  • 寄存器:R0~R7
  • 分组寄存器:R8~R14
  • 程序计数器R15(PC)
  • 当前程序状态寄存器(CPSR)
ARM的两种工状态
  • Thumb状态: 16位对其的Thumb指令 FP对应R11 IP对应R12 SP对应R13 LR对应R14 PC对应R15
  • ARM状态: 32位对其的ARM指令
ARM语言结构程序结构 书中166页 对于这个文档我内心是拒绝的

Android平台使用GNU ARM汇编格式,使用GAS(GNU Assembler)汇编器
手册地址 http://sourceware.org/binutils/docs/as/index.html

书中167 使用Android NDK的GCC编译helloWord.c
  • gcc -E hello.c -o hello.i
  • gcc hello.o -o hello
  • Android NDK 支持使用ARM汇编语言辨析的.s结尾的文件作为程序的源文件。
ARM汇编程序构成 书中166页
  • 处理器架构定义
  • 数据段
  • 代码段
  • main函数
处理器架构定义
  • .arch 指定ARM处理器架构
  • .fpu 指定协议处理器类型
  • .eabi_attribute 指定接口属性 接口规范
段定义 汇编语言我们学过,就是一块连续的内存区域
  • .section name[,”flags”[,%type[,flag_specific_arguments]]]
  • name:段名
  • flags:读,写,可执行
  • type:段类型 probits:段中包含数据 note:包含的数据非程序本身 flag_specific_arguments:指定平台相关参数
  • .section .rodata :定义制度数据段 属性采用默认
  • .text 定义代码段 没有使用.section关键字
  • .section .note.GUN-stack,%progbits 定义.note.GUN-stack段 禁止生成可执行堆栈
汇编指令

有人做了总结 直接当工具查看即可

https://blog.csdn.net/zqixiao_09/article/details/50726544

https://www.cnblogs.com/uestcbutcher/p/7244799.html

子程序和参数传递 这个和汇编语言不大一样
.global  函数名
.type    函数名,%function
函数名:
    <...函数体...>
.global MyAdd
.type   MyAdd,%function
MyAdd:
        ADD r0,r0,r1 ;(r0) = (r0)+ ((r0)-(r1))
        MOV pc,1r  @函数返回
  • R0~R4 传递参数 超出参数用堆栈传递
  • R0用来存放返回值
  • 函数返回前面无须回复寄存器内容
ARM处理器寻址方式
  • 立即寻址 MOV R0,#1234 #表16位
  • 寄存器寻址 MOV R0,R1
  • 寄存器移位寻址
  • 寄存器间接寻址 LDR R0,[R1] :(R0) = ([R1])
  • 基址寻址 LDR R0,[R1,#-4]:(R0) = ((R1)-4)
  • 多寄存器寻址 LDMIA R0,{R1,R2,R3,R4} IA表示R0自增1个字,R1=[R0],R2=[R0+#4],R3=[R0+#8],R4=[R0+#12]
  • 堆栈寻址 LDM和STM为指令前缀,表示对寄存器寻址,FA,EA,FD,ED为指令后缀 集齐出入栈
  • 块拷贝寻址 LDM和STM为指令前缀,表示对寄存器寻址,FA,EA,FD,ED为指令后缀 集齐出入内存
  • 相对寻址 BL跳转指令 程序计数器PC为当前值基地址
寄存器移位寻址

LSL:逻辑左移 空出低位补0,MOV R0,R1,LSL #2 :R1左移2位后赋值给R0寄存器,执行R0 = R1*4 这个不应该是16进制的吗?居然是二进制左移
LSR:逻辑右移 空出高位补0
ASR:算数右移 符号位不变
ROR:循环右移,低位填入高位
RRX:带扩展的循环右移 右移一位 高位用C标志的值填充

指令格条件码

https://note.youdao.com/yws/public/resource/0233563604a0fc2481623f08edfdf5a6/xmlnote/02E263A1964A4843B8B2F76BA94F0ED1/16604

跳转指令
  • B{cond}lable :满足条件cond 跳转到lable
  • BL{cond}lable:满足条件cond 将下一条指令的地址赋给R14接LR,子程序返回使用MOV PC,LR
  • BX带状态切换的跳转指令:BX{cond}Rm :满足条件cond 判断Rm为[0]位1则将CPSR标志T置位(反之复位) 将目标地址代码解释为Thumb代码
  • BLX 带链接和状态切换的条件指令 集合了BL和BX的功能
存储器访问指令(内存访问指令)
  • LDR{type}{cond}Rd,labl
  • LDRD{type}{cond}Rd,Rd2,labl 加载双子数据 存入 Rd 和R2中
  • STR{type}{cond}Rd,label 存储数据到指定地址存储单元中
  • LDM{addr_mode}{cond}Rn{!}reglist 读取内存读取多个数据到寄存器列表 i为可选后缀表示最终地址写到Rn寄存器中,reglist表示寄存器集合
  • STN{addr_mode}{conde}Rn{!}reglist 读取寄存器列表到寄存器
  • PUSH{cond}reglist 寄存器推入堆栈
  • POP{cond}reglist 寄存器列表弹出堆栈
  • SWP{B}{cond}Rd,Rm,[Rn] 寄存器和存储器交换数据 B:交换字节否则32位字 Rm:写入到存储器的寄存器 Rd:加载数据的寄存器
SWPB R1,R2,[R0] 
B表示一个字节 高24位清空
(R1) = ([R0]) 
([R0]) = (R2)
type 含义
B 无符号字节(零扩展成32位)
SB 有符号字节(符号扩展成32位)
H 无符号字节(零扩展成32位)
SH 有符号字节(符号展成32位)

lable读取内存地址

label1 含义
直接偏移量 LDR R8,[R9,#04]
寄存器偏移 LDR R8,[R9,R10,#04]
相对PC LDR R8,label1
addr_mode 含义
IA Increase After 基址寄存器执行后 增加
IB Increase Before 基址寄存器执行前 增加
DA Decrease After 基址寄存器执行后 减少
DB Decrease After 基址寄存器执行前 减少
FD 堆栈向低地址生长 SP–
FA 堆栈向高地址生长 SP++
ED 堆栈向低地址生长
EA 堆栈向高地址生长
数据处理指令
指令 含义 模板 注意
MOV 传送指令 MOV{cond}{s}Rd,operand2 将8位立即数或寄存器内容赋给目标寄存器
MVN 数据非传送指令 MVN{cond}{s}Rd,operand2 将8位立即数或寄存器内容取反赋给目标寄存器
ADD 加法指令 ADD{cond}{s}Rd,Rn,operand2 将Rn和operand2的值相加存入Rd中
ADC 带进位的加法指令 ADC{cond}{s}Rd,Rn,operand2 将Rn和operand2的值相加z再加上CPSR(汇编的FLAG)寄存器的C条件标志位的值最后存入Rd中
SUB 减法指令 SUB{cond}{s}Rd,Rn,operand2 (Rd) = (Rn)-(operand2) 寄存器相减影响标志位
RSB 逆向减法 RSB{cond}{s}Rd,Rn,operand2 (Rd) = (operand2)-(Rn)
SBC 带进位的减法指令 SBC{cond}{s}Rd,Rn,operand2 (Rd) = (Rn)-(operand2)-C
RSC 带进位的逆向减法 RSC{cond}{s}Rd,Rn,operand2 (Rd) = (Rn)-(operand2)-C
MUL 乘法指令 MUL{cond}{S}Rd,Rm,Rn 32位乘法 (Rd) = (Rm)*(Rn) S影响CPSR的N和X
MLS 先乘后减指令 MLS{cond}{S}Rd,Rm,Rn,Ra (Rd) =(Ra)- (Rm)*(Rn)
UMULL 先乘(当无符号数)后减指令 MLS{cond}{S}Rd,Rm,Rn,Ra,低32位给Rd 高32位给Rm
UMLAL 先乘(当无符号数)后加指令 UMLAL{cond}{S}Rd,Rm,Rn,Ra,将结果于Rd和Rn组成的64位
SMULL 先乘(当有符号数)后减指令 SMULL{cond}{S}Rd,Rm,Rn,Ra,低32位给Rd 高32位给Rm
SMLAL 先乘(当有符号数)后加指令 SMLAL{cond}{S}Rd,Rm,Rn,Ra,将结果于Rd和Rn组成的64位数相加,低32位给Rd 高32位给Rm
SMLAD Rm的低半字节和Rn的低半字节乘,Rm的高半字节和Rn的高半字节乘,最后两个乘积与Ra相加 存入Rd SMLAL{cond}{S}Rd,Rm,Rn,Ra
SMLSD Rm的低半字节和Rn的低半字节乘,Rm的高半字节和Rn的高半字节乘,最后两个乘积相减最后与Ra相加 存入Rd SMLSD{cond}{S}Rd,Rm,Rn,Ra
SDIV 有符号的除法 SDIV{cond}{S}Rd,Rm,Rn (Rd) = (Rm)*(Rn)
UDIV 无符号的除法 UDIV{cond}{S}Rd,Rm,Rn
ASR 算数右移指令 将Rm右移operand2位,使用有符号数填充空位,保存到Rd ASR{cond}{S}Rd,Rm,operand2
AND 逻辑与指令 将(Rd) = (Rm) & operand2 AND{cond}{S}Rd,Rm,operand2 相同位1
ORR 逻辑或指令 将(Rd) = (Rm) operand2 ORR{cond}{S}Rd,Rm,operand2
EOR 逻辑异或指令 将(Rd) = (Rm) xor operand2 EOR{cond}{S}Rd,Rm,operand2 不同为1
BIC 清除指令 operand2取反后与Rm 存入 Rd BIC{cond}{S}Rd,Rm,operand2
LSL 逻辑左移 Rm左移operand2位空位清零 存入 Rd LSL{cond}{S}Rd,Rm,operand2
LSR 逻辑右移 Rm右移operand2位空位清零 存入 Rd LSR{cond}{S}Rd,Rm,operand2
ROR 循环右移 Rm右移operand2位 右移出部分移到左边 结果存入 Rd ROR{cond}{S}Rd,Rm,operand2
RRX 带扩展的循环右移 Rm右移1位 最高位用标志位值填充,结果存入 Rd RRX{cond}{S}Rd,Rm
CMP 比较指令 判断Rd-operand2的值是否为零 并设置标志位 结果不存入Rd CMP{cond}Rd,operand2
CMN 判断Rd+operand2的值 并设置标志位 结果不存入Rd CMN{cond}Rd,operand2
TST 位测试指令 判断Rd&operand2的值 并设置标志位 TST{cond}Rd,operand2 TST R0,#1 判断R0最低位是否为1
TEQ 异或运算Rd xor operand2 并设置标志位 结果不存入Rd TEQ{cond}Rd,operand2 TEQ R0,R1 判断R0和R1的值是否相等
SWI 软中断指令 实现用户模式到管理员模式的切换 SWI{cond},immed_24 immed_24为24位的中断号,R7存系统调用号,R0~R3传递系统调用的前四个参数,剩余使用堆栈传递
NOP 空操作指令 用于空操作或字节对齐
MRS 读状态寄存器指令 读取CPSR寄存器到Rd MRS Rd CPSR
MSR 写状态寄存器指令 MSR Rd,psr_field,operand2 psr 取值CPSR或者SPSR filed指定传送区域
field 含义
c 控制域屏蔽字节 psr[7···0]
x 扩展域屏蔽字节 psr[15···8]
s 状态域屏蔽字节 psr[23···16]
f 标志域屏蔽字节 psr[31···24]
MRS R0,CPSR         @读取CPSR寄存器到R0寄存器中
BIC R0,R0,#0x80     @清除R0寄存器第7位
MSR CPSR_c,R0       @开启IRQ中断
MOV PC,LR           @子程序返回
mov R0,#0  参数为0
MOV R7,#1  系统功能1为exit
SWI #0     执行exit(0)
书中185页将的 NEON和VFP指令集 在Android_NDK中的申请

第七章 Android NDK程序逆向分析(Native Development Kit)

我的NDK和make.exe目录
D:\android_sdk\ndk-bundle\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin

D:\android_sdk\ndk-bundle\prebuilt\windows-x86_64\bin

make 
make install
adb shell /data/local/hello
arm-linux-androideabi功能说明

arm-linux-androideabi功能说明

书中189页介绍了makefile文件编写和windows环境编译c代码的过程
有必要分析一下makefile的文件

参考https://www.cnblogs.com/bingghost/p/5721423.html

#ndk根目录
NDK_ROOT=c:/android-ndk-r8  
#编译器根目录
TOOLCHAINS_ROOT=$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/windows
#编译器目录
TOOLCHAINS_PREFIX=$(TOOLCHAINS_ROOT)/bin/arm-linux-androideabi
#头文件搜索路径
TOOLCHAINS_INCLUDE=$(TOOLCHAINS_ROOT)/lib/gcc/arm-linux-androideabi/4.4.3/include-fixed
#SDK根目录
PLATFORM_ROOT=$(NDK_ROOT)/platforms/android-14/arch-arm
#sdk头文件搜索路径
PLATFORM_INCLUDE=$(PLATFORM_ROOT)/usr/include
#sdk库文件搜索路径
PLATFORM_LIB=$(PLATFORM_ROOT)/usr/lib
#文件名称
MODULE_NAME=hello
#删除
RM=del
#编译选项   -nostdlib 是因为Google配置了自己的Bionic库
FLAGS=-I$(TOOLCHAINS_INCLUDE) \
    -I$(PLATFORM_INCLUDE) \
    -L$(PLATFORM_LIB) \
    -nostdlib \
    -lgcc \
    -Bdynamic \
    -lc
#所有obj文件
OBJS=$(MODULE_NAME).o \
    $(PLATFORM_LIB)/crtbegin_dynamic.o \
    $(PLATFORM_LIB)/crtend_android.o
#编译器链接
all:
    $(TOOLCHAINS_PREFIX)-gcc $(FLAGS) -c $(MODULE_NAME).c -o $(MODULE_NAME).o
    $(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) -o $(MODULE_NAME)
#删除所有.o文件
clean:
    $(RM) *.o
#安装程序到手机
install:
    adb push $(MODULE_NAME) /data/local/
    adb shell chmod 755 /data/local/$(MODULE_NAME)
#运行程序
run:
    adb shell /data/local/$(MODALE_NAME) 

如上三个自定义的make命令步骤如下:
- make
- 连接夜神 adb connect 127.0.0.1:62001
- make install ()
- make run(或者 直接 adb shell /data/local/hello)

DOS下命令如下:

D:\gccTest>adb connect 127.0.0.1:62001
connected to 127.0.0.1:62001

D:\gccTest>make install
adb push hello /data/local/
hello: 1 file pushed. 3.0 MB/s (6188 bytes in 0.002s)
adb shell chmod 755 /data/local/hello

D:\gccTest> adb shell /data/local/tmp/hello
/system/bin/sh: /data/local/tmp/hello: not found

D:\gccTest> adb shell /data/local/hello
Hello ARM!

注意:

D:\gccTest>make
makefile:37: *** missing separator.  Stop.

这个问题是run命令缩进必须为一个tab键,如下

参考 https://blog.csdn.net/limanjihe/article/details/52231243

run:
    adb shell /data/local/$(MODALE_NAME) 

如下使用真机错误 这个需要给root 并修改data目录chmod 777

D:\gccTest>make install
adb push hello /data/local/
adb: error: failed to copy 'hello' to '/data/local/hello': remote couldn't create file: Permission denied
hello: 0 files pushed. 0.1 MB/s (6188 bytes in 0.057s)
make: *** [install] Error 1
使用ndk_build编译

Android 命令行 目前只支持以下几种

Supported commands are:
android list target
android list avd
android list device
android create avd
android move avd
android delete avd
android list sdk
android update sdk
  • Android.mk:工程的编译脚本,描述了编译原生程序支持的ARM硬件指令集,工程编译脚本,StL支持
  • Application.mk:工程编译脚本,描述原生程序的编译选项、头文件、源文件及依赖库。
Android.mk分析

系统自带的.mk文件 D:\android-ndk-r10e\build\core

参考 点个灯
http://blog.sina.com.cn/s/blog_5de73d0b0102xql5.html

https://blog.csdn.net/yuanjize1996/article/details/54376228

LOCAL_PATH := $(call my-dir)       ;定义本地源码路径 my-dir制定了调用my-dir宏 返回android.mk本身路径
include $(CLEAR_VARS)              ;清理除"LOCAL_PATH"之外所有"LOCAL_",开头的变量CLEAR_VARS指向(NDK_ROOT)/build/core/clear-vars.mk; 
LOCAL_ARM_MODE :=arm                ;指定使用ARM指令模式 32位的arm指令系统
LOCAL_MODULE := hello               ;指定模块名称 即原生程序生成后的文件名,如果是生成共享库模块,将会生成hello.so
LOCAL_SRC_FILES := hello.c          ;指定C或者C++源文件列表,这里指hello.c  指定多个文件 用tab或空格隔开 或者用“\” 有层级关系需加上相应路径
include $(BUILD_EXECUTABLE)            ;指定生成文件类型 分可执行(BUILD_EXECUTABLE) 动态(BUILD_SHARED_LIBRARY) (BUILD_STATIC_LIBRARY)静态库 指定生成文件类型,指向makefile((NDK_ROOT)/build/core/build-shared-library.mk)

在build_gradle中加入以下代码 自动编译生成so

 sourceSets.main{
        jni.srcDirs=[]//禁用as自动生成mk
    }
    task ndkBuild(type:Exec,description:'Compile JNI source via NDK'){
        commandLine "E:\\android-ndk-r10b\\ndk-build.cmd",//配置ndk的路径
                'NDK_PROJECT_PATH=build/intermediates/ndk',//ndk默认的生成so的文件
                'NDK_LIBS_OUT=src/main/jniLibs',//配置的我们想要生成的so文件所在的位置
                'APP_BUILD_SCRIPT=src/main/jni/Android.mk',//指定项目以这个mk的方式
                'NDK_APPLOCATION_MK=src/main/jni/Application.mk'//指定项目以这个mk的方式
    }

    tasks.withType(JavaCompile){//使用ndkBuild
        compileTask ->compileTask.dependsOn ndkBuild
    }

错误

call __ndk_info,Your APP_BUILD_SCRIPT points to an unknown file: $(APP_BUILD
//我自己写的jni目录写成jin了 我艹

编译错误

D:\androidSpace\HelloGcc\app\src\main\jin>ndk-build
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-14.
Android NDK: WARNING: APP_PLATFORM android-14 is higher than android:minSdkVersion 1 in D:/androidSpace/HelloGcc/app/src/main/AndroidManifest.xml. NDK
 binaries will *not* be compatible with devices older than android-14. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_pro
blems.md for more information.
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: D:/androidSpace/HelloGcc/app/src/main/jni/Android.mk
D:/android_sdk/ndk-bundle/build//../build/core/add-application.mk:101: *** Android NDK: Aborting...    .  Stop.

查询参考 https://blog.csdn.net/xn4545945/article/details/9033925

书中198页研究main函数执行流程
原生文件格式 ARM ELF File Format

ARM开发者中心
http://infocenter.arm.com/help/index.jsp

ELF官方文档
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf

原生C程序逆向分析 书中200页

两大工具
- objdump
- IDA Pro

我的arm-linux-androideabi-objdump.exe路径

D:\android-ndk-r10e\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin\arm-linux-androideabi-objdump.exe

反编译命令行

D:\android-ndk-r10e\toolchains\arm-linux-androideabi-4.9\prebuilt\windows-x86_64\bin>arm-linux-androideabi-objdump -S D:\gccTest\hello

输出

D:\gccTest\hello:     file format elf32-littlearm


Disassembly of section .plt:

00008220 <puts@plt-0x14>:
    8220:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)
    8224:       e59fe004        ldr     lr, [pc, #4]    ; 8230 <puts@plt-0x4>
    8228:       e08fe00e        add     lr, pc, lr
    822c:       e5bef008        ldr     pc, [lr, #8]!
    8230:       00001db8                        ; <UNDEFINED> instruction: 0x00001db8

00008234 <puts@plt>:
    8234:       e28fc600        add     ip, pc, #0, 12
    8238:       e28cca01        add     ip, ip, #4096   ; 0x1000
    823c:       e5bcfdb8        ldr     pc, [ip, #3512]!        ; 0xdb8

00008240 <__libc_init@plt>:
    8240:       e28fc600        add     ip, pc, #0, 12
    8244:       e28cca01        add     ip, ip, #4096   ; 0x1000
    8248:       e5bcfdb0        ldr     pc, [ip, #3504]!        ; 0xdb0

0000824c <__cxa_atexit@plt>:
    824c:       e28fc600        add     ip, pc, #0, 12
    8250:       e28cca01        add     ip, ip, #4096   ; 0x1000
    8254:       e5bcfda8        ldr     pc, [ip, #3496]!        ; 0xda8

Disassembly of section .text:

00008258 <main>:
    8258:       e92d4800        push    {fp, lr}
    825c:       e28db004        add     fp, sp, #4
    8260:       e24dd008        sub     sp, sp, #8
    8264:       e50b0008        str     r0, [fp, #-8]
    8268:       e50b100c        str     r1, [fp, #-12]
    826c:       e59f3018        ldr     r3, [pc, #24]   ; 828c <main+0x34>
    8270:       e08f3003        add     r3, pc, r3
    8274:       e1a00003        mov     r0, r3
    8278:       ebffffed        bl      8234 <puts@plt>
    827c:       e3a03000        mov     r3, #0
    8280:       e1a00003        mov     r0, r3
    8284:       e24bd004        sub     sp, fp, #4
    8288:       e8bd8800        pop     {fp, pc}
    828c:       000000c8        .word   0x000000c8

00008290 <__atexit_handler_wrapper>:
    8290:       e3500000        cmp     r0, #0
    8294:       e92d4008        push    {r3, lr}
    8298:       08bd8008        popeq   {r3, pc}
    829c:       e12fff30        blx     r0
    82a0:       e8bd8008        pop     {r3, pc}

000082a4 <_start>:
    82a4:       e59fc05c        ldr     ip, [pc, #92]   ; 8308 <_start+0x64>
    82a8:       e92d4800        push    {fp, lr}
    82ac:       e59f2058        ldr     r2, [pc, #88]   ; 830c <_start+0x68>
    82b0:       e28db004        add     fp, sp, #4
    82b4:       e24dd010        sub     sp, sp, #16
    82b8:       e08fc00c        add     ip, pc, ip
    82bc:       e59f304c        ldr     r3, [pc, #76]   ; 8310 <_start+0x6c>
    82c0:       e79c2002        ldr     r2, [ip, r2]
    82c4:       e59f1048        ldr     r1, [pc, #72]   ; 8314 <_start+0x70>
    82c8:       e50b2014        str     r2, [fp, #-20]  ; 0xffffffec
    82cc:       e59f2044        ldr     r2, [pc, #68]   ; 8318 <_start+0x74>
    82d0:       e79c3003        ldr     r3, [ip, r3]
    82d4:       e50b3010        str     r3, [fp, #-16]
    82d8:       e59f303c        ldr     r3, [pc, #60]   ; 831c <_start+0x78>
    82dc:       e79c1001        ldr     r1, [ip, r1]
    82e0:       e50b100c        str     r1, [fp, #-12]
    82e4:       e79c2002        ldr     r2, [ip, r2]
    82e8:       e50b2008        str     r2, [fp, #-8]
    82ec:       e28b0004        add     r0, fp, #4
    82f0:       e79c2003        ldr     r2, [ip, r3]
    82f4:       e3a01000        mov     r1, #0
    82f8:       e24b3014        sub     r3, fp, #20
    82fc:       ebffffcf        bl      8240 <__libc_init@plt>
    8300:       e24bd004        sub     sp, fp, #4
    8304:       e8bd8800        pop     {fp, pc}
    8308:       00001d28        .word   0x00001d28
    830c:       ffffffec        .word   0xffffffec
    8310:       fffffff0        .word   0xfffffff0
    8314:       fffffff4        .word   0xfffffff4
    8318:       fffffff8        .word   0xfffffff8
    831c:       fffffffc        .word   0xfffffffc

00008320 <atexit>:
    8320:       e1a01000        mov     r1, r0
    8324:       e59f200c        ldr     r2, [pc, #12]   ; 8338 <atexit+0x18>
    8328:       e59f000c        ldr     r0, [pc, #12]   ; 833c <atexit+0x1c>
    832c:       e08f2002        add     r2, pc, r2
    8330:       e08f0000        add     r0, pc, r0
    8334:       eaffffc4        b       824c <__cxa_atexit@plt>
    8338:       00001ccc        .word   0x00001ccc
    833c:       ffffff58        .word   0xffffff58
  • 程序输出.plt和.text段的内容
  • .plt段用于函数的重定位
  • .text段位程序代码段
IDA pro 分析
  • 空格键切换视图
  • Ctrl+1 Proximity Browser 查看视图
for循环分析

源码

//
// Created by Administrator on 2018/7/31.
//
#include<stdio.h>

int nums[5] = {1, 2, 3, 4, 5};

int for1(int n) {
    int i; 
    int s = 0;
    for (int i = 0; i < n; ++i) {
        s += i * 2;
    }
    return s;
}

int for2(int n) {
    int i;
    int s = 0;
    for (int i = 0; i < n; ++i) {
        s += i *i+ nums[n-1];
    }
    return s;
}

int main(int argc, int **argv[]) {
    printf("for1:%d\n",for1(5));
    printf("for2:%d\n",for2(5));
    return 0;
}

编译

make

报错

Cfors.c: In function 'for1':
Cfors.c:11:14: error: redeclaration of 'i' with no linkage
     for (int i = 0; i < n; ++i) {
              ^
Cfors.c:9:9: note: previous declaration of 'i' was here
     int i;
         ^
Cfors.c:11:5: error: 'for' loop initial declarations are only allowed in C99 or C11 mode
     for (int i = 0; i < n; ++i) {
     ^
Cfors.c:11:5: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code
Cfors.c: In function 'for2':
Cfors.c:20:14: error: redeclaration of 'i' with no linkage
     for (int i = 0; i < n; ++i) {
              ^
Cfors.c:18:9: note: previous declaration of 'i' was here
     int i;
         ^
Cfors.c:20:5: error: 'for' loop initial declarations are only allowed in C99 or C11 mode
     for (int i = 0; i < n; ++i) {
     ^
make: *** [all] Error 1

解决 在makefile的FLAGS中加入 -std=gnu11\(这里“\”起分隔符作用)

 note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code

猜你喜欢

转载自blog.csdn.net/qq_20330595/article/details/81239840