Chwilio am wendidau yn Porwr UC

Chwilio am wendidau yn Porwr UC

Cyflwyniad

Ar ddiwedd mis Mawrth rydym adroddwyd, eu bod wedi darganfod gallu cudd i lwytho a rhedeg cod heb ei wirio yn Porwr UC. Heddiw, byddwn yn edrych yn fanwl ar sut mae'r lawrlwythiad hwn yn digwydd a sut y gall hacwyr ei ddefnyddio at eu dibenion eu hunain.

Beth amser yn ôl, hysbysebwyd Porwr UC a'i ddosbarthu'n ymosodol iawn: fe'i gosodwyd ar ddyfeisiau defnyddwyr gan ddefnyddio malware, a ddosbarthwyd o wahanol wefannau dan gochl ffeiliau fideo (hy, roedd defnyddwyr yn meddwl eu bod yn lawrlwytho, er enghraifft, fideo porn, ond yn lle hynny derbyn APK gyda'r porwr hwn), defnyddio baneri brawychus gyda negeseuon bod y porwr yn hen ffasiwn, yn agored i niwed, a phethau felly. Yn y grŵp Porwr UC swyddogol ar VK mae yna pwnc, lle gall defnyddwyr gwyno am hysbysebu annheg, mae yna lawer o enghreifftiau yno. Yn 2016 roedd yna wastad hysbysebu fideo yn Rwsieg (ie, hysbysebu ar gyfer porwr sy'n rhwystro hysbysebion).

Ar adeg ysgrifennu hwn, mae gan UC Browser dros 500 o osodiadau ar Google Play. Mae hyn yn drawiadol - dim ond Google Chrome sydd â mwy. Ymhlith yr adolygiadau gallwch weld cryn dipyn o gwynion am hysbysebu ac ailgyfeirio i rai cymwysiadau ar Google Play. Dyma oedd y rheswm dros ein hymchwil: fe benderfynon ni weld a oedd Porwr UC yn gwneud rhywbeth drwg. Ac mae'n troi allan ei fod yn ei wneud!

Yn y cod cais, darganfuwyd y gallu i lawrlwytho a rhedeg cod gweithredadwy, sy'n groes i'r rheolau ar gyfer cyhoeddi ceisiadau ar Google Play. Yn ogystal â lawrlwytho cod gweithredadwy, mae Porwr UC yn gwneud hynny mewn modd ansicr, y gellir ei ddefnyddio i lansio ymosodiad MitM. Gawn ni weld a allwn ni gynnal ymosodiad o'r fath.

Mae popeth sydd wedi'i ysgrifennu isod yn berthnasol i'r fersiwn o UC Browser a oedd ar gael ar Google Play ar adeg yr astudiaeth:

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

Fector ymosodiad

Ym maniffest Porwr UC gallwch ddod o hyd i wasanaeth gydag enw hunanesboniadol com.uc.deployment.UpgradeDeployService.

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

Pan fydd y gwasanaeth hwn yn cychwyn, mae'r porwr yn gwneud cais POST i puds.ucweb.com/upgrade/index.xhtml, sydd i'w weld mewn traffig beth amser ar ôl y cychwyn. Mewn ymateb, efallai y bydd yn derbyn gorchymyn i lawrlwytho rhywfaint o ddiweddariad neu fodiwl newydd. Yn ystod y dadansoddiad, ni roddodd y gweinydd orchmynion o'r fath, ond fe wnaethom sylwi, pan geisiwn agor PDF yn y porwr, ei fod yn gwneud ail gais i'r cyfeiriad a nodir uchod, ac ar ôl hynny mae'n lawrlwytho'r llyfrgell frodorol. I gyflawni'r ymosodiad, fe wnaethom benderfynu defnyddio'r nodwedd hon o UC Browser: y gallu i agor PDF gan ddefnyddio llyfrgell frodorol, nad yw yn yr APK ac y mae'n ei lawrlwytho o'r Rhyngrwyd os oes angen. Mae'n werth nodi, yn ddamcaniaethol, y gellir gorfodi Porwr UC i lawrlwytho rhywbeth heb ryngweithio â defnyddwyr - os ydych chi'n darparu ymateb wedi'i ffurfio'n dda i gais a weithredir ar ôl lansio'r porwr. Ond i wneud hyn, mae angen inni astudio'r protocol rhyngweithio â'r gweinydd yn fwy manwl, felly fe wnaethom benderfynu y byddai'n haws golygu'r ymateb rhyng-gipio a disodli'r llyfrgell ar gyfer gweithio gyda PDF.

Felly, pan fydd defnyddiwr eisiau agor PDF yn uniongyrchol yn y porwr, gellir gweld y ceisiadau canlynol yn y traffig:

Chwilio am wendidau yn Porwr UC

Yn gyntaf mae cais POST i puds.ucweb.com/upgrade/index.xhtml, yna
Mae archif gyda llyfrgell ar gyfer gwylio PDF a fformatau swyddfa yn cael ei lawrlwytho. Mae'n rhesymegol tybio bod y cais cyntaf yn trosglwyddo gwybodaeth am y system (o leiaf y bensaernïaeth i ddarparu'r llyfrgell ofynnol), ac mewn ymateb iddo mae'r porwr yn derbyn rhywfaint o wybodaeth am y llyfrgell y mae angen ei lawrlwytho: y cyfeiriad ac, o bosibl , Rhywbeth arall. Y broblem yw bod y cais hwn wedi'i amgryptio.

