安卓逆向 和 手游辅助 学习 路线

 

From:https://zhuanlan.zhihu.com/p/95915254

知乎:Android 逆向分析学习路线?:https://www.zhihu.com/question/25626303

入门篇 --- 学习Android安全和逆向开发的路线总结:https://www.52pojie.cn/thread-1065039-1-1.html

 

 

路线 1

 

0. 安卓逆向基础(建议1周)

  • 1. 学习安卓逆向第一步必须先把环境搭建好,这是你学习安卓逆向的开始,环境搭建好后表示正式迈入安卓逆向。在环境安装的工程中会遇到很多细节上的问题。
  • 2. 第二步就是要了解我们要分析的是什么文件,很多0基础的同学都不知道安卓逆向分析的什么文件。我们要分析的是应用程序或者安装包(就是.apk文件),了解 apk 是怎么生成的,以及如何安装到我们的手机里面,apk是怎么运行的,也是我们探讨的内容。
  • 3. 第三步如何逆向分析 .apk 文件,掌握 apk 反编译 以及 回编译,完成这个操作使用的工具是 apktool。

 

一. Java 层逆向(建议3周)

  • 1. 掌握 Java 语法基础,达到能看懂 Java 代码。
  • 2. 了解 smali 语法,能看懂 smali 代码。
  • 3. 掌握 逆向分析 apk 中常用的方法和技巧。

 

二. Native 层逆向(建议4周)

  • 1. 了解 安卓操作系统四大组件
  • 2. 了解 NDK 开发流程,自己编写案例练习。
  • 3. 掌握常用 ARM 汇编指令,达到能看懂 ARM 汇编指令。
  • 4. 掌握 ida工具的使用,熟练使用 ida 进行各种操作

 

三. APK 保护策略(建议1周)

  • 1. 了解 Java 代码混淆、资源混淆
  • 2. 掌握签名验证、文件校验、模拟器检测
  • 3. 本地验证、网络验证
  • 4. 案例练习

 

四. 反调试反-反调试(建议1周)

  • 1. 掌握常用 反调试方法过反调试 技巧,比如关键文件检测、调试端口检测、进程名称检测、防附加、轮训检测 TracerPid 值、时间检测、信号检测等反调试。
  • 2. 掌握 IDA 过反调试 思路
  • 3. 案例练习。

 

五. HOOK 框架(建议2周)

  • 1. 掌握 HOOK 插件开发。
  • 2. 掌握 Xposed、Substrate、Fridad 等框架。
  • 3. 案例练习。

 

六. 常见加密算法(建议2周)

  • 1. 掌握编码算法、消息摘要算法、对称加密算法(Java加密与解密的艺术)。
  • 2. 掌握非对称加密算法、数字签名算法。

 

七. 协议加解密分析(建议4周)

  • 1. 了解客户端与服务器如何进行交互的(OSI模型、TCP/IP模型)。
  • 2. 掌握常用的抓包工具及环境配置,HTTP协议与HTTPS安全协议,数字证书、SSL证书检测(计算机网络与通信、信息安全工程师)。
  • 3. 案例练习。

 

八. 文件结构(建议2周)

  • 1. 掌握 DEX、ELF、XML、ARSC 等文件结构。
  • 2. 自编写文件解析工具。

 

九. 系统源码分析(建议2周)

  • 1. 了解安卓操作系统启动流程、Zygote 启动流程。
  • 2. 掌握 Dalvik虚拟机、ART虚拟机、SO 加载流程。

 

十. 加固与脱壳(建议4周)

  • 1. 了解 dex 文件整体加密、Dex 代码抽取加密。
  • 2. 了解 so 文件整体加密、函数加密、区段加密、加壳、混淆。
  • 3. 分析通用脱壳机的实现原理及应用场景。
  • 4. 了解主流加固特点及对应的脱壳技巧。

 

十一. 按键 + 内存(建议2周)

  • 1. 环境搭建,搜索内存数据,对内存数据进行读写操作。
  • 2. 市面上的模拟器辅助,有一部分就是通过搜索内存数据来找特征码,因为游戏中有的数据是不会发生变化的,我们选择这部分不变的数据作为特征码。通过特征码来搜索内存数据,找到特征码的地址,再通过特征码的地址+距离(偏移)来实现定位。
  • 3. 案例练习。

 

十二. 篡改内存数据 + 注入技术 + HOOK技术(建议4周)

  • 1. 掌握模块基地址获取、非注入式篡改数据、注入式篡改内存数据。
  • 2. 掌握注入技术原理( Ptrace注入、Zygote注入、静态感染ELF文件注入)。
  • 3. 掌握 HOOK 技术的实质,就是对函数进行重写( Inline HOOK、异常HOOK、导入表HOOK)。
  • 4. 掌握 C++ 游戏逆向分析技巧。
  • 5. 案例练习。

 

十三. Lua 游戏(建议4周)

  • 1. 掌握 Lua 游戏逆向分析流程。
            第一步:查看 lib 文件夹的 so 文件就可以确定该游戏是不是 Lua 游戏,
            第二步:如果是 Lua 游戏就在 assets 文件夹下查找 lua 脚本。
  • 2. Lua 游戏功能实现都在 Lua 脚本,重点分析 Lua 脚本(Lua明文、LuaC、Luajit)。
  • 3. Lua 文件加密与加密,内存 dump Lua 脚本,HOOK 插件开发。
  • 4. Cocos2dx-Lua 引擎源码分析
  • 5. 案例练习

 

十四. Unity 3D 游戏(建议4周)

  • 1. 掌握 Unity 3D 游戏逆向分析流程。
            第一步:查看 lib文件下的 so 文件就可以确定该游戏是不是 Unity 3D 游戏,
            第二步:如果是 Unity 3D 游戏,就在 assets 文件下查找相应的文件。
  • 2. Unity 3D 有两种框架(MONO框架、IL2CPP框架)。
            MONO框架对应的游戏逻辑实现在 dll 文件,
            IL2CPP框架对应的游戏逻辑实现在 libil2cpp.so 文件。
  • 3. Unity 3D 引擎源码分析。
  • 4. DLL 文件处理,DLL混淆,DLL隐藏,DLL加密。
  • 5. 内存dump dll文件,HOOK插件开发,注入+HOOK。
  • 6. 案例练习

 

十五. 游戏协议(建议2周)

  • 1. WPE环境搭建,拦截发送包和接收包,多截包对比分析封包数据。
  • 2. 分析喊话功能,找出加密规律,各种游戏功能封包拦截分析。
  • 3. 案例练习。

 

十六. 学习方法

  • 1. 看一遍教程后自己实战操作,养成做笔记的习惯。
  • 2. 注重基础,一定要把 Java层 和 Native层 搞懂。
  • 3. 学习中不要纠结细节,要学会抓大放小。
  • 4. 学完后自己画脑图,回顾学习的过程中那些不理解在花时间去看,此过程就是查漏补缺。

 

 

安卓逆向 大纲

 

 

 

Android 逆向

 

常用工具:

JDK / SDK / NDK
eclpise集成开发环境 / Android Studio
AndroidKiller / jeb / jadx / GDA / Androidk逆向助手
IDA / GDB
apkhelper / getsign / APK上上签
模拟器(雷电3.59)
MT管理器 / RE文件管理器

工具安装注意事项:

1. jdk 安装路径中不能有中文
2. ndk 的配置路径中不能有中文和空格,可以把它放在根目录
3. 安装包文件不全,运行会出错
4. 没有配置环境变量可以按住shift+右键=>”在此处打开命令窗口”选项
5. ndk安装正确的提示
      D:\android-ndk-r10e>ndk-build
      Android NDK: Could not find application project directory !
      Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.
      D:\android-ndk-r10e\build/core/build-local.mk:143: *** Android NDK: Aborting.  Stop.

 

 

APK 文件:

APK 是 Android Package 的缩写,即 Android安装包。APK是类似 Symbian Sis 或 Sisx 的文件格式。通过将 APK 文件直接传到Android模拟器 或 Android手机 中执行即可安装。

 

APK 文件目录:

assets     不经过 aapt 编译的资源文件
lib        .so文件
META-INF   文件摘要,摘要加密和签名证书文件目录
      CERT.RSA      公钥和加密算法描述
      CERT.SF       加密文件,它是使用私钥对摘要明文加密后得到的密文信息,
                    只有使用私钥配对的公钥才能解密该文件
      MANIFEST.MF   程序清单文件,它包含包中所有文件的摘要明文
