コード例 #1
0
static void fbt_lib_list_resize()
{
  library_list = (struct lib_list_entry*) realloc(library_list, 2 * lib_list_capacity * sizeof(*library_list));
  if (NULL == library_list) {
    fbt_suicide_str("fbt_lib_list_resize: could not reallocate memory for library list (lib_list_resize: fbt_mem_protection.c)\n");
  }
  lib_list_capacity *= 2;
}
コード例 #2
0
void fbt_memprotect_init()
{
  if (0 == lib_list_capacity) {
    library_list = (struct lib_list_entry*) malloc(LIB_LIST_INITSIZE * sizeof(*library_list));
    if (NULL == library_list) {
      fbt_suicide_str("fbt_memprotect_init: could not allocate memory for library list (memprotect_init: fbt_mem_protection.c)\n");
    }
    lib_list_capacity = LIB_LIST_INITSIZE;
  }
}
コード例 #3
0
ファイル: fbt_actions.c プロジェクト: HexHive/libdetox
enum translation_state action_copy(struct translate *ts) {
  unsigned char *addr = ts->cur_instr;
  unsigned char* transl_addr = ts->transl_instr;
  long length = ts->next_instr - ts->cur_instr;

  PRINT_DEBUG_FUNCTION_START("action_copy(*addr=%p, *transl_addr=%p, "
                             "length=%i)", addr, transl_addr, length);
  /* copy instruction verbatim to translated version */
  fbt_memcpy(transl_addr, addr, length);

  PRINT_DEBUG_FUNCTION_END("-> neutral, transl_length=%i", length);
  ts->transl_instr += length;
  if (*(ts->cur_instr) == 0xcc || *(ts->cur_instr) == 0xcd ||
      *(ts->cur_instr) == 0xce) {
#if defined(AUTHORIZE_SYSCALLS)
    if (*(ts->cur_instr) == 0xcd) {
      if (*(ts->first_byte_after_opcode) != 0x80) {
        fbt_suicide_str("Illegal interrupt encountered (fbt_actions.c)\n");
      }
      /* undo copy of the interrupt */
      ts->transl_instr -= length;
      transl_addr = ts->transl_instr;
      /* store location of this syscall */
      MOVL_IMM32_MEM32(transl_addr, 0x05, addr, &(ts->tld->syscall_location));
      MOVL_IMM32_MEM32(transl_addr, 0x05, 0x00c0ff33, &(ts->tld->ind_target));

      /* pointer to after int80 */
      ulong_t *ptr = (ulong_t*)(transl_addr-sizeof(void*));
      /* write: jump instruction to trampoline */
      JMP_REL32(transl_addr, (ulong_t)(ts->tld->int80_trampoline));
      *ptr = (ulong_t)(transl_addr);
      ts->transl_instr = transl_addr;
    }
#endif
    /*
     * we just copied an interrupt
     * 0xcc - int3 (debug)
     * 0xcd - int imm8
     * 0xce - into (int 4) if overflow flag is set to 1
     * because we might execute a kernel routine (which might not return or
     * might do something funny to our stack) we finish the TU here and
     * issue some glue code to restart the translation if we resume after the
     * int itself
     */
    PRINT_DEBUG("Encountered an interrupt - closing TU with some glue code\n");
    return CLOSE_GLUE;
  }
  return NEUTRAL;
}
コード例 #4
0
ファイル: fbt_actions.c プロジェクト: HexHive/libdetox
enum translation_state action_jmp(struct translate *ts) {
  unsigned char *addr = ts->cur_instr;

  unsigned char *original_addr = addr;
  
  PRINT_DEBUG("original_addr=%x / addr=%x\n", original_addr, addr);  
  
  unsigned char* transl_addr = ts->transl_instr;
  int length = ts->next_instr - ts->cur_instr;

  PRINT_DEBUG_FUNCTION_START("action_jmp(*addr=%p, *transl_addr=%p, length=%i)",
                             addr, transl_addr, length);
#if defined(FBT_STATISTIC)
  fbt_nr_translated_jmp++;
#endif

