写这么个标题是为了让搜索引擎更容易搜到,嘿嘿
终于还是忍不住在机器上装了linux。原本打算自己从编译内核开始手工打造一个linux系统,但编译内核首先得有一个能够运行的linux环境(先有鸡还是先有蛋的问题),再加上手工打造linux系统对我来说实在是个比较浩大的工程,所以还是先装个发行版吧。
选了ubuntu,主要是因为安装简单,界面漂亮。这2点上已经快赶上windows了,但不得不说MS的UE设计确实牛。
言归正传。安装过程中还是遇到了一些问题,在网上搜来搜去,得到了一些帮助,不过也有一些问题网上没有答案,所以写下来,希望能够帮助将来遇到这些问题的人:)
first,安装grub的问题。由于不想把grub装到主分区上(总觉得windows重装的频率会高些),所以在安装的时候选择了grub安装到 sda10(ubuntu所在分区)。结果在装到grub的时候安装失败,退出了……
解决方法:
mkdir /tmp/mnt
sudo mount /dev/sda10 /tmp/mnt
sudo grub-install /dev/sda10 –root-directory=/tmp/mnt /dev/sda10 (这一步,网上很多人问为什么grub-install失败,我一开始也是,提示”Not found or not a block device”,其实只要执行了mount,然后加上–root-directory就ok了)
然后上网搜一个menu.lst放到/boot/grub/下,改一改。
然后再mount一个windows分区,执行:
sudo dd if=/dev/sda10 of=/mnt/win_c/ubuntu.bin bs=512 count=1
重启到windows下把boot.ini里加上ubuntu.bin就大功告成了。
另外,安装grub之前最好用dd把mbr的512字节备份出来,否则装错了很麻烦的。
second,安装vim的问题。默认安装的vim居然连syntax on都不支持,只好再apt-get install vim了。但是安装完之后vim还是有个问题:在终端下一些关键字显示成灰色,根本没法看。百度了半天,是有不少人碰到同样的问题,但回答基本都一样 colorscheme evening。可是对于我来说没有效果啊。后来怀疑到显卡问题,ubuntu好像对ati的显卡支持不好。在终端下ll /dev了一下,发现原来应该显示成黄色的字体全都显示成灰色,看来跟vim没关系了。
解决方法:
在grub.lst里,kernel一行后面加上vga=791 (1024*768的分辨率),然后重启进终端,OK了。
最后,ubuntu的root口令的问题。其实很简单,sudo passwd root,然后先输入第一用户的密码就可以设置root的口令了。不过好像不是随便一个用户就可以用su成为root的。
uptime命令打出来的load average分别代表了系统在过去1分钟,5分钟和15分钟的平均负载。
这个平均负载是什么嘞?
是 指平均有多少个进程等待使用CPU,所以这个值不超过系统的CPU个数就OK了。
Running uptime on a system provides three values that relate to the system’s load average. They represent the load average during the last 1 minute, 5 minutes, and 15 mintues, respectively. Each value represents an average of the number of processes waiting to run on a CPU. The ideal load for a system is equal to the number of processors in the system.
————————————————————————-
◆ Unix/ELF文件格式及病毒分析
————————————————————————-
★ 介绍
本文介绍了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病毒尽管不流行,但的确可行。