Di geroka UC de li qelsiyan digerin

Di geroka UC de li qelsiyan digerin

Pîrozbahiyê

Di dawiya meha Adarê de em ragihandin, ku wan şiyanek veşartî ji bo barkirin û meşandina koda nerastkirî di Geroka UC de kifş kir. Îro em ê bi hûrgulî binihêrin ka ev dakêşandin çawa çêdibe û çawa hacker dikarin wê ji bo armancên xwe bikar bînin.

Demek berê, UC Browser pir bi tundî hate reklam kirin û belav kirin: ew li ser cîhazên bikarhêneran bi karanîna malware hate saz kirin, di bin navê pelên vîdyoyê de ji malperên cihêreng hate belav kirin (ango, bikarhêneran difikirîn ku ew, wek nimûne, vîdyoyek porno dakêşin, lê di şûna wê de bi vê gerokê re APK wergirt), pankartên tirsnak bi peyamên ku gerok kevnar, lawaz, û tiştên mîna wiya ye bikar anîn. Di koma fermî ya Geroka UC ya li ser VK de heye mijarê, ku tê de bikarhêner dikarin ji reklama neheq gilî bikin, li wir gelek mînak hene. Di sala 2016’an de jî hebû reklama vîdyoyê bi rûsî (erê, reklama ji bo gerokek astengkirina reklamê).

Di dema nivîsandinê de, Geroka UC li ser Google Play zêdetirî 500 sazkirin hene. Ev balkêş e - tenê Google Chrome bêtir heye. Di nav nirxandinan de hûn dikarin gelek gilî li ser reklam û beralîkirina hin serîlêdanên li ser Google Play-ê bibînin. Sedema lêkolîna me ev bû: me biryar da ku em bibînin ka geroka UC tiştek xirab dike. Û derket holê ku ew dike!

Di koda serîlêdanê de, şiyana dakêşandin û xebitandina koda darvekirinê hate kifş kirin, ku li dijî qaîdeyên ji bo weşandina sepanan e li ser Google Play. Digel dakêşana koda darvekirinê, geroka UC wiya bi rengek neewle dike, ku dikare ji bo destpêkirina êrişek MitM were bikar anîn. Ka em bibînin ka em dikarin êrîşek wiha pêk bînin.

Her tiştê ku li jêr hatî nivîsandin ji bo guhertoya Geroka UC ya ku di dema lêkolînê de li Google Play-ê peyda bû têkildar e:

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

Vektora êrîşê

Di manîfestoya Geroka UC de hûn dikarin karûbarek bi navek xwe-ravekirî bibînin com.uc.deployment.UpgradeDeployService.

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

Dema ku ev karûbar dest pê dike, gerok daxwazek POST dike puds.ucweb.com/upgrade/index.xhtml, ku piştî destpêkirinê demek şûnda di trafîkê de tê dîtin. Di bersivê de, dibe ku ew fermanek werbigire ku hin nûvekirin an modulek nû dakêşîne. Di dema analîzê de, server emrên weha neda, lê me dît ku dema ku em hewl didin ku PDF-yek di gerokê de vekin, ew daxwazek duyemîn ji navnîşana ku li jor hatî destnîşan kirin dike, piştî ku ew pirtûkxaneya xwemalî dakêşîne. Ji bo pêkanîna êrîşê, me biryar da ku em vê taybetmendiya Geroka UC bikar bînin: şiyana vekirina PDF-ê bi karanîna pirtûkxaneyek xwemalî, ku ne di APK-ê de ye û heke hewce bike ew ji Înternetê dakêşîne. Hêjayî gotinê ye ku, ji hêla teorîkî ve, Geroka UC dikare bê neçar kirin ku tiştek bêyî dakêşana bikarhêner dakêşîne - heke hûn bersivek xweş-formandî bidin daxwazek ku piştî destpêkirina gerokê tête darve kirin. Lê ji bo kirina vê yekê, pêdivî ye ku em protokola danûstendina bi serverê re bi hûrgulî bixwînin, ji ber vê yekê me biryar da ku ew ê hêsantir be ku bersiva navborî biguhezînin û pirtûkxaneyê ji bo xebata bi PDF-ê re biguhezînin.

