Android VPN վերագրում. getConnectionOwnerUid ընդդեմ /proc/net
Android 10-ը թաքցնում է /proc/net-ը հավելվածներից։ VPN-ը դեռ ստանում է փաթեթներ, բայց վերագրումը տեղափոխվում է getConnectionOwnerUid՝ իր tuple-ի և թույլտվությունների կանոններով։
Android 10-ում և ավելի նորերում /proc/net/tcp բացող հավելվածը ստանում է մուտքի մերժում՝ socket-ների աղյուսակի փոխարեն։ VPN ինտերֆեյսին դա չի հետաքրքրում. փաթեթները շարունակում են գալ, որովհետև թունելը երթուղավորման որոշում է, ոչ թե ֆայլի թույլտվություն։ Փոխվել է ավելի նեղ հարցի պատասխանը՝ «ո՞ր հավելվածն է տիրապետում այս կապին»։ Փոխարինողը ConnectivityManager.getConnectionOwnerUid-ն է, աջակցվող հարցումը յուրաքանչյուր կապի համար, և հին սովորության ու նոր API-ի միջև ընտրությունը կախված է նրանից, թե երեք աշխատանքներից որն ունեք իրականում՝ փաթեթներ ստանալ, հոսք վերագրել կամ օգտագործումը գումարել։
Սեփականատիրոջ հարցին պատասխանելու երկու եղանակ
API 29-ից առաջ արտոնյալ կամ root պրոցեսը կարող էր կարդալ միջուկի socket-ների աղյուսակները, համադրել քառյակը և իմանալ սեփականատեր UID-ը։ Դա մեկ ֆայլում տալիս էր ամբողջ սարքի գույքագրում։ API 29-ից ֆայլային համակարգի տեսքը սահմանափակ է, և աջակցվող ուղին յուրաքանչյուր կապի հարցումն է՝ պրոտոկոլով, տեղական հասցեով և հեռավոր հասցեով։
| Question | Legacy socket tables | getConnectionOwnerUid |
|---|---|---|
| Who owns this flow? | Scan and match the tuple | Ask once per tuple |
| Device-wide inventory? | Yes, when readable | No, one flow at a time |
| Historical usage? | No | No; that is NetworkStatsManager |
| Works without root? | No on modern releases | Yes, for authorized callers |
Համեմատությունը «հինն ընդդեմ նորի» չէ։ Դրանք երեք տարբեր աշխատանքներ են, որոնք նախկինում բավարարում էր մեկ ընթեռնելի ֆայլ, իսկ այժմ՝ երեք տարբեր ինտերֆեյսներ՝ երեք տարբեր պայմանագրերով։
Տեղական հասցեն հավելվածի ծայրն է
Մեթոդի ստորագրությունը վերցնում է պրոտոկոլ, տեղական socket-ի հասցե և հեռավոր socket-ի հասցե։ «Տեղականը» նշանակում է կապի հավելվածի կողմը, ոչ այն ծայրը, որը ստուգման պահին պատահաբար փաթեթի աղբյուրն է։ Հեռավոր սերվերից հավելված վերադարձող փաթեթի աղբյուրն ու նպատակակետը այդ սահմանման նկատմամբ շրջված են։
ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);Այս ուղղության սխալը ամենատարածված պատճառն է, որ աշխատող հարցումը սխալ պատասխան է տալիս կամ ընդհանրապես ոչ մեկը։ Գրանցեք երկու ծայրերը մինչև կանչը և հաստատեք զույգը այն կապի հետ, որը հավելվածը իրականում բացել է։ Հասցեների ընտանիքը նույնպես կարևոր է. IPv4-ով քարտեզագրված հասցեն և սեփական IPv6 հասցեն տարբեր քառյակներ են, ինչպես նաև նույն պորտերի TCP և UDP հոսքերը։
Լիազորումը և անհայտ արդյունքները պայմանագրի մասն են
Հարցումը սահմանափակված է լիազորված կանչողներով, ներառյալ համապատասխան օգտատիրոջ ակտիվ VPN հավելվածը։ Անկապ մոնիթորինգի հավելվածը չի կարող ենթադրել, որ կարող է թվարկել այլ պրոֆիլի կապերը, և չլիազորված կանչը կարող է նետել անվտանգության բացառություն՝ ընկերական սխալի փոխարեն։ Կարգավորեք երկու արդյունքներն էլ բացահայտ։
Բացակա սեփականատերը փաթեթի անուն չէ։ Հոսքը կարող էր փակվել փաթեթի և հարցման միջև, քառյակը կարող է թերի լինել, կամ կանչողին պարզապես չի թույլատրվում իմանալ։ Առաջին հաջող պատասխանը պահելը և հետագայում վերաօգտագործելը նոր կապը վերագրում է հին հավելվածին, ինչը ավելի վատ է, քան անհայտ հայտնելը։
Երբ վերագրումը ձախողվում է, երթևեկությունը դեռ որոշում է պահանջում. արգելափակել այնտեղ, որտեղ քաղաքականությունը պահանջում է, թույլատրել միայն ձեր հասկացած շրջանակը, կամ ցույց տալ բացը։ Այդ պահուստը պատկանում է դիզայնին, ոչ մեկնաբանությանը։
Հաշվառումը այլ հարց է
NetworkStatsManager-ը հաղորդում է օգտագործման մասին իր սեփական մուտքի կանոններով։ Այն կարող է ասել, թե որքան է UID-ը փոխանցել ժամանակահատվածում. այն չի կարող ասել կամայական կենդանի փաթեթի սեփականատիրոջը։ Ցանցի հնարավորությունները նկարագրում են ցանցի հատկություններ, ոչ թե socket-ների տեղեկատու։ Երեքը խառնելը ստեղծում է վահանակներ, որոնք հետևողական են թվում և այդպիսին չեն։
Պարզ դեպքում՝ որոշելու, թե որ հավելվածներն են օգտագործում թունելը, Android-ի աջակցվող թույլատրված և արգելված հավելվածների ցանկերն արդեն արտահայտում են քաղաքականությունը՝ առանց որևէ scraping-ի։ Պահեք հոսքի վերագրումը այն դեպքերի համար, որոնք իսկապես դրա կարիքն ունեն, և բյուջե նախատեսեք պահպանվող իրականացման համար, որը միասին կարգավորում է ուղղությունը, թույլտվությունները և անհայտ վիճակները։
Ընտրեք գործիքն ըստ աշխատանքի, ապա փորձարկեք եզրերը
Եթե պահանջը թունելի ընտրությունն է, օգտագործեք հարթակի ցանկը։ Եթե դա կենդանի վերագրում է, օգտագործեք սեփականատիրոջ հարցումը և յուրաքանչյուր անպատասխան քառյակ համարեք անհայտ։ Եթե դրանք պատմական գումարներ են, օգտագործեք stats API-ը։ Ապա փորձարկեք երեք եզրերը, որոնք կոտրում են միամիտ իրականացումները՝ հակառակ ուղղության փաթեթ, հարցումից առաջ փակվող հոսք և կանչող, որը ակտիվ VPN-ը չէ։
Այս ոչ մեկը չի պահանջում անջատել SELinux-ը, root անել հեռախոսը կամ միջուկի աղյուսակները դարձնել համաշխարհային ընթեռնելի։ Սահմանափակումը հենց իմաստն է. հարթակը վերագրումը տեղափոխեց պայմանագրի հետևում, որպեսզի պատասխանը հասանելի լինի այն բաղադրիչին, որն օրինականորեն դրա կարիքն ունի, և ոչ ոքի այլ։