前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dex文件字节码解析

dex文件字节码解析

作者头像
提莫队长
发布2020-06-02 15:29:41
6310
发布2020-06-02 15:29:41
举报
文章被收录于专栏:刘晓杰刘晓杰

这一篇解析dex文件.还是由上一篇文章MainActivity生成的dex文件.dex文件比较大,我就不贴16进制代码了,大致讲一下具体怎么操作.由于手工解析太困难了,所以我就借助代码和两篇参考文章来解析的 Android逆向之旅---解析编译之后的Dex文件格式 Android dex文件解析 接下来开始解析.

1.头文件

头文件格式包含magic, checksum,file_size等信息,但是要注意他们都是小端数据,所以需要调换顺序

代码语言:javascript
复制
    private static void parseHeader(byte[] content) {

        System.out.println("magic:0x" + Utils.bytesToString(content, 0, 8, false));// 默认0x6465780a30333500=dex\n035\0

        System.out.println("checksum:0x" + Utils.bytesToString(content, 8, 4, true));

        System.out.println("siganature:0x" + Utils.bytesToString(content, 12, 20, true));

        String file_size = Utils.bytesToString(content, 32, 4, true);
        System.out.println("file_size:0x" + file_size + "=" + Integer.parseInt(file_size, 16));

        String header_size = Utils.bytesToString(content, 36, 4, true);
        System.out.println("header_size:0x" + header_size + "=" + Integer.parseInt(header_size, 16));

        System.out.println("endian_tag:0x" + Utils.bytesToString(content, 40, 4, true));// 默认小端

        String number = Utils.bytesToString(content, 44, 4, true);
        System.out.println("link_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 48, 4, true);
        System.out.println("link_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 52, 4, true);
        System.out.println("map_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 56, 4, true);
        System.out.println("string_ids_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 60, 4, true);
        System.out.println("string_ids_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 64, 4, true);
        System.out.println("type_ids_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 68, 4, true);
        System.out.println("type_ids_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 72, 4, true);
        System.out.println("proto_ids_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 76, 4, true);
        System.out.println("proto_ids_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 80, 4, true);
        System.out.println("field_ids_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 84, 4, true);
        System.out.println("field_ids_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 88, 4, true);
        System.out.println("method_ids_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 92, 4, true);
        System.out.println("method_ids_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 96, 4, true);
        System.out.println("class_defs_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 100, 4, true);
        System.out.println("class_defs_off:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 104, 4, true);
        System.out.println("data_size:0x" + number + "=" + Integer.parseInt(number, 16));

        number = Utils.bytesToString(content, 108, 4, true);
        System.out.println("data_off:0x" + number + "=" + Integer.parseInt(number, 16));
    }

Utils.bytesToString的代码我后面会贴上,现在需要记住最后一个参数true表示是小端数据

2.string_ids

接下来就是string_ids,要注意数据格式,offset是偏移地址,找到偏移地址以后,读出对应的string,这个string直接读,不需要小端转化.另外uleb128的数据格式需要去了解(解析的string需要存起来,有用)

代码语言:javascript
复制
    private static void parseStringIds(byte[] content) throws Exception {
        // 观察parseHeader的日志.string_ids_off+string_ids_size*4=type_ids_off
        int string_ids_size = Integer.parseInt(Utils.bytesToString(content, 56, 4, true), 16);// string条数
        System.out.println("总共string:" + string_ids_size);
        int string_ids_off = Integer.parseInt(Utils.bytesToString(content, 60, 4, true), 16);
        for (int i = 0; i < string_ids_size; i++) {
            String string_ids_item = Utils.bytesToString(content, string_ids_off + i * 4, 4, true);// 获取item
            int index = Integer.parseInt(string_ids_item, 16);// 获取每个string对应的偏移值
            string_ids.add(index);
            Pair uleb128 = Utils.readUnsignedLeb128(content, index);
            int string_size = Utils.coypByte(content, uleb128.first, uleb128.second);// 获取string对应的长度
            String string = new String(Utils.bytesToBytes(content, uleb128.first, string_size, false), "utf-8");// 注意不需要大小端
            string_datas.add(string);
            if (i >= 1000 && i <= 2000) {
                // System.out.println(string);
            }
        }
    }

3.type_ids

注意格式,同时找到的index,这个index需要去对应的string数组里面找

代码语言:javascript
复制
    private static void parseTypeIds(byte[] content) throws Exception {
        int type_ids_size = Integer.parseInt(Utils.bytesToString(content, 64, 4, true), 16);
        System.out.println("总共type:" + type_ids_size);
        int type_ids_off = Integer.parseInt(Utils.bytesToString(content, 68, 4, true), 16);
        for (int i = 0; i < type_ids_size; i++) {
            String type_ids_item = Utils.bytesToString(content, type_ids_off + i * 4, 4, true);// 获取item
            int index = Integer.parseInt(type_ids_item, 16);// 获取每个type对应的偏移值
            type_ids.add(index);
            String string = string_datas.get(index);
            if (i >= 10 && i <= 20) {
                System.out.println(string);
            }
        }
    }

4.proto_ids

数据结构比上面的复杂

代码语言:javascript
复制
public class ProtoIdsItem {
    public int shorty_idx;
    public String shorty_idx_name;//自己添加的,不是公共字段
    public int return_type_idx;
    public String return_type_idx_name;//自己添加的,不是公共字段
    public int parameters_off;

    // 存数据用的,自己添加的,不是公共字段
    public List<String> parametersList = new ArrayList<String>();
    public int parameterCount;

    @Override
    public String toString() {
        return "shorty_idx_name:" + shorty_idx_name + ",return_type_idx_name:" + return_type_idx_name + ",parameterCount:" + parameterCount;
    }
}
代码语言:javascript
复制
    private static void parseProtoIds(byte[] content) throws Exception {
        int proto_ids_size = Integer.parseInt(Utils.bytesToString(content, 72, 4, true), 16);
        System.out.println("总共proto:" + proto_ids_size);
        int proto_ids_off = Integer.parseInt(Utils.bytesToString(content, 76, 4, true), 16);
        for (int i = 0; i < proto_ids_size; i++) {
            int base_index = proto_ids_off + i * 12;
            ProtoIdsItem item = new ProtoIdsItem();
            item.shorty_idx = Integer.parseInt(Utils.bytesToString(content, base_index, 4, true), 16);
            item.shorty_idx_name = string_datas.get(item.shorty_idx);
            item.return_type_idx = Integer.parseInt(Utils.bytesToString(content, base_index + 4, 4, true), 16);
            item.return_type_idx_name = string_datas.get(type_ids.get(item.return_type_idx));
            item.parameters_off = Integer.parseInt(Utils.bytesToString(content, base_index + 8, 4, true), 16);
            if (item.parameters_off > 0) {
                item.parameterCount = Integer.parseInt(Utils.bytesToString(content, item.parameters_off, 4, true), 16);
                for (int j = 0; j < item.parameterCount; j++) {
                    // size是int,但是index却是short
                    int index = Integer.parseInt(Utils.bytesToString(content, item.parameters_off + 4 + j * 2, 2, true),
                            16);
                    String name = string_datas.get(type_ids.get(index));
                    item.parametersList.add(name);
                }
            } else {
                item.parameterCount = 0;
            }
            proto_ids.add(item);
        }
    }

注意size是short,只需要两位

5.field_ids

解析过程和proto_ids有点类似

代码语言:javascript
复制
public class FieldIdsItem {
    public short class_idx;
    public String class_name;//自己添加的,不是公共字段
    public short type_idx;
    public String type_name;//自己添加的,不是公共字段
    public int name_idx;
    public String name;//自己添加的,不是公共字段

    @Override
    public String toString() {
        return "class_name:" + class_name + ",type_name:" + type_name + ",name:" + name;
    }
}
代码语言:javascript
复制
    private static void parseFieldIds(byte[] content) {
        int field_ids_size = Integer.parseInt(Utils.bytesToString(content, 80, 4, true), 16);
        int field_ids_off = Integer.parseInt(Utils.bytesToString(content, 84, 4, true), 16);
        for (int i = 0; i < field_ids_size; i++) {
            int base_index = field_ids_off + i * 8;
            FieldIdsItem item = new FieldIdsItem();
            item.class_idx = (short) Integer.parseInt(Utils.bytesToString(content, base_index, 2, true), 16);
            item.class_name = string_datas.get(type_ids.get(item.class_idx));
            item.type_idx = (short) Integer.parseInt(Utils.bytesToString(content, base_index + 2, 2, true), 16);
            item.type_name = string_datas.get(type_ids.get(item.type_idx));
            item.name_idx = Integer.parseInt(Utils.bytesToString(content, base_index + 4, 4, true), 16);
            item.name = string_datas.get(item.name_idx);
            field_ids.add(item);
            // System.out.println(item);
        }
    }

6.method_ids

代码语言:javascript
复制
public class MethodIdsItem {
    public short class_idx;
    public String class_name;//自己添加的,不是公共字段
    public short proto_idx;
    public String proto_name;//自己添加的,不是公共字段
    public int name_idx;
    public String name;//自己添加的,不是公共字段

    @Override
    public String toString() {
        return "class_name:" + class_name + ",proto_name:" + proto_name + ",name:" + name;
    }
}
代码语言:javascript
复制
    private static void parseMethodIds(byte[] content) {
        int method_ids_size = Integer.parseInt(Utils.bytesToString(content, 88, 4, true), 16);
        int method_ids_off = Integer.parseInt(Utils.bytesToString(content, 92, 4, true), 16);
        for (int i = 0; i < method_ids_size; i++) {
            int base_index = method_ids_off + i * 8;
            MethodIdsItem item = new MethodIdsItem();
            item.class_idx = (short) Integer.parseInt(Utils.bytesToString(content, base_index, 2, true), 16);
            item.class_name = string_datas.get(type_ids.get(item.class_idx));
            item.proto_idx = (short) Integer.parseInt(Utils.bytesToString(content, base_index + 2, 2, true), 16);
            item.proto_name = proto_ids.get(item.proto_idx).shorty_idx_name;
            item.name_idx = Integer.parseInt(Utils.bytesToString(content, base_index + 4, 4, true), 16);
            item.name = string_datas.get(item.name_idx);
            method_ids.add(item);
            // System.out.println(item);
        }
    }

7.class

class的结构如下(注意,大多都是index)

代码语言:javascript
复制
    public int class_idx;
    public int access_flags;
    public int superclass_idx;
    public int interfaces_off;
    public int source_file_idx;
    public int annotations_off;
    public int class_data_off;
    public int static_value_off;

这是最复杂的,但是有了上面几个的解析其实对应写起来也不算难,本人写过,但是快写吐了,就没贴出来,具体的方法和上面的也是一样的

可以看到,其实dex文件比class文件要复杂得多.一方面是小端排列,另一方面需要寻址.最重要的一点是,class文件的类索引里面所有的信息都是直接排进去的,但是dex文件里面的类都是存的索引,dex文件更为紧凑.也就是意味着,如果需要修改dex文件,那么他的成本会比修改class文件难得多

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.头文件
  • 2.string_ids
  • 3.type_ids
  • 4.proto_ids
  • 5.field_ids
  • 6.method_ids
  • 7.class
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档