This past January I performed a deep analysis of an Android rootnik malware variant and posted them to this blog. Since then, I have continued to monitor this Android malware family. In early June, FortiGuard Labs found a new variant of the Android rootnik malware that disguises itself as a legal app. It then uses open-sourced Android root exploit tools to gain root access on an Android device.
To be clear, this malware was NOT found in Google Play. The developer of the malware app repackaged a legal app from Google Play and inserted the malicious codes into it. Both the legal and the repackaged versions of this app use the same package name “net.gotsun.android.wifi_configuration”. As a result, this disguise can trick even careful users. In addition, the malware uses advanced anti-debug techiques to prevent it from being reverse-engineered.
After successfully gaining root privileges on the Android device, the malware is able to perform serveral malicious operations, including app and ad promotion, silent app installation, pushing notifications, send SMS message etc. In this blog, I’ll provide a deep analysis of this malware variant.
A Quick Look at the Malware
The malware app is a repackaged version of an app named “Wi-Fi Auto-connect,” which is a wifi utility. The legal, uninfected version of this app is still available on Google Play.
Figure 1. The malware app icon
Figure 2. The legal app in Google Play
We compared the decompiled structures of the malware’s APK file with the legal app’s APK file, as shown in Figure 3.
Figure 3. The comparison of decompiled structure between the malware app and the legal app
We then compared the AndroidManifest.xml in both of them.
Figure 4. The AndroidManifest.xml of the legal app
Figure 5. The AndroidManifest.xml of the malware app
As shown in Figures 4 and 5, we can see that the package name of both of these is “net.gotsun.android.wifi_configuration”. We can also see that the AndroidManifest.xml file of the malware app includes a copy of the legal app’s AndroidManifest.xml. In addition, it also includes a large number of other Android components that support its malicious activities.
As can be seen in Figure 3, we can’t find the structure of the legal app’s APK inside the malware app. This means that this malware app was packed. So, we need to first unpack it to get the real DEX file of the malware app. Then we can analyze the real DEX file.
How to Unpack the Malware App
The following is the file structure inside the malware app’s APK file.
Figure 6. The file structure inside the malware app’s APK file
As shown in Figure 6, we can see that com.tencent.StubShell.TxAppEntry is the entry of the malware app. The following is the function attachBaseContext() in the class com.tencent.StubShell.TxAppEntry.
Figure 7. The function attachBaseContext() of the class com.tencent.StubShell.TxAppEntry
In this function, the malware loads the library libshella-184.108.40.206.so, then invokes the native method load that is used to decrypt the data hidden in classes.dex in order to get the real DEX and load it.
After executing the function attachBaseContext(), the function onCreate() can be executed.
Figure 8. The function onCreate() of the class com.tencent.StubShell.TxAppEntry
The native method runCreate() can be invoked to run the real app.
Next, we begin to analyze and debug libshella-220.127.116.11.so. The section header of the ELF file libshella-18.104.22.168.so is modified. This causes IDA Pro to not recognize and parse it correctly when we statically analyze it. Which means we need to debug it dynamically with IDA Pro.
In the segment .init_array of libshella_22.214.171.124.so, there are two function addresses.
The following are the segments of libshella_126.96.36.199.so before executing .init_array.
Figure 9. The segments of libshella_188.8.131.52.so before executing .init_array
The second function is sub_19C0(). The following is the key code snippet in the function sub_19C0().
Figure 10. The key code snippet in the function sub_19C0
The function sub_CED4() is used to do some anti-debugging work.
Figure 11. The function sub_ CED4()
We set a breakpoint at offset 0x221C in the segment of libshella_184.108.40.206.so. This allows the invoking of the anti-debugging function sub_CED4() at this address. In order to bypass anti-debugging, we only modify the memory of instruction to |00 00 A0 E1|(mov r0,r0).
Figure 12. The anti-debugging function
Figure 13. Bypass the anti-debugging code
At this point, let’s look at the segments of libshella_220.127.116.11.so. We find there are two more segments for libshella_18.104.22.168.so. The codes before invoking anti-debugging function sub_CED4() decrypt the data from 0x4000 to 0xCF68 in libshella_22.214.171.124.so, and remap the decrypted data into the memory segment with RX mode. Actually, the function JNI_Onload is just in that memory.
Figure 14. Two more segments for libshella_126.96.36.199.so
Next, we set a breakpoint at the function JNI_Onload(). Its offset is 0x99EC. It can invoke the function sub_4BDC() at offset 0x9F44. The Android log after executing the function sub_4BDC() is shown below.
Figure 15. The Android log after executing the function sub_4BDC()
Figure 16. Before invoking the function sub_4BDC()
Figure 17. After executing the function sub_4BDC()
As shown in Figure 17, we can see that a new code segment, debug102, is created after executing the function sub_4BDC().
Next, we continue to set a breakpoint at offset 0xa0a8 in libshella_188.8.131.52.so.
Figure 18. Trigger the breakpoint at offset 0xa0a8 in libshella_184.108.40.206.so
At the offset 0xA0C4, it can jump the function address offset 0x590C in segment debug102.
Next, we set a breakpoint at offset 0x58AC in segment debug102. At the offset 0x58AC, the program invokes the function registerNatives to dynamically register five native methods, which are: “load”, “runCreate”, “changeEnv”, ”reciver”, and “txEntries”. Respectively, their offsets are 0xcd51, 0x8f5d, 0x8b71, 0x52b1, 0x8a85.
Figure 19. Invoke the function registerNatives to register five native methods
Then, we focus on the native function load. It’s at offset 0xcd51 in segment debug102. This function is used to decrypt the data hidden in classes.dex in order to get the real DEX and load it. The following is the C code of the function load:
Figure 20. The native function load
In the function, it can invoke the function sub_C1DC() for Dalvik mode. The following is the key code snippet of the function sub_C1DC().
Figure 21. The key code snippet of the function sub_C1DC()
Following is the explanation for the key code snippet.
- Get the base address of the odex file data@email@example.com_configurationfirstname.lastname@example.org.
- Get offset of the real DEX via the fields data_size and data_off in the DEX header. data_size is 233944 and data_off is 38588. The offset is 0x43000 through calculating ((0x42894+0x1000) >>12) << 12.
- Copy the DEX header data with length 224 of the real DEX in a newly allocated memory. The start address of the real DEX is equal to the base address of classes.dex, plus 0x43000, and plus 0x28.
- Decrypt the DEX header data of the real DEX.
The following is the C code of the decryption function sub_ F3D8().
Figure 22. The decryption function of DEX header
So far, we have obtained the decryption algorithm of the DEX header. Meanwhile, I developed an offline unpacking tool to get the real DEX file. The following is the encrypted DEX header data in classes.dex (ODEX format).
Figure 23. The encrypted DEX header data in classes.dex
The decrypted DEX header of the real DEX is shown below.
Figure 24. The decrypted DEX header of the real DEX
The decompiled structure of the real DEX file is shown below.
Figure 25. The real DEX file of the malware app
At this point we have finished the analysis of the native layer and obtained the real DEX file of the malware app. In Part II of this blog we will analyze the DEX file.
The malware sample is detected by Fortinet Antivirus signature Android/Rootnik.AE!tr. The traffic communicating with remote C2 server can be detected by Fortinet IPS signature Android.Rootnik.Malware.C2 and web filter.