Linux 一次进程的执行过程及一个ELF文件头问题

当我们在用户层调用execve()函数时,进程触发了一个软中断(software interruptINT 80进行系统调用。下图描写了系统低调用的过程:

blob.png

经过上面的系统调用处理开始执行execve的系统调用sys_execve()。在执行execve前系统主要做的准备工作。

结构体struct linux_binprm用来描述一个进程。该结构体包括了进程内存的所有信息,其定义如下(include/linux/binfmts.h):

/*

 * This structure is used to hold the   arguments that are used when loading binaries.

 */

struct linux_binprm{

       char   buf[BINPRM_BUF_SIZE];

#ifdef CONFIG_MMU

       struct vm_area_struct *vma;

       unsigned long vma_pages;

#else

# define MAX_ARG_PAGES 32

       struct   page *page[MAX_ARG_PAGES];

#endif

       struct mm_struct *mm;

       unsigned   long p; /* current top of mem */

       unsigned   int

              cred_prepared:1,/*   true if creds already prepared (multiple

                             * preps happen for interpreters) */

              cap_effective:1;/*   true if has elevated effective capabilities,

                             * false if not; except for init which   inherits

                             * its parent's caps anyway */

#ifdef __alpha__

       unsigned   int taso:1;

#endif

       unsigned   int recursion_depth;

       struct file * file;

       struct cred *cred; /*   new credentials */

       int   unsafe;            /* how unsafe this   exec is (mask of LSM_UNSAFE_*) */

       unsigned   int per_clear;      /* bits to clear in   current->personality */

       int argc, envc;

       char * filename;   /*   Name of binary as seen by procps */

       char * interp;              /* Name of the binary really executed.   Most

                               of the time same as filename, but could be

                               different for binfmt_{misc,script} */

       unsigned   interp_flags;

       unsigned   interp_data;

       unsigned   long loader, exec;

};

注:本人用红色标注出了结构体中重要的内容

 

系统调用函数sys_execve,其主要功能在do_execve函数中完成。在do_execve()中,首先调用kzalloc分配描述进程内存的结构体struct linux_binprm,接下来调用prepare_bprm_creds初始化cred结构,即为新的进程创建了一个新的cred结构。

blob.png

 

为执行函数的功能,之前还需加载和解析二进制文件。 load_elf_binary函数完成该工作:检测ELF文件头是否有效,设置codebrkstack等段,设置虚拟内存地址区域。主要函数调用如下:

blob.png

blob.png

Sys_execveàdo_execveàsearch_binary_handlerà[load_elf_binary](linux_binfmt结构体内的load_binary函数)

下图描述进程内存的布置情况。

blob.png

当描述进程的linux_binprm结构已初始化完毕,内核就可以创建线程来执行。

static int load_elf_binary(struct   linux_binprm *bprm, struct pt_regs *regs)

{

      ……

current->mm->end_code   = end_code;

       current->mm->start_code   = start_code;

       current->mm->start_data   = start_data;

       current->mm->end_data   = end_data;

       current->mm->start_stack   = bprm->p;

     ……

start_thread(regs, elf_entry,   bprm->p);

……

}

在加载EFL文件时,发现一个有趣的现象,load_elf_binnary函数中没有用到elf_shdr结构体,只是用elf_phdr结构体来创建VMAs。所以可以猜想ELF header结构体中的e_shoffe_shentsizee_shnumeshstrndx成员可能没有作用。

 

下面通过一个简单的antidebug程序检测下

hello.c

#include<stdio.h>
 
  int main()
  {
      printf("hello monkee\n");
      return 0;
  }

 

测试过程如下:

root@ubuntu:~/code/ELF# gcc -o hello hello.c -g

root@ubuntu:~/code/ELF# ./hello

hello monkee

root@ubuntu:~/code/ELF# ./anti-debug ./hello

[+] Binary size : 8160 octets

— Step 1

[+] Clean sections…

[+] Clean section [DONE]

— Step 2

[+] Clean elf header…

[+] Clean elf header [DONE]

— Step 3

[+] Writting binary…

[+] Writting binary [DONE]

root@ubuntu:~/code/ELF# objdump -d hello

objdump: hello: File truncated

root@ubuntu:~/code/ELF# ./hello

hello monkee

root@ubuntu:~/code/ELF# gdb ./hello

GNU gdb (GDB) 7.6.1-ubuntu

Copyright (C) 2013 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show   warranty" for details.

This GDB was configured as "i686-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>…

"/root/code/ELF/./hello": not in executable   format: File truncated

(gdb) r

Starting program: 

No executable file specified.

Use the "file" or "exec-file" command.

(gdb)

 

readelf查看anti_debug前后ELF文件头

blob.png

blob.png

 

附:anti_debug.c

/*

**  Copyright (C)   2013 – Jonathan Salwan – http://twitter.com/JonathanSalwan

**

**  This program   is free software: you can redistribute it and/or modify

**  it under the   terms of the GNU General Public License as published by

**  the Free   Software Foundation, either version 3 of the License, or

**  (at your   option) any later version.

**

**  This program   is distributed in the hope that it will be useful,

**  but WITHOUT   ANY WARRANTY; without even the implied warranty of

**  MERCHANTABILITY   or FITNESS FOR A PARTICULAR PURPOSE.    See the

**  GNU General   Public License for more details.

**

**  You should   have received a copy of the GNU General Public License

**  along with   this program.  If not, see   <http://www.gnu.org/licenses/>.

*/

 

#include <elf.h>

#include <fcntl.h>

#include <stdio.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/mman.h>

#include <stdlib.h>

 

Elf32_Ehdr    *pElf_Header;

Elf32_Phdr    *pElf32_Phdr;

Elf32_Shdr    *pElf32_Shdr;

char          *pMapElf;

struct stat   filestat;

 

static unsigned char *set_header(char *file)

{

  int fd;

  unsigned char *data;

 

  fd = open(file, O_RDONLY, 0644);

  stat(file, &filestat);

  printf("[+]   Binary size : %d octets\n", (int)filestat.st_size);

  data = malloc(filestat.st_size * sizeof(char));

  read(fd, data, filestat.st_size);

  pMapElf = mmap(0, filestat.st_size, PROT_READ, MAP_SHARED, fd, 0);

  pElf_Header = (Elf32_Ehdr *)data;

  pElf32_Shdr = (Elf32_Shdr *)((char *)data + pElf_Header->e_shoff);

  pElf32_Phdr = (Elf32_Phdr *)((char *)data + pElf_Header->e_phoff);

  close(fd);

 

  return (data);

}

 

int main(int argc, char **argv)

{

  unsigned char *data;

  unsigned nb_section;

  Elf32_Shdr *current;

  int i, fd;

 

  if (argc < 2){

    printf("Syntax:   ./%s <bin>\n", argv[0]);

    return -1;

  }

 

  data = set_header(argv[1]);

 

  printf("— Step   1 —\n");

  printf("[+] Clean   sections…\n");

  nb_section = pElf_Header->e_shnum;

  for (i = 0 ; i < nb_section ; i++){

    pElf32_Shdr->sh_name = 0;

    pElf32_Shdr->sh_type = 0;

    pElf32_Shdr->sh_flags = 0;

    pElf32_Shdr->sh_addr = 0;

    pElf32_Shdr->sh_offset = 0;

    pElf32_Shdr->sh_size = 0;

    pElf32_Shdr->sh_link = 0;

    pElf32_Shdr->sh_info = 0;

    pElf32_Shdr->sh_addralign = 0;

    pElf32_Shdr->sh_entsize = 0;

    pElf32_Shdr++;

  }

  printf("[+] Clean   section [DONE]\n");

 

  printf("— Step   2 —\n");

  printf("[+] Clean   elf header…\n");

  pElf_Header->e_shnum = 0;

  pElf_Header->e_shstrndx = 0;

  pElf_Header->e_shentsize = 0;

  pElf_Header->e_version = 0;

  pElf_Header->e_ehsize = 0;

  pElf_Header->e_shoff = 123;

  printf("[+] Clean   elf header [DONE]\n");

 

  printf("— Step   3 —\n");

  printf("[+]   Writting binary…\n");

  fd = open(argv[1], O_WRONLY, 0644);

  write(fd, data, filestat.st_size);

  close(fd);

  printf("[+]   Writting binary [DONE]\n");

 

  free(data);

  return 0;

}

 

参考:

Linux process execution and the useless ELF header fields

ELFLinux下的加载过程

Anti_debug