Ji ber vê yekê, gava ku bikarhênerek bixwaze PDF-ek rasterast di gerokê de veke, daxwazên jêrîn dikarin di seyrûseferê de werin dîtin:

Di geroka UC de li qelsiyan digerin

Pêşî daxwazek POST heye puds.ucweb.com/upgrade/index.xhtml, wê hingê
Arşîvek bi pirtûkxaneyek ji bo dîtina formatên PDF û ofîsê tê daxistin. Ev mentiqî ye ku meriv texmîn bike ku daxwaza yekem agahdariya li ser pergalê vediguhezîne (bi kêmanî mîmariya ku pirtûkxaneya hewce peyda dike), û di bersivê de gerok hin agahdariya li ser pirtûkxaneya ku divê were dakêşandin distîne: navnîşan û, dibe ku , tiştekî din. Pirsgirêk ev e ku ev daxwaz bi şîfre ye.

Daxwaza parçeyê

Parçeya bersivê

Di geroka UC de li qelsiyan digerin

Di geroka UC de li qelsiyan digerin

Pirtûkxane bixwe di ZIP de tê pak kirin û ne şîfrekirî ye.

Di geroka UC de li qelsiyan digerin

Li koda şîfrekirina trafîkê bigerin

Ka em hewl bidin ku bersiva serverê deşîfre bikin. Ka em li koda polê binêrin com.uc.deployment.UpgradeDeployService: ji rêbazê onStartCommand biçe com.uc.deployment.bx, û ji wê re 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);
}

Em li vir damezrandina daxwazek POST-ê dibînin. Em bala xwe didin afirandina komek ji 16 byte û dagirtina wê: 0x5F, 0, 0x1F, -50 (=0xCE). Bi ya ku me di daxwaznameya jorîn de dît hevûdu ye.

Di heman polê de hûn dikarin çînek hêlînek bibînin ku rêbazek din a balkêş heye:

        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");
}
}

Rêbaz komek byte wekî têketinê digire û kontrol dike ku byta sifir 0x60 an ya sêyemîn 0xD0 e, û byta duyemîn 1, 11 an 0x1F ye. Em li bersiva serverê dinêrin: byte sifir 0x60 e, ya duyemîn 0x1F, ya sêyemîn 0x60 e. Dengên ku em hewce ne. Li gorî rêzikên (mînakek "up_decrypt") dadbar kirin, divê li vir rêbazek were gotin ku dê bersiva serverê şîfre bike.
Ka em herin ser rêbazê gj. Bala xwe bidinê ku argumana yekem byte ye li ser 2-ê (ango di doza me de 0x1F), û ya duyemîn jî bersiva serverê ye bêyî
yekem 16 bytes.

     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;
}

Eşkere ye, li vir em algorîtmayek deşîfrekirinê, û heman byte ku di me de ye, hilbijêrin
doza 0x1F wekhev e, yek ji sê vebijarkên gengaz destnîşan dike.

Em analîzkirina kodê berdewam dikin. Piştî çend bazdanan em xwe di rêbazeke bi navekî xweser de dibînin decryptBytesByKey.

Li vir du bytên din ji bersiva me têne veqetandin, û rêzek ji wan tê wergirtin. Eşkere ye ku bi vî awayî mifteya deşîfrekirina peyamê tê hilbijartin.

    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;
}

Li pêş çavan, em bala xwe didinê ku di vê qonaxê de em hîna mifteyek nagirin, lê tenê "nasnameya" wê bi dest dixin. Girtina mifteyê hinekî tevlihevtir e.

Di rêbaza paşîn de, du pîvanên din li yên heyî têne zêdekirin, ku çar ji wan têne çêkirin: hejmara sêrbaz 16, nasnavê mifteyê, daneya şîfrekirî, û rêzek nefêmkirî (di rewşa me de, vala).

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

Piştî rêze veguherînan em digihîjin rêbazê staticBinarySafeDecryptNoB64 navrû com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Di koda serîlêdana sereke de dersên ku vê navberê bicîh dikin tune. Di dosyayê de çîneke wiha heye lib/armeabi-v7a/libsgmain.so, ku bi rastî ne .so, lê .jar e. Rêbaza ku em jê re eleqedar dibin bi vî rengî pêk tê:

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);
}
//...
}

