UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

పరిచయం

మార్చి చివరిలో మేము నివేదించారు, UC బ్రౌజర్‌లో ధృవీకరించని కోడ్‌ను లోడ్ చేయడానికి మరియు అమలు చేయడానికి వారు దాచిన సామర్థ్యాన్ని కనుగొన్నారు. ఈ డౌన్‌లోడ్ ఎలా జరుగుతుందో మరియు హ్యాకర్లు తమ స్వంత ప్రయోజనాల కోసం దీన్ని ఎలా ఉపయోగించవచ్చో ఈ రోజు మనం వివరంగా పరిశీలిస్తాము.

కొంతకాలం క్రితం, UC బ్రౌజర్ చాలా దూకుడుగా ప్రచారం చేయబడింది మరియు పంపిణీ చేయబడింది: ఇది మాల్వేర్‌ని ఉపయోగించి వినియోగదారుల పరికరాలలో ఇన్‌స్టాల్ చేయబడింది, వీడియో ఫైల్‌ల ముసుగులో వివిధ సైట్‌ల నుండి పంపిణీ చేయబడింది (అంటే, వినియోగదారులు తాము డౌన్‌లోడ్ చేస్తున్నట్లు భావించారు, ఉదాహరణకు, పోర్న్ వీడియో, కానీ బదులుగా ఈ బ్రౌజర్‌తో APKని అందుకున్నారు), బ్రౌజర్ పాతది, హాని కలిగించేది మరియు అలాంటి అంశాలు వంటి సందేశాలతో భయానక బ్యానర్‌లను ఉపయోగించారు. VKలోని అధికారిక UC బ్రౌజర్ సమూహంలో ఉంది థీమ్, దీనిలో వినియోగదారులు అన్యాయమైన ప్రకటనల గురించి ఫిర్యాదు చేయవచ్చు, అక్కడ చాలా ఉదాహరణలు ఉన్నాయి. 2016లో కూడా ఉంది వీడియో ప్రకటన రష్యన్ భాషలో (అవును, యాడ్-బ్లాకింగ్ బ్రౌజర్ కోసం ప్రకటనలు).

వ్రాసే సమయంలో, UC బ్రౌజర్ Google Playలో 500కి పైగా ఇన్‌స్టాలేషన్‌లను కలిగి ఉంది. ఇది ఆకట్టుకుంటుంది - Google Chrome మాత్రమే ఎక్కువ కలిగి ఉంది. సమీక్షల్లో మీరు Google Playలో కొన్ని అప్లికేషన్‌లకు ప్రకటనలు మరియు దారి మళ్లింపుల గురించి చాలా ఫిర్యాదులను చూడవచ్చు. ఇది మా పరిశోధనకు కారణం: UC బ్రౌజర్ ఏదైనా చెడు చేస్తుందో లేదో చూడాలని మేము నిర్ణయించుకున్నాము. మరియు అతను చేస్తాడని తేలింది!

అప్లికేషన్ కోడ్‌లో, ఎక్జిక్యూటబుల్ కోడ్‌ను డౌన్‌లోడ్ చేసి, అమలు చేయగల సామర్థ్యం కనుగొనబడింది, ఇది అప్లికేషన్‌లను ప్రచురించే నిబంధనలకు విరుద్ధం Google Playలో. ఎక్జిక్యూటబుల్ కోడ్‌ని డౌన్‌లోడ్ చేయడంతో పాటు, UC బ్రౌజర్ అసురక్షిత పద్ధతిలో చేస్తుంది, ఇది MitM దాడిని ప్రారంభించడానికి ఉపయోగించవచ్చు. మరి అలాంటి దాడి చేస్తారేమో చూద్దాం.

దిగువ వ్రాసిన ప్రతిదీ అధ్యయనం సమయంలో Google Playలో అందుబాటులో ఉన్న UC బ్రౌజర్ సంస్కరణకు సంబంధించినది:

package: com.UCMobile.intl
versionName: 12.10.8.1172
versionCode: 10598
sha1 APK-файла: f5edb2243413c777172f6362876041eb0c3a928c

దాడి వెక్టర్

UC బ్రౌజర్ మానిఫెస్ట్‌లో మీరు స్వీయ వివరణాత్మక పేరుతో సేవను కనుగొనవచ్చు com.uc.deployment.UpgradeDeployService.

    <service android_exported="false" android_name="com.uc.deployment.UpgradeDeployService" android_process=":deploy" />

ఈ సేవ ప్రారంభమైనప్పుడు, బ్రౌజర్ దీనికి POST అభ్యర్థనను చేస్తుంది puds.ucweb.com/upgrade/index.xhtml, ఇది ప్రారంభమైన కొంత సమయం తర్వాత ట్రాఫిక్‌లో చూడవచ్చు. ప్రతిస్పందనగా, అతను కొంత నవీకరణ లేదా కొత్త మాడ్యూల్‌ను డౌన్‌లోడ్ చేయమని ఆదేశాన్ని అందుకోవచ్చు. విశ్లేషణ సమయంలో, సర్వర్ అటువంటి ఆదేశాలను ఇవ్వలేదు, కానీ మేము బ్రౌజర్‌లో PDFని తెరవడానికి ప్రయత్నించినప్పుడు, ఎగువ పేర్కొన్న చిరునామాకు ఇది రెండవ అభ్యర్థనను చేస్తుంది, ఆ తర్వాత అది స్థానిక లైబ్రరీని డౌన్‌లోడ్ చేస్తుంది. దాడిని నిర్వహించడానికి, మేము UC బ్రౌజర్ యొక్క ఈ లక్షణాన్ని ఉపయోగించాలని నిర్ణయించుకున్నాము: స్థానిక లైబ్రరీని ఉపయోగించి PDFని తెరవగల సామర్థ్యం, ​​ఇది APKలో లేదు మరియు అవసరమైతే ఇంటర్నెట్ నుండి డౌన్‌లోడ్ చేస్తుంది. మీరు బ్రౌజర్ ప్రారంభించిన తర్వాత అమలు చేయబడిన అభ్యర్థనకు బాగా రూపొందించిన ప్రతిస్పందనను అందించినట్లయితే - సిద్ధాంతపరంగా, UC బ్రౌజర్ వినియోగదారు పరస్పర చర్య లేకుండా ఏదైనా డౌన్‌లోడ్ చేయవలసి ఉంటుంది. కానీ దీన్ని చేయడానికి, మేము సర్వర్‌తో పరస్పర చర్య యొక్క ప్రోటోకాల్‌ను మరింత వివరంగా అధ్యయనం చేయాలి, కాబట్టి అంతరాయం కలిగించిన ప్రతిస్పందనను సవరించడం మరియు PDFతో పని చేయడానికి లైబ్రరీని భర్తీ చేయడం సులభం అని మేము నిర్ణయించుకున్నాము.

కాబట్టి, వినియోగదారు నేరుగా బ్రౌజర్‌లో PDFని తెరవాలనుకున్నప్పుడు, కింది అభ్యర్థనలను ట్రాఫిక్‌లో చూడవచ్చు:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ముందుగా ఒక POST అభ్యర్థన ఉంది puds.ucweb.com/upgrade/index.xhtmlదాని తరువాత
PDF మరియు ఆఫీస్ ఫార్మాట్‌లను వీక్షించడానికి లైబ్రరీతో కూడిన ఆర్కైవ్ డౌన్‌లోడ్ చేయబడింది. మొదటి అభ్యర్థన సిస్టమ్ గురించి సమాచారాన్ని ప్రసారం చేస్తుందని భావించడం తార్కికం (కనీసం అవసరమైన లైబ్రరీని అందించడానికి ఆర్కిటెక్చర్), మరియు దానికి ప్రతిస్పందనగా బ్రౌజర్ డౌన్‌లోడ్ చేయవలసిన లైబ్రరీ గురించి కొంత సమాచారాన్ని పొందుతుంది: చిరునామా మరియు, బహుశా , ఇంకేదో. సమస్య ఏమిటంటే ఈ అభ్యర్థన గుప్తీకరించబడింది.

భాగాన్ని అభ్యర్థించండి

జవాబు భాగం

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

లైబ్రరీ జిప్‌లో ప్యాక్ చేయబడింది మరియు గుప్తీకరించబడలేదు.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ట్రాఫిక్ డిక్రిప్షన్ కోడ్ కోసం శోధించండి

