上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372。接下来我们将介绍另一种防止apk反编译的技术-运行时修改字节码。这种方法是在工作中在实现app wrapping时,看到国外的一篇关于android 安全的介绍实现的并且独创。下面我们来介绍一下这种方法。

我们知道apk生成后所有的java生成的class文件都被dx命令整合成了一个classes.dex文件,当apk运行时dalvik虚拟机加载classes.dex文件并且用dexopt命令进行进一步的优化成odex文件。我们的方法就是在这个过程中修改dalvik指令来达到我们的目的。

一、dex文件格式

dex的文件格式通常有7个主要部分和数据區组成,格式如下:

header部分记录了主要的信息其他的部分只是索引,索引的内容存在data区域。

Header部分结构如下:

字段名称

偏移值

长度

描述

magic

0x0

8

'Magic'值,即魔数字段,格式如”dex/n035/0”,其中的035表示结构的版本。

checksum

0x8

4

校验码。

signature

0xC

20

SHA-1签名。

file_size

0x20

4

Dex文件的总长度。

header_size

0x24

4

文件头长度,009版本=0x5C,035版本=0x70。

endian_tag

0x28

4

标识字节顺序的常量,根据这个常量可以判断文件是否交换了字节顺序,缺省情况下=0x78563412。

link_size

0x2C

4

连接段的大小,如果为0就表示是静态连接。

link_off

0x30

4

连接段的开始位置,从本文件头开始算起。如果连接段的大小为0,这里也是0。

map_off

0x34

4

map数据基地址。

string_ids_size

0x38

4

字符串列表的字符串个数。

string_ids_off

0x3C

4

字符串列表表基地址。

type_ids_size

0x40

4

类型列表里类型个数。

type_ids_off

0x44

4

类型列表基地址。

proto_ids_size

0x48

4

原型列表里原型个数。

proto_ids_off

0x4C

4

原型列表基地址。

field_ids_size

0x50

4

字段列表里字段个数。

field_ids_off

0x54

4

字段列表基地址。

method_ids_size

0x58

4

方法列表里方法个数。

method_ids_off

0x5C

4

方法列表基地址。

class_defs_size

0x60

4

类定义类表中类的个数。

class_defs_off

0x64

4

类定义列表基地址。

data_size

0x68

4

数据段的大小,必须以4字节对齐。

data_off

0x6C

4

数据段基地址

dex与class文件相比的一个优势,就是将所有的常量字符串集统一管理起来了,这样就可以减少冗余,最终的dex文件size也能变小一些。详细的dex文件介绍就不说了,有兴趣的可以查看android 源码dalvik/docs目录下的dex-format.html文件有详细介绍。不过我记得在android4.0版本后就没有了这个文件。

根据上面的dex文件的格式结构,dalvik虚拟机运行dex文件执行的字节码就存在method_ids区域里面。我们查看dalvik虚拟机源码会有一个

struct DexCode {

u2  registersSize;

u2  insSize;

u2  outsSize;

u2  triesSize;

u4  debugInfoOff;       /* file offset to debug info stream */

u4  insnsSize;          /* size of the insns array, in u2 units */

u2  insns[1];

/* followed by optional u2 padding */

/* followed by try_item[triesSize] */

/* followed by uleb128 handlersSize */

/* followed by catch_handler_item[handlersSize] */

};

这样一个结构,这里的insns数组存放的就是dalvik的字节码。我们只要定位到相关类方法的DexCode数据段,即可通过修改insns数组,从而实现我们的目的。

二、odex文件格式

apk安装或启动时,会通过dexopt来将dex生成优化的odex文件。过程是将apk中的classes.dex解压后,用dexopt处理并保存为/data/dalvik-cache/data@app@<package-name>-X.apk@classes.dex文件。

odex文件结构如下:

从上图中我们发现dex文件作为优化后的odex的一部分,我们只需要从odex中找出dex的部分即可以了。

三、方法实现

要实现修改字节码,就需要先定位到想要修改得代码的位置,这就需要先解析dex文件。dex文件的解析在dalvik源码的dexDump.cpp给出了我们具体的实现,根据它的实现我们可以查找我们需要的类及方法。具体实现步骤如下:

(1)    找到我们apk生成的odex文件,获得odex文件在内存中的映射地址和大小。实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  void *base = NULL;
 
  int module_size = 0;
 
  char filename[512];
 
  // simple test code  here!
 
  for(int i=0; i<2; i++){
 
         sprintf(filename,"/data/dalvik-cache/data@app@%s-%d.apk@classes.dex""com.android.dex", i+1);
 
base = get_module_base(-1, filename);//获得odex文件在内存中的映射地址
 
  if(base != NULL){
 
         break;
 
  }
 
  }
 
 module_size = get_module_size(-1, filename); //获得odex文件大小

(2)    知道dex文件在odex中的偏移,以便解析dex文件。代码如下:

1
2
3
4
5
6
7
8
9
10
11
// search dex from odex
 
  void *dexBase = searchDexStart(base);
 
  if(checkDexMagic(dexBase) == false){
 
         ALOGE("Error! invalid dex format at: %p", dexBase);
 
         return;
 
  }

(3)    找到dex偏移以后就可以解析dex文件,从而查找我们要进行替换的方法所在的类,然后在该类中找到该方法并返回该方法对应的DexCode结构体。函数实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static const DexCode *dexFindClassMethod(DexFile *dexFile, const char *clazz, const char *method)
 
{
 
    DexClassData* classData = dexFindClassData(dexFile, clazz);
 
    if(classData == NULL) return NULL;
 
    const DexCode* code = dexFindMethodInsns(dexFile, classData, method);
 
    if(code != NULL) {
 
        dumpDexCode(code);
 
    }
 
    return code;
 
}