Li vir navnîşa me ya pîvanan bi du hejmarên din ve tê temam kirin: 2 û 0.
her tişt, 2 tê wateya deşîfrekirinê, wekî di rêbazê de doFinal çîna pergalê javax.crypto.Cipher. Û ev hemî ji Routerek diyarkirî ya bi hejmara 10601 ve tê veguheztin - ev eşkere hejmara fermanê ye.

Piştî zincîra paşîn a veguheztinê em çînek dibînin ku navrûyê pêk tîne IRouterComponent û rêbaz 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);
}
}

Û her weha polê Pirtûkxaneya JNICL, ku tê de rêbaza xwemalî tê ragihandin doCommandNative:

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

Ev tê vê wateyê ku divê em di koda xwemalî de rêbazek bibînin doCommandNative. Û ev e ku kêfê dest pê dike.

Têkbirina koda makîneyê

Di pelê de libsgmain.so (ku bi rastî .jar e û me tê de pêkanîna hin navberên bi şîfrekirinê tenê li jor dît) pirtûkxaneyek xwemalî heye: libsgmainso-6.4.36.so. Em wê di IDA de vedikin û komek qutiyên diyalogê yên bi xeletî digirin. Pirsgirêk ev e ku tabloya sernavê beşê nederbasdar e. Ev bi mebest tê kirin ku analîzê tevlihev bike.

Di geroka UC de li qelsiyan digerin

Lê ew ne hewce ye: ji bo rast barkirina pelek ELF û analîzkirina wê, tabloyek sernavê bernameyê bes e. Ji ber vê yekê, em tenê tabloya beşê jêbirin, qadên têkildar ên di serî de sifir dikin.

Di geroka UC de li qelsiyan digerin

Pelê dîsa li IDA vekin.

Du away hene ku meriv ji makîneya virtual Java-yê bêje ku tam di pirtûkxaneya xwemalî de pêkanîna rêbazek ku di koda Java-yê de wekî xwemalî hatî ragihandin li ku ye. Ya yekem ew e ku navê cureyê jê re were dayîn Java_package_name_ClassName_MethodName.

Ya duyemîn ew e ku dema barkirina pirtûkxaneyê wê tomar bike (di fonksiyonê de JNI_OnLoad)
bikaranîna banga fonksiyonê RegisterNatives.

Di rewşa me de, heke em rêbaza yekem bikar bînin, divê nav wiha be: Java_com_taobao_wireless_security_adapter_JNICLlibrary_doCommandNative.

Di nav fonksiyonên hinardekirî de fonksiyonek wusa tune, ku tê vê wateyê ku hûn hewce ne ku li bangekê bigerin RegisterNatives.
Ka em biçin fonksiyonê JNI_OnLoad û em vê wêneyê dibînin:

Di geroka UC de li qelsiyan digerin

Li vir çi diqewime? Di nihêrîna pêşîn de, destpêk û dawiya fonksiyonê ji bo mîmariya ARM-ê tîpîk e. Telîmata yekem a li ser stakê naveroka tomarên ku fonksiyon dê di xebata xwe de bikar bîne (di vê rewşê de, R0, R1 û R2), û her weha naveroka tomara LR, ku navnîşana vegerê ya ji fonksiyonê vedihewîne, digire. . Talîmata paşîn tomarên tomarkirî vedigerîne, û navnîşana vegerê tavilê di qeyda PC-yê de tê danîn - bi vî rengî ji fonksiyonê vedigere. Lê heke hûn ji nêz ve lê mêze bikin, hûn ê ferq bikin ku talîmata pêşdawî navnîşana vegerê ya ku li ser stikê hatî hilanîn diguhezîne. Werin em hesab bikin ka dê paşê çawa be
pêkanîna kodê. Navnîşanek diyar 1xB0 li R130 tê barkirin, 5 jê tê derxistin, dûv re ew li R0 tê veguheztin û 0x10 lê zêde dibe. Ew 0xB13B derdikeve. Bi vî rengî, IDA difikire ku fermana paşîn vegerek fonksiyonek normal e, lê bi rastî ew diçe navnîşana hesabkirî 0xB13B.