Cais darn

Ateb darn

Chwilio am wendidau yn Porwr UC

Chwilio am wendidau yn Porwr UC

Mae'r llyfrgell ei hun wedi'i phecynnu mewn ZIP ac nid yw wedi'i hamgryptio.

Chwilio am wendidau yn Porwr UC

Chwilio am god dadgryptio traffig

Gadewch i ni geisio dehongli ymateb y gweinydd. Edrychwn ar god y dosbarth com.uc.deployment.UpgradeDeployService: o ddull onStartCommand mynd i com.uc.deployment.bx, ac oddi wrtho i 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);
}

Rydym yn gweld ffurfio cais POST yma. Rydyn ni'n talu sylw i greu amrywiaeth o 16 beit a'i lenwad: 0x5F, 0, 0x1F, -50 (=0xCE). Yn cyd-fynd â'r hyn a welsom yn y cais uchod.

Yn yr un dosbarth gallwch weld dosbarth nythu sydd â dull diddorol arall:

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

Mae'r dull yn cymryd amrywiaeth o beit fel mewnbwn ac yn gwirio bod y sero beit yn 0x60 neu'r trydydd beit yn 0xD0, a'r ail beit yn 1, 11 neu 0x1F. Edrychwn ar yr ymateb gan y gweinydd: y sero beit yw 0x60, yr ail yw 0x1F, y trydydd yw 0x60. Mae'n swnio fel yr hyn sydd ei angen arnom. A barnu yn ôl y llinellau (“up_decrypt”, er enghraifft), dylid galw dull yma a fydd yn dadgryptio ymateb y gweinydd.
Gadewch i ni symud ymlaen at y dull gj. Sylwch mai'r arg gyntaf yw'r beit ar wrthbwyso 2 (h.y. 0x1F yn ein hachos ni), a'r ail yw ymateb y gweinydd heb
16 beit cyntaf.

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

Yn amlwg, yma rydym yn dewis algorithm dadgryptio, a'r un beit sydd yn ein
achos sy'n hafal i 0x1F, yn dynodi un o dri opsiwn posibl.

Rydym yn parhau i ddadansoddi'r cod. Ar ôl ychydig o neidiau cawn ein hunain mewn dull ag enw hunanesboniadol dadgryptioBytesByKey.

Yma mae dau beit arall yn cael eu gwahanu oddi wrth ein hymateb, a cheir llinyn ohonynt. Mae'n amlwg mai yn y modd hwn y dewisir yr allwedd ar gyfer dadgryptio'r neges.

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

Wrth edrych ymlaen, nodwn nad ydym ar hyn o bryd yn cael allwedd, ond dim ond ei “dynodydd”. Mae cael yr allwedd ychydig yn fwy cymhleth.

Yn y dull nesaf, mae dau baramedr arall yn cael eu hychwanegu at y rhai presennol, gan wneud pedwar ohonynt: y rhif hud 16, y dynodwr allweddol, y data wedi'i amgryptio, a llinyn annealladwy (yn ein hachos ni, yn wag).

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

Ar ôl cyfres o drawsnewidiadau rydym yn cyrraedd y dull staticBinarySafeDecryptNoB64 rhyngwyneb com.alibaba.wireless.security.open.staticdataencrypt.IStaticDataEncryptComponent. Nid oes unrhyw ddosbarthiadau yn y prif god cais sy'n gweithredu'r rhyngwyneb hwn. Mae dosbarth o'r fath yn y ffeil lib/armeabi-v7a/libsgmain.so, nad yw mewn gwirionedd yn .so, ond yn .jar. Mae'r dull y mae gennym ddiddordeb ynddo yn cael ei weithredu fel a ganlyn:

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

