[转载]Linux和ELF文件格式汇编语言教程

信息来源:cvc电脑病毒论坛
作者:LiTlLe VxW
翻译:pker / CVC.GB
      =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=====
      =====                                     =====
      ====     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, &#39;退出&#39;                 ;
    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    <----&#39;--- 编译(参阅NASM文档!)
./hello            <-------- 运行hello程序(别忘了‘./’)

如果你初次接触linux:

cd Desktop  <--- 进入‘Desktop’目录(大小写敏感)
cd ..     <--- 返回到前一级目录(父目录)
ls       <--- 在屏幕上当前目录下的文件
./RUNit    <--- 在运行的文件前加./



  .-,                  ,-.
  \      什么是系统调用?    /
  &#39;                   &#39;
   &#39;._________.----._________.&#39;


在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 ---> 错误码



  .-,                    ,-.
  \      什么是文件描述符?     /
  &#39;                      &#39;
   &#39;.___________.----.___________.&#39;


文件描述符(fd)是用来指定一个打开的文件(打开一个文件 ---> 分配一个fd) 的双
字值。有3个默认的打开的文件:

fd=0:标准输入(键盘)
fd=1:标准输出(屏幕)
fd=2:标准错误(在屏幕上输出错误信息)



  .-,              ,-.
  \     什么是inode ?   /
  &#39;                &#39;
   &#39;._______.---._______.&#39;


Linux操作系统(还用unix类操作系统)不处理ascii文件名(搜索、写、读、...)事实
上,每个文件都有它自己的inode号,系统通过inode号(而不是像DOS/win平台中那样使
用文件名)。处理文件。一个目录仅仅是一个包含其他文件信息的文件(文件名、inode
号)。



  .-,             ,-.
  \    系统调用列表    /
  &#39;              &#39;
   &#39;.______.---.______.&#39;


这里是一些有用的系统调用列表:

+===========+==========+================================================+
| 系统调用  |  名称  |              功  能               |
+===========+==========+================================================+
|    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个系统调用。



  .-,              ,-.
  \  一些重要的注意事项  /
  &#39;               &#39;
   &#39;.______.-----._____.&#39;


注意:* 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



  .-,                 ,-.
  \    关于系统调用的描述    /
  &#39;                  &#39;
   &#39;.________.-----.________.&#39;


____________________________________________
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
____________________________________________



  .-,                ,-.
  \      重要数据结构     /
  &#39;                  &#39;
   &#39;.________.----.________.&#39;


          ************
         *** 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



  .-,                  ,-.
  \      重要标志的描述      /
  &#39;._________.--------.________.&#39;


和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 其他人可执行/搜索



  .-,                   ,-.
  \      如何使用系统调用     /
  &#39;.__________.-------._________.&#39;


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                  &#39;
&#39;      在屏幕上打印文本: 系统调用 4 (write)    &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;


我们把屏幕也看作文件,它的文件描述符为1

  mov eax,4        ; 系统调用‘write’
  mov ebx,1        ; 屏幕的文件描述符
  mov ecx,test      ; 写缓冲的偏移地址
  mov edx,7        ; 要写的字节数
int 80h            ; 80h中断

test db "HELLO",13,10,0


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                  &#39;
&#39;     获得关于内核的信息:系统调用 122 (uname)    &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;


  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                     ......


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                          &#39;
&#39;     打开文件:系统调用 5 (open)     &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;


  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


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                &#39;
&#39;        打开目录:系统调用 5 (open)        &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;


  mov eax,5        ; 系统调用‘open’
  mov ebx,direct    ; 指向要打开目录的ascii名(null结尾)的偏移地址
  mov ecx,200000h    ; 目录标志
  mov edx,7777h     ; 完全许可标志
int 80h            ; 80h中断
  mov dword [fd],eax  ; 保存系统调用返回的描述符

direct db &#39;DiReCtOrY&#39;,0

; 注意:
; 打开当前文件  :open &#39;.&#39;,0
; 打开父目录   :open &#39;..&#39;,0


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                             &#39;
&#39;       关闭文件描述符(打开的文件):系统调用 6 (close)        &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;

  mov eax,6        ; 系统调用‘close’
  mov ebx,dword [fd]  ; 系统调用‘open’返回的文件描述符
int 80h


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                &#39;
&#39;       创建新文件:系统调用 14 (mknod)      &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;

  mov eax,14       ; 系统调用‘mknod’
  mov ebx,filename   ; 要创建文件的文件名的偏移地址
  mov ecx,7777h     ; 文件许可标志
  mov edx,0        ; 忽略
