/*
* (c) petter wahlman, badeip@binary-art.net
*
* binary analysis tool: combined cat, dd, hexdump
*
* To get colorized output with a pager, use e.g 'less'
* with -R
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#include <signal.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <bat.h>
static u32 opt_hex_print = 0;
static u32 opt_verbose = 0;
static u32 opt_count = 0;
u32 print_usage(void)
{
fprintf(stderr, "by petter wahlman. badeip@binary-art.net\n");
fprintf(stderr, "usage: bat source [option(s)]\n"
" -s, --soff <offset>\n"
" -d, --doff <offset>\n"
" -c, --count <bytes> dump #bytes data\n"
" -h, --hex dump output in hex/dwords/ascii\n"
" -W, --nodwords don't display 32-bit hex values\n"
" -C, --nocolors don't use colorized output\n"
" -e, --eswap swap endianness of 32-bit values in hex output\n"
" -v, --verbose display verbose output [only if printing to terminal]\n"
"\n"
);
exit(1);
}
#define HEX_SWAP_ENDIAN 1
#define HEX_PRINT_DWORDS 2
#define HEX_PRINT_COLORS 4
off_t g_src_offset;
u32 g_quit = 0;
void sigint_handler(s32 sig) { g_quit = 1; }
static void hex_print(s32 fd, u8 *data, u32 bytes, u32 flags)
{
s8 line[512];
s32 i;
if (!bytes)
return;
i = 0;
while (i < bytes) {
u32 idx, len, chunk, values;
u32 colorize;
len = 0;
//---[ offset: ]---
len += sprintf(line, "0x%08lx |", g_src_offset);
//---[ 4 chunks of 4 characters ]---
idx = 0;
colorize = 0;
for (chunk = 0; chunk < 4; chunk++) {
colorize ^= 1;
if (colorize && (flags & HEX_PRINT_COLORS))
len += sprintf(&line[len], LIGHT);
//---[ 1 * 4 chars ]---
values = 0;
while (values++ < 4)
len += sprintf(&line[len], "%.2x ", data[i + idx++]);
if (flags & HEX_PRINT_COLORS)
len += sprintf(&line[len], NORMAL);
}
len += sprintf(&line[len], "|");
//---[ 4 * 1 dword ]----
idx = 0;
if (flags & HEX_PRINT_DWORDS) {
values = 0;
while (values++ < 4) {
colorize ^= 1;
if (colorize && (flags & HEX_PRINT_COLORS))
len += sprintf(&line[len], LIGHT);
len += sprintf(&line[len], "%.8x ", flags & HEX_SWAP_ENDIAN ? htonl(*(u32 *)&data[i + idx]) : *(u32 *)&data[i + idx]) ;
idx += sizeof(u32);
if (flags & HEX_PRINT_COLORS)
len += sprintf(&line[len], NORMAL);
}
len += sprintf(&line[len], "|");
}
//---[ 16 * 1 chars ]----
idx = 0;
values = 0;
while (values++ < 16) {
if (isprint(data[i + idx]))
len += sprintf(&line[len], "%c", data[i + idx++]);
else {
if (flags & HEX_PRINT_COLORS) {
sprintf(&line[len], RED);
len += strlen(RED);
}
len += sprintf(&line[len], "."), idx++;
if (flags & HEX_PRINT_COLORS) {
sprintf(&line[len], NORMAL);
len += strlen(NORMAL);
}
}
}
len += sprintf(&line[len], "|");
i += 16;
g_src_offset += 16;
puts(line);
if (g_quit) {
printf("bye\n");
break;
}
}
return;
}
int main(int argc, char **argv)
{
struct sigaction act;
struct stat st;
off_t soff, doff;
u8 buf[8192];
s8 *source;
s8 *dest;
u32 hex_flags;
u32 blocksize;
u32 count;
u32 fd, fdd;
u32 nr;
u32 rc = 1;
soff = 0;
doff = 0;
count = 0;
fd = fileno(stdin);
fdd = fileno(stdout);
blocksize = sizeof(buf);
hex_flags = HEX_PRINT_DWORDS | HEX_PRINT_COLORS;
// handle SIGINT:
act.sa_handler = sigint_handler;
act.sa_flags = 0;
sigemptyset (&act.sa_mask);
sigaction(SIGINT, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
while (1) {
u32 c;
s32 option_index = 0;
static struct option long_options[] = {
{ "soff", 1, 0, 's' },
{ "doff", 1, 0, 'd' },
{ "count", 1, 0, 'c' },
{ "hex", 0, 0, 'x' },
{ "nodwords",0, 0, 'W' },
{ "nocolors",0, 0, 'C' },
{ "eswap", 0, 0, 'e' },
{ "verbose", 0, 0, 'v' },
{ NULL, 0, 0, 0 }
};
c = getopt_long(argc, argv, "s:d:c:hWCexv", long_options, &option_index);
if (-1 == c)
break;
switch (c) {
case 's':
soff = strtoul(optarg, NULL, 0x0);
break;
case 'd':
doff = strtoul(optarg, NULL, 0x0);
break;
case 'c':
count = strtoul(optarg, NULL, 0x0);
opt_count = 1;
break;
case 'x':
opt_hex_print = 1;
break;
case 'W':
hex_flags &= ~HEX_PRINT_DWORDS;
break;
case 'C':
hex_flags &= ~HEX_PRINT_COLORS;
break;
case 'e':
hex_flags |= HEX_SWAP_ENDIAN;
break;
case 'v':
if (isatty(1))
opt_verbose = 1;
break;
default:
print_usage();
}
}
if (argc > optind) {
source = argv[optind++];
fd = open(source, O_RDONLY);
if (-1 == fd) {
perror(source);
return rc;
}
fstat(fd, &st);
if (soff > st.st_size) {
fprintf(stderr, "source offset: 0x%08lx > file size: 0x%08lx\n", soff, st.st_size);
return rc;
}
g_src_offset = lseek(fd, soff, SEEK_SET);
if (((off_t)-1 == g_src_offset) || (soff != g_src_offset)) {
fprintf(stderr, "unable to seek to offset: 0x%08lx\n", soff);
close(fd);
return rc;
}
} else // can't seek on stdin
soff = 0;
if (argc > optind) {
dest = argv[optind++];
fdd = open(dest, O_WRONLY | O_CREAT, 0644);
if (-1 == fdd) {
perror(dest);
close(fd);
return rc;
}
fstat(fd, &st);
if (soff > st.st_size) {
fprintf(stderr, "offset: 0x%08lx > file size: 0x%08lx\n", soff, st.st_size);
return rc;
}
#if 1 // this is legal, but rarely wanted:
if (doff != lseek(fdd, doff, SEEK_SET)) {
fprintf(stderr, "unable to seek to offset: 0x%08lx\n", soff);
close(fd);
return rc;
}
#endif
} else // can't seek on stdout
doff = 0;
if (opt_verbose) {
printf("src: offset: 0x%08lx (%.10ld): %s\n", soff, soff, source ? source : "stdin");
printf("dst: offset: 0x%08lx (%.10ld): %s\n\n", doff, doff, dest ? dest : "stdout");
}
if (opt_count) {
if (blocksize > count)
blocksize = count;
}
nr = 0;
do {
if (opt_count) {
count -= nr;
if (!count)
break;
if (blocksize > count)
blocksize = count;
}
nr = read(fd, buf, blocksize);
if (-1 == nr)
break;
opt_hex_print ? hex_print(fdd, buf, nr, hex_flags) : write(fdd, buf, nr);
if (g_quit)
break;
} while(nr);
close(fd);
if (dest)
close(fdd);
return 0;
}