从android锁屏病毒来探索smali语法
字数 2152 2025-08-22 22:47:30
Android锁屏病毒分析与Smali语法教学
引言
本文通过分析一个简单的Android锁屏病毒样本,深入讲解Smali语法和Android恶意软件分析技术。该病毒通过设备管理器实现锁屏功能,利用开机广播和服务实现开机锁屏,解锁方式是通过电话拨入并与解密后的资源文件字符串匹配。
样本基本信息
- 应用名称: 红包强盗(后台版)
- MD5: F3ADAADC7A8CB0D16A1AD05AADC8B1F2
- 包名: com.cjk
样本行为分析
1. 主要行为特征
- 申请设备管理员权限
- 弹出锁屏界面
- 通过开机广播实现持久化
- 通过电话拨入特定号码解锁
2. 可疑文件
res/raw目录下四个可疑文本文件(加密字符串)res/drawable目录下的QQ二维码
Smali语法详细解析
1. 方法定义
Smali方法定义格式:
.method [修饰符] 方法名(参数类型)返回值类型
.registers N // 使用的寄存器数量
.annotation // 注解
.end annotation
// 方法体
.end method
示例:
.method public onBind(Intent)IBinder
.registers 6
.annotation runtime Override
.end annotation
move-object v0, p0
move-object v1, p1
const/4 v3, 0
check-cast v3, IBinder
move-object v0, v3
return-object v0
.end method
2. 寄存器使用
pX表示参数寄存器(p0通常是this)vX表示局部变量寄存器.registers N声明使用的寄存器总数
3. 常见指令解析
数据移动指令
move-object vA, vB: 将vB中的对象引用移动到vAmove/from16 vAA, vBBBB: 从vBBBB移动到vAA(16位)const/4 vA, #+B: 将4位常量B存入vAconst-string vA, string: 将字符串常量存入vA
方法调用指令
invoke-virtual: 调用虚方法invoke-static: 调用静态方法invoke-direct: 调用直接方法(构造方法等)invoke-super: 调用父类方法
示例:
invoke-static MD5Util->getMD5String(String)String, v3
控制流指令
if-eq vA, vB, target: 如果vA == vB则跳转if-ne vA, vB, target: 如果vA != vB则跳转goto target: 无条件跳转
数组操作
new-array vA, vB, type: 创建新数组array-length vA, vB: 获取数组长度aget vA, vB, vC: 获取数组元素aput vA, vB, vC: 存储数组元素
4. 类与对象操作
new-instance vA, type: 创建新实例check-cast vA, type: 类型检查转换iput-object vA, vB, field: 设置实例字段iget-object vA, vB, field: 获取实例字段
病毒组件分析
1. 服务组件(s)
构造方法分析
构造方法中初始化了几个敏感数据:
const-string v3, "by:彼岸花 qq:127****738"
iput-object v3, v2, s->bahk:String
然后对字符串进行MD5处理:
iget-object v3, v3, s->bahk:String
invoke-static MD5Util->getMD5String(String)String, v3
move-result-object v3
iput-object v3, v2, s->Lycorisradiata:String
MD5处理函数分析
getMD5String方法实现了复杂的加密过程:
- 对输入字符串进行MD5摘要计算
- 创建长度为摘要字节数组两倍的char数组
- 对每个字节进行两次加密处理
加密逻辑伪代码:
char[] charArray1 = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
byte[] md5ByteArray = MessageDigest.getInstance("MD5").digest(string.getBytes());
char[] charArray2 = new char[2 * md5ByteArray.length];
for(int i=0; i<md5ByteArray.length; i++) {
byte b = md5ByteArray[i];
// 第一次加密
charArray2[j++] = charArray1[(b >> 4) & 0xF];
// 第二次加密
charArray2[j++] = charArray1[b & 0xF];
}
return new String(charArray2);
onCreate方法分析
- 注册监听短信和电话状态的广播
- 初始化DES加密对象
- 设置手机震动模式(每0.1秒震动1.5秒)
关键代码:
// 注册广播接收器
new-instance v8, s$IncomingCallReceiver
invoke-direct s$IncomingCallReceiver-><init>(s)V
iput-object v8, v7, s->mReceiver:s$IncomingCallReceiver
// 设置震动模式
const/4 v8, 4
new-array v8, v8, [J
const/4 v10, 0
const/16 v11, 100
int-to-long v11, v11
aput-wide v11, v9, v10 // longArray[0] = 100
const/4 v10, 1
const/16 v11, 1500
int-to-long v11, v11
aput-wide v11, v9, v10 // longArray[1] = 1500
// 以此类推设置震动模式
onStartCommand方法
主要调用私有方法c(),该方法:
- 创建悬浮窗口布局
- 设置窗口参数
- 解密资源文件内容并显示在TextView中
窗口参数设置:
const/16 v16, 201
iput v0, v15, WindowManager$LayoutParams->type:I // type = 201
const/16 v16, 1
iput v0, v15, WindowManager$LayoutParams->format:I // format = 1
const/16 v16, 0x050
iput v0, v15, WindowManager$LayoutParams->flags:I // flags = 0x050
const/16 v16, 49
iput v0, v15, WindowManager$LayoutParams->gravity:I // gravity = 49
2. 广播接收器(bbb)
处理开机完成广播,启动服务:
invoke-virtual Intent->getAction()String
const-string v8, "android.intent.action.BOOT_COMPLETED"
invoke-virtual String->equals(Object)Z // 检查是否为开机广播
new-instance v7, Intent
const-string v10, "com.cjk.s"
invoke-static Class->forName(String)Class // 获取服务类
invoke-direct Intent-><init>(Context, Class)V // 创建Intent
const/high16 v8, 0x10000000
invoke-virtual Intent->addFlags(I)Intent // 添加标志
invoke-virtual Context->startService(Intent)ComponentName // 启动服务
3. 设备管理员组件(MyAdmin)
onEnabled方法
- 解密资源文件获取密码
- 启动服务
- 重置锁屏密码
关键代码:
const v12, 0x7F060002
invoke-virtual Resources->openRawResource(I)InputStream // 打开资源文件
invoke-static BAH->getString(InputStream)String // 读取内容
const-string v12, "\n"
const-string v13, ""
invoke-virtual String->replaceAll(String, String)String // 去除换行
invoke-static DU->getsss(String)String // 解密得到密码
invoke-virtual MyAdmin->getManager(Context)DevicePolicyManager // 获取设备策略管理器
const/4 v13, 0
invoke-virtual DevicePolicyManager->resetPassword(String, I)Z // 重置密码
onPasswordChanged和onDisableRequested方法
这两个方法都会执行锁屏和重置密码操作,防止用户修改密码或取消设备管理员权限。
解密算法分析
1. 资源文件解密
病毒使用DU类的getsss方法解密资源文件:
invoke-static DU->getsss(String)String
2. 电话解锁机制
当有电话拨入时,广播接收器会:
- 获取来电号码
- 与解密后的资源文件字符串比较
- 如果匹配则:
- 结束前台呼叫
- 移除锁屏布局
- 设置铃声静音
- 停止服务
防御与检测建议
- 设备管理员权限:谨慎授予设备管理员权限,特别是来源不明的应用
- 开机启动:监控应用的BOOT_COMPLETED广播接收器
- 窗口类型:注意type=201的悬浮窗口
- 密码重置:警惕应用调用DevicePolicyManager.resetPassword
- 广播优先级:注意priority="2147483647"的高优先级广播
Smali分析技巧总结
- 从AndroidManifest.xml入手:优先分析注册的组件
- 关注服务组件:恶意行为常放在服务中
- 跟踪方法调用链:特别是onCreate、onStartCommand等生命周期方法
- 注意加密字符串:资源文件中的可疑字符串可能是关键
- 分析广播接收器:特别是高优先级和系统广播接收器
- 设备管理员相关:注意DeviceAdminReceiver的子类
参考资源
通过本案例的详细分析,我们不仅了解了Android锁屏病毒的工作原理,也掌握了Smali代码的分析方法和关键语法点,为后续的Android逆向分析和安全研究打下了坚实基础。