• Bundle结构入门


    0x1 结构

    1.1 基本

    对Bundle进行序列化时,依次写入携带所有数据的长度、Bundle魔数(0x4C444E42)和键值对。见BaseBundle.writeToParcelInner方法

    int lengthPos = parcel.dataPosition();
    parcel.writeInt(-1); // dummy, will hold length
    parcel.writeInt(BUNDLE_MAGIC);
    int startPos = parcel.dataPosition();
    parcel.writeArrayMapInternal(map);
    int endPos = parcel.dataPosition();
    // Backpatch length
    parcel.setDataPosition(lengthPos);
    int length = endPos - startPos;
    parcel.writeInt(length);
    parcel.setDataPosition(endPos);
    

    pacel.writeArrayMapInternal方法写入键值对,先写入Hashmap的个数,然后依次写入键和值

    /**
         * Flatten an ArrayMap into the parcel at the current dataPosition(),
         * growing dataCapacity() if needed.  The Map keys must be String objects.
         */
    /* package */ void writeArrayMapInternal(ArrayMap<String, Object> val) {
        ...
        final int N = val.size();
        writeInt(N);
        ... 
        int startPos;
        for (int i=0; i<N; i++) {
            if (DEBUG_ARRAY_MAP) startPos = dataPosition();
            writeString(val.keyAt(i));
            writeValue(val.valueAt(i));
        ...
    

    接着,调用writeValue时依次写入Value类型和Value本身,如果是Parcelable对象,则调用writeParcelable方法,后者会调用Parcelable对象的writeToParcel方法

    public final void writeValue(Object v) {
            if (v == null) {
                writeInt(VAL_NULL);
            } else if (v instanceof String) {
                writeInt(VAL_STRING);
                writeString((String) v);
            } else if (v instanceof Integer) {
                writeInt(VAL_INTEGER);
                writeInt((Integer) v);
            } else if (v instanceof Map) {
                writeInt(VAL_MAP);
                writeMap((Map) v);
            } else if (v instanceof Bundle) {
                // Must be before Parcelable
                writeInt(VAL_BUNDLE);
                writeBundle((Bundle) v);
            } else if (v instanceof PersistableBundle) {
                writeInt(VAL_PERSISTABLEBUNDLE);
                writePersistableBundle((PersistableBundle) v);
            } else if (v instanceof Parcelable) {
                // IMPOTANT: cases for classes that implement Parcelable must
                // come before the Parcelable case, so that their specific VAL_*
                // types will be written.
                writeInt(VAL_PARCELABLE);
                writeParcelable((Parcelable) v, 0);
    

    一个典型的Bundle格式可以包含以下几个部分:

    1. Magic Number (魔数): 标识符,用于验证数据的合法性。
    2. Length (长度): 整个Bundle数据的总长度。
    3. Key-Value Pairs (键值对): 包含键和值的多个键值对。
      每个键值对通常包括以下字段:
    4. Key Length (键长度): 键的长度。
    5. Key (键): 键的实际值。
    6. Value Type (值类型): 指示值的类型。
    7. Value Length (值长度): 值的长度。
    8. Value (值): 值的实际内容。
      下面是一个更详细的示例布局:
    [Header]
    Magic Number (4 bytes) | Total Length (4 bytes) | Number of Pairs (4 bytes)
    
    [Key-Value Pair 1]
    Key Length (4 bytes) | Key (variable length) | Padding (variable length) | 
    Value Length (4 bytes) | Value Type (4 bytes) | Value (variable length) | Padding (variable length)
    
    [Key-Value Pair 2]
    Key Length (4 bytes) | Key (variable length) | Padding (variable length) | 
    Value Length (4 bytes) | Value Type (4 bytes) | Value (variable length) | Padding (variable length)
    
    ...
    
    [Key-Value Pair N]
    Key Length (4 bytes) | Key (variable length) | Padding (variable length) | 
    Value Length (4 bytes) | Value Type (4 bytes) | Value (variable length) | Padding (variable length)
    

    1.2 对齐规则

    需要注意的是对齐规则,在Android的Bundle中,数据对齐(alignment)是一个重要的概念,特别是在序列化和反序列化过程中。Android的Parcel类用于读取和写入Bundle的数据,而Parcel类在处理数据时遵循特定的对齐规则,以确保数据在内存中正确对齐。这些对齐规则帮助提高访问效率,并确保数据在不同架构上的兼容性

    1. 基础对齐:
    • 所有写入Parcel的数据都按照4字节对齐。也就是说,数据的起始地址必须是4的倍数。
    • 如果数据的大小不是4的倍数,Parcel会在数据后面填充额外的字节(填充0),以确保下一个数据项从4字节边界开始。
    1. 数据类型对齐:
    • 整数(int, long, float, double)和指针:这些类型的数据在写入Parcel时,都会按照4字节对齐。
    • 字符串和字节数组:字符串和字节数组的长度首先写入Parcel,长度是4字节对齐的,然后是实际的数据部分。数据部分也可能需要填充,以确保下一个数据项从4字节边界开始。
    • 复杂数据类型(如对象数组、Bundle等):这些类型的数据会递归地遵循4字节对齐规则。

    1.3 字符串

    在Android的Parcel和Bundle中,字符串是以UTF-16编码存储的。UTF-16编码的每个字符都占用两个字节

    1.4 类型

    在往Intent的Bundle对象中添加键值对(Key Value)时,Key为String类型,而Value则可以为各种数据类型,包括int、Boolean、String和Parcelable对象等等,Parcel类中维护着这些类型信息

    // Keep in sync with frameworks/native/include/private/binder/ParcelValTypes.h.
    private static final int VAL_NULL = -1;
    private static final int VAL_STRING = 0;
    private static final int VAL_INTEGER = 1;
    private static final int VAL_MAP = 2; // length-prefixed
    private static final int VAL_BUNDLE = 3;
    private static final int VAL_PARCELABLE = 4; // length-prefixed
    private static final int VAL_SHORT = 5;
    private static final int VAL_LONG = 6;
    private static final int VAL_FLOAT = 7;
    private static final int VAL_DOUBLE = 8;
    private static final int VAL_BOOLEAN = 9;
    private static final int VAL_CHARSEQUENCE = 10;
    private static final int VAL_LIST  = 11; // length-prefixed
    private static final int VAL_SPARSEARRAY = 12; // length-prefixed
    private static final int VAL_BYTEARRAY = 13;
    private static final int VAL_STRINGARRAY = 14;
    private static final int VAL_IBINDER = 15;
    private static final int VAL_PARCELABLEARRAY = 16; // length-prefixed
    private static final int VAL_OBJECTARRAY = 17; // length-prefixed
    private static final int VAL_INTARRAY = 18;
    private static final int VAL_LONGARRAY = 19;
    private static final int VAL_BYTE = 20;
    private static final int VAL_SERIALIZABLE = 21; // length-prefixed
    private static final int VAL_SPARSEBOOLEANARRAY = 22;
    private static final int VAL_BOOLEANARRAY = 23;
    private static final int VAL_CHARSEQUENCEARRAY = 24;
    private static final int VAL_PERSISTABLEBUNDLE = 25;
    private static final int VAL_SIZE = 26;
    private static final int VAL_SIZEF = 27;
    private static final int VAL_DOUBLEARRAY = 28;
    private static final int VAL_CHAR = 29;
    private static final int VAL_SHORTARRAY = 30;
    private static final int VAL_CHARARRAY = 31;
    private static final int VAL_FLOATARRAY = 32;
    // The initial int32 in a Binder call's reply Parcel header:
    // Keep these in sync with libbinder's binder/Status.h.
    private static final int EX_SECURITY = -1;
    private static final int EX_BAD_PARCELABLE = -2;
    private static final int EX_ILLEGAL_ARGUMENT = -3;
    private static final int EX_NULL_POINTER = -4;
    private static final int EX_ILLEGAL_STATE = -5;
    private static final int EX_NETWORK_MAIN_THREAD = -6;
    private static final int EX_UNSUPPORTED_OPERATION = -7;
    private static final int EX_SERVICE_SPECIFIC = -8;
    private static final int EX_PARCELABLE = -9;
    public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127; 
    private static final int EX_HAS_STRICTMODE_REPLY_HEADER = -128;  
    private static final int EX_TRANSACTION_FAILED = -129;
    private static final int ARRAY_ALLOCATION_LIMIT = 1000000;
    private static final int SIZE_BYTE = 1;
    private static final int SIZE_CHAR = 2;
    private static final int SIZE_SHORT = 2;
    private static final int SIZE_BOOLEAN = 4;
    private static final int SIZE_INT = 4;
    private static final int SIZE_FLOAT = 4;
    private static final int SIZE_DOUBLE = 8;
    private static final int SIZE_LONG = 8;
    

    0x2 实战分析

    2.1 测试DEMO

    我们先编写了一个这样的Java代码

    static void bundle_parcel_study(){
        Bundle b = new Bundle();
        int[] a = {1,1};
        b.putIntArray("aaa",a);
        Hacktools.bundleToBytes(b);
    }
    

    然后获取到了它的Hexdump形式

    Length: 40 (0x28)
    0000: 20 00 00 00 42 4E 44 4C 01 00 00 00 03 00 00 00 |  ...BNDL........
    0010: 61 00 61 00 61 00 00 00 12 00 00 00 02 00 00 00 | a.a.a...........
    0020: 01 00 00 00 01 00 00 00                         | ........
    

    2.2 构建Header

    根据hexdump,我们可以看到:

    • Total Length: 20 00 00 00 (32 bytes)
    • Magic Number: BNDL
    • Number of Pairs: 01 00 00 00 (1 pair)
    20 00 00 00 42 4E 44 4C 01 00 00 00
    

    2.3 分析Key-Value Pair

    • 03 00 00 00:表示键的长度为 3
    • 61 00 61 00 61 00 00 00:键"aaa",UTF-16编码,因为要保持 4 字节对齐故自动补齐
    • 12 00 00 00:值的长度:值的类型 + 内容(18字节)。
    • 02 00 00 00:值的类型(整数数组)。
    • 01 00 00 00:第一个整数值1。
    • 01 00 00 00:第二个整数值1。

    0x3 工具代码

    package com.anonymous.bundlemismatchtest;
    import android.util.Log;
    
    public class HexDump {
    
        private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
    
        public static void printHexDump(String tag, byte[] bytes) {
            if (bytes == null || bytes.length == 0) {
                Log.d(tag, "Empty or null byte array");
                return;
            }
    
            StringBuilder hexDump = new StringBuilder();
            int length = bytes.length;
            hexDump.append(String.format("length: %d (0x%X)\n", length, length));
    
            for (int i = 0; i < length; i += 16) {
                hexDump.append(String.format("%04X: ", i));
    
                // Hex part
                for (int j = 0; j < 16; j++) {
                    if (i + j < length) {
                        int v = bytes[i + j] & 0xFF;
                        hexDump.append(HEX_ARRAY[v >>> 4]);
                        hexDump.append(HEX_ARRAY[v & 0x0F]);
                        hexDump.append(' ');
                    } else {
                        hexDump.append("   ");
                    }
                }
    
                hexDump.append("| ");
    
                // ASCII part
                for (int j = 0; j < 16; j++) {
                    if (i + j < length) {
                        byte b = bytes[i + j];
                        if (b >= 32 && b <= 126) {
                            hexDump.append((char) b);
                        } else {
                            hexDump.append('.');
                        }
                    }
                }
    
                hexDump.append('\n');
            }
    
            Log.d(tag, hexDump.toString());
        }
    }
    
    package com.anonymous.bundlemismatchtest;
    
    import android.os.Bundle;
    import android.os.Parcel;
    import android.util.Log;
    
    public class Hacktools {
        //bundle to bytes
        public static byte[] bundleToBytes(Bundle bundle) {
            Parcel parcel = Parcel.obtain();
            parcel.writeBundle(bundle);
            byte[] bytes = parcel.marshall();
            HexDump.printHexDump("MyTag", bytes);
            //parcel.recycle();
            return bytes;
        }
    
        // bytes to bundle
        public static Bundle bytesToBundle(byte[] bytes) {
            Parcel parcel = Parcel.obtain();
            parcel.unmarshall(bytes, 0, bytes.length);
            parcel.setDataPosition(0);
            Bundle bundle = Bundle.CREATOR.createFromParcel(parcel);
            //parcel.recycle();
            return bundle;
        }
    
        // hexdump
        public static String hexdump(byte[] bytes) {
            StringBuilder sb = new StringBuilder();
            for (byte b : bytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        }
        //simulate the bundle mismatch
        static void simulateBundleMismatch(Bundle b0) {
            // 从exp中发送:序列化
            byte[] exp_send = bundleToBytes(b0);
            // system_server接收:反序列化 readFromParcel
            Bundle b1 = bytesToBundle(exp_send);
            // system_server发送:序列化 writeToParcel
            byte[] system_server_send = bundleToBytes(b1);
            // Setting接收:序列化
            Bundle b2 = bytesToBundle(system_server_send);
        }
    }
    
  • 相关阅读:
    一种有效的基于VPS和RSS的科研小白文献阅读策略
    d3dcompiler_43.dll是什么文件?缺失d3dcompiler_43.dll文件修复与解决方法
    大数据必学Java基础(四十七):异常的讲解
    GDB/MI断点信息
    数据结构——二叉树线索化遍历(前中后序遍历)
    Vertica常用的sql
    SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.5 变更缓存供应商 Ehcache
    【Python基础】高级数据类型:初识 “列表” || 列表的增删改查 || del关键字 || 列表的定义
    大数据(9h)FlinkSQL双流JOIN、Lookup Join
    辽宁2022农民丰收节 国稻种芯:4个主会场31个分会场同步
  • 原文地址:https://blog.csdn.net/kelxLZ/article/details/140447051