Hêjayî bibîrxistinê ye ku li vir pêvajoyên ARM du mod û du rêzikên rêwerzan hene: ARM û Thumb. Bitka herî kêm girîng a navnîşanê ji pêvajoyê re dibêje ka kîjan koma rêwerzan tê bikar anîn. Ango, navnîş bi rastî 0xB13A ye, û yek di bit-a herî kêm girîng de moda Thumb nîşan dide.

Di vê pirtûkxaneyê de "adapter"ek wekhev li destpêka her fonksiyonê hatî zêdekirin û
koda çopê. Em ê bêtir li ser wan bi hûrgulî nesekinin - em tenê bi bîr tînin
ku destpêka rastîn ya hema hema hemî fonksiyonan hinekî dûr e.

Ji ber ku kod bi eşkereyî naçe 0xB13A, IDA bixwe nas nekir ku kod li vê derê ye. Ji ber heman sedemê, ew piraniya kodê di pirtûkxaneyê de wekî kod nas nake, ku analîzê hinekî dijwar dike. Em ji IDA re dibêjin ku ev kod e, û tiştê ku diqewime ev e:

Di geroka UC de li qelsiyan digerin

Tablo bi eşkere li 0xB144 dest pê dike. Di sub_494C de çi ye?

Di geroka UC de li qelsiyan digerin

Dema ku em vê fonksiyonê di qeyda LR de vedixwînin, em navnîşana tabloya ku berê hatî behs kirin (0xB144) distînin. Di R0 de - index di vê tabloyê de. Ango nirx ji tabloyê tê girtin, li LR-ê tê zêdekirin û encam derdikeve
navnîşana ku biçin. Ka em hewl bidin ku wê hesab bikin: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Em diçin navnîşana wergirtî û bi rastî çend rêwerzên kêrhatî dibînin û dîsa diçin 0xB140:

Di geroka UC de li qelsiyan digerin

Naha dê bi nîşana 0x20 ji tabloyê veguheztinek li dûrbûnê hebe.

Li gorî mezinahiya tabloyê dadbar kirin, dê di kodê de gelek veguherînên weha hebin. Pirs derdikeve holê gelo gengaz e ku meriv bi rengekî bixweber bi vê yekê re mijûl bibe, bêyî ku bi destan navnîşanan hesab bike. Û skrîpt û şiyana patchkirina kodê di IDA de têne alîkariya me:

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"

Kursorê li ser xeta 0xB26A bixin, skrîptê bimeşînin û veguheztina 0xB4B0 bibînin:

Di geroka UC de li qelsiyan digerin

IDA dîsa ev herêm wekî kod nas nekir. Em alîkariya wê dikin û li wir sêwiranek din dibînin:

Di geroka UC de li qelsiyan digerin

Talîmatên piştî BLX-ê pir maqûl nabin, ew bêtir wekî celebek jicîhûwarkirinê ye. Ka em li sub_4964 binêrin:

Di geroka UC de li qelsiyan digerin

Û bi rastî, li vir dwordek li navnîşana ku di LR-ê de ye tê girtin, li vê navnîşanê tê zêdekirin, pişt re nirxa navnîşana encam tê girtin û li ser stikê tê danîn. Di heman demê de, 4 li LR-ê tê zêdekirin da ku piştî ku ji fonksiyonê vedigere, ev heman veqetandî tê avêtin. Piştî ku fermana POP {R1} nirxa encam ji stikê digire. Ger hûn li tiştê ku li navnîşana 0xB4BA + 0xEA = 0xB5A4 cih digire binêrin, hûn ê tiştek mîna tabloya navnîşan bibînin:

Di geroka UC de li qelsiyan digerin

Ji bo paqijkirina vê sêwiranê, hûn ê hewce ne ku du parameteran ji kodê bistînin: jimare û hejmara qeydê ya ku hûn dixwazin encamê tê de bixin. Ji bo her qeydek gengaz, hûn ê neçar bin ku ji berê ve perçeyek kodê amade bikin.

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"

Em kursorê li destpêka avahiya ku em dixwazin biguhezînin - 0xB4B2 - bi cîh dikin û skrîptê dimeşînin:

Di geroka UC de li qelsiyan digerin

