分享
 
 
 

Unix/ELF文件格式及病毒分析

王朝system·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Unix/ELF文件格式及病毒分析

[Silvio Cesare]

介绍

本文介绍了Unix病毒机制、具体实现以及ELF文件格式。简述了Unix病毒检测和反检

测技术,提供了Linux/i386架构下的一些例子。需要一些初步的Unix编程经验,能够

理解Linux/i386下汇编语言,如果理解ELF本身更好。

本文没有任何实际意义上的病毒编程技术,仅仅是把病毒原理应用到Unix环境下。这

里也不打算从头介绍ELF规范,感兴趣的读者请自行阅读ELF规范。

感染 ELF 格式文件

进程映象包含"文本段"和"数据段",文本段的内存保护属性是r-x,因此一般自修改

代码不能用于文本段。数据段的内存保护属性是rw-。

段并不要求是页尺寸的整数倍,这里用到了填充。

关键字:

[...] 一个完整的页

M 已经使用了的内存

P 填充

页号

#1 [PPPPMMMMMMMMMMMM] #2 [MMMMMMMMMMMMMMMM] |-- 一个段

#3 [MMMMMMMMMMMMPPPP] /

段并没有限制一定使用多个页,因此单页的段是允许的。

页号

#1 [PPPPMMMMMMMMPPPP] <-- 一个段

典型的,数据段不需要从页边界开始,而文本段要求起始页边界对齐,一个进程映象

的内存布局可能如下:

关键字:

[...] 一个完整的页

T 文本段内容

D 数据段内容

P 填充

页号

#1 [TTTTTTTTTTTTTTTT] <-- 文本段内容

#2 [TTTTTTTTTTTTTTTT] <-- 文本段内容

#3 [TTTTTTTTTTTTPPPP] <-- 文本段内容(部分)

#4 [PPPPDDDDDDDDDDDD] <-- 数据段内容(部分)

#5 [DDDDDDDDDDDDDDDD] <-- 数据段内容

#6 [DDDDDDDDDDDDPPPP] <-- 数据段内容(部分)

页1、2、3组成了文本段

页4、5、6组成了数据段

从现在开始,为简便起见,段描述图表用单页,如下:

页号

#1 [TTTTTTTTTTTTPPPP] <-- 文本段

#2 [PPPPDDDDDDDDPPPP] <-- 数据段

在i386下,堆栈段总是在数据段被给予足够空间之后才定位的,一般堆栈位于内存高

端,它是向低端增长的。

在ELF文件中,可装载段都是物理映象:

ELF Header

.

.

Segment 1 <-- 文本段

Segment 2 <-- 数据段

.

.

每个段都有一个定位自身起始位置的虚拟地址。可以在代码中使用这个地址。

为了插入寄生代码,必须保证原来的代码不被破坏,因此需要扩展相应段所需内存。

文本段事实上不仅仅包含代码,还有 ELF 头,其中包含动态链接信息等等。如果直

接扩展文本段插入寄生代码,带来的问题很多,比如引用绝对地址等问题。可以考虑

保持文本段不变,额外增加一个段存放寄生代码。然而引入一个额外的段的确容易引

起怀疑,很容易被发现。

向高端扩展文本段或者向低端扩展数据段都有可能引起段重叠,在内存中重定位一个

段又会使那些引用了绝对地址的代码产生问题。可以考虑向高端扩展数据段,这不是

个好主意,有些Unix完整地实现了内存保护机制,数据段是不可执行的。

段边界上的页填充提供了插入寄生代码的地方,只要空间允许。在这里插入寄生代码

不破坏原有段内容,不要求重定位。文本段结尾处的页填充是个很好的地方,最后看

上去象下面这个样子:

关键字:

[...] 一个完整的页

V 寄生代码

T 文本段内容

D 数据段内容

P 填充

页号

#1 [TTTTTTTTTTTTVVPP] <-- 文本段

