第二章 基礎技術
一、執行單一例項
通過建立系統命名互斥物件的方式來實現
1、實現原理
通過CreateMutex函式建立一個命名的互斥物件,如果物件建立成功,而且通過呼叫GetLastError函式獲取的返回碼為ERROR_ALREADY_EXISTS,則表示該命名互斥物件存在,即程式重複執行。否則,認為程式首次執行。
2、API
CreateMutex
https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createmutexa
3、實現程式碼如下
#include <windows.h> #include<tchar.h> #include<stdio.h> BOOL IsAlreadyRun() { HANDLE hMutex = NULL; hMutex = ::CreateMutex(NULL, FALSE, _T("TEST")); if (hMutex) { if (ERROR_ALREADY_EXISTS == ::GetLastError()) { return true; } } return false; } int main() { if (IsAlreadyRun()) { printf("Already RUN"); system("pause"); } else { printf("NOT Already Run"); system("pause"); } }
4、小結
這個程式實現起來並不難,關鍵是熟悉CreateMutex函式的呼叫。在呼叫CreateMutex函式來建立命名的互斥物件時,注意互斥物件的名稱不要與現有事件、訊號量或者檔案對映物件等名稱相同,否則建立互斥物件會失敗。
在實現過程中,特別要注意,程式一定不要呼叫CloseHandle函式來關閉由CreateMutex函式創建出來的互斥物件的控制代碼,否則會導致互斥物件判斷失敗。因為CloseHandle函式會關閉互斥物件的控制代碼,釋放資源。這樣,系統上便不會存在對應的命名互斥物件了,通過CreateMutex建立的命名互斥物件都是不會重複的。
5、安全小貼士
使用CreateMutex函式建立的互斥物件,可以通過呼叫CloseHandle函式來關閉互斥物件的控制代碼,從屬於它的所有控制代碼都關閉後,就會刪除該物件。
線上程同步操作中, ReleaseMutex函式可以釋放執行緒對互斥物件的控制權。
二、DLL延遲載入
使用延遲載入方式編譯連結可執行檔案
1、實現原理
本程式以載入第三方庫——skin++庫為例進行講解演示。首先匯入skin++庫檔案,然後編碼,最後對程式編譯連結生成exe可執行檔案。使用PE檢視器PEview.exe檢視可執行檔案的匯入表,便可知道可執行檔案必需的DLL檔案了。
這樣做的好處是可以把必需的DLL檔案以資源形式插入到程式中,並使用DLL延遲載入技術延遲載入。在正式呼叫必需的DLL之前,程式都是可以正常執行的。程式可以在這段時間內,把資源中的DLL釋放到本地,等到正式呼叫DLL的時候釋放的檔案就會正確地載入執行。這樣當使用程式的時候,只需把exe檔案傳送給使用者,而不需要附加DLL檔案了,也不需要擔心程式會丟失DLL檔案。
DLL延遲載入的具體設定步驟為:
屬性-->連結器-->輸入-->延遲載入的DLL-->輸入:SkinPPWTL.dll
2、小結
DLL延遲載入技術不需要編碼來實現,只需對VS開發環境設定連結器即可完成。DLL延遲載入技術,配合資源釋放技術,可以使程式變得更加方便易用。
3、安全小貼士
在PE結構中, DLL延遲載入的資訊儲存在ImgDelayDescr延遲匯入表中,可以通過資料目錄DataDirectory中的IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT項獲取延遲匯入表RVA相對的偏移地址和資料大小。
三、資源釋放
病毒木馬之所以會廣泛使用資源釋放技術,是因為它可以使程式變得更簡潔。如果程式額外需要載入一些DLL檔案、文字檔案、圖片檔案,或者其他的音/視訊檔案等,則可以把它們作為資源插入到程式裡,等到程式執行後,再把它們釋放到本地上。這樣做的好處是編譯出來的程式只有一個exe檔案,而不需要附帶其他檔案,因而程式變得很簡潔。只需把exe植入到使用者計算機上,而不需要連同其他檔案一起植入,這降低了被發現的風險。
1、資源插入步驟
首先新建一個資源520.txt
再自定義新增對話方塊
最後,匯入該對話方塊
2、API
FindResource 確定模組中指定型別和名稱的資源所在位置
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-findresourcea
sizeofResource 獲取指定資源的位元組數
https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-sizeofresource
LoadResource 裝載指定資源到全域性儲存器
https://docs.microsoft.com/en-us/previous-versions/ms915421(v=msdn.10)
LockResource 鎖定資源並得到資源在記憶體中第一個位元組的指標
https://docs.microsoft.com/en-us/previous-versions/aa932898(v=msdn.10)
3、實現原理
首先,通過FindResource定位程式裡的資源,主要是根據"資源型別"和"資源名稱"進行定位,從而獲取資源資訊塊的控制代碼.
其次,根據上面獲取的資源資訊塊的控制代碼,利用sizeofResource獲取資源的大小之後,再通過LoadResource把資源載入程式記憶體中.
接著,通過LockResource鎖定載入到記憶體中的資源,防止程式中的其他操作影響這塊記憶體。其中,返回值就是資源在程序記憶體中的起始地址。
最後,根據資源大小以及程序記憶體的起始地址,可將資源資料讀取出來並儲存為本地檔案。
注意:要必須明確資源所在的模組,要指明所在模組控制代碼並且統一。
4、實現程式碼如下
#include <windows.h>
#include <stdio.h>
#include "resource.h"
void FreeRes_ShowError(char* pszText)
{
char szErr[MAX_PATH] = { 0 };
wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
}
BOOL FreeMyResource(UINT uiResourceName, char* lpszResourceType, char *lpszSaveFileName)
{
//獲取指定模組裡的資源
HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResourceName), lpszResourceType);
if (NULL == hRsrc)
{
FreeRes_ShowError("FindResource");
return FALSE;
}
// 獲取資源的大小
DWORD dwSize = ::SizeofResource(NULL, hRsrc);
if (0 >= dwSize)
{
FreeRes_ShowError("SizeofResource");
return FALSE;
}
// 將資源載入到記憶體裡
HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
if (NULL == hGlobal)
{
FreeRes_ShowError("LoadResource");
return FALSE;
}
// 鎖定資源
LPVOID lpVoid = ::LockResource(hGlobal);
if (NULL == lpVoid)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
// 儲存資源為檔案
FILE* fp = NULL;
fopen_s(&fp, lpszSaveFileName, "wb+");
if (NULL == fp)
{
FreeRes_ShowError("LockResource");
return FALSE;
}
fwrite(lpVoid, sizeof(char), dwSize, fp);
fclose(fp);
return TRUE;
}
int main(){
char szSaveName[MAX_PATH] = "520.txt";
// 釋放資源
BOOL bRet = FreeMyResource(IDR_MYRES2, "MYRES", szSaveName);
if (FALSE == bRet)
{
MessageBox(NULL, "Free Resource Error!", "ERROR", MB_OK);
}
else
{
MessageBox(NULL, "Free Resource OK!", "OK", MB_OK);
}
return 0;
}
一隻小the bug