Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
LeakValue — CVE-2022-20452 के लिए एक्सप्लॉइट, इंस्टॉल किए गए ऐप से सिस्टम ऐप (या किसी अन्य ऐप) तक Android पर विशेषाधिकार वृद्धि, recycle() के बाद Parcel का उपयोग करके LazyValue के माध्यम से। | Kitploit
उपकरण/GitHubGitHub/michalbednarski/leakvalue
एंड्रॉइड सुरक्षाविशेषाधिकार वृद्धिभेद्यता विश्लेषणशोषणमोबाइल सुरक्षाबाइनरी शोषण
GitHubmichalbednarski/leakvalue

LeakValue

CVE-2022-20452 के लिए एक्सप्लॉइट, इंस्टॉल किए गए ऐप से सिस्टम ऐप (या किसी अन्य ऐप) तक Android पर विशेषाधिकार वृद्धि, recycle() के बाद Parcel का उपयोग करके LazyValue के माध्यम से।

रिपॉजिटरी देखें
347653 साल पहलेKitploit द्वारा समीक्षित

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें

Android 13 Parcel सीरियलाइज़ेशन तंत्र को सख्त करने के लिए कई संवर्द्धन प्रस्तुत करता है

यहाँ Android सुरक्षा और गोपनीयता टीम द्वारा किए गए संवर्द्धन पर प्रस्तुति है

यह बहुत अच्छा है, निश्चित रूप से कई कमज़ोरियों को समाप्त कर देता है या उन्हें अशोषणीय बना देता है। साथ ही वे मेरे पिछले शोषण को तोड़ने का वर्णन करते हैं, जो ऐप्स को अपने कोड को अन्य ऐप्स (सिस्टम ऐप्स सहित) में लोड करने की अनुमति देता है

लेकिन अब मैं एक नए शोषण के साथ वापस आया हूँ जो समान परिणाम प्राप्त करता है, हालाँकि अलग तरीके से। यह निम्नलिखित कमज़ोरियों पर निर्भर करता है जो उपरोक्त Parcel सख्तीकरण के दौरान पेश की गई थीं:

  • CVE-2022-20452 (बुलेटिन, पैच)
  • CVE-2022-20474 (बुलेटिन, पैच)

एप्लिकेशन द्वारा टेक्स्ट प्रदर्शित करते हुए स्क्रीनशॉट। शीर्षक: LeakValue। मुख्य टेक्स्ट: 6 ValueLeaker-s बनाए गए। ActivityTaskManagerService लॉक किया जा रहा है। ActivityTaskManagerService लॉक किया गया। ActivityTaskManagerService अनलॉक किया जा रहा है। ActivityTaskManagerService अनलॉक किया गया। leakedBinders=[android.os.BinderProxy@f06702e]। लीक हुआ इंटरफ़ेस: android.app.IApplicationThread। कोड निष्पादन का अनुरोध किया जा रहा है। शेलकोड निष्पादित किया गया है uid=1000 pid=6904 packageName=com.android.settings uid=1000(system) gid=1000(system) groups=1000(system),1007(log),1065(reserved_disk),1077(external_storage),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct),9997(everybody) context=u:r:system_app:s0 में। स्क्रीन के नीचे दो बटन हैं: START और MANUAL TESTING

(साथ ही ऐप निष्पादन से logcat, लॉग में शोषण शोरगुल वाला है)

Parcel और Parcelable असंगति बग्स का परिचय

Android का Parcel वर्ग प्रक्रियाओं के बीच संचार का आधार है

ऑब्जेक्ट्स को Parcel में लिखने की अनुमति देने के लिए वे Parcelable इंटरफ़ेस लागू कर सकते हैं, उदाहरण के लिए (AOSP से कॉपी किया गया):```java public class UsbAccessory implements Parcelable { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public UsbAccessory createFromParcel(Parcel in) { String manufacturer = in.readString(); String model = in.readString(); String description = in.readString(); String version = in.readString(); String uri = in.readString(); IUsbSerialReader serialNumberReader = IUsbSerialReader.Stub.asInterface( in.readStrongBinder());

root@kitploit:~
        return new UsbAccessory(manufacturer, model, description, version, uri,
                serialNumberReader);
    }
};

public void writeToParcel(Parcel parcel, int flags) {
    parcel.writeString(mManufacturer);
    parcel.writeString(mModel);
    parcel.writeString(mDescription);
    parcel.writeString(mVersion);
    parcel.writeString(mUri);
    parcel.writeStrongBinder(mSerialNumberReader.asBinder());

} }

root@kitploit:~
ध्यान दें कि `Parcel` आंतरिक रूप से उस स्थिति को संग्रहीत करता है जिस पर write या read किया जाता है, `readString()` डेटा को String में parse करता है और साथ ही स्थिति को आगे बढ़ाता है। उस स्थिति को [`dataPosition()`](https://developer.android.com/reference/android/os/Parcel#dataPosition())/[`setDataPosition()`](https://developer.android.com/reference/android/os/Parcel#setDataPosition(int)) के माध्यम से मैन्युअल रूप से प्राप्त/सेट किया जा सकता है। `Parcelable` इंटरफ़ेस के कार्यान्वयन को यह सुनिश्चित करना चाहिए कि उनके `writeToParcel` और `createFromParcel` समान मात्रा में डेटा लिखें/पढ़ें, अन्यथा बाद के सभी read गलत offsets से डेटा प्राप्त करेंगे।

[`Bundle`](https://developer.android.com/reference/android/os/Bundle) (key-value मैप जिसे प्रक्रियाओं के बीच भेजा जा सकता है) में [विभिन्न प्रकार की वस्तुएँ हो सकती हैं जिन्हें `writeValue()` के माध्यम से Parcel में लिखा जा सकता है](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=1792-1937)। जब `Bundle` की सामग्री `Parcel` से पढ़ी जाती है, तो सिस्टम में उपलब्ध कोई भी `Parcelable` वर्ग वहाँ पढ़ा जा सकता है।

`Bundle` संपूर्ण parcelled डेटा की लंबाई `Parcel` में लिखकर और फिर [मूल Parcel के प्रासंगिक भाग को `mParcelledData` में संग्रहीत द्वितीयक Parcel में प्रतिलिपि करके](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=1675-1683) सामग्री के वास्तविक parsing को स्थगित करता है (यह उदाहरण के लिए [`Activity.onSaveInstanceState()`](https://developer.android.com/reference/android/app/Activity#onSaveInstanceState(android.os.Bundle)) को ऐसे `Parcelable` प्रदान करने की अनुमति देता है जो `system_server` में उपलब्ध नहीं हैं; पूरा `Bundle` तब बिना सामग्री parse किए verbatim `system_server` को और वापस भेज दिया जाता है)।

हालाँकि, एक बार जब `Bundle` में किसी भी मान तक पहुँचा गया, तो `Bundle` के अंदर के सभी मान [unparcelled कर दिए गए](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/BaseBundle.java;l=227-313) और [प्रत्येक मौजूदा key-value जोड़ी को parse किया गया](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/core/java/android/os/Parcel.java;l=3613-3632)। यदि ऐसे मानचित्र में ऐसा `Parcelable` था जिसमें असंतुलित `writeToParcel` और `createFromParcel` विधियाँ थीं और बाद में ऐसा `Bundle` किसी अन्य प्रक्रिया को अग्रेषित किया गया, तो उस अन्य प्रक्रिया को `Bundle` की भिन्न सामग्री दिखाई दे सकती थी। इसने सिस्टम में उपलब्ध वर्गों में ऐसे सभी [mismatch को सिस्टम भेद्यता बना दिया](https://github.com/michalbednarski/ReparcelBug), क्योंकि [सिस्टम में ऐसे स्थान हैं जहाँ `Bundle` को सुरक्षित होने के लिए निरीक्षित किया जाता है](https://cs.android.com/android/platform/superproject/+/android-12.1.0_r8:frameworks/base/services/core/java/com/android/server/accounts/AccountManagerService.java;l=5037-5046) और फिर उसे किसी अन्य प्रक्रिया को अग्रेषित किया जाता है।

इस writeup में मैं ऐसे `Bundle` को self-changing `Bundle` कह रहा हूँ जो एक सामग्री प्रस्तुत करता है और अग्रेषित किए जाने के बाद दूसरी सामग्री प्रस्तुत करता है।

यहाँ एक और महत्वपूर्ण बात यह है कि केवल बाइट्स (Strings, संख्याएँ, उपरोक्त से बनी वस्तुएँ) के अलावा, `Parcel` में File Descriptors और `Binder` भी हो सकते हैं। `Binder` ऐसी वस्तुएँ हैं जिन पर कोई RPC कॉल कर सकता है, अर्थात एक प्रक्रिया `Binder` object बनाती है और [`onTransact()` method](https://developer.android.com/reference/android/os/Binder#onTransact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) को override करती है। फिर `Binder` को किसी अन्य प्रक्रिया को भेजा जाता है, ऊपर दिए गए उदाहरण कोड में आप `read`/`writeStrongBinder()` कॉल देख सकते हैं जिनका उपयोग इसे `Parcel` में पढ़ने और लिखने के लिए किया जाता है। दूसरी प्रक्रिया में, जब `readStrongBinder()` का उपयोग किया जाता है तो एक `BinderProxy` object बनता है (जो [`IBinder` interface](https://developer.android.com/reference/android/os/IBinder) के पीछे छिपा होता है)। फिर वह अन्य प्रक्रिया उस object पर [`transact()`](https://developer.android.com/reference/android/os/IBinder#transact(int,%20android.os.Parcel,%20android.os.Parcel,%20int)) कॉल कर सकती है और मूल object में `onTransact()` निष्पादित होगा। आमतौर पर, हालाँकि, कोई `transact()`/`onTransact()` मैन्युअल रूप से नहीं लिखता बल्कि [इसके बजाय AIDL का उपयोग करता है](https://developer.android.com/guide/components/aidl)।

# `LazyValue` का आगमन: self-changing `Bundle`s का अंत

चूँकि अतीत में `writeToParcel`/`createFromParcel` mismatch वाले वर्गों के कई मामले थे, Android 13 ने [LazyValue को पेश करके](https://android.googlesource.com/platform/frameworks/base/+/9ca6a5e21a1987fd3800a899c1384b22d23b6dee%5E%21/) सिस्टम में कहीं भी ऐसे किसी भी वर्ग के उपस्थित होने की समस्या को हल कर दिया जो self-changing `Bundle` के निर्माण की अनुमति देता था।

अब, जब `writeValue` का उपयोग किया जाता है, यदि लिखा जा रहा मान primitive नहीं है, तो [मान की लंबाई भी `Parcel` में लिखी जाती है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=2331-2344;drc=03c34f57c05feecfb090de3917787f049cb5f804)।

जब कोई सामान्य ऐप सीधे `Parcel.readValue()` का उपयोग करता है, [सब कुछ पहले जैसा होता है, सिवाय इसके कि यदि `Parcel` से पढ़ी गई `length` वास्तव में पढ़े गए डेटा के आकार से मेल नहीं खाती है तो एक चेतावनी प्रदर्शित होती है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4330-4348;drc=03c34f57c05feecfb090de3917787f049cb5f804) (ध्यान दें कि [`Slog.wtfStack` कभी throw नहीं करता](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/util/Slog.java;l=108-116;drc=23c7543b8e608ebcbb38b952761b54bb56065577))।

हालाँकि, `Bundle` अब इसके बजाय [`Parcel.readLazyValue()`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4350-4420;drc=03c34f57c05feecfb090de3917787f049cb5f804) का उपयोग करता है।

आइए करीब से देखें कि यह कैसे काम करता है: `LazyValue` वर्ग में हमारे पास [`Parcel` के अंदर `LazyValue` डेटा की संरचना समझाने वाली एक अच्छी टिप्पणी है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4392-4399;drc=03c34f57c05feecfb090de3917787f049cb5f804):```
                     |   4B   |   4B   |
