dex文件字节码解析

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

1.头文件

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

    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需要存起来,有用)

    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数组里面找

    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

数据结构比上面的复杂

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 parametersList = new ArrayList();
    public int parameterCount;

    @Override
    public String toString() {
        return "shorty_idx_name:" + shorty_idx_name + ",return_type_idx_name:" + return_type_idx_name + ",parameterCount:" + parameterCount;
    }
}
    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有点类似

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;
    }
}
    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

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;
    }
}
    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)

    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文件难得多

你可能感兴趣的:(dex文件字节码解析)