సర్వర్ ప్రతిస్పందనను అర్థంచేసుకోవడానికి ప్రయత్నిద్దాం. క్లాస్ కోడ్ చూద్దాం com.uc.deployment.UpgradeDeployService: పద్ధతి నుండి ఆన్‌స్టార్ట్ కమాండ్ వెళ్ళండి com.uc.deployment.bx, మరియు దాని నుండి com.uc.browser.core.dcfe:

    public final void e(l arg9) {
int v4_5;
String v3_1;
byte[] v3;
byte[] v1 = null;
if(arg9 == null) {
v3 = v1;
}
else {
v3_1 = arg9.iGX.ipR;
StringBuilder v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]product:");
v4.append(arg9.iGX.ipR);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]version:");
v4.append(arg9.iGX.iEn);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]upgrade_type:");
v4.append(arg9.iGX.mMode);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]force_flag:");
v4.append(arg9.iGX.iEo);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]silent_mode:");
v4.append(arg9.iGX.iDQ);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]silent_type:");
v4.append(arg9.iGX.iEr);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]silent_state:");
v4.append(arg9.iGX.iEp);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]silent_file:");
v4.append(arg9.iGX.iEq);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]apk_md5:");
v4.append(arg9.iGX.iEl);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]download_type:");
v4.append(arg9.mDownloadType);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]download_group:");
v4.append(arg9.mDownloadGroup);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]download_path:");
v4.append(arg9.iGH);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]apollo_child_version:");
v4.append(arg9.iGX.iEx);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]apollo_series:");
v4.append(arg9.iGX.iEw);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]apollo_cpu_arch:");
v4.append(arg9.iGX.iEt);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]apollo_cpu_vfp3:");
v4.append(arg9.iGX.iEv);
v4 = new StringBuilder("[");
v4.append(v3_1);
v4.append("]apollo_cpu_vfp:");
v4.append(arg9.iGX.iEu);
ArrayList v3_2 = arg9.iGX.iEz;
if(v3_2 != null && v3_2.size() != 0) {
Iterator v3_3 = v3_2.iterator();
while(v3_3.hasNext()) {
Object v4_1 = v3_3.next();
StringBuilder v5 = new StringBuilder("[");
v5.append(((au)v4_1).getName());
v5.append("]component_name:");
v5.append(((au)v4_1).getName());
v5 = new StringBuilder("[");
v5.append(((au)v4_1).getName());
v5.append("]component_ver_name:");
v5.append(((au)v4_1).aDA());
v5 = new StringBuilder("[");
v5.append(((au)v4_1).getName());
v5.append("]component_ver_code:");
v5.append(((au)v4_1).gBl);
v5 = new StringBuilder("[");
v5.append(((au)v4_1).getName());
v5.append("]component_req_type:");
v5.append(((au)v4_1).gBq);
}
}
j v3_4 = new j();
m.b(v3_4);
h v4_2 = new h();
m.b(v4_2);
ay v5_1 = new ay();
v3_4.hS("");
v3_4.setImsi("");
v3_4.hV("");
v5_1.bPQ = v3_4;
v5_1.bPP = v4_2;
v5_1.yr(arg9.iGX.ipR);
v5_1.gBF = arg9.iGX.mMode;
v5_1.gBI = arg9.iGX.iEz;
v3_2 = v5_1.gAr;
c.aBh();
v3_2.add(g.fs("os_ver", c.getRomInfo()));
v3_2.add(g.fs("processor_arch", com.uc.b.a.a.c.getCpuArch()));
v3_2.add(g.fs("cpu_arch", com.uc.b.a.a.c.Pb()));
String v4_3 = com.uc.b.a.a.c.Pd();
v3_2.add(g.fs("cpu_vfp", v4_3));
v3_2.add(g.fs("net_type", String.valueOf(com.uc.base.system.a.Jo())));
v3_2.add(g.fs("fromhost", arg9.iGX.iEm));
v3_2.add(g.fs("plugin_ver", arg9.iGX.iEn));
v3_2.add(g.fs("target_lang", arg9.iGX.iEs));
v3_2.add(g.fs("vitamio_cpu_arch", arg9.iGX.iEt));
v3_2.add(g.fs("vitamio_vfp", arg9.iGX.iEu));
v3_2.add(g.fs("vitamio_vfp3", arg9.iGX.iEv));
v3_2.add(g.fs("plugin_child_ver", arg9.iGX.iEx));
v3_2.add(g.fs("ver_series", arg9.iGX.iEw));
v3_2.add(g.fs("child_ver", r.aVw()));
v3_2.add(g.fs("cur_ver_md5", arg9.iGX.iEl));
v3_2.add(g.fs("cur_ver_signature", SystemHelper.getUCMSignature()));
v3_2.add(g.fs("upgrade_log", i.bjt()));
v3_2.add(g.fs("silent_install", String.valueOf(arg9.iGX.iDQ)));
v3_2.add(g.fs("silent_state", String.valueOf(arg9.iGX.iEp)));
v3_2.add(g.fs("silent_file", arg9.iGX.iEq));
v3_2.add(g.fs("silent_type", String.valueOf(arg9.iGX.iEr)));
v3_2.add(g.fs("cpu_archit", com.uc.b.a.a.c.Pc()));
v3_2.add(g.fs("cpu_set", SystemHelper.getCpuInstruction()));
boolean v4_4 = v4_3 == null || !v4_3.contains("neon") ? false : true;
v3_2.add(g.fs("neon", String.valueOf(v4_4)));
v3_2.add(g.fs("cpu_cores", String.valueOf(com.uc.b.a.a.c.Jl())));
v3_2.add(g.fs("ram_1", String.valueOf(com.uc.b.a.a.h.Po())));
v3_2.add(g.fs("totalram", String.valueOf(com.uc.b.a.a.h.OL())));
c.aBh();
v3_2.add(g.fs("rom_1", c.getRomInfo()));
v4_5 = e.getScreenWidth();
int v6 = e.getScreenHeight();
StringBuilder v7 = new StringBuilder();
v7.append(v4_5);
v7.append("*");
v7.append(v6);
v3_2.add(g.fs("ss", v7.toString()));
v3_2.add(g.fs("api_level", String.valueOf(Build$VERSION.SDK_INT)));
v3_2.add(g.fs("uc_apk_list", SystemHelper.getUCMobileApks()));
Iterator v4_6 = arg9.iGX.iEA.entrySet().iterator();
while(v4_6.hasNext()) {
Object v6_1 = v4_6.next();
v3_2.add(g.fs(((Map$Entry)v6_1).getKey(), ((Map$Entry)v6_1).getValue()));
}
v3 = v5_1.toByteArray();
}
if(v3 == null) {
this.iGY.iGI.a(arg9, "up_encode", "yes", "fail");
return;
}
v4_5 = this.iGY.iGw ? 0x1F : 0;
if(v3 == null) {
}
else {
v3 = g.i(v4_5, v3);
if(v3 == null) {
}
else {
v1 = new byte[v3.length + 16];
byte[] v6_2 = new byte[16];
Arrays.fill(v6_2, 0);
v6_2[0] = 0x5F;
v6_2[1] = 0;
v6_2[2] = ((byte)v4_5);
v6_2[3] = -50;
System.arraycopy(v6_2, 0, v1, 0, 16);
System.arraycopy(v3, 0, v1, 16, v3.length);
}
}
if(v1 == null) {
this.iGY.iGI.a(arg9, "up_encrypt", "yes", "fail");
return;
}
if(TextUtils.isEmpty(this.iGY.mUpgradeUrl)) {
this.iGY.iGI.a(arg9, "up_url", "yes", "fail");
return;
}
StringBuilder v0 = new StringBuilder("[");
v0.append(arg9.iGX.ipR);
v0.append("]url:");
v0.append(this.iGY.mUpgradeUrl);
com.uc.browser.core.d.c.i v0_1 = this.iGY.iGI;
v3_1 = this.iGY.mUpgradeUrl;
com.uc.base.net.e v0_2 = new com.uc.base.net.e(new com.uc.browser.core.d.c.i$a(v0_1, arg9));
v3_1 = v3_1.contains("?") ? v3_1 + "&dataver=pb" : v3_1 + "?dataver=pb";
n v3_5 = v0_2.uc(v3_1);
m.b(v3_5, false);
v3_5.setMethod("POST");
v3_5.setBodyProvider(v1);
v0_2.b(v3_5);
this.iGY.iGI.a(arg9, "up_null", "yes", "success");
this.iGY.iGI.b(arg9);
}

మేము ఇక్కడ POST అభ్యర్థన ఏర్పడటాన్ని చూస్తాము. మేము 16 బైట్‌ల శ్రేణిని సృష్టించడం మరియు దాని పూరకంపై శ్రద్ధ చూపుతాము: 0x5F, 0, 0x1F, -50 (=0xCE). ఎగువ అభ్యర్థనలో మనం చూసిన దానితో సమానంగా ఉంటుంది.

అదే తరగతిలో మీరు మరొక ఆసక్తికరమైన పద్ధతిని కలిగి ఉన్న సమూహ తరగతిని చూడవచ్చు:

        public final void a(l arg10, byte[] arg11) {
f v0 = this.iGQ;
StringBuilder v1 = new StringBuilder("[");
v1.append(arg10.iGX.ipR);
v1.append("]:UpgradeSuccess");
byte[] v1_1 = null;
if(arg11 == null) {
}
else if(arg11.length < 16) {
}
else {
if(arg11[0] != 0x60 && arg11[3] != 0xFFFFFFD0) {
goto label_57;
}
int v3 = 1;
int v5 = arg11[1] == 1 ? 1 : 0;
if(arg11[2] != 1 && arg11[2] != 11) {
if(arg11[2] == 0x1F) {
}
else {
v3 = 0;
}
}
byte[] v7 = new byte[arg11.length - 16];
System.arraycopy(arg11, 16, v7, 0, v7.length);
if(v3 != 0) {
v7 = g.j(arg11[2], v7);
}
if(v7 == null) {
goto label_57;
}
if(v5 != 0) {
v1_1 = g.P(v7);
goto label_57;
}
v1_1 = v7;
}
label_57:
if(v1_1 == null) {
v0.iGY.iGI.a(arg10, "up_decrypt", "yes", "fail");
return;
}
q v11 = g.b(arg10, v1_1);
if(v11 == null) {
v0.iGY.iGI.a(arg10, "up_decode", "yes", "fail");
return;
}
if(v0.iGY.iGt) {
v0.d(arg10);
}
if(v0.iGY.iGo != null) {
v0.iGY.iGo.a(0, ((o)v11));
}
if(v0.iGY.iGs) {
v0.iGY.a(((o)v11));
v0.iGY.iGI.a(v11, "up_silent", "yes", "success");
v0.iGY.iGI.a(v11);
return;
}
v0.iGY.iGI.a(v11, "up_silent", "no", "success");
}
}