Yma ategir ein rhestr o baramedrau gan ddau gyfanrif arall: 2 a 0. Beirniadu yn ôl
popeth, mae 2 yn golygu dadgryptio, fel yn y dull doFinal dosbarth system javax.crypto.Cipher. Ac mae hyn i gyd yn cael ei drosglwyddo i lwybrydd penodol gyda'r rhif 10601 - mae'n debyg mai dyma'r rhif gorchymyn.

Ar ôl y gadwyn nesaf o drawsnewidiadau rydym yn dod o hyd i ddosbarth sy'n gweithredu'r rhyngwyneb IRouterComponent a dull 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);
}
}

A hefyd dosbarth JNICLlibrary, yn yr hwn y datgenir y dull brodorol doCommandNative:

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

Mae hyn yn golygu bod angen i ni ddod o hyd i ddull yn y cod brodorol doCommandNative. A dyma lle mae'r hwyl yn dechrau.

Obfuscation o god peiriant

Mewn ffeil libsgmain.so (sef .jar mewn gwirionedd a lle canfuom weithrediad rhai rhyngwynebau cysylltiedig ag amgryptio ychydig uwchben) mae un llyfrgell frodorol: libsgmainso-6.4.36.so. Rydyn ni'n ei agor yn IDA ac yn cael criw o flychau deialog gyda gwallau. Y broblem yw bod tabl pennyn yr adran yn annilys. Gwneir hyn yn bwrpasol i gymhlethu'r dadansoddiad.

Chwilio am wendidau yn Porwr UC

Ond nid oes ei angen: i lwytho ffeil ELF yn gywir a'i ddadansoddi, mae tabl pennawd rhaglen yn ddigonol. Felly, rydym yn syml yn dileu'r tabl adran, gan sero allan y meysydd cyfatebol yn y pennawd.

Chwilio am wendidau yn Porwr UC

Agorwch y ffeil yn IDA eto.

Mae dwy ffordd i ddweud wrth y peiriant rhithwir Java ble yn union yn y llyfrgell frodorol y mae gweithredu dull a ddatganwyd yn y cod Java fel un brodorol. Y cyntaf yw rhoi enw rhywogaeth iddo Java_pecyn_enw_ClassName_MethodName.

Yr ail yw ei gofrestru wrth lwytho'r llyfrgell (yn y swyddogaeth JNI_Ar Llwyth)
gan ddefnyddio galwad ffwythiant Cofrestru Brodorion.

Yn ein hachos ni, os ydym yn defnyddio'r dull cyntaf, dylai'r enw fod fel hyn: Java_com_taobao_wireless_security_adapter_JNICLibrary_doCommandNative.

Nid oes swyddogaeth o'r fath ymhlith y swyddogaethau a allforir, sy'n golygu bod angen i chi chwilio am alwad Cofrestru Brodorion.
Gadewch i ni fynd i'r swyddogaeth JNI_Ar Llwyth a gwelwn y llun hwn:

Chwilio am wendidau yn Porwr UC

Beth sy'n digwydd yma? Ar yr olwg gyntaf, mae dechrau a diwedd y swyddogaeth yn nodweddiadol ar gyfer pensaernïaeth ARM. Mae'r cyfarwyddyd cyntaf ar y pentwr yn storio cynnwys y cofrestrau y bydd y swyddogaeth yn eu defnyddio wrth ei gweithredu (yn yr achos hwn, R0, R1 a R2), yn ogystal â chynnwys y gofrestr LR, sy'n cynnwys y cyfeiriad dychwelyd o'r swyddogaeth . Mae'r cyfarwyddyd olaf yn adfer y cofrestrau sydd wedi'u cadw, ac mae'r cyfeiriad dychwelyd yn cael ei roi ar unwaith yn y gofrestr PC - gan ddychwelyd o'r swyddogaeth. Ond os edrychwch yn ofalus, fe sylwch fod y cyfarwyddyd olaf ond un yn newid y cyfeiriad dychwelyd sydd wedi'i storio ar y pentwr. Gadewch i ni gyfrifo sut beth fydd ar ôl
gweithredu cod. Mae cyfeiriad penodol 1xB0 yn cael ei lwytho i R130, mae 5 yn cael ei dynnu ohono, yna caiff ei drosglwyddo i R0 ac ychwanegir 0x10 ato. Mae'n troi allan 0xB13B. Felly, mae'r IDA yn meddwl mai dychweliad swyddogaeth arferol yw'r cyfarwyddyd olaf, ond mewn gwirionedd mae'n mynd i'r cyfeiriad cyfrifedig 0xB13B.