mSource = Parcel{... |  type  | length | object | ...}
                     a        b        c        d
length = d - c
mPosition = a
mLength = d - a

mSource मूल Parcel का संदर्भ है जिस पर readLazyValue() कॉल किया गया था

mPosition और mLength मूल Parcel में संपूर्ण LazyValue डेटा के स्थान का वर्णन करते हैं, जिसमें type और length शामिल हैं

"length" (शुरुआत में "m" के बिना) Parcel में लिखे गए length मान को संदर्भित करता है और हेडर (type और length) को बाहर रखता है

तो यहाँ वही होता है जब कोई (सिस्टम या ऐप) Parcel से पढ़े गए Bundle से मान लेता है:

  1. कॉलर Bundle क्लास के कई get*() तरीकों में से एक का उपयोग करता है, उदाहरण के लिए टाइप तर्क के साथ नया getParcelable() (प्रवाह नए और पुराने दोनों तरीकों के लिए समान होगा, बस नए तरीके सुनिश्चित करते हैं कि clazz तर्क null न हो जबकि पुराने इसे null पर सेट करते हैं)
  2. unparcel() कहा जाता है, जो जाँचेगा कि इस Bundle में mParcelledData है या नहीं (अर्थात इसे Parcel से पढ़ा गया था लेकिन अभी तक कोई मान एक्सेस नहीं किया गया और कुंजी नामों को अनपैक नहीं किया गया, यदि ऐसा नहीं है तो चरण 5 पर जाएँ।)
  3. unparcel() unparcel(boolean itemwise) को सौंपता है, , को पर सेट किया जाता है, जो द्वारा बनाई गई की एक प्रति है, और पैरामीटर को पर सेट किया जाता है ताकि यह संकेत दिया जा सके कि पारित के स्वामित्व में है और उस पर कॉल करना ठीक है

यदि Bundle आगे भेजा जा रहा है जबकि उसमें अभी भी LazyValue मौजूद है (अर्थात उस विशेष मान को एक्सेस नहीं किया गया था, लेकिन उस Bundle से कुछ अन्य मान एक्सेस किया गया था (यानी unparcel() कॉल किया गया था, लेकिन उस आइटम के लिए LazyValue.apply() नहीं किया गया था)):

  1. LazyValue का पता Parcel.writeValue() द्वारा लगाया जाता है और लेखन को LazyValue.writeToParcel() को सौंपा जाता है
  2. LazyValue.writeToParcel() मूल Parcel से संपूर्ण LazyValue डेटा की प्रतिलिपि बनाने के लिए out.appendFrom(source, mPosition, mLength) का उपयोग करता है (फिर से, mPosition और mLength में LazyValue हेडर शामिल है, इसलिए यह मूल Parcel से type और की भी प्रतिलिपि बनाता है)

Parcel.ReadWriteHelper और Parcel.readSquashed

(इनका विवरण इस एक्सप्लॉइट के लिए महत्वपूर्ण नहीं है, यहाँ केवल प्रासंगिक बात यह है कि ये तंत्र मौजूद हैं)

Parcel की एक और दिलचस्प विशेषता लिखे गए Strings और ऑब्जेक्ट्स को डिडुप्लिकेट करने की वैकल्पिक क्षमता है

Strings का डिडुप्लिकेशन Parcel.ReadWriteHelper क्लास को ओवरराइड करके किया जाता है: Parcel.readString() वास्तव में ReadWriteHelper को सौंपता है और डिफ़ॉल्ट हेल्पर सीधे Parcel से String पढ़ता है

Parcel.ReadWriteHelper का वैकल्पिक कार्यान्वयन readString कॉल्स को पहले से Strings का पूल पढ़ने और पूल में Strings के इंडेक्स प्राप्त करने के लिए readInt का उपयोग करने से बदल सकता है; हालाँकि, यह ऐप-नियंत्रित Parcels के साथ कभी नहीं किया जाता है

Parcel hasReadWriteHelper() विधि प्रदान करता है, जो कॉलर्स को ऐसे डिडुप्लिकेशन तंत्र की सक्रिय उपस्थिति का पता लगाने और इसके साथ असंगत सुविधाओं को अक्षम करने की अनुमति देता है

Parcel में उपलब्ध अन्य डिडुप्लिकेशन तंत्र squashing है:

  1. सबसे पहले, squashing को Parcel.allowSquashing() के साथ सक्षम करना होता है
  2. फिर, जब squashing का समर्थन करने वाला क्लास लिखा जा रहा होता है, यह पहले कॉल करता है Parcel.maybeWriteSquashed(this)। यदि वह विधि true लौटाती है तो इसका मतलब है कि ऑब्जेक्ट पहले ही इस Parcel में लिखा जा चुका है और अब केवल पिछले ऑब्जेक्ट डेटा का ऑफसेट Parcel में लिखा गया था। अन्यथा (या तो squashing सक्षम नहीं है या यह पहली बार है जब यह ऑब्जेक्ट लिखा गया है) maybeWriteSquashed ऑफसेट के रूप में शून्य लिखता है ताकि यह संकेत दिया जा सके कि ऑब्जेक्ट squashed नहीं है और कॉलर को यह बताने के लिए false लौटाता है कि उन्हें वास्तविक ऑब्जेक्ट डेटा लिखना चाहिए
  3. पढ़ते समय, Parcel.readSquashed कॉल किया जाता है और वास्तविक रीड फ़ंक्शन उसे लैम्ब्डा के रूप में पारित किया जाता है। readSquashed जाँचता है कि क्या द्वारा लिखा गया ऑफसेट यह दर्शाता है कि ऑब्जेक्ट का कोई अन्य उदाहरण पहले पढ़ा गया था: यदि हाँ तो पहले पढ़ा गया ऑब्जेक्ट लौटाया जाता है, अन्यथा प्रदान किया गया लैम्ब्डा इसे अभी पढ़ने के लिए कॉल किया जाता है

Use-after-Parcel.recycle()

Java पक्ष पर Parcel ऑब्जेक्ट्स को पूल में रीसायकल किया जा सकता है, अर्थात एक बार जब आप Parcel के साथ पूरा कर लेते हैं तो आप उस पर recycle() कॉल करते हैं और अगली बार जब कोई Parcel.obtain() कॉल करेगा तो उसे पहले रीसायकल किया गया Parcel मिलेगा। यह ऑब्जेक्ट आवंटन की मात्रा और उसके बाद के Garbage Collection को कम करने की अनुमति देता है

दूसरी ओर, इस तरह का मैनुअल मेमोरी प्रबंधन Java में Use-After-Free जैसी बग्स की संभावना लाता है (हालाँकि प्रकार सुरक्षा के साथ, C में सामान्य Use-After-Free के विपरीत)

