/* hexed.c - A command-line driven file editor This tool can do most common tasks that regular hex editors can, such as: inserting, replacing, removing and copying data, searching and diffing files, and viewing parts of files with hex+ASCII. */ #include #include #include typedef unsigned char u8; // this struct is used for three unique purposes: // files, arrays, and "filler" (empty) content typedef struct { char *name; u8 *data; int size; } buffer; void strip(char *str) { if (!str) return; int i; for (i = 0; str[i] >= ' ' && str[i] <= '~'; i++); str[i] = 0; } int is_empty(buffer *buf) { if (!buf) return 1; if (!buf->data || buf->size < 1) return 2; return 0; } void close_buffer(buffer *buf) { if (!buf) return; if (buf->name) free(buf->name); if (buf->data) free(buf->data); memset(buf, 0, sizeof(buffer)); } int load_file(buffer *buf, char *name) { if (!buf || !name) return -1; if (buf->data) free(buf->data); memset(buf, 0, sizeof(buffer)); FILE *f = fopen(name, "rb"); if (!f) return -2; buf->name = strdup(name); fseek(f, 0, SEEK_END); int sz = ftell(f); if (sz < 1) return 0; // All good but nothing else to do rewind(f); buf->size = sz; buf->data = malloc(buf->size); fread(buf->data, 1, buf->size, f); fclose(f); return 0; } void save_buffer(buffer *buf) { if (!buf || !buf->name) return; FILE *f = fopen(buf->name, "wb"); if (!f || !buf->data || buf->size < 1) return; fwrite(buf->data, 1, buf->size, f); fclose(f); } void create_filler(buffer *buf, u8 fill, int size) { if (!buf || size < 1) return; if (buf->data) free(buf->data); memset(buf, 0, sizeof(buffer)); buf->data = calloc(size, 1); buf->size = size; memset(buf->data, fill, buf->size); } void swap_buffer(buffer *buf, int off, int len, int swap) { if (is_empty(buf) || swap <= 1 || off > buf->size - swap) return; if (off < 0) off = 0; if (len < 1) len = buf->size; if (off+len > buf->size) len = buf->size - off; len -= len % swap; if (len <= 0) return; u8 *temp = malloc(swap); int i, j; for (i = off; i < off+len; i += swap) { memcpy(temp, buf->data + i, swap); for (j = 0; j < swap; j++) buf->data[i+j] = temp[swap-j-1]; } free(temp); } void add_byte(buffer *buf, u8 x) { buf->data = realloc(buf->data, ++buf->size); buf->data[buf->size - 1] = x; } void read_array(buffer *array, char **str, int n_strs) { if (!array || !str || n_strs < 1) return; if (array->name) free(array->name); if (array->data) free(array->data); memset(array, 0, sizeof(buffer)); int i, j, len = 0, mode = 0; for (i = 0; i < n_strs; i++) { if (!str[i]) continue; int dg_left = 2, digit = 0, byte = 0; for (j = 0; str[i][j]; j++) { char c = str[i][j]; if (c < ' ' || c > '~') continue; if (c == '"') { mode = !mode; continue; } if (!mode) { int sub = 0; if (c >= '0' && c <= '9') sub = '0'; if (c >= 'A' && c <= 'F') sub = 'A' - 0xa; if (c >= 'a' && c <= 'f') sub = 'a' - 0xa; if (!sub) continue; digit = c - sub; dg_left--; } else { digit = c; dg_left = 0; } byte <<= 4; byte |= digit; if (dg_left <= 0) { add_byte(array, byte); byte = 0; dg_left = 2; } } if (mode && i < n_strs-1) add_byte(array, ' '); if (!mode && dg_left == 1) add_byte(array, byte); } } void apply_buffer(buffer *dst, buffer *src, int dst_pos, int src_pos, int len, int insert) { if (!dst || !src || (!dst->data && !src->data) || (dst->size < 1 && src->size < 1) || src_pos >= src->size) return; if (src_pos < 0) src_pos = 0; if (len < 1) len = src->size - src_pos; if (dst_pos < -len) dst_pos = -len; if (dst_pos > dst->size) dst_pos = dst->size; if (insert) { if (dst_pos < 0) dst_pos = 0; dst->data = realloc(dst->data, dst->size + len); if (dst_pos < dst->size) { memmove(dst->data + dst_pos + len, dst->data + dst_pos, dst->size - dst_pos); } dst->size += len; } else { if (dst_pos < 0) { src_pos = -dst_pos; if (src_pos > src->size) return; dst_pos = 0; } if (dst_pos + len > dst->size) { len = dst->size - dst_pos; if (len < 1) return; } } memcpy(dst->data + dst_pos, src->data + src_pos, len); } void remove_buffer(buffer *buf, int off, int len) { if (is_empty(buf) || off >= buf->size || off+len <= 0) return; if (len <= 0) len = buf->size; if (off < 0) { len += off; off = 0; } if (off+len >= buf->size) { buf->size = off; buf->data = realloc(buf->data, buf->size); return; } memmove(buf->data + off, buf->data + off+len, buf->size - (off+len)); buf->size -= len; buf->data = realloc(buf->data, buf->size); } void search_buffer(buffer *results, buffer *buf, buffer *term) { if (!results || is_empty(buf) || is_empty(term) || term->size > buf->size) return; close_buffer(results); int i, j, *res = NULL, count = 0; for (i = 0; i < buf->size - term->size; i++) { for (j = 0; j < term->size; j++) { if (buf->data[i+j] != term->data[j]) break; } if (j == term->size) { res = realloc(res, ++count * sizeof(int)); res[count-1] = i; printf("%#x\n", i); } } printf("\nTotal results: %d\n\n", count); results->data = (u8*)res; results->size = count * sizeof(int); } void diff_buffer(buffer *results, buffer *a, buffer *b) { if (!results || is_empty(a) || is_empty(b)) return; close_buffer(results); int sz = a->size < b->size ? a->size : b->size; int i, *diff = NULL, count = 0; for (i = 0; i < sz; i++) { if (a->data[i] == b->data[i]) continue; diff = realloc(diff, ++count * sizeof(int)); diff[count-1] = i; printf("%#x\n", i); } printf("\nTotal results: %d\n", count); results->data = (u8*)diff; results->size = count * sizeof(int); } #ifndef _WIN32_ #define PRINT_HL printf("\x1b[7;40m") #define PRINT_HL_END printf("\x1b[0m") #else #define PRINT_HL #define PRINT_HL_END #endif void view_buffer(buffer *b, int off, int len, buffer *offsets) { if (is_empty(b) || off >= b->size) return; if (len <= 0) len = b->size; if (off+len <= 0) return; if (off < 0) { len += off; off = 0; } if (off+len > b->size) len = b->size - off; int n_digits = 0, x = (off+len-1) & ~0xf; if (x) { while (x) { n_digits++; x >>= 4; } } else n_digits = 1; char *blank = calloc(50, 1); memset(blank, ' ', 48); int hl_idx = 0, old_idx = 0; int i = 0, idx, mode = 0, hl = 0, row_end; int *offs = offsets ? (int*)offsets->data : NULL; u8 *p = NULL; while (i < len) { idx = off + i; p = b->data + idx; row_end = i >= ((len-1) & ~0xf) ? ((len-1) & 0xf) : 0xf; if (offsets && hl_idx < offsets->size / sizeof(int) && idx == offs[hl_idx]) { PRINT_HL; hl = 1; hl_idx++; } if (!mode) { if (i % 16 == 0) { printf(" %0*x | ", n_digits, idx); old_idx = hl_idx; } printf("%02x ", *p); if (i % 16 == row_end) { printf("%s| ", blank + 48 - (15 - row_end) * 3); hl_idx = old_idx; mode = 1; i -= row_end + 1; } } else { putchar((*p >= ' ' && *p <= '~') ? *p : '.'); if (i % 16 == row_end) { printf("%s |\n", blank + 48 - (15 - row_end)); mode = 0; } } if (hl) { PRINT_HL_END; hl = 0; } i++; } printf(" %0*x\n", n_digits, off+len); free(blank); } char *options = "nNwpifFcCrsSdv"; // argument type requirements (used in reverse order) // one digit per argument (multiple digits per command) // 0 = end, 1 = string, 2 = input file, 3 = number, 4 = byte array // +8 if optional int arg_reqs[] = { 0xc1, 0xbb1, 0xbb32, 0x432, 0x432, // -n, -N, -w, -p, -i 0xb332, 0xb332, 0xbb232, 0xbb232, // -f, -F, -c, -C 0xb32, 0x42, 0x42, 0x922, 0xabb2 // -r, -s, -S, -d, -v }; void close_args(void ***args_ref, int n_args, int mode) { if (!args_ref || !*args_ref || n_args <= 0 || mode < 0 || mode >= strlen(options)) return; void **args = *args_ref; int i, req = arg_reqs[mode]; for (i = 0; i < n_args; i++) { int t = req & 7; req >>= 4; if ((t == 2 || t == 4) && args[i]) { close_buffer(args[i]); free(args[i]); args[i] = NULL; } } free(args); *args_ref = NULL; } char *usage_str = "Usage:\n" "hexed