ఈ పద్ధతి బైట్‌ల శ్రేణిని ఇన్‌పుట్‌గా తీసుకుంటుంది మరియు సున్నా బైట్ 0x60 లేదా మూడవ బైట్ 0xD0 మరియు రెండవ బైట్ 1, 11 లేదా 0x1F అని తనిఖీ చేస్తుంది. మేము సర్వర్ నుండి ప్రతిస్పందనను పరిశీలిస్తాము: సున్నా బైట్ 0x60, రెండవది 0x1F, మూడవది 0x60. మనకు కావాల్సింది అనిపిస్తుంది. పంక్తుల ద్వారా నిర్ణయించడం ("up_decrypt", ఉదాహరణకు), సర్వర్ ప్రతిస్పందనను డీక్రిప్ట్ చేసే పద్ధతిని ఇక్కడ పిలవాలి.
పద్ధతికి వెళ్దాం gj. మొదటి ఆర్గ్యుమెంట్ ఆఫ్‌సెట్ 2 వద్ద ఉన్న బైట్ (అనగా మా విషయంలో 0x1F), మరియు రెండవది సర్వర్ ప్రతిస్పందన లేకుండా ఉంటుంది.
మొదటి 16 బైట్లు.

     public static byte[] j(int arg1, byte[] arg2) {
if(arg1 == 1) {
arg2 = c.c(arg2, c.adu);
}
else if(arg1 == 11) {
arg2 = m.aF(arg2);
}
else if(arg1 != 0x1F) {
}
else {
arg2 = EncryptHelper.decrypt(arg2);
}
return arg2;
}

సహజంగానే, ఇక్కడ మనం ఒక డిక్రిప్షన్ అల్గారిథమ్‌ని ఎంచుకుంటాము మరియు మాలో ఉన్న అదే బైట్
కేసు 0x1Fకి సమానం, మూడు సాధ్యమైన ఎంపికలలో ఒకదానిని సూచిస్తుంది.

మేము కోడ్‌ను విశ్లేషించడం కొనసాగిస్తాము. రెండు జంప్‌ల తర్వాత మనం స్వీయ-వివరణాత్మక పేరుతో ఒక పద్ధతిని కనుగొంటాము decryptBytesByKey.

ఇక్కడ మా ప్రతిస్పందన నుండి మరో రెండు బైట్‌లు వేరు చేయబడ్డాయి మరియు వాటి నుండి స్ట్రింగ్ పొందబడింది. ఈ విధంగా సందేశాన్ని డీక్రిప్ట్ చేయడానికి కీ ఎంపిక చేయబడిందని స్పష్టమవుతుంది.

    private static byte[] decryptBytesByKey(byte[] bytes) {
byte[] v0 = null;
if(bytes != null) {
try {
if(bytes.length < EncryptHelper.PREFIX_BYTES_SIZE) {
}
else if(bytes.length == EncryptHelper.PREFIX_BYTES_SIZE) {
return v0;
}
else {
byte[] prefix = new byte[EncryptHelper.PREFIX_BYTES_SIZE];  // 2 байта
System.arraycopy(bytes, 0, prefix, 0, prefix.length);
String keyId = c.ayR().d(ByteBuffer.wrap(prefix).getShort()); // Выбор ключа
if(keyId == null) {
return v0;
}
else {
a v2 = EncryptHelper.ayL();
if(v2 == null) {
return v0;
}
else {
byte[] enrypted = new byte[bytes.length - EncryptHelper.PREFIX_BYTES_SIZE];
System.arraycopy(bytes, EncryptHelper.PREFIX_BYTES_SIZE, enrypted, 0, enrypted.length);
return v2.l(keyId, enrypted);
}
}
}
}
catch(SecException v7_1) {
EncryptHelper.handleDecryptException(((Throwable)v7_1), v7_1.getErrorCode());
return v0;
}
catch(Throwable v7) {
EncryptHelper.handleDecryptException(v7, 2);
return v0;
}
}
return v0;
}

ముందుకు చూస్తే, ఈ దశలో మనం ఇంకా కీని పొందలేదని, కానీ దాని “ఐడెంటిఫైయర్” మాత్రమేనని మేము గమనించాము. కీని పొందడం కొంచెం క్లిష్టంగా ఉంటుంది.

తదుపరి పద్ధతిలో, ఇప్పటికే ఉన్న వాటికి మరో రెండు పారామితులు జోడించబడతాయి, వాటిలో నాలుగు: మ్యాజిక్ నంబర్ 16, కీ ఐడెంటిఫైయర్, ఎన్‌క్రిప్టెడ్ డేటా మరియు అపారమయిన స్ట్రింగ్ (మా విషయంలో, ఖాళీ).

    public final byte[] l(String keyId, byte[] encrypted) throws SecException {
return this.ayJ().staticBinarySafeDecryptNoB64(16, keyId, encrypted, "");
}

పరివర్తనల శ్రేణి తర్వాత మేము పద్ధతికి వస్తాము staticBinarySafeDecryptNoB64 ఇంటర్ఫేస్ com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. ఈ ఇంటర్‌ఫేస్‌ని అమలు చేసే ప్రధాన అప్లికేషన్ కోడ్‌లో తరగతులు లేవు. ఫైల్‌లో అటువంటి తరగతి ఉంది lib/armeabi-v7a/libsgmain.so, ఇది నిజానికి a .so కాదు, కానీ a .jar. మాకు ఆసక్తి ఉన్న పద్ధతి క్రింది విధంగా అమలు చేయబడుతుంది:

package com.alibaba.wireless.security.a.i;
// ...
public class a implements IStaticDataEncryptComponent {
private ISecurityGuardPlugin a;
// ...
private byte[] a(int mode, int magicInt, int xzInt, String keyId, byte[] encrypted, String magicString) {
return this.a.getRouter().doCommand(10601, new Object[]{Integer.valueOf(mode), Integer.valueOf(magicInt), Integer.valueOf(xzInt), keyId, encrypted, magicString});
}
// ...
private byte[] b(int magicInt, String keyId, byte[] encrypted, String magicString) {
return this.a(2, magicInt, 0, keyId, encrypted, magicString);
}
// ...
public byte[] staticBinarySafeDecryptNoB64(int magicInt, String keyId, byte[] encrypted, String magicString) throws SecException {
if(keyId != null && keyId.length() > 0 && magicInt >= 0 && magicInt < 19 && encrypted != null && encrypted.length > 0) {
return this.b(magicInt, keyId, encrypted, magicString);
}
throw new SecException("", 301);
}
//...
}

ఇక్కడ మా పారామితుల జాబితా మరో రెండు పూర్ణాంకాలతో అనుబంధించబడింది: 2 మరియు 0. దీని ద్వారా నిర్ణయించడం
ప్రతిదీ, 2 అంటే డిక్రిప్షన్, పద్ధతిలో వలె ఫైనల్ సిస్టమ్ తరగతి javax.crypto.Cipher. మరియు ఇవన్నీ 10601 సంఖ్యతో నిర్దిష్ట రూటర్‌కు బదిలీ చేయబడతాయి - ఇది స్పష్టంగా కమాండ్ నంబర్.

పరివర్తనల తదుపరి గొలుసు తర్వాత మేము ఇంటర్‌ఫేస్‌ను అమలు చేసే తరగతిని కనుగొంటాము IRouterComponent మరియు పద్ధతి doCommand:

package com.alibaba.wireless.security.mainplugin;
import com.alibaba.wireless.security.framework.IRouterComponent;
import com.taobao.wireless.security.adapter.JNICLibrary;
public class a implements IRouterComponent {
public a() {
super();
}
public Object doCommand(int arg2, Object[] arg3) {
return JNICLibrary.doCommandNative(arg2, arg3);
}
}

మరియు తరగతి కూడా JNIC లైబ్రరీ, దీనిలో స్థానిక పద్ధతి ప్రకటించబడింది doCommandNative:

package com.taobao.wireless.security.adapter;
public class JNICLibrary {
public static native Object doCommandNative(int arg0, Object[] arg1);
}

అంటే మనం స్థానిక కోడ్‌లో ఒక పద్ధతిని కనుగొనవలసి ఉంటుంది doCommandNative. మరియు ఇక్కడే సరదా ప్రారంభమవుతుంది.

యంత్రం కోడ్ యొక్క అస్పష్టత

ఫైల్‌లో libsgmain.so (ఇది వాస్తవానికి .jar మరియు దీనిలో కొన్ని గుప్తీకరణ-సంబంధిత ఇంటర్‌ఫేస్‌ల అమలును మేము పైన కనుగొన్నాము) ఒక స్థానిక లైబ్రరీ ఉంది: libsgmainso-6.4.36.so. మేము దానిని IDAలో తెరిచి, లోపాలతో కూడిన డైలాగ్ బాక్స్‌ల సమూహాన్ని పొందుతాము. సమస్య ఏమిటంటే విభాగం హెడర్ టేబుల్ చెల్లదు. ఇది విశ్లేషణను క్లిష్టతరం చేయడానికి ఉద్దేశపూర్వకంగా చేయబడుతుంది.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