जैसा कि ऊपर उल्लेख किया गया है, Bundle Parcel की प्रति बनाता है और यदि LazyValue मौजूद है तो Parcel.recycle() कॉल नहीं करेगा, हालाँकि यह मामला नहीं है यदि Parcel.hasReadWriteHelper() true हो, उस स्थिति में:

  1. initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle); कॉल किया जाता है, इसका मतलब है कि Bundle Parcel को रीसायकल नहीं करेगा क्योंकि यह अभी भी कॉलर का है, हालाँकि यह LazyValues बनाता है जो मूल Parcel को संदर्भित करते हैं और मूल Parcel के जीवनकाल से अधिक जीवित रह सकते हैं
  2. इसलिए, उसके बाद अगली चीज़ कॉल है unparcel(/* itemwise */ true), जो getValueAt() का उपयोग सभी आइटमों पर करेगा ताकि Bundle में मौजूद सभी LazyValues को वास्तविक मानों से बदल दिया जा सके

अब, क्या हम इन LazyValues को चरण 2 से बचा सकते हैं और उस व्यवहार को Use-After-Recycle में बदल सकते हैं?

यदि डिसीरियलाइज़ेशन विफल होता है (उदाहरण के लिए Parcel के अंदर निर्दिष्ट नाम वाला क्लास नहीं मिल सका), तो एक BadParcelableException फेंका जाता है और फिर getValueAt() द्वारा पकड़ा जाता है। यदि BaseBundle.sShouldDefuse स्टेटिक फ़ील्ड true है, तो कोई अपवाद नहीं उठाया जाता है और निष्पादन आगे बढ़ता है, Bundle को LazyValue के साथ छोड़ देता है जो मूल Parcel को संदर्भित करता है। sShouldDefuse इंगित करता है कि Bundle से अनुपलब्ध मानों को विशेष प्रक्रिया में अपवाद नहीं उठाना चाहिए और यह system_server में पर सेट है

यदि मूल Parcel को रीसायकल किया जाता है और उसके बाद उससे पढ़ा गया Bundle दूसरे Parcel में लिखा जाएगा, तो मूल Parcel की सामग्री को गंतव्य Parcel में कॉपी किया जाएगा, लेकिन उस समय मूल Parcel को किसी और चीज़ के लिए पुनः उपयोग किया जा सकता है और असंबंधित IPC ऑपरेशन का डेटा कॉपी किया जा सकता है

ठीक है, लेकिन हम कैसे सुनिश्चित करें कि हमारे द्वारा प्रदान किया गया Bundle डिसीरियलाइज़ होते समय Parcel.hasReadWriteHelper() true हो?

पता चलता है कि RemoteViews क्लास (सामान्य रूप से उदाहरण के लिए विजेट्स को होम स्क्रीन पर पास करने के लिए उपयोग होता है) इसमें नेस्टेड Bundles को पढ़ते समय स्पष्ट रूप से ReadWriteHelper सेट करता है। यह ReadWriteHelper String डिडुप्लिकेशन नहीं करता है और केवल Bundle को माध्यमिक Parcel में डेटा कॉपी करने से छोड़ने के लिए उपस्थित होता है। इसके किए जाने का कारण यह है कि RemoteViews squashing सक्षम करता है ताकि इसमें नेस्टेड ApplicationInfo ऑब्जेक्ट्स को डिडुप्लिकेट किया जा सके, लेकिन यह Bundle के अंदर मौजूद ApplicationInfo ऑब्जेक्ट्स को भी squashed कर सकता है, इसलिए उस का पठन स्थगित नहीं किया जा सकता क्योंकि तब वे squashed ऑब्जेक्ट्स unsquash होने में विफल हो जाएंगे

system_server में Parcelables डालना और उन्हें पुनः प्राप्त करना

तो अब हम चाहते हैं कि system_server हमारे RemoteViews को पढ़े जिसमें Bundle हो, जिसमें LazyValue हो जो डिसीरियलाइज़ करने में विफल रहता है, और बाद में (एक अन्य Binder IPC लेनदेन में) उस ऑब्जेक्ट को हमें वापस भेजे

यह संभवतः कुछ वैध तरीकों से किया जा सकता था, जैसे खुद को app widget host के रूप में पंजीकृत करना (लेकिन इसके लिए हमें अनुमति देने हेतु उपयोगकर्ता की सहभागिता की आवश्यकता होती) या Notification जिसमें contentView सेट हो पोस्ट करना (लेकिन इससे अन्य प्रक्रियाओं के साथ सहभागिता होती और/या उपयोगकर्ता को दिखाई देता, और मैंने इन दोनों से बचना पसंद किया)

मैंने इसके बजाय MediaSession बनाने और उस पर setQueue(List<MediaSession.QueueItem> queue) कॉल करने का निर्णय लिया ताकि ऑब्जेक्ट को system_server को भेजा जा सके और बाद में MediaController की List<MediaSession.QueueItem> getQueue() विधि के माध्यम से इसे वापस प्राप्त किया जा सके (जिसे MediaSession.getController() के माध्यम से प्राप्त किया जा सकता है)। जबकि ये विधियाँ ऐसी नहीं दिखतीं जैसे वे RemoteViews स्वीकार कर सकती हैं, वे वास्तव में Java Type Erasure और इस तथ्य के कारण करती हैं कि अंतर्निहित रूप से वे List पर सामान्य सीरियलाइज़ेशन ऑपरेशनों का उपयोग करके कार्यान्वित की जाती हैं

हालाँकि, मैं इन SDK विधियों का उपयोग नहीं कर रहा हूँ, मैं अंतर्निहित Binder लेनदेन के लिए डेटा मैन्युअल रूप से लिख रहा हूँ (क्योंकि मुझे विकृत सीरियलाइज़्ड डेटा लिखना और बाद में पढ़ना है), तो आइए देखें कि ये विधियाँ कैसे काम करती हैं

इन दोनों विधियों को इस तथ्य का ध्यान रखना था कि queue का कुल आकार Binder लेनदेन के अधिकतम आकार से अधिक हो सकता है, इसलिए स्थानांतरण को कई लेनदेनों में विभाजित किया जा सकता है

system_server को "queue" भेजना सामान्य रूप से इस प्रकार होता है:

  1. MediaSession.setQueue() सबसे पहले कॉल करता है ISession.getBinderForSetQueue()
  2. system_server की ओर वह विधि ParcelableListBinder ऑब्जेक्ट का निर्माण करती है और उसे लौटाती है
  3. उसके बाद, MediaSession.setQueue() कॉल करता है ParcelableListBinder.send() जो प्रदान किए गए Binder में सूची सामग्री भेजेगा, संभवतः कई लेनदेनों में:
    • पहले लेनदेन की शुरुआत में पहले भाग की सामग्री से पहले उन आइटमों की कुल संख्या होती है जो सूची में होंगे
    • फिर, प्रत्येक स्थानांतरित आइटम के लिए 1 लिखा जाता है और वास्तविक आइटम के माध्यम से लिखा जाता है (जो भेजे जा रहे क्लास का नाम लिखता है और फिर डेटा भेजने के लिए कॉल करता है)

दूसरी ओर, "queue" प्राप्त करना थोड़ा अलग तरीके से होता है:

  1. MediaController.getQueue() केवल कॉल करता है ISessionController.getQueue() और प्राप्त ParceledListSlice को अनरैप करता है
  2. system_server की ओर, getQueue() केवल mQueue को ParceledListSlice में लपेटता है और उसे लौटाता है
  3. संपूर्ण कई-लेनदेनों-में-विभाजन तर्क ParceledListSlice.writeToParcel() और createFromParcel() विधियों के अंदर है, विशेष रूप से, writeToParcel() सुरक्षित आकार सीमा तक पहुँचने पर ऑब्जेक्ट लिखेगा जो अगले हिस्सों को प्राप्त करने की अनुमति देता है

जहाँ तक इनके अलग होने का सवाल है: यह सुनिश्चित करने के लिए निरंतर प्रयास चल रहा है कि system_server अन्य ऐप्स को बाहर जाने वाली सिंक्रोनस Binder कॉल्स न करे, क्योंकि यदि ये कॉल्स हैंग हो जाएँ तो पूरा system_server हैंग हो सकता है। इसका मतलब है कि system_server को ParceledListSlices प्राप्त नहीं करना चाहिए। जबकि ऐसा कोड है जो system_server से बाहर जाने वाले सिंक्रोनस लेनदेन के बारे में चेतावनी देता है, इसे अभी तक लागू नहीं किया जा सका क्योंकि अभी भी ऐसे मामले हैं जहाँ system_server ऐसी कॉल्स करता है, उदाहरण के लिए वास्तव में ParceledListSlice प्राप्त करके

लीक टारगेट चुनना

तो अब हमारे पास system_server से यह करवाने के लिए आवश्यक प्रिमिटिव्स हैं: parcel_that_will_be_sent_to_us.appendFrom(some_recycled_parcel, somewhat_controlled_position, controlled_size)

हम या तो सिस्टम से Parcel डेटा खींचने का यादृच्छिक प्रयास कर सकते हैं या कुछ विशिष्ट लेने के लिए चीज़ों की व्यवस्था कर सकते हैं