  /* read call argument (either 8bit or 32bit offset) and add EIP (EIP = addr +
     length) to argument --> absolute target address = addr + length + offset */
  #if defined(ASSERTIONS)
  assert(!HAS_PREFIX(*addr)); /* no prefixes allowed */
  #endif

  int32_t jump_target=0;
  if (*addr == 0xE9) {
    /* 32bit offset */
    jump_target = *((int32_t*)(addr + 1)) + (int32_t)original_addr + length;
    PRINT_DEBUG("jump_target = %x + %x + %x", *(int32_t*)(addr + 1), original_addr, length);
  } else {
    /* our argument is only an 8bit offset */
    jump_target = (int32_t)(*((char*)(addr + 1)) + original_addr + length);
  }
  
  PRINT_DEBUG("original jmp_target: %p", (void*)jump_target);

/* It seems that some binaries like /bin/ls uses direct jmp's into plt instead of direct calls. */
/* We need to handle and inline this jmps correctly. */
#if defined(INLINE_PLT_CALLS)
/* Check if call targets plt section and resolve destination address */
unsigned long resolved_addr = sl_resolve_plt_call(jump_target, ts->tld->dso_objects);
if (resolved_addr != jump_target) {
  /* PLT call, check transfer and inline it */
  #if defined(VERIFY_CFTX)

  #if defined(ENABLE_TRANSLATION_TIME_SYMBOL_LOOKUP)
  struct sh_symbol* symbol = fbt_find_symbol(ts->tld, addr);
  fbt_check_transfer(ts->tld, original_addr, (unsigned char*)&resolved_addr, CFTX_CALL_IND, symbol); /* treat like an indirect call */
  #else
  fbt_check_transfer(ts->tld, original_addr, (unsigned char*)&resolved_addr, CFTX_CALL_IND); /* treat like an indirect call */
  #endif
  
  #endif /* VERIFY_CFTX */
  
  jump_target = resolved_addr;
}
#endif
/* End - PLT Jmps inlining! */

  /* check if the target is already translated; if it is not, do so now */
  void *transl_target = fbt_ccache_find(ts->tld, (void*)jump_target);
  if (transl_target == NULL) {
    /* we still have to translate the call target */
    PRINT_DEBUG_FUNCTION_END("-> open, transl_length=0");
    /* no need to actually jump
      simply change the next instr pointer to the first instr of the function
      this will put the body of the function right as the next instr in the
      translated code */
    ts->next_instr = (unsigned char*)jump_target;
    /* put the target into the tcache so later jumps can use the translated
       code */
    #ifndef TRACK_BASIC_BLOCKS
    //fbt_ccache_add_entry(ts->tld, (void*)jump_target, ts->transl_instr);
    #endif
    
    return OPEN;
  }

  PRINT_DEBUG("translated jmp_target: %p", transl_target);

  /* write: jmp */
  #if defined(TRACK_CFTX)
  struct control_flow_transfer cft = {
    .location = transl_addr + 1,
    .original = addr
  };
  fbt_store_cftx(ts->tld, &cft);
  #endif /* TRACK_CFTX */

  JMP_REL32(transl_addr, (int32_t)transl_target);

  PRINT_DEBUG_FUNCTION_END("-> close, transl_length=%i",
                           transl_addr - ts->transl_instr);
  ts->transl_instr = transl_addr;
  return CLOSE;
}

enum translation_state action_jmp_indirect(struct translate *ts) {
  unsigned char *addr = ts->cur_instr;
  unsigned char* transl_addr = ts->transl_instr;
  unsigned char *first_byte_after_opcode = ts->first_byte_after_opcode;
  int length = ts->next_instr - ts->cur_instr;

  PRINT_DEBUG_FUNCTION_START("action_jmp_indirect(*addr=%p, *transl_addr=%p, " \
                             "length=%i)", addr, transl_addr, length);

#if defined(FBT_STATISTIC)
  fbt_nr_translated_jmp_ind++;
#endif

  #if defined(SECURITY_METRICS_AND_STATS) && defined(VERIFY_CFTX)
  	struct dso_chain *cur = ts->tld->dso_objects;
  	long ret;
  	