కానీ ఇది అవసరం లేదు: ELF ఫైల్‌ను సరిగ్గా లోడ్ చేయడానికి మరియు దానిని విశ్లేషించడానికి, ప్రోగ్రామ్ హెడర్ టేబుల్ సరిపోతుంది. కాబట్టి, మేము కేవలం విభాగం పట్టికను తొలగిస్తాము, హెడర్‌లోని సంబంధిత ఫీల్డ్‌లను సున్నా చేస్తాము.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఫైల్‌ను మళ్లీ IDAలో తెరవండి.

జావా వర్చువల్ మెషీన్‌కు స్థానిక లైబ్రరీలో జావా కోడ్‌లో స్థానికంగా ప్రకటించబడిన పద్ధతి యొక్క అమలు ఎక్కడ ఉందో చెప్పడానికి రెండు మార్గాలు ఉన్నాయి. మొదటిది దానికి జాతి పేరు పెట్టడం Java_package_name_ClassName_MethodName.

రెండవది లైబ్రరీని లోడ్ చేస్తున్నప్పుడు దాన్ని నమోదు చేయడం (ఫంక్షన్‌లో JNI_Onload)
ఫంక్షన్ కాల్ ఉపయోగించి రిజిస్టర్ స్థానికులు.

మా విషయంలో, మేము మొదటి పద్ధతిని ఉపయోగిస్తే, పేరు ఇలా ఉండాలి: Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

ఎగుమతి చేసిన ఫంక్షన్లలో అటువంటి ఫంక్షన్ ఏదీ లేదు, అంటే మీరు కాల్ కోసం వెతకాలి రిజిస్టర్ స్థానికులు.
ఫంక్షన్‌కి వెళ్దాం JNI_Onload మరియు మేము ఈ చిత్రాన్ని చూస్తాము:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఏమి జరుగుతుంది ఇక్కడ? మొదటి చూపులో, ఫంక్షన్ ప్రారంభం మరియు ముగింపు ARM ఆర్కిటెక్చర్‌కు విలక్షణమైనవి. స్టాక్‌లోని మొదటి సూచన ఫంక్షన్ దాని ఆపరేషన్‌లో ఉపయోగించే రిజిస్టర్‌ల కంటెంట్‌లను నిల్వ చేస్తుంది (ఈ సందర్భంలో, R0, R1 మరియు R2), అలాగే LR రిజిస్టర్‌లోని కంటెంట్‌లు, ఇందులో ఫంక్షన్ నుండి రిటర్న్ చిరునామా ఉంటుంది. . చివరి సూచన సేవ్ చేయబడిన రిజిస్టర్లను పునరుద్ధరిస్తుంది మరియు తిరిగి వచ్చే చిరునామా వెంటనే PC రిజిస్టర్లో ఉంచబడుతుంది - అందువలన ఫంక్షన్ నుండి తిరిగి వస్తుంది. కానీ మీరు నిశితంగా పరిశీలిస్తే, చివరి సూచన స్టాక్‌లో నిల్వ చేయబడిన రిటర్న్ చిరునామాను మారుస్తుందని మీరు గమనించవచ్చు. తర్వాత ఎలా ఉంటుందో లెక్క చూద్దాం
కోడ్ అమలు. ఒక నిర్దిష్ట చిరునామా 1xB0 R130లోకి లోడ్ చేయబడింది, దాని నుండి 5 తీసివేయబడుతుంది, తర్వాత అది R0కి బదిలీ చేయబడుతుంది మరియు దానికి 0x10 జోడించబడుతుంది. ఇది 0xB13Bగా మారుతుంది. ఈ విధంగా, IDA చివరి సూచన సాధారణ ఫంక్షన్ రిటర్న్ అని భావిస్తుంది, అయితే వాస్తవానికి ఇది లెక్కించిన చిరునామా 0xB13Bకి వెళుతుంది.

ARM ప్రాసెసర్‌లు రెండు మోడ్‌లు మరియు రెండు సెట్ల సూచనలను కలిగి ఉన్నాయని ఇక్కడ గుర్తుచేసుకోవడం విలువ: ARM మరియు Thumb. చిరునామాలోని అతి తక్కువ ముఖ్యమైన బిట్ ఏ సూచనల సెట్‌ని ఉపయోగించబడుతుందో ప్రాసెసర్‌కు తెలియజేస్తుంది. అంటే, చిరునామా వాస్తవానికి 0xB13A, మరియు అతి తక్కువ ముఖ్యమైన బిట్‌లో ఒకటి థంబ్ మోడ్‌ను సూచిస్తుంది.

ఈ లైబ్రరీలోని ప్రతి ఫంక్షన్ ప్రారంభంలో ఇదే విధమైన “అడాప్టర్” జోడించబడింది మరియు
చెత్త కోడ్. మేము వాటిపై మరింత వివరంగా నివసించము - మనకు గుర్తుంది
దాదాపు అన్ని ఫంక్షన్ల యొక్క నిజమైన ప్రారంభం కొంచెం దూరంలో ఉంది.

కోడ్ స్పష్టంగా 0xB13Aకి వెళ్లనందున, కోడ్ ఈ ప్రదేశంలో ఉందని IDA గుర్తించలేదు. అదే కారణంగా, ఇది లైబ్రరీలోని చాలా కోడ్‌లను కోడ్‌గా గుర్తించదు, ఇది విశ్లేషణను కొంత కష్టతరం చేస్తుంది. ఇది కోడ్ అని మేము IDA కి చెప్పాము మరియు ఇది జరుగుతుంది:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

పట్టిక స్పష్టంగా 0xB144 వద్ద ప్రారంభమవుతుంది. sub_494Cలో ఏముంది?

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

LR రిజిస్టర్‌లో ఈ ఫంక్షన్‌ను కాల్ చేసినప్పుడు, మేము గతంలో పేర్కొన్న పట్టిక (0xB144) చిరునామాను పొందుతాము. R0 లో - ఈ పట్టికలో సూచిక. అంటే, విలువ పట్టిక నుండి తీసుకోబడింది, LRకి జోడించబడింది మరియు ఫలితం
వెళ్లవలసిన చిరునామా. దీన్ని లెక్కించడానికి ప్రయత్నిద్దాం: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. మేము అందుకున్న చిరునామాకు వెళ్లి, అక్షరాలా కొన్ని ఉపయోగకరమైన సూచనలను చూడండి మరియు మళ్లీ 0xB140కి వెళ్తాము:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఇప్పుడు పట్టిక నుండి ఇండెక్స్ 0x20తో ఆఫ్‌సెట్‌లో పరివర్తన ఉంటుంది.

పట్టిక పరిమాణం ద్వారా నిర్ణయించడం, కోడ్లో ఇటువంటి అనేక పరివర్తనాలు ఉంటాయి. చిరునామాలను మాన్యువల్‌గా లెక్కించకుండా, దీన్ని మరింత స్వయంచాలకంగా ఎదుర్కోవడం సాధ్యమేనా అనే ప్రశ్న తలెత్తుతుంది. మరియు IDAలో స్క్రిప్ట్‌లు మరియు ప్యాచ్ కోడ్ సామర్థ్యం మా సహాయానికి వస్తాయి:

def put_unconditional_branch(source, destination):
offset = (destination - source - 4) >> 1
if offset > 2097151 or offset < -2097152:
raise RuntimeError("Invalid offset")
if offset > 1023 or offset < -1024:
instruction1 = 0xf000 | ((offset >> 11) & 0x7ff)
instruction2 = 0xb800 | (offset & 0x7ff)
patch_word(source, instruction1)
patch_word(source + 2, instruction2)
else:
instruction = 0xe000 | (offset & 0x7ff)
patch_word(source, instruction)
ea = here()
if get_wide_word(ea) == 0xb503: #PUSH {R0,R1,LR}
ea1 = ea + 2
if get_wide_word(ea1) == 0xbf00: #NOP
ea1 += 2
if get_operand_type(ea1, 0) == 1 and get_operand_value(ea1, 0) == 0 and get_operand_type(ea1, 1) == 2:
index = get_wide_dword(get_operand_value(ea1, 1))
print "index =", hex(index)
ea1 += 2
if get_operand_type(ea1, 0) == 7:
table = get_operand_value(ea1, 0) + 4
elif get_operand_type(ea1, 1) == 2:
table = get_operand_value(ea1, 1) + 4
else:
print "Wrong operand type on", hex(ea1), "-", get_operand_type(ea1, 0), get_operand_type(ea1, 1)
table = None
if table is None:
print "Unable to find table"
else:
print "table =", hex(table)
offset = get_wide_dword(table + (index << 2))
put_unconditional_branch(ea, table + offset)
else:
print "Unknown code", get_operand_type(ea1, 0), get_operand_value(ea1, 0), get_operand_type(ea1, 1) == 2
else:
print "Unable to detect first instruction"

0xB26A లైన్‌లో కర్సర్‌ను ఉంచండి, స్క్రిప్ట్‌ను అమలు చేయండి మరియు 0xB4B0కి పరివర్తనను చూడండి:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

IDA మళ్లీ ఈ ప్రాంతాన్ని కోడ్‌గా గుర్తించలేదు. మేము ఆమెకు సహాయం చేస్తాము మరియు అక్కడ మరొక డిజైన్‌ని చూస్తాము:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

BLX తర్వాత సూచనలు చాలా అర్ధవంతంగా కనిపించడం లేదు, ఇది ఒక రకమైన స్థానభ్రంశం వంటిది. sub_4964ని చూద్దాం:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