निम्नलिखित बातों पर विचार करना होगा:* जब Parcel.recycle() कॉल किया जाता है, तो उस Parcel की सामग्री साफ़ कर दी जाती है। इसका मतलब है कि जिस Parcel से हम डेटा कॉपी करना चाहते हैं उसे recycle() नहीं किया जाना चाहिए, जो लगभग यह है कि हम उस Binder ट्रांज़ैक्शन से डेटा नहीं ले सकते जो समाप्त हो चुका है

  • वैकल्पिक रूप से, हम सिस्टम में मौजूद किसी Bundle के किसी Parcel से डेटा ले सकते हैं (इसमें Intent extras और Activity का savedInstanceState शामिल है)। ये आमतौर पर recycle() बिल्कुल नहीं किए जाते (इन्हें Garbage Collector साफ़ करता है और ये पूल में वापस नहीं जाते; जब पूल खत्म हो जाता है, Parcel.obtain() नए Parcel ऑब्जेक्ट बनाता है। बेशक जिन Parcels के संदर्भ हम रख रहे हैं वे GCed नहीं होंगे, भले ही सिस्टम को उनका कोई और उपयोग न हो)
  • आने वाले Binder ट्रांज़ैक्शन के लिए उपयोग किए जाने वाले Parcels सिस्टम के अन्य Parcels से अलग पूल का उपयोग करते हैं। जब कोई बाहर जाने वाला Binder ट्रांज़ैक्शन बनाया जा रहा होता है, डेटा को द्वितीयक में कॉपी करता है, या कोई ऐप अपने उद्देश्यों के लिए का उपयोग करता है, तो वे । दूसरी ओर, जब कोई आने वाला ट्रांज़ैक्शन होता है, , जो । दोनों ही मामलों में बाद में का उपयोग होता है और यह का ध्यान रखता है। इसका मतलब है कि एक्सप्लॉइट में को उसी पूल के से पढ़ा जाना चाहिए जिस पूल से हम डेटा लीक करना चाहते हैं। इससे पहले कि मैंने विशेष वैरिएंट तय किया, मैंने दोनों लिख दिए थे, इसलिए आप मेरी में और दोनों विधियाँ पा सकते हैं

अंत में मैंने IApplicationThread Binder को पकड़ने का प्रयास करने का निर्णय लिया, जिसे ऐप प्रोसेस शुरू होने पर ऐप द्वारा system_server को भेजा जाता है और system_server इसका उपयोग एप्लिकेशन को यह बताने के लिए करता है कि उसे कौन से कॉम्पोनेंट लोड करने चाहिए

जब एप्लिकेशन प्रोसेस शुरू में प्रारंभ होता है, तो वह सबसे पहले जो काम करता है उनमें से एक है attachApplication() को कॉल करके IApplicationThread को system_server को भेजना और यह वह ट्रांज़ैक्शन है जिससे मैं उस Binder को पकड़ूँगा। ऐसे अन्य स्थान भी हैं जहाँ IApplicationThread को Parcel में डाला जाता है, जैसे कि गतिविधि शुरू करते समय सिस्टम द्वारा कॉलर पहचान के लिए पारित किया जाना (लेकिन लक्ष्य एप्लिकेशन ऐसा कब करता है, इस पर मेरा अधिक नियंत्रण नहीं था) या सिस्टम द्वारा Activity जीवनचक्र प्रबंधन के भाग के रूप में एप्लिकेशन को भेजा जाना (लेकिन यह system_server से बाहर जाने वाले oneway ट्रांज़ैक्शन में किया जाता है और Parcel.recycle() के विरुद्ध रेस जीतने की संभावना कम होती)

फिर भी, attachApplication() ट्रांज़ैक्शन के दौरान system_server द्वारा प्राप्त किए जा रहे Binder को पकड़ना भी आसान नहीं है और कुछ समस्याएँ थीं जिन्हें दूर करना था

Parcel को रिवाइंड करना

attachApplication() के लिए डेटा जिस Parcel से प्राप्त होता है, उससे IApplicationThread Binder को पकड़ने में पहली समस्या यह है कि यह Binder काफी प्रारंभिक/कम dataPosition() पर होता है, जो RemoteViews में Bundle में हमारे LazyValue से काफी कम हो सकता है

attachApplication() ट्रांज़ैक्शन के लिए डेटा केवल RPC हेडर और उसके बाद IApplicationThread Binder से बना होता है। RPC हेडर (Parcel.writeInterfaceToken() के माध्यम से लिखा गया) में कुछ ints और इंटरफ़ेस का नाम होता है, इस मामले में "android.app.IActivityManager"

इस बीच, RemoteViews में एम्बेडेड Bundle को पढ़ने के लिए हमें कम से कम इनसे आगे निकलना होगा (कुछ छोटी चीज़ें छोड़ दी गई हैं):

  • आइटम उपस्थिति फ़्लैग readParcelable शुरू करने के लिए
  • Parcelable का नाम: "android.view.RemoteViews"
  • RemoteViews में मौजूद काफी बड़ा ApplicationInfo ऑब्जेक्ट (साथ ही यह null नहीं होना चाहिए और इसका packageName गैर-null होना चाहिए अन्यथा RemoteViews.writeToParcel() विफल हो जाएगा जब हम इस ऑब्जेक्ट को वापस भेजने के लिए प्राप्त करने का प्रयास करेंगे)
  • अंत में हम तक पहुँचते हैं, जो को कॉल करता है, जो का निर्माण करता है, जो अंत में

अब Bundle में, हमें बस String कुंजी डालनी है और LazyValue का पढ़ना शुरू हो जाता है, Parcel में स्थिति याद रखी जाती है, लेकिन इस बिंदु पर यह उस स्थिति से काफी आगे होती है जहाँ IApplicationThread Binder होगा

क्या हम शायद इस बिंदु पर पहुँचकर Parcel में स्थिति को रिवाइंड कर सकते हैं? दूसरे शब्दों में, क्या हम Parcel.setDataPosition() को ऐसे मान के साथ बुलवा सकते हैं जो वर्तमान स्थिति से पहले की स्थिति की ओर इशारा करता हो?

