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

文章目录

前言

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

链接: https://pan.baidu.com/s/1jHWVR-At9qT3jn2zf1K4bw
提取码: veev

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_list7 types
2Test
3java.io.PrintStream
4java.lang.Object
5java.lang.String
6java.lang.System
8void
Ajava.lang.String[]
struct string_id_list14strings
0
1Hello World!
2LTest;
3java.io.PrintStream;
4java.lang.Object;
5java.lang.String;
6java.lang.System;
8V(void;)
9VL
7Test.java
A[Ljava/lang/String;
Bmain
Cout
Dprintln
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_list3 prototypes
0void()
1void (java.lang.String)
2void (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标志的值填充

指令格条件码

指令格式跳转码

跳转指令
  • 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]
相对PCLDR R8,label1
addr_mode含义
IAIncrease After 基址寄存器执行后 增加
IBIncrease Before 基址寄存器执行前 增加
DADecrease After 基址寄存器执行后 减少
DBDecrease 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,Rn32位乘法 (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
SMLADRm的低半字节和Rn的低半字节乘,Rm的高半字节和Rn的高半字节乘,最后两个乘积与Ra相加 存入RdSMLAL{cond}{S}Rd,Rm,Rn,Ra
SMLSDRm的低半字节和Rn的低半字节乘,Rm的高半字节和Rn的高半字节乘,最后两个乘积相减最后与Ra相加 存入RdSMLSD{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位,使用有符号数填充空位,保存到RdASR{cond}{S}Rd,Rm,operand2
AND逻辑与指令 将(Rd) = (Rm) & operand2AND{cond}{S}Rd,Rm,operand2相同位1
ORR逻辑或指令 将(Rd) = (Rm)operand2ORR{cond}{S}Rd,Rm,operand2
EOR逻辑异或指令 将(Rd) = (Rm) xor operand2EOR{cond}{S}Rd,Rm,operand2不同为1
BIC清除指令 operand2取反后与Rm 存入 RdBIC{cond}{S}Rd,Rm,operand2
LSL逻辑左移 Rm左移operand2位空位清零 存入 RdLSL{cond}{S}Rd,Rm,operand2
LSR逻辑右移 Rm右移operand2位空位清零 存入 RdLSR{cond}{S}Rd,Rm,operand2
ROR循环右移 Rm右移operand2位 右移出部分移到左边 结果存入 RdROR{cond}{S}Rd,Rm,operand2
RRX带扩展的循环右移 Rm右移1位 最高位用标志位值填充,结果存入 RdRRX{cond}{S}Rd,Rm
CMP比较指令 判断Rd-operand2的值是否为零 并设置标志位 结果不存入RdCMP{cond}Rd,operand2
CMN判断Rd+operand2的值 并设置标志位 结果不存入RdCMN{cond}Rd,operand2
TST位测试指令 判断Rd&operand2的值 并设置标志位TST{cond}Rd,operand2TST R0,#1 判断R0最低位是否为1
TEQ异或运算Rd xor operand2 并设置标志位 结果不存入RdTEQ{cond}Rd,operand2TEQ R0,R1 判断R0和R1的值是否相等
SWI软中断指令 实现用户模式到管理员模式的切换SWI{cond},immed_24immed_24为24位的中断号,R7存系统调用号,R0~R3传递系统调用的前四个参数,剩余使用堆栈传递
NOP空操作指令 用于空操作或字节对齐
MRS读状态寄存器指令 读取CPSR寄存器到RdMRS Rd CPSR
MSR写状态寄存器指令MSR Rd,psr_field,operand2psr 取值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

使用IDA打开Cfors文件

; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
EXPORT main
main

var_C= -0xC
var_8= -8                   ;栈变量

STMFD   SP!, {R11,LR}       ;堆栈寻址指令  入栈
ADD     R11, SP, #4         ;设置R11的值,作为栈帧指针使用
SUB     SP, SP, #8          ;Sp = Sp-8 指针向下移动8个字节
STR     R0, [R11,#var_8]    ;将R0中的字数据写入以R11-8为地址的存储器中。
STR     R1, [R11,#var_C]    
MOV     R0, #5              ;R0等于#5
BL      for1                ;跳转for1处
MOV     R2, R0              ;
LDR     R3, =(aFor1D - 0x128);将(aFor1D - 0x128)的值直接赋给R3
ADD     R3, PC, R3      ; "for1:%d\n" ;IDA双击aFor1D可查看该字符串
MOV     R0, R3          ; format
MOV     R1, R2
BL      printf
MOV     R0, #5
BL      for2
MOV     R2, R0
LDR     R3, =(aFor2D - 0x148)
ADD     R3, PC, R3      ; "for2:%d\n"
MOV     R0, R3          ; format
MOV     R1, R2
BL      printf
MOV     R3, #0
MOV     R0, R3
SUB     SP, R11, #4
LDMFD   SP!, {R11,PC}
; End of function main
LDR r, label 和 LDR r, =label的区别:

LDR r, =label 会把label表示的值加载到寄存器中,而LDR r,
label会把label当做地址,把label指向的地址中的值加载到寄存器中。

譬如 label的值是 0x8000,** LDR r, =label会将 0x8000加载到寄存器中**,而LDR r,
label则会将内存0x8000处的值加载到寄存器中。

for1方法区块如图;

  • BL for1处 双击for1 跳转到for1代码段 再空格键转换成视图
  • 蓝色箭头:顺序执行
  • 绿色箭头:满足条件执行
  • 红色箭头:不满足条件执行

IDA

我们对着C代码看

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

loc_44部分

  • LDR R2,[R11,#var_C] ;R2 = ([R11+var_C]) 即n=R2
  • LDR R3,[R11,#var_10] ;R2 = ([R11+var_10]) 即i=R3
  • CMP R2,R3 ;比较R2,R3
  • BLT loc_24 ;LT:N!V 有符号数小于 也就是当R2<R3时 走绿线 即loc_24

loc_24部分

  • MoV R3,R3,LSL#1 ;R3 = R32 即i2
  • ADD R3,R2,R3 ;R3 = R3+R2 即 s=s+i
  • ADD R3,R3,#1 ;R3++ 即i++
  • STR R3,[R11,#varc]?[R11+#varc]) = R3 即保存i的值 之后走蓝线顺序执行到loc_44

for2部分
for2

对应c代码

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

在loc_94块中

var_C = i

var_10=n

var_8 =s

取nums

LDR     R3, =(nums_ptr - _GLOBAL_OFFSET_TABLE_)
LDR     R3, [R1,R3]     ; nums

关于_GLOBAL_OFFSET_TABLE_这个参数 作者解释为:

静态常量统一保存在.got这样一个区块中。

R3定位到.got:0000019C

R2d定位到000001A0

.got:0000019C ; ===========================================================================
.got:0000019C
.got:0000019C ; Segment type: Pure data
.got:0000019C                 AREA .got, DATA, READONLY
.got:0000019C                 ; ORG 0x19C
.got:0000019C _GLOBAL_OFFSET_TABLE_ DCD 0             ; DATA XREF: for2+14↑o
.got:0000019C                                         ; .text:off_F4↑o ...
.got:000001A0 nums_ptr        DCD nums                ; DATA XREF: for2+44↑r
.got:000001A0                                         ; .text:off_F8↑o
.got:000001A0 ; .got          ends
.got:000001A0

nums的结构

.data:00000168 ; ===========================================================================
.data:00000168
.data:00000168 ; Segment type: Pure data
.data:00000168                 AREA .data, DATA
.data:00000168                 ; ORG 0x168
.data:00000168                 EXPORT nums
.data:00000168 nums            DCB    1                ; DATA XREF: for2+44↑o
.data:00000168                                         ; .got:nums_ptr↓o
.data:00000169                 DCB    0
.data:0000016A                 DCB    0
.data:0000016B                 DCB    0
.data:0000016C                 DCB    2
.data:0000016D                 DCB    0
.data:0000016E                 DCB    0
.data:0000016F                 DCB    0
.data:00000170                 DCB    3
.data:00000171                 DCB    0
.data:00000172                 DCB    0
.data:00000173                 DCB    0
.data:00000174                 DCB    4
.data:00000175                 DCB    0
.data:00000176                 DCB    0
.data:00000177                 DCB    0
.data:00000178                 DCB    5
.data:00000179                 DCB    0
.data:0000017A                 DCB    0
.data:0000017B                 DCB    0
.data:0000017B ; .data         ends
.data:0000017B
.bss:0000017C ; ===========================================================================

i*i

LDR     R3, [R11,#var_C]
LDR     R2, [R11,#var_C]
MUL     R2, R3, R2

n-1

LDR     R3, [R11,#var_10]
SUB     R0, R3, #1

nums[n-1]

LDR     R3, =(nums_ptr - _GLOBAL_OFFSET_TABLE_)
LDR     R3, [R1,R3]     ; nums
LDR     R3, [R3,R0,LSL#2]  ;R3 = R3+R0*4    看上面的角标就能够你理解角标移动n次等于偏移地址为n*4

s+=i*i+nums[n-1]

ADD     R3, R2, R3      ;i*i+nums[n-1]
LDR     R2, [R11,#var_8];R2 = ([R11,#var_8]) 即 R2 = s
ADD     R3, R2, R3      ;R3 = (s += R2)
STR     R3, [R11,#var_8]            ;把新的s值存入[var_8]中
LDR     R3, [R11,#var_C]            ;R3的值重新置为i
ADD     R3, R3, #1                  ;i++
STR     R3, [R11,#var_C]            ;新的i存入[R11,#var_C]
书中203~217页对 for ifelse siwtch dowhile while 等操作流做了相当详细的介绍 务必亲自动手做一遍
C++原生逆向
  • 直接将会作者的Android.mk和cpp1.cpp复制到 自建项目中的jni文件夹中 build一下 生成libcpp1.so
  • 用IDA pro打开

main方法源码

int main(int argc, char* argv[]){
	aclass *a = new aclass(3, 'c');
	a->setM(5);
	a->setC('a');
	a->add(2, 8);
	printf("%d\n", a->getM());
	delete a;
	return 0;
}

反编译代码如下:

CODE16

; int __cdecl main(int argc, const char **argv, const char **envp)
EXPORT main
main
; __unwind {
PUSH    {R3-R5,LR}      ;入栈 保护现场
MOVS    R0, #8
BL      sub_1BA8        ;operator new(uint)
MOVS    R4, R0          ;保存分配的内存对象地址 也就是[R4]指向对象首地址
LDR     R0, =(aConstructorCal - 0xCA6)
LDR     R5, =(aD - 0xCAE)
ADD     R0, PC          ; "Constructor called."
BL      sub_1BB8        ;operator new(uint)
MOVS    R3, #5          ;m=5
ADD     R5, PC          ; "%d\n"
STR     R3, [R4]        ;([R4]) = R3 即 setM(5)
MOVS    R3, #0x61 ; 'a' 
STRB    R3, [R4,#4]     ;即设置对象aclass的第二个成员c,setC('a')
MOVS    R0, R5          ;format
MOVS    R1, #0xA        ;add(2,8)=10=0xAH;
BL      sub_1BC8        ;printf方法
LDR     R1, [R4]        ;
MOVS    R0, R5
BL      sub_1BC8
LDR     R0, =(aDestructorCall - 0xCC8)
ADD     R0, PC          ; "Destructor called."
BL      sub_1BB8
MOVS    R0, R4
BL      sub_1BD8
MOVS    R0, #0
POP     {R3-R5,PC}      ;出栈 方法返回
; End of function main

这里的 sub_1BA8 即源代码的 new 操作 注意: _Znwj

.text:00001BA8 sub_1BA8                                ; CODE XREF: main+4↑p
.text:00001BA8                 BX      PC
.text:00001BA8 ; ---------------------------------------------------------------------------
.text:00001BAA                 ALIGN 4
.text:00001BAC                 CODE32
.text:00001BAC
.text:00001BAC loc_1BAC                                ; CODE XREF: sub_1BA8↑j
.text:00001BAC                 LDR     R12, =(_Znwj - 0x1BB8)
.text:00001BB0                 ADD     PC, R12, PC     ; operator new(uint)
.text:00001BB0 ; End of function sub_1BA8

这里的 sub_1BC8 即源代码的printf

.text:00001BC8 sub_1BC8                                ; CODE XREF: main+22↑p
.text:00001BC8                                         ; main+2A↑p
.text:00001BC8                 BX      PC
.text:00001BC8 ; ---------------------------------------------------------------------------
.text:00001BCA                 ALIGN 4
.text:00001BCC                 CODE32
.text:00001BCC
.text:00001BCC loc_1BCC                                ; CODE XREF: sub_1BC8↑j
.text:00001BCC                 LDR     R12, =(printf - 0x1BD8)
.text:00001BD0                 ADD     PC, R12, PC     ; printf
.text:00001BD0 ; End of function sub_1BC8
.text:00001BD0
.text:00001BD0 ; ---------------------------------------------------------------------------

这里的sub_1BD8 是析构函数 注意: _ZdlPv

.text:00001BD8 sub_1BD8                                ; CODE XREF: main+38↑p
.text:00001BD8                 BX      PC
.text:00001BD8 ; ---------------------------------------------------------------------------
.text:00001BDA                 ALIGN 4
.text:00001BDC                 CODE32
.text:00001BDC
.text:00001BDC loc_1BDC                                ; CODE XREF: sub_1BD8↑j
.text:00001BDC                 LDR     R12, =(_ZdlPv - 0x1BE8)
.text:00001BE0                 ADD     PC, R12, PC     ; operator delete(void *)
.text:00001BE0 ; End of function sub_1BD8
疑问那类怎么没有被描述?

答案:aclass总共两个成员变量,共占用8个字节的存储单元 其他的setM和setC方法
编程直接访问R4所指存储单元的代码。STRB R3, [R4,#4]即设置对象aclass的第二个成员c。

书中227页 对动静STL做了介绍-没懂~
静态分析Android NDK程序
已标记关键词 清除标记
Android软件安全逆向分析》由浅入深、循序渐进地讲解了Android系统的软件安全逆向分析加密解密技术。包括Android软件逆向分析和系统安全方面的必备知识及概念、如何静态分析Android软件、如何动态调试Android 软件Android 软件的破解反破解技术的探讨,以及对典型Android病毒的全面剖析。 作者简介:丰生强网名非虫Android软件安全专家,看雪论坛Android安全版版主,安卓巴士开发交流版版主。 目录 第1章  Android程序分析环境搭建 1.1  Windows分析环境搭建 1.1.1  安装JDK 1.1.2  安装Android SDK 1.1.3  安装Android NDK 1.1.4  Eclipse集成开发环境 1.1.5  安装CDT、ADT插件 1.1.6  创建Android Virtual Device 1.1.7  使用到的工具 1.2  Linux分析环境搭建 1.2.1  本书的Linux环境 1.2.2  安装JDK 1.2.3  在Ubuntu上安装Android SDK 1.2.4  在Ubuntu上安装Android NDK 1.2.5  在Ubuntu上安装 Eclipse集成开发环境 1.2.6  在Ubuntu上安装CDT、ADT插件 1.2.7  创建Android Virtual Device 1.2.8  使用到的工具 1.3  本章小结 第2章  如何分析Android程序 2.1  编写第一个Android程序 2.1.1  使用Eclipse创建Android工程 2.1.2  编译生成APK文件 2.2  破解第一个程序 2.2.1  如何动手? 2.2.2  反编译APK文件 2.2.3  分析APK文件 2.2.4  修改Smali文件代码 2.2.5  重新编译APK文件并签名 2.2.6  安装测试 2.3  本章小结 第3章  进入Android Dalvik虚拟机 3.1  Dalvik虚拟机的特点--掌握Android程序的运行原理 3.1.1  Dalvik虚拟机概述 3.1.2  Dalvik虚拟机Java虚拟机的区别 3.1.3  Dalvik虚拟机是如何执行程序的 3.1.4  关于Dalvik虚拟机JIT即时编译 3.2  Dalvik汇编语言基础为分析Android程序做准备 3.2.1  Dalvik指令格式 3.2.2  DEX文件反汇编工具 3.2.3  了解Dalvik寄存器 3.2.4  两种不同的寄存器表示方法--v命名法p命名法 3.2.5  Dalvik字节码的类型、方法字段表示方法 3.3  Dalvik指令集 3.3.1  指令特点 3.3.2  空操作指令 3.3.3  数据操作指令 3.3.4  返回指令 3.3.5  数据定义指令 3.3.6  锁指令 3.3.7  实例操作指令 3.3.8  数组操作指令 3.3.9  异常指令 3.3.10  跳转指令 3.3.11  比较指令 3.3.12  字段操作指令 3.3.13  方法调用指令 3.3.14  数据转换指令 3.3.15  数据运算指令 3.4  Dalvik指令集练习--写一个Dalvik版的Hello World 3.4.1  编写smali文件 3.4.2  编译smali文件 3.4.3  测试运行 3.5  本章小结 第4章  Android可执行文件 4.1  Android程序的生成步骤 4.2  Android程序的安装流程 4.3  dex文件格式 4.3.1  dex文件中的数据结构 4.3.2  dex文件整体结构 4.3.3  dex文件结构分析 4.4  odex文件格式 4.4.1  如何生成odex文件 4.4.2  odex文件整体结构 4.4.3  odex文件结构分析 4.5  dex文件的验证优化工具dexopt的工作过程 4.6  Android应用程序另类破解方法 4.7  本章小结 第5章  静态分析Android程序 5.1  什么是静态分析 5.2  快速定位Android程序的关键代码 5.2.1  反编译apk程序 5.2.2  程序的主Activity 5.2.3  需重点关注的Application类 5.2.4  如何定位关键代码--六种方法 5.3  smali文件格式 5.4  Android程序中的类 5.4.1  内部类 5.4.2  监听器 5.4.3  注解类 5.4.4  自动生成的类 5.5  阅读反编译的smali代码 5.5.1  循环语句 5.5.2  
相关推荐
Android软件安全逆向分析》由浅入深、循序渐进地讲解了Android系统的软件安全逆向分析加密解密技术。包括Android软件逆向分析和系统安全方面的必备知识及概念、如何静态分析Android软件、如何动态调试Android 软件Android 软件的破解反破解技术的探讨,以及对典型Android病毒的全面剖析。 作者简介:丰生强网名非虫Android软件安全专家,看雪论坛Android安全版版主,安卓巴士开发交流版版主。 目录 第1章  Android程序分析环境搭建 1.1  Windows分析环境搭建 1.1.1  安装JDK 1.1.2  安装Android SDK 1.1.3  安装Android NDK 1.1.4  Eclipse集成开发环境 1.1.5  安装CDT、ADT插件 1.1.6  创建Android Virtual Device 1.1.7  使用到的工具 1.2  Linux分析环境搭建 1.2.1  本书的Linux环境 1.2.2  安装JDK 1.2.3  在Ubuntu上安装Android SDK 1.2.4  在Ubuntu上安装Android NDK 1.2.5  在Ubuntu上安装Eclipse集成开发环境 1.2.6  在Ubuntu上安装CDT、ADT插件 1.2.7  创建Android Virtual Device 1.2.8  使用到的工具 1.3  本章小结 第2章  如何分析Android程序 2.1  编写第一个Android程序 2.1.1  使用Eclipse创建Android工程 2.1.2  编译生成APK文件 2.2  破解第一个程序 2.2.1  如何动手? 2.2.2  反编译APK文件 2.2.3  分析APK文件 2.2.4  修改Smali文件代码 2.2.5  重新编译APK文件并签名 2.2.6  安装测试 2.3  本章小结 第3章  进入Android Dalvik虚拟机 3.1  Dalvik虚拟机的特点--掌握Android程序的运行原理 3.1.1  Dalvik虚拟机概述 3.1.2  Dalvik虚拟机Java虚拟机的区别 3.1.3  Dalvik虚拟机是如何执行程序的 3.1.4  关于Dalvik虚拟机JIT即时编译 3.2  Dalvik汇编语言基础为分析Android程序做准备 3.2.1  Dalvik指令格式 3.2.2  DEX文件反汇编工具 3.2.3  了解Dalvik寄存器 3.2.4  两种不同的寄存器表示方法--v命名法p命名法 3.2.5  Dalvik字节码的类型、方法字段表示方法 3.3  Dalvik指令集 3.3.1  指令特点 3.3.2  空操作指令 3.3.3  数据操作指令 3.3.4  返回指令 3.3.5  数据定义指令 3.3.6  锁指令 3.3.7  实例操作指令 3.3.8  数组操作指令 3.3.9  异常指令 3.3.10  跳转指令 3.3.11  比较指令 3.3.12  字段操作指令 3.3.13  方法调用指令 3.3.14  数据转换指令 3.3.15  数据运算指令 3.4  Dalvik指令集练习--写一个Dalvik版的Hello World 3.4.1  编写smali文件 3.4.2  编译smali文件 3.4.3  测试运行 3.5  本章小结 第4章  Android可执行文件 4.1  Android程序的生成步骤 4.2  Android程序的安装流程 4.3  dex文件格式 4.3.1  dex文件中的数据结构 4.3.2  dex文件整体结构 4.3.3  dex文件结构分析 4.4  odex文件格式 4.4.1  如何生成odex文件 4.4.2  odex文件整体结构 4.4.3  odex文件结构分析 4.5  dex文件的验证优化工具dexopt的工作过程 4.6  Android应用程序另类破解方法 4.7  本章小结 第5章  静态分析Android程序 5.1  什么是静态分析 5.2  快速定位Android程序的关键代码 5.2.1  反编译apk程序 5.2.2  程序的主Activity 5.2.3  需重点关注的Application类 5.2.4  如何定位关键代码--六种方法 5.3  smali文件格式 5.4  Android程序中的类 5.4.1  内部类 5.4.2  监听器 5.4.3  注解类 5.4.4  自动生成的类 5.5  阅读反编译的smali代码 5.5.1  循环语句 5.5.2  switch分支语句 5.5.3  try/catch语句 5.6  使用IDA Pro静态分析Android程序 5.6.1  IDA Pro对Android的支持 5.6.2  如何操作 5.6.3  定位关键代码--使用IDA Pro进行破解的实例 5.7  恶意软件分析工具包--Androguard 5.7.1  Androguard的安装配置 5.7.2  Androguard的使用方法 5.7.3  使用Androguard配合Gephi进行静态分析 5.7.4  使用androlyze.py进行静态分析 5.8  其他静态分析工具 5.9  阅读反编译的Java代码 5.9.1  使用dex2jar生成jar文件 5.9.2  使用jd-gui查看jar文件的源码 5.10  集成分析环境--santoku 5.11  本章小结 第6章  基于Android的ARM汇编语言基础--逆向原生! 6.1  AndroidARM处理器 6.1.1  ARM处理器架构概述 6.1.2  ARM处理器家族 6.1.3  Android支持的处理器架构 6.2  原生程序ARM汇编语言--逆向你的原生Hello ARM 6.2.1  原生程序逆向初步 6.2.2  原生程序的生成过程 6.2.3  必须了解的ARM知识 6.3  ARM汇编语言程序结构 6.3.1  完整的ARM汇编程序 6.3.2  处理器架构定义 6.3.3  段定义 6.3.4  注释标号 6.3.5  汇编器指令 6.3.6  子程序参数传递 6.4  ARM处理器寻址方式 6.4.1  立即寻址 6.4.2  寄存器寻址 6.4.3  寄存器移位寻址 6.4.4  寄存器间接寻址 6.4.5  基址寻址 6.4.6  多寄存器寻址 6.4.7  堆栈寻址 6.4.8  块拷贝寻址 6.4.9  相对寻址 6.5  ARMThumb指令集 6.5.1  指令格式 6.5.2  跳转指令 6.5.3  存储器访问指令 6.5.4  数据处理指令 6.5.5  其他指令 6.6  用于多媒体编程浮点计算的NEONVFP指令集 6.7  本章小结 第7章  Android NDK程序逆向分析 7.1  Android中的原生程序 7.1.1  编写一个例子程序 7.1.2  如何编译原生程序 7.2  原生程序的启动流程分析 7.2.1  原生程序的入口函数 7.2.2  main函数究竟何时被执行 7.3  原生文件格式 7.4  原生C程序逆向分析 7.4.1  原生程序的分析方法 7.4.2  for循环语句反汇编代码的特点 7.4.3  if...else分支语句反汇编代码的特点 7.4.4  while循环语句反汇编代码的特点 7.4.5  switch分支语句反汇编代码的特点 7.4.6  原生程序的编译时优化 7.5  原生C++程序逆向分析 7.5.1  C++类的逆向 7.5.2  Android NDK对C++特性的支持 7.5.3  静态链接STL动态链接STL的代码区别 7.6  Android NDK JNI API逆向分析 7.6.1  Android NDK提供了哪些函数 7.6.2  如何静态分析Android NDK程序 7.7  本章小结 第8章  动态调试Android程序 8.1  Android动态调试支持 8.2  DDMS的使用 8.2.1  如何启动DDMS 8.2.2  使用LogCat查看调试信息 8.3  定位关键代码 8.3.1  代码注入法--让程序自己吐出注册码 8.3.2  栈跟踪法 8.3.3  Method Profiling 8.4  使用AndBug调试Android程序 8.4.1  安装AndBug 8.4.2  使用AndBug 8.5  使用IDA Pro调试Android原生程序 8.5.1  调试Android原生程序 8.5.2  调试Android原生动态链接库 8.6  使用gdb调试Android原生程序 8.6.1  编译gdbgdbserver 8.6.2  如何调试 8.7  本章小结 第9章  Android软件的破解技术 9.1  试用版软件 9.1.1  试用版软件的种类 9.1.2  实例破解--针对授权KEY方式的破解 9.2  序列号保护 9.3  网络验证 9.3.1  网络验证保护思路 9.3.2  实例破解--针对网络验证方式的破解 9.4  In-app Billing应用内付费 9.4.1  In-app Billing原理 9.4.2  In-app Billing破解方法 9.5  Google Play License保护 9.5.1  Google Play License保护机制 9.5.2  实例破解--针对Google Play License方式的破解 9.6  重启验证 9.6.1  重启验证保护思路 9.6.2  实例破解--针对重启验证方式的破解 9.7  如何破解其他类型的Android程序 9.7.1  Mono for Android开发的程序及其破解方法 9.7.2  Qt for Android开发的程序及其破解方法 9.8  本章小结 第10章  Android程序的反破解技术 10.1  对抗反编译 10.1.1  如何对抗反编译工具 10.1.2  对抗dex2jar 10.2  对抗静态分析 10.2.1  代码混淆技术 10.2.2  NDK保护 10.2.3  外壳保护 10.3  对抗动态调试 10.3.1  检测调试器 10.3.2  检测模拟器 10.4  防止重编译 10.4.1  检查签名 10.4.2  校验保护 10.5  本章小结 第11章  Android系统攻击防范 11.1  Android系统安全概述 11.2  手机ROOT带来的危害 11.2.1  为什么要ROOT手机 11.2.2  手机ROOT后带来的安全隐患 11.2.3  Android手机ROOT原理 11.3  Android权限攻击 11.3.1  Android权限检查机制 11.3.2  串谋权限攻击 11.3.3  权限攻击检测 11.4  Android组件安全 11.4.1  Activity安全及Activity劫持演示 11.4.2  Broadcast Receiver 安全 11.4.3  Service安全 11.4.4  Content Provider安全 11.5  数据安全 11.5.1  外部存储安全 11.5.2  内部存储安全 11.5.3  数据通信安全 11.6  ROM安全 11.6.1  ROM的种类 11.6.2  ROM的定制过程 11.6.3  定制ROM的安全隐患 11.6.4  如何防范 11.7  本章小结 第12章  DroidKongFu变种病毒实例分析 12.1  DroidKongFu病毒介绍 12.2  配置病毒分析环境 12.3  病毒执行状态分析 12.3.1  使用APIMonitor初步分析 12.3.2  使用DroidBox动态分析 12.3.3  其他动态分析工具 12.4  病毒代码逆向分析 12.4.1  Java层启动代码分析 12.4.2  Native层启动代码分析 12.4.3  Native层病毒核心分析 12.5  DroidKongFu病毒框架总结 12.6  病毒防治 12.7  本章小结
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页