eresi_Addr lookup_libc_offset(char *libc, char *function) { elfshobj_t *f; elfsh_Rela *r; elfsh_Sym *s; int i; char *cmpname; f = elfutils_read_elf_file(libc); elfshsect_t *dynsym, *strtab; dynsym = elfsh_get_section_by_name(f,".dynsym", NULL, NULL, NULL); // lookup strtab strtab = elfsh_get_section_by_index(f, elfsh_get_section_link(dynsym->shdr), NULL, NULL); // get symbol elfsh_Sym *table; table = (elfsh_Sym *) dynsym->data; eresi_Addr numsym = elfsh_get_section_size(dynsym->shdr)/sizeof(elfsh_Sym); char *strs = strtab->data; for (i = 0; i < numsym; i++) { //look at each entry s = &(table[i]); cmpname = strs + s->st_name; if(strcmp(function, cmpname) == 0){ return s->st_value; } } return 0; }
/** * Called from elfsh_fixup_symtab * When trying to inject part of the libc, some bss symbols have a wrong sctndx * @param symtab * @return */ elfshsect_t *elfsh_fixup_sctndx(elfshsect_t *symtab) { int index; elfsh_Sym *sym; elfsh_SAddr offset; elfsh_Shdr *shdr; elfshsect_t *sct; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); //return (symtab); // XXX sym = symtab->data; shdr = symtab->parent->sht + symtab->index; for (index = 0; index < shdr->sh_size / sizeof(elfsh_Sym); index++) { if (elfsh_get_symbol_link(sym + index) != SHN_COMMON) { if (elfsh_get_symbol_type(sym + index) == STT_SECTION) continue; sct = elfsh_get_parent_section(symtab->parent, elfsh_get_symbol_value(sym + index), &offset); if (sct == NULL) { sct = elfsh_get_section_by_index(symtab->parent, elfsh_get_symbol_link(sym + index), NULL, NULL); if (sct && elfsh_get_section_type(sct->shdr) == SHT_NOBITS) { #if __DEBUG_MAP__ printf(" [*] Symbol [%s] sctndx changed from %u to SHN_COMMON\n", elfsh_get_symbol_name(symtab->parent, sym + index), elfsh_get_symbol_link(sym + index)); #endif elfsh_set_symbol_link(sym + index, SHN_COMMON); continue; } } if (sct && elfsh_get_section_type(sct->shdr) == SHT_NOBITS) { elfsh_set_symbol_link(sym + index, SHN_COMMON); #if __DEBUG_MAP__ printf(" [*] Symbol [%s] sctndx changed to SHN_COMMON\n", elfsh_get_symbol_name(symtab->parent, sym + index)); #endif } } } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, symtab); }
/** * Relocate the object * @param file * @param rel * @param stage * @return */ int elfsh_relocate_object(elfshobj_t *file, elfshobj_t *rel, u_char stage) { elfshsect_t *sect; elfshsect_t *reltab; char sctname[BUFSIZ]; u_int index; u_int found; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Last pass : relocate each inserted section */ for (found = index = 0; index < rel->hdr->e_shnum; index++) { sect = elfsh_get_section_by_index(rel, index, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant get section in ET_REL", -1); /* Check if the section is mapped */ if (elfsh_get_section_allocflag(sect->shdr) && sect->shdr->sh_size && sect->shdr->sh_type == SHT_PROGBITS) { /* Find the associate relocation section */ snprintf(sctname, sizeof(sctname), "%s%s", (IS_REL(sect) ? ".rel" : ".rela"), sect->name); reltab = elfsh_get_section_by_name(rel, sctname, NULL, NULL, NULL); if (reltab == NULL) continue; found++; /* Find the injected instance of this allocatable section in the ET_EXEC */ snprintf(sctname, sizeof(sctname), "%s%s", sect->parent->name, sect->name); sect = elfsh_get_section_by_name(file, sctname, NULL, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant get section in ET_EXEC", -1); if (elfsh_relocate_etrel_section(sect, reltab, stage) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to relocate section", -1); } } /* Note that we might have done no relocation if no table was available */ /* This can happen on very simple .o files */ PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Remap a section using its extra relocation entries * @param cur * @param diff * @return */ int elfsh_reloc_raw(elfshsect_t *cur, eresi_Addr diff) { u_int index; eresi_Addr addr; elfshsect_t *target; char *str; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); if (cur == NULL || cur->shdr == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); if (elfsh_readmem(cur) == NULL || cur->rel == NULL) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); /* Read the actual section and find valid references */ for (index = 0; index < cur->srcref; index++) switch (cur->rel[index].type) { /* Relocate by : section[idx_dst].vaddr + off_dst */ case ELFSH_RELOC_SECTBASE: target = elfsh_get_section_by_index(cur->parent, cur->rel[index].idx_dst, NULL, NULL); if (target == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid IDX_DST", -1); str = elfsh_readmem(cur) + cur->rel[index].off_src; addr = target->shdr->sh_addr + cur->rel[index].off_dst + diff; memcpy(str, &addr, sizeof(eresi_Addr)); /* Do not relocate */ case ELFSH_RELOC_FP: default: break; } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (cur->srcref)); }
/** * Retreive strtab * @param file * @param index * @return */ elfshsect_t *elfsh_get_strtab(elfshobj_t *file, int index) { elfshsect_t *s; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (file == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", NULL); s = file->secthash[ELFSH_SECTION_STRTAB]; if (s) PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, s); /* Read the string table */ if (index > 0) s = elfsh_get_section_by_index(file, index, NULL, NULL); else s = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_STRTAB, NULL, NULL, NULL); /* Section is present */ if (s != NULL) { file->secthash[ELFSH_SECTION_STRTAB] = s; s->shdr->sh_link = file->secthash[ELFSH_SECTION_SYMTAB]->index; if (s->data == NULL) { s->data = elfsh_load_section(file, s->shdr); if (s->data == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to load STRTAB", NULL); } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (s)); } /* Section is not present */ s = elfsh_rebuild_strtab(file); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (s)); }
/** * Inject a ET_REL object into a ET_EXEC object * @param file * @param rel * @return */ int elfsh_inject_etrel(elfshobj_t *file, elfshobj_t *rel) { u_int mod; u_int pgsize; u_int index; elfshsect_t *sect; elfshsect_t *hooks; int ret = 0; static int depth = 0; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sanity checks */ if (file == NULL || file->hdr == NULL || rel == NULL || rel->hdr == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Invalid NULL parameter", -1); if (rel->hdr->e_type != ET_REL || (file->hdr->e_type != ET_EXEC && file->hdr->e_type != ET_DYN)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Bad parameter types", -1); #if __DEBUG_RELADD__ if (rel->pending) { printf("[DEBUG_RELADD] BUG BUG BUG \n"); exit(0); } printf("[DEBUG_RELADD] INJECTING %s in %s (depth %d)\n", rel->name, file->name, depth++); #endif /* Set pending injection flag */ rel->pending = 1; /* If not already done */ elfsh_setup_hooks(); /* First physically insert all BSS in the file and fuse ** the module's BSS with the last one */ if (elfsh_fuse_bss(file, rel) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant fuze BSS sections", -1); } /* First pass : find and inject all allocatable sections */ for (index = 0; index < rel->hdr->e_shnum; index++) { /* Get the current section */ sect = elfsh_get_section_by_index(rel, index, NULL, NULL); if (sect == NULL) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant read section in ET_REL", -1); } /* Check if the current section need to be mapped */ if (elfsh_get_section_allocflag(sect->shdr) && sect->shdr->sh_size && sect->shdr->sh_type == SHT_PROGBITS) { mod = 0; if (elfsh_inject_etrel_section(file, sect, mod) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to inject ET_REL section", -1); } } } /* compute the inject modulo */ mod = elfsh_get_pagesize(file); //mod = sizeof(eresi_Addr); /* Do a copy of the procedure linkage table for eventual redirection */ if (!elfsh_static_file(file) && elfsh_copy_plt(file, mod) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to copy PLT", -1); } /* Create an additional hook table for non-plt function redirection */ hooks = elfsh_get_section_by_name(file, ELFSH_SECTION_NAME_HOOKS, 0, 0, 0); if (!hooks) { int mode; /* get injection mode */ ELFSH_SELECT_INJECTION(file,NULL,mode); pgsize = elfsh_get_pagesize(file); pgsize *= 4; /* We need a lot more than a page to trace big binaries like ssh */ hooks = elfsh_insert_section(file, ELFSH_SECTION_NAME_HOOKS, NULL, mode, pgsize - 1, pgsize); if (!hooks) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cannot inject .hooks", -1); } hooks->curend = 0; } #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Entering intermediate symbol injection loop\n"); #endif /* Intermediate pass 2 : Inject ET_REL symbol table into host file */ if (elfsh_fuse_etrel_symtab(file, rel) < 0) { rel->pending = 0; PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to fuze symbol tables", -1); } #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Entering final relocation loop\n"); elfsh_print_sectlist(file, "before relocation"); #endif /* Now call the relocation on the object's sections */ ret = elfsh_relocate_object(file, rel, ELFSH_RELOC_STAGE1); rel->pending = 0; depth--; PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, (ret)); }
/** * The intermediate pass of theglobal algorithm for ET_REL injection * We fuze symbol tables from the ET_REL and the host binary * * @param file * @param rel * @return */ int elfsh_fuse_etrel_symtab(elfshobj_t *file, elfshobj_t *rel) { elfshsect_t *sect; elfsh_Sym newsym; elfsh_Half type; u_int index; char sctname[BUFSIZ]; elfsh_Sym *sym; int symnbr; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); sym = elfsh_get_symtab(rel, &symnbr); for (index = 0; index < symnbr; index++) { type = elfsh_get_symbol_type(sym + index); /* Avoid non-injectable symbols */ if (type != STT_FUNC && type != STT_OBJECT) continue; if (sym[index].st_shndx >= rel->hdr->e_shnum) continue; /* Find target section in ET_REL */ sect = elfsh_get_section_by_index(rel, sym[index].st_shndx, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find extracted section in ET_REL\n", -1); /* Filter symbols using source section */ if (sect->shdr->sh_type != SHT_PROGBITS || !sect->shdr->sh_size || !elfsh_get_section_allocflag(sect->shdr)) continue; /* Find corresponding inserted section in ET_EXEC */ snprintf(sctname, sizeof(sctname), "%s%s", rel->name, sect->name); sect = elfsh_get_section_by_name(file, sctname, NULL, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find inserted section in ET_EXEC\n", -1); #if __DEBUG_RELADD__ printf("[DEBUG_RELADD] Injected ET_REL symbol %-20s ["XFMT"] \n", elfsh_get_symbol_name(rel, sym + index), (eresi_Addr) (sect->shdr->sh_addr + sym[index].st_value)); #endif /* Add symbol in host file */ newsym = elfsh_create_symbol(sect->shdr->sh_addr + sym[index].st_value, sym[index].st_size, elfsh_get_symbol_type(sym + index), elfsh_get_symbol_bind(sym + index), 0, sect->index); if (elfsh_insert_symbol(file->secthash[ELFSH_SECTION_SYMTAB], &newsym, elfsh_get_symbol_name(rel, sym + index)) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to insert ET_REL symbol", -1); } /* Resynchronize sorted instances of symbol table */ if (elfsh_sync_sorted_symtab(file->secthash[ELFSH_SECTION_SYMTAB]) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to synchronize host symtab", -1); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Relocate the just injected section * @param enew * @param reltab * @param stage * @return */ static int elfsh_relocate_etrel_section(elfshsect_t *enew, elfshsect_t *reltab, u_char stage) { elfsh_Rel *cur; volatile u_int index; elfsh_Sym *sym; volatile u_int size; eresi_Addr *dword; eresi_Addr addr; char *name; char tmpname[BUFSIZ]; elfshsect_t *sect; u_int entsz; elfshsect_t *plt; void *data; elfsh_Half symtype; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* ET_REL object is not mapped we use unconditionaly the ondisk relocation tables for such operation */ data = reltab->data; #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Using reloc table from %s [%s] data at %p \n", reltab->parent->name, reltab->name, data); #endif /* Loop on the relocation table entries */ size = (reltab->shdr->sh_type == SHT_RELA ? sizeof(elfsh_Rela) : sizeof(elfsh_Rel)); size = reltab->shdr->sh_size / size; plt = elfsh_get_plt(enew->parent, NULL); if (NULL == plt && elfsh_dynamic_file(enew->parent)) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get plt", -1); entsz = elfsh_get_pltentsz(enew->parent); if (entsz < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to get pltentsz", -1); for (index = 0; index < size; index++) { #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] relocation loop stage %u for section %s index %u \n", stage, enew->name, index); #endif /* We try a enew relocation now that the ET_REL dependence is mapped */ retry: /* Get symbol value in ET_REL */ cur = (reltab->shdr->sh_type == SHT_RELA ? (void *) (((elfsh_Rela *) data) + index) : (void *) (((elfsh_Rel *) data) + index)); sym = elfsh_get_symbol_from_reloc(reltab->parent, cur); name = elfsh_get_symname_from_reloc(reltab->parent, cur); if (sym == NULL || name == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to find symbol in ET_REL", -1); /* Grab a pointer on the dword that need to be relocated */ dword = (eresi_Addr *) ((char *) elfsh_readmem(enew) + cur->r_offset); /* ** If symbol type is NOTYPE, we use ET_EXEC symtab, else if ** symbol link is COMMON, we use ET_REL symbol inserted in ET_EXEC ** during BSS sizescan in bss.c:elfsh_find_bsslen() */ symtype = elfsh_get_symbol_type(sym); if (elfsh_get_symbol_bind(sym) != STB_LOCAL && /* patch BEOS */ (symtype == STT_NOTYPE || elfsh_get_symbol_link(sym) == SHN_COMMON)) { if (stage == ELFSH_RELOC_STAGE2 && !strstr(name, "old_")) continue; /* If the symbol is not found and we are still in the first stage relocation, just pass it */ sym = elfsh_get_metasym_by_name(enew->parent, name); if (!sym) { switch (elfsh_find_relocsym(enew, reltab, &sym, name, stage, symtype)) { case 2: #if __DEBUG_STATIC__ fprintf(stderr, "[DEBUG_STATIC] RETRY\n"); #endif goto retry; break; case 0: continue; case 1: break; case -1: default: PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to satisfy symbol in ET_REL", -1); } } addr = sym->st_value; #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Relocate using existing symbol %-20s " AFMT "]\n", name, (eresi_Addr) addr); #endif } /* Compute addr giving the injected section's vaddr in ET_EXEC */ else { /* All the following relocs are computed in stage 1 */ if (stage == ELFSH_RELOC_STAGE2) continue; /* Find target section in ET_REL */ sect = elfsh_get_section_by_index(reltab->parent, sym->st_shndx, NULL, NULL); if (sect == NULL) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find extracted section in ET_REL", -1); #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Found -%s- section (idx = %u), now looking at " "injected base address\n", sect->name, sect->index); #endif /* Find corresponding inserted section in ET_EXEC */ snprintf(tmpname, sizeof(tmpname), "%s%s", reltab->parent->name, sect->name); sect = elfsh_get_section_by_name(enew->parent, tmpname, NULL, NULL, NULL); if (sect == NULL) { #if __DEBUG_RELADD__ elfsh_print_sectlist(reltab->parent, "HEH"); fprintf(stderr, "[DEBUG_RELADD] Did not found %s section (sym = %s) \n", tmpname, name); #endif PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Cant find inserted section in ET_EXEC", -1); } /* Compute pointer value */ addr = sect->shdr->sh_addr; addr += ((elfsh_get_symbol_type(sym) == STT_SECTION && !FILE_IS_SPARC(sect->parent) && !FILE_IS_ALPHA64(sect->parent) && !FILE_IS_MIPS(sect->parent)) ? *dword : sym->st_value); #if __DEBUG_RELADD__ fprintf(stderr, "[DEBUG_RELADD] Relocate using section %-20s base [-> " AFMT "] \n", sect->name, (eresi_Addr) addr); #endif } /* Perform relocation */ if (elfsh_relocate_entry(enew, cur, dword, addr, reltab) < 0) PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unable to relocate entry", -1); } PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }
/** * Print the chosen symbol table * @param file * @param sect * @param tab * @param num * @param regx * @param get_symname * @return */ int ds(elfshobj_t *file, elfshsect_t *sect, u_int num, regex_t *regx, char *(*get_symname)(elfshobj_t *f, elfsh_Sym *s)) { elfsh_Sym *table; char *name; char *type; char *bind; u_int typenum; u_int bindnum; u_int foff; u_int index; char *sect_name; char buff[512]; char off[50]; char type_unk[ERESI_MEANING + 1]; char bind_unk[ERESI_MEANING + 1]; PROFILER_IN(__FILE__, __FUNCTION__, __LINE__); /* Sort the table if necessary */ if (world.state.sort != NULL) switch (*world.state.sort) { case ELFSH_SORT_BY_ADDR: table = sect->altdata; break; case ELFSH_SORT_BY_SIZE: table = sect->terdata; break; default: PROFILER_ERR(__FILE__, __FUNCTION__, __LINE__, "Unknown sort mode", -1); } /* Avoid reading inexistant memory in the process for .symtab */ else table = (elfsh_Sym *) (sect->shdr->sh_addr ? elfsh_readmem(sect) : sect->data); /* Browse symtab */ for (index = 0; index < num; index++) { /* Retreive names */ typenum = elfsh_get_symbol_type(table + index); bindnum = elfsh_get_symbol_bind(table + index); type = (char *) (typenum > ELFSH_SYMTYPE_MAX ? revm_build_unknown(type_unk, "type", typenum) : elfsh_sym_type[typenum].desc); bind = (char *) (bindnum >= ELFSH_SYMBIND_MAX ? revm_build_unknown(bind_unk, "type", bindnum) : elfsh_sym_bind[bindnum].desc); name = get_symname(world.curjob->curfile, table + index); sect_name = NULL; sect = elfsh_get_parent_section(world.curjob->curfile, table[index].st_value, NULL); if (sect == NULL && table[index].st_shndx) sect = elfsh_get_section_by_index(world.curjob->curfile, table[index].st_shndx, NULL, NULL); if (sect != NULL) sect_name = elfsh_get_section_name(world.curjob->curfile, sect); /* Fixup names */ if (name == NULL || *name == 0) name = ELFSH_NULL_STRING; if (type == NULL || *type == 0) type = ELFSH_NULL_STRING; if (bind == NULL || *bind == 0) bind = ELFSH_NULL_STRING; if (sect_name == NULL) sect_name = ELFSH_NULL_STRING; foff = (!table[index].st_value ? 0 : elfsh_get_foffset_from_vaddr(world.curjob->curfile, table[index].st_value)); if (sect && sect->shdr->sh_addr != table[index].st_value) snprintf(off, sizeof(off), " + %s", revm_colornumber("%u", (u_int) (table[index].st_value - sect->shdr->sh_addr))); else *off = '\0'; /* Different output depending on the quiet flag */ if (!world.state.revm_quiet) { snprintf(buff, sizeof(buff), " %s %s %s %s %s%s " "%s%s %s%s %s%s => %s%s\n", revm_colornumber("[%03u]", index), revm_coloraddress(XFMT, (eresi_Addr) elfsh_get_symbol_value(table + index) + file->rhdr.base), revm_colortypestr_fmt("%-8s", type), revm_colorstr_fmt("%-40s", name), revm_colorfieldstr("size:"), revm_colornumber("%010u", elfsh_get_symbol_size(table + index)), revm_colorfieldstr("foffset:"), revm_colornumber("%06u", foff), revm_colorfieldstr("scope:"), revm_colortypestr_fmt("%-6s", bind), revm_colorfieldstr("sctndx:"), revm_colornumber("%02u", elfsh_get_symbol_link(table + index)), revm_colorstr(sect_name), off); } else { snprintf(buff, sizeof(buff), " %s %s %s %s %s%s %s%s %s%-6s\n", revm_colornumber("[%03u]", index), revm_coloraddress(XFMT, (eresi_Addr) elfsh_get_symbol_value(table + index) + file->rhdr.base), revm_colortypestr_fmt("%-8s", type), revm_colorstr_fmt("%-15s", name), revm_colorfieldstr("sz:"), revm_colornumber("%06u", elfsh_get_symbol_size(table + index)), revm_colorfieldstr("foff:"), revm_colornumber("%06u", foff), revm_colorfieldstr("scop:"), revm_colortypestr_fmt("%-6s", bind)); } if (regx == NULL || (regx != NULL && regexec(regx, buff, 0, 0, 0) == 0)) { /* If the user ask quit, we just break */ if (revm_output(buff) == -1) break; } revm_endline(); } revm_endline(); revm_output("\n"); PROFILER_ROUT(__FILE__, __FUNCTION__, __LINE__, 0); }