		while (cur != 0) {
			/* is the pointer in the current dso? */
            if (PTR_IN_REGION(addr, cur->baseaddr, cur->endaddr-cur->baseaddr)) {
			    FBT_UNPROT_DATA(cur, sizeof(struct dso_chain), ret, "action_jmp_indirect: unprotect dso failed.");
			    if(cur->nr_ijmps >= ICF_TABLES_MAX_ENTRIES) {
						llprintf("ERROR: ijmps table out of space (fbt_actions.c), nr of entries: %d\n", cur->nr_ijmps);
						fbt_exit_suicide(45);
				}
				
				int i = 0;
				int found = 0;
				/* let's see if we already have this ICF in the list */
			    for(i = 0; i < cur->nr_ijmps; i++) {
					if(addr == cur->ijmps[i]) {
						found = 1;
						break;
					}
				}
				if(!found) {
					cur->ijmps[cur->nr_ijmps] = addr;
					cur->nr_ijmps++;
				}
				
				/* TODO: make this thread safe! */
				//FBT_PROT_DATA(cur, sizeof(struct dso_chain), ret, "action_jmp_indirect: protect dso failed.");
			   break;
			}
			cur = cur->next;
		}
	
  #endif /* defined(SECURITY_METRICS_AND_STATS) && defined(VERIFY_CFTX) */
  
  if (ts->num_prefixes != 0) {
    /* no prefixes allowed */
    fbt_suicide_str("No prefixes handled in action_jmp_indirect! " \
                    "(fbt_actions.c)\n");
  }

  /* this is a fast version of the ind jmp - handoptimized assembler code
   * which does a fast lookup in the hashtable and dispatches if it hits
   * otherwise it recovers to an indirect jump
   */

  /**
   * pushl $target
   * jmpl  tld->ind_jump_trampoline
   */

  /* write: push indirect target */
  *transl_addr++ = 0xFF;

  /*
   * 0xFF expects a ModR/M byte following the opcode
   * The bits 3-5 are part of the opcode (opcode extension into ModR/M byte),
   * so we copy the ModR/M byte, but modify the opcode extension to 110
   */
  *transl_addr++ = (*first_byte_after_opcode & 0xC7) | 0x30;

  //llprintf("action_jmp_indirect: %x\n", addr);

