{
	"id": "11c7e304-5d0a-4ad6-86e3-580b8e391f00",
	"created_at": "2026-04-06T00:07:01.337136Z",
	"updated_at": "2026-04-10T03:30:33.573245Z",
	"deleted_at": null,
	"sha1_hash": "cf490c555123ea439117ec0cd6b8b6fe90553d9d",
	"title": "N Ways to Unpack Mobile Malware – Pentest Blog",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1366311,
	"plain_text": "N Ways to Unpack Mobile Malware – Pentest Blog\r\nPublished: 2019-03-13 · Archived: 2026-04-05 18:29:32 UTC\r\nThis article will briefly explain methods behind the mobile malware unpacking. It will be focusing on Anubis\r\nsince it is the latest trending malware for almost a year now. Actors use dropper applications as their primary\r\nmethod of distribution. Droppers find their ways to Google Play store under generic names thereby infecting\r\ndevices with Anubis. An example of a such dropper may found in the references. There were at least forty cases in\r\nGoogle Play in the last fall targeting Turkish users. @LukasStefanko’s twitter thread may be helpful to get an\r\noverview of such campaigns. Anubis malware already analysed by fellows from the industry in a detailed manner.\r\nTherefore readers should find it more valuable to have an article focusing on packer mechanisms of Anubis.\r\nThe sample used in this article is available at the references section. I strongly recommend downloading the\r\nsample and following through the article. I will be dividing this post into three sections.\r\nPackers in Android Ecosystem\r\nCatching Packers with Frida\r\nHow To Defeat Packers\r\nDynamically\r\nStatically\r\nPackers in Android Ecosystem\r\nMobile malwares also make use of packers to hide their malicious payloads from researchers and AV programs.\r\nThis includes reflection, obfuscation, code-flow flattening and trash codes to make unpacking process stealthy. All\r\nmechanisms mentioned are used by the Anubis packer and therefore will be explored in this article.\r\nLoading classes at runtime\r\nAndroid applications must define their used services, receivers, activity classes in AndroidManifest file to use\r\nthem. In Anubis samples, it is clear that there are many classes not defined in the Manifest file that are simply\r\npresent in the source code.\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 1 of 19\n\nThis means that a file with non-defined classes should be loaded into application at run-time. There are two main\r\nways of run-time loading in Android:\r\nFrom file:\r\ndalvik.system.DexFile.loadDex depreciated after API 26\r\ndalvik.system.DexClassLoader\r\ndalvik.system.PathClassLoader\r\nFrom memory:\r\ndalvik.system.InMemoryDexClassLoader (not common in malwares)\r\nLoading from the file requires a dex/jar file to be present in file system. Anubis unpacks the encrypted data file\r\nand then drops the decrypted version. Later on malware proceeds loading decrypted dex into the application. After\r\nloading with DexClassLoader, malware removes the decrypted dex file. Tracing the dexClassLoader should make\r\nthe loading routine clear. Since dexClassLoader is a class of dalvik.system package\r\n“dalvik.system.dexClassLoader” should be in the code but it is nowhere to be found.\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 2 of 19\n\nReflection\r\nAnother useful method when dealing with malware is reflection. Reflection is an important concept in Java which\r\nlets you to call methods/classes without knowing about them in compile time. There are several classes/methods\r\nfor reflection.\r\njava.lang.Class.forName\r\njava.lang.ClassLoader.loadClass\r\njava.lang.reflect.Method\r\njava.lang.Class.getMethods\r\nExample usage of forName\r\ncObj = Class.forName(\"dalvik.system.dexClassLoader\");\r\ncObj = Class.forName(\"dalvik.system.dexClassLoader\");\r\ncObj = Class.forName(\"dalvik.system.dexClassLoader\");\r\ncObj variable holds the class object of dexClassLoader. This enables program to call methods of any given class.\r\nThe problem is to find where function calls are made to reflection methods.\r\nCatching packers with Frida\r\nfrida is a dynamic instrumentation toolkit supported by nearly every operating system. Frida makes it possible to\r\ninject a piece of code to manipulate target program and also to trace program calls. In this case it will be used for\r\ntracing which reflection calls are made thereby analysing the threads. When previously mentioned function calls\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 3 of 19\n\nare made, console.log will be called additionally. But before that, let’s take a quick recap on how to setup Frida on\r\nandroid emulator.\r\nDownload frida-server suitable with your emulator from:\r\n(e.g Genymotion uses x86 architecture.)\r\nhttps://github.com/frida/frida/releases.\r\nadb push frida-server /data/local/tmp\r\nadb push frida-server /data/local/tmp adb shell cd /data/local/tmp chmod +x frida-server ./frida-server \u0026\r\nadb push frida-server /data/local/tmp\r\nadb shell\r\ncd /data/local/tmp\r\nchmod +x frida-server\r\n./frida-server \u0026\r\nFrida tools should be installed in host machine by running\r\npip install frida-tools\r\nAfter the setup, we can write a script to hook our target methods. We will start by defining variables for classes of\r\nour methods.\r\nvar classDef = Java.use('java.lang.Class');\r\nvar classLoaderDef = Java.use('java.lang.ClassLoader');\r\nvar loadClass = classLoaderDef.loadClass.overload('java.lang.String', 'boolean');\r\nvar forName = classDef.forName.overload('java.lang.String', 'boolean', 'java.lang.ClassLoader');\r\nvar reflect = Java.use('java.lang.reflect.Method')\r\nvar member = Java.use('java.lang.reflect.Member')\r\nvar dalvik = Java.use(\"dalvik.system.DexFile\")\r\nvar dalvik2 = Java.use(\"dalvik.system.DexClassLoader\")\r\nvar dalvik3 = Java.use(\"dalvik.system.PathClassLoader\")\r\n//var dalvik4 = Java.use(\"dalvik.system.InMemoryDexClassLoader\")\r\nvar f = Java.use(\"java.io.File\")\r\nvar url = Java.use(\"java.net.URL\")\r\nvar obj = Java.use(\"java.lang.Object\")\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 4 of 19\n\nvar fo = Java.use(\"java.io.FileOutputStream\")\r\nvar classDef = Java.use('java.lang.Class'); var classLoaderDef = Java.use('java.lang.ClassLoader'); var loadClass =\r\nclassLoaderDef.loadClass.overload('java.lang.String', 'boolean'); var forName =\r\nclassDef.forName.overload('java.lang.String', 'boolean', 'java.lang.ClassLoader'); var reflect =\r\nJava.use('java.lang.reflect.Method') var member = Java.use('java.lang.reflect.Member') var dalvik =\r\nJava.use(\"dalvik.system.DexFile\") var dalvik2 = Java.use(\"dalvik.system.DexClassLoader\") var dalvik3 =\r\nJava.use(\"dalvik.system.PathClassLoader\") //var dalvik4 = Java.use(\"dalvik.system.InMemoryDexClassLoader\")\r\nvar f = Java.use(\"java.io.File\") var url = Java.use(\"java.net.URL\") var obj = Java.use(\"java.lang.Object\") var fo =\r\nJava.use(\"java.io.FileOutputStream\")\r\nvar classDef = Java.use('java.lang.Class');\r\nvar classLoaderDef = Java.use('java.lang.ClassLoader');\r\nvar loadClass = classLoaderDef.loadClass.overload('java.lang.String', 'boolean');\r\nvar forName = classDef.forName.overload('java.lang.String', 'boolean', 'java.lang.ClassLoader');\r\nvar reflect = Java.use('java.lang.reflect.Method')\r\nvar member = Java.use('java.lang.reflect.Member')\r\nvar dalvik = Java.use(\"dalvik.system.DexFile\")\r\nvar dalvik2 = Java.use(\"dalvik.system.DexClassLoader\")\r\nvar dalvik3 = Java.use(\"dalvik.system.PathClassLoader\")\r\n//var dalvik4 = Java.use(\"dalvik.system.InMemoryDexClassLoader\")\r\nvar f = Java.use(\"java.io.File\")\r\nvar url = Java.use(\"java.net.URL\")\r\nvar obj = Java.use(\"java.lang.Object\")\r\nvar fo = Java.use(\"java.io.FileOutputStream\")\r\nWe will be using this code snippet to change implementation of a method.\r\nclass.targetmethod.implementation = function(){\r\nconsole.log(\"[+] targetmethod catched !\")\r\nreturn this.targetmethod()\r\nclass.targetmethod.implementation = function(){ console.log(\"[+] targetmethod catched !\") stackTrace() return\r\nthis.targetmethod() }\r\nclass.targetmethod.implementation = function(){\r\n console.log(\"[+] targetmethod catched !\")\r\n stackTrace()\r\n return this.targetmethod()\r\n}\r\nconsole.log(\"[+] {x} function catched !\") will enable us to see if the function is called. If function takes any\r\nparameters such as a string, logging those may become helpful during the analysis. Then we can get more\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 5 of 19\n\ninformation about the thread we are in. Frida is able to call any android function including getStackTrace() . But\r\nthat requires a reference to the current thread object. Let’s start by getting instance of the thread class:\r\nvar ThreadDef = Java.use('java.lang.Thread');\r\nvar ThreadObj = ThreadDef.$new();\r\nvar ThreadDef = Java.use('java.lang.Thread'); var ThreadObj = ThreadDef.$new();\r\nvar ThreadDef = Java.use('java.lang.Thread');\r\nvar ThreadObj = ThreadDef.$new();\r\nThreadObj holds instance of the Thread class and currentThread() can be used to get thread according to\r\nhttps://developer.android.com/reference/java/lang/Thread.html.\r\nWe can now use getStackTrace() and also loop through stackElements to print the call stack.\r\nconsole.log(\"------------START STACK---------------\")\r\nvar stack = ThreadObj.currentThread().getStackTrace();\r\nfor (var i = 0; i \u003c stack.length; i++) {\r\nconsole.log(i + \" =\u003e \" + stack[i].toString());\r\nconsole.log(\"------------END STACK---------------\");\r\nfunction stackTrace() { console.log(\"------------START STACK---------------\") var stack =\r\nThreadObj.currentThread().getStackTrace(); for (var i = 0; i \u003c stack.length; i++) { console.log(i + \" =\u003e \" +\r\nstack[i].toString()); } console.log(\"------------END STACK---------------\"); }\r\n function stackTrace() {\r\n console.log(\"------------START STACK---------------\")\r\n var stack = ThreadObj.currentThread().getStackTrace();\r\n for (var i = 0; i \u003c stack.length; i++) {\r\n console.log(i + \" =\u003e \" + stack[i].toString());\r\n }\r\n console.log(\"------------END STACK---------------\");\r\n }\r\nPrinting call stack helps to identify call graph of reflections and unpacking mechanisms. For example\r\ndexClassLoader might have created with reflection. But when frida hooks into dexClassLoader and prints the call\r\nstack, we can see the functions before dexClassLoader is called. Unpacking routines are called at the very\r\nbeginning of the application. Therefore frida should be attached as soon as possible to catch the unpacking\r\nprocess. Fortunately -f option in frida enables frida to spawn target app itself. frida accepts scripts with the -l\r\nparameter.\r\nfrida -U -f appname -l dereflect.js\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 6 of 19\n\nThen frida waits input from the user to continue. %resume will resume the process. Full script is available at my\r\ngithub repository.\r\nhttps://github.com/eybisi/nwaystounpackmobilemalware/blob/master/dereflect.js\r\nOutput without the stackTrace():\r\nWith stackTrace()\r\nVoila.\r\nYou can see the functions called before the write method. After tracing these interval functions, you can\r\nsee RNlkfTEUX and lqfRafMrGew are called right before them. And turns out they are very important functions\r\nused in decryption of the encrypted file which we will come back later on.\r\nHow to Defeat Packers\r\nWe can divide unpacking methods into two sections. Both ways lead to the decrypted file.\r\nDynamically\r\nBy hooking:\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 7 of 19\n\nIntercept file.delete (Java level)\r\nIntercept unlink syscall (system level)\r\nFrom memory:\r\nDump the memory with gameguardian\r\nDump the memory with custom tools\r\nStatically:\r\nHands on manual unpacking\r\nDynamically:\r\nIntercepting methods is the easiest way.\r\nBy hooking : Java Level\r\nWhen I first encountered Anubis and realized it was dropping a file, my first solution was hooking into file.delete\r\nfunction.\r\nJava.perform(function() {\r\nvar f = Java.use(\"java.io.File\")\r\nf.delete.implementation = function(a){\r\ns = this.getAbsolutePath()\r\nconsole.log(\"[+] Delete catched =\u003e\" +this.getAbsolutePath())\r\nJava.perform(function() { var f = Java.use(\"java.io.File\") f.delete.implementation = function(a){ s =\r\nthis.getAbsolutePath() if(s.includes(\"jar\")){ console.log(\"[+] Delete catched =\u003e\" +this.getAbsolutePath()) } return\r\ntrue } })\r\nJava.perform(function() {\r\n var f = Java.use(\"java.io.File\")\r\n f.delete.implementation = function(a){\r\n s = this.getAbsolutePath()\r\n if(s.includes(\"jar\")){\r\n console.log(\"[+] Delete catched =\u003e\" +this.getAbsolutePath())\r\n }\r\n return true\r\n }\r\n})\r\nThis piece of code always returns true to file.delete function. After intercepting we can pull the dropped jar file. ✔\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 8 of 19\n\nIn addition to this we can automate our job with python bindings of frida and go through the folder that our target\r\nfiles are in. There are generally thousands of apks generated from those c\u0026c servers. Since each of them can have\r\ndifferent IP addresses embedded, an automated tool could make our life easier.\r\nBy Hooking : System Level\r\nBut what if malware uses native code to delete files? We can not always hook at Java level. We need to get deeper.\r\nWhat syscall is responsible for deleting file from file system and in libc?\r\nUnlink function takes one parameter, a pointer to filename. We can hook unlink with the help of\r\nfindExportByName. Code is taken from https://www.fortinet.com/blog/threat-research/defeating-an-android-packer-with-frida.html but I tweaked little bit so deleted file will be printed.\r\nvar unlinkPtr = Module.findExportByName(null, 'unlink');\r\nInterceptor.replace(unlinkPtr, new NativeCallback( function (a){\r\nconsole.log(\"[+] Unlink : \" + Memory.readUtf8String(ptr(a)))\r\nvar unlinkPtr = Module.findExportByName(null, 'unlink'); Interceptor.replace(unlinkPtr, new NativeCallback(\r\nfunction (a){ console.log(\"[+] Unlink : \" + Memory.readUtf8String(ptr(a))) }, 'int', ['pointer']));\r\nvar unlinkPtr = Module.findExportByName(null, 'unlink');\r\nInterceptor.replace(unlinkPtr, new NativeCallback( function (a){\r\n console.log(\"[+] Unlink : \" + Memory.readUtf8String(ptr(a)))\r\n }, 'int', ['pointer']));\r\nLet’s run the script.\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 9 of 19\n\nWe intercepted the unlink call, since our script just replaced code of original function with console.log() , file will\r\nnot be deleted from the file system. ✔\r\nFrom Memory:\r\nEven when file is deleted from file system because file was loaded into process, we can get trails of the deleted\r\nfile from memory of that process. Since Android inherits from Linux, we can use /proc/pid folder to give us\r\ninformation about memory regions of a specified process. Let’s look at our target with cat /proc/pid/maps |\r\ngrep dex filtering the dex.\r\nWe have found the trails of dex files. Now we need to dump these sections.\r\nDump the Memory with Gameguardian:\r\nFirst way is by “cheating” 🙂 There is a tool called GameGuardian which is used in game hacking. You can do\r\nmany interesting things with GameGuardian but we will only use dump mechanism for now.\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 10 of 19\n\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 11 of 19\n\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 12 of 19\n\nLet’s start by installing and running the APK. Then launch GameGuardian and select the app name from left\r\nupmost button. Select right upmost button and the one underneath it. Now you can see dump memory option in\r\nmenu. Put the hex codes of regions or select regions by clicking arrow buttons and press save. Yay!\r\nWe can pull dumped regions with :\r\nadb pull /storage/emulated/0/packer . ✔\r\nThen you will see 2 files in packer folder.\r\ncom.eqrxhpdv.cbunlkwsqtz-dfb5a000-e0080000.bin com.eqrxhpdv.cbunlkwsqtz-maps.txt\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 13 of 19\n\nWhen examined with file command it detects our dex file as a data file.\r\nWe need to fix it by removing parts do not belong to our file.\r\nDump the Memory with Custom Tools:\r\nThanks to @theempire_h we can dump regions of memory of the target app with a C program.\r\nhttps://github.com/CyberSaxosTiGER/androidDump\r\nHere is how to dump a region with androidDump.\r\nadb push androidDump /data/local/tmp\r\nadb push androidDump /data/local/tmp adb shell cd /data/local/tmp chmod +x androidDump ./androidDump\r\nappname\r\nadb push androidDump /data/local/tmp\r\nadb shell\r\ncd /data/local/tmp\r\nchmod +x androidDump\r\n./androidDump appname\r\nIt dumps 3 blobs of data. ✔\r\nBut after dumping it, file command still do not give us the correct type 🙁 It turns out that we should modify\r\nthe file a little bit. To find magic byte of dex I wrote this script.\r\nwith open(filename, 'rb') as f:\r\nh = binascii.hexlify(content).split(b'6465780a')\r\nh = b'6465780a' + b''.join(h)\r\ndex = open(sys.argv[1][:-4]+\".dex\",\"wb\")\r\ndex.write(binascii.a2b_hex(h))\r\nimport binascii import sys filename = sys.argv[1] with open(filename, 'rb') as f: content = f.read() h =\r\nbinascii.hexlify(content).split(b'6465780a') h.pop(0) h = b'6465780a' + b''.join(h) dex = open(sys.argv[1]\r\n[:-4]+\".dex\",\"wb\") dex.write(binascii.a2b_hex(h)) dex.close()\r\nimport binascii\r\nimport sys\r\nfilename = sys.argv[1]\r\nwith open(filename, 'rb') as f:\r\n content = f.read()\r\nh = binascii.hexlify(content).split(b'6465780a')\r\nh.pop(0)\r\nh = b'6465780a' + b''.join(h)\r\ndex = open(sys.argv[1][:-4]+\".dex\",\"wb\")\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 14 of 19\n\ndex.write(binascii.a2b_hex(h))\r\ndex.close()\r\nhttps://github.com/eybisi/nwaystounpackmobilemalware/blob/master/deDex.py\r\nAfter running our script on the file, we open it.\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 15 of 19\n\nWe found our lost classes 🙂\r\nStatically:\r\nHere is a blog post explaining unpacking process from a different perspective.\r\nhttps://sysopfb.github.io/malware,/reverse-engineering/2018/08/30/Unpacking-Anubis-APK.html\r\nI found rc4 key with the help of stackTrace. But apparently searching for ^ value is a very efficient way to find\r\nRC4 routines for Anubis 🙂\r\nTo find rc4 key easily in JADX, here is quick tip:\r\nsearch “% length”\r\nright click to method you are in, press find Usage\r\nbArr2 will be used as rc4 key to decrypt.\r\nHere is our sample’s decryption key as bArr2 . Does it look familiar?\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 16 of 19\n\nAfter decrypting and unzipping, we get our dex.\r\nAfter extracting the config, there is one more step to get the address of c\u0026c server. Malware gets page source of\r\nthe telegram address and changes Chinese characters with ASCII letters. It then processes the base64 string. After\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 17 of 19\n\ndecoding base64, it uses service to decrypt data that encrypted with rc4 scheme. Here is a snippet for\r\ndecrypting Chinese chars to c\u0026c addresses.\r\nhttps://github.com/eybisi/nwaystounpackmobilemalware/blob/master/solve_chinese.py\r\nI managed to decrypt the Anubis payload with Androguard without running the APK in an emulator! After\r\ndumping the dex file, my script will find the config class printing the c2 and the encryption key. Config class is in\r\none of the a,b or c or in ooooooooooooo{0,2}o classes in newer versions.\r\nBy checking counts of “this” keywords in class source code I managed to decrypt all versions of anubis (lazy :P).\r\nHere is output of my script to get c2 and key from an Anubis sample.\r\nhttps://github.com/eybisi/nwaystounpackmobilemalware/blob/master/getc2_imp.py\r\nConclusion\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 18 of 19\n\nThere are many ways to unpack mobile malware and trace packing mechanisms. We might see\r\ndalvik.system.InMemoryDexClassLoader used in the future. If this is used, delete hooks will not be able to catch\r\ndropped files because everything will be done in memory 🙂 But dumping memory will catch these methods.\r\nKnowing different ways always helps. If you have any question feel free to ask in comment section or through\r\n@0xabc0\r\nCheers.\r\nSpecial thanks to @godelx0\r\nLinks \u0026 References\r\nDropper sample:\r\n3c35f97b9000d55a2854c86eb201bd467702100a314486ff1dbee9774223bf0e\r\nAnubis sample:\r\ne01ed0befbc50eeedcde5b5c07bf8a51ab39c5b20ee6e1f5afe04e161d072f1d\r\nhttps://codeshare.frida.re/@razaina/get-a-stack-trace-in-your-hook/\r\nhttps://www.fortinet.com/blog/threat-research/defeating-an-android-packer-with-frida.html\r\nhttps://medium.com/@fs0c131y/reverse-engineering-of-the-anubis-malware-part-1-741e12f5a6bd3\r\nAll materials:\r\nhttps://github.com/eybisi/nwaystounpackmobilemalware\r\nSource: https://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nhttps://pentest.blog/n-ways-to-unpack-mobile-malware/\r\nPage 19 of 19",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"ETDA",
		"Malpedia"
	],
	"references": [
		"https://pentest.blog/n-ways-to-unpack-mobile-malware/"
	],
	"report_names": [
		"n-ways-to-unpack-mobile-malware"
	],
	"threat_actors": [
		{
			"id": "75108fc1-7f6a-450e-b024-10284f3f62bb",
			"created_at": "2024-11-01T02:00:52.756877Z",
			"updated_at": "2026-04-10T02:00:05.273746Z",
			"deleted_at": null,
			"main_name": "Play",
			"aliases": null,
			"source_name": "MITRE:Play",
			"tools": [
				"Nltest",
				"AdFind",
				"PsExec",
				"Wevtutil",
				"Cobalt Strike",
				"Playcrypt",
				"Mimikatz"
			],
			"source_id": "MITRE",
			"reports": null
		}
	],
	"ts_created_at": 1775434021,
	"ts_updated_at": 1775791833,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/cf490c555123ea439117ec0cd6b8b6fe90553d9d.pdf",
		"text": "https://archive.orkl.eu/cf490c555123ea439117ec0cd6b8b6fe90553d9d.txt",
		"img": "https://archive.orkl.eu/cf490c555123ea439117ec0cd6b8b6fe90553d9d.jpg"
	}
}