पता चला, हम कर सकते हैं, LazyValue में एक और बग के कारण। इसे पढ़ने के लिए उपयोग किया गया कोड यह है:```java 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); } }

root@kitploit:~
([Original in AOSP](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4376-4388;drc=03c34f57c05feecfb090de3917787f049cb5f804), `LazyValue` कंस्ट्रक्टर केवल पैरामीटर को फ़ील्ड्स में असाइन करता है)

बात यह है कि `MathUtils.addOrThrow()` overflow की जाँच करता है, [लेकिन negative values के साथ पूरी तरह ठीक है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/util/MathUtils.java;l=276;drc=290055a76e6ef80dd8ad7bc812d78f4fc0c5be86)

यदि हम negative `mLength` (जो `valueLength` पैरामीटर से भरा गया है) के साथ `LazyValue` पर `Parcel.writeValue()` करने का प्रयास करें तो [वह `appendFrom()` पर throw करेगा](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4446;drc=03c34f57c05feecfb090de3917787f049cb5f804), हालाँकि चूँकि हम `Bundle` की read के दौरान हैं और `Parcel.hasReadWriteHelper()` `true` है, सभी `LazyValue`s पढ़े जाने के बाद unparcelled हो जाते हैं और हमें इसे `LazyValue` बनाए रखने के लिए जानबूझकर इसके अंदर faulty `Parcelable` डालना पड़ा। यदि हम `LazyValue` की स्थिति पर valid parcelled डेटा डालते हैं, तो वह unparcelled हो जाएगा और जैसा कि पहले बताया गया है, mismatched length केवल `logcat` में message ट्रिगर करेगा। यह विशेष exploit type को `VAL_MAP` और key-value जोड़ों की संख्या को zero सेट करता है। उस मान को पढ़ने पर `logcat` में हम यह message देख सकते हैं: "`E Parcel  : android.util.Log$TerribleFailure: Unparcelling of {} of type VAL_MAP  consumed 4 bytes, but -540 expected.`"

(साथ ही negative length निर्दिष्ट किया गया `LazyValue` (इस writeup में वर्णित अन्य bugs का उपयोग किए बिना) self-changing `Bundle` बनाने के लिए उपयोग किया जा सकता है, वह चीज़ जिसे खत्म करने के लिए `LazyValue` बनाया गया था। लेकिन यह एक और कहानी है (और अलग से Google को रिपोर्ट की गई है), इस exploit में मैं और अधिक करने का लक्ष्य रख रहा हूँ)

तो हम कितना रिवाइंड करना चाहते हैं?

`setDataPosition()` कॉल होने के बाद, पढ़ना `Bundle` में अगले key-value जोड़े पर आगे बढ़ेगा, इसलिए हमें ऐसी स्थिति चुननी होगी जहाँ हमारे पास होगा:

1. Bundle key, `Parcel.readString()` का उपयोग करके पढ़ा गया, लगभग कुछ भी हो सकता है, जिसमें invalid length की ओर इशारा करना शामिल है (negative या कुल `Parcel` आकार से अधिक), उस स्थिति में `readString()` `null` लौटाएगा जो `Bundle` में valid key है
2. Value type, यह उन [types में से एक होना चाहिए जिनके लिए `isLengthPrefixed()` `true` लौटाता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4695-4712;drc=03c34f57c05feecfb090de3917787f049cb5f804)
3. Value length, यह भी हमारे नियंत्रण वाला मान होना चाहिए, `Parcel.appendFrom()` विफल हो जाएगा यदि length aligned नहीं है या source `Parcel` के कुल आकार से अधिक है

तो `Parcel` में वह कौन-सी स्थिति हो सकती है, यह देखते हुए कि वही डेटा पहले ही पढ़ा जा चुका है और इस बिंदु तक पहुँचने के लिए आवश्यक है:

* `Parcelable` के नाम (`"android.view.RemoteViews"`) से पहले नहीं, क्योंकि पर्याप्त जगह नहीं है
* `Parcelable` के नाम के अंदर नहीं, क्योंकि हम type और length सेट करने में असमर्थ हैं
* `Parcelable` के नाम के ठीक बाद नहीं, क्योंकि `RemoteViews` में पहली चीज़ `mode` है जिसे हमें [अपने कोड तक पहुँचने के लिए `MODE_NORMAL` पर सेट करना चाहिए](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/widget/RemoteViews.java;l=3799;drc=03c34f57c05feecfb090de3917787f049cb5f804)
* उसके बाद नहीं, क्योंकि वह उस बिंदु से आगे है जहाँ `IApplicationThread` `Binder` है

हम्म, जब `RemoteViews` parcelled डेटा में सबसे बाहरी ऑब्जेक्ट होता है तो कोई अच्छी जगह नहीं है

हमें कुछ अन्य `Parcelable` खोजने की ज़रूरत है जो:

1. शुरुआत में या उसके पास ऐसी जगह हो जहाँ हम arbitrary डेटा रख सकें (जैसे `int`s या `String`s जो केवल डेटा हैं और serialization प्रक्रिया को प्रभावित नहीं करते)
2. `RemoteViews` शामिल कर सके (सीधे या arbitrary `readParcelable` के माध्यम से)
3. उसका fully qualified class name बहुत लंबा न हो, क्योंकि हम अभी भी उस स्थिति से आकार में सीमित हैं जिस पर `IApplicationThread` target `Parcel` में रहता है

इसलिए मैंने system में `Parcelable` classes की सूची ली, उसे fully qualified class name की बढ़ती लंबाई के अनुसार क्रमबद्ध किया और यह देखने के लिए उस सूची की वस्तुओं की जाँच शुरू की कि क्या वे शर्त 2 को पूरा करती हैं

इस तरह मैं [`"android.os.Message"`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Message.java;drc=afdb23ab6f909c5438fa69aad458a11497cff216) तक पहुँचा, जिसका यह exploit उपयोग करता है। अब हमारे तैयार किए गए ऑब्जेक्ट को `Parcel` से पढ़ने की प्रक्रिया इस प्रकार है:

* [आइटम उपस्थिति फ़्लैग](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/java/android/media/session/ParcelableListBinder.java;l=85;drc=23c7543b8e608ebcbb38b952761b54bb56065577) से `readParcelable` शुरू करने के लिए
* [`Parcelable` का नाम](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=4858;drc=03c34f57c05feecfb090de3917787f049cb5f804): `"android.os.Message"`
* कुछ [`int`s जिन्हें हम अपनी इच्छानुसार मानों पर सेट कर सकते हैं](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Message.java;l=652-656;drc=afdb23ab6f909c5438fa69aad458a11497cff216) फ़ील्ड्स में पढ़े जाते हैं
* हम `readParcelable()` कॉल तक पहुँचते हैं, जो ऊपर वर्णित तरीके से `RemoteViews` के माध्यम से आगे बढ़ता है और `Parcel.hasReadWriteHelper` के `true` होने पर `Bundle` पढ़ना शुरू करता है
* उस `Bundle` में दो key-value जोड़े होने की घोषणा है। पहले मान में हमारे पास negative length वाला `LazyValue` है, जो `Parcel.setDataPosition()` को उस स्थिति पर ट्रिगर करता है जहाँ `"android.os.Message"` `String` है
* पढ़ना दूसरे key-value जोड़े पर आगे बढ़ता है, key `"android.os.Message"` है और `LazyValue` का type, length और data तीसरे bullet में वर्णित `int`s से लिया जाता है। मुझे वांछित `mPosition` और `mLength` वाला `LazyValue` मिल गया। वाह!
* `LazyValue`s पढ़े जाने के बाद उन्हें unparcelled किया जाता है। negative size वाला सफलतापूर्वक unparcelled होकर खाली `Map` से बदल दिया जाता है, जबकि दूसरे का deserialization विफल होता है, लेकिन वह exception पकड़ लिया जाता है और&nbsp;`LazyValue` बस `Bundle` में ही रहता है
* `readParcelable()` समाप्त होता है, लेकिन यह `Message` डेटा का अंत नहीं है। `Message.readFromParcel()` अब रिवाइंड के बाद डेटा पढ़ना जारी रखता है और उसे वह डेटा दिखता है जो शुरू में `RemoteViews` के भाग के रूप में लिखा गया था। यदि&nbsp;इस बिंदु पर कोई exception आता है तो पूरी योजना विफल हो जाती है
* पहला संभावित exception, [`readBundle()` कॉल](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Message.java;l=660;drc=afdb23ab6f909c5438fa69aad458a11497cff216) है। [`Bundle` के पास magic value होती है और यदि वह गलत है तो exception फेंका जाएगा](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/BaseBundle.java;l=1809-1815;drc=a9cb2102da997f016245da9a2a56f9ef134e8f91)। हालाँकि वह magic value मौजूद नहीं होती यदि length [zero](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/BaseBundle.java;l=1800-1804;drc=a9cb2102da997f016245da9a2a56f9ef134e8f91) या [negative](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Parcel.java;l=3323-3327;drc=03c34f57c05feecfb090de3917787f049cb5f804) है, और ऐसा ही हुआ जब `LazyValue` डेटा की length उस मान पर सेट की गई थी जिसकी मुझे `IApplicationThread` पकड़ने के लिए आवश्यकता थी। तो मैं यहाँ केवल भाग्यशाली रहा
* अगली संभावित समस्या [`Messenger.readMessengerOrNullFromParcel()` कॉल](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Message.java;l=661;drc=afdb23ab6f909c5438fa69aad458a11497cff216) हो सकती है। यह वास्तव में wrapped `Binder` ऑब्जेक्ट है। उस `Binder` का पढ़ना विफल हो जाता है, क्योंकि `Binder` `Parcel` में विशेष ऑब्जेक्ट है और उसे पढ़ने के लिए out-of-band annotate किया जाना चाहिए। यह समस्या [native side पर `Parcel` द्वारा पहचानी और लॉग की जाती है, हालाँकि इसे error के रूप में प्रचारित नहीं किया जाता और केवल `null` लौटाया जाता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/Parcel.cpp;l=2473-2476;drc=8b12e8333bbe17ccf4b30efb825244e1923d3a71)

# `attachApplication()` को अटकाना

ठीक है, तो पिछले चरण में हमने सफलतापूर्वक ऐसा ऑब्जेक्ट बना लिया है जो हमें `attachApplication()` विधि के चलने के दौरान `IApplicationThread` ऑब्जेक्ट पकड़ने की अनुमति देगा

बात यह है कि वह विधि जल्दी पूरी हो जाती है और इसके पूरा होने के खिलाफ निष्पक्ष दौड़ में हमारी संभावनाएँ काफी कम होंगी

हालाँकि, वह विधि कुछ mutexes प्राप्त करती है (Java के `synchronized () {}` blocks के उपयोग के माध्यम से), यदि हम ऐसे किसी एक mutex को प्राप्त करके वहाँ अटक जाते हैं, तो यह विधि भी अटक जाएगी

अब आइए कुछ उन बातों पर लौटते हैं जो इस writeup में पहले ही कही जा चुकी हैं और इस उद्देश्य के लिए उपयोगी होंगी:

* `Bundle` अपने अंदर के मानों का deserialization तब करता है जब ये मान एक्सेस किए जाते हैं
* `ParceledListSlice` class है जो deserialization के दौरान serialized डेटा के अंदर निर्दिष्ट ऑब्जेक्ट पर blocking outgoing `Binder` कॉल करेगी

इन सभी चीजों को जोड़ते हुए: यदि हम `system_server` में वह स्थान ढूँढ लें जहाँ app द्वारा प्रदान किए गए `Bundle` की सामग्री को mutex के अंतर्गत एक्सेस किया जाता है जिसका उपयोग `attachApplication()` भी करता है, तो हम `attachApplication()` को तब तक अटका सकेंगे जब तक हमारी प्रक्रिया को किया गया `Binder` transaction समाप्त नहीं हो जाता

[`ActivityOptions`](https://developer.android.com/reference/android/app/ActivityOptions) एक class है जो `Activity` प्रारंभ से संबंधित विभिन्न मापदंडों का वर्णन करती है (उदाहरण के लिए animation)। `system_server` को भेजे गए मापदंडों का वर्णन करने वाली अन्य classes के विपरीत, यह `Parcelable` implement नहीं करती बल्कि इसके बजाय इसे `Bundle` में बदलने की विधि प्रदान करती है

`system_server` पक्ष पर, वह [`Bundle` वापस `ActivityOptions` में परिवर्तित होता है, जिससे deserialization ट्रिगर होता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityOptions.java;l=1096-1204;drc=03c34f57c05feecfb090de3917787f049cb5f804)। मैंने वह स्थान ढूँढ लिया है जहाँ [वह ऑपरेशन `ActivityTaskManagerService.mGlobalLock` mutex के hold होने के दौरान `ActivityTaskManagerService.moveTaskToFront()` में किया जाता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java;l=2088;drc=03c34f57c05feecfb090de3917787f049cb5f804)

इसलिए मैं [`ActivityManager.moveTaskToFront()`](https://developer.android.com/reference/android/app/ActivityManager#moveTaskToFront(int,%20int,%20android.os.Bundle)) को कॉल करता हूँ, जिसमें `Bundle` पास करता हूँ जिसमें अपेक्षित type के मान के बजाय `ParceledListSlice` होता है। वह [`ParceledListSlice` मेरी प्रक्रिया में `Binder` कॉल करता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/pm/BaseParceledListSlice.java;l=82-105;drc=23c7543b8e608ebcbb38b952761b54bb56065577) और जब तक मैं उस कॉल से वापस नहीं लौटता, `ActivityTaskManagerService.mGlobalLock` mutex locked रहेगा

# विभिन्न `Parcel`s की ओर इशारा करते हुए कई `LazyValue`s बनाना

`Parcel.recycle()` और `Parcel.obtain()` [Last-In-First-Out तरीके](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) से काम करते हैं

इसका मतलब है कि यदि मैं rigged `LazyValue` तब बनाता हूँ जब `system_server` में कोई अन्य `Binder` transaction नहीं चल रहा हो, तो मुझे ऐसा `LazyValue` मिलेगा जो उस `Parcel` की ओर इशारा करेगा जिसका उपयोग हमेशा तब होता है जब `system_server` में केवल एक transaction आ रहा हो (जब तक कि ऐसा न हो कि `system_server` में आने वाले दो समवर्ती transactions non-stack क्रम में शुरू और समाप्त हों)

चूँकि मेरा नियंत्रण नहीं है कि `system_server` में अन्य कौन-से transactions आ रहे हैं, exploit की विश्वसनीयता बढ़ाने के लिए मैंने विभिन्न `Parcel`s की ओर इशारा करते हुए कई `LazyValue`s बनाए हैं

चूँकि मेरे पास `system_server` से अपनी प्रक्रिया में synchronous `Binder` transaction ट्रिगर करने की क्षमता है, मैंने उस क्षमता का उपयोग अपनी प्रक्रिया और `system_server` के बीच recursion के विभिन्न स्तरों पर `LazyValue` बनाने के लिए किया (हालाँकि इस बार मैंने global mutex hold किए बिना ऐसा किया)

तो:

* मैं `LazyValue` बनाता हूँ
* मैं `system_server` को कॉल ट्रिगर करता हूँ, `system_server` मुझे वापस कॉल करता है
    * मैं `LazyValue` बनाता हूँ
    * मैं `system_server` को कॉल ट्रिगर करता हूँ, `system_server` मुझे वापस कॉल करता है
        * मैं `LazyValue` बनाता हूँ
        * मैं `system_server` को कॉल ट्रिगर करता हूँ, `system_server` मुझे वापस कॉल करता है
            * ...

फिर जब मेरे पास पर्याप्त `LazyValue`s हो जाते हैं तो मैं ऐसा करना समाप्त कर देता हूँ, इन सभी कॉल्स से लौटता हूँ और इन कॉल्स द्वारा आरक्षित सभी `Parcel`s को `recycle()` कर दिया जाता है

मेरे द्वारा बनाए गए प्रत्येक `LazyValue` को [`getQueue()` द्वारा निर्मित अलग `ParceledListSlice`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/media/MediaSessionRecord.java;l=1597;drc=03c34f57c05feecfb090de3917787f049cb5f804) में लपेटा गया है और मैं `ParceledListSlice` `Binder` को कॉल कर सकता हूँ ताकि `system_server` इसे serialize करके मेरी प्रक्रिया में भेज दे

(ऐसा करने का वैकल्पिक तरीका कई `MediaSession`s बनाना होगा)

# लक्षित app प्रक्रिया शुरू करना

अब हमारे पास `attachApplication()` होने पर उससे `IApplicationThread` कैप्चर करने के लिए आवश्यक सब कुछ है, लेकिन हमें अभी भी `attachApplication()` होने वाला बनाना है

सामान्य तौर पर [कुछ प्रकार के app components होते हैं जिनके साथ अन्य app इंटरैक्ट कर सकती है](https://developer.android.com/guide/components/fundamentals#Components), जिनमें से प्रत्येक के लिए app प्रक्रिया शुरू होना आवश्यक है

मैं system Settings app शुरू करना चाहता था (जो [system uid के अंतर्गत चलता है](https://cs.android.com/android/platform/superproject/+/master:packages/apps/Settings/AndroidManifest.xml;l=5;drc=d131bdfbec1209548db9fbb0c63cfbaa4977ef74) और इसलिए उसके पास [Android permissions के पीछे की हर चीज़](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityManager.java;l=3948-3952;drc=16c119018a80b8630c7736a5c6dc35ebef130c5b) तक पहुँच है)

शुरू में मैंने इसे `startActivity()` के माध्यम से लॉन्च करने का प्रयास किया, हालाँकि जब मैंने वह प्रयास किया तो `ActivityTaskManagerService` lock को छोड़ने तक प्रक्रिया शुरू नहीं हुई। ऐसा क्यों था इसका विवरण "Additional note: `Binder` calls and mutexes reentrancy" अनुभाग में है, लेकिन समाधान के रूप में मैंने `Activity` के बजाय उस app से [`ContentProvider`](https://cs.android.com/android/platform/superproject/+/master:packages/apps/Settings/AndroidManifest.xml;l=4031-4034;drc=d131bdfbec1209548db9fbb0c63cfbaa4977ef74) के लिए system से अनुरोध करने का निर्णय लिया। इसका अतिरिक्त लाभ यह था कि यह मेरे UI के साथ हस्तक्षेप करने से बचता था

मैंने [SDK द्वारा उजागर आधिकारिक `ContentResolver` API](https://developer.android.com/reference/android/content/ContentResolver) का उपयोग नहीं किया, बल्कि [system internal वाली का उपयोग किया, क्योंकि मुझे asynchronous API की आवश्यकता थी](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/IActivityManager.aidl;l=153-154;drc=3d7be31a284d295af4c118c5675896e6fcc28907) क्योंकि `ContentProvider` से binding तब तक समाप्त नहीं होगी जब तक `attachApplication()` नहीं होता, जिसे मैं अटका रहा हूँ, हालाँकि दूसरा thread शुरू करना वैकल्पिक हो सकता था

(इस विशेष `ContentProvider` का ऑफ़र क्या है इससे कोई फर्क नहीं पड़ता, केवल प्रासंगिक बात यह है कि मैं उससे connection स्थापित कर सकता हूँ)

तो इस तरह मैं Settings app की प्रक्रिया शुरू करता हूँ। मैं यह सुनिश्चित करता हूँ कि वह पहले से नहीं चल रही हो, इसके लिए [आधिकारिक रूप से उपलब्ध `ActivityManager.killBackgroundProcesses()` विधि](https://developer.android.com/reference/android/app/ActivityManager#killBackgroundProcesses(java.lang.String)) का उपयोग करता हूँ

# सब कुछ एक साथ रखना

आदिम (primitives) अब वर्णित किए जा चुके हैं, तो अब यहाँ बताया गया है कि यह सब एक साथ कैसे काम करता है (यह काफी हद तक इस exploit के `MainActivity.doAllStuff()` विधि का प्रतिलेखन है):

1. hidden API access सक्षम करें (hidden APIs सुरक्षा सीमा नहीं हैं और [पहले से सार्वजनिक रूप से उपलब्ध workarounds मौजूद हैं](https://www.xda-developers.com/bypass-hidden-apis/), हालाँकि यहाँ मैंने [`Property.of()`](https://developer.android.com/reference/android/util/Property#of(java.lang.Class%3CT%3E,%20java.lang.Class%3CV%3E,%20java.lang.String)) पर आधारित विधि का उपयोग किया है, जिसे मैंने कहीं और नहीं देखा है)
2. (केवल यदि हम पहले प्रयास के बाद exploit को दोबारा चला रहे हैं) पिछले execution के दौरान चरण 6 में स्थापित `ContentProvider` से connection जारी करें। हमें ऐसा करना होगा क्योंकि अन्यथा `ActivityManager.killBackgroundProcesses()` लक्षित प्रक्रिया को "background" नहीं मानेगा और उसे kill नहीं करेगा
3. `ActivityManager.killBackgroundProcesses()` का उपयोग करके victim अनुप्रयोग प्रक्रिया को kill करें, क्योंकि `attachApplication()` केवल प्रक्रिया startup पर कॉल किया जाता है
4. `system_server` से बाद में recycle किए जाने वाले `Parcel` की ओर इशारा करते हुए `LazyValue` वाले bunch ऑब्जेक्ट बनाने का अनुरोध करें। मुझे `LazyValue` वाले प्रत्येक ऑब्जेक्ट के लिए `ParceledListSlice` `Binder` reference मिलता है और मैं उस पर `Binder` transaction कर सकता हूँ ताकि system उसे वापस लिखने के लिए ट्रिगर हो। प्रत्येक `LazyValue` ऑब्जेक्ट का निर्माण `system_server` और मेरी app के बीच [म्युचुअली-रिकर्सिव](https://en.wikipedia.org/wiki/Mutual_recursion) कॉल्स की अलग-अलग गहराई पर किया जाता है ताकि यह संभावना बने कि इनमें से प्रत्येक `LazyValue` का विभिन्न `Parcel` ऑब्जेक्ट पर dangling reference होगा
5. मैं `ActivityTaskManagerService.moveTaskToFront()` को कॉल करके `ActivityTaskManagerService.mGlobalLock` को lock करता हूँ, जिसमें argument के रूप में ऐसा `Bundle` पास करता हूँ जो deserialization पर मेरी प्रक्रिया में synchronous `Binder` transaction करता है। अगले चरण उस callback से किए जाते हैं और इसलिए उस lock को hold किए हुए किए जाते हैं
6. मैं `ActivityManagerService` से victim app के `ContentProvider` से connection का अनुरोध करता हूँ (ध्यान दें कि नाम में "`Task`" नहीं है, `ActivityTaskManagerService` वह class है जो मुख्य रूप से apps के `Activity` components को संभालने पर केंद्रित है, जबकि `ActivityManagerService` अन्य [app components](https://developer.android.com/guide/components/fundamentals#Components) (साथ ही समग्र प्रक्रिया startup) को संभालती है, यह [विभाजन Android 10 में हुआ, पहले `Activity` और अन्य app components दोनों की हैंडलिंग `ActivityManagerService` में थी](https://android.googlesource.com/platform/frameworks/base/+/595070969de0a7334d251d5448b641e856e052bc))
7. मैं थोड़ी देर `sleep()` करता हूँ ताकि नई लॉन्च हुई प्रक्रिया को `attachApplication()` कॉल करना शुरू करने का समय मिल सके
8. जब lock अभी भी hold है, मैं पहले बनाए गए सभी `ParceledListSlice` ऑब्जेक्ट्स से उनकी शेष सामग्री (जो प्रारंभिक transaction में फिट नहीं हुई थी) भेजने का अनुरोध करता हूँ, यानी recycled `Parcel` की ओर इशारा करते हुए `LazyValue` वाले ऑब्जेक्ट। फिर `attachApplication()` को पास किए गए `IApplicationThread` की स्थिति से मेल खाते hardcoded offset से मैं `Binder` ऑब्जेक्ट पढ़ता हूँ। इस समय मैं केवल प्राप्त `Binder`s को `ArrayList` में सहेज रहा हूँ ताकि lock hold किए हुए बहुत अधिक काम न करना पड़े
9. यह उस कोड का अंत है जो मैं चरण 5 में शुरू किए गए callback से करता हूँ। `ActivityTaskManagerService.mGlobalLock` अनलॉक हो जाता है
10. मेरे पास `IApplicationThread` `Binder` है। अब मैं इसे अगले अनुभाग में वर्णित अनुसार victim app में अपना कोड लोड करने के लिए आसानी से उपयोग कर सकता हूँ

# मैं `IApplicationThread` का उपयोग कैसे करूँ

जैसा कि पहले बताया गया है [`IApplicationThread`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/IApplicationThread.aidl;drc=45f4b4aaa5fd8af2d0c685b2fef8acf75ed37452) `Binder`, app प्रक्रिया शुरू होने पर app द्वारा `system_server` को भेजा जाता है और फिर `system_server` इसका उपयोग application को यह बताने के लिए करता है कि उसे कौन-से components लोड करने चाहिए

यह मान लिया जाता है कि यह ऑब्जेक्ट केवल `system_server` को ही पास किया जाता है और इसलिए वहाँ `Binder.getCallingUid()`-आधारित कोई जाँच नहीं होती, इसलिए हम उस interface द्वारा प्रदान की गई विधियों को सीधे कॉल कर सकते हैं

मैंने [अपने पिछले writeup में वर्णन किया है कि कैसे मैं `scheduleReceiver()` arguments में हेरफेर करके code execution प्राप्त करता हूँ](https://github.com/michalbednarski/ReparcelBug2#what-then-happens-within-handlereceiver)। अब स्थिति वही है, सिवाय इसके कि इस बार मैं स्वयं `scheduleReceiver()` को कॉल कर रहा हूँ जबकि उस समय मैं `system_server` द्वारा किए गए कॉल के arguments की व्याख्या के साथ छेड़छाड़ कर रहा था

# अतिरिक्त नोट्स

इस अनुभाग में मैं कुछ चीजों का वर्णन कर रहा हूँ जो अंततः इस मामले में उपयोगी&nbsp;नहीं&nbsp;रहीं, हालाँकि वे ऐसी विशेषताएँ हो सकती हैं जिनके बारे में जागरूक रहना उचित&nbsp;है या&nbsp;संभावित bugs हो सकती हैं

## अतिरिक्त नोट: `Bundle.clear()`सरलता के लिए, मैंने यहाँ अद्यतन `Bundle` का वर्णन उस [कमिट के बिना किया है जो बाद में पेश किया गया था, जो `Bundle` में `LazyValue`s को बैक करने के लिए उपयोग किए गए `Parcel` को `Bundle.clear()` कॉल करके रीसायकल करने की अनुमति देता है](https://android.googlesource.com/platform/frameworks/base/+/1b74a666d3b4c6a5bf063671eb5dac62a74a9c21%5E%21/)

जैसा कि कमिट संदेश में बताया गया है, यह ट्रैक किया जाता है कि `Bundle` कॉपी किया गया है या नहीं और उस स्थिति में `clear()` `Parcel` को रीसायकल नहीं करेगा

हालाँकि वह कमिट [`BaseBundle.initializeFromParcelLocked()`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/BaseBundle.java;l=408-457;drc=52ae6c85e4151bb0d6c7700ae4f3a5eb697cd3c1) के `recycleParcel` पैरामीटर/चर के अर्थ को भी बदलता है

पहले `recycleParcel` का `false` होना यह संकेत देता था कि `Parcel` को रीसायकल नहीं किया जाना चाहिए, या तो इसलिए क्योंकि [कॉलर ने `recycleParcel` को `false` सेट किया था ताकि यह संकेत दिया जा सके कि `Parcel` `Bundle` के स्वामित्व में नहीं है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/BaseBundle.java;l=1842-1847;drc=52ae6c85e4151bb0d6c7700ae4f3a5eb697cd3c1) या फिर [`parcelledData.readArrayMap()` के परिणाम के आधार पर इसे `false` सेट किया गया था](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/BaseBundle.java;l=441;drc=52ae6c85e4151bb0d6c7700ae4f3a5eb697cd3c1)

अब `recycleParcel` के `false` होने के कारण वही हैं, हालाँकि इसकी व्याख्या बदल गई है, अब इसका मतलब "इस `Parcel` को रीसायकल न करें" नहीं है, बल्कि इसका मतलब है "`Parcel` के रीसायकल को `Bundle.clear()` कॉल तक स्थगित करें"

इसका मतलब है कि यदि `clear()` को ऐसे `Bundle` पर कॉल किया जाता है जो `Parcel.hasReadWriteHelper()` के `true` होने पर बनाया गया था, तो इससे `Parcel` रीसायकल हो जाएगा, जबकि उस `Bundle` के निर्माण को आह्वान करने वाला कोड भी उस `Parcel` को रीसायकल करेगा, जिससे दोहरी `recycle()` होगी, जो double-free जैसा व्यवहार उत्पन्न करती है: `Parcel.obtain()` के अगले कॉल एक ही ऑब्जेक्ट को दो बार लौटाएँगे

हालाँकि, मुझे ऐसे `Bundle` पर `clear()` बुलाए जाने का कोई तरीका नहीं मिला

चूँकि मैंने मूल रूप से यह लिखा था, [`recycle()` का व्यवहार बदल दिया गया है और अब अतिरिक्त recycle कोई-क्रिया नहीं है और `Log.wtf()` के माध्यम से संभावित क्रैश हो सकता है](https://android.googlesource.com/platform/frameworks/base/+/64ff38669a0e1f945b54c4c62ed9316282a6588d%5E%21/) ([कॉन्फ़िगरेशन पर निर्भर करता है, लेकिन `system_server` को कभी क्रैश नहीं करता](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;l=8619-8648;drc=ab23aee04d50b9bdabd52481e77265e976558056)। मैं कहूँगा कि नया व्यवहार अभी भी खतरनाक हो सकता है, खासकर जब हमारे पास अन्य प्रक्रिया में हो रहे deserialization को प्रोग्रामेटिक रूप से रोकने की क्षमता हो, लेकिन double-recycle को संभालने का कोई अच्छा तरीका वास्तव में नहीं है

## अतिरिक्त नोट: `Binder` कॉल और म्यूटेक्स पुनःप्रवेश

`Binder` की एक बहुत प्रसिद्ध न होने वाली विशेषता यह है कि यह मूल थ्रेड पर पुनरावर्ती कॉलों को भेजने का समर्थन करता है

अर्थात् यदि प्रक्रिया A, प्रक्रिया B को सिंक्रोनस `Binder` कॉल करती है और फिर प्रक्रिया B उसी थ्रेड पर इसे संभालते समय प्रक्रिया A को सिंक्रोनस `Binder` कॉल करती है, तो प्रक्रिया A में वह कॉल उसी थ्रेड पर भेजी जाएगी जो प्रक्रिया B को मूल कॉल पूरा होने की प्रतीक्षा कर रहा है

दूसरी बात यह है कि Java में `synchronized () {}` अनुभाग पुनःप्रवेशी म्यूटेक्स हैं, जिसका अर्थ है कि यदि आप उसी थ्रेड से इसमें दो बार प्रवेश करते हैं, तो यह आपको अंदर आने देगा और डेडलॉक नहीं होगा

इसका अर्थ है कि सिद्धांत रूप में जब हम `ActivityTaskManagerService.mGlobalLock` को लॉक रख रहे हैं, हम फिर भी `startActivity(new Intent(Settings.ACTION_SETTINGS))` का उपयोग करके Settings ऐप शुरू कर सकते हैं और हम उस [`synchonized` ब्लॉक में सफलतापूर्वक प्रवेश करेंगे जिसे हम अवरुद्ध कर रहे हैं](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java;l=626;drc=8854e6eb5960c1a9b233fd0fa6e36a366b2f802d), हालाँकि उस `Activity` को शुरू करने में `Task` निर्माण भी शामिल है, जिसमें [`notifyTaskCreated()` को कॉल करना शामिल है, जो संदेश पोस्ट करता है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/TaskChangeNotificationController.java;l=424-429;drc=09f52aa440ab32e66dfabeb4cb40b72166930b4f) और इसलिए [`DisplayThread`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/DisplayThread.java;l=25-31;drc=92b9365f9e1ea5d735e8acb06f790604036ee547) पर जाता है, और [उसका संभालना](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/TaskChangeNotificationController.java;l=207-208;drc=09f52aa440ab32e66dfabeb4cb40b72166930b4f) [उस लॉक को अधिग्रहित करने का प्रयास करता है जिसे हम दूसरे थ्रेड से अवरुद्ध कर रहे हैं](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/TaskChangeNotificationController.java;l=320;drc=09f52aa440ab32e66dfabeb4cb40b72166930b4f)। इसलिए जब तक `ActivityTaskManagerService.mGlobalLock` जारी नहीं होता, `DisplayThread` थ्रेड अवरुद्ध रहेगा। बाद में, `Activity` शुरू करने की प्रक्रिया में [उसी थ्रेड पर ऐप प्रक्रिया शुरू करने के लिए संदेश पोस्ट करना](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java;l=4659-4664;drc=c76db81ef9d400ffed200f69c7bbda923cdb941c) शामिल है। इन सबका मतलब है कि इस स्थिति में ऐप प्रक्रिया तब तक शुरू नहीं होगी जब तक हम लॉक जारी नहीं करते और&nbsp;कारण यह है कि हम इस लॉक को पहली बार में इसलिए पकड़ रहे थे ताकि `attachApplication()` लेनदेन को पूरा होने से रोका जा सके ताकि हम उससे हैंडल हथिया सकें, लेकिन इस स्थिति में वह लेनदेन वास्तव में शुरू नहीं होगा

यहाँ तक कि यदि हम ऐसा `Activity` लॉन्च करते हैं जो वर्तमान `Task` का ही हिस्सा होगा (अर्थात, हम Settings ऐप से एक अलग `Activity` लॉन्च करेंगे, जो `android:launchMode="singleTask"` निर्दिष्ट नहीं करता है), उस प्रक्रिया में फिर भी [`notifyTaskDescriptionChanged()`](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/wm/TaskChangeNotificationController.java;l=444-450;drc=09f52aa440ab32e66dfabeb4cb40b72166930b4f) शामिल होगा, जिसका यहाँ वही प्रभाव है जो `notifyTaskCreated()` का है

इसलिए जबकि मेरा थ्रेड उन विधियों में कॉल कर सकता था जो `synchronized (ActivityTaskManagerService.mGlobalLock) {}` का उपयोग कर रही हैं, `startActivity()` के बाद नई ऐप प्रक्रिया शुरू करने में उस लॉक का उपयोग विभिन्न थ्रेड से शामिल था और यह इस मामले में उपयोगी नहीं था, इसलिए मैंने इसके बजाय `ContentProvider` के माध्यम से ऐप प्रक्रिया की शुरुआत को ट्रिगर करना चुना

## अतिरिक्त नोट: `IApplicationThread` का उपयोग करने के अन्य तरीके

`IApplicationThread` एक अत्यंत विशेषाधिकार प्राप्त हैंडल है, इसलिए मैं इसे प्राप्त करने के बाद इसका उपयोग करना पोस्ट-एक्सप्लॉइटेशन मानता हूँ

इस एक्सप्लॉइट में मैंने इसे सीधे लक्ष्य प्रक्रिया में कोड निष्पादन का अनुरोध करने के लिए उपयोग किया, इस तथ्य का लाभ उठाते हुए कि उस ऑपरेशन तक पहुँच क्षमता (capability) द्वारा नियंत्रित होती है (यहाँ लीक किए गए `Binder` ऑब्जेक्ट का कब्ज़ा) न कि `Binder.getCallingUid()` द्वारा

[`ApplicationThread.scheduleReceiver()` (जिसका उपयोग हमने यहाँ कोड निष्पादन का अनुरोध करने के लिए किया)](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java;l=985-997;drc=09f52aa440ab32e66dfabeb4cb40b72166930b4f) और `ApplicationThread` की अन्य विधियों में `Binder.getCallingUid()` जाँच जोड़ना (क्योंकि `scheduleReceiver()` `IApplicationThread` में एकमात्र ऐसी विधि नहीं है जो कोड लोडिंग की अनुमति देती है) फिर भी `IApplicationThread` का उपयोग करके किसी अन्य ऐप की प्रक्रिया में कोड लोड करने से नहीं रोकेगा, क्योंकि हमलावर लीक किए गए `IApplicationThread` को अपने स्वयं के स्थान पर `attachApplication()` में पारित कर सकता है

प्रक्रिया में कोड लोड करने के अलावा, `IApplicationThread` होने से उस प्रक्रिया के विशेषाधिकारों का उपयोग करके [`grantUriPermission()` करने की अनुमति मिलती है, जिससे वह हैंडल संबंधित है](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;l=5631-5632;drc=09f52aa440ab32e66dfabeb4cb40b72166930b4f)
टूल डाउनलोड करें
जो initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative); कॉल करता है
source
mParcelledData
Bundle
Parcel
recycleParcel
true
Parcel
Bundle
Parcel.recycle()
  • initializeFromParcel कॉल करता है recycleParcel &= parcelledData.readArrayMap(map, count, !parcelledByNative, /* lazy */ true, mClassLoader) ताकि key-value मैप सामग्री पढ़ी जा सके। कुंजियाँ Strings हैं और मान readLazyValue() का उपयोग करके पढ़े जाते हैं, जो उन मानों के लिए LazyValue ऑब्जेक्ट बनाता है जिनके प्रकार length उपसर्ग के साथ लिखे गए हैं। readArrayMap() एक मान लौटाता है जो दर्शाता है कि Parcel को रीसायकल करना ठीक है या नहीं। यदि कोई LazyValue ऑब्जेक्ट मौजूद थे तो recycleParcel को false पर सेट किया जाता है और जिस Parcel का संदर्भ LazyValues लेते हैं उसे रीसायकल नहीं किया जाएगा (इसका एक अपवाद है, लेकिन यह यहाँ प्रासंगिक नहीं है, मैं इसे "Additional note: Bundle.clear()" अनुभाग में वर्णित करूँगा)
  • जब unparcel() पूरा हो जाता है, mMap सेट (not null) होता है और String कुंजियों को या तो वास्तविक मानों से मैप करता है यदि वे तैयार हैं या LazyValue ऑब्जेक्ट्स से
  • उसके बाद, getValue() कॉल किया जाता है, जो कुंजी (String) को इंडेक्स (int) से मैप करता है और उसे getValueAt() को पास करता है
  • getValueAt() instanceof BiFunction के माध्यम से LazyValue का पता लगाता है और इसे डिसीरियलाइज़ करने के लिए apply() कॉल करता है
  • LazyValue.apply() Parcel को LazyValue.mPosition की स्थिति में रिवाइंड करता है और सामान्य Parcel.readValue() कॉल करता है जिसके बारे में मैं पहले ही कह चुका हूँ
  • सफल डिसीरियलाइज़ेशन पर LazyValue को mMap में बदल दिया जाता है, ताकि उसी कुंजी के लिए अगला Bundle.get*() कॉल सीधे मान लौटाए और LazyValue डिसीरियलाइज़ेशन दोहराया न जाए। जब Bundle को आगे भेजा जाता है तो वह मान दोबारा सीरियलाइज़ किया जाएगा, बजाय मूल डेटा की शब्दशः प्रतिलिपि बनाए जाने के (हालाँकि आगे भेजे गए Bundle को पढ़े जाने के बाद, वह मान फिर से LazyValue होगा और कोई भी संभावित writeToParcel/createFromParcel बेमेल अन्य मानों को प्रभावित नहीं कर पाएगा)
  • length
    maybeWriteSquashed()
    true
    Bundle
    Parcel.writeParcelable()
    Parcelable.writeToParcel
  • यदि हम Binder लेनदेन आकार की सीमा के करीब पहुँच गए हैं, तो 0 लिखा जाता है ताकि यह संकेत दिया जा सके कि इस लेनदेन में और कोई आइटम नहीं हैं और अगले आइटम दूसरे लेनदेन में भेजे जाएंगे
  • जब ParcelableListBinder ने पहले लेनदेन में निर्दिष्ट तत्वों की संख्या प्राप्त कर ली है, यह अपने कंस्ट्रक्टर को पारित लैम्ब्डा को लागू करता है, जो इस मामले में प्राप्त सूची को MediaSessionRecord.mQueue को सौंपता है
  • Binder
  • जब ParceledListSlice को Parcel से पढ़ा जाता है, यह पहले भाग को सीधे Parcel से पढ़ता है और फिर यदि सभी तत्व इनलाइन नहीं लिखे गए थे, तो इन आइटमों को प्राप्त करने के लिए यह Binder को कॉल करता है जो Parcel में लिखा गया था
  • Bundle
    Parcel
    Parcel
    Parcel.obtain() को कॉल करते हैं, जो Parcel.sOwnedPool का उपयोग करता है
    Binder
    सिस्टम Parcel.obtain(long obj) को कॉल करता है
    Parcel.sHolderPool का उपयोग करता है
    Parcel.recycle()
    Parcel ऑब्जेक्ट को उपयुक्त पूल में लौटाने
    RemoteViews
    Parcel
    ValueLeakerMaker क्लास
    makeOwnedLeaker
    makeHolderLeaker
    RemoteViews.readActionsFromParcel()
    getActionFromParcel()
    ReflectionAction
    सामान्य BaseReflectionAction पैरामीटर पढ़ने के बाद
    Bundle का निर्माण करता है