A native packer for Android/MoqHao
Update May 20, 2021. Added info on Pinterest URLs + Kudos to @bl4ckh0l3z + actually was discovered on May 12 not May 13.
On May 12, 2021 a new sample of Android/MoqHao (aka XLoader, Wroba) banking trojan was detected. There are several changes compared to 2019: new commands, communicating CnC URL through malicious Pinterest accounts etc. See below.
In this article, we will focus on the packer which is quite interesting because it uses a native library + the decryption algorithm has changed (see table above).
Decrypting the payload
The malware is packed. The unpacking process consists in processing correctly an encrypted file in an asset directory named
./whrlrsu. The asset is encrypted with an XOR key, and zipped. The XOR key is memorized in the encrypted file at the 12th byte.
I implemented a payload decryptor, available on GitHub.
Preparing dynamic class
Loading dynamic classes is typically done via the
DexClassLoader class, from the Android API. To conceal it loads a dynamic class, the malware does not directly call
DexClassLoader. Instead, it implements a native library (
libgdx.so) that calls
DexClassLoader from the native layer.
The native library implements the following low level tasks:
Object cr(Class class): calls
create()for the given class (
com.Loader). This actually instantiates a
Object lrd(int arg0, Object arg1, String classname, String arg3): call
loadClass()on the given class name and return the loaded class object.
String g(int arg0): returns a different string depending on the argument. Beware, JEB currently decompiles it incorrectly: you must read the assembly.
In our case, the malware uses the routine with argument 1, so
g() returns “com.Loader”. This is provided to
lrd(), so the malware will load a class named
com.Loader and contained in the dynamic DEX. Finally, it locates the method
There are some other native functions, but they are not used in the next stage. Note that up to know, the malware does not execute its payload, it only “prepares” things. This is all
OmApplication.onCreate() does. Execution is within the next stage.
Executing the payload
The next stage occurs when the main activity is launched. Actually, strangely, the manifest references 2 main activities:
adlbect.BnActivity, but actually
adlbect.kvActivity does nothing more than calling
BnActivity starts the
WqService — we’ll discuss it later — and calls native function
a.ed(). The method decompiles in JEB quite nicely, and we quickly recognize code to hide an application icon.
As for the WqService, it launches
com.Loader — this is how the banking trojan payload actually starts — and sets an alarm in 30 seconds.
The implementation hardens the reversing because it does not call methods directly but delegates the work to 2 native functions:
a.snc() to set the alarm.