Ji bilî strukturên ku berê hatine behs kirin, koda jêrîn jî heye:

Di geroka UC de li qelsiyan digerin

Mîna ku di doza berê de, piştî fermana BLX veqetandinek heye:

Di geroka UC de li qelsiyan digerin

Em li ser navnîşana LR-ê guheztinê digirin, li LR-ê zêde dikin û diçin wir. 0x72044 + 0xC = 0x72050. Nivîsara vê sêwiranê pir hêsan e:

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"

Encama pêkanîna skrîptê:

Di geroka UC de li qelsiyan digerin

Gava ku her tişt di fonksiyonê de hate qewirandin, hûn dikarin IDA-ya destpêka wê ya rastîn destnîşan bikin. Ew ê hemî koda fonksiyonê li hev bicivîne, û ew dikare bi karanîna HexRays were veqetandin.

Têlên deşîfrekirin

Em fêr bûne ku em di pirtûkxaneyê de bi nefretkirina koda makîneyê re mijûl bibin libsgmainso-6.4.36.so ji geroka UC û koda fonksiyonê wergirt 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;
}

Ka em bi hûrgulî li rêzikên jêrîn binêrin:

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

Di fonksiyonê de sub_73E24 navê polê bi eşkere tê deşîfrekirin. Wekî parametreyên vê fonksiyonê, nîşanek daneya ku mîna daneyên şîfrekirî, tamponek diyar û hejmarek derbas dibe. Eşkere ye, piştî bangkirina fonksiyonê, dê di tamponê de xêzek deşîfrekirî hebe, ji ber ku ew ji fonksiyonê re derbas dibe. FindClass, ku navê polê wekî pîvana duyemîn digire. Ji ber vê yekê, hejmar mezinahiya tampon an dirêjahiya rêzê ye. Ka em hewl bidin ku navê polê deşîfre bikin, divê ew ji me re bêje ka em di rêça rast de diçin. Werin em ji nêz ve li tiştê ku diqewime binêre 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;
}

function sub_7AF78 ji bo rêzikên byte yên bi mezinahiya diyarkirî mînakek konteynerek diafirîne (em ê bi hûrgulî li ser van konteyneran nesekinin). Li vir du konteynerên weha têne afirandin: yek rêzê dihewîne "DcO/lcK+h?m3c*q@" (hêsan e ku meriv texmîn bike ku ev mifteyek e), ya din daneyên şîfrekirî dihewîne. Dûv re, her du tişt di avahiyek diyar de têne danîn, ku ji fonksiyonê re derbas dibe sub_6115C. Werin em di vê avahîsaziyê de qadek bi nirxa 3 jî nîşan bikin. Ka em bibînin ka paşê çi dibe bila bibe.

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;
}

Parametreya guhêrbar qadeke avahîsaziyê ye ku berê nirxa 3 jê re hatibû veqetandin. Li rewşa 3 binêrin: li fonksiyonê sub_6364C Parametreyên ji strukturên ku di fonksiyona berê de li wir hatine zêdekirin têne derbas kirin, ango daneyên sereke û şîfrekirî. Ger hûn ji nêz ve lê binêrin sub_6364C, hûn dikarin algorîtmaya RC4 tê de nas bikin.

Algorîtmek û mifteyek me heye. Ka em hewl bidin ku navê polê deşîfre bikin. Li vir çi qewimî: com / taobao / bêtêl / ewlekarî / adapter / JNICLlibrary. Ecêb! Em li ser rêya rast in.

Dara fermanê

Niha em hewce ne ku dijwariyek bibînin RegisterNatives, ku dê fonksiyonê nîşanî me bide doCommandNative. Ka em li fonksiyonên ku jê têne gazî kirin binêrin JNI_OnLoad, û em wê tê de bibînin 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;
}

Û bi rastî, rêbazek xwemalî ya bi navê li vir hatî tomar kirin doCommandNative. Niha em navnîşana wî dizanin. Ka em bibînin ka ew çi dike.

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;
}

Bi navê xwe hûn dikarin texmîn bikin ku li vir xala têketina hemî fonksiyonên ku pêşdebiran biryar dane ku veguhezînin pirtûkxaneya xwemalî ye. Em bala fonksiyona jimare 10601 dikin.

