Ոչ, VpnService հավելվածը չի կարող միացնել հեռախոսի VPN-ը
Հավելվածը տիրապետում է մեկ թունելի և դրա կյանքի ցիկլին, ոչ թե հեռախոսի VPN անջատիչին։ Համաձայնությունը կարելի է հետ վերցնել, միաժամանակ աշխատում է միայն մեկ VPN, և փաթեթները ձեզ մնում է վերլուծել։
Սխալ ենթադրությունն այն է, որ VpnService հավելվածը շրջում է համակարգային անջատիչը և դրանից հետո հեռախոսը պաշտպանված է։ Իրականում կատարվողը ավելի նեղ է՝ հավելվածը համաձայնություն է խնդրում, կառուցում է ինտերֆեյս, ֆայլի դեսկրիպտորի միջոցով կարդում և գրում է փաթեթներ, և դադարելիս վերադարձնում է ինտերֆեյսը։ Միաժամանակ կենդանի կարող է լինել միայն մեկ այդպիսի թունել, և համակարգը կարող է այն վերցնել առանց հարցնելու։ Մարդկանց կողմից արձանագրությանը վերագրվող շփոթեցնող վարքագծի մեծ մասը գալիս է հենց այդ սեփականության մոդելից։
Յուրաքանչյուր անցում կյանքի ցիկլում դիտարկեք որպես ձևավորման մաս, ոչ թե որպես եզրային դեպք։
Առասպել. հավելվածը միացնում է հեռախոսի VPN-ը
Հոսքը սկսվում է VpnService.prepare()-ով, որը վերադարձնում է Intent, որը օգտատերը պետք է ընդունի, և շարունակվում է establish()-ով, որը հետագայում դեռ կարող է ձախողվել այն պատճառներով, որոնք հավելվածը չի վերահսկում։ Երկրորդ թունելի ստեղծումը անջատում է առաջին ինտերֆեյսը և փոխարինում համակարգային ծանուցումը։ Այդ հաջորդականության ոչ մի կետում հավելվածը չի տիրապետում ամբողջ սարքի անջատիչին, այլ տիրապետում է իրեն տրված դեսկրիպտորին և դրա վրա ընկնող փաթեթներին։ Նույնիսկ մշտական ծանուցումը պատկանում է համակարգին, ոչ թե հավելվածի ձևավորմանը։
Քանի որ ինտերֆեյսը տրվում է, այլ ոչ թե խլվում, մեկ այլ հաճախորդ կարող է տեղը զբաղեցնել ցանկացած պահի։ Հավելվածն այդ մասին իմանում է հետադարձ կանչով, ոչ թե վերադարձվող արժեքով։
Հավելվածի հարկադիր դադարեցումը ծանուցումների վահանակում երևում է նույն պատճառով՝ համակարգը հաղորդում է, ինչ տվել է, ոչ թե ինչ է հավելվածն ընտրել ցուցադրել։
Առասպել. համաձայնությունը տրվում է մեկ անգամ
Երբ համակարգը թունելը հանձնում է մեկ այլ հաճախորդի, այն հետ կանչելու գործարք է ուղարկում այն binder-ին, որը բազային դասը վերադարձնում է onBind()-ից։ Հավելվածը, որը փոխարինում է այդ binder-ը իր սեփականով, պետք է ինքը փոխանցի հետ կանչելու գործարքը, այլապես VpnService.onRevoke() երբեք չի գործարկվի, և ծառայությունը կշարունակի գրել դեսկրիպտորի մեջ, որն այլևս գոյություն չունի։ Համաձայնությունը կարող է հետ վերցնել նաև օգտատերը, և համակարգը կարող է դադարեցնել ծառայությունը վերագործարկման կամ մշտապես միացված վերամիացման ընթացքում։
Դրա հետ վարվելը կոնկրետ աշխատանք է, ոչ թե մատյանի տող՝ դադարեցրեք դեսկրիպտորի ընթերցումը, փակեք այն, մաքրեք միացված դրոշակը և ասեք օգտատիրոջը, թե որ վիճակում է նա։ Այնուհետև որոշեք՝ հավելվածը պետք է վերականգնի, նորից հարցնի, թե սպասի, քանի որ մշտապես միացված կարգավորման դեպքում համակարգը կարող է ինքը վերադարձնել ծառայությունը։
Հավելվածը, որը հետ կանչելը դիտարկում է որպես բացառություն, այլ ոչ թե վիճակ, կհայտնի միացված լինելու մասին, մինչդեռ իր գրած յուրաքանչյուր փաթեթ դեն է նետվում, ինչը գաղտնիության գործիքի համար հնարավոր ամենավատ ձախողման ձևն է։
Առասպել. ինտերֆեյսը ձեր փոխարեն զտում է երթևեկությունը
Ինչ հասնում է, հում IP է։ Որսալը կամ զտելը նշանակում է վերլուծել IP, TCP, UDP և IPv6 վերնագրերը, հետևել հոսքերին և պատասխանները հետ գրել դեսկրիպտորին, ինչը որսացող հավելվածը դարձնում է փոքր ցանցային փաթեթ, այլ ոչ թե համակարգային միջոց։ protect() գոյություն ունի, որպեսզի հավելվածի սեփական տրանսպորտային սոքեթը շրջանցի իր թունելը՝ փոխանակ ինքն իրեն որսալու, և այն վերադարձնում է տրամաբանական արժեք, որն արժե ստուգել թունելի բարձրացումից հետո ստեղծված յուրաքանչյուր սոքեթում։
Կառուցիչի թույլատրված և արգելված հավելվածների ցանկերը որոշում են, թե ում երթևեկությունն է մտնում ինտերֆեյս։ Դրանք մուտք չեն տալիս ինտերֆեյսի կրած IP փաթեթներից այն կողմ բեռնվածքին, և ոչ մի API չի բացահայտում այլ հաճախորդի թունելը կամ թվարկում է համակարգի VPN պրոֆիլները։ Ցանցային հնարավորությունները կարող են ասել, որ ցանցը VPN տրանսպորտ է, բայց ոչ թե ումն է թունելը։
Այդ պատճառով այլ հավելվածների կարգավիճակի մասին հարցերը հակասական պատասխաններ են ստանում՝ տեղեկությունը պարզապես բացահայտված չէ ձեր պրոցեսին։
Ինչ է իրականում պահանջում կյանքի ցիկլը
Հետևեք ձեր վերահսկած վիճակներին՝ պատրաստված, հաստատված, հետ կանչված, դադարեցված։ Վերամիացումը պետք է լինի ապամոնտաժում, ապա հաստատում, դեսկրիպտորի և տրանսպորտային սոքեթի մեկ տիրոջով, քանի որ հաստատելու համար մրցող երկու բաղադրիչ կպայքարեն միակ ազատ տեղի համար։ Ժամանակակից Android-ում ֆոնային մեկնարկները սահմանափակ են, ուստի boot հեռարձակումը երաշխիք չէ, օգտագործեք առաջին պլանի ծառայություն իր ծանուցմամբ այնտեղ, որտեղ API մակարդակը թույլ է տալիս։
Գրանցեք անցումը, ոչ միայն վերջնական վիճակը, և պահեք վիճակների մեքենան բավական փոքր, որպեսզի ստուգեք յուրաքանչյուր ուղին։ Վիճակը, որին հնարավոր չէ հասնել փորձարկման ժամանակ, այն վիճակն է, որին դաշտում կհանդիպեք առանց մատյանի։
Փորձարկեք կյանքի ցիկլը սարքի վրա
Էմուլյատորները կարող են կեղծել կապը, բայց ոչ համոզիչ կերպով համաձայնության և հետ կանչելու ուղին, ուստի դրանք գործարկեք իրական սարքում, որտեղ տեղը գողանալու համար տեղադրված է երկրորդ VPN հաճախորդ։
adb shell dumpsys connectivity | grep -i -A3 vpn
adb shell dumpsys activity services | grep -i -A2 vpnservice
adb logcat -s VpnService:V AndroidRuntime:E
adb shell am force-stop com.example.vpnapp
Մեկնարկեք թունելը, հաստատեք ծանուցումը և ինտերֆեյսը, ապա միացրեք մյուս հաճախորդին և հետևեք՝ արդյոք գործարկվում է հետ կանչելու հետադարձ կանչը և մաքրվում է վիճակը։ Հարկադիր դադարեցրեք հավելվածը և ստուգեք՝ արդյոք ինչ-որ բան դեռ հաղորդում է կենդանի դեսկրիպտորի մասին։
Կրկնեք հաջորդականությունը մշտապես միացված վերագործարկումից հետո, քանի որ այդ ուղին տարբերվում է օգտատիրոջ կողմից սկսված միացումից։ Դուք փնտրում եք ոչ թե կանաչ պատկերակ, այլ ապացույց, որ հավելվածը դադարեց օգտագործել դեսկրիպտոր, որն այլևս իրենը չէր։