(4)    找到DexCode后就可以进行指令替换了。实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const DexCode  *code =
 
    dexFindClassMethod(&gDexFile, "Lcom/android/dex/myclass;""setflagHidden");
 
const DexCode*code2 =
 
 dexFindClassMethod(&gDexFile, "Lcom/android/dex/myclass;""setflag");
 
    // remap!!!!
 
    if(mprotect(base, module_size, PROT_READ | PROT_WRITE | PROT_EXEC) == 0){
 
    DexCode *pCode = (DexCode *)code2;
 
    // Modify!
 
    pCode->registersSize = code->registersSize;
 
        for(u4 k=0; k<code->insnsSize; k++){
 
               pCode->insns[k] = code->insns[k];
 
        }
 
      mprotect(base, module_size, PROT_READ | PROT_EXEC);
 
}

注意:由于是在运行时修改的dalvik指令,这是进程的内存映射为只读的,所以需要调用mprotect函数将只读改为读写才能进行指令的修改。

根据上面的讲述相信大家对运行时修改字节码的技术有了一定的了解,下一篇我们将讲解另一种android apk防止反编译技术,期待大家的捧场。如果对这篇讲的技术有任何疑问及想要获得这篇文章讲的技术的工程源码

欢迎关注个人微信公众平台:程序员互动联盟(coder_online),扫一扫下方二维码或搜索微信号coder_online即可关注,我们可以在线交流。

android apk 防止反编译技术第二篇-运行时修改Dalvik指令的更多相关文章

  1. android apk 防止反编译技术第二篇-运行时修改字节码

    上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...

  2. android apk 防止反编译技术第一篇-加壳技术

    做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习.现在将最近学习成果做一下整理总结.学习的这些成 ...

  3. android apk 防止反编译技术第四篇-对抗JD-GUI

    又到周末一个人侘在家里无事可干,这就是程序员的悲哀啊.好了我们利用周末的时间继续介绍android apk防止反编译技术的另一种方法.前三篇我们讲了加壳技术(http://my.oschina.net ...

  4. android apk 防止反编译技术第三篇-加密

    上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...

  5. 转: android apk 防止反编译技术(1~5连载)

    转: android apk 防止反编译技术 做android framework方面的工作将近三年的时间了,现在公司让做一下android apk安全方面的研究,于是最近就在网上找大量的资料来学习. ...

  6. APK防反编译技术

    APK防反编译技术 下载地址:地址 我们的APK实际上就是一个ZIP压缩文件,里面包括有一个classes.dex.我们编译后生成的程序代码就所有在那里了, 通过apktool等工具能够轻松地将它们反 ...

  7. Android Apk的反编译和加密

    这几天在上海出差,忙里偷闲学习了一下Apk的反编译工具的基本使用.下面就简单介绍一下如何将我们从网上下载的Apk文件进行反编译得到我们想要获得的资源文件和源码. Android的应用程序APK文件说到 ...

  8. Android Apk的反编译与代码混淆

    一.反编译 1.获取工具: 既然是反编译,肯定要用到一些相关的工具,工具可以到这里下载,里面包含三个文件夹,用于反编译,查看反编译之后的代码: 其实这两工具都是google官方出的,也可在google ...

  9. android apk 文件反编译

    最近,自己坑逼的把一个android 项目修改版本的代码删除了.这个项目居然还没上传到源代码管理器.幸好还有apk文件,修改的代码也不多可以反编译一下. 1.下载 dex2jar  获取源码工具  地 ...

随机推荐

  1. Python 时间处理---------笔记

    时区处理&格式化 import pytz from datetime import datetime # 设置时区 timezone = pytz.timezone('Asia/Shangha ...

  2. 【译】x86程序员手册13-第5章 内存管理

    Chapter 5 Memory Management 内存管理 The 80386 transforms logical addresses (i.e., addresses as viewed b ...

  3. C# 字符串的入门

    1."@"表示字符串中的"\"不当成转义符. 2.转义符或者特殊处理的一些字符只是针对于代码中直接写出的字符串中,对于程序运行中读取出来的转义符或者特殊处理的字 ...

  4. lnmp环境搭建后续-php安装

    安装PHP7: 下载# wget http://PHP.net/get/php-7.0.2.tar.gz/from/a/mirror 建议安装之前先看看安装帮助文件INSTALL 解压安装 # tar ...

  5. 在Windows下安装Elasticsearch5.0

    1.准备工作 安装和配置Java环境 2.下载 地址:https://www.elastic.co/downloads/elasticsearch 老版本:https://www.elastic.co ...

  6. python mongodb压力测试脚本

    $ pip install pymongo #!/usr/bin/env python #coding=utf-8 #Author: Ca0Gu0 from pymongo import MongoC ...

  7. Java中的方法重写

    方法的重载: 在同一个类中,出现多个同名的方法,参数列表不同,与返回值类型,修饰符无关 方法的重写: 子类中出现和父类中一模一样的方法(包括返回值类型,方法名,参数列表) 方法重写的注意事项: 1.重 ...

  8. 浅谈animation里的forwards

    forwards可译为向前走, animation-fill-mode(动画填充模式),定义动画播放时间之外的状态 顾名思义,就是在动画播放完了之后给它一个状态 animation-fill-mode ...

  9. css3 background-clip和background-origin 区别

    在css3中,background-clip和background-origin它们2个的功能大致相同,但又有些细微差别. 1.background-clip:规定背景的绘制区域,当背景是纯颜色时与图 ...

  10. 51nod1103 N的倍数

    [题解] 先预处理出模N意义下的前缀和sum[i]. 1.如果sum[i]=0,那么1~i的数之和就是N的倍数 2.sum[i]%N总共有0~N-1这N种情况:根据1,如果sum[i]为0则必定有解: ...