Hûn dikarin ji kodê bibînin ku hejmara fermanê sê hejmaran çêdike: ferman / 10000, ferman % 10000 / 100 и ferman % 10, ango, di rewşa me de, 1, 6 û 1. Ev her sê hejmar û her weha nîşanek JNIEnv û argumanên ku ji fonksiyonê re derbas dibin li avahiyek têne zêdekirin û derbas dibin. Bi karanîna sê hejmarên hatine bidestxistin (em wan N1, N2 û N3 destnîşan bikin), dara fermanê tê çêkirin.

Tiştek weha:

Di geroka UC de li qelsiyan digerin

Dar bi dînamîk tê dagirtin JNI_OnLoad.
Sê hejmar di darê de rêyê şîfre dikin. Her pelê darê navnîşana pocked ya fonksiyona têkildar dihewîne. Mifteya di girêka dêûbav de ye. Ger hûn hemî strukturên ku têne bikar anîn fam bikin (em wan rave nakin da ku gotarek jixwe pir mezin bişewitînin) peydakirina cîhê kodê ku fonksiyona ku em hewce ne li darê lê zêde kirin ne dijwar e.

Bêhtir tevlihevî

Me navnîşana fonksiyona ku divê seyrûsefera şîfre bike wergirt: 0x5F1AC. Lê hê zû ye ku em şa bibin: pêşdebirên UC Browser ji me re surprîzek din amade kirine.

Piştî wergirtina pîvanên ji rêzika ku di koda Java de hatî çêkirin, em digirin
fonksiyona li navnîşana 0x4D070. Û li vir celebek din a nermalava kodê li benda me ye.

Em di R7 û R4 de du nîşanan danîn:

Di geroka UC de li qelsiyan digerin

Em navnîşa yekem vediguhezînin R11:

Di geroka UC de li qelsiyan digerin

Ji bo ku navnîşek ji tabloyê bistînin, navnîşek bikar bînin:

Di geroka UC de li qelsiyan digerin

Piştî ku diçe navnîşana yekem, navnîşa duyemîn tê bikaranîn, ku di R4 de ye. Di tabloyê de 230 hêman hene.

Li ser wê çi bikin? Hûn dikarin ji IDA-yê re bibêjin ku ev guhêrbar e: Biguherîne -> Din -> Îdyoma guhastinê diyar bike.

Di geroka UC de li qelsiyan digerin

Koda encam tirsnak e. Lê, ku hûn riya xwe di nav daristana wê de derbas bikin, hûn dikarin bangek fonksiyonek ku jixwe ji me re naskirî ye bibînin sub_6115C:

Di geroka UC de li qelsiyan digerin

Veguhezek hebû ku di doza 3 de bi karanîna algorîtmaya RC4 deşîfreyek hebû. Û di vê rewşê de, avahiya ku ji fonksiyonê re derbas dibe ji pîvanên ku jê re derbas bûne tije ye doCommandNative. Ka em li wir tiştên ku me hebûn bînin bîra xwe magicInt bi nirxa 16. Em li doza têkildar dinêrin - û piştî çend veguheztinan em koda ku algorîtma pê tê nasîn bibînin.

Di geroka UC de li qelsiyan digerin

Ev AES e!

Algorîtmak heye, ya ku dimîne ev e ku meriv pîvanên wê bistîne: mod, key û, dibe ku, vektora destpêkê (hebûna wê bi moda xebitandinê ya algorîtmaya AES ve girêdayî ye). Avahiya bi wan re divê li cîhek berî banga fonksiyonê were çêkirin sub_6115C, lê ev beşê kodê bi taybetî baş tê qewirandin, ji ber vê yekê fikir çêdibe ku kodê biqelînin da ku hemî pîvanên fonksiyona deşîfrekirinê di pelek de werin avêtin.

Pîne

Ji bo ku hûn hemî koda patchê bi zimanê meclîsê bi destan nenivîsin, hûn dikarin Android Studio-yê dest pê bikin, li wir fonksiyonek binivîsin ku heman pîvanên têketinê wekî fonksiyona meya deşîfrekirinê distîne û li pelek dinivîse, dûv re koda ku berhevkar dê bişopîne-pêç bike. xûlqkirin.