res                   资源文件目录,二进制格式
      drawable        图片
      layout          布局
      menu            菜单
resources.arsc        经过 aapt 编译过的资源文件
classes.dex           可执行文件
AndroidManifest.xml   配置文件

 

APK 打包流程:

      打包资源(res/assets/AndroidManifest.xml/Android基础类库)文件,
             生成 R.java和resources.ap_文件

      处理AIDL文件,生成对应的.java文件

      编译Java文件,生成对应的.class文件

      把.class文件转化成Davik VM支持的.dex文件(.java=>.class=>.dex)

      打包生成未签名的.apk文件

      对未签名.apk文件进行签名

      对签名后的.apk文件进行对齐处理

 

APK 安装流程:

1.安装方式
      系统程序安装
      通过Android市场安装
      ADB安装(adb devices:显示当前连接的设备;adb install 安装包路径)
      手机自带安装  

2.安装过程
      复制APK安装包到/data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保
      存到/data/dalvik-cache目录,并/data/data目录下创建对应的应用数据目录。

3.安装后文件所在目录
      /system/app    系统自带的应用程序,获得adb root权限才能删除
      /data/app      用户程序安装的目录,安装时把apk文件复制到此目录
      /data/data     存放应用程序的数据
      /data/dalvik-cache    将apk中的dex文件安装到dalvik-cache目录下  

4.卸载过程
      删除安装过程中在上述三个目录下创建的文件及目录。  

 

虚拟机:

1.java虚拟机  
    java字节码
    基于栈架构

2.dalvik虚拟机(jit机制) 
  【dalvik加载执行的odex文件: .dex => dexopt  => .odex】
    Android 5.0以下
    dalvik字节码
    dalvik可执行文件体积更小
    基于寄存器架构

3.art虚拟机(aot机制) 
  【art加载执行的是oat文件: .dex => dex2oat => .oat】
    Android 5.0版本及以上            

 

Dalvik 字节码:

1.了解dalvik寄存器
      dalvik中的寄存器都是32位

2.寄存器之v命名法与p命名法
      参数寄存器          P0-Pn  
      局部变量寄存器      V0-Vn

3.dex文件反汇编工具
      smali.jar\ddx.jar

4.类型
      smali ==> Java
      V    void
      Z    boolean
      B    byte
      C    char
      S    short
      I     int
      J     long
      F    float
      D    double
      L    java类
      [     数组

5.字段
        Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;

6.方法
        Lpackage/name/ObjectName;->MethodName (III) Z

 

Dalvik 指令:

基础字节码-名称后缀/字节码后缀 目的寄存器  源寄存器

    名称后缀是 wide,表示数据宽度为64位
    字节码后缀是 from16,表示源寄存器为16位

空操作指令    nop

数据操作指令
    move(-wide/-object/-result/-result-object/-result-wide/-exception)
    move(-wide/-object)/from16
    move(-object)/16

返回指令(重点)
    return-void(表示方法的放回值为空)
    return(表示方法的反回值为32位非对象类型的值)
    return-wide(表示方法的反回值为64位非对象类型的值)
    return-object(表示方法的反回值为对象类型的值)

数据定义指令(重点)
    const(-wide/-string/-class)
    const/4
    const(-wide)/16
    const(-wide)/32
    const(-wide)/high16

实例操作指令
    check-cast(类型转换)
    instance-of(检查) 
    new-instance(新建)

数组操作指令
    array-length(获取数组长度)
    new-array(新建数组)
    filled-new-array(定义数组并初始化)
    filled-new-array/range(定义数组并初始化/指定取值范围)
    filled-array-data(数组元素取值与赋值)

异常指令(throw)

跳转指令(重点)
    goto 紧跟一个标签直接跳转到标签所在位置
    packed-switch(有规律)、sparse-switch(无规律)
    if-eq(等于)/if-ne(不等于)
    if-lt(小于)/if-le(小于等于)
    if-gt(大于)/if-ge(大于等于)

比较指令(cmp)
    大于(1)/等于(0)/小于(-1)=>cmpg、cmp
    大于(-1)/等于(0)/小于(1)=>cmpl

字段操作指令
    普通字段=>iget(读)/iput(写)
    静态字段=>sget(读)/sput(写)

方法调用指令(重点)
    invoke-virtual(调用实例的虚方法) Java当中的普通方法
    invoke-super(调用实例的父类/基类方法)
    invoke-direct(调用实例的直接方法) 调用java当中的构造方法
    invoke-static(调用实例的静态方法)
    invoke-interface(调用实例的接口方法)

数据转换指令
    neg-数据类型=>求补
    not-数据类型=>求反
    数据类型1-to-数据类型2=>将数据类型1转换为数据类型2

数据运算指令
    add/sub/mul/div/rem 加/减/乘/除/模
    and/or/xor  与/或/非
    sh1/shr/ushr   有符号左移/有符号右移/无符号右移

 

APK反编译 与 回编译:

先查壳,再反编译看验证

.apk文件 ==> 反编译apk(dex/配置文件/资源文件(apk反编译败))
==> 修改关键文件实现自己的目的 ==> 重新打包签名(无法重新打包) ==> apk安装后无法运行。

        apktool  .dex=>.smali 
        dex2jar  .dex=>.jar
      
    Apktool工具实际上只反编译以下三种类型文件:.xml文件、.dex文件、.arsc文件

去除广告和弹窗,撇开不存在于smali的这种情况,
        容易的就是可以在XML中寻到Activity,
        难的就是寻不到,发生这种情况时,就要分析代码,程序逻辑,抓住关键信息,界面和函数。

      注意:当字符串等关键信息搜不到时,可以从三个方向考虑:
            1. 字符串在so层;
            2. 字符串被加密了
            3. 结合了服务器,服务器返回,本地显示。

 

 

smali 文件:

     无论是普通类、抽象类、接口类或者内部类,在反编译出的代码中,它们都以单独的 smali 文件来存放。每个 smali 文件都由若干条语句组成, 所有的语句都遵循着一套语法规范。

1.描述类的信息
.class < 访问权限> [ 修饰关键字] < 类名>
.super < 父类名>
.source <源文件名>

2. 静态字段
# static fields
.field < 访问权限> static [ 修饰关键字] < 字段名>:< 字段类型>

3.实例字段
# instance fields
.field < 访问权限> [ 修饰关键字] < 字段名>:< 字段类型>

4.直接方法
# direct methods
.method <访问权限> [ 修饰关键字] < 方法原型>
          <.locals>   “.locals ”指定了使用的局部变量的个数
          [.param] “.param”指定了方法的参数
   [.prologue]      “.prologue ”指定了代码的开始处
   [.line]            “.line ”指定了该处指令在源代码中的行号
   <代码体>
   .end method

5. 虚方法的声明与直接方法相同,只是起始处的注释为“virtual methods”。

6.接口
   # interfaces
   .implements < 接口名>“.implements ”是接口关键字,后面的接口
   名是 DexClassDef 结构中 interfacesOff 字段指定的内容。

 7.注解
     # annotations
     .annotation [ 注解属性] < 注解类名>
         [ 注解字段 = 值]
     .end annotation
   注解的作用范围可以是类、方法或字段。如果注解的作用范围是类,
   “.annotation ”指令会直接定义在 smali 文件中,
   如果是方法或字段,“.annotation ”指令则会包含在方法或字段定义中。

 

快速定位关键代码:

1.分析流程
     搜索特征字符串
     搜索关键api
     通过方法名来判断方法的功能

2.快速定位关键代码
     反编译 APK 程序,AndroidManifest.xml => 包名/系统版本/组件
     程序的主 activity(程序入口界面)
           每个 Android 程序有且只有一个 主Activity
           分析程序的执行流程
     需重点关注的 application
           application 执行时间
           授权验证

3.定位关键代码的技巧
     信息反馈法 ( 资源id/字符串 )
     特征函数法 ( api函数 )
     顺序查看法 ( 分析程序执行流程 / 病毒分析 )
     代码注入法 ( 动态调式 / 插入log / 查看logcat/分析加解密 )
     栈跟踪法   ( 动态调式 / 函数调用流程)
     Method Profiling ( 方法剖析 => 动态调式 / 热点分析/ 函数调用流程 )

 

动态调式:

      jdwp ( 调试有线协议 ) => jdb

      ddms ( dalvik 调式监视器服务 )

通过该命令(adb shell getprop ro.debuggable)获取当前设备 ro.debuggable的值,当值为1时开启系统调试开关。

      android.debuggable="true"

      logcat(查看调式信息) / method profiling(跟踪程序的执行流程)

 

游戏内购:

去除可能会产生费用的危险权限
     android.permission.SEND_SMS
     android.permission.CALL_PHONE

支付接口
     电信支付接口
         logcat字符串定位(Egame支付成功/Egame支付Cancel/orderid) 
         => 搜索字符串,向上分析,回溯分析/函数名替换

     联通支付接口
           logcat字符串定位(Unicom支付cancel/Unicom支付成功)
           =>在下面类修改处理,可以用goto或switch方法

     移动支付接口
           logcat字符串定位(购买道具:[] 成功/购买道具:[] 失败) 
           =>在下面类修改

特征码                    
     中国移动    46000、46002、46007、46020  
     中国联通    46001、46006、46010          
     中国电信    46003、46005、46011

内购关键词
和游戏搜索方法名
onResult,onchinabilling,resulton,Paycenter,Callback

联通游戏搜索方法名
OnPayResult,PyaResulton,Activity,result,callback

电信爱游戏搜索方法名
paySuccess成功,payCancel取消,payFailed失败

移动mm搜索方法名
onBillingFinish,Billing,CallBack,onresult无用,

对关键onsuccess()函数的筛选与定位

支付宝和银行卡方法名

handle,message

支付宝搜索字符

搜索字符串“9000”

360支付

onfinishedon,Activityresult

4399

notifyDeliverGoods 

内购技巧

     从静态代码分析程序逻辑,找到修改点

     程序逻辑分析=>最重要的又是流程分析/流程图=>核心(层次)

     关键性信息查找=>静态(整体分析与局部分析)/动态

 

 

Android NDK:

    1. 如何编译原生程序
           Application.mk(ARM硬件指令集/工程编译脚本/stl支持等)
           Android.mk(编译选项/头文件/源文件及依赖库等)
           local_path(call my-dir)
           include $(clear_vars)
           local_arm_mode:= arm指令模式
           local_module:=模块名称
           local_src_files:=源文件

           build_executable(可执行文件)
           build_shared_library(动态链接库)
           build_static_library(静态链接库))

      2. 原生程序的启动流程
           原生程序的入口函数
                 动态库的加载/程序参数argc和argv的初始化
                 静态链接/动态链接(动态链接程序/动态链接库)
                 静态链接(crtbedin_static.o/crend_android.o)
                 动态链接(crtbegin_dynamic.o/crtend_android.o/加载器 (system/bin/linker))
                 静态链接程序在启动时不需要额外的加载其他的动态库(init/adbd/linker)
               静态链接与动态链接程序的入口函数相同,动态链接程序在执行入口函数前需要通
               过linker进行额外的初始化

           main函数究竟何时被执行
                  静态链接(libc_init_static)/动态链接程序(libc_init_dynamic)

      3. 原生 C++ 程序逆向分析

           C++类的逆向
                 C++中的类可以理解为C语言中的结构体,每一个成员变量就是一个结构字段,
                 每一个成员函数的代码都被优化到了类的外部,它们不占据存储空间

           Android NDK对C++特性的支持(app_stl)
                 system
                 gabi++(rtti)=>gabi++_static/gabi_shared
                 stlport(rtti/stl)=>stlport_static/stlport_shared
                 gnustl(c++异常/rtti/stl)=>gnustl_static/gnustl_shared

      4. Android NDK JNI API逆向分析
           Android NDK提供了那些函数
                 Linux C/C++
                 Android NDK<=>JNI接口<=>java
                 JNINativeInterfasce(jni本地接口)
                 JNIInvokeInterface(jni调用接口)
                 移植/保护核心代码

           如何静态分析Android NDK程序
                 file=>load file=>parse c header file=>jni.h=>structures

 

 

