手機殺毒原理
特徵碼匹配
在手機系統中,每個應用都有它特有的特徵信息,它是該應用所特有的,每個應用都是不同的,這就是手機的特徵碼。
手機應用的特徵碼包括:包名和應用簽名。
Android中,同一個包名的程序,只允許安裝一個。所以每個應用的包名都是不同的。每個程序的簽名文件都不相同,因爲每個開發者的簽名文件都是特有的。
基於特徵碼的殺毒方式,是在已知病毒應用特徵碼的情況下,通過檢測手機安裝應用的特徵碼跟病毒數據庫中的特徵碼是否相同,如果找到相同的數據庫,則說明該應用爲病毒應用。所以病毒數據庫是關鍵。各殺毒軟件的原理都是相同的,區別主要在於殺毒引擎。殺毒引擎用於獲取手機應用的特徵碼,並依據此特徵碼碼查詢病毒數據庫的算法。
【基於簽名的特徵碼的掃描(hash碼-md5特徵碼的掃描)】
- 特徵:只能查殺已知的病毒,不能查殺未知的病毒。
- 原理:病毒庫中保存病毒對應的hash,通過比較程序簽名和病毒庫中的簽名判斷是否病毒。
- 殺毒引擎用於獲取硬件上文件的hash碼,並依據此hash碼查詢數據庫的算法.
【特徵庫的建立】
首先,我們需要部署大量的“蜜罐”服務器,也就是沒有安裝殺毒軟件、防火牆等的赤裸裸的手機或服務器,讓病毒很輕易的入侵。
然後,由病毒工程師來對它們進行分析,提取特有的特徵碼,加入到特徵碼服務器裏面,以供手機安全衛士調用。當有新的病毒的時候,它們會被及時的下載到手機安全衛士裏面,以供比對查殺。
當進行雲查殺的時候,手機安全衛士提取本地所有應用的特徵碼,發送到雲服務器進行比對,然後將比對結果返回給手機安全衛士,告訴手機用戶查殺結果,比如:哪些軟件是病毒,哪些遊戲會竊取話費,哪些壁紙具有高危風險等。
高危API調用
如果在未知病毒特徵碼的情況下進行殺毒,那就是通過監控高危的API權限的調用,這是一種主動防禦手段。
例如監控以下敏感操作:
- 更改瀏覽器主頁
- 註冊開機啓動的行爲
- 應用程序的內存注入
實現代碼
下面代碼基於病毒的特徵值,在安全防護軟件的本地資源路徑下建立並保存病毒特徵值數據庫,然後在殺毒程序中將本機應用包的簽名值與病毒特徵值進行對比,如果匹配則進行病毒處理。
其中,病毒數據庫的具體內容使用adb工具查看如下:
首先建立獲取特徵值列表的數據庫底層操作類:
/**
* FileName: AntiVirusDao <br>
* Description: 從本地數據庫讀取病毒特徵值的數據庫操作類 <br>
* Author: 沈濱偉 <br>
* Date: 2019/4/30 16:22
*/
public class AntiVirusDao {
public static final String tag = "AntiVirusDao";
// 病毒庫拷貝到本地後的路徑地址
public static String virusPathName = "data/data/cauc.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: 沈濱偉-13042299081 <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 "";
}
}