మరియు నిజానికి, ఇక్కడ LRలో ఉన్న చిరునామాలో ఒక dword తీసుకోబడింది, ఈ చిరునామాకు జోడించబడింది, దాని తర్వాత ఫలిత చిరునామాలోని విలువ తీసుకోబడుతుంది మరియు స్టాక్‌పై ఉంచబడుతుంది. అలాగే, LRకి 4 జోడించబడింది, తద్వారా ఫంక్షన్ నుండి తిరిగి వచ్చిన తర్వాత, ఇదే ఆఫ్‌సెట్ దాటవేయబడుతుంది. దీని తర్వాత POP {R1} ఆదేశం స్టాక్ నుండి ఫలిత విలువను తీసుకుంటుంది. మీరు చిరునామా 0xB4BA + 0xEA = 0xB5A4 వద్ద ఉన్నదానిని చూస్తే, మీరు చిరునామా పట్టికను పోలి ఉంటుంది:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఈ డిజైన్‌ను ప్యాచ్ చేయడానికి, మీరు కోడ్ నుండి రెండు పారామితులను పొందాలి: ఆఫ్‌సెట్ మరియు రిజిస్టర్ నంబర్ మీరు ఫలితాన్ని ఉంచాలనుకుంటున్నారు. సాధ్యమయ్యే ప్రతి రిజిస్టర్ కోసం, మీరు ముందుగానే కోడ్ ముక్కను సిద్ధం చేయాలి.

patches = {}
patches[0] = (0x00, 0xbf, 0x01, 0x48, 0x00, 0x68, 0x02, 0xe0)
patches[1] = (0x00, 0xbf, 0x01, 0x49, 0x09, 0x68, 0x02, 0xe0)
patches[2] = (0x00, 0xbf, 0x01, 0x4a, 0x12, 0x68, 0x02, 0xe0)
patches[3] = (0x00, 0xbf, 0x01, 0x4b, 0x1b, 0x68, 0x02, 0xe0)
patches[4] = (0x00, 0xbf, 0x01, 0x4c, 0x24, 0x68, 0x02, 0xe0)
patches[5] = (0x00, 0xbf, 0x01, 0x4d, 0x2d, 0x68, 0x02, 0xe0)
patches[8] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0x80, 0xd8, 0xf8, 0x00, 0x80, 0x01, 0xe0)
patches[9] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0x90, 0xd9, 0xf8, 0x00, 0x90, 0x01, 0xe0)
patches[10] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0xa0, 0xda, 0xf8, 0x00, 0xa0, 0x01, 0xe0)
patches[11] = (0x00, 0xbf, 0xdf, 0xf8, 0x06, 0xb0, 0xdb, 0xf8, 0x00, 0xb0, 0x01, 0xe0)
ea = here()
if (get_wide_word(ea) == 0xb082 #SUB SP, SP, #8
and get_wide_word(ea + 2) == 0xb503): #PUSH {R0,R1,LR}
if get_operand_type(ea + 4, 0) == 7:
pop = get_bytes(ea + 12, 4, 0)
if pop[1] == 'xbc':
register = -1
r = get_wide_byte(ea + 12)
for i in range(8):
if r == (1 << i):
register = i
break
if register == -1:
print "Unable to detect register"
else:
address = get_wide_dword(ea + 8) + ea + 8
for b in patches[register]:
patch_byte(ea, b)
ea += 1
if ea % 4 != 0:
ea += 2
patch_dword(ea, address)
elif pop[:3] == 'x5dxf8x04':
register = ord(pop[3]) >> 4
if register in patches:
address = get_wide_dword(ea + 8) + ea + 8
for b in patches[register]:
patch_byte(ea, b)
ea += 1
patch_dword(ea, address)
else:
print "POP instruction not found"
else:
print "Wrong operand type on +4:", get_operand_type(ea + 4, 0)
else:
print "Unable to detect first instructions"

మేము భర్తీ చేయాలనుకుంటున్న నిర్మాణం ప్రారంభంలో కర్సర్‌ను ఉంచుతాము - 0xB4B2 - మరియు స్క్రిప్ట్‌ను అమలు చేయండి:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఇప్పటికే పేర్కొన్న నిర్మాణాలకు అదనంగా, కోడ్ కింది వాటిని కూడా కలిగి ఉంది:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

మునుపటి సందర్భంలో వలె, BLX సూచన తర్వాత ఆఫ్‌సెట్ ఉంది:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

మేము ఆఫ్‌సెట్‌ను LR నుండి చిరునామాకు తీసుకొని, దానిని LRకి జోడించి అక్కడికి వెళ్తాము. 0x72044 + 0xC = 0x72050. ఈ డిజైన్ కోసం స్క్రిప్ట్ చాలా సులభం:

def put_unconditional_branch(source, destination):
offset = (destination - source - 4) >> 1
if offset > 2097151 or offset < -2097152:
raise RuntimeError("Invalid offset")
if offset > 1023 or offset < -1024:
instruction1 = 0xf000 | ((offset >> 11) & 0x7ff)
instruction2 = 0xb800 | (offset & 0x7ff)
patch_word(source, instruction1)
patch_word(source + 2, instruction2)
else:
instruction = 0xe000 | (offset & 0x7ff)
patch_word(source, instruction)
ea = here()
if get_wide_word(ea) == 0xb503: #PUSH {R0,R1,LR}
ea1 = ea + 6
if get_wide_word(ea + 2) == 0xbf00: #NOP
ea1 += 2
offset = get_wide_dword(ea1)
put_unconditional_branch(ea, (ea1 + offset) & 0xffffffff)
else:
print "Unable to detect first instruction"

స్క్రిప్ట్ అమలు ఫలితం:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఫంక్షన్‌లో ప్రతిదీ ప్యాచ్ చేయబడిన తర్వాత, మీరు IDAని దాని అసలు ప్రారంభానికి సూచించవచ్చు. ఇది అన్ని ఫంక్షన్ కోడ్‌ను కలిపి ఉంచుతుంది మరియు దీనిని HexRays ఉపయోగించి డీకంపైల్ చేయవచ్చు.

డీకోడింగ్ స్ట్రింగ్స్

మేము లైబ్రరీలో మెషిన్ కోడ్‌ను అస్పష్టంగా ఎదుర్కోవడం నేర్చుకున్నాము libsgmainso-6.4.36.so UC బ్రౌజర్ నుండి మరియు ఫంక్షన్ కోడ్‌ని అందుకుంది JNI_Onload.

int __fastcall real_JNI_OnLoad(JavaVM *vm)
{
int result; // r0
jclass clazz; // r0 MAPDST
int v4; // r0
JNIEnv *env; // r4
int v6; // [sp-40h] [bp-5Ch]
int v7; // [sp+Ch] [bp-10h]
v7 = *(_DWORD *)off_8AC00;
if ( !vm )
goto LABEL_39;
sub_7C4F4();
env = (JNIEnv *)sub_7C5B0(0);
if ( !env )
goto LABEL_39;
v4 = sub_72CCC();
sub_73634(v4);
sub_73E24(&unk_83EA6, &v6, 49);
clazz = (jclass)((int (__fastcall *)(JNIEnv *, int *))(*env)->FindClass)(env, &v6);
if ( clazz
&& (sub_9EE4(),
sub_71D68(env),
sub_E7DC(env) >= 0
&& sub_69D68(env) >= 0
&& sub_197B4(env, clazz) >= 0
&& sub_E240(env, clazz) >= 0
&& sub_B8B0(env, clazz) >= 0
&& sub_5F0F4(env, clazz) >= 0
&& sub_70640(env, clazz) >= 0
&& sub_11F3C(env) >= 0
&& sub_21C3C(env, clazz) >= 0
&& sub_2148C(env, clazz) >= 0
&& sub_210E0(env, clazz) >= 0
&& sub_41B58(env, clazz) >= 0
&& sub_27920(env, clazz) >= 0
&& sub_293E8(env, clazz) >= 0
&& sub_208F4(env, clazz) >= 0) )
{
result = (sub_B7B0(env, clazz) >> 31) | 0x10004;
}
else
{
LABEL_39:
result = -1;
}
return result;
}

కింది పంక్తులను నిశితంగా పరిశీలిద్దాం:

  sub_73E24(&unk_83EA6, &v6, 49);
clazz = (jclass)((int (__fastcall *)(JNIEnv *, int *))(*env)->FindClass)(env, &v6);

ఫంక్షన్ లో ఉప_73E24 తరగతి పేరు స్పష్టంగా డీక్రిప్ట్ చేయబడుతోంది. ఈ ఫంక్షన్‌కి పారామీటర్‌లుగా, ఎన్‌క్రిప్టెడ్ డేటాకు సమానమైన డేటాకు పాయింటర్, నిర్దిష్ట బఫర్ మరియు నంబర్ పాస్ చేయబడతాయి. సహజంగానే, ఫంక్షన్‌కు కాల్ చేసిన తర్వాత, బఫర్‌లో డీక్రిప్టెడ్ లైన్ ఉంటుంది, ఎందుకంటే ఇది ఫంక్షన్‌కు పంపబడుతుంది FindClass, ఇది తరగతి పేరును రెండవ పరామితిగా తీసుకుంటుంది. కాబట్టి, సంఖ్య అనేది బఫర్ యొక్క పరిమాణం లేదా పంక్తి పొడవు. తరగతి పేరును అర్థాన్ని విడదీయడానికి ప్రయత్నిద్దాం, మనం సరైన దిశలో వెళ్తున్నామో లేదో అది మాకు తెలియజేయాలి. లో ఏమి జరుగుతుందో నిశితంగా పరిశీలిద్దాం ఉప_73E24.