Android 与 ARM 处理器:

      ARM 处理器架构概述

      ARM 处理器家族

      Android 支持的处理器架构:ARM / x86 / MIPS

 

原生程序 与 ARM汇编语言:

      原生程序逆向初步

           代码混淆技术

           ARM指令集

           堆栈指针寄存器sp / 堆栈寻址指令 ( stmfd压栈 / ldmfd出栈 )

           存储器访问指令 ( str写入数据到存储器中 / ldr从存储器中读取数据到寄存器 )

           数据处理指令(mov) / 带链接跳转指令(bl)

 

      原生程序的生成过程 ( 交叉编译 / 跨平台编译 )

           预处理(预处理指令) 编译 汇编 链接

 

     必须了解的 ARM 知识

           汇编:芯片,高级语言的逆向,中间语言

           分析和修改汇编指令:赋值、跳转、算术运算、移位运算、堆栈操作、内存读写指令、函数调用约定

           Thumb(16位)、Thumb2(32位)、ARM(32位)

           用户模式(usr):

           不分组寄存器(R0-R7)

           分组寄存器(R8-R14)

           传递参数与返回值(R0-R3)

           保存栈顶地址(R13/SP)

           保存函数的返回地址(R14/LR)

           程序计数器R15(PC)

           状态寄存器CPSR

           ARM 处理器:ARM 状态 ( 执行32位对齐指令的ARM指令 ),Thumb 状态 ( 执行16位对齐的Thumb指令 )

 

ARM汇编语言程序结构:

     完整的 ARM 汇编程序

     处理器架构定义

     段定义

     注释与标号

           @    => 单行注释

           /**/   => 多行注释

     汇编器指令

 

子程序 参数传递

            R0 - R3 这4个寄存器用来传递函数调用的第1到第4个参数,超出的参数通过堆栈来传递
            R0 寄存器 同时用来存放函数调用的 返回值
            被调用的函数在返回前无需恢复这些寄存器的内容

 