  /* if there follows a displacement copy this to the ccf */
  if (length > 2) {
    fbt_memcpy(transl_addr, (addr + 2), length - 2);
    //llprintf("action_jmp_indirect length > 2: %x\n", addr + 2);
    transl_addr += length - 2;
  }

#if defined(VERIFY_CFTX)

#if defined(VERIFY_CFTX_ENABLE_IJMP_LSYMOPT) || defined(ENABLE_TRANSLATION_TIME_SYMBOL_LOOKUP)
struct sh_symbol* symbol = fbt_find_symbol(ts->tld, addr);
#endif

#if defined(VERIFY_CFTX_ENABLE_IJMP_LSYMOPT)
if(symbol && symbol->start && symbol->size) {

  BEGIN_ASM(transl_addr)
    movl %esp, {ts->tld->stack-2}
    movl ${ts->tld->stack-2}, %esp
コード例 #5
0
/**
 * callback function used to scan the loaded libraries.
 * This function is used as callback in dl_iterate_phdr(3) to scan the sections
 * of a loaded library. The first object in the list (position 0) is always the
 * executable, the second one the vdso ("linux-gate.so"). The third object is
 * the secuBT library (libfastbt.so), because it is loaded with LD_PRELOAD.
 * The sections of the objects are added to the red-black-tree structure along
 * with information on whether they contain executable code.
 * For more info on dl_iterate_phdr, refer to its man page.
 * @param info the object info structure provided by dl_iterate_phdr
 * @param size the size of the info structure
 * @param data is used to store the position in the list of libraries
 */
static int fbt_memprotect_callback(struct dl_phdr_info *info, size_t size, void *data)
{
  int libnum = *((int*) data);

  // look for the base address in the library list
  int i = 0;
  for (i = 0; i < lib_list_size; i++) {
    if (library_list[i].base_addr ==  (void*) info->dlpi_addr) {
      // the entry for this object already exists in the library list
      (*((int*)data))++;
      return 0;
    }
  }

  if (lib_list_size >= lib_list_capacity) {
    fbt_lib_list_resize();
  }

  const char *name = info->dlpi_name;

  /*
   * The first entry of dl_iterate_phdr's table of objects is always the
   * executable itself, the second the vdso. They don't have dlpi_addr as
   * base address and have to be caught in order not to be processed again.
   * After these two come the shared objects.
   */
  if (0 == libnum) {
    if (lib_list_size > 0) {
      // executable has to be already be in the library list
      (*((int*)data))++;
      return 0;
    }
    name = "/proc/self/exe";
  } else if (1 == libnum) {
    if (lib_list_size > 1) {
      // vdso has to be already be in the library list
      (*((int*)data))++;
      return 0;
    }
    // ugly hack to get correct address of vdso
    void *vdso_addr = (void*) (info->dlpi_addr + info->dlpi_phdr[0].p_vaddr);

    // add vdso to library table
    library_list[lib_list_size].base_addr = vdso_addr;
    library_list[lib_list_size].length = PAGESIZE;
    library_list[lib_list_size].name = (char *) malloc(5 * sizeof(char));
    fbt_strcpy(library_list[lib_list_size].name, "vdso");
    lib_list_size++;


    // add vdso entry to sections tree
    struct mem_info *info = malloc(sizeof(*info));
    info->node.addr_begin = vdso_addr;
    info->node.addr_end = vdso_addr + PAGESIZE;
    info->sec_name = library_list[lib_list_size - 1].name;
    info->lib_index = lib_list_size - 1;
    info->flags = INFO_RFLAG | INFO_XFLAG;
    sections_root = rb_insert(sections_root, (struct rb_node*) info);

    (*((int*)data))++;
    return 0;
  }
  if (0 == fbt_strcmp(name, "")) {
    (*((int*)data))++;
    return 0;
  }

  // entry in library list
  library_list[lib_list_size].base_addr = (void*) info->dlpi_addr;
  library_list[lib_list_size].length = 0;
  library_list[lib_list_size].name = malloc(fbt_strnlen(name,0) + 1);
  fbt_strcpy(library_list[lib_list_size].name, name);
  lib_list_size++;

  /*
   * we map library file again to memory, because when it is loaded for execution, the
   * section header table and the section containing the section name strings are not
   * mapped to memory.
   */
  // open the file -> file descriptor
  int fd;
  fbt_open(name, O_RDONLY, 0x0, fd);
  if (-1 == fd) {
    fbt_suicide_str("fbt_iterate_sections: could not open executable or library file (memprotect_callback: fbt_mem_protection.c)\n");
    (*((int*)data))++;
    return -1;
  }

  // find out file size
  int filesize;
  fbt_lseek(fd, 0, SEEK_END, filesize);
  if (-1 == filesize) {
    fbt_suicide_str("fbt_iterate_sections: fstat failure, cannot find out file size (memprotect_callback: fbt_mem_protection.c)\n");
    return -1;
  }

  void *libmap;
  fbt_mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0, libmap);
  if ((void*) -1 == libmap) {
    fbt_suicide_str("fbt_iterate-sections: failed to map library to memory (memprotect_callback: fbt_mem_protection.c)\n");
    //fbt_close(fd);
    //(*((int*)data))++;
    return -1;
  }

  Elf32_Ehdr *header = (Elf32_Ehdr*) libmap;

  fbt_sym *dyn_symtab = NULL;
  char *dyn_strtab = NULL;
  void *dyn_hashtab = NULL;

  if (0 != header->e_shnum) {
    Elf32_Shdr *section_header = (Elf32_Shdr*) (libmap + header->e_shoff);

    int i = 0;
    for (i = 0; i < header->e_shnum; i++) {
      // only consider sections loaded into memory on execution
      if ((section_header[i].sh_size < 1)
          || ((section_header[i].sh_flags & SHF_ALLOC) == 0)) {
        continue;
      }

      // fill in node information
      struct mem_info *meminfo = (struct mem_info*) malloc(sizeof(*meminfo));
      meminfo->node.addr_begin = (void*) (info->dlpi_addr
                                          + section_header[i].sh_addr);
      meminfo->node.addr_end = meminfo->node.addr_begin
                               + section_header[i].sh_size;

      // calculate start address of the executable
      if ((0 == libnum) && (0 == library_list[0].base_addr)) {
        library_list[0].base_addr = (void*) ((long) (meminfo->node.addr_begin)
                                             & ~(PAGESIZE - 1));
      }

      // section name
      if (SHN_UNDEF != header->e_shstrndx) {
        const char *name_entry = (const char*) (libmap
                                                + section_header[header->e_shstrndx].sh_offset
                                                + section_header[i].sh_name);
        char *section_name = (char*) malloc(fbt_strnlen(name_entry,0) + 1);
        fbt_strcpy(section_name, name_entry);
        meminfo->sec_name = section_name;
      } else {
        const char* no_sec_names = "no section name string table";
        char *section_name = (char*) malloc(fbt_strnlen(no_sec_names,0) + 1);
        fbt_strcpy(section_name, no_sec_names);
        meminfo->sec_name = section_name;
      }
      // update size in memory
      int length = meminfo->node.addr_end
                   - library_list[lib_list_size - 1].base_addr;
      if (length > library_list[lib_list_size - 1].length) {
        library_list[lib_list_size - 1].length = length;
      }

      // flags
      meminfo->flags = INFO_RFLAG;
      if (section_header[i].sh_flags & SHF_WRITE) {
        meminfo->flags |= INFO_WFLAG;
      }
      if (2 == libnum) {
        // memory in libfastbt.so
        meminfo->flags |= INFO_BTFLAG;
      }
      if (section_header[i].sh_flags & SHF_EXECINSTR) {
        // only mark as executable if we memory not part of libfastbt.so
        meminfo->flags |= INFO_XFLAG;
      }
      meminfo->lib_index = lib_list_size - 1;

      sections_root = rb_insert(sections_root, (struct rb_node*) meminfo);

      // code for symbol checking
      if (SHT_DYNSYM == section_header[i].sh_type) {
        // we have found the dynamic linking symbol table
        dyn_symtab = (Elf32_Sym*) (info->dlpi_addr
                                   + section_header[i].sh_addr);
        if (2 == libnum) {
          // handling libfastbt.so, need to know # of symbols
          fbt_dyn_nsyms = section_header[i].sh_size / sizeof(fbt_sym);
        }
      }
      if (!fbt_strcmp(".dynstr", meminfo->sec_name)) {
        // string table for dynamic linking symbols
        dyn_strtab = (char*) (info->dlpi_addr
                              + section_header[i].sh_addr);
      }
      if (!fbt_strcmp(".gnu.hash", meminfo->sec_name)) {
        // hash table for dynamic linking symbols (gnu format)
        dyn_hashtab = (void*) (info->dlpi_addr
                               + section_header[i].sh_addr);
      }
    }
  }
#ifdef SECU_NX_PROG
  // only executed on first run of this function
  if (0 == libnum) {
    /*
     * Set the memory of the program non-executable. We cannot do the same
     * to the libraries, as we need some of them ourselves.
     */
    /* TODO: protect libs as well and compile bt statically */
    int ret;
    fbt_mprotect(library_list[0].base_addr, library_list[0].length,
                 PROT_READ | PROT_WRITE, ret);
  }
#endif
#ifdef SECU_DETECT_SYMBOL_COLLISIONS
  if ((NULL != dyn_symtab) && (NULL != dyn_strtab) && (NULL != dyn_hashtab)) {
    if (0 == libnum) {
      /*
       * we can not check the symbols of the executable now because
       * libfastbt.so has not been processed yet.
       */
      exe_dyn_symtab = dyn_symtab;
      exe_dyn_strtab = dyn_strtab;
      exe_dyn_hashtab = dyn_hashtab;
    } else if (2 == libnum) {
      fbt_dyn_symtab = dyn_symtab;
      fbt_dyn_strtab = dyn_strtab;

      // ok, now we can check the symbols of the executable
      if (!fbt_check_sym_collision(exe_dyn_symtab, exe_dyn_strtab,
                                   exe_dyn_hashtab)) {
        // symbol collision detected
        fbt_suicide_str("Symbol collision in the executable (memprotect_callback: fbt_mem_protection.c)\n");
      }
    } else if (libnum > 2) {
      // check symbols of library
      if (!fbt_check_sym_collision(dyn_symtab, dyn_strtab, dyn_hashtab)) {
        llprintf("Symbol collision in %s\n", name);
        ffflush();
        fbt_suicide();
      }
    }
  } else {
    // one of the tables was not found!
    llprintf("Fatal: One of the tables for symbol checking was not found!\n"
             "symtab: %p, strtab: %p, hashtab: %p\n", dyn_symtab,
             dyn_strtab, dyn_hashtab);
    ffflush();
    fbt_suicide();
  }
#endif
  (void)dyn_hashtab;
  (void)dyn_strtab;
  (void)dyn_symtab;

  int ret;
  fbt_munmap(libmap, filesize, ret);
  fbt_close(fd, ret);
  (*((int*)data))++;
  return 0;
}
コード例 #6
0
ファイル: fbt_syscall.c プロジェクト: chubbymaggie/libdetox
static enum syscall_auth_response auth_signal(struct thread_local_data *tld,
                                              ulong_t syscall_nr, ulong_t arg1,
                                              ulong_t arg2, ulong_t arg3,
                                              ulong_t arg4, ulong_t arg5,
                                              ulong_t *arg6,
                                              ulong_t is_sysenter,
                                              ulong_t *retval) {
  if (syscall_nr != SYS_signal && syscall_nr != SYS_sigaction &&
      syscall_nr != SYS_rt_sigaction) {
    fbt_suicide_str("Invalid system call number in signal auth (fbt_syscall.c).");
  }

  /* arg1: signal number
     arg2: { const struct sigaction *act | sighandler_t }
   */
  if (syscall_nr == SYS_signal) {
    void *oldfunction = tld->signals[arg1].sigaction;
    tld->signals[arg1].mask = 0x0;
    tld->signals[arg1].flags = 0x0;
    tld->signals[arg1].restorer = 0x0;
    tld->signals[arg1].sigaction = (void*)arg2;
    if ((void*)arg2 == SIG_IGN || (void*)arg2 == SIG_DFL) {
      fbt_signalE(arg1, arg2, retval);
    } else {
      fbt_signalE(arg1, &sighandler, retval);
    }
    /* if we got an error in the signal syscall then we return that error.
       otherwise we return the old value of the signal handler */
    if (*retval < (ulong_t)(-(128 + 1))) {
      *retval = (ulong_t)oldfunction;
    }
    return SYSCALL_AUTH_FAKE;
  }
  
  if (syscall_nr == SYS_sigaction || syscall_nr == SYS_rt_sigaction) {
    *retval = 0x0;
    /* store the _old_ target for this signal */
    if (arg3 != 0x0) {
      struct fbt_sigaction *sigaction = (struct fbt_sigaction*)arg3;
      sigaction->sigaction = tld->signals[arg1].sigaction;
      sigaction->mask = tld->signals[arg1].mask;
      sigaction->flags = tld->signals[arg1].flags;
      sigaction->restorer = tld->signals[arg1].restorer;
    }
    /* interpret the _new_ sigaction struct */
    if (arg2 != 0x0) {
      struct fbt_sigaction *sigaction = (struct fbt_sigaction*)arg2;
      #if defined(DEBUG)
      PRINT_DEBUG("sigaction: %p (%d) %p\n", arg2, arg1, sigaction->sigaction);
      #endif
      tld->signals[arg1].mask = sigaction->mask;
      tld->signals[arg1].flags = sigaction->flags;
      tld->signals[arg1].restorer = sigaction->restorer;
      tld->signals[arg1].sigaction = sigaction->sigaction;

      if (syscall_nr == SYS_sigaction) {
        fbt_sigactionE(arg1, &(tld->signals[arg1]), 0x0, retval);
      } else {
		/* TODO: verify the 4th parameter of rt_sigaction! */
        fbt_rt_sigactionE(arg1, &(tld->signals[arg1]), 0x0, 0x8, retval);
        /* sizeof(sigset_t) ? */
      }
    }
    return SYSCALL_AUTH_FAKE;
  }

  return deny_syscall(tld, syscall_nr, arg1, arg2, arg3, arg4, arg5, arg6,
                      is_sysenter, retval);
}