Hevalên me yên ji tîmê gerokê UC di heman demê de rehetiya zêdekirina kodê digirin. Ka em ji bîr mekin ku di destpêka her fonksiyonê de kodek çopê heye ku bi hêsanî dikare bi yekî din re were guheztin. Pir rehet 🙂 Lêbelê, di destpêka fonksiyona armancê de cîhê têr tune ji bo kodê ku hemî pîvanan li pelek tomar dike. Min neçar kir ku wê perçe perçe bikim û blokên çopê ji fonksiyonên cîran bikar bînim. Bi tevahî çar beş bûn.

Beşa yekem:

Di geroka UC de li qelsiyan digerin

Di mîmariya ARM de, çar parametreyên fonksiyonê yên yekem di nav qeydên R0-R3 de têne derbas kirin, yên mayî, heke hebe, di stikê de têne derbas kirin. Qeyda LR navnîşana vegerê digire. Pêdivî ye ku ev hemî were hilanîn da ku fonksiyon karibe piştî ku em pîvanên xwe davêjin bixebite. Her weha pêdivî ye ku em hemî tomarên ku em ê di pêvajoyê de bikar bînin hilînin, ji ber vê yekê em PUSH.W {R0-R10,LR} dikin. Di R7-ê de em navnîşana navnîşa parametreyên ku bi riya stakê ve ji fonksiyonê re derbas bûne digirin.

Bikaranîna fonksiyonê fopen em pelê vekin /dane/herêmî/tmp/aes di moda "ab".
yanî ji bo zêdekirinê. Di R0 de em navnîşana navê pelê bar dikin, di R1 de - navnîşana rêza ku modê nîşan dide. Û li vir koda çopê bi dawî dibe, ji ber vê yekê em diçin fonksiyona din. Ji bo ku ew xebata xwe bidomîne, em di destpêkê de veguheztina koda rastîn a fonksiyonê, çopê derbas dikin, û li şûna çopê em berdewamiyek paçê lê zêde dikin.

Di geroka UC de li qelsiyan digerin

Gazî kirin fopen.

Sê pîvanên pêşîn ên fonksiyonê aes tîp hene int. Ji ber ku me di destpêkê de tomar li stikê tomar kir, em dikarin bi hêsanî fonksiyonê derbas bikin fwrite navnîşanên wan li ser stackê.

Di geroka UC de li qelsiyan digerin

Dûv re sê strukturên me hene ku mezinahiya daneyê û nîşanek daneya ji bo mifteyê, vektora destpêkê û daneya şîfrekirî vedihewîne.

Di geroka UC de li qelsiyan digerin

Di dawiyê de, pelê bigire, tomaran vegerîne û kontrolê veguhezîne fonksiyona rastîn aes.

Em APK-yek bi pirtûkxaneyek patched berhev dikin, wê îmze dikin, wê li cîhazê/emulatorê bar dikin û dest pê dikin. Em dibînin ku çopê me tê çêkirin, û gelek dane li wir têne nivîsandin. Gerok ne tenê ji bo seyrûseferê şîfrekirinê bikar tîne, û hemî şîfrekirin bi fonksiyona pirsê re derbas dibe. Lê ji ber hin sedeman daneyên pêwîst li wir nîn in, û daxwaza pêwîst di trafîkê de nayê dîtin. Ji bo ku em li bendê nemînin heya ku Geroka UC-ê daxwaznameya hewce bike, em bersiva şîfrekirî ji servera ku berê hatî wergirtin bistînin û serîlêdanê ji nû ve bişopînin: Deşîfrekirinê li serCreate ya çalakiya sereke zêde bikin.

    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

Em kom dikin, îmze dikin, saz dikin, dest pê dikin. Em NullPointerException distînin ji ber ku rêbaz null vedigere.

Di dema analîzkirina bêtir kodê de, fonksiyonek ku rêzikên balkêş deşîfre dike hate dîtin: "META-INF/" û ".RSA". Wusa dixuye ku serîlêdan sertîfîkaya xwe verast dike. An jî ji wê kilîtan çêdike. Ez bi rastî naxwazim bi tiştê ku bi sertîfîkayê re diqewime re mijûl bibim, ji ber vê yekê em ê tenê wê sertîfîkaya rast derxînin. Ka em rêzika şîfrekirî bişkînin da ku li şûna "META-INF/" em "BLABLINF/" bistînin, peldankek bi wî navî di APK-ê de biafirînin û sertîfîkaya geroka squirrel lê zêde bikin.