int __fastcall sub_73E56(unsigned __int8 *in, unsigned __int8 *out, size_t size)
{
int v4; // r6
int v7; // r11
int v8; // r9
int v9; // r4
size_t v10; // r5
int v11; // r0
struc_1 v13; // [sp+0h] [bp-30h]
int v14; // [sp+1Ch] [bp-14h]
int v15; // [sp+20h] [bp-10h]
v4 = 0;
v15 = *(_DWORD *)off_8AC00;
v14 = 0;
v7 = sub_7AF78(17);
v8 = sub_7AF78(size);
if ( !v7 )
{
v9 = 0;
goto LABEL_12;
}
(*(void (__fastcall **)(int, const char *, int))(v7 + 12))(v7, "DcO/lcK+h?m3c*q@", 16);
if ( !v8 )
{
LABEL_9:
v4 = 0;
goto LABEL_10;
}
v4 = 0;
if ( !in )
{
LABEL_10:
v9 = 0;
goto LABEL_11;
}
v9 = 0;
if ( out )
{
memset(out, 0, size);
v10 = size - 1;
(*(void (__fastcall **)(int, unsigned __int8 *, size_t))(v8 + 12))(v8, in, v10);
memset(&v13, 0, 0x14u);
v13.field_4 = 3;
v13.field_10 = v7;
v13.field_14 = v8;
v11 = sub_6115C(&v13, &v14);
v9 = v11;
if ( v11 )
{
if ( *(_DWORD *)(v11 + 4) == v10 )
{
qmemcpy(out, *(const void **)v11, v10);
v4 = *(_DWORD *)(v9 + 4);
}
else
{
v4 = 0;
}
goto LABEL_11;
}
goto LABEL_9;
}
LABEL_11:
sub_7B148(v7);
LABEL_12:
if ( v8 )
sub_7B148(v8);
if ( v9 )
sub_7B148(v9);
return v4;
}

ఫంక్షన్ ఉప_7AF78 పేర్కొన్న పరిమాణంలోని బైట్ శ్రేణుల కోసం కంటైనర్ యొక్క ఉదాహరణను సృష్టిస్తుంది (మేము ఈ కంటైనర్‌లపై వివరంగా నివసించము). ఇక్కడ అటువంటి రెండు కంటైనర్లు సృష్టించబడ్డాయి: ఒకటి లైన్ కలిగి ఉంటుంది "DcO/lcK+h?m3c*q@" (ఇది కీ అని ఊహించడం సులభం), మరొకటి గుప్తీకరించిన డేటాను కలిగి ఉంటుంది. తరువాత, రెండు వస్తువులు ఒక నిర్దిష్ట నిర్మాణంలో ఉంచబడతాయి, ఇది ఫంక్షన్‌కు పంపబడుతుంది సబ్_6115C. ఈ స్ట్రక్చర్‌లో 3 వాల్యూ ఉన్న ఫీల్డ్‌ను కూడా మార్క్ చేద్దాం. తర్వాత ఈ స్ట్రక్చర్‌కి ఏమి జరుగుతుందో చూద్దాం.

int __fastcall sub_611B4(struc_1 *a1, _DWORD *a2)
{
int v3; // lr
unsigned int v4; // r1
int v5; // r0
int v6; // r1
int result; // r0
int v8; // r0
*a2 = 820000;
if ( a1 )
{
v3 = a1->field_14;
if ( v3 )
{
v4 = a1->field_4;
if ( v4 < 0x19 )
{
switch ( v4 )
{
case 0u:
v8 = sub_6419C(a1->field_0, a1->field_10, v3);
goto LABEL_17;
case 3u:
v8 = sub_6364C(a1->field_0, a1->field_10, v3);
goto LABEL_17;
case 0x10u:
case 0x11u:
case 0x12u:
v8 = sub_612F4(
a1->field_0,
v4,
*(_QWORD *)&a1->field_8,
*(_QWORD *)&a1->field_8 >> 32,
a1->field_10,
v3,
a2);
goto LABEL_17;
case 0x14u:
v8 = sub_63A28(a1->field_0, v3);
goto LABEL_17;
case 0x15u:
sub_61A60(a1->field_0, v3, a2);
return result;
case 0x16u:
v8 = sub_62440(a1->field_14);
goto LABEL_17;
case 0x17u:
v8 = sub_6226C(a1->field_10, v3);
goto LABEL_17;
case 0x18u:
v8 = sub_63530(a1->field_14);
LABEL_17:
v6 = 0;
if ( v8 )
{
*a2 = 0;
v6 = v8;
}
return v6;
default:
LOWORD(v5) = 28032;
goto LABEL_5;
}
}
}
}
LOWORD(v5) = -27504;
LABEL_5:
HIWORD(v5) = 13;
v6 = 0;
*a2 = v5;
return v6;
}

స్విచ్ పరామితి అనేది గతంలో విలువ 3 కేటాయించబడిన నిర్మాణ ఫీల్డ్. కేస్ 3: ఫంక్షన్‌కి చూడండి సబ్_6364C మునుపటి ఫంక్షన్‌లో జోడించబడిన నిర్మాణం నుండి పారామితులు పంపబడతాయి, అనగా కీ మరియు ఎన్‌క్రిప్టెడ్ డేటా. నిశితంగా పరిశీలిస్తే సబ్_6364C, మీరు దానిలోని RC4 అల్గారిథమ్‌ను గుర్తించవచ్చు.

మాకు ఒక అల్గోరిథం మరియు కీ ఉన్నాయి. తరగతి పేరును అర్థంచేసుకోవడానికి ప్రయత్నిద్దాం. ఏమి జరిగిందో ఇక్కడ ఉంది: com/taobao/wireless/security/adapter/JNICLibrary. గొప్ప! మేము సరైన మార్గంలో ఉన్నాము.

కమాండ్ ట్రీ

ఇప్పుడు మనం ఒక సవాలును కనుగొనాలి రిజిస్టర్ స్థానికులు, ఇది మాకు ఫంక్షన్‌కు గురి చేస్తుంది doCommandNative. నుండి పిలువబడే ఫంక్షన్లను చూద్దాం JNI_Onload, మరియు మేము దానిని కనుగొంటాము ఉప_B7B0:

int __fastcall sub_B7F6(JNIEnv *env, jclass clazz)
{
char signature[41]; // [sp+7h] [bp-55h]
char name[16]; // [sp+30h] [bp-2Ch]
JNINativeMethod method; // [sp+40h] [bp-1Ch]
int v8; // [sp+4Ch] [bp-10h]
v8 = *(_DWORD *)off_8AC00;
decryptString((unsigned __int8 *)&unk_83ED9, (unsigned __int8 *)name, 0x10u);// doCommandNative
decryptString((unsigned __int8 *)&unk_83EEA, (unsigned __int8 *)signature, 0x29u);// (I[Ljava/lang/Object;)Ljava/lang/Object;
method.name = name;
method.signature = signature;
method.fnPtr = sub_B69C;
return ((int (__fastcall *)(JNIEnv *, jclass, JNINativeMethod *, int))(*env)->RegisterNatives)(env, clazz, &method, 1) >> 31;
}

మరియు నిజానికి, పేరుతో స్థానిక పద్ధతి ఇక్కడ నమోదు చేయబడింది doCommandNative. ఇప్పుడు అతని చిరునామా మనకు తెలుసు. ఏం చేస్తాడో చూద్దాం.

int __fastcall doCommandNative(JNIEnv *env, jobject obj, int command, jarray args)
{
int v5; // r5
struc_2 *a5; // r6
int v9; // r1
int v11; // [sp+Ch] [bp-14h]
int v12; // [sp+10h] [bp-10h]
v5 = 0;
v12 = *(_DWORD *)off_8AC00;
v11 = 0;
a5 = (struc_2 *)malloc(0x14u);
if ( a5 )
{
a5->field_0 = 0;
a5->field_4 = 0;
a5->field_8 = 0;
a5->field_C = 0;
v9 = command % 10000 / 100;
a5->field_0 = command / 10000;
a5->field_4 = v9;
a5->field_8 = command % 100;
a5->field_C = env;
a5->field_10 = args;
v5 = sub_9D60(command / 10000, v9, command % 100, 1, (int)a5, &v11);
}
free(a5);
if ( !v5 && v11 )
sub_7CF34(env, v11, &byte_83ED7);
return v5;
}

డెవలపర్‌లు స్థానిక లైబ్రరీకి బదిలీ చేయాలని నిర్ణయించుకున్న అన్ని ఫంక్షన్‌ల ఎంట్రీ పాయింట్ ఇక్కడ ఉందని పేరు ద్వారా మీరు ఊహించవచ్చు. మేము ఫంక్షన్ నంబర్ 10601 పట్ల ఆసక్తి కలిగి ఉన్నాము.

కమాండ్ నంబర్ మూడు సంఖ్యలను ఉత్పత్తి చేస్తుందని మీరు కోడ్ నుండి చూడవచ్చు: కమాండ్/10000, కమాండ్ % 10000 / 100 и ఆదేశం% 10, అంటే, మన విషయంలో, 1, 6 మరియు 1. ఈ మూడు సంఖ్యలు, అలాగే ఒక పాయింటర్ JNIEnv మరియు ఫంక్షన్‌కు పంపబడిన ఆర్గ్యుమెంట్‌లు ఒక స్ట్రక్చర్‌కి జోడించబడతాయి మరియు పంపబడతాయి. పొందిన మూడు సంఖ్యలను ఉపయోగించి (వాటిని N1, N2 మరియు N3ని సూచిస్తాము), కమాండ్ ట్రీ నిర్మించబడింది.

ఇలాంటిది ఏదైనా:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

చెట్టు డైనమిక్‌గా నిండి ఉంటుంది JNI_Onload.
మూడు సంఖ్యలు చెట్టులోని మార్గాన్ని ఎన్కోడ్ చేస్తాయి. చెట్టు యొక్క ప్రతి ఆకు సంబంధిత ఫంక్షన్ యొక్క పాక్డ్ చిరునామాను కలిగి ఉంటుంది. కీ పేరెంట్ నోడ్‌లో ఉంది. ఉపయోగించిన అన్ని నిర్మాణాలను మీరు అర్థం చేసుకుంటే మనకు అవసరమైన ఫంక్షన్ చెట్టుకు జోడించబడే కోడ్‌లో స్థలాన్ని కనుగొనడం కష్టం కాదు (ఇప్పటికే పెద్ద కథనాన్ని ఉబ్బిపోకుండా మేము వాటిని వివరించము).

మరింత అస్పష్టత

ట్రాఫిక్‌ను డీక్రిప్ట్ చేయాల్సిన ఫంక్షన్ చిరునామాను మేము అందుకున్నాము: 0x5F1AC. కానీ సంతోషించడం చాలా తొందరగా ఉంది: UC బ్రౌజర్ డెవలపర్‌లు మా కోసం మరొక ఆశ్చర్యాన్ని సిద్ధం చేశారు.

జావా కోడ్‌లో ఏర్పడిన శ్రేణి నుండి పారామితులను స్వీకరించిన తర్వాత, మనకు లభిస్తుంది
చిరునామా 0x4D070 వద్ద ఫంక్షన్‌కు. మరియు ఇక్కడ మరొక రకమైన కోడ్ అస్పష్టత మాకు వేచి ఉంది.

మేము R7 మరియు R4లో రెండు సూచికలను ఉంచాము:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

మేము మొదటి సూచికను R11కి మారుస్తాము:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

పట్టిక నుండి చిరునామాను పొందడానికి, సూచికను ఉపయోగించండి:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

మొదటి చిరునామాకు వెళ్ళిన తర్వాత, రెండవ సూచిక ఉపయోగించబడుతుంది, ఇది R4లో ఉంది. పట్టికలో 230 అంశాలు ఉన్నాయి.

దానికి ఏం చేయాలి? ఇది స్విచ్ అని మీరు IDAకి తెలియజేయవచ్చు: సవరించు -> ఇతరం -> స్విచ్ ఇడియమ్‌ని పేర్కొనండి.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఫలితంగా కోడ్ భయానకంగా ఉంది. కానీ, దాని అడవి గుండా వెళుతూ, ఇప్పటికే మాకు తెలిసిన ఒక ఫంక్షన్‌కి కాల్ చేయడాన్ని మీరు గమనించవచ్చు సబ్_6115C:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

RC3 అల్గోరిథం ఉపయోగించి 4 డిక్రిప్షన్ ఉన్న సందర్భంలో ఒక స్విచ్ ఉంది. మరియు ఈ సందర్భంలో, ఫంక్షన్‌కు పంపబడిన నిర్మాణం ఆమోదించబడిన పారామితుల నుండి నింపబడుతుంది doCommandNative. మనం అక్కడ ఉన్న వాటిని గుర్తుచేసుకుందాం magicInt విలువతో 16. మేము సంబంధిత కేసును చూస్తాము - మరియు అనేక పరివర్తనాల తర్వాత మేము అల్గోరిథం గుర్తించగల కోడ్‌ను కనుగొంటాము.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఇది AES!

అల్గోరిథం ఉంది, దాని పారామితులను పొందడం మాత్రమే మిగిలి ఉంది: మోడ్, కీ మరియు, బహుశా, ప్రారంభ వెక్టర్ (దాని ఉనికి AES అల్గోరిథం యొక్క ఆపరేటింగ్ మోడ్‌పై ఆధారపడి ఉంటుంది). ఫంక్షన్ కాల్‌కు ముందు వారితో కూడిన నిర్మాణం ఎక్కడో ఏర్పడాలి సబ్_6115C, కానీ కోడ్ యొక్క ఈ భాగం ప్రత్యేకంగా అస్పష్టంగా ఉంది, కాబట్టి కోడ్‌ను ప్యాచ్ చేయాలనే ఆలోచన వస్తుంది, తద్వారా డిక్రిప్షన్ ఫంక్షన్ యొక్క అన్ని పారామితులు ఫైల్‌లోకి డంప్ చేయబడతాయి.

ప్యాచ్

అసెంబ్లీ భాషలో అన్ని ప్యాచ్ కోడ్‌లను మాన్యువల్‌గా వ్రాయకుండా ఉండటానికి, మీరు Android స్టూడియోని ప్రారంభించవచ్చు, అక్కడ మా డిక్రిప్షన్ ఫంక్షన్ వలె అదే ఇన్‌పుట్ పారామితులను స్వీకరించే మరియు ఫైల్‌కు వ్రాసే ఒక ఫంక్షన్‌ను వ్రాయవచ్చు, ఆపై కంపైలర్ చేసే కోడ్‌ను కాపీ-పేస్ట్ చేయండి. ఉత్పత్తి.

UC బ్రౌజర్ బృందంలోని మా స్నేహితులు కూడా కోడ్‌ని జోడించే సౌలభ్యాన్ని చూసుకున్నారు. ప్రతి ఫంక్షన్ ప్రారంభంలో మనం చెత్త కోడ్‌ని కలిగి ఉన్నామని గుర్తుంచుకోండి, దానిని సులభంగా మరొకదానితో భర్తీ చేయవచ్చు. చాలా సౌకర్యవంతంగా ఉంటుంది 🙂 అయితే, టార్గెట్ ఫంక్షన్ ప్రారంభంలో అన్ని పారామితులను ఫైల్‌కి సేవ్ చేసే కోడ్‌కు తగినంత స్థలం లేదు. నేను దానిని భాగాలుగా విభజించి, పొరుగు ఫంక్షన్ల నుండి చెత్త బ్లాకులను ఉపయోగించాల్సి వచ్చింది. మొత్తం నాలుగు భాగాలు ఉండేవి.

మొదటి భాగం:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ARM ఆర్కిటెక్చర్‌లో, మొదటి నాలుగు ఫంక్షన్ పారామితులు R0-R3 రిజిస్టర్‌ల ద్వారా పంపబడతాయి, మిగిలినవి ఏవైనా ఉంటే స్టాక్ ద్వారా పంపబడతాయి. LR రిజిస్టర్ రిటర్న్ చిరునామాను కలిగి ఉంటుంది. మేము దాని పారామితులను డంప్ చేసిన తర్వాత ఫంక్షన్ పని చేసేలా ఇవన్నీ సేవ్ చేయాలి. మేము ప్రాసెస్‌లో ఉపయోగించే అన్ని రిజిస్టర్‌లను కూడా సేవ్ చేయాలి, కాబట్టి మేము PUSH.W {R0-R10,LR} చేస్తాము. R7లో మనం స్టాక్ ద్వారా ఫంక్షన్‌కు పంపబడిన పారామితుల జాబితా చిరునామాను పొందుతాము.

ఫంక్షన్ ఉపయోగించి ఫోపెన్ ఫైల్‌ని ఓపెన్ చేద్దాం /data/local/tmp/aes "ab" మోడ్‌లో
అంటే అదనంగా. R0 లో మేము ఫైల్ పేరు యొక్క చిరునామాను లోడ్ చేస్తాము, R1 లో - మోడ్‌ను సూచించే లైన్ చిరునామా. మరియు ఇక్కడ చెత్త కోడ్ ముగుస్తుంది, కాబట్టి మేము తదుపరి ఫంక్షన్‌కు వెళ్తాము. ఇది పని చేయడం కొనసాగించడానికి, మేము ప్రారంభంలో ఫంక్షన్ యొక్క నిజమైన కోడ్‌కు పరివర్తనను ఉంచాము, చెత్తను దాటవేస్తాము మరియు చెత్తకు బదులుగా మేము ప్యాచ్ యొక్క కొనసాగింపును జోడిస్తాము.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

పిలుస్తోంది ఫోపెన్.

ఫంక్షన్ యొక్క మొదటి మూడు పారామితులు AES రకం కలిగి ఉంటాయి పూర్ణాంకానికి. మేము ప్రారంభంలో స్టాక్‌లో రిజిస్టర్‌లను సేవ్ చేసినందున, మనం ఫంక్షన్‌ను పాస్ చేయవచ్చు fwrite స్టాక్‌లో వారి చిరునామాలు.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

తదుపరి మేము డేటా పరిమాణం మరియు కీ, ప్రారంభ వెక్టర్ మరియు ఎన్‌క్రిప్టెడ్ డేటా కోసం డేటాకు పాయింటర్‌ను కలిగి ఉన్న మూడు నిర్మాణాలను కలిగి ఉన్నాము.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ముగింపులో, ఫైల్‌ను మూసివేసి, రిజిస్టర్‌లను పునరుద్ధరించండి మరియు నియంత్రణను నిజమైన ఫంక్షన్‌కు బదిలీ చేయండి AES.