#2 [PPPPDDDDDDDDPPPP] <-- 数据段

一个更完整的ELF可执行布局如下:

ELF Header

Program header table

Segment 1

Segment 2

Section header table

Section 1

.

.

Section n

典型的,额外的节(那些没有相应段的节)用于存放调试信息、符号表等等。

下面是一些来自 ELF 规范的内容:

ELF 头位于最开始,保存一张"road map",描述了文件的组织结构。节保存大量链接

信息、符号表、重定位信息等等。

如果存在一个"program header table",将告诉操作系统如何建立进程映象(执行一

个程序)。可执行文件必须有一个"program header table",可重定位的文件不需要

该表。"section header table"描述了文件的节组织。每个节在该表中都有一个表项,

表项包含了诸如节名、节尺寸等信息。链接过程中被用到的文件自身必须有一个

"section header table",其他目标文件可有可无该表。

插入寄生代码之后,ELF 文件布局如下:

ELF Header

Program header table

Segment 1 - 文本段(主体代码)

- 寄生代码

Segment 2

Section header table

Section 1

.

.

Section n

寄生代码必须物理插入到ELF文件中,文本段必须扩展以包含新代码。

下面的信息来自/usr/include/elf.h

/* The ELF file header. This appears at the start of every ELF file. */

#define EI_NIDENT (16)

typedef struct

{

unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */

Elf32_Half e_type; /* Object file type */

Elf32_Half e_machine; /* Architecture */

Elf32_Word e_version; /* Object file version */

Elf32_Addr e_entry; /* Entry point virtual address */

Elf32_Off e_phoff; /* Program header table file offset */

Elf32_Off e_shoff; /* Section header table file offset */

Elf32_Word e_flags; /* Processor-specific flags */

Elf32_Half e_ehsize; /* ELF header size in bytes */

Elf32_Half e_phentsize; /* Program header table entry size */

Elf32_Half e_phnum; /* Program header table entry count */

Elf32_Half e_shentsize; /* Section header table entry size */

Elf32_Half e_shnum; /* Section header table entry count */

Elf32_Half e_shstrndx; /* Section header string table index */

} Elf32_Ehdr;

e_entry 保存了程序入口点的虚拟地址。

e_phoff 是"program header table"在文件中的偏移。因此为了读取

"program header table",需要调用lseek()定位该表。

e_shoff 是"section header table"在文件中的偏移。该表位于文件尾部,在文本段

尾部插入寄生代码之后,必须更新e_shoff指向新的偏移。

/* Program segment header. */

typedef struct

{

Elf32_Word p_type; /* Segment type */

Elf32_Off p_offset; /* Segment file offset */

Elf32_Addr p_vaddr; /* Segment virtual address */

Elf32_Addr p_paddr; /* Segment physical address */

Elf32_Word p_filesz; /* Segment size in file */

Elf32_Word p_memsz; /* Segment size in memory */

Elf32_Word p_flags; /* Segment flags */

Elf32_Word p_align; /* Segment alignment */

} Elf32_Phdr;

可装载段(文本段/数据段)在"program header"中由成员变量p_type标识出是可装载

的,其值为PT_LOAD (1)。与"ELF header"中的e_shoff一样,这里的p_offset成员

必须在插入寄生代码后更新以指向新偏移。

p_vaddr 指定了段的起始虚拟地址。以p_vaddr为基地址,重新计算e_entry,就可以

指定程序流从何处开始。

可以利用p_vaddr指定程序流从何处开始。

p_filesz 和 p_memsz 分别对应该段占用的文件尺寸和内存尺寸。

.bss 节对应数据段里未初始化的数据部分。我们不想让未初始化的数据占用文件空

间,但是进程映象必须保证能够分配足够的内存空间。.bss 节位于数据段尾部,任

何超过文件尺寸的定位都假设位于该节中。

/* Section header. */

typedef struct

{

Elf32_Word sh_name; /* Section name (string tbl index) */

Elf32_Word sh_type; /* Section type */

Elf32_Word sh_flags; /* Section flags */

Elf32_Addr sh_addr; /* Section virtual addr at execution */

Elf32_Off sh_offset; /* Section file offset */

Elf32_Word sh_size; /* Section size in bytes */

Elf32_Word sh_link; /* Link to another section */

Elf32_Word sh_info; /* Additional section information */

Elf32_Word sh_addralign; /* Section alignment */

Elf32_Word sh_entsize; /* Entry size if section holds table */

} Elf32_Shdr;

sh_offset 指定了节在文件中的偏移。

为了在文本段末尾插入寄生代码,我们必须做下列事情:

* 修正"ELF header"中的 p_shoff

* 定位"text segment program header"

* 修正 p_filesz

* 修正 p_memsz

* 对于文本段phdr之后的其他phdr

* 修正 p_offset

* 对于那些因插入寄生代码影响偏移的每节的shdr

* 修正 sh_offset

* 在文件中物理地插入寄生代码到这个位置

text segment p_offset + p_filesz (original)

这里存在一个大问题,ELF 规范中指出,

p_vaddr mod PAGE_SIZE == p_offset mod PAGE_SIZE

为了满足这个要求:

* 修正"ELF header"中的 p_shoff ,增加 PAGE_SIZE 大小

* 定位"text segment program header"

* 修正 p_filesz

* 修正 p_memsz

* 对于文本段phdr之后的其他phdr

* 修正 p_offset ,增加 PAGE_SIZE 大小

* 对于那些因插入寄生代码影响偏移的每节的shdr

* 修正 sh_offset ,增加 PAGE_SIZE 大小

* 在文件中物理地插入寄生代码以及填充(确保构成一个完整页)到这个位置

text segment p_offset + p_filesz (original)

我们还需要修正程序入口点的虚拟地址,使得寄生代码先于宿主代码执行。同时需要

在寄生代码尾部能够跳转回宿主代码原入口点继续正常流程。

* 修正"ELF header"中的 p_shoff ,增加 PAGE_SIZE 大小

* 修正寄生代码的尾部,使之能够跳转回宿主代码原入口点

* 定位"text segment program header"

* 修正 "ELF header"中的 e_entry ,指向 p_vaddr + p_filesz

* 修正 p_filesz

* 修正 p_memsz

* 对于文本段phdr之后的其他phdr

* 修正 p_offset ,增加 PAGE_SIZE 大小

* 对于文本段的最后一个shdr

* 修正sh_len(应该是sh_size吧,不确定),增加寄生代码大小

* 对于那些因插入寄生代码影响偏移的每节的shdr

* 修正 sh_offset ,增加 PAGE_SIZE 大小

* 在文件中物理地插入寄生代码以及填充(确保构成一个完整页)到这个位置

text segment p_offset + p_filesz (original)

病毒可以随机遍历一个目录树,寻找那些e_type等于 ET_EXEC 或者 ET_DYN 的文件,

加以感染,这分别是可执行文件和动态链接库文件。

分析Linux病毒

病毒要求不使用库,避开libc,转而使用系统调用机制。

为了动态申请堆内存用于phdr table和shdr table,应该使用brk系统调用。

利用与缓冲区溢出相同的技术取得常量字符串的地址。

使用gcc -S编译c代码,观察调整asm代码。

注意在进入/离开寄生代码的时候保存/恢复寄存器。

利用objdump -D观察调整一些需要确定的偏移量。

检测病毒

这里描述的病毒很容易检测。最显眼的是程序入口点不在常规节中,甚至干脆不在任

何节中。清理病毒的过程和感染病毒的过程类似。

用objdump --all-headers很容易定位程序入口点,用objdump --disassemble-all

跟踪下去就可以得到程序原入口点。

缺省程序入口点是_start,但是可以在链接的时候更改它。

结论

Unix病毒尽管不流行,但的确可行。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有