int fllwrite(int fd, const char* str) { int written = 0; int length = fbt_strnlen(str, 0); while (written < length) { int64_t retval = fbt_write(fd, (uint64_t)(str + written), (length - written), "error in write"); written += retval; } return written; }
/** * 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; }
int fllprintfva(int fd, const char* format, va_list app) { char buf[BUFSIZE_L+1]; int bi = 0; // index in the output string int fi = 0; // index in the format string int end = 0; while ((bi < (BUFSIZE_L)) && !end) { // parse format string and write contents to buffer switch (format[fi]) { case '%': fi++; unsigned char len = 0; /* limit len of output/datatype? */ if (format[fi]=='.') { len=format[++fi]-0x30; if (len>BUFSIZE_S) len=0; fi++; if (format[fi]-0x30>=0 && format[fi]-0x30<=9) { /* two digit number */ len*=10; len+=format[fi]-0x30; fi++; } } char *pointer; char revbuf[BUFSIZE_S+1]; int i, val, length; unsigned int abs_d; for (i=0; i<BUFSIZE_S+1; i++) revbuf[i]=0x0; i=BUFSIZE_S; // initialize to back of buffer; switch (format[fi]) { case '%': buf[bi++] = '%'; break; case 'd': case 'i': val = va_arg(app, int); abs_d = ABS(val); while ((abs_d > 0) && (i > 0)) { revbuf[--i] = (char) (0x30 + abs_d % 10); abs_d /= 10; } if (val == 0) revbuf[--i] = '0'; else if (val < 0) revbuf[--i] = '-'; // if enough space in large buffer copy all length = MIN(BUFSIZE_L - bi, BUFSIZE_S - i); fbt_strncpy(&buf[bi], &revbuf[i], length); bi += length; break; case 'p': buf[bi++]='0'; buf[bi++]='x'; if (len<2) len=10; /* ensure that len is large enough */ if (len!=0) len-=2; /* make len smaller (preceding '0x') */ case 'x': abs_d = va_arg(app, unsigned int); while ((abs_d > 0) && (i > 0)) { if ((abs_d&0xf) < 0xa) { revbuf[--i] = 0x30 + (abs_d&0xf); } else { revbuf[--i] = 0x57 + (abs_d&0xf); } abs_d /= 16; } if (abs_d == 0) revbuf[--i] = '0'; /* fill leading 0s */ if (len!=0) while (len>(BUFSIZE_S-i)) revbuf[--i]='0'; length = MIN(BUFSIZE_L - bi, BUFSIZE_S - i); if (length>len && len!=0) { /* ensure that we stay in buffer */ i=BUFSIZE_S-len; length = len; } fbt_strncpy(&buf[bi], &revbuf[i], length); bi += length; break; case 's': pointer = va_arg(app, char*); /* ensure that we stay in buffer */ int slen = fbt_strnlen(pointer, BUFSIZE_L - bi); if (slen>len && len!=0) slen=len; fbt_strncpy(&buf[bi], pointer, slen); bi += slen; break; case '\0': /* end of format string, break out of while loop */ buf[bi]=format[fi]; end = 1; break; default: /* we have a % but no valid conversion specifier -> we copy the '%' and the following character verbatim */ buf[bi] = '%'; bi++; buf[bi] = format[fi]; bi++; } break; case '\0': /* end of format string, break out of while loop */ buf[bi] = '\0'; end = 1; break; default: buf[bi] = format[fi]; bi++; } fi++; } buf[BUFSIZE_L]=0x0; /* guard */ return fllwrite(fd, buf); }