int 80h

filename db "file_test.txt",0


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                  &#39;
&#39;        改变文件指针:系统系统 19 (lseek)      &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;

  mov eax,19       ; 系统调用‘lseek’
  mov ebx,dword[fd]  ; 系统调用‘open’返回的文件描述符
  mov ecx,15       ; 移动指针的字节数
  mov edx,0        ; 如何移动:0 = 从文件头
int 80h


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                        &#39;
&#39;         写(已打开)文件:系统调用 4 (write)        &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;

  mov eax,4        ; 系统调用‘write’
  mov ebx,dword[fd]  ; 系统调用‘open’返回的文件描述符
  mov ecx,buffer    ; 要写入文件的数据
  mov ecx,10       ; 真正写入的字节数
int 80h

buffer db "WRITE THIS"


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                        &#39;
&#39;       从(已打开)文件中读:系统调用 3 (read)      &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;

  mov eax,3        ; 系统调用‘read’
  mov ebx,dword[fd]  ; 系统调用‘open’返回的文件描述符
  mov ecx,buffer    ; 接受读取数据的缓冲
  mov edx,16       ; 要读取数据的字节数
int 80h

buffer resb 16


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                        &#39;
&#39;        在目录中搜索文件:系统调用141 (getdents)      &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;


;---------------------------------------------------------------------+
; 这样便以文件        ;                              ;
;                ;                              ;
; 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 &#39;.&#39;,0                                       ;
                                              ;
section .bss                                       ;
                                              ;
fd resd 1                                         ;
dirent resb 600                                     ;
;--------------------------------------------------+------------------+
; 注意:                             |
;                                  |
; 打开当前文件  :open &#39;.&#39;,0                |
; 打开父目录   :open &#39;..&#39;,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 | ................ |
----------+-------------------------------------------------+------------------+


                 _____反转字节_____
                /            \
                /              \
            ,----&#39;----,        ,----&#39;----,

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   = &#39;SubDirectory&#39;,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   = &#39;file&#39;,0 (文件名)

所以我们得到了这些文件:.
                ..
                SubDirectory
                file
                stupidfile2

当调用getdents,dirent结构像DOS或者Win32下一样返回“.”“..”。

可以在 Metaphase Issue #2 中读到‘using the getdents(2) Linux syscall to read
directory entries from disk.’教程。


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                     &#39;
&#39;        在目录中搜索文件:系统调用89 (readdir)     &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;


一些教程和文档可以告诉你什么是readdir系统调用以及如何如何使用getdents系统调用
替代readdir(在我安装的linux中,两者都可以使用)

readdir和getdents的区别在于readdir只返回一个dirent (不是像getdents那样返回几
个 dirent!): 使用readdir, 就像DOS下的DTA和Win32下使用 FindNext API 返回的
Win32_Data结构。


  .-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-.
&#39;                                &#39;
&#39;       读键盘:系统调用 89 (read)      &#39;
&#39;._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.&#39;

  mov eax,3     ; 系统调用‘read’
  mov ebx,0     ; 键盘的文件描述符
  mov ecx,buffer  ; 接收按键的缓冲
  mov edx,1     ; 读入输入个数
int 80h         ; 80h中断



  .-,          ,-.
  \    错误码    /
  &#39;._____.--._____.&#39;


在内核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 | 企图创建交叉设备连接                      |
+--------------+-----+-----------------------------------------------------+




              ,......           .-,        ,-.
            .&#39;     &#39;.          \  内容 提要  /
            .        .          &#39;         &#39;
           :  .-.  .-.  :          &#39;.__.---.__.&#39;
           : : _ : : _ : :
           : :(_); :(_); :          * 介绍
           :   ,  &#39;.   :          * ELF文件头
           :  ,   :;  :          * 程序头
           :  .-___- .  :          * 节表头
            : &#39;.___.&#39;  &#39;.          * 注释了的例子
           ;.        ..:         * ‘注释了的例子’的代码
          :...      ....:        * ‘注释了的例子’的代码的十六
          ;::::      .::::::         进制表示
         :::::;      :::::::
        ;::::;        :::::::
        .::::;        &#39;::::::.
       .;:::::          ::::;.
       ;::::;   ELF文件    &#39;:::;
       .:;;              &#39;::,
        &#39;:      格式       &#39;:
        :               :
        :  LiTlLe VxW 编写  :
        &#39;.              .
         &#39;.             ;
          &#39;. .--..___..--&#39;.  ;
          ,  .       ,   ,
         ._._._&#39;      ._._._.



             为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)和其他一些值:
&#39;-------

               前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:    决定文件的类型。
&#39;------
          值       含义

          0        无此类型
          1        可重定位文件
          2        可执行文件
          3        可共享目标文件
          4        Core文件
          fe00      操作系统指定
          feff      操作系统指定
          ff00      处理器指定
          ffff      处理器指定


\ e_machine:  运行次文件的机器类型
&#39;---------
          值       含义
       (十进制?)

          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&#39;s educational 64-bit processor
          81       Harvard University machine-independent object files
          82       SiTera Prism

(译注:我想上面这个表不需要翻译了吧 :P)

\ e_version:  ELF文件头版本。
&#39;-------
          值       含义

          0        无效版本
          1        当前版本

\ e_entry:    入口的虚拟地址,是程序入口处(代码的开始)的偏移。
&#39;-------

\ e_phoff:    程序头的偏移,如果程序没有程序头表,e_phoff为0。
&#39;-------

\ e_shoff:    节表头的偏移。如果程序没有节表,e_shoff为0。
&#39;-------

\ e_flags:    与文件相关的处理器标志。
&#39;-------

\ e_ehsize:   ELF文件头的字节数
&#39;--------

\ e_phentsize: 程序头表中一个入口的大小,所有入口大小相同。
&#39;-----------

\ e_phnum:    程序头表中入口的个数,如果程序没有程序头表,e_phnum为0。
&#39;-------

\ e_shentsize: 节表中一个入口的大小,所有入口大小相同。
&#39;-----------

\ e_shunum:   节表中入口的个数, 如果程序没有节表,e_shnum为0。
&#39;--------

\ e_shstrndx:  这个字段保存着跟节名字相关的节的入口索引, 如果这个文件没有节
&#39;----------   名表,这个字段的值为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:    段类型
&#39;------
          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:   文件中段内第一个字节的虚拟地址
&#39;--------

\ p_vaddr:    内存中段内第一个字节的虚拟地址
&#39;-------

\ p_addr:    物理地址(如果相关,否则等于p_vaddr)
&#39;------

\ p_filesz:   段在文件中的字节数;可能为0
&#39;--------

\ p_memsz:   段在内存中的字节数;可能为0
&#39;-------

\ p_flags:    许可标志:
&#39;-------
          名称      值    含义

          PF_X      1      可执行
          PF_W      2      可写
          PF_R      4      可读
          PF_MASKOS  0ff00000 未指定
          PF_MASKPROC f0000000 未指定

\ p_align:    对齐粒度
&#39;-------     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:    节的名称。它的值为一个在节名称表中的索引。
&#39;-------

\ sh_type:    节的类型
&#39;-------

名称          值    描述

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位标志
&#39;--------

\ sh_addr:    如果这个节被映射到进程的内存空间中, 这个字段指定了这个节的第
&#39;-------     一个字节的地址。否则,这个字段为0。

\ sh_offset:  从文件开头到节的第一个字节的偏移
&#39;---------

\ sh_size:    节的字节数
&#39;-------

\ sh_link:    这个字段包含一个节头表索引链接,这个链接的含义依节类型而定。
&#39;-------

\ sh_info:    这个字段包含了其他一些信息,它的解释依节类型而定。
&#39;-------

\ sh_addralign:有些节强制地址对齐。比如,如果一个节包含一个双字, 系统必须保
&#39;------------  证整个节是按双字对齐的。这个值必须是与0适应的,模sh_addralign
          的值。

\ sh_entsize:  一些节包含一个固定长度入口的表,比如符号表。 对于这样一个节,
&#39;----------   这个字段指定了每个入口的字节数。

就像我所说的,我不会列举一大堆的标志描述,那是在浪费你的硬盘空间。 你可以在一
份 -好的- ELF文档中找得到(4.3中的urlz)。

这些都是文档。这是在Linux的世界中。让我们来看一写代码吧!!!


_____________________________________________________________________________


              ___________________
         _ _ __/             \__ _ _
         _ _ __   注释了的例子    __ _ _
             \___________________/

我们来看一看这份教程的最后一部分,你可以从中找到:

     ‘注释了的例子’的代码
     ‘注释了的例子’的代码的十六进制表示

我将用这些来揭开ELF文件的神秘面纱。

让我们来看一下我们的hello程序的十六进制表示:

+----------+----------------------------------------------