Mae'n werth cofio yma fod gan broseswyr ARM ddau fodd a dwy set o gyfarwyddiadau: ARM a Thumb. Mae'r darn lleiaf arwyddocaol o'r cyfeiriad yn dweud wrth y prosesydd pa set gyfarwyddiadau sy'n cael ei defnyddio. Hynny yw, y cyfeiriad yw 0xB13A mewn gwirionedd, ac mae un yn y darn lleiaf arwyddocaol yn nodi'r modd Bawd.

Mae “addasydd” tebyg wedi'i ychwanegu at ddechrau pob swyddogaeth yn y llyfrgell hon a
cod garbage. Ni arhoswn yn fanwl arnynt yn mhellach — yr ydym yn unig yn cofio
bod dechrau gwirioneddol bron pob swyddogaeth ychydig ymhellach i ffwrdd.

Gan nad yw'r cod yn neidio'n benodol i 0xB13A, nid oedd yr IDA ei hun yn cydnabod bod y cod wedi'i leoli yn y lleoliad hwn. Am yr un rheswm, nid yw'n cydnabod y rhan fwyaf o'r cod yn y llyfrgell fel cod, sy'n gwneud dadansoddi braidd yn anodd. Rydyn ni'n dweud wrth yr IDA mai dyma'r cod, a dyma beth sy'n digwydd:

Chwilio am wendidau yn Porwr UC

Mae'r tabl yn amlwg yn dechrau ar 0xB144. Beth sydd yn sub_494C?

Chwilio am wendidau yn Porwr UC

Wrth alw'r swyddogaeth hon yn y gofrestr LR, rydym yn cael cyfeiriad y tabl a grybwyllwyd yn flaenorol (0xB144). Yn R0 - mynegai yn y tabl hwn. Hynny yw, cymerir y gwerth o'r tabl, ei ychwanegu at LR a'r canlyniad yw
y cyfeiriad i fynd iddo. Gadewch i ni geisio ei gyfrifo: 0xB144 + [0xB144 + 8* 4] = 0xB144 + 0x120 = 0xB264. Rydyn ni'n mynd i'r cyfeiriad a dderbyniwyd ac yn gweld yn llythrennol ychydig o gyfarwyddiadau defnyddiol ac eto yn mynd i 0xB140:

Chwilio am wendidau yn Porwr UC

Nawr bydd trawsnewidiad wedi'i wrthbwyso gyda mynegai 0x20 o'r tabl.

A barnu yn ôl maint y tabl, bydd llawer o drawsnewidiadau o'r fath yn y cod. Mae'r cwestiwn yn codi a yw'n bosibl delio â hyn yn fwy awtomatig rywsut, heb gyfrifo cyfeiriadau â llaw. Ac mae sgriptiau a'r gallu i glytio cod yn IDA yn dod i'n cymorth ni:

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"

Rhowch y cyrchwr ar linell 0xB26A, rhedeg y sgript a gweld y trawsnewid i 0xB4B0:

Chwilio am wendidau yn Porwr UC

Unwaith eto, nid oedd yr IDA yn cydnabod y maes hwn fel cod. Rydyn ni'n ei helpu ac yn gweld dyluniad arall yno:

Chwilio am wendidau yn Porwr UC

Nid yw'n ymddangos bod y cyfarwyddiadau ar ôl BLX yn gwneud llawer o synnwyr, mae'n debycach i ryw fath o ddadleoli. Edrychwn ar sub_4964:

Chwilio am wendidau yn Porwr UC

Ac yn wir, dyma dword yn cael ei gymryd yn y cyfeiriad sy'n gorwedd yn LR, wedi'i ychwanegu at y cyfeiriad hwn, ac ar ôl hynny mae'r gwerth yn y cyfeiriad canlyniadol yn cael ei gymryd a'i roi ar y pentwr. Hefyd, mae 4 yn cael ei ychwanegu at LR fel bod yr un gwrthbwyso hwn yn cael ei hepgor ar ôl dychwelyd o'r swyddogaeth. Ar ôl hynny mae'r gorchymyn POP {R1} yn cymryd y gwerth canlyniadol o'r pentwr. Os edrychwch ar yr hyn sydd wedi'i leoli yn y cyfeiriad 0xB4BA + 0xEA = 0xB5A4, fe welwch rywbeth tebyg i dabl cyfeiriad:

Chwilio am wendidau yn Porwr UC

I glytio'r dyluniad hwn, bydd angen i chi gael dau baramedr o'r cod: y gwrthbwyso a'r rhif cofrestru rydych chi am roi'r canlyniad ynddo. Ar gyfer pob cofrestr bosibl, bydd yn rhaid i chi baratoi darn o god ymlaen llaw.

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"