Em kom dikin, îmze dikin, saz dikin, dest pê dikin. Bingo! Mifta me heye!

MitM

Me mifteyek û vektorek destpêkbûnê bi qasî mifteyê wergirt. Ka em hewl bidin ku di moda CBC de bersiva serverê deşîfre bikin.

Di geroka UC de li qelsiyan digerin

Em URL-ya arşîvê, tiştek mîna MD5, "extract_unzipsize" û hejmarek dibînin. Em kontrol dikin: MD5-ya arşîvê yek e, mezinahiya pirtûkxaneya nepakkirî yek e. Em hewl didin ku vê pirtûkxaneyê patch bikin û bidin gerokê. Ji bo ku nîşan bidin ku pirtûkxaneya meya patched barkirî ye, em ê Nîyetekê bidin destpêkirin ku SMSek bi nivîsa "PWNED!" Em ê du bersivên ji serverê biguhezînin: puds.ucweb.com/upgrade/index.xhtml û ji bo daxistina arşîvê. Di ya yekem de em MD5-ê diguhezînin (mezin piştî vekirina pakkirinê nayê guheztin), di ya duyemîn de em arşîvê bi pirtûkxaneya paçkirî re didin.

Gerok çend caran hewl dide ku arşîvê dakêşîne, piştî ku ew xeletiyek dide. Xuyaye tiştek
ew hez nake. Di encama analîzkirina vê formata gemar de, derket holê ku server mezinahiya arşîvê jî vediguhezîne:

Di geroka UC de li qelsiyan digerin

Ew di LEB128 de tê kod kirin. Piştî patchê, mezinahiya arşîvê bi pirtûkxaneyê re hinekî guherî, ji ber vê yekê gerokê fikirî ku arşîv bi xeletî hate dakêşandin, û piştî çend hewldanan ew xeletiyek derxist.

Em mezinahiya arşîvê eyar dikin... Û – serketin! 🙂 Encam di vîdyoyê de ye.

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

Encam û berteka pêşdebiran

Bi heman awayî, hacker dikarin taybetmendiya neewle ya UC Browser bikar bînin da ku pirtûkxaneyên xerab belav bikin û bimeşînin. Ev pirtûkxane dê di çarçoveya gerokê de bixebitin, ji ber vê yekê ew ê hemî destûrên pergala wê bistînin. Wekî encamek, şiyana xuyangkirina pencereyên phishing, û her weha gihîştina pelên xebatê yên çîpka çînî ya porteqalî, tevî têketin, şîfre û çerezên ku di databasê de hatine hilanîn.

Me bi pêşdebirên UC Browser re têkilî danî û me wan di derbarê pirsgirêka ku me dît de agahdar kir, me hewl da ku em qelsî û xetereya wê destnîşan bikin, lê wan tiştek bi me re nîqaş nekir. Di vê navberê de, gerok berdewam kir ku taybetmendiya xwe ya xeternak li ber çavan eşkere bike. Lê gava ku me hûrguliyên lawaziyê eşkere kir, êdî ne mimkun bû ku em wekî berê piştguh bikin. 27ê Adarê bû
guhertoyek nû ya UC Browser 12.10.9.1193 hate berdan, ku bi riya HTTPS ve gihîştiye serverê: puds.ucweb.com/upgrade/index.xhtml.

Wekî din, piştî "serrastkirinê" û heya dema nivîsandina vê gotarê, hewldana vekirina PDF-yek di gerokek de di encamê de peyamek çewtiyek bi nivîsa "Oops, tiştek xelet derket!" Dema ku hewl dida vekirina PDF-ê daxwazek ji serverê re nehat kirin, lê dema ku gerok hate destpêkirin daxwazek hate kirin, ku ev yek li ser berdewamiya kapasîteya dakêşana koda darvekirinê di binpêkirina qaîdeyên Google Play de destnîşan dike.

Source: www.habr.com

Add a comment