手机杀毒原理
特征码匹配
在手机系统中,每个应用都有它特有的特征信息,它是该应用所特有的,每个应用都是不同的,这就是手机的特征码。
手机应用的特征码包括:包名和应用签名。
Android中,同一个包名的程序,只允许安装一个。所以每个应用的包名都是不同的。每个程序的签名文件都不相同,因为每个开发者的签名文件都是特有的。
基于特征码的杀毒方式,是在已知病毒应用特征码的情况下,通过检测手机安装应用的特征码跟病毒数据库中的特征码是否相同,如果找到相同的数据库,则说明该应用为病毒应用。所以病毒数据库是关键。各杀毒软件的原理都是相同的,区别主要在于杀毒引擎。杀毒引擎用于获取手机应用的特征码,并依据此特征码码查询病毒数据库的算法。
【基于签名的特征码的扫描(hash码-md5特征码的扫描)】
- 特征:只能查杀已知的病毒,不能查杀未知的病毒。
- 原理:病毒库中保存病毒对应的hash,通过比较程序签名和病毒库中的签名判断是否病毒。
- 杀毒引擎用于获取硬件上文件的hash码,并依据此hash码查询数据库的算法.
【特征库的建立】
首先,我们需要部署大量的“蜜罐”服务器,也就是没有安装杀毒软件、防火墙等的赤裸裸的手机或服务器,让病毒很轻易的入侵。
然后,由病毒工程师来对它们进行分析,提取特有的特征码,加入到特征码服务器里面,以供手机安全卫士调用。当有新的病毒的时候,它们会被及时的下载到手机安全卫士里面,以供比对查杀。
当进行云查杀的时候,手机安全卫士提取本地所有应用的特征码,发送到云服务器进行比对,然后将比对结果返回给手机安全卫士,告诉手机用户查杀结果,比如:哪些软件是病毒,哪些游戏会窃取话费,哪些壁纸具有高危风险等。
高危API调用
如果在未知病毒特征码的情况下进行杀毒,那就是通过监控高危的API权限的调用,这是一种主动防御手段。
例如监控以下敏感操作:
- 更改浏览器主页
- 注册开机启动的行为
- 应用程序的内存注入
实现代码
下面代码基于病毒的特征值,在安全防护软件的本地资源路径下建立并保存病毒特征值数据库,然后在杀毒程序中将本机应用包的签名值与病毒特征值进行对比,如果匹配则进行病毒处理。
其中,病毒数据库的具体内容使用adb工具查看如下:
首先建立获取特征值列表的数据库底层操作类:
/**
* FileName: AntiVirusDao <br>
* Description: 从本地数据库读取病毒特征值的数据库操作类 <br>
* Author: Tr0e <br>
* Date: 2019/4/30 16:22
*/
public class AntiVirusDao {
public static final String tag = "AntiVirusDao";
// 病毒库拷贝到本地后的路径地址
public static String virusPathName = "data/data/xxxx.edu.cn.mobilesafer/files/antivirus.db";
/**
* 从本地病毒数据库读取特征值列表
* @return MD5特征值列表
*/
public static List<String> getVirusList() {
SQLiteDatabase db = SQLiteDatabase.openDatabase(virusPathName, null, SQLiteDatabase.OPEN_READONLY);
Cursor cursor = db.query("datable", new String[]{"md5"}, null, null, null, null, null);
List<String> virusList = new ArrayList<String>();
while (cursor.moveToNext()) {
virusList.add(cursor.getString(0));
}
cursor.close();
db.close();
return virusList;
}
}
逻辑代码
具体代码如下:
/**
* FileName: AntiVirusActivity <br>
* Description: 手机杀毒模块进行手机病毒查杀的活动 <br>
* Author: Tr0e <br>
* Date: 2019/4/30 15:49
*/
public class AntiVirusActivity extends AppCompatActivity {
// 正在扫描病毒
private static final int SCANNING = 0;
// 扫描完成
private static final int SCANNING_END = 1;
//扫毒过程动态转圈图形
private ImageView mActScanningImage;
// 动态添加每个软件的扫描结果
private LinearLayout mScanningAddLayout;
//当前正在扫描的软件的名称
private TextView mScanningNameText;
private ProgressBar mScanningProgressBar;
//用于记录扫描过程中发现的病毒列表
private List<VirusScanningInfo> mVirusScanningList;
private int count;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anti_virus);
// 初始化布局文件中view
initView();
// 初始化扫描动画
initScanningAnimation();
// 检测手机病毒
checkVirus();
}
// 异步处理机制。处理扫毒子线程传递过来的反馈信息并进行UI更新(Aandroid子线程中不能有UI更新操作)
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// 正在扫描中,处理扫毒子线程实时传过来的每个应用的染毒信息并显示
case 0:
VirusScanningInfo info = (VirusScanningInfo) msg.obj;
TextView textView = new TextView(getApplicationContext());
mScanningNameText.setText(info.name);
if (info.isVirus) {
textView.setText("发现病毒:" + info.name);
textView.setTextColor(Color.RED);
} else {
textView.setText("扫描安全:" + info.name);
textView.setTextColor(Color.GREEN);
}
//将每个应用的扫毒结果实时动态地显示在UI界面中
mScanningAddLayout.addView(textView, 0);
break;
// 扫描已结束,进行病毒处理或无毒提示
case 1:
//停止扫毒动画的播放
mActScanningImage.clearAnimation();
mScanningNameText.setText("扫描完成");
// 清除病毒
unStallVirus(mVirusScanningList);
break;
}
super.handleMessage(msg);
}
};
/**
* 检测手机病毒
*/
private void checkVirus() {
new Thread(new Runnable() {
@Override
public void run() {
count = 0;
// 获取病毒相关信息集合
List<String> virusList = AntiVirusDao.getVirusList();
// 将扫描到的病毒信息添加到病毒集合
mVirusScanningList = new ArrayList<VirusScanningInfo>();
// 扫描到的所有应用的信息集合
List<VirusScanningInfo> scanningList = new ArrayList<VirusScanningInfo>();
// 获取包管理者对象
PackageManager packageManager = getPackageManager();
List<PackageInfo> packageInfoList = packageManager.getInstalledPackages(PackageManager.GET_SIGNATURES
+ PackageManager.GET_UNINSTALLED_PACKAGES);
mScanningProgressBar.setMax(packageInfoList.size());
// 循环遍历所有已获得的应用包集合
for (PackageInfo packageInfo : packageInfoList) {
// 包信息获取所有的签名的数组
Signature[] signatures = packageInfo.signatures;
// 获取签名文件的第一位
String signature = signatures[0].toCharsString();
// 加密获取到的签名文件的第一位(感觉不对,无需进行MD5加密,直接与病毒特征库比较即可)
// String signatureMD = MD5Util.encodePassword(signature);
VirusScanningInfo scanningInfo = new VirusScanningInfo();
// if (virusList.contains(signatureMD)) {
if (virusList.contains(signature)) {
// 扫描到病毒,记录病毒后提醒用户卸载
scanningInfo.isVirus = true;
mVirusScanningList.add(scanningInfo);
} else {
scanningInfo.isVirus = false;
}
// 扫描到的所有应用的信息集合
scanningList.add(scanningInfo);
scanningInfo.packageName = packageInfo.packageName;
scanningInfo.name = packageInfo.applicationInfo.loadLabel(packageManager).toString();
// 暂缓线程
try {
Thread.sleep(50 + new Random().nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
// 扫描过程中,实时更新进度条
count++;
mScanningProgressBar.setProgress(count);
//扫描尚未结束,将每个应用的扫毒结果实时传给主线程给Handler处理,实时更新UI
Message msg = Message.obtain();
msg.what = SCANNING;
msg.obj = scanningInfo;
mHandler.sendMessage(msg);
}
//扫描已结束,返回到Handler进行病毒扫描结果最终处理
Message msg = Message.obtain();
msg.what = SCANNING_END;
mHandler.sendMessage(msg);
}
}).start();
}
/**
* 卸载病毒
*/
private void unStallVirus(final List<VirusScanningInfo> virusScanningList) {
// 卸载病毒程序
if (!virusScanningList.isEmpty()) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("警告!");
builder.setMessage("发现" + virusScanningList.size() + "个病毒,请立即清理!");
builder.setPositiveButton("立即清理", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
for (VirusScanningInfo virusScanningInfo : virusScanningList) {
Intent intent = new Intent("android.intent.action.DELETE");
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse("package:" + virusScanningInfo.packageName));
startActivity(intent);
}
}
});
} else {
ToastUtil.show(getApplicationContext(), "扫描完成,未发现木马病毒");
}
}
/**
* 初始化扫描动画
*/
private void initScanningAnimation() {
RotateAnimation rotateAnimation = new RotateAnimation(0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnimation.setDuration(1000);
// 设置动画无限循环
rotateAnimation.setRepeatCount(Animation.INFINITE);
// 设置插值器,匀速循环且不停顿
rotateAnimation.setInterpolator(new LinearInterpolator());
rotateAnimation.setFillAfter(true);
mActScanningImage.startAnimation(rotateAnimation);
}
/**
* 初始化布局文件中view
*/
private void initView() {
mActScanningImage = (ImageView) findViewById(R.id.act_scanning_img);
mScanningNameText = (TextView) findViewById(R.id.current_scanning_text);
mScanningAddLayout = (LinearLayout) findViewById(R.id.linear_anti_add_text);
mScanningProgressBar = (ProgressBar) findViewById(R.id.scanning_progress);
}
class VirusScanningInfo {
String packageName;
String name;
boolean isVirus;
}
}
UI界面布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
style="@style/TitleText"
android:text="手机杀毒" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_scanner_malware" />
<ImageView
android:id="@+id/act_scanning_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/act_scanning_03" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/current_scanning_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在杀毒"
android:textSize="18sp"
android:textColor="#000000"/>
<ProgressBar
android:id="@+id/scanning_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:progressDrawable="@drawable/progress_back_ground"
style="@style/Base.Widget.AppCompat.ProgressBar.Horizontal"/>
</LinearLayout>
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<!--根据杀毒的进度条扫描进度,动态添加TextView-->
<LinearLayout
android:id="@+id/linear_anti_add_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</LinearLayout>
直观视图:
扫描结果:
加盐加密存储
病毒MD5特征值中涉及了MD5的加密存储问题,故此处解释下加盐加密存储。
加盐加密是一种对系统登录口令的加密方式,它实现的方式是将每一个口令同一个叫做”盐“(salt)的n位随机数相关联。无论何时只要口令改变,随机数就改变。随机数以未加密的方式存放在口令文件中,这样每个人都可以读。不再只保存加密过的口令,而是先将口令和随机数连接起来然后一同加密,加密后的结果放在口令文件中。
几种常见的破解密码的方法: 最简单、常见的破解方式当属字典破解(Dictionary Attack)和暴力破解(Brute Force Attack)方式。这两种方法说白了就是猜密码。
字典破解和暴力破解都是效率比较低的破解方式。如果你知道了数据库中密码的哈希值,你就可以采用一种更高效的破解方式,查表法(Lookup Tables)。还有一些方法,比如逆向查表法(Reverse Lookup Tables)、彩虹表(Rainbow Tables)等,都和查表法大同小异。现在我们来看一下查表法的原理。
查表法不像字典破解和暴力破解那样猜密码,它首先将一些比较常用的密码的哈希值算好,然后建立一张表,当然密码越多,这张表就越大。当你知道某个密码的哈希值时,你只需要在你建立好的表中查找该哈希值,如果找到了,你就知道对应的密码了。
从上面的查表法可以看出,即便是将原始密码加密后的哈希值存储在数据库中依然是不够安全的。那么有什么好的办法来解决这个问题呢?答案是加盐。盐(Salt)是什么?就是一个随机生成的字符串。我们将盐与原始密码连接(concat)在一起(放在前面或后面都可以),然后将concat后的字符串加密。采用这种方式加密密码,查表法就不灵了(因为盐是随机生成的)。
实现代码:
public class MD5Util {
/**
* 对指定的字符串进行MD5加密处理
* @param password 待加密的原始密码值
* @return MD5加盐加密后的密码值
*/
public static String encodePassword(String password) {
try {
// 此处采取密码加盐加密处理,确保密码更加安全,因为MD5可将通过暴力建立表格与密文对比,查出密码
// 加盐是将明文与随机数拼接后再加密存储,同时将该随机数保存至数据库,便于后面密码校验进行身份验证
// 但此处的加盐并没有使用动态的随机值,而是直接粗暴地使用固定值"mobilesafer",且未保存至数据库
password = password + "mobilesafer";
// 获取MessageDigest实例,并指定加密算法类型
MessageDigest digest = MessageDigest.getInstance("MD5");
// 将需要加密的字符串转换成byte数组后进行随机哈希过程
byte[] byteArray = password.getBytes();
// 信息摘要对象对字节数组进行摘要,得到摘要字节数组
byte[] md5Byte = digest.digest(byteArray);
StringBuffer buffer = new StringBuffer();
// 循环遍历byte类型数组,让其生成32位字符串
for (byte b : md5Byte) {
int i = b & 0xff;
String str = Integer.toHexString(i);
if (str.length() < 2) {
str = "0" + str;
}
buffer.append(str);
}
return buffer.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}