Rydyn ni'n gosod y cyrchwr ar ddechrau'r strwythur rydyn ni am ei ddisodli - 0xB4B2 - ac yn rhedeg y sgript:

Chwilio am wendidau yn Porwr UC

Yn ogystal â'r strwythurau a grybwyllwyd eisoes, mae'r cod hefyd yn cynnwys y canlynol:

Chwilio am wendidau yn Porwr UC

Fel yn yr achos blaenorol, ar ôl y cyfarwyddyd BLX mae gwrthbwyso:

Chwilio am wendidau yn Porwr UC

Rydyn ni'n cymryd y gwrthbwyso i'r cyfeiriad o LR, yn ei ychwanegu at LR ac yn mynd yno. 0x72044 + 0xC = 0x72050. Mae'r sgript ar gyfer y dyluniad hwn yn eithaf syml:

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"

Canlyniad gweithredu'r sgript:

Chwilio am wendidau yn Porwr UC

Unwaith y bydd popeth wedi'i glytio yn y swyddogaeth, gallwch chi bwyntio IDA at ei ddechrau go iawn. Bydd yn rhoi'r holl god swyddogaeth at ei gilydd, a gellir ei ddadgrynhoi gan ddefnyddio HexRays.

Llinynnau dadgodio

Rydym wedi dysgu sut i ddelio â gorbwysedd cod peiriant yn y llyfrgell libsgmainso-6.4.36.so gan UC Browser a derbyniodd y cod swyddogaeth JNI_Ar Llwyth.

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

Gadewch i ni edrych yn agosach ar y llinellau canlynol:

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

Mewn swyddogaeth is_73E24 mae enw'r dosbarth yn amlwg yn cael ei ddadgryptio. Fel paramedrau i'r swyddogaeth hon, mae pwyntydd i ddata tebyg i ddata wedi'i amgryptio, byffer penodol a nifer yn cael eu pasio. Yn amlwg, ar ôl galw'r swyddogaeth, bydd llinell ddadgryptio yn y byffer, gan ei fod yn cael ei drosglwyddo i'r swyddogaeth FindClass, sy'n cymryd enw'r dosbarth fel yr ail baramedr. Felly, maint y byffer neu hyd y llinell yw'r rhif. Gadewch i ni geisio dehongli enw'r dosbarth, dylai ddweud wrthym a ydym yn mynd i'r cyfeiriad cywir. Gadewch i ni edrych yn agosach ar yr hyn sy'n digwydd yn is_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;
}

Swyddogaeth is_7AF78 yn creu enghraifft o gynhwysydd ar gyfer araeau beit o'r maint penodedig (ni fyddwn yn aros yn fanwl ar y cynwysyddion hyn). Yma mae dau gynhwysydd o'r fath yn cael eu creu: mae un yn cynnwys y llinell "DcO/lcK+h?m3c*q@" (mae'n hawdd dyfalu mai allwedd yw hon), mae'r llall yn cynnwys data wedi'i amgryptio. Nesaf, gosodir y ddau wrthrych mewn strwythur penodol, sy'n cael ei drosglwyddo i'r swyddogaeth is_6115C. Gadewch i ni hefyd farcio maes gyda'r gwerth 3 yn y strwythur hwn.Gadewch i ni weld beth sy'n digwydd i'r strwythur hwn nesaf.

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

Mae'r paramedr switsh yn faes strwythur a neilltuwyd yn flaenorol y gwerth 3. Edrychwch ar achos 3: i'r swyddogaeth is_6364C mae paramedrau'n cael eu trosglwyddo o'r strwythur a ychwanegwyd yno yn y swyddogaeth flaenorol, h.y. yr allwedd a'r data wedi'i amgryptio. Os edrychwch yn ofalus ar is_6364C, gallwch chi adnabod yr algorithm RC4 ynddo.

Mae gennym algorithm ac allwedd. Gadewch i ni geisio dehongli enw'r dosbarth. Dyma beth ddigwyddodd: com/taobao/diwifr/security/adapter/JNICLibrary. Gwych! Rydym ar y trywydd iawn.

Coeden gorchymyn

Nawr mae angen i ni ddod o hyd i her Cofrestru Brodorion, a fydd yn ein cyfeirio at y swyddogaeth doCommandNative. Gadewch i ni edrych ar y swyddogaethau a elwir o JNI_Ar Llwyth, a chawn ef yn is_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;
}

Ac yn wir, mae dull brodorol gyda'r enw wedi'i gofrestru yma doCommandNative. Nawr rydym yn gwybod ei gyfeiriad. Gawn ni weld beth mae'n ei wneud.

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

Wrth yr enw gallwch chi ddyfalu mai dyma bwynt mynediad yr holl swyddogaethau y penderfynodd y datblygwyr eu trosglwyddo i'r llyfrgell frodorol. Mae gennym ddiddordeb yn swyddogaeth rhif 10601.

Gallwch weld o'r cod bod y rhif gorchymyn yn cynhyrchu tri rhif: gorchymyn/10000, gorchymyn % 10000 / 100 и gorchymyn % 10, h.y., yn ein hachos ni, 1, 6 ac 1. Mae'r tri rhif hyn, yn ogystal â pwyntydd i JNIEnv ac mae'r dadleuon a drosglwyddir i'r swyddogaeth yn cael eu hychwanegu at strwythur a'u trosglwyddo. Gan ddefnyddio'r tri rhif a gafwyd (gadewch i ni eu dynodi yn N1, N2 ac N3), mae coeden orchymyn yn cael ei hadeiladu.

Rhywbeth fel hyn:

Chwilio am wendidau yn Porwr UC

Mae'r goeden wedi'i llenwi'n ddeinamig JNI_Ar Llwyth.
Mae tri rhif yn amgodio'r llwybr yn y goeden. Mae pob deilen o'r goeden yn cynnwys cyfeiriad pigog y ffwythiant cyfatebol. Mae'r allwedd yn y nod rhiant. Nid yw'n anodd dod o hyd i'r lle yn y cod lle mae'r swyddogaeth sydd ei hangen arnom yn cael ei hychwanegu at y goeden os ydych chi'n deall yr holl strwythurau a ddefnyddir (nid ydym yn eu disgrifio er mwyn peidio â chwyddo erthygl sydd eisoes yn eithaf mawr).

Mwy o obfuscation

Cawsom gyfeiriad y swyddogaeth a ddylai ddadgryptio traffig: 0x5F1AC. Ond mae'n rhy gynnar i lawenhau: mae datblygwyr Porwr UC wedi paratoi syrpreis arall i ni.

Ar ôl derbyn y paramedrau o'r arae a ffurfiwyd yn y cod Java, rydym yn cael
i'r swyddogaeth yn y cyfeiriad 0x4D070. Ac yma mae math arall o obfuscation cod yn ein disgwyl.

Rydyn ni'n rhoi dau fynegai yn R7 ac R4:

Chwilio am wendidau yn Porwr UC

Rydym yn symud y mynegai cyntaf i R11:

Chwilio am wendidau yn Porwr UC

I gael cyfeiriad o dabl, defnyddiwch fynegai:

Chwilio am wendidau yn Porwr UC

Ar ôl mynd i'r cyfeiriad cyntaf, defnyddir yr ail fynegai, sydd yn R4. Mae 230 o elfennau yn y tabl.

Beth i'w wneud amdano? Gallwch ddweud wrth IDA mai switsh yw hwn: Golygu -> Arall -> Nodwch idiom switsh.

Chwilio am wendidau yn Porwr UC

Mae'r cod canlyniadol yn frawychus. Ond, wrth wneud eich ffordd trwy ei jyngl, gallwch sylwi ar alwad i swyddogaeth sydd eisoes yn gyfarwydd i ni is_6115C:

Chwilio am wendidau yn Porwr UC

Roedd switsh lle rhag ofn 3 roedd dadgryptio gan ddefnyddio'r algorithm RC4. Ac yn yr achos hwn, mae'r strwythur a drosglwyddir i'r swyddogaeth yn cael ei lenwi o'r paramedrau a basiwyd i doCommandNative. Gadewch i ni gofio beth oedd gennym ni yno hudolInt gyda'r gwerth 16. Edrychwn ar yr achos cyfatebol - ac ar ôl sawl trawsnewidiad rydym yn dod o hyd i'r cod y gellir ei ddefnyddio i adnabod yr algorithm.

Chwilio am wendidau yn Porwr UC

Dyma AES!

Mae'r algorithm yn bodoli, y cyfan sy'n weddill yw cael ei baramedrau: modd, allwedd ac, o bosibl, y fector cychwyn (mae ei bresenoldeb yn dibynnu ar ddull gweithredu'r algorithm AES). Rhaid i'r strwythur gyda nhw gael ei ffurfio yn rhywle cyn yr alwad swyddogaeth is_6115C, ond mae'r rhan hon o'r cod wedi'i guddio'n arbennig o dda, felly mae'r syniad yn codi i glytio'r cod fel bod holl baramedrau'r swyddogaeth dadgryptio yn cael eu dympio i mewn i ffeil.

Patch

Er mwyn peidio ag ysgrifennu'r holl god patch yn iaith y cynulliad â llaw, gallwch chi lansio Android Studio, ysgrifennu swyddogaeth yno sy'n derbyn yr un paramedrau mewnbwn â'n swyddogaeth dadgryptio ac yn ysgrifennu at ffeil, yna copïwch-gludwch y cod y bydd y casglwr yn ei wneud. cynhyrchu.

Roedd ein ffrindiau o dîm Porwr UC hefyd yn gofalu am gyfleustra ychwanegu cod. Gadewch inni gofio bod gennym god sothach ar ddechrau pob swyddogaeth y gellir ei ddisodli'n hawdd ag unrhyw un arall. Cyfleus iawn 🙂 Fodd bynnag, ar ddechrau'r swyddogaeth darged nid oes digon o le ar gyfer y cod sy'n arbed yr holl baramedrau i ffeil. Roedd yn rhaid i mi ei rannu'n rhannau a defnyddio blociau sbwriel o swyddogaethau cyfagos. Roedd pedair rhan i gyd.

Rhan gyntaf:

Chwilio am wendidau yn Porwr UC

Yn y bensaernïaeth ARM, mae'r pedwar paramedrau swyddogaeth cyntaf yn cael eu pasio trwy gofrestrau R0-R3, mae'r gweddill, os o gwbl, yn cael eu pasio trwy'r pentwr. Mae'r cyfeiriad dychwelyd ar y gofrestr LR. Mae angen arbed hyn i gyd fel y gall y swyddogaeth weithio ar ôl i ni adael ei baramedrau. Mae angen i ni hefyd gadw'r holl gofrestrau y byddwn yn eu defnyddio yn y broses, felly rydym yn gwneud PUSH.W {R0-R10,LR}. Yn R7 rydym yn cael cyfeiriad y rhestr o baramedrau a drosglwyddir i'r swyddogaeth trwy'r pentwr.

Defnyddio'r swyddogaeth fopen gadewch i ni agor y ffeil /data/lleol/tmp/aes yn y modd "ab".
h.y. ar gyfer ychwanegiad. Yn R0 rydym yn llwytho cyfeiriad enw'r ffeil, yn R1 - cyfeiriad y llinell sy'n nodi'r modd. Ac yma mae'r cod sothach yn dod i ben, felly symudwn ymlaen i'r swyddogaeth nesaf. Er mwyn iddo barhau i weithio, rydyn ni'n rhoi'r newid i god go iawn y swyddogaeth ar y dechrau, gan osgoi'r sothach, ac yn lle'r sothach rydyn ni'n ychwanegu parhad o'r clwt.

Chwilio am wendidau yn Porwr UC

Yn galw fopen.

Tri pharamedr cyntaf y swyddogaeth AES wedi math int. Ers i ni gadw'r cofrestrau i'r pentwr ar y dechrau, gallwn basio'r swyddogaeth yn syml ysgrifennu eu cyfeiriadau ar y pentwr.

Chwilio am wendidau yn Porwr UC

Nesaf mae gennym dri strwythur sy'n cynnwys maint y data a phwyntydd i'r data ar gyfer yr allwedd, fector ymgychwyn a data wedi'i amgryptio.

Chwilio am wendidau yn Porwr UC

Ar y diwedd, caewch y ffeil, adfer y cofrestrau a throsglwyddo rheolaeth i'r swyddogaeth wirioneddol AES.

Rydym yn casglu APK gyda llyfrgell glytiog, ei lofnodi, ei uwchlwytho i'r ddyfais / efelychydd, a'i lansio. Gwelwn fod ein dymp yn cael ei greu, ac mae llawer o ddata yn cael ei ysgrifennu yno. Mae'r porwr yn defnyddio amgryptio nid yn unig ar gyfer traffig, ac mae'r holl amgryptio yn mynd trwy'r swyddogaeth dan sylw. Ond am ryw reswm nid yw'r data angenrheidiol yno, ac nid yw'r cais gofynnol yn weladwy yn y traffig. Er mwyn peidio ag aros nes bod Porwr UC yn dylunio i wneud y cais angenrheidiol, gadewch i ni gymryd yr ymateb wedi'i amgryptio gan y gweinydd a dderbyniwyd yn gynharach a chlytio'r cais eto: ychwanegu'r dadgryptio i onCreate o'r prif weithgaredd.

    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

Rydym yn ymgynnull, yn llofnodi, yn gosod, yn lansio. Rydym yn derbyn NullPointerException oherwydd bod y dull wedi dychwelyd null.

Wrth ddadansoddi'r cod ymhellach, darganfuwyd swyddogaeth sy'n dehongli llinellau diddorol: “META-INF/” a “.RSA”. Mae'n edrych fel bod y cais yn gwirio ei dystysgrif. Neu hyd yn oed yn cynhyrchu allweddi ohono. Dydw i ddim wir eisiau delio â'r hyn sy'n digwydd gyda'r dystysgrif, felly byddwn ni'n llithro'r dystysgrif gywir iddi. Gadewch i ni glytio’r llinell wedi’i hamgryptio fel ein bod ni’n cael “BLABLINF/” yn lle “META-INF/”, creu ffolder gyda’r enw hwnnw yn yr APK ac ychwanegu tystysgrif porwr gwiwerod yno.

Rydym yn ymgynnull, yn llofnodi, yn gosod, yn lansio. Bingo! Mae gennym yr allwedd!

MitM

Cawsom allwedd a fector ymgychwyn yn hafal i'r allwedd. Gadewch i ni geisio dadgryptio ymateb y gweinydd yn y modd CBS.

Chwilio am wendidau yn Porwr UC

Rydym yn gweld URL yr archif, rhywbeth tebyg i MD5, “extract_unzipsize” a rhif. Rydyn ni'n gwirio: mae MD5 yr archif yr un peth, mae maint y llyfrgell heb ei bacio yr un peth. Rydym yn ceisio clytio'r llyfrgell hon a'i rhoi i'r porwr. I ddangos bod ein llyfrgell glytiog wedi llwytho, byddwn yn lansio Bwriad i greu SMS gyda'r testun “PWNED!” Byddwn yn disodli dau ymateb gan y gweinydd: puds.ucweb.com/upgrade/index.xhtml ac i lawrlwytho'r archif. Yn y cyntaf rydym yn disodli MD5 (nid yw'r maint yn newid ar ôl dadbacio), yn yr ail rydym yn rhoi'r archif gyda'r llyfrgell glytiog.

Mae'r porwr yn ceisio lawrlwytho'r archif sawl gwaith, ac ar ôl hynny mae'n rhoi gwall. Mae'n debyg rhywbeth
nid yw'n hoffi. O ganlyniad i ddadansoddi'r fformat aneglur hwn, daeth yn amlwg bod y gweinydd hefyd yn trosglwyddo maint yr archif:

Chwilio am wendidau yn Porwr UC

Mae wedi'i amgodio yn LEB128. Ar ôl y clwt, newidiodd maint yr archif gyda'r llyfrgell ychydig, felly roedd y porwr o'r farn bod yr archif wedi'i lawrlwytho'n gam, ac ar ôl sawl ymgais fe daflodd gwall.

Rydym yn addasu maint yr archif... A – buddugoliaeth! 🙂 Mae'r canlyniad yn y fideo.

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

Canlyniadau ac ymateb y datblygwr

Yn yr un modd, gallai hacwyr ddefnyddio nodwedd ansicr Porwr UC i ddosbarthu a rhedeg llyfrgelloedd maleisus. Bydd y llyfrgelloedd hyn yn gweithio yng nghyd-destun y porwr, felly byddant yn derbyn ei holl ganiatadau system. O ganlyniad, mae'r gallu i arddangos ffenestri gwe-rwydo, yn ogystal â mynediad at y ffeiliau gwaith y wiwer oren Tsieineaidd, gan gynnwys mewngofnodi, cyfrineiriau a cwcis storio yn y gronfa ddata.

Fe wnaethom gysylltu â datblygwyr Porwr UC a'u hysbysu am y broblem a welsom, ceisio tynnu sylw at y bregusrwydd a'i berygl, ond ni wnaethant drafod unrhyw beth gyda ni. Yn y cyfamser, parhaodd y porwr i flaunt ei nodwedd beryglus yn amlwg. Ond ar ôl i ni ddatgelu manylion y bregusrwydd, nid oedd bellach yn bosibl ei anwybyddu fel o'r blaen. Mawrth 27 oedd
rhyddhawyd fersiwn newydd o UC Browser 12.10.9.1193, a gyrchodd y gweinydd trwy HTTPS: puds.ucweb.com/upgrade/index.xhtml.

Yn ogystal, ar ôl y “trwsio” a hyd at amser ysgrifennu'r erthygl hon, arweiniodd ceisio agor PDF mewn porwr at neges gwall gyda'r testun “Wps, aeth rhywbeth o'i le!” Ni wnaed cais i'r gweinydd wrth geisio agor PDF, ond gwnaed cais pan lansiwyd y porwr, sy'n awgrymu y gellid parhau i lawrlwytho cod gweithredadwy yn groes i reolau Google Play.

Ffynhonnell: hab.com

Ychwanegu sylw