ARM 指令集:

  • 立即寻址 (mov r0,#0x123)
  • 寄存器寻址 (mov r0,r1)
  • 寄存器移位寻址 (mov r0,r1,lsl #0x2)
               LSL  ( 逻辑左移 )
               LSR ( 逻辑右移 )
               ASR ( 算术右移 )
               ROR ( 循环右移 )
               RRX ( 带扩展的循环右移 )
  • 寄存器间接寻址 (ldr r0,[r1])
  • 基址寻址 (ldr r0,[r1,#-4])
  • 多寄存器寻址 (ldmia r0,{r1,r2,r3,r4})
  • 堆栈寻址 (stmfd sp!,{r1-r7,lr}(入栈保护)  /  ldmfd sp,{r1-r7,lr}(出栈恢复))
               ldmfa   /  stmfa
               ldmea  /  stmea
               ldmfd   /  stmfd
               ldmed  /  stmed
  • 块拷贝寻址
               ldmia   /  stmia
               ldmda  /  stmda
               ldmib   /  stmib
               ldmdb  /  stmdb
  • 相对寻址:相对寻址以程序计算器 pc 的当前值作为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数有效地址ARM与Thum指令集

 

     指令格式

           :指令助记符
           {}:执行条件
           {s}:是否影响cpsr寄存器的值
           {.w}:指令宽度说明符.w表示是32位
           {.n}:指令宽度说明符.n表示16位
           :目的寄存器
           ,:第一个操作数寄存器
           {,}:第二个操作数:立即数、寄存器、寄存器移位

 

安卓逆向 和 手游辅助 学习 路线_第1张图片

           eq:相等/z=1
           ne:不相等/标志z=0
           hi:无符号数大于/c=1,z=0  
           cs/hs:无符号数大于或等于/c=1
           cc/lo:无符号数小于/c=0 
           ls:无符号数小于或等于/c=0,z=1
           gt:有符号数大于/z=0,n=v  
           ge:有符号数大于或等于/n=v
           lt:有符号数小于/n!=v  
           le:有符号数小于或等于/z=1,n!=v
           mi:负数/n=1          
           pl:整数或0/n=0
           vs:溢出/v=1
           vc:没有溢出

 

     跳转指令

           B    无条件跳转
           BL   带链接的无条件跳转
           BX   带状态切换的无条件跳转
           BLX  带链接和状态切换的无条件跳转

 

     寄存器访问指令

           ldr(<-) / ldrd
           str(->) / strd
           ldm(->)
           stm(<-)
           push(入栈) / pop(出栈)
           swp

 

     数据处理指令

           数据传送指令(mov)  /  数据非传送指令(mvn)

           算术运算指令

                 add/adc         
                 sub/rsb/sbc/rsc
                 mul/mls/mla   umull/umlal smull/smlal smlad/smlsd
                 sdiv/udiv
                 asr(算术右移指令)

           逻辑运算指令

                 and/orr/eor   逻辑与/逻辑或/逻辑异或
                 bic(位清楚指令)
                 lsl/lsr   逻辑左移/逻辑右移
                 ror/rrx   循环右移/带扩展的循环右移

           比较指令

                 cmp / cmn / tst / teq

      其他指令

           nop ( 空操作指令 )
           swi ( 软中断指令 )
           mrs ( 读状态寄存器指令 )
           msr ( 写状态寄存器指令 )

 

ARM 指令机器码:

B / BL 指令
                00001BD0  BEQ loc_1c04
                31-28:cond=>0000     
                27-25:101               
                25-24:L=>B:0 /BL:1 
                23-0:offset:目标地址与该指令的相对偏移
                offset:(1c04-(1bd0+8))/4=1011
                0000 101 0 000000000000000000001011
                0A 00 00 0B
                00001BD0 0B 00 00 0A

LDR / STR 指令

                00001BC0 LDR R2,[R12]
                00001BC0 00 20 9C E5
                E5 9C 20 00
                1110 01 011001 1100 0010 000000000000
                cond    1110
                指令标识   01
                IPUBWL   011001
                Rn:R12    1100
                Rd:R2      0010
                offset:0    000000000000

MOV 指令

                MOV R1, #0x56000000
                56 14 A0 E3
                E3 A0 14 56
                1110 00 1 1101 0 0000 0001 0100 01010110
                Thumb 指令
                CPSR 寄存器
                T位:1-thumb,0-arm

 

 

IDA Pro 工具介绍:

 

     交互式反汇编器,是典型的递归下降反汇编器。

     导航条

           蓝色 :表示常规的指令函数
           黑色 :节与节之间的间隙
           银白色 :数据内容
           粉色 :表示外部导入符号
           暗黄色: 表示 ida 未识别的内容

     IDA主界面

           IDA View三种反汇编视图:文本视图、图表视图、路径视图
           Hex View 十六进制窗口
           Struceures 结构体窗口

Enums 枚举窗口

Imports 导入函数窗口

           Exports 导出函数窗口
           Strings 字符串窗口

IDA常用功能及快捷键:

     空格键: 切换文本视图与图表视图
     ESC   : 返回上一个操作地址
     G     : 搜索地址和符号
     N     : 对符号进行重命名
     冒号键: 常规注释
     分号键: 可重复注释
     Alt+M : 添加标签
     Ctrl+M: 查看标签
     Ctrl+S: 查看段的信息
     代码数据 的 切换:
               C  =>  解析成 代码
               D  =>  解析成 数据   
               A  =>  解析成 ascii 字符串 
               U  =>  解析成 未定义的内容
               p  =>  解析成 一个函数
     X     : 查看交叉应用
     F5    : 查看伪代码
     Alt+T : 搜索文本
     Alt+B : 搜索十六进制

 

IDA 静态分析:

        导入 jni.h 分析 jni 库函数

        拷贝 伪C代码反汇编 窗口:右键  =>  copy to assembly    # 把伪 c代码复制到反汇编窗口的汇编代码。

        IDA 可以修改 so 的 hex 来修改 so,edit  =>  edit-patchrogram

        在这里建议使用 winhex 来实现。

 

 

IDA 动态调试:

 

IDA 调式界面

  • 1. 反汇编窗口
                 C   将当前地址处的 数据 解析成 代码
                 P   将当前地址处的 数据 解析成 函数
  • 2. 十六进制窗口。编辑内存数据和代码
  • 3. 寄存器窗口。修改寄存器的值
  • 4. 模块窗口。模块路径和地址
  • 5. 线程窗口
  • 6. 栈窗口
  • 7. 输出信息窗口

 

IDA 调式常用功能

  • 1.断点和运行。
         设置断点 F2
         设置断点不可用 Disable breakpoint
         编辑断点 Edit breakpoint
         删除断点 Delete breakpoint
         继续运行 F9
         查看当前所有断点 Ctrl+Alt+B
  • 2.单步调式。
         单步步入 F7
         单步步过 F8
         运行到函数的返回地址 Ctrl+F7
         运行到光标处 F4
  • 3. IDC 脚本
         static main(void)
         {
               auto fp, dexAddress, end, size;
               dexAddress = 0x77607640;
               size = 0x19E118;
               end = dexAddress + size;
               fp = fopen("D:\\classes.dex", "wb");
               for ( ; dexAddress < end; dexAddress++ )
               fputc(Byte(dexAddress), fp);
         }
  • 4. 修改内存数据。
  • 5. 修改寄存器。修改PC寄存器时,最好在跳转指令改,如果对程序流程掌握很好,可以随便PC,只要程序不出错
  • 7. NOP函数或代码。
         NOP函数MOV R0,R0(00 00 A0 E1/00 1C)
         清空指令(00 00 00 00/00 00)
         函数头直接返回mov pc,lr(0E F0 A0 E1/F7 46)
  • 8.改变执行流程。
         修改寄存器的值
         修改跳转指令

注意:修改内存修改代码 的时机选择不同,

  • 因为修改内存和寄存器,必须调试到某一处,才能让寄存器和内存是想要的值,此时修改数据的值一般对程序影响不大,
  • 而修改代码就不同了,如果已经走到了那一句代码,才去修改代码,是要报错的,提前在它运行到前面1-2条指令时修改代码。原因是PC指令预读三级流 水线:
               1. 读取指令
               2. 解析指令
               3. 执行指令
               ADD R6,PC,R6 =>R6=PC+R6=847C+1840=9CBC+8? 9CC4

 

 

JNI_OnLoad 下断方法技巧

 

so 内寻找
基址 + 偏移

dvmloadnativecode 函数
dlsym(handle,"jni_onload")

.init 和 .init_array 下断技巧

linker 大法,先勾选三项,F9运行,再执行jdb,当目标so被加载时通过基址+偏移下段

 

 

APK 保护策略:

 

Java层混淆、资源混淆、SO层混淆(ollvm)

签名验证、文件验证、整包验证

本地验证、服务器验证、综合验证

本地验证
        java层验证
        so层验证
网络验证
        java层验证
        so层验证
通信/实时控制
        服务器
        判断验证
去签名验证
        PackageManager
        getAppSignature
        签名hash值
        signatures

 

 

反调试:

        java层Debug类的isDebugged方法
        
        native层的isDebugged方法
        
        检测 status文件的TracePid
        
        检测开放端口
        
        inotify文件监控/proc/pid/maps等文件打开事件与检测其中内存运行的文件gettimeofday时间循环检测
        
        fork父子循环检测
        
        异常触发、非法指令、引入与异常信号陷阱
        
        检测代码暂停时间

 

 

反-反调试:

        修改java层isDebugged方法返回
        
        利用py脚本HOOK native层Debug方法
        
        HOOK open、fopen函数动态下断修改TracePid的值或修改为过反调试的内核
        
        利用其他端口调式
        
        修改内核改变文件打开事件响应与修改打开/proc/pid/maps后内存运行文件结果
        
        HOOK gettimeofday函数让其不能循环
        
        修改内核fork方法与多IDA挂起kill19等方法调式
        
        修改异常指令信号与识别非法指令使用异常HOOK
        
        HOOK 常用获取时间的系统函数
        
        修改检测时间,让其只检测一次

 

 

常见加密算法:

 

古典密码学(以字符为基本加密单元)
        移位密码/错位密码
        替代密码/置换密码
现代密码学(以信息块为基本加密单元)
        哈希算法
        非对称加密与解密
        椭圆曲线
        数字签名
        数据分组与校验
散列函数:用于数据完整性校验
        MD  消息摘要算法
        SHA 安全散列算法
        MAC 消息认证算法
        CRC 循环冗余校验算法

对称加密算法:加密密钥 == 解密密钥
流密码:指加密时每次加密一位或一个字节的明文。
        同步流密码
             同步性 无错误传递性 主动攻击性
             音频/视频数据提供版权保护
        自同步流密码
             自同步性 错误传递有限性
主动攻击性 明文统计扩散性     
        RC4/SEAL
分组密码(http://www.seacha.com/tools/):指加密时将明文分成固定长度的组,用同一密钥和算法对每一块加密,输出也是固定长度的密文。
        常用于网络加密
        安全性=>扩散/混乱原则
        实现性=>软件/硬件
        ECB 电子密码本模式
        CBC 密文链接模式
        CFB 密文反馈模式
        OFB 输出反馈模式
        CTR 计数器模式
DES
        DESede/3DES/Triple DES
        AES
        TEA
        DEA    
        PBE

非对称加密算法 :加密密钥 != 解密密钥
公钥==>对外公开,私钥==>对外保密。
私钥加密==>公钥解密,公钥加密==>私钥解密
DH 密钥交换算法
基于因子分解
   RSA 数据加密/解密 数字签名
基于离散对数
   ElGameal 数据加密/解密 数字签名
   DSS数据签名标准=>DSA 数字签名
   ECC 椭圆曲线加密算法
使用对称加密算法对数据进行加密与解密,使用非对称加密算法对对称加密算法所使用的密钥进行加密与解密。
数字签名
私钥==>签名
公钥==>验证
单向认证
双向认证
RSA
DSA
ECC+DSA==>ECDSA 椭圆曲线数字签名算法
认证/鉴别服务
数据完整性服务
抗否认性服务
数字证书
消息摘要算法
对称加密算法
非对称加密算法
数字签名
鉴别/认证
数据保密性
数据完整性
抗否认性
证书管理
KeyTool
         构建自签名证书
         构建CA签发证书
OpenSSL
         根证书
         服务器证书
         客户端证书
密钥库==>管理私钥
数字证书==>管理公钥
加密/解密
签名/验证
X.509
服务器数字证书

 

 

协议加解密分析:

 

        协议与安全协议 ( 服务器协议关键,就是先抓包,再解密 )        
        概念:协议就是服务器与客户端交互信息的一种规则        
        客户端和服务器连接:实质都是连接服务器的IP地址和开放端口        
        POST:客户端提交数据给服务器        
        GET:客户端获取服务器数据        
        http,ftp,smtp: 应用层        
        tcp,udp: 传输层        
        IP:网络层        
        帧相关协议:数据链路层        
        HTTP/HTTP2        
        HTTPS协议  =>  https是基于sll/tls的http协议        
        ssl协议  =>  安全套接字层        
        tls协议   =>  传输层安全        
        socket    => tcp/udp=>http/ftp  

 

 

抓包工具:

 

        http / https
                fiddler/charels
        
        socket/tcp
                抓网卡
                wireshark / sniffer
        
        防止代理
          小米wifi + wpe

     

抓不到包原因:

        1. 如网游副本,有的就是没有包;        
        2. fiddler 只能抓 HTTP/HTTPS,如果是 socket 抓不到的;        
        3. 在动态调试时,没有走到发包的地方。

 

tcpdump + Wireshark:

adb shell tcpdump -p -vv -s 0 -w /sdcard/capture.pcap 开始抓包

ctrl+c    结束抓包

adb pull /sdcard/capture.pcap 导出文件

 

 

Dex文件结构:

 

1. dex文件中的数据结构

      u1/uint8_t     =>  表示1字节的无符号数
      u2/uint16_t    =>  表示2字节的无符号数
      u4/uint32_t    =>  表示4字节的无符号数
      u8/unit64_t    =>  表示8字节的无符号数
      sleb128        =>  有符号leb128,可变长度为1-5字节
      uleb128        =>  无符号符号leb128,可变长度为1-5字节
      uleb128p1      =>  无符号leb128值加1,可变长度为1-5字节

2.dex文件整体结构

              struct DexFile {
                    DexHeader
                    DexStringId
                    DexTypeId
                    DexProtoId  //对 DexType 进一步说明
                    DexFieldId
                    DexMethodId
                    DexClassDef
                    DexData
                    DexLink
              }
        以索引为线索

3. DEX 的内存映射

        与静态类似,只是变为 xxxItem 结构        
        ClassObject 结构由六个部分组成:   
              PDvmDex:        // DEX文件字段    
              super:          // 超类   
              sfields:        // 对应DexClassData结构中的staticFields静态字段     
              iFields:        // 对应DexClassData结构中的instanceFields实例字段      
              directMethods:  // 对应DexClassData结构中的directMethods直接方法字段  
              virtualMethods: // 对应DexClassData结构中的virtualMethods虚方法字段

DexClassDef :  class_def_item  
DexClassData:  class_data_item    
DexFiled(staticFields): sfileds     
DexFiled(instanceFields): ifileds        
DexMethod(directMethods):directMethods    
DexMethod(virtualMethods):virtualMethods       
DexCode: code_item 

 

 

ELF文件结构:

      linux elf 文件、windows pe文件

      Android 操作系统内核采用 Linux 内核框架实现  Android ELF文件

 

ELF文件整体结构:

      ELF Header            ---> ELF文件头的位置是固定的
      Segment Header Table  ---> ELF程序头描述的是段的相关信息
      .init
      .text
      .rodata
      .data
      .symtab  符号表
      .line        
      .strtab  字符串表
      Section Header Table  ---> ELF节头表描述的是节区的信息
动态用段,静态用节

     

readelf 的使用:

        -a
        -h
        -l
        -S
        -e
        -s

 

arsc 文件:

arsc 文件解析

xml 文件:

xml 文件解析及工具

xml 加密

 

xml 和 arsc 简单十六进制加密:

以 arsc 为例:在0x00000028h的文件偏移地址之前进行修改,如改头      部第一个HEX操作数为00。搜索字符,'1c' 搜索3下。把1c前面的   01改00,就可以了。看020行,头部字节流,把尾部的00改为和头 部一样。

 

思考:ARSC、xml文件很难去做单纯的动态加载,如DEX,这也是文件根本性质决定的,还有一个原因,就是安装后这个APK必须要打开。我们最多能做到的,在动态上(自然这就比外部混淆、十六进制加密要强些),就是用壳的apk,然后结合APK动态加载,让真实APK的资源文件是正确的,可以考虑动态代码生成(如动态生成真实APK的配置清单xml,而在asset文件夹下的APK压缩包中并没有配置清单xml),或者解密替换(这就分两种,你是保护xml\arsc文件,还是那些png等外部资源,当然原理一样,文件的加密解密)等。事实是,DEX和APK的动态加载往往必须要考虑资源怎么办的问题,解决方法往往是资源保护的一个反面,将动态加载需要的资源文件中数据先写在壳的资源文件中。毕竟,在代码与资源保护直接抉择,肯定是保护代码优先。

 

 

DEX类加载过程解析:

 

http://androidxref.com/

1. DEX文件优化与验证:

run_dexopt:

static const char* Dex_OPT_BIN = "/system/bin/dexopt"

// 读取和抽出dex,加上odex文件头,设置优化选项,可以看作DEX文件优化的主控函数
\dexopt\Optmain.cpp:extractAndProcessZip() 

// 写入odex文件,可以说优化与验证工作的完成就是写入odex文件
\vm\analysis\DexPrepare.cpp:dvmContinueOptimization()

 

2.DEX文件解析:

\vm\RawDexFile.cpp:dvmRawDexFileOpen()  // DEX文件解析的主控函数
\libdex\OptInvocation.cpp:dexOptGenerateCacheFileName() // 生成该dex对应odex文件
\vm\DvmDex.cpp:dvmDexFileOpenFromFd()   // 完成对DEX文件映射,设置为只读文件,并进一步优化
\libdex\DexFile.cpp:dexFileParse()      // 解析DEX

 

在实践中,我们发现并不是所有的dexfileopenpartial都能断下来,但 _Z27dexOptGenerateCacheFileNamePKcS0、dvmdexfileopenfromfd、dexfileparse 这些函数一定能断下来,而 dexfileopenpartial 常常在有壳时,也就是真正 dex 出现时才可以断下来,由此可知,其实 dalvik 很喜欢走的路就是用 odex,而不是 dex。dex 文件只有在 odex 文件不好用时才去用,这与 oat 文件一致。

 

3. DEX类加载

\vm\native\dalvik_system_DexFile.cpp:
Dalvik_dalvik_system_DexFile_defineClassNative // 类加载的主控函数
\vm\oo\Class.cpp:dvmDefineClass()       // 确认类加载描述符进行类加载
\vm\oo\Class.cpp:findClaaNoInit()           // 完成实际类加载工作
\vm\oo\Class.cpp:loadClassFromDex()   // 返回值是一个ClassObject结构体
往后便是 FindClass、GetStaticMthodID、CallStaticMthod、dvmInterpretStd

 

 

so 的加载、链接、执行过程解析:

 

begin.S:__linker_init

  • 1.dlopen函数。dlfcn.cpp
  • 2.整体流程:
            linker.cpp       
            do_dlopen函数:        
            find_library函数:完成对so的解析装载、分配、链接        
            CallConstructors函数:完成对so中.init和.initarray的执行
  • 3.先看 CallConstructors函数,从后面的执行来看
            CallFunction("DT_INIT", init_func);        
            CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
  • 4.回到 find_library 函数,看 so 的具体加载
            find_library_interna
            load_library 是整个 so 加载的主控函数

 

5. so 加载 的 详细步骤

解析装载:
        elf_reader.Load函数包括了下列函数:
                 ReadElfHeader() &&
                 VerifyElfHeader() &&
                 ReadProgramHeader() &&
                 ReserveAddressSpace() &&
                 LoadSegments() &&
                 FindPhdr();
        所以, so的装载是一种解析式装载,这与dex有一定区别,dex是先加载进行优化验证生成odex,再去解析odex文件,

         而so更像边解析边装载,在装载过程中主要解析是load段。


分配 soinfo:
        soinfo_alloc函数,生成了一个soinfo结构

链接:
        soinfo_link_image函数,也就是soinfo结构链接镜像,从而得到一个以后用的镜像文件
        so中用于动态链接的结构就是.dynamic节,即动态节区
        a.定位动态节
        b.解析动态节
        c.加载依赖so
        d.重定位:
        主要函数为soinfo_relocate,此函数会被soinfo_link_image函
        数调用解析重定位项:.rel(.plt安卓并不用),重定位表
        导入符号信息:.dynsym, 符号表修正需要重定位的地址

 

DEX 类加载过程在加固脱壳应用:

        1. dump dex,脱壳。
        2. 内存动态替换 dex,dex 自修改,自调用底层函数解析dex。
        3. dex 自解析重构,完成 dex 重组脱壳。

 

Linker 在加固脱壳应用:

1.在.init和.intarray下断,若.initarray节存在dex解密,dump dex,也就是dex脱壳。
2.dump so再修复,也就是so脱壳,一般选择在jni_onload。
3.因为在.init和.intarray运行之后,如果有jni_onload,就调用,变相断jni_onload。
4.自定义linker。
5.HOOK dlopen等函数,判断是否加载了so,如果加载就解密,同理在整个so的装载、链接过程中可以对so进行变形后的修复,so解密脱壳最迟要在jni_onload,当然混淆等基于内部保护的不在考虑之列。

 

DEX文件加固:

        DEX 文件结构变形
        DEX 文件结构拼接隐藏
        DEX 文件整体加密

 

ELF 文件加固:

        ELF文件结构变形
        so文件结构拼接隐藏
        so文件反汇编注入
        so文件整体加密、函数加密、区段加密、加壳

 

 

Apk保护:

 

1.app 存在的危险

      代码修改(广告植入、替换广告id)
      资源修改(界面替换广告、链接替换)
      破解(应用收费、内购)
      篡改数据(无限金币、钻石)
      加入恶意代码(木马、隐私、交易)
      动态注入(数据拦截、窃取、修改)
      本地数据修改、数据库文件修改
      协议修改(服务器欺骗、向服务器发送假数据)

 

2.加固目的

      需要防止逆向分析(防逆向)--防止核心代码被反编译
      防止二次打包(防篡改)--校验完整性、签名、防止盗版
      防止调式和注入(防调试)--防止动态调式,注入获取关键数据
      防止应用数据窃取(防窃取)--加密敏感数据
      防止协议直接被盗刷--加密协议通信

 

3.常见加固厂商

      360/娜迦/梆梆
      爱加密/阿里
      百度/腾讯/网秦/通付盾

 

4.常用加固方式

      类加载技术
            针对apk中的classes.dex文件进行处理,放入特定的文件中,通过native代码来对其运行时解密
            使用厂商(娜迦/爱加密/梆梆)
            对原dex文件整体压缩加密,保存在壳代理的dex文件尾部,加载到内存中解密运行
            使用厂商(360)
      方法替换技术
            将classes.dex文件中的方法代码进行提取,抽取方法,在运行时对其进行动态解密还原
            使用厂商(娜迦/梆梆)

           

5.加固厂商特征

      娜迦:libchaosvmp.so, libddog.so libfdog.so
      梆梆:libsecexe.so, libsecmain.so, libSecShell.so
      梆梆企业版:libDexHelper.so, libDexHelper-x86.so
      爱加密:libexec.so, libexecmain.so, ijiami.dat
      360:libprotectClass.so, libjiagu.so; libjiagu.so, libjiagu_art.so; libjiagu.so, libjiagu_x86.so
      百度:libbaiduprotect.so
      阿里聚安全:aliprotect.dat, libsgmain.so, libsgsecuritybody.so 
      腾讯:libtup.so, libexec.so, libshell.so; mix.dex; lib/armeabi/mix.dex, lib/armeabi/mixz.dex  
      腾讯御安全:libtosprotection.armeabi.so, libtosprotection.armeabi-v7a.so, libtosprotection.x86.so
      通付盾:libegis.so, libNSaferOnly.so
      网秦:libnqshield.so
      网易易盾:libnesec.so
      APKProtect:libAPKProtect.so
      几维安全:libkwscmm.so, libkwscr.so, libkwslinker.so
      顶像科技:libx3g.so

     

6. 脱壳手法

      修改系统源码自动脱壳
      通过hook方式对关键函数进行脱壳
      开源工具如zjdroid,dexhunter进行脱壳
      利用IDA或者GDB动态调式进行脱壳     

分析 virualapp

去框架 HOOK

制作 vm 策略

实现 vm 置换

置换后销毁

 

其他

dex vmp (制作native oncreate)

典型非主流加固

脱壳机

虚拟机技术

 

 

加固技术:

 

安卓逆向 和 手游辅助 学习 路线_第2张图片

 

第一代加固 DEX 加密

      proguard 等混淆、其他弱加密
      DEX字符串加密
      静态DEX文件整体加密解密
      资源加密(xml与arsc文件加密及十六进制加密)
      对抗反编译(添加垃圾类)
      反调试
      自定义DexClassLoader

 

第二代加固 DEX抽取与SO加固

      DEX动态加载(分为利用jni和自定义jni即自定义底层函数)
      DEX代码抽取到外部(类抽取加密按需解密和动态方法修改替换)
      SO加密

 

第三代加固 DEX动态解密与SO混淆

      Dex代码动态解密

      SO代码膨胀混淆

     

第四代加固 arm vmp

      jni反射抽取方法成本地原生代码即半vmp(分为jni反射解密DEX方  法和完全翻译为C代码)
      初级vm即dex opcode vm指令虚拟使用jni指令还原(分为自定义后      端编译器和jni反射置换表)
      高级vm即解释框架dex opcode不存在还原使用解释引擎的vm文件

 

脱第一代壳:

内存Dump法
      内存中寻找dex.035或者dey.036
      /proc/xxx/maps中查找后,手动Dump
文件监视法
      Dex优化生成odex
      监视文件变化(inotifywait-for-Android)
      监视DexOpt输出(notifywait-for-Android)
Hook法
      Hook dvmDexFileOpenPartial
定制系统
      修改安卓源码并刷机

 

脱第二代壳:

内存重组法
      Dex ZjDroid
            对付一切内存中完整的dex,包括壳与动态加载的jar
      SO elfrebuild
            构造soinfo,然后对其进行重建

Hook法
      针对无代码抽取且Hook dvmDexFileOpenPartial失败
            Hook dexFileParse
      针对无代码抽取且Hook dexFileParse失败
            Hook memcmp   

定制系统
      修改安卓源码并刷机-针对无抽取代码
      DexHunter
      绕过三进程反调试-修改系统源码
      断点mmap调试,针对Hook dexFileParse无效
      dexopt优化时,dvmContinueOptimization()->mmap()

静态脱壳机
      分析SO壳逻辑并还原加密算法
      自定义linker脱SO壳

 

脱第三代壳:

dex2oat    
      ART模式下,dex2oat生成oat时,内存中的DEX是完整的

定制系统
      Hook Dalvik_dalvik_system_DexFile_defineClassNative
      枚举所有DexClassDef,对所有的class,调用dvmDefineClass进行   强制加载

 

开源脱壳机参考( 1 之 3 代 壳 )

  • 一代壳:ZjDroid(2014)。最早一批的通用脱壳工具,利用Xposed注入,实时对内存dex文件进行baksmali与smali。自带各类辅助性功能,代码写得很好,值得一看。
        https://bbs.pediy.com/thread-190494.htm
        https://github.com/halfkiss/ZjDroid.git
  • 二代壳:DexHunter(2015)。首次提出的通过修改系统镜像实现的针对二代壳脱壳功能,自带主动类加载功能,在当时有效对抗各类加固。从这代开始,脱壳机开始涉及到Android源码修改了,研发难度有所上升。
        https://bbs.pediy.com/thread-203776.htm
        https://github.com/zyq8709/DexHunter.git
  • 三代壳:FUPK3(2018)。dexhunter 的改进版,首次把脱壳的颗粒度上升到函数维度,配合主动函数调用,能破掉大部分壳的函数抽取功能。
        https://bbs.pediy.com/thread-246117.htm
        https://github.com/F8LEFT/FUPK3
  • FART(2019)。脱壳思路类似于Fupk3的通用脱壳机,目标运行平台修改为art。在脱壳上,art虚拟机要比dalvik虚拟机简单得多,所以该版本能更有效的进行脱壳。可惜的是作者没有提供指令回填程序。
        https://bbs.pediy.com/thread-252630.htm
        https://github.com/hanbinglengyue/FART
  • Youpk(2020)。新出的脱壳机,实现上更为成熟。在 FART上更进一步发展,主动函数调用回调进一步深入到解析引擎上,可以控制函数执行的流程,并附带了一键化指令回填。到此为止,基本上终结了三代壳的生命。( https://bbs.pediy.com/thread-259854.htm )

脱壳原理讲解

  1. apk格式
  2. dex文件格式(代码文件)
  3. 系统代码加载流程
  4. 类抽取脱壳思路
  • 参考链接:https://bbs.pediy.com/thread-260052.htm

 

脱第四代壳

so + vmp
阶段发展(换个角度,要从解密后dex文件的状态来看)
静态:dex文件解密后以静态文件存储(如某一个文件夹下的odex文件)
动态:dex文件解密后也在内存中
整体加密:真实、完整的dex出现在内存中
抽取加密:真实、完整的dex从不出现在内存中
对于整体加密和抽取加密,从实现来看,整体加密就是把dex整体隐藏在so中,而抽取加密就是先从so里解密出一个待修复的dex文件,真正的dex从这个文件里抽取部分。

 

为什么 dex 从内存中抠出是不完整的?

1.dex内存中不连续(如修改DexHeader结构)
2.dex在内存中执行是以结构体存在的,如果修复时只修复结构体,根本没有修复dex/odex文件本身(如只修复DexMethod结构体)
3.dex文件的一些方法不是事先解密,而是用到再解密,用完就删掉(如百度加固的onCreate方法)
4.dex文件在加载或运行时,被HOOK,解密后,使之一些结构体或一些结构体的一些字段人为改为错误,或删除(如阿里加固对于annotionoff注解字段的处理)
5.dex文件本身就是错的,结构体的字段值错误,或方法是干扰无效的,在加载或运行时,被HOOK,解密修复成正确的,解密后再删掉或继续用错误部分填充。

 

三代脱壳机实现与关键函数:

        规律:无论加固还是脱壳,都往底层发展。
        1.dex优化与验证过程(实例:在dexFileParse函数HOOK)
        2.虚拟机动态加载打开dex过程
        3.dex类加载的过程

 

脱壳时机总结(DEX文件从打开到方法指令执行前):

        1.打开dex文件时
        存在dex优化验证时的打开和虚拟机本地native打开两种,分别对应着一代和二代脱壳。
        
        2.加载类时:defineclassnative函数
        虽然类加载过程的函数很多,但我们一般脱壳只在defineclassnative函数。
        这里列举出类的整个加载过程的函数
              defineclassnative函数分析与类加载概念
              defineclass函数分析
              findnoinit函数分析(包括了dvmLookupClass)
              loadClassFromDEX函数分析
              loadClassFromDEX0函数分析与解析概念
              dvmLinkClass函数分析
              loadMethodFromDEX函数分析
        3.类的初始化时:dvmIsClassInitialized和dvmInitClass这两个函数
        4.查找类FindClass
        5.获得方法ID并调用方法 GetStaticMethodID,CallStaticVoidMethod
        (其实在GetStaticMethodID执行了类的初始化,可以尝试从这里)
        方法指令执行(一般不考虑)

 

DEX自解析重构技术:

DexHunter介绍
DexHunter使用方法与注意
DexHunter源码详细分析
/dalvik/vm/native/dalvik_system_DexFile.cpp
https://github.com/zyq8709/DexHunter
https://github.com/kesuki/DexHunter

如果你想脱壳一个APP,在运行APP之前,你需要把“dexname”这个文件推入到手机的“/data/”文件夹下。
在"dexname"的第一行是特征字符串(参照"slide.pptx").
第二行是目标APP所在的数据路径(例如,/data/data/com.example.seventyfour.tencenttest/)。
dexname文本必须是linux/Unix的风格形式,如果在windows下输入,则需要用winhex修改换行符的十六进制。
你可以使用"logcat"命令获得log日志来判断壳是否已经脱下。
一旦完成,生成的"whole.dex"文件就是想要的结果。


特征字符串:
360        /data/data/XXX/.jiagu/classes.dex
Ali        /data/data/XXX/files/libmobisecy1.zip
Baidu      /data/data/XXX/.1/classes.jar
Bangcle    /data/data/XXX/.cache/classes.jar
Tencent    /data/app/XXX-1.apk (/data/app/XXX-2.apk)
ijiami     /data/data/XXX/cache/.

 

注意,他本身的系统 img 是4.4.3,如果想做到版本通用,只需要单单替换system.img就行。一定不能把他原版的img直接文件夹替换,那样模拟器会直接死翘翘。

安卓逆向 和 手游辅助 学习 路线_第3张图片

 

 

IDA 自动插件源码分析与 dexhunter 比较:

 

相同点:

      1.本质相同,也就是最根本的自解析重构思想和方法一样,都是主要采  用readsignleb128等自解析函数,进行自解析重构;

      2.由于两种工具基本原理一样,所以需要升级,改进,和失效、缺陷的  地方,也是类似的;

      3.都是类加载时的通用脱壳机,关键原理函数都是defineClassNative    函数。

不同点:

      1.直接dump的方法不同,DexHunter用的是strcpy函数,而IDA脱 壳脚本用的是getword函数;

      2.相比来言,IDA脱壳脚本重构的更为彻底一点,它把每个结构都进行  重构,把字段全部先初始化为0,然后利用直接dump或自解析重新获    取的办法填充回去;

      3.IDA脱壳脚本得到的是一个ODEX,而DexHunter是一个DEX文      件,IDA脱壳脚本还重构了DEXHEADER头结构,修复了magic等字      段,这一点也比DexHunter较好。

      4.使用的方式不同,DexHunter使用的方式是HOOK,而且对类进行了      主动一次性加载和初始化,而IDA脱壳脚本是一个脚本,其当初设计的 想法应该是当动态调试时,得到了正确的clazz结构体后,去使用。

      5.总的来说,IDA脱壳脚本比DexHunter显得先进一点,但依然不是更      进一步彻底的重构,如annotionoff字段就是直接dump,但这个地方 往往会偏移出错,如阿里加固。

 

DexHunter 升级:

  1. 它是用DEXHEADER结构的header.stasticsize等来获取静态字段数      量,然而我们初始化类后,只能保证clazz结构是正确的,我们应该尽      量用clazz结构中的内容。
  2. 对于百度加固来说,由于真实指令只在运行时才出现,此时重构过早。      试想当classdef都是不完整情况下,得到的data数据区肯定也是缺失      的,因为data数据是代码的真实数据区。DEXHUNTER采用直接              dump data区明显是不行的,我们先把oncreate001中指令骗出来,       再去dump data,接着重构。
  3. DEXHUNTER采取以修复为主的重构,明显是不彻底的,其可以看到在   dump时,DexClassDefItem结构中的anonotionoff等偏移也存在错      误,我们要利用重构的办法对这个地方也进行重构。

 

 

脱壳神器 ZjDroid:

 

1.介绍

zj 是一个比较特殊的通用脱壳工具,是第一个利用系统组件漏洞来进行脱壳的脱壳机,也就是发广播,当zj接受到正确广播后,会根据pid,把广播发给pid,另pid进程返回信息。zj之所以特殊,它无法按照我说的划分为第几代脱壳,乃至现在依然可以风骚使用。这对于一个2014年就诞生的脱壳机而言是不可思议的。zj的核心是mcookie文件,而这个文件却介于解密后文件和解密后内存之间的中间状态。如果我们深入系统源码,就会发现cookie会被直接用于类结构体的生成,如果按照此定义,它应属于第2代-第3代脱壳机。但奇妙之处在于,只要存在解密,无论放不放在文件,也无论是解密的是dex还是jar、odex,理所应当都会有一个cookie出现。因此,除非真正的vmp,即完全通过编译器so壳去解释一个完全虚拟的文件或内存中的指令,否则zj都会有效果。zj很强大,但缺点也很明显,太容易被防。如果壳做好了系统组件保护策略,不允许第三方广播,或者对xpossed框架和类似zj插件进行保护,那么zj就无效。

 

2.zj 使用全攻略:

zj 不适合模拟器,适合真机,我们的调式机就非常理想,不同机器,运行效率差别很大。

  • 安装 xpossed 框架:
            adb install d:\xpossed.apk
            安装框架,软重启
  • 安装zj插件:
            adb install d:\ZjDroid.apk
            安装插件,勾选模块,软重启
  • 安装目标游戏:adb install d:\捕鱼达人3.apk
  • 打开一个 cmd,专门用于查看动态逆向信息:
            adb shell
            logcat | grep zjdroid-shell-org.cocos2d.fishingjoy3
            记下PID,建议一定要把命令先写好
  • 再打开一个cmd,运行 zj 命令
    adb shell
    su
    查看内存中存在的 dex 的 cookie 信息:
    am broadcast -a com.zjdroid.invoke --ei target 4755 --es cmd '{action:dump_dexinfo}'
    观察log的cmd窗口,记下选择的dexpath信息,如果有dex就选dex,其次是jar,最后是apk
    查看cookie的可加载类,顺便看看是否有自己想要的类:
    am broadcast -a com.zjdroid.invoke --ei target 4755 --es cmd '{"action":"dump_class","dexpath":"/data/app/org.cocos2d.fishingjoy3-1.apk"}'
  • dump 出 dex 文件:am broadcast -a com.zjdroid.invoke --ei target 4755 --es cmd '{"action":"dump_dexfile","dexpath":"/data/app/org.cocos2d.fishingjoy3-1.apk"}'
  • dump 出 smali 文件:am broadcast -a com.zjdroid.invoke --ei target 4755 --es cmd '{action:backsmali, "dexpath":"/data/app/org.cocos2d.fishingjoy3-1.apk"}'
  • 等待一段时间,查看脱壳后的dex和smali文件:
    ll /data/data/org.cocos2d.fishingjoy3/files
    ll /data/data/org.cocos2d.fishingjoy3/files/smali/
    把 smali 文件夹拷贝到sd卡,然后传到电脑:
    adb pull /mnt/sdcard/smali/ d:\1

zj另外还可以:

内存 dump,由于我们有dd,和更好的工具,一般不用 zj 去 dump内存:

adb shell am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"dump_mem","start":1234567,"length":123}'

 

运行自编写lua脱壳插件,找到解密函数,用lua调用java:

由于这需要我们编写lua代码,还要分析解密函数,如果做到了这样,我们有更好的办法去处理,一般也不用运行lua代码

adb shell am broadcast -a com.zjdroid.invoke --ei target pid --es cmd '{"action":"invoke","filepath":"****"}'

 

3. zj 拓展

做如下修改即可解除一定的防zj保护:

a.在设备上创建特定目录(如/data/local)并 chmod 为777

b.复制zjdroid.apk到该目录,并修改文件名为zjdroid.jar

c.修改/data/data/de.robv.android.xposed.installer/conf/modules.list,模块代码文件修改为zjdroid.jar,然后重启设备即可。

 

DD大法:

dd if=/dev/zero of=sun.txt bs=1 count=1

if 代表输入文件。如果不指定if,默认就会从stdin中读取输入,/dev/zero 是一个字符设备,会不断返回0值字节(\0)。

of 代表输出文件。如果不指定of,默认就会将stdout作为默认输出。

bs 代表字节为单位的块大小。

count 代表被复制的块数。

 

抠 DEX:

dex是看0x20处找大小,odex是看0x32处就有。

dd if=/proc/2175/mem of=/data/local/tmp/dump.dex skip=3077606936 bs=1 count=43576

 

GDB 调试:

adb push d:\gdbserver /data/local/tmp/gdbserver
adb shell
su
chmod 777 /data/local/tmp/gdbserver
ps | grep com.zyx.wifi
ls /proc/1964/task
/data/local/tmp/gdbserver :31928 --attach 2968
adb forward tcp:31928 tcp:31928

启动本地 gdb
target remote :31928
gcore,一直等待完成,时间比较久
    b   下断点
    c   继续运行到断点
    s   等价于step into
    n   等价于step over
    bt  查看堆栈
    info registers    查看寄存器内容
    info b            查看断点信息
    delete break [n]  删除编号为n的断点
    disas 0xFFFF,+20  显示这个地址后面 20 行内的汇编指令
    kill              终止进程

 

脱掉梆梆的壳:

梆梆是把原dex文件加密放到了secData0.jar,所以直接拿到dex文件,修复配置文件的程序入口点就可以重打包完美运行。

open、mmap

dexfileopenpartial

dvmRawdexFileOpen

 

脱掉爱加密的壳:

爱加密在fopen,fget原因

dexfileopenpartial

openDexFileNative

defineClassNative

 

脱掉360的壳:

1.识别360加固的代数

2.第一代360脱壳:xposed插件脱壳

3.第二代360脱壳:so手动dump并修复、mmap手动脱壳、open+memcmp手动脱壳

4.第三代360脱壳:drizzledumper脱壳、dex2oat脱壳

 

VM 修复:

一个结构,
fix (){
  Method
  offset
}
结合dex文件结构,opcode,偏移地址
00-FF 0-255
确定00 vm的opcode
00-A8 168
01-A9
02-AA
03-AB

 

脱掉百度的壳:

1.加密流程:

采用动态加载assets下的baiduprotect.jar。然后采用重写onCreate,用onCreate001代替,onCreate内容为修复onCreate001代码、执行onCreate001代码、清楚onCreate001代码。修复代码不能连续运行两次。

3.采用Hook DexParse来获取Dex相关数据,然后遍历ClassDef将所有onCreate001类直接解码。

4.Dump出修复好的Dex。

5.然而Dump的Dex还要修复(可以根据ClassDef自动修改)

      Lcom/baidu/protect/A;->d(Ljava/lang/String;)V->解密方法

      Lcom/baidu/protect/A;->e(Ljava/lang/String;)V->加密方法

      Lcom/qsq/qianshengqian/XXXXX;->

onCreate001(Landroid/os/Bundle;)V->加解密传入参数

xdex脱壳机与骗出jni_onload的oncreate抽取指令

vm一维置换表和二维置换表

 

脱掉腾讯的壳:

libart.so

OpenMemory

R1是dex文件的地址,R2是dex文件的大小

 

 

 

 

你可能感兴趣的:(游戏辅助)