文章来源:http://blog.chinaunix.net/u/31268/article_0_7.html
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=====
===== =====
==== Linux和ELF文件格式汇编语言教程 LiTlLe VxW著 ====
==== pker / CVC.GB 译 ====
===== =====
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=====
,...... .-, ,-.
.' '. / 内容 提要 /
. . ' '
: .-. .-. : '.__.---.__.'
: : _ : : _ : :
: :(_); :(_); : * 介绍
: , '. : * 汇编语法
: , :; : * 汇编HELLO WORLD
: .-___- . : * 纯汇编HELLO WORLD
: '.___.' '. * 什么是系统调用?
;. ..: * 什么是文件描述符?
:... ....: * 什么是inode?
;:::: .:::::: * 系统调用列表
:::::; ::::::: * 一些重要的注意事项
;::::; ::::::: * 关于系统调用的描述
.::::; LINUX '::::::. * 重要数据结构
.;::::: ::::;. - dirent
;::::; 汇编语言 ':::; - utsname
.:;; '::, * 重要标志的描述
': 教 程 ': - 文件访问位
: : - 文件许可标识
: LiTlLe VxW 编写 : * 如何使用系统调用
'. . - 在屏幕上打印文本
'. ; - 获得关于内核的信息
'. .--..___..--'. ; - 打开文件
, . , , - 打开目录
._._._' ._._._. - 关闭文件描述符
- 创建新文件
- 改变文件指针
- 写(已打开)文件
为29A编写 - 从(已打开)文件中读
- 通过getdents在目录中搜索文件
- 通过readdir在目录中搜索文件
- 读键盘
* 错误码
* 结束语
.-, ,-.
/ 介绍 /
' '
'.____.----.____.'
由于文档和教程较少,并且LINUX是用C写成的,所以对“一般”程序员来说,在linux下用
汇编语言编写程序并不是个好的选择。我只找到很少的一些用汇编语法取代笨重的C语法
来描述结构、数据类型...的汇编教程。 对人们来说,Linux就意味着“用C编写”,但是C
库是怎么编写的呢?用C么?;-) 没一个操作系统都把汇编语言当作第一语言! UNIX类
平台同样不例外!!!
这篇文章是一篇很好的关于在了linux和UNIX类平台下用汇编语言编程的介绍性文章。用
了这篇文章,你就有了所有的关于搜索、打开、读写文件的信息 ;-) 你还可以找到一些
关于系统调用、系统结构、高级汇编编程...的资料
在这里我将使用nasm语法, 因为我在win32下也同样适用它。 如果你已经正确地安装了
LINUX,你就可以在你的硬盘上找到汇编编程所需要的一切。
这篇教程在RED HAT 7.3,内核版本2.4.18-3上通过测试。
你所需要的是:
- 用你的眼睛来阅读这篇教程
- 一般的英语知识(在病毒教程中用到的英语)
- 关于系统调用的文档
- 随便一个文本编辑器
- nasm(在你的Linux安装光盘中)
- ld(在你的Linux安装光盘中)
- 基本的汇编知识(intel语法) (如果你不明白mov eax,0000029ah那么还是离开吧
!!!)
- 你的linux系统中的文档、源代码还有.h文件
.-, ,-.
/ 汇编语法 /
' '
'.____.--.____.'
Intel语法和AT&T汇编:
例如: Intel语法 | AT&T语法
----------------------+--------------------
mov eax,1 | movl $1,%eax
mov ebx,0ffh | movl $0xff,%ebx
int 80h | int $0x80
mov eax,[ebx] | movl (%ebx),%eax
mov eax,[ebx+3] | movl 3(%ebx),%eax
lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax
我选择Intel语法来写这篇教程(我不懂AT&T汇编语法 ;-)),当然还适用NASM编译器。
有一些程序可以把AT&T语法的汇编文件转换成Intel语法的程序。
让我们从过这个著名的HELLO WORLD程序开始:
.-, ,-.
/ 汇编 HELLO WORLD /
' '
'.______.-----._______.'
这个不是“纯汇编”版本。这是一个介于C和汇编之间的版本,我要给你展示这个因为你会
看到如下代码:
;------------------------------------
; 这样编译文件: ; ;
; ; ;
; nasm -f elf hello.asm ; ;
; gcc hello.o -o hello ; ;
;- - - - - - - - - - - - - ;
;
BITS 32 ;
;
EXTERN puts ;
;
SECTION .text ;
;
GLOBAL main ;
;
main: ;
push dword hello ;
call puts ;
add esp, 4 ;
ret ;
;
SECTION .data ;
;
hello db "Hello world !", 0 ;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
; 注意:* gcc使用ld来生成可执行文件 ;
; * 这个文件编译后为13470字节!!! ;
;-----------------------------------------------------------+
.-, ,-.
/ 纯汇编HELLO WORLD /
' '
'.________.-------.________.'
这里是一个纯汇编语言编写的HELLO WORLD,使用int 80h:
;--------------------------------------------------------------------
; 这样编译文件: ; ;
; ; ;
; nasm -f elf hello.asm ; ;
; ld -o hello hello.o ; ;
;- - - - - - - - - - - - - ;
;
BITS32 ; <--- 告诉nasm这是32位模式 ;
;
section .text ; <--- 代码节从这里开始 ;
;
global _start ; <---. ;
_start: ; <----/__为NASM ;
;
mov eax,4 ; 系统调用 #4,‘写’ ;
mov ebx,1 ; 屏幕的文件描述符(fd) ;
mov ecx,message ; 写缓冲的偏移 ;
mov edx,23 ; 要写的字节数 ;
int 80h ; 80h中断 ;
;
mov eax,1 ; 系统调用 #1, '退出' ;
mov ebx,0 ; 结束码 ;
int 80h ; 80h中断 ;
;
section .data ; <--- 数据节从这里开始i ;
;
message db "hello LINUX world !!!",13,10,0 ;
;
section .bss ;
;
;- - - - - - - - - - - - - - - - - - - - - - - - - ;
; 注意: * .text是代码(程序)节 ; ;
; * .data是初始化数据 ; ;
; * .bss是未初始化数据 ; ;
; * 在NASM中,mov eax,10中十进制的10d, ; ;
; 不是十六进制 ; ;
; * 屏幕被看作一个文件,文件描述符为1 ; ;
; * 文件编译后为889字节 ; ;
;-------------------------------------------------------------------;-;
创建hello.asm文件,编写代码并保存。然后在控制台中输入:
nasm -f elf hello.asm <---.
ld -o hello hello.o <----'--- 编译(参阅NASM文档!)
./hello <-------- 运行hello程序(别忘了‘./’)
如果你初次接触linux:
cd Desktop <--- 进入‘Desktop’目录(大小写敏感)
cd .. <--- 返回到前一级目录(父目录)
ls <--- 在屏幕上当前目录下的文件
./RUNit <--- 在运行的文件前加./
.-, ,-.
/ 什么是系统调用? /
' '
'._________.----._________.'
在DOS汇编中, 你需要把功能号放到ah中, 把参数放到其他寄存器中, 然后调用中断
(int 21h,int 22h,int 23h,...,int 10h为BIOS调用)
例如: mov ah,4ch ; 功能 4ch = ‘结束当前程序’
mov al,00h ; 结束码
int 21h ; 21h中断(dos中断)
在win32中,你需要在API调用前把参数压入堆栈中
例如: push dword 0 ; 结束码
call ExitProcess ; 结束当前程序的调用
在LINUX中,你需要把系统调用号放在eax中,把参数放在ebx,ecx,edx,... 然后调用
int 80h(只使用80h中断)。
例如: mov eax,1 ; 系统调用 1 = ‘结束当前程序’
mov ebx,0 ; 结束码
int 80h ; 80h中断
系统调用是通过一个向内核的调用来完成我们需要完成的事情。所有的linux版本不可能
使用相同的系统调用,但是大部分系统调用在所有的linux版本中相同。
Linux系统有效的系统调用列表安装在:
/usr/man/man2/unistd.h (或者在一个叫做‘man’的自目录中)
/usr/include/sys/syscall.h.
/usr/include/asm/unistd.h
要使用一个系统调用你应该:
- 把系统调用号放到eax中
- 把参数放到 ebx,ecx,edx,esi,edi(,ebp)中(这个顺序在系统调用的描述中
使用)
- int 80h
多数系统调用的返回值在eax中,它可以是错误码或者有别的含义。
警告:
在内核1.0中:eax=0 --> 成功
eax=0ffffffffh --> 错误
在内核2.4中:0fffff000h < eax < 0ffffffffh ---> 错误码
.-, ,-.
/ 什么是文件描述符? /
' '
'.___________.----.___________.'
文件描述符(fd)是用来指定一个打开的文件(打开一个文件 ---> 分配一个fd) 的双
字值。有3个默认的打开的文件:
fd=0:标准输入(键盘)
fd=1:标准输出(屏幕)
fd=2:标准错误(在屏幕上输出错误信息)
.-, ,-.
/ 什么是inode ? /
' '
'._______.---._______.'
Linux操作系统(还用unix类操作系统)不处理ascii文件名(搜索、写、读、...)事实
上,每个文件都有它自己的inode号,系统通过inode号(而不是像DOS/win平台中那样使
用文件名)。处理文件。一个目录仅仅是一个包含其他文件信息的文件(文件名、inode
号)。
.-, ,-.
/ 系统调用列表 /
' '
'.______.---.______.'
这里是一些有用的系统调用列表:
+===========+==========+================================================+
| 系统调用 | 名称 | 功 能 |
+===========+==========+================================================+
| 1 | exit | 结束当前进程 |
+-----------+----------+------------------------------------------------+
| 3 | read | 从文件描述符中读到缓冲中 |
+-----------+----------+------------------------------------------------+
| 4 | write | 把缓冲中的内容写到文件中 |
+-----------+----------+------------------------------------------------+
| 5 | open | 打开、创建或者截短文件/设备 |
+-----------+----------+------------------------------------------------+
| 6 | close | 关闭文件 |
+-----------+----------+------------------------------------------------+
| 11 | execve | 运行一个程序 |
+-----------+----------+------------------------------------------------+
| 12 | chdir | 改变当前工作路径 |
+-----------+----------+------------------------------------------------+
| 14 | mknod | 创建一个文件系统节点(文件、设备、特殊文件、 |
| | | 命名管道) |
+-----------+----------+------------------------------------------------+
| 15 | chmod | 改变文件属性 |
+-----------+----------+------------------------------------------------+
| 19 | lseek | 改变文件指针 |
+-----------+----------+------------------------------------------------+
| 38 | rename | 移动/重命名文件 |
+-----------+----------+------------------------------------------------+
| 39 | mkdir | 创建目录 |
+-----------+----------+------------------------------------------------+
| 40 | rmdir | 删除一个空目录 |
+-----------+----------+------------------------------------------------+
| 89 | readdir | 读一个目录 |
+-----------+----------+------------------------------------------------+
| 122 | uname | 获得当前内核的名称和信息 |
+-----------+----------+------------------------------------------------+
| 141 | getdents | 获得目录入口 |
+-----------+----------+------------------------------------------------+
你应该在你的硬盘上找到的文件和文档:
系统调用列表: unistd.h
syscall.h
数据结构: linux/types.h
asm/posix_types.h
linux/kernel.h
错误码: include/asm/errno.h
根据asmutils-0.14,Linux(2.2/4)、 FREEBSD、 NETBSD、 OPENBSD、B EOS、ATHEOS
通常有46个系统调用。
.-, ,-.
/ 一些重要的注意事项 /
' '
'.______.-----._____.'
注意:* Linux下所有的东西都看作是文件:屏幕、目录、键盘、设备、文本文件,...
* 在有些版本的Linux中,readdir系统调用可以用getdents系统调用替换
* FreeBSD不像Linux那样使用80h中断:(这是我从书中读到的但是我从没有测试
过!也许在其他的教程中会有):
例如:
; 打开一个文件(系统调用 5)
;
open: push dword mode
push dword flags
push dword path
mov eax,5
push eax
int 80h
add espk,byte 16
ret
.-, ,-.
/ 关于系统调用的描述 /
' '
'.________.-----.________.'
____________________________________________
1 |
___| exit
输入: eax = 1
ebx = 结束码
输出: 无
____________________________________________
3 |
___| read
输入: eax = 3
ebx = 文件描述符
ecx = 指向缓冲的指针
edx = 要读出的字节数
输出: eax = 读出的字节数(文件指针根据edx设置)
错误码: EAGAIN, EBADF, EFAULT, EINTR, EINVAL, EIO, EISDIR
____________________________________________
4 |
___| write
输入: eax = 4
ebx = 文件描述符
ecx = 写缓冲的指针
edx = 要写的字节
输出: eax = 写入的字节数
错误码: EAGAIN, EBADF, EFAULT, EINTR, EINVAL, EIO, ENOSPC, EPIPE
____________________________________________
5 |
___| open
输入: eax = 5
ebx = 指向文件绝对路径或相对路径名称的地指针
ecx = 文件访问位
edx = 文件许可,模式
输出: eax = fd(16位文件描述符)
错误码: ACCESS, EXIST, FAULT, ISDIR, LOOP, MFILE,
NAMETOOLONG, NFILE, NOENT, NODEV, NODIR,
NOMEM, NOSPC, NXIO, ROFS, TXTBSY
____________________________________________
6 |
___| close
输入: eax=6
ebx=要关闭文件的文件描述符
输出: 无
错误码: EBADF
____________________________________________
11 |
___| execve
输入: eax= = 11
ebx = 指向<nul>字符结尾程序路径名字符串
ecx = 指向0结尾的<nul>字符结尾程序名字符串列表
edx = 指向0结尾的<nul>字符结尾环境名字符串列表
输出: 无
错误码: eax = 2big, acces, inval, io, isdir, libbad loop, nfile,
noexec, noent, nomem, notdir, fault, nametoolong,
perm, txtbusy
____________________________________________
12 |
___| chdir
输入: eax = 12
ebx = 指向绝对或相对路径文件名字符串
输出: 无
错误码: nametoolong, noent, nomem, notdir
____________________________________________
14 |
___| mknod
输入: eax = 14
ebx = 指向绝对或相对路径文件名字符串
ecx = 文件许可标识
edx = 指定新创建设备、特殊文件的主、副个数;否则忽略
输出: 无
错误码: acces, exist, fault, inval, loop, nametoolong,
noent, nomem, nospc, notdir, perm, rofs
____________________________________________
15 |
___| chmod
输入: eax = 15
ebx = 指向绝对或相对路径文件名字符串
ecx = 文件许可标识
输出: 无
错误码: acces, badf, fault, io, loop, nametoolong,
noent, nomem, notdir, perm, rofs
____________________________________________
19 |
___| lseek
输入: eax = 19
ebx = 文件描述符
ecx = 要移动的字节数
edx = 如何移动(其中一个标识)
SEEK_SET 0:从文件头开始移动
SEEK_CUR 1:从当前位置开始移动
SEEK_END 2:从文件尾开始移动
输出: 文件指针从文件头开始的偏移量
错误码: badf, inval, ispipe
____________________________________________
38 |
___| rename
输入: eax = 38
ebx = 原文件名
ecx = 新文件名
输出: 无
错误码: busy, exist, isdir, notempty, xdev (and other f.s. errors)
____________________________________________
39 |
___| mkdir
输入: eax = 39
ebx = 要创建目录的名称
ecx = 文件许可标识
输出: 无
错误码: ?
____________________________________________
40 |
___| rmdir
输入: eax = 40
ebx = 要删除目录的名称
输出: 无
错误码: access, busy, fault, loop, nametoolong,
noent, nomem, notdir, notempty, perm, rofs
____________________________________________
89 |
___| readdir
输入: eax = 89
ebx = 文件描述符
ecx = 指向dirent结构的指针
edx = 计数(忽略???)
输出: 无
错误码: badf, fault, inval, noent, notdir
____________________________________________
122|
___| uname
输入: eax = 122
ebx = 指向utsname结构的指针
输出: 无
错误码: fault
____________________________________________
141|
___| getdents
输入: eax = 141
ebx = 文件描述符
ecx = 指向dirent结构的指针
edx = 缓冲大小
输出: eax = 要读的字节数
错误码: badf, fault, inval, noent, notdir
____________________________________________
.-, ,-.
/ 重要数据结构 /
' '
'.________.----.________.'
************
*** dirent ***
************
有两种类型的dirent结构(dirent和dirent64),为什么呢??? 问得好!我对此并不
确定,但是如果你看一下ELF文件格式, 你可以在文件头偏移4的位置上找到一个字节类
型的值(EI_CLASS),这个值决定了ELF文件数据结构的类型:
EI_CLASS = 1 ---> 32位结构
EI_CLASS = 2 ---> 64位结构
dirent结构的nasm语法表示:
dirent: ; 结构最大:266字节
d_ino resd 1 ; inode号
d_off resd 1 ; 目录文件偏移(目录文件开头的偏移,
; 决定了文件的入口)
d_reclen resw 1 ; 记录长度
d_ino resb 256 ; 文件名,null结尾
dirent64结构的C语法描述(不是我写的,是‘CTRL+C,CTRL+V’)(64表示是两个双字类
型,short意味着字类型)
内核版本 2.4.xx:
struct dirent64 {
__u64 d_ino; (resd 2)
__s64 d_off; (resd 2)
unsigned short d_reclen; (resw 1)
unsigned char d_type;
char d_name[256];
};
内核版本 2.4.18(fs/readdir.c):
struct linux_dirent64 {
u64 d_ino;
s64 d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[0];
};
*************
*** utsname ***
*************
new_utsname: | old_utsname: | oldold_utsname:
| |
sysname resb 65 | sysname resb 65 | sysname resb 9
nodename resb 65 | nodename resb 65 | nodename resb 9
release resb 65 | release resb 65 | release resb 9
version resb 65 | version resb 65 | version resb 9
machine resb 65 | machine resb 65 | machine resb 9
domainname resb 65 | |
在内核2.4中,似乎使用的是new_utsname
.-, ,-.
/ 重要标志的描述 /
'._________.--------.________.'
和DOS或者win32编程一样:你可以重叠使用这些标志。这些值用16进制表示:
文件访问位
--------------------
O_ACCMODE 0000003
O_RDONLY 0000000 <--- 只读
O_WRONLY 0000001 <--- 只写
O_RDWR 0000002 <--- 读写
O_CREAT 0000100
O_EXCL 0000200
O_NOCTTY 0000400
O_TRUNC 0001000
O_APPEND 0002000
O_NONBLOCK 0004000
O_NDELAY O_NONBLOCK
O_SYNC 0010000 制定ext2文件系统和块设备
FASYNC 0020000 fcntl,BSD兼容
O_DIRECT 0040000 直接磁盘访问魔数 - 通常忽略
O_LARGEFILE 0100000
O_DIRECTORY 0200000 必须是目录
O_NOFOLLOW 0400000 不要连接
文件许可标识
----------------------
S_ISUID 04000 执行时设置用户ID
S_ISGID 02000 执行时设置组ID
S_ISVTX 01000 sticky标志位(译注:设置了这个位的可执行文件相应地对内核发送
一个请求,当程序执行结束后仍驻留在内存中。不过现在这个标志已
经过时了,现在使用给予代码页共享等方法)
S_IRUSR 00400 所有者可读
S_IWUSR 00200 所有者可写
S_IXUSR 00100 所有者可执行/搜索
S_IRGRP 00040 组内可读
S_IWGRP 00020 组内可写
S_IXGRP 00010 组内可执行/搜索
S_IROTH 00004 其他人可读
S_IWOTH 00002 其他人可写
S_IXOTH 00001 其他人可执行/搜索
.-, ,-.
/ 如何使用系统调用 /
'.__________.-------._________.'
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 在屏幕上打印文本: 系统调用 4 (write) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
我们把屏幕也看作文件,它的文件描述符为1
mov eax,4 ; 系统调用‘write’
mov ebx,1 ; 屏幕的文件描述符
mov ecx,test ; 写缓冲的偏移地址
mov edx,7 ; 要写的字节数
int 80h ; 80h中断
test db "HELLO",13,10,0
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 获得关于内核的信息:系统调用 122 (uname) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,122 ; 系统调用‘uname’
mov ebx,utsname ; 指向uname的指针
int 80h ; 80h中断
utsname:
sysname resb 65
nodename resb 65
release resb 65
version resb 65
machine resb 65
domainname resb 65
由uname系统调用返回的utsname结构的例子:
偏移 |00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F| ASCII view
---------+-----------------------------------------------+----------------
0000:0000 4c 69 6e 75 78 00 00 00 00 00 00 00 00 00 00 00 Linux...........
0000:0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0040 00 6c 6f 63 61 6c 68 6f 73 74 2e 6c 6f 63 61 6c .localhost.local
0000:0050 64 6f 6d 61 69 6e 00 00 00 00 00 00 00 00 00 00 domain..........
0000:0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0080 00 00 32 2e 34 2e 31 38 2d 33 00 00 00 00 00 00 ..2.4.18-3......
0000:0090 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:00a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:00c0 00 00 00 23 31 20 54 68 75 20 41 70 72 20 31 38 ...#1 Thu Apr 18
0000:00d0 20 30 37 3a 33 32 3a 34 31 20 45 44 54 20 32 30 07:32:41 EDT 20
0000:00e0 30 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02..............
0000:00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0100 00 00 00 00 69 36 38 36 00 00 00 00 00 00 00 00 ....i686........
0000:0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0140 00 00 00 00 00 28 6e 6f 6e 65 29 00 00 00 00 00 .....(none).....
0000:0150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0000:0180 00 00 00 00 00 00 ......
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 打开文件:系统调用 5 (open) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,5 ; 系统调用‘open’
mov ebx,file ; 指向要打开文件的ascii文件名(null结尾)的偏移地址
mov ecx,2 ; 文件读写访问标志
mov edx,7777h ; 完全许可标志
int 80h ; 80h中断
mov dword [fd],eax ; 保存系统调用返回的描述符
file db "FiLe",0
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 打开目录:系统调用 5 (open) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,5 ; 系统调用‘open’
mov ebx,direct ; 指向要打开目录的ascii名(null结尾)的偏移地址
mov ecx,200000h ; 目录标志
mov edx,7777h ; 完全许可标志
int 80h ; 80h中断
mov dword [fd],eax ; 保存系统调用返回的描述符
direct db 'DiReCtOrY',0
; 注意:
; 打开当前文件 :open '.',0
; 打开父目录 :open '..',0
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 关闭文件描述符(打开的文件):系统调用 6 (close) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,6 ; 系统调用‘close’
mov ebx,dword [fd] ; 系统调用‘open’返回的文件描述符
int 80h
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 创建新文件:系统调用 14 (mknod) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,14 ; 系统调用‘mknod’
mov ebx,filename ; 要创建文件的文件名的偏移地址
mov ecx,7777h ; 文件许可标志
mov edx,0 ; 忽略
int 80h
filename db "file_test.txt",0
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 改变文件指针:系统系统 19 (lseek) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,19 ; 系统调用‘lseek’
mov ebx,dword[fd] ; 系统调用‘open’返回的文件描述符
mov ecx,15 ; 移动指针的字节数
mov edx,0 ; 如何移动:0 = 从文件头
int 80h
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 写(已打开)文件:系统调用 4 (write) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,4 ; 系统调用‘write’
mov ebx,dword[fd] ; 系统调用‘open’返回的文件描述符
mov ecx,buffer ; 要写入文件的数据
mov ecx,10 ; 真正写入的字节数
int 80h
buffer db "WRITE THIS"
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 从(已打开)文件中读:系统调用 3 (read) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,3 ; 系统调用‘read’
mov ebx,dword[fd] ; 系统调用‘open’返回的文件描述符
mov ecx,buffer ; 接受读取数据的缓冲
mov edx,16 ; 要读取数据的字节数
int 80h
buffer resb 16
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 在目录中搜索文件:系统调用141 (getdents) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
;---------------------------------------------------------------------+
; 这样便以文件 ; ;
; ; ;
; nasm -f elf test.asm ; ;
; ld -o test test.o ; ;
;- - - - - - - - - - - - - ;
;
BITS32 ;
section .text ;
global _start ;
_start ;
;----------------------------------+ ;
; 艘县我们要打开一个目录 ; ;
;----------------------------------+ ;
;
mov eax,5 ; 系统调用‘open’ ;
mov ebx,dir ; 要打开目录的ascii名(null结尾)的偏移地址 ;
mov ecx,0200000h ; 目录标志 ;
mov edx,7777h ; 完全许可标志 ;
int 80h ; 80h中断 ;
; 应该检查错误 ;
mov dword[fd],eax ; 保存系统调用返回的描述符 ;
;
call getdents ; 在打开目录中读文件 ;
call getdents ; 再读一次 ;
call getdents ; 再读一次 ;
;
mov eax,1 ; 系统调用‘exit’ ;
int 80h ;
;
getdents: ;
mov eax,141 ; 系统调用‘getdents’ ;
mov ebx,dword[fd] ; 文件描述符 ;
mov ecx,dirent ; 指向‘dirent’结构 ;
mov edx,600 ; 缓冲大小 ;
int 80h ;
ret ;
;
section .data ;
;
dir db '.',0 ;
;
section .bss ;
;
fd resd 1 ;
dirent resb 600 ;
;--------------------------------------------------+------------------+
; 注意: |
; |
; 打开当前文件 :open '.',0 |
; 打开父目录 :open '..',0 |
;--------------------------------------------------+
记住:
dirent: ; 结构最大:266字节
d_ino resd 1 ; inode号
d_off resd 1 ; 目录文件偏移(目录文件开头的偏移,
; 决定了文件的入口)
d_reclen resw 1 ; 记录长度
d_ino resb 256 ; 文件名,null结尾
所以我们要读3次这个打开的目录,我们看一看在我们的缓冲中返回了什么:(我喜欢16进
制表示!!!)
----------+-------------------------------------------------+------------------+
offset | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | view in ascii |
----------+-------------------------------------------------+------------------+
0000:0000 | 50 c3 10 00 0c 00 00 00 0c 00 2e 00 63 81 28 00 | P+..........c.(. |
0000:0010 | 18 00 00 00 10 00 2e 2e 00 00 00 00 7d 83 34 00 | ............}.4. |
0000:0020 | 2c 00 00 00 18 00 53 75 62 44 69 72 65 63 74 6f | ,.....SubDirecto |
0000:0030 | 72 79 00 00 ef c3 10 00 38 00 00 00 10 00 66 69 | ry..?..8.....fi |
0000:0040 | 6c 65 00 00 f0 c3 10 00 00 10 00 00 18 00 73 74 | le..?........st |
0000:0050 | 75 70 69 64 66 69 6c 65 32 00 00 00 00 00 00 00 | upidfile2....... |
0000:0060 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ |
----------+-------------------------------------------------+------------------+
_____反转字节_____
/ /
/ /
,----'----, ,----'----,
offset 00 to 03 : 50 c3 10 00 : d_ino = 0010c350 (inode号)
offset 04 to 07 : 0c 00 00 00 : d_off = 0000000c (目录文件偏移)
offset 08 to 09 : 0c 00 : d_reclen = 000c (记录长度)
offset 0a to 0b : 2e 00 : d_ino = ".",0 (文件名)
第一个dirent结构的长度为0ch,所以我们在偏移0+0ch处(记录的入口+长度)找下一个
结构
offset 0c to 0f : 63 81 28 00 : d_ino = 00288163 (inode号)
offset 10 to 13 : 18 00 00 00 : d_off = 00000018 (目录文件偏移)
offset 14 to 15 : 10 00 : d_reclen = 0010 (记录长度)
offset 16 to 18 : 2e 2e 00 : d_ino = "..",0 (文件名)
第二个dirent结构的长度为10h, 所以我们在偏移0c+10=1c处(dirent#2的长度记录的+
记录的长度)找下一个结构
offset 1c to 1f : 7d 83 34 00 : d_ino = 00288163 (inode号)
offset 20 to 23 : 2c 00 00 00 : d_off = 0000002c (目录文件偏移)
offset 24 to 25 : 18 00 : d_reclen = 0018 (记录长度)
offset 26 to 32 : 5375624469726563746f727900 : d_ino = 'SubDirectory',0 (文件名)
第三个dirent结构的长度为18h, 所以我们在偏移18+1c=34处(dirent#2的长度记录的+
记录的长度)找下一个结构
offset 34 to 37 : ef c3 10 00 : d_ino = 0010c3ef (inode号)
offset 38 to 3b : 38 00 00 00 : d_off = 00000038 (目录文件偏移)
offset 3c to 3d : 10 00 : d_reclen = 0010 (记录长度)
offset 3e to 32 : 66696c6500 : d_ino = 'file',0 (文件名)
所以我们得到了这些文件:.
..
SubDirectory
file
stupidfile2
当调用getdents,dirent结构像DOS或者Win32下一样返回“.”“..”。
可以在 Metaphase Issue #2 中读到‘using the getdents(2) Linux syscall to read
directory entries from disk.’教程。
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 在目录中搜索文件:系统调用89 (readdir) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
一些教程和文档可以告诉你什么是readdir系统调用以及如何如何使用getdents系统调用
替代readdir(在我安装的linux中,两者都可以使用)
readdir和getdents的区别在于readdir只返回一个dirent (不是像getdents那样返回几
个 dirent!): 使用readdir, 就像DOS下的DTA和Win32下使用 FindNext API 返回的
Win32_Data结构。
.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
' '
' 读键盘:系统调用 89 (read) '
'._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.'
mov eax,3 ; 系统调用‘read’
mov ebx,0 ; 键盘的文件描述符
mov ecx,buffer ; 接收按键的缓冲
mov edx,1 ; 读入输入个数
int 80h ; 80h中断
.-, ,-.
/ 错误码 /
'._____.--._____.'
在内核2.4中:0fffff000h < eax <= 0ffffffffh ---> 错误码
错误码的值是 int 80h 指令执行后 eax 中的返回值(这里十进制: -1等于十六进制:
0ffffffffh)
在以后的内核版本中或者在其他的linux操作系统或者FreeBSD中错误码可能不同
+--------------+-----+-----------------------------------------------------+
| ERROR |value| DESCRIPTION |
| NAME |(DEC)| |
+--------------+-----+-----------------------------------------------------+
| E2BIG | -7 | 参数列表(或向量)过长 |
+--------------+-----+-----------------------------------------------------+
| EACCESS | | 没有足够权限完成操作 |
+--------------+-----+-----------------------------------------------------+
| EAGAIN | -11 | 再试一次 |
+--------------+-----+-----------------------------------------------------+
| EBADF | -9 | 错误的文件描述符 |
+--------------+-----+-----------------------------------------------------+
| EBADFD | -77 | 文件描述符失效 |
+--------------+-----+-----------------------------------------------------+
| EEXIST | -17 | 企图创建已存在文件 |
+--------------+-----+-----------------------------------------------------+
| EFAULT | -14 | 引用无效地址 |
+--------------+-----+-----------------------------------------------------+
| EINTR | -4 | 系统调用被信号打断 |
+--------------+-----+-----------------------------------------------------+
| EINVAL | -22 | 无效参数 |
+--------------+-----+-----------------------------------------------------+
| EIO | -5 | 发生I/O错误 |
+--------------+-----+-----------------------------------------------------+
| EISDIR | -21 | 文件描述符制定一个目录 |
+--------------+-----+-----------------------------------------------------+
| ELIBBAD | -80 | 访问无效库 |
+--------------+-----+-----------------------------------------------------+
| ELOOP | -40 | 发现过多连接或环路 |
+--------------+-----+-----------------------------------------------------+
| ENAMETOOLONG | -36 | 文件名过长 |
+--------------+-----+-----------------------------------------------------+
| ENFILE | -23 | 内核存在过多打开文件 |
+--------------+-----+-----------------------------------------------------+
| ENODEV | -19 | 无次设备 |
+--------------+-----+-----------------------------------------------------+
| ENOENT | -2 | 路径未找到 |
+--------------+-----+-----------------------------------------------------+
| ENOEXEC | -8 | Exec格式错误 |
+--------------+-----+-----------------------------------------------------+
| ENOMEM | -12 | 没有足够的内核内存完成调用 |
+--------------+-----+-----------------------------------------------------+
| ENOSPC | -28 | 设备没有足够空间完成调用 |
+--------------+-----+-----------------------------------------------------+
| ENOTDIR | -20 | (NODIR?)应该为目录的路径不为目录 |
+--------------+-----+-----------------------------------------------------+
| ENOTEMPTY | -39 | 目标目录非空 |
+--------------+-----+-----------------------------------------------------+
| ENXIO | -6 | 找不到设备或地址 |
+--------------+-----+-----------------------------------------------------+
| EPERM | -1 | 操作不能允许 |
+--------------+-----+-----------------------------------------------------+
| EPIPE | -32 | 管道损坏 |
+--------------+-----+-----------------------------------------------------+
| EROFS | -30 | 企图修改只读文件系统 |
+--------------+-----+-----------------------------------------------------+
| ESPIPE | -29 | (ISPIPE?)非法搜索 |
+--------------+-----+-----------------------------------------------------+
| ETXTBSY | -26 | 文本文件忙 |
+--------------+-----+-----------------------------------------------------+
| EXDEV | -18 | 企图创建交叉设备连接 |
+--------------+-----+-----------------------------------------------------+
,...... .-, ,-.
.' '. / 内容 提要 /
. . ' '
: .-. .-. : '.__.---.__.'
: : _ : : _ : :
: :(_); :(_); : * 介绍
: , '. : * ELF文件头
: , :; : * 程序头
: .-___- . : * 节表头
: '.___.' '. * 注释了的例子
;. ..: * ‘注释了的例子’的代码
:... ....: * ‘注释了的例子’的代码的十六
;:::: .:::::: 进制表示
:::::; :::::::
;::::; :::::::
.::::; '::::::.
.;::::: ::::;.
;::::; ELF文件 ':::;
.:;; '::,
': 格式 ':
: :
: LiTlLe VxW 编写 :
'. .
'. ;
'. .--..___..--'. ;
, . , ,
._._._' ._._._.
为29A编写
______________
_ _ __/ /__ _ _
_ _ __ 介绍 __ _ _
/______________/
ELF文件结构是这样的:
+-------------------+
| ELF文件头 |
| |
+-------------------+
| 程序头 |
| (c0h字节) |
+-------------------+
| 程序节 #1 |
+-------------------+
| 程序节 #2 |
+-------------------+
. . . . . .
. . . . . .
. . . . . .
+-------------------+
| 程序节 #n |
+-------------------+
| 节表头 |
| (n*20h字节) |
+-------------------+
____________
_ _ __/ /__ _ _
_ _ __ ELF 文件头 __ _ _
/____________/
+==========================================================================+
| ELF 文件头 |
+=====+===========+=============+==========================================+
|偏移 | 长度 | 引用 | 描 述
+-----+-----------+-------------+-------------------------------------------
| 0 | 10h bytes | e_ident | ‘ELF’标识和其他值
| 10 | word | e_type | 文件类型
| 12 | word | e_machine | 运行这个文件的机器的类型
| 14 | dword | e_version | ELF文件头版本。通常为1
| 18 | dword | e_entry | 入口的虚拟地址
| 1c | dword | e_phoff | 程序头的偏移
| 20 | dword | e_shoff | 节表头的偏移
| 24 | dword | e_flags | 标识
| 28 | word | e_ehsize | ELF文件头长度
| 2A | word | e_phentsize | 程序头重一个入口的长度
| 2C | word | e_phnum | 程序头中入口的个数
| 2E | word | e_shentsize | 节表头中一个入口的长度
| 30 | word | e_shunum | 节表头中入口的个数
| 32 | word | e_shstrndx | 名称字符串节的入口个数
+-----+-----------+-------------+-------------------------------------------
elf_header给出了其他头的信息,包括如何执行程序文件以及其他信息(结构,...)
/ e_ident:‘ELF’标识(464c457f)和其他一些值:
'-------
前10h字节的结构:
+-----+---------------+---------+------------------------------------------+
|偏移 | 名称 | 长度 | 描 述 |
+-----+---------------+---------+------------------------------------------+
| 0 | e_ident | dword | ELF标识 |
| 4 | EI_CLASS | byte | 文件类标识或者字长 |
| 5 | EI_DATA | byte | 数据编码 |
| 6 | EI_VERSION | byte | 文件版本 |
| 7 | EI_OSABI | byte | 操作系统/ABI表识 |
| 8 | EI_ABIVERSION | byte | ABI版本 |
| 9 | EI_PAD | 8 bytes | 未使用/保留 |
| 0F | EI_NIDENT | byte | e_ident结构大小??? |
+-----+---------------+---------+------------------------------------------+
e_ident: ‘ELF’标识(464c457f)
EI_CLASS: 文件类型, 或字长定义了对象文件容器使用的数据结构所使用的基本
类型
ELFCLASSNONE 0 无效类型
ELFCLASS32 1 32位对象 <--- 32位结构
ELFCLASS64 2 64位对象 <--- 64位结构
EI_DATA: 指定对象文件容器使用的数据结构和对象文件节中包含的数据的编码
ELFDATANONE 0 无效数据编码
ELFDATA2LSB 1 制定了2的补数值,最小有意义的字节占最低地址
ELFDATA2MSB 2 制定了2的补数值,最大有意义的字节占最低地址
EI_VERSION: ELF文件头版本
EI_OSABI: 标识目标对象的操作系统和ABI
EI_ABIVERSION: 标识目标对象的ABI版本
EI_PAD: 未使用/保留
/ e_type: 决定文件的类型。
'------
值 含义
0 无此类型
1 可重定位文件
2 可执行文件
3 可共享目标文件
4 Core文件
fe00 操作系统指定
feff 操作系统指定
ff00 处理器指定
ffff 处理器指定
/ e_machine: 运行次文件的机器类型
'---------
值 含义
(十进制?)
0 No machine
1 AT&T WE 32100
2 SPARC
3 Intel 80386
4 Motorola 68000
5 Motorola 88000
7 Intel 80860
8 MIPS I Architecture
9 IBM System/370 Processor
10 MIPS RS3000 Little-endian
15 Hewlett-Packard PA-RISC
17 Fujitsu VPP500
18 Enhanced instruction set SPARC
19 Intel 80960
20 PowerPC
21 64-bit PowerPC
36 NEC V800
37 Fujitsu FR20
38 TRW RH-32
39 Motorola RCE
40 Advanced RISC Machines ARM
41 Digital Alpha
42 Hitachi SH
43 SPARC Version 9
44 Siemens Tricore embedded processor
45 Argonaut RISC Core, Argonaut Technologies Inc.
46 Hitachi H8/300
47 Hitachi H8/300H
48 Hitachi H8S
49 Hitachi H8/500
50 Itanium-based platform
51 Stanford MIPS-X
52 Motorola ColdFire
53 Motorola M68HC12
54 Fujitsu MMA Multimedia Accelerator
55 Siemens PCP
56 Sony nCPU embedded RISC processor
57 Denso NDR1 microprocessor
58 Motorola Star*Core processor
59 Toyota ME16 processor
60 STMicroelectronics ST100 processor
61 Advanced Logic Corp. TinyJ embedded processor family
66 Siemens FX66 microcontroller
67 STMicroelectronics ST9+ 8/16 bit microcontroller
68 STMicroelectronics ST7 8-bit microcontroller
69 Motorola MC68HC16 Microcontroller
70 Motorola MC68HC11 Microcontroller
71 Motorola MC68HC08 Microcontroller
72 Motorola MC68HC05 Microcontroller
73 Silicon Graphics SVx
74 STMicroelectronics ST19 8-bit microcontroller
75 Digital VAX
76 Axis Communications 32-bit embedded processor
77 Infineon Technologies 32-bit embedded processor
78 Element 14 64-bit DSP Processor
79 LSI Logic 16-bit DSP Processor
80 Donald Knuth's educational 64-bit processor
81 Harvard University machine-independent object files
82 SiTera Prism
(译注:我想上面这个表不需要翻译了吧 :P)
/ e_version: ELF文件头版本。
'-------
值 含义
0 无效版本
1 当前版本
/ e_entry: 入口的虚拟地址,是程序入口处(代码的开始)的偏移。
'-------
/ e_phoff: 程序头的偏移,如果程序没有程序头表,e_phoff为0。
'-------
/ e_shoff: 节表头的偏移。如果程序没有节表,e_shoff为0。
'-------
/ e_flags: 与文件相关的处理器标志。
'-------
/ e_ehsize: ELF文件头的字节数
'--------
/ e_phentsize: 程序头表中一个入口的大小,所有入口大小相同。
'-----------
/ e_phnum: 程序头表中入口的个数,如果程序没有程序头表,e_phnum为0。
'-------
/ e_shentsize: 节表中一个入口的大小,所有入口大小相同。
'-----------
/ e_shunum: 节表中入口的个数, 如果程序没有节表,e_shnum为0。
'--------
/ e_shstrndx: 这个字段保存着跟节名字相关的节的入口索引, 如果这个文件没有节
'---------- 名表,这个字段的值为SHN_UNDEF。
________________
_ _ __/ /__ _ _
_ _ __ 程序头 __ _ _
/________________/
程序头是一个用来为系统描述如何为程序的执行做准备的结构。
文件通过ELF文件头中的‘e_phentsize’和‘e_phnum’字段指定自己程序头的大小。
注意ELF头中的EI_CLASS,它有32位和64位的模式:
+============================================================================+
| 程序头表(32位) |
+=======+=========+==========+===============================================+
| 偏移 | 长度 | 名称 | 含 义 |
+-------+---------+----------+-----------------------------------------------+
| 00 | dword | p_type | 段类型 |
| 04 | dword | p_offset | 段开始处的物理偏移(译注:在磁盘文件上的偏移)|
| 08 | dword | p_vaddr | 内存中的虚拟地址 |
| 0c | dword | p_paddr | 物理地址 |
| 10 | dword | p_filesz | 从偏移处读取的数据长度 |
| 14 | dword | p_memsz | 内存中段的长度 |
| 18 | dword | p_flags | 段标志(读、写、可执行标志) |
| 1c | dword | p_align | 对齐粒度 |
+-------+---------+----------+-----------------------------------------------+
+====================================================================+
| 程序头表(64位) |
+=========+==========+===============================================+
| 长度 | 名称 | 含 义 |
+=========+==========+===============================================+
| dword | p_type | 段类型 |
| dword | p_flags | 段标志(读、写、可执行标志) |
| 8 bytes | p_offset | 段开始处的物理偏移(译注:在磁盘文件上的偏移 |
| 8 bytes | p_vaddr | 内存中的虚拟地址 |
| 8 bytes | p_paddr | 物理地址 |
| 8 bytes | p_filesz | 从偏移处读取的数据长度 |
| 8 bytes | p_memsz | 内存中段的长度 |
| 8 bytes | p_align | 对齐粒度 |
+---------+----------+-----------------------------------------------+
/ p_type: 段类型
'------
PT_NULL 0 未使用
PT_LOAD 1 见下
PT_DYNAMIC 2 动态链接信息
PT_INTERP 3 见下
PT_NOTE 4 辅助信息的位置和长度
PT_SHLIB 5 保留
PT_PHDR 6 见下
PT_LOOS 60000000 操作系统保留
PT_HIOS 6fffffff 操作系统保留
PT_LOPROC 70000000 处理器保留
PT_HIPROC 7fffffff 处理器保留
PT_LOAD: 可加载段,由p_filesz和p_memsz描述。文件中的内容被映射到内存段
的起始处。如果段的内存长度(p_memsz)大于文件长度,那么‘其余’
的内容被置为0并跟在段的初始化区域之后。文件长度不可能大于内存
中文件长度。可加载段在程序的入口根据p_vadder的值按升序排列。
PT_INTERP: 以解释的方式被调用的null结尾的路径名的位置和长度。 这个段只对
可执行文件有效。
PT_PHDR: 如果存在,则同时指定文件头表的在文件和内存中的位置和长度。 只
有程序头表程序在内存中映像的一部分时发生。 如果不存在,它必须
在任何一个可加载段之前。
/ p_offset: 文件中段内第一个字节的虚拟地址
'--------
/ p_vaddr: 内存中段内第一个字节的虚拟地址
'-------
/ p_addr: 物理地址(如果相关,否则等于p_vaddr)
'------
/ p_filesz: 段在文件中的字节数;可能为0
'--------
/ p_memsz: 段在内存中的字节数;可能为0
'-------
/ p_flags: 许可标志:
'-------
名称 值 含义
PF_X 1 可执行
PF_W 2 可写
PF_R 4 可读
PF_MASKOS 0ff00000 未指定
PF_MASKPROC f0000000 未指定
/ p_align: 对齐粒度
'------- 0和1表示不需要对齐, 否则,p_align应该是一个为2的幂的正整数,
并且p_vaddr应该等于p_offset,模p_align。
________________
_ _ __/ /__ _ _
_ _ __ 节头 __ _ _
/________________/
ELF文件中的所有节都可以通过节表找到。节头和程序头相似。每一个入口关联文件中的
一个节。也许我在这里的描述会有错误 --> 节头是可选的。
+---------------------------------------------------------------------------+
| 节头(32位模式) |
+-----+--------------+-------+----------------------------------------------+
|偏移 | 名称 | 长度 | 描 述 |
+-----+--------------+-------+----------------------------------------------+
| 00 | sh_name | dword | 指向节的ascii名称 |
| 04 | sh_type | dword | 节类型 |
| 08 | sh_flags | dword | 标志 |
| 0c | sh_addr | dword | 虚拟地址 |
| 10 | sh_offset | dword | 物理偏移 |
| 14 | sh_size | dword | 尺寸 |
| 18 | sh_link | dword | 其值依赖于节类型 |
| 1c | sh_info | dword | 其值依赖于节类型 |
| 20 | sh_addralign | dword | 对齐 |
| 24 | sh_entsize | dword | 当节中包含固定长度入口时使用 |
+-----+--------------+-------+----------------------------------------------+
+-----------------------------------------------------------------------------+
| 节头(32位模式) |
+-----+--------------+---------+----------------------------------------------+
|偏移 | 名称 | 长度 | 描 述 |
+-----+--------------+---------+----------------------------------------------+
| 00 | sh_name | dword | 指向节的ascii名称 |
| 04 | sh_type | dword | 节类型 |
| 08 | sh_flags | dword | 标志 |
| 0c | sh_addr | 8 bytes | 虚拟地址 |
| 14 | sh_offset | 8 bytes | 物理偏移 |
| 1c | sh_size | dword | 尺寸 |
| 20 | sh_link | dword | 其值依赖于节类型 |
| 24 | sh_info | dword | 其值依赖于节类型 |
| 28 | sh_addralign | dword | 对齐 |
| 3c | sh_entsize | dword | 当节中包含固定长度入口时使用 |
+-----+--------------+---------+----------------------------------------------+
/ sh_name: 节的名称。它的值为一个在节名称表中的索引。
'-------
/ sh_type: 节的类型
'-------
名称 值 描述
SHT_NULL 0 标志节头为非活动的。
SHT_PROGBITS 1 这个节包含程序定义的信息
SHT_SYMTAB 2 这个节包含符号表,为链接提供
SHT_STRTAB 3 这个节包含字符串表
SHT_RELA 4 这个节包含字符串表(译注: 应为:这个节包含具有明
确加数的重定位入口)
SHT_HASH 5 这个节包含符号哈希表
SHT_DYNAMIC 6 这个节包含动态链接信息
SHT_NOTE 7 这个节包含以某种方式标志这个文件的信息
SHT_NOBITS 8 这个节不占用空间
SHT_REL 9 这个节不包含具有明确加数的重定位入口
SHT_SHLIB a 保留
SHT_DYNSYM b 包含动态链接符号的最小集合
SHT_INIT_ARRAY e 包含指向初始化例程的指针数组
SHT_FINI_ARRAY f 包含指向结束例程的指针数组
SHT_PREINIT_ARRAY 10 包含指向在其他初始化例程前被调用的例程的指针数组
SHT_LOOS 60000000 操作系统保留
SHT_HIOS 6fffffff 操作系统保留
SHT_LOPROC 70000000 处理器保留
SHT_HIPROC 7fffffff 处理器保留
SHT_LOUSER 80000000 应用程序保留的索引下界
SHT_HIUSER ffffffff 应用程序保留的索引上界
/ sh_flags: 用来描述复合属性的1位标志
'--------
/ sh_addr: 如果这个节被映射到进程的内存空间中, 这个字段指定了这个节的第
'------- 一个字节的地址。否则,这个字段为0。
/ sh_offset: 从文件开头到节的第一个字节的偏移
'---------
/ sh_size: 节的字节数
'-------
/ sh_link: 这个字段包含一个节头表索引链接,这个链接的含义依节类型而定。
'-------
/ sh_info: 这个字段包含了其他一些信息,它的解释依节类型而定。
'-------
/ sh_addralign:有些节强制地址对齐。比如,如果一个节包含一个双字, 系统必须保
'------------ 证整个节是按双字对齐的。这个值必须是与0适应的,模sh_addralign
的值。
/ sh_entsize: 一些节包含一个固定长度入口的表,比如符号表。 对于这样一个节,
'---------- 这个字段指定了每个入口的字节数。
就像我所说的,我不会列举一大堆的标志描述,那是在浪费你的硬盘空间。 你可以在一
份 -好的- ELF文档中找得到(4.3中的urlz)。
这些都是文档。这是在Linux的世界中。让我们来看一写代码吧!!!
_____________________________________________________________________________
___________________
_ _ __/ /__ _ _
_ _ __ 注释了的例子 __ _ _
/___________________/
我们来看一看这份教程的最后一部分,你可以从中找到:
‘注释了的例子’的代码
‘注释了的例子’的代码的十六进制表示
我将用这些来揭开ELF文件的神秘面纱。
让我们来看一下我们的hello程序的十六进制表示:
+----------+-------------------------------------------------+------------------+
| 偏移 | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ascci表示 |
+----------+-------------------------------------------------+------------------+
|0000:0000 | 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 | .ELF............ |
|0000:0010 | 02 00 03 00 01 00 00 00 80 80 04 08 34 00 00 00 | ............4... |
|0000:0020 | 10 01 00 00 00 00 00 00 34 00 20 00 02 00 28 00 | ........4. ...(. |
|0000:0030 | 08 00 05 00 | ...... |
+----------+-------------------------------------------------+------------------+
这是ELF文件头,所有的ELF文件都从这样一个文件头开始:
at offset 00h : ‘ELF’标识 :464c457f
at offset 04h : 数据结构类型 :01h ---> 32位结构
at offset 10h : 文件类型 :0002h ---> 可执行文件
at offset 12h : 机器标志, 0003h :Intel 80386
at offset 14h : ELF文件头版本 :00000001h
. . 18h : 入口虚拟地址 :08048080h
. . 1ch : 程序头偏移 :00000034h
. . 20h : 节头偏移 :00000110h
. . 28h : ELF文件头长度 :34h字节
. . 2ah : 程序头表中一个入口的长度 :0020h
. . 2ch : 程序头中入口的个数 :0002h
. . 2eh : 节表头中一个入口的长度 :0028h
. . 30h : 节表头中入口的个数 :0008h
. . 32h : 名称字符串节的入口个数 :0005h
注意:程序头表中的入口是对ELF文件一个节的描述(程序头结构)。
我们在ELF文件头中可以看到程序头的偏移:00000034h,所以我们从偏移34h出开
始!!!
(EI_CLASS指定了这是一个32位的结构!)
+----------+-------------------------------------------------+------------------+
| 偏移 | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ascci表示 |
+----------+-------------------------------------------------+------------------+
|0000:0030 | 01 00 00 00 00 00 00 00 00 80 04 08 | ............ |
|0000:0040 | 00 80 04 08 a2 00 00 00 a2 00 00 00 05 00 00 00 | ....?..?...... |
|0000:0050 | 00 10 00 00 01 00 00 00 a4 00 00 00 a4 90 04 08 | ........?...?... |
|0000:0060 | a4 90 04 08 18 00 00 00 18 00 00 00 06 00 00 00 | ?............... |
|0000:0070 | 00 10 00 00 | .... |
+----------+-------------------------------------------------+------------------+
ELF文件头中的e_phnum指定了程序头中入口(节)的个数:这里有2个节...
节 #1 :
at offset 34h : 段类型 : 00000001h ----> 可加载段
at offset 38h : 物理偏移 : 00000000h
. . 3ch : 虚拟地址 : 08048000h
. . 40h : 物理地址 : 08048000h
. . 44h : 物理长度 : 000000a2h
. . 48h : 虚拟长度 : 00000002h
. . 4ch : 标志 : 00000005h (可读/可执行)
. . 50h : 对齐 : 00001000h
节 #2 :
at offset 54h : 段类型 : 00000001h ----> loadable segment
at offset 58h : 物理偏移 : 000000a4h
. . 5ch : 虚拟地址 : 080490a4h
. . 60h : 物理地址 : 080490a4h
. . 64h : 物理长度 : 00000018h
. . 68h : 虚拟长度 : 00000018h
. . 6ch : 标志 : 00000006h (可读/可写/可执行)
. . 70h : 对齐 : 00001000h
这里是节表:有8个入口。这些‘节表’结构提供了关于‘程序头’中描述的节和文件中
的其他节的信息。
+----------+-------------------------------------------------+------------------+
| 偏移 | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ascci表示 |
+----------+-------------------------------------------------+------------------+
|0000:0110 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ |
|0000:0120 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ |
|0000:0130 | 00 00 00 00 00 00 00 00 1b 00 00 00 01 00 00 00 | ................ |
|0000:0140 | 06 00 00 00 80 80 04 08 80 00 00 00 22 00 00 00 | ............"... |
|0000:0150 | 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 | ................ |
|0000:0160 | 21 00 00 00 01 00 00 00 03 00 00 00 a4 90 04 08 | !...........?... |
|0000:0170 | a4 00 00 00 18 00 00 00 00 00 00 00 00 00 00 00 | ?............... |
|0000:0180 | 04 00 00 00 00 00 00 00 27 00 00 00 08 00 00 00 | ........'....... |
|0000:0190 | 03 00 00 00 bc 90 04 08 bc 00 00 00 00 00 00 00 | ....?...?....... |
|0000:01a0 | 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 | ................ |
|0000:01b0 | 2c 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 | ,............... |
|0000:01c0 | bc 00 00 00 1f 00 00 00 00 00 00 00 00 00 00 00 | ?............... |
|0000:01d0 | 01 00 00 00 00 00 00 00 11 00 00 00 03 00 00 00 | ................ |
|0000:01e0 | 00 00 00 00 00 00 00 00 db 00 00 00 35 00 00 00 | ........?..5... |
|0000:01f0 | 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................ |
|0000:0200 | 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 | ................ |
|0000:0210 | 50 02 00 00 f0 00 00 00 07 00 00 00 0b 00 00 00 | P...?.......... |
|0000:0220 | 04 00 00 00 10 00 00 00 09 00 00 00 03 00 00 00 | ................ |
|0000:0230 | 00 00 00 00 00 00 00 00 40 03 00 00 39 00 00 00 | ........@...9... |
|0000:0240 | 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................ |
+----------+-------------------------------------------------+------------------+
从偏移 110h 到 137h : 节表中的入口 #1
从偏移 138h 到 15fh : 节表中的入口 #2
从偏移 160h 到 187h : 节表中的入口 #3
从偏移 188h 到 1afh : 节表中的入口 #4
从偏移 1b0h 到 1d7h : 节表中的入口 #5
从偏移 1d8h 到 1ffh : 节表中的入口 #6
从偏移 200h 到 227h : 节表中的入口 #7
从偏移 228h 到 24fh : 节表中的入口 #8
sh_name关联字符串表的起始,可以通过它找到节的ascii名称。 字符串表在偏移340h处
(见入口 #8的描述)
入口 #1:
---------
sh_name : 00h : 指向节名称
sh_type : 00h : 非活动节头
sh_addr : 00h : 虚拟地址
sh_offset : 00h : 物理地址
sh_size : 00h : 长度
入口 #2:
---------
sh_name : 1bh : 指向节名称
sh_type : 01h : 包含程序定义的信息
sh_addr : 08048080h : 虚拟地址
sh_offset : 80h : 物理地址
sh_size : 22h : 长度
入口 #3:
---------
sh_name : 21h : 指向节名称
sh_type : 01h : 包含程序定义的信息
sh_addr : 080490a4h : 虚拟地址
sh_offset : a4h : 物理地址
sh_size : 18h : 长度
入口 #4:
---------
sh_name : 27h : 指向节名称
sh_type : 08h : 不占用空间
sh_addr : 080490bch : 虚拟地址
sh_offset : bch : 物理地址
sh_size : 00h : 长度
入口 #5:
---------
sh_name : 2ch : 指向节名称
sh_type : 01h : 包含程序定义的信息
sh_addr : 00h : 虚拟地址
sh_offset : bch : 物理地址
sh_size : 1fh : 长度
入口 #6:
---------
sh_name : 11h : 指向节名称
sh_type : 03h : 包含程序定义的信息
sh_addr : 00h : 虚拟地址
sh_offset : dbh : 物理地址
sh_size : 35h : 长度
入口 #7:
---------
sh_name : 01h : 指向节名称
sh_type : 02h : 这个节包含符号表,为链接提供
sh_addr : 00h : 虚拟地址
sh_offset : 250h : 物理地址
sh_size : f0h : 长度
入口 #8:
---------
sh_name : 09h : 指向节名称
sh_type : 03h : 包含字符串表
sh_addr : 00h : 虚拟地址
sh_offset : 340h : 物理地址
sh_size : 39h : 长度
______________________________
_ _ __/ /__ _ _
_ _ __ ‘注释了的例子’的代码 __ _ _
/______________________________/
;----------------------------------------------------.
; 这样编译这个文件: ; ;
; ; ;
; nasm -f elf hello.asm ; ;
; ld -o hello hello.o ; ;
;- - - - - - - - - - - - - ;
;
BITS32 ;
section .text ; 代码节 ;
global _start ; <---. ;
_start: ; <----/__为nasm ;
mov eax,4 ; ‘写’系统调用 ;
mov ebx,1 ; 屏幕的文件描述符 ;
mov ecx,message ; 写缓冲的偏移 ;
mov edx,23 ; 要写的字节数 ;
int 80h ; 80h中断 ;
;
mov eax,1 ; ‘结束’系统调用 ;
mov ebx,0 ; 结束码 ;
int 80h ; 80h中断 ;
;
section .data ; 数据节 ;
;
message db "hello LINUX world !!!",13,10,0 ;
;----------------------------------------------------'
______________________________________________
_ _ __/ /__ _ _
_ _ __ ‘注释了的例子’的代码的十六进制表示 __ _ _
/______________________________________________/
+----------+-------------------------------------------------+------------------+
| 偏移 | 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ascci表示 |
+----------+-------------------------------------------------+------------------+
|0000:0000 | 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 | .ELF............ | <-. <--------+
|0000:0010 | 02 00 03 00 01 00 00 00 80 80 04 08 34 00 00 00 | ............4... | /_ ELF文件 |
|0000:0020 | 10 01 00 00 00 00 00 00 34 00 20 00 02 00 28 00 | ........4. ...(. | / 头 |
|0000:0030 | 08 00 05 00 01 00 00 00 00 00 00 00 00 80 04 08 | ................ | <-< (3ch) |
|0000:0040 | 00 80 04 08 a2 00 00 00 a2 00 00 00 05 00 00 00 | ....?..?...... | / |
|0000:0050 | 00 10 00 00 01 00 00 00 a4 00 00 00 a4 90 04 08 | ........?...?... | > 程序头 +- 节 #1
|0000:0060 | a4 90 04 08 18 00 00 00 18 00 00 00 06 00 00 00 | ?............... | / (73h)|
|0000:0070 | 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ | <-' |
|0000:0080 | b8 04 00 00 00 bb 01 00 00 00 b9 a4 90 04 08 ba | ?....?...?...?| |
|0000:0090 | 17 00 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 00 | ....??....?... | |
|0000:00a0 | cd 80 00 00 68 65 6c 6c 6f 20 4c 49 4e 55 58 20 | ?..hello LINUX | <-------------+_ 节 # 2
|0000:00b0 | 77 6f 72 6c 64 20 21 21 21 0d 0a 00 00 54 68 65 | world !!!....The | <-------------'
|0000:00c0 | 20 4e 65 74 77 69 64 65 20 41 73 73 65 6d 62 6c | Netwide Assembl |
|0000:00d0 | 65 72 20 30 2e 39 38 2e 32 32 00 00 2e 73 79 6d | er 0.98.22...sym |
|0000:00e0 | 74 61 62 00 2e 73 74 72 74 61 62 00 2e 73 68 73 | tab..strtab..shs |
|0000:00f0 | 74 72 74 61 62 00 2e 74 65 78 74 00 2e 64 61 74 | trtab..text..dat |
|0000:0100 | 61 00 2e 62 73 73 00 2e 63 6f 6d 6d 65 6e 74 00 | a..bss..comment. |
|0000:0110 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ | <--.
|0000:0120 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ | |
|0000:0130 | 00 00 00 00 00 00 00 00 1b 00 00 00 01 00 00 00 | ................ | |
|0000:0140 | 06 00 00 00 80 80 04 08 80 00 00 00 22 00 00 00 | ............"... | |
|0000:0150 | 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 | ................ | |
|0000:0160 | 21 00 00 00 01 00 00 00 03 00 00 00 a4 90 04 08 | !...........?... | |
|0000:0170 | a4 00 00 00 18 00 00 00 00 00 00 00 00 00 00 00 | ?............... | |
|0000:0180 | 04 00 00 00 00 00 00 00 27 00 00 00 08 00 00 00 | ........'....... | |
|0000:0190 | 03 00 00 00 bc 90 04 08 bc 00 00 00 00 00 00 00 | ....?...?....... | |
|0000:01a0 | 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 | ................ | | 节表
|0000:01b0 | 2c 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 | ,............... | |
|0000:01c0 | bc 00 00 00 1f 00 00 00 00 00 00 00 00 00 00 00 | ?............... | |
|0000:01d0 | 01 00 00 00 00 00 00 00 11 00 00 00 03 00 00 00 | ................ | |
|0000:01e0 | 00 00 00 00 00 00 00 00 db 00 00 00 35 00 00 00 | ........?..5... | |
|0000:01f0 | 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................ | |
|0000:0200 | 01 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 | ................ | |
|0000:0210 | 50 02 00 00 f0 00 00 00 07 00 00 00 0b 00 00 00 | P...?.......... | |
|0000:0220 | 04 00 00 00 10 00 00 00 09 00 00 00 03 00 00 00 | ................ | |
|0000:0230 | 00 00 00 00 00 00 00 00 40 03 00 00 39 00 00 00 | ........@...9... | |
|0000:0240 | 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 | ................ | <--'
|0000:0250 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ | <--.
|0000:0260 | 00 00 00 00 80 80 04 08 00 00 00 00 03 00 01 00 | ................ | |
|0000:0270 | 00 00 00 00 a4 90 04 08 00 00 00 00 03 00 02 00 | ....?........... | |
|0000:0280 | 00 00 00 00 bc 90 04 08 00 00 00 00 03 00 03 00 | ....?........... | |
|0000:0290 | 00 00 00 00 00 00 00 00 00 00 00 00 03 00 04 00 | ................ | |
|0000:02a0 | 00 00 00 00 00 00 00 00 00 00 00 00 03 00 05 00 | ................ | |
|0000:02b0 | 00 00 00 00 00 00 00 00 00 00 00 00 03 00 06 00 | ................ | |_ 符号表
|0000:02c0 | 00 00 00 00 00 00 00 00 00 00 00 00 03 00 07 00 | ................ | |
|0000:02d0 | 01 00 00 00 00 00 00 00 00 00 00 00 04 00 f1 ff | .............. | |
|0000:02e0 | 0b 00 00 00 80 80 04 08 00 00 00 00 00 00 01 00 | ................ | |
|0000:02f0 | 12 00 00 00 a4 90 04 08 00 00 00 00 00 00 02 00 | ....?........... | |
|0000:0300 | 1a 00 00 00 80 80 04 08 00 00 00 00 10 00 01 00 | ................ | |
|0000:0310 | 21 00 00 00 bc 90 04 08 00 00 00 00 11 00 f1 ff | !...?......... | |
|0000:0320 | 2d 00 00 00 bc 90 04 08 00 00 00 00 11 00 f1 ff | -...?......... | |
|0000:0330 | 34 00 00 00 bc 90 04 08 00 00 00 00 11 00 f1 ff | 4...?......... | <--'
|0000:0340 | 00 68 65 6c 6c 6f 2e 61 73 6d 00 42 49 54 53 33 | .hello.asm.BITS3 | <--.
|0000:0350 | 32 00 6d 65 73 73 61 67 65 00 5f 73 74 61 72 74 | 2.message._start | |_ 字符串表
|0000:0360 | 00 5f 5f 62 73 73 5f 73 74 61 72 74 00 5f 65 64 | .__bss_start._ed | |
|0000:0370 | 61 74 61 00 5f 65 6e 64 00 | ata._end. | <--'
+----------+-------------------------------------------------+------------------+
结束语:
我喜欢linux,我也在问我自己为什么要写这样一篇教程, 因为我不想我所喜欢的linux
系统被那些带有破坏性载体弱智病毒感染。我痛恨破坏性病毒的作者! 他们杀死了他们
的病毒的宿主, 他们还会因为里面的版本信息被跟踪和逮捕。只有反病毒公司喜欢这些
人... 如果你说没有毁灭就没有新生,那么我要回答你:“你是阻止创造生命的神么?”
我希望你们喜欢这篇教程...再见。
感谢:
Silvio Cesare 为你的关于linux病毒的教程
mandragore 为你在你的感染linux教程中提供的汇编代码
Lord Julus 我从你那里学到了很多。 通过很棒的VXtazy杂志和你的
dead mailling list... TKT制作完成了么?
VX Heaver站长 为你的站点,一定要保持下去啊...
Gigabyte 所有的毒客都想要见到你。 但是下回别再对世界宣称“
我是个女性,我也写病毒!!!”
IKX成员 你们都睡了么? 大家都在等着你们的杂志快开醒醒!!
新/旧29a成员 你们的文章让我失眠 ;-)
你 为你阅读这篇文章...
糟糕的 VB coder 是的VB病毒不错,因为它们算是个新技术, 但是不要再
写那些垃圾VB病毒了,谢谢汇编吧!!!