په UC براوزر کې د زیانونو په لټه کې

په UC براوزر کې د زیانونو په لټه کې

پېژندنه

د مارچ په پای کې موږ راپور ورکړی، چې دوی په UC براوزر کې د غیر تصدیق شوي کوډ بارولو او چلولو پټ وړتیا وموندله. نن ورځ موږ به په تفصیل سره وګورو چې دا ډاونلوډ څنګه پیښیږي او څنګه هیکرز کولی شي دا د خپلو موخو لپاره وکاروي.

یو څه وخت دمخه ، د UC براوزر په خورا جارحانه ډول اعلان او توزیع شوی و: دا د مالویر په کارولو سره د کاروونکو په وسیلو کې نصب شوی و ، د ویډیو فایلونو په څیر د مختلف سایټونو څخه توزیع شوی (د مثال په توګه ، کاروونکو فکر کاوه چې دوی ډاونلوډ کوي ، د مثال په توګه ، یو فحش ویډیو ، مګر پرځای یې د دې براوزر سره یو APK ترلاسه کړ)، ډارونکي بینرونه د پیغامونو سره کارولي چې براوزر زوړ، زیان منونکی او ورته شیان دي. په VK کې د رسمي UC براوزر ګروپ کې شتون لري موضوعپه کوم کې چې کاروونکي کولی شي د غیر عادلانه اعلاناتو په اړه شکایت وکړي، دلته ډیری مثالونه شتون لري. په 2016 کې حتی شتون درلود ویډیو اعلانونه په روسی کې (هو، د اعلاناتو د بندولو براوزر لپاره اعلانونه).

د لیکلو په وخت کې، UC براوزر په ګوګل پلی کې 500 نصبونه لري. دا اغیزمن دی - یوازې ګوګل کروم نور لري. د بیاکتنو په مینځ کې تاسو کولی شئ په ګوګل پلی کې ځینې غوښتنلیکونو ته د اعلاناتو او لارښودونو په اړه خورا ډیر شکایتونه وګورئ. دا زموږ د څیړنې دلیل و: موږ پریکړه وکړه چې وګورو چې ایا د UC براوزر یو څه خراب کوي. او دا معلومه شوه چې هغه کوي!

د غوښتنلیک کوډ کې، د اجرا وړ کوډ ډاونلوډ او چلولو وړتیا وموندل شوه، کوم چې د غوښتنلیکونو خپرولو لپاره د مقرراتو سره مخالف دی په ګوګل پلی کې. د اجرا وړ کوډ ډاونلوډ کولو سربیره، UC براوزر دا په ناامنه توګه ترسره کوي، کوم چې د MitM برید پیل کولو لپاره کارول کیدی شي. راځئ وګورو چې ایا موږ کولی شو دا ډول برید ترسره کړو.

هر څه چې لاندې لیکل شوي د 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 براوزر کې د زیانونو په لټه کې

کتابتون پخپله په ZIP کې بسته شوی او کوډ شوی نه دی.

په UC براوزر کې د زیانونو په لټه کې

د ترافیک کوډ کوډ لټون

راځئ هڅه وکړو چې د سرور ځواب تشریح کړو. راځئ چې د ټولګي کوډ وګورو com.uc.deployment.UpgradeDeployService: له طریقې څخه onStartCommand ورتګ 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, کوم چې په حقیقت کې یو .so نه دی، مګر یو .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);
}
}

او هم ټولګي JNICL کتابتون، په کوم کې چې اصلي میتود اعلان شوی 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 او ګوتو. د پتې لږترلږه د پام وړ برخه پروسیسر ته وایي چې کوم لارښوونې سیټ کارول کیږي. دا دی، پته په حقیقت کې 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 کې پروت پته کې یوه کلمه اخیستل کیږي ، پدې پته کې اضافه کیږي ، وروسته له دې چې په پایله کې پته ارزښت اخیستل کیږي او په سټیک کې ایښودل کیږي. همدارنګه، 4 په LR کې اضافه شوي ترڅو د فعالیت څخه بیرته راستنیدو وروسته، دا ورته آفسیټ پریښودل شي. وروسته له دې چې د 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);

په فعالیت کې sub_73E24 د ټولګي نوم په ښکاره ډول کوډ شوی دی. د دې فنکشن د پیرامیټونو په توګه، د کوډ شوي ډیټا په څیر ډیټا ته اشاره کونکي، یو مشخص بفر او شمیره تیریږي. په ښکاره ډول، د فنکشن زنګ وهلو وروسته، په بفر کې به یو کوډ شوی کرښه وي، ځکه چې دا فنکشن ته لیږدول کیږي کلاس ومومئ، کوم چې د ټولګي نوم د دوهم پیرامیټر په توګه اخلي. نو ځکه، شمیره د بفر اندازه یا د کرښې اوږدوالی دی. راځئ هڅه وکړو چې د ټولګي نوم تشریح کړو، دا باید موږ ته ووایي چې ایا موږ سم لوري ته روان یو. راځئ چې نږدې وګورو چې څه پیښیږي sub_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;
}