మేము ప్యాచ్ చేయబడిన లైబ్రరీతో APKని సేకరిస్తాము, దానిపై సంతకం చేసి, పరికరం/ఎమ్యులేటర్‌కి అప్‌లోడ్ చేసి, దాన్ని లాంచ్ చేస్తాము. మా డంప్ సృష్టించబడటం మరియు అక్కడ చాలా డేటా వ్రాయబడటం మేము చూస్తాము. బ్రౌజర్ ట్రాఫిక్ కోసం మాత్రమే గుప్తీకరణను ఉపయోగిస్తుంది మరియు అన్ని ఎన్‌క్రిప్షన్ ప్రశ్నలోని ఫంక్షన్ ద్వారా వెళుతుంది. కానీ కొన్ని కారణాల వల్ల అవసరమైన డేటా లేదు మరియు అవసరమైన అభ్యర్థన ట్రాఫిక్‌లో కనిపించదు. UC బ్రౌజర్ అవసరమైన అభ్యర్థనను రూపొందించే వరకు వేచి ఉండకుండా ఉండటానికి, ముందుగా స్వీకరించిన సర్వర్ నుండి ఎన్‌క్రిప్ట్ చేసిన ప్రతిస్పందనను తీసుకొని, అప్లికేషన్‌ను మళ్లీ ప్యాచ్ చేద్దాం: ప్రధాన కార్యాచరణ యొక్క onCreateకి డిక్రిప్షన్‌ను జోడించండి.

    const/16 v1, 0x62
new-array v1, v1, [B
fill-array-data v1, :encrypted_data
const/16 v0, 0x1f
invoke-static {v0, v1}, Lcom/uc/browser/core/d/c/g;->j(I[B)[B
move-result-object v1
array-length v2, v1
invoke-static {v2}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;
move-result-object v2
const-string v0, "ololo"
invoke-static {v0, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

మేము సమీకరించాము, సంతకం చేస్తాము, ఇన్‌స్టాల్ చేస్తాము, ప్రారంభించాము. మేము ఒక NullPointerExceptionని అందుకుంటాము ఎందుకంటే పద్ధతి శూన్యంగా తిరిగి వచ్చింది.

కోడ్ యొక్క తదుపరి విశ్లేషణ సమయంలో, ఆసక్తికరమైన పంక్తులను అర్థంచేసే ఒక ఫంక్షన్ కనుగొనబడింది: "META-INF/" మరియు ".RSA". అప్లికేషన్ దాని సర్టిఫికేట్‌ను ధృవీకరిస్తున్నట్లు కనిపిస్తోంది. లేదా దాని నుండి కీలను కూడా ఉత్పత్తి చేస్తుంది. సర్టిఫికేట్‌తో ఏమి జరుగుతుందో నేను నిజంగా పరిష్కరించాలనుకోవడం లేదు, కాబట్టి మేము దానిని సరైన సర్టిఫికేట్‌గా స్లిప్ చేస్తాము. ఎన్‌క్రిప్టెడ్ లైన్‌ను ప్యాచ్ చేద్దాం, తద్వారా “META-INF/”కి బదులుగా మనకు “BLABLINF/” వస్తుంది, APKలో ఆ పేరుతో ఫోల్డర్‌ను సృష్టించండి మరియు అక్కడ స్క్విరెల్ బ్రౌజర్ సర్టిఫికేట్‌ను జోడించండి.

మేము సమీకరించాము, సంతకం చేస్తాము, ఇన్‌స్టాల్ చేస్తాము, ప్రారంభించాము. పేకాట! మా దగ్గర కీ ఉంది!

MitM

మేము కీ మరియు కీకి సమానమైన ప్రారంభ వెక్టార్‌ని అందుకున్నాము. CBC మోడ్‌లో సర్వర్ ప్రతిస్పందనను డీక్రిప్ట్ చేయడానికి ప్రయత్నిద్దాం.

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

మేము ఆర్కైవ్ URLని చూస్తాము, MD5, “extract_unzipsize” మరియు సంఖ్యను పోలి ఉంటుంది. మేము తనిఖీ చేస్తాము: ఆర్కైవ్ యొక్క MD5 ఒకేలా ఉంటుంది, ప్యాక్ చేయని లైబ్రరీ పరిమాణం ఒకే విధంగా ఉంటుంది. మేము ఈ లైబ్రరీని ప్యాచ్ చేసి బ్రౌజర్‌కి ఇవ్వడానికి ప్రయత్నిస్తున్నాము. మా ప్యాచ్ చేయబడిన లైబ్రరీ లోడ్ అయిందని చూపించడానికి, మేము “PWNED!” అనే వచనంతో SMSని సృష్టించడానికి ఉద్దేశ్యాన్ని ప్రారంభిస్తాము. మేము సర్వర్ నుండి రెండు ప్రతిస్పందనలను భర్తీ చేస్తాము: puds.ucweb.com/upgrade/index.xhtml మరియు ఆర్కైవ్‌ను డౌన్‌లోడ్ చేయడానికి. మొదటిదానిలో మేము MD5 ను భర్తీ చేస్తాము (అన్ప్యాక్ చేసిన తర్వాత పరిమాణం మారదు), రెండవది మేము ప్యాచ్డ్ లైబ్రరీతో ఆర్కైవ్ను ఇస్తాము.

బ్రౌజర్ ఆర్కైవ్‌ను చాలాసార్లు డౌన్‌లోడ్ చేయడానికి ప్రయత్నిస్తుంది, దాని తర్వాత అది లోపాన్ని ఇస్తుంది. స్పష్టంగా ఏదో
అతనికి ఇష్టం లేదు. ఈ మురికి ఆకృతిని విశ్లేషించిన ఫలితంగా, సర్వర్ ఆర్కైవ్ పరిమాణాన్ని కూడా ప్రసారం చేస్తుందని తేలింది:

UC బ్రౌజర్‌లో దుర్బలత్వాల కోసం వెతుకుతోంది

ఇది LEB128లో ఎన్కోడ్ చేయబడింది. ప్యాచ్ తర్వాత, లైబ్రరీతో ఉన్న ఆర్కైవ్ పరిమాణం కొద్దిగా మారింది, కాబట్టి బ్రౌజర్ ఆర్కైవ్ వంకరగా డౌన్‌లోడ్ చేయబడిందని భావించింది మరియు అనేక ప్రయత్నాల తర్వాత అది లోపాన్ని విసిరింది.

మేము ఆర్కైవ్ పరిమాణాన్ని సర్దుబాటు చేస్తాము... మరియు - విజయం! 🙂 ఫలితం వీడియోలో ఉంది.

https://www.youtube.com/watch?v=Nfns7uH03J8

పరిణామాలు మరియు డెవలపర్ ప్రతిచర్య

అదే విధంగా, హానికరమైన లైబ్రరీలను పంపిణీ చేయడానికి మరియు అమలు చేయడానికి హ్యాకర్లు UC బ్రౌజర్ యొక్క అసురక్షిత లక్షణాన్ని ఉపయోగించవచ్చు. ఈ లైబ్రరీలు బ్రౌజర్ సందర్భంలో పని చేస్తాయి, కాబట్టి అవి దాని అన్ని సిస్టమ్ అనుమతులను స్వీకరిస్తాయి. ఫలితంగా, ఫిషింగ్ విండోలను ప్రదర్శించే సామర్థ్యం, ​​అలాగే డేటాబేస్లో నిల్వ చేయబడిన లాగిన్లు, పాస్‌వర్డ్‌లు మరియు కుక్కీలతో సహా ఆరెంజ్ చైనీస్ స్క్విరెల్ యొక్క పని చేసే ఫైల్‌లకు ప్రాప్యత.

మేము UC బ్రౌజర్ డెవలపర్‌లను సంప్రదించి, మేము కనుగొన్న సమస్య గురించి వారికి తెలియజేసాము, దుర్బలత్వం మరియు దాని ప్రమాదాన్ని సూచించడానికి ప్రయత్నించాము, కానీ వారు మాతో ఏమీ చర్చించలేదు. ఇంతలో, బ్రౌజర్ దాని ప్రమాదకరమైన లక్షణాన్ని సాదా దృష్టిలో చూపుతూనే ఉంది. కానీ మేము దుర్బలత్వానికి సంబంధించిన వివరాలను వెల్లడించిన తర్వాత, మునుపటిలా దానిని విస్మరించడం సాధ్యం కాదు. మార్చి 27 ఉంది
UC బ్రౌజర్ 12.10.9.1193 యొక్క కొత్త వెర్షన్ విడుదల చేయబడింది, ఇది HTTPS ద్వారా సర్వర్‌ను యాక్సెస్ చేసింది: puds.ucweb.com/upgrade/index.xhtml.

అదనంగా, “పరిష్కారం” తర్వాత మరియు ఈ కథనాన్ని వ్రాసే సమయం వరకు, బ్రౌజర్‌లో PDFని తెరవడానికి ప్రయత్నించడం వలన “అయ్యో, ఏదో తప్పు జరిగింది!” అనే టెక్స్ట్‌తో దోష సందేశం వచ్చింది. PDFని తెరవడానికి ప్రయత్నిస్తున్నప్పుడు సర్వర్‌కు అభ్యర్థన చేయలేదు, కానీ బ్రౌజర్ ప్రారంభించబడినప్పుడు అభ్యర్థన చేయబడింది, ఇది Google Play నిబంధనలను ఉల్లంఘించి ఎక్జిక్యూటబుల్ కోడ్‌ను డౌన్‌లోడ్ చేసే నిరంతర సామర్థ్యాన్ని సూచిస్తుంది.

మూలం: www.habr.com

ఒక వ్యాఖ్యను జోడించండి