دنده sub_7AF78 د ټاکل شوي اندازې د بایټ سرې لپاره د کانټینر مثال رامینځته کوي (موږ به پدې کانټینرونو کې په تفصیل سره نه اوسو). دلته دوه داسې کانټینرونه جوړ شوي: یو لیک لري "DcO/lcK+h?m3c*q@" (دا اټکل کول اسانه دي چې دا کلیدي ده)، بل یې کوډ شوي ډاټا لري. بیا، دواړه شیان په یو ځانګړي جوړښت کې ځای پر ځای شوي، کوم چې فعالیت ته لیږدول کیږي sub_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 قضیه وګورئ: فنکشن ته sub_6364C پیرامیټونه د جوړښت څخه تیریږي کوم چې هلته په تیرو فنکشن کې اضافه شوي، د بیلګې په توګه کلیدي او کوډ شوي ډاټا. که تاسو له نږدې وګورئ sub_6364C، تاسو کولی شئ پدې کې RC4 الګوریتم پیژنئ.

موږ یو الګوریتم او کلیدي لرو. راځئ هڅه وکړو چې د ټولګي نوم تشریح کړو. دلته هغه څه دي چې پیښ شوي: com/taobao/wireless/security/adapter/JNICLlibrary. غوره! موږ په سمه لار روان یو.

د قوماندې ونه

اوس موږ اړتیا لرو چې ننګونه ولرو د ثبت نام، کوم چې به موږ فنکشن ته اشاره وکړي doCommandNative. راځئ چې وګورو چې له دې څخه نومیږي JNI_Onload, او موږ یې په کې پیدا کوو sub_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 براوزر کې د زیانونو په لټه کې

پایله کوډ خورا خطرناک دی. مګر ، د دې ځنګل له لارې خپله لاره کول ، تاسو کولی شئ یو فنکشن ته زنګ ووهئ چې دمخه موږ ته پیژندل شوی sub_6115C:

په UC براوزر کې د زیانونو په لټه کې

یو سویچ شتون درلود چې په 3 حالت کې د RC4 الګوریتم په کارولو سره ډیکریپشن شتون درلود. او په دې حالت کې، فنکشن ته لیږدول شوی جوړښت د پارامترونو څخه ډک شوی دی doCommandNative. راځئ چې په یاد ولرو چې موږ هلته څه درلودل magicInt د 16 ارزښت سره. موږ ورته قضیه ګورو - او د څو لیږدونو وروسته موږ کوډ پیدا کوو چې د الګوریتم په واسطه پیژندل کیدی شي.

په UC براوزر کې د زیانونو په لټه کې

دا AES دی!

الګوریتم شتون لري، ټول هغه څه چې پاتې دي د دې پیرامیټرو ترلاسه کول دي: حالت، کلیدي او ممکن د ابتدايي ویکتور (د دې شتون د AES الګوریتم عملیاتي حالت پورې اړه لري). د دوی سره جوړښت باید د فنکشن کال دمخه یو ځای جوړ شي sub_6115C، مګر د کوډ دا برخه په ځانګړي ډول ښه مبهم ده ، نو دا نظر رامینځته کیږي چې کوډ پیچ کړئ ترڅو د کوډ کولو فعالیت ټول پیرامیټونه په فایل کې ډوب شي.

پیچ

د دې لپاره چې ټول پیچ کوډ د اسمبلۍ په ژبه کې په لاسي ډول ونه لیکل شي، تاسو کولی شئ د Android سټوډیو پیل کړئ، هلته یو فنکشن ولیکئ چې زموږ د ډیکریپشن فنکشن په څیر ورته ان پټ پیرامیټونه ترلاسه کوي او فایل ته لیکي، بیا هغه کوډ کاپي پیسټ کړئ چې کمپیلر به یې وکړي. پیدا کول

د UC براوزر ټیم څخه زموږ ملګرو هم د کوډ اضافه کولو اسانتیا ته پاملرنه وکړه. راځئ چې په یاد ولرو چې د هر فعالیت په پیل کې موږ د کثافاتو کوډ لرو چې په اسانۍ سره د بل سره بدلیدلی شي. خورا اسانه 🙂 په هرصورت ، د هدف فعالیت په پیل کې د کوډ لپاره کافي ځای شتون نلري چې ټول پیرامیټرې فایل ته خوندي کړي. زه باید دا په برخو ویشم او د ګاونډیو دندو څخه د کثافاتو بلاکونه وکاروم. په مجموع کې څلور برخې وې.

لومړۍ برخه:

په UC براوزر کې د زیانونو په لټه کې

د ARM په جوړښت کې، د فعالیت لومړی څلور پیرامیټونه د R0-R3 راجسترونو له لارې تیریږي، پاتې نور، که کوم وي، د سټیک له لارې تیریږي. د LR راجستر د بیرته ستنیدو پته لري. دا ټول باید خوندي شي ترڅو فنکشن کار وکړي وروسته له دې چې موږ د دې پیرامیټرو ډوب کړو. موږ باید ټول هغه راجسترونه خوندي کړو چې موږ به یې په پروسه کې وکاروو، نو موږ PUSH.W {R0-R10,LR} کوو. په R7 کې موږ د پیرامیټرونو لیست پته ترلاسه کوو چې د سټیک له لارې فنکشن ته لیږدول شوي.

د فنکشن کارول fopen راځئ چې فایل خلاص کړو /data/local/tmp/aes په "ab" حالت کې
د بیلګې په توګه د اضافه کولو لپاره. په R0 کې موږ د فایل نوم پته پورته کوو، په R1 کې - د کرښې پته چې موډ څرګندوي. او دلته د کثافاتو کوډ پای ته رسیږي، نو موږ راتلونکي فعالیت ته ځو. د دې لپاره چې دې کار ته دوام ورکړي ، موږ په پیل کې د فعالیت اصلي کوډ ته لیږد واچوو ، د کثافاتو په تیریدو سره ، او د کثافاتو پرځای موږ د پیچ ​​دوام اضافه کوو.

په UC براوزر کې د زیانونو په لټه کې

زنګ وهل fopen.

د فعالیت لومړی درې پیرامیټونه AES ډول لري اینټ. څرنګه چې موږ راجسترونه په پیل کې سټیک ته خوندي کړل، موږ کولی شو په ساده ډول فنکشن تیر کړو لیکل د دوی پته په سټیک کې.

په 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 کې د دې نوم سره فولډر جوړ کړئ او هلته د squirrel براوزر سند اضافه کړئ.

موږ راټول، لاسلیک، نصب، پیل کوو. بنګو! موږ کلیدي لرو!

د ‏‎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 براوزر ناامنه ځانګړتیا وکاروي ترڅو ناوړه کتابتونونه توزیع او چل کړي. دا کتابتونونه به د براوزر په شرایطو کې کار وکړي، نو دوی به د دې سیسټم ټولې اجازې ترلاسه کړي. د پایلې په توګه، د فشینګ وینډوز ښودلو وړتیا، په بیله بیا د نارنجي چینایي squirrel کاري فایلونو ته لاسرسی، په شمول په ډیټابیس کې زیرمه شوي ننوتل، پاسورډونه او کوکیز.

موږ د UC براوزر د پراختیا کونکو سره اړیکه ونیوله او دوی ته مو د هغه ستونزې په اړه خبر ورکړ چې موږ یې موندلي، هڅه مو وکړه چې زیانمنونکي او د هغې خطر په ګوته کړو، مګر دوی له موږ سره هیڅ خبرې ونه کړې. په عین وخت کې، براوزر په ساده لید کې خپل خطرناک ځانګړتیا ته دوام ورکړ. مګر یوځل چې موږ د زیان مننې توضیحات په ګوته کړل ، نور امکان نلري چې د پخوا په څیر یې له پامه غورځول شي. د مارچ ۲۷مه وه
د UC براوزر 12.10.9.1193 نوې نسخه خپره شوې، کوم چې سرور ته د HTTPS له لارې لاسرسی لري: puds.ucweb.com/upgrade/index.xhtml.

سربیره پردې ، د "سمه کولو" وروسته او د دې مقالې لیکلو وخت پورې ، په براوزر کې د پی ډی ایف خلاصولو هڅه د متن سره د خطا پیغام پایله وه "هو ، یو څه غلط شو!" سرور ته غوښتنه نه وه شوې کله چې د پی ډی ایف خلاصولو هڅه وکړئ ، مګر غوښتنه شوې وه کله چې براوزر په لاره واچول شو ، کوم چې د ګوګل پلی قواعدو څخه سرغړونه کې د اجرا وړ کوډ ډاونلوډ کولو دوامداره وړتیا ته اشاره کوي.

سرچینه: www.habr.com

Add a comment