All pastes #827726 Raw Edit

Untitled

public text v1 · immutable
#827726 ·published 2007-12-21 23:22 UTC
rendered paste body
/*
 * hikaru.c
 *
 * Used for parsing a binary file and determining
 * possible instruction sequences.
 *
 * Requires libdisasm 0.21-pre2, and libelf 0.8.4
 *
 * Email all questions/comments to comsatcat at gmail.com
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <err.h>
#include <sysexits.h>
#include <sys/mman.h>
#include <libelf.h>
#include <gelf.h>
#include <libdis.h>

#define TRUE    1
#define FALSE   2
#define M_ANALYZE       1
#define M_PROCESS       2

#define MAX_SIZE 32

void usage(char *);
int process(char *, int bytes, Elf *);
uint32_t getentrypoint(Elf *);
void listsections(Elf *);
void getsectioninfo(char *, Elf *);
void x86dis_manual_print( x86_insn_t *, void *);
void analyze(char *, Elf *);
int analyze_process(char *, Elf *);

struct {
        uint32_t offset;
        uint32_t size;
} si;

int fd;
int output = FALSE;
char outbuf[8192];

int main(int argc, char **argv) {
        int bytes = 0;
        int ch, list = 0, mode = 0;
        char *file = NULL, *section;
        enum x86_options options = opt_none;
        Elf *elf;

        while((ch = getopt(argc, argv, "m:f:b:s:l")) != -1) {
                switch(ch) {
                        case 'm':
                                if(!strcasecmp(optarg, "analyze"))
                                        mode = M_ANALYZE;
                                else if(!strcasecmp(optarg, "process"))
                                        mode = M_PROCESS;
                        break;
                        case 'f':
                                if(!(file = malloc(strlen(optarg)+1))) {
                                        perror("malloc");
                                        exit(1);
                                }
                                strcpy(file, optarg);
                        break;
                        case 'b':
                                bytes = atoi(optarg);
                        break;
                        case 's':
                                section = malloc(strlen(optarg) + 1);
                                strcpy(section, optarg);
                        break;
                        case 'l':
                                list = TRUE;
                        break;
                }
        }

        if((!mode) || (!file) || ((mode == M_PROCESS) && (!bytes) && (!section)))
                usage(argv[0]);

        if((fd = open(file, O_RDONLY)) == -1) {
                perror("open");
                exit(1);
        }

        if(elf_version(EV_CURRENT) == EV_NONE) {
                errx(EX_SOFTWARE, "ELF library initialization failed: %s", elf_errmsg(-1));
        }

        if(!(elf = elf_begin(fd, ELF_C_READ, NULL))) {
                errx(EX_NOINPUT, "open \"%s\" failed", file);
        }

        if(elf_kind(elf) != ELF_K_ELF) {
                errx(EX_DATAERR, "%s is not an ELF object.", file);
        }

        if(mode == M_ANALYZE) {
                if(list) {
                        printf("Entry Point: 0x%x\n\n", getentrypoint(elf));
                        listsections(elf);
                } else {
                        analyze(file, elf);
                }
        } else if(mode == M_PROCESS) {
                x86_init(options, NULL);
                if(process(section, bytes, elf))
                        errx(EX_SOFTWARE, "failed to process file.");
        }

        close(fd);
        elf_end(elf);
        exit(0);
}

void analyze(char *file, Elf *elf) {
        char *name;
        size_t shstrndx;
        Elf_Scn *scn = NULL;
        GElf_Shdr shdr;

        if(elf_getshstrndx(elf, &shstrndx)) {
                errx(EX_SOFTWARE, "getshstrndx failed: %s", elf_errmsg(-1));
        }

        while ((scn = elf_nextscn(elf, scn)) != NULL) {
                if (gelf_getshdr(scn, &shdr) != &shdr)
                        errx(EX_SOFTWARE, "getshdr failed: %s.", elf_errmsg(-1));
                if (!(name = elf_strptr(elf, shstrndx, shdr.sh_name)))
                        errx(EX_SOFTWARE, "elf_strptr failed: %s.", elf_errmsg(-1));

                if(shdr.sh_flags & PF_X) {
                        printf("Analyzing [%04jd] (%s) @ offset 0x%08x ", (uintmax_t) elf_ndxscn(scn), name, (uint32_t)shdr.sh_offset);
                        printf("(%d bytes)\n", (uint32_t)shdr.sh_size);
                        analyze_process(name, elf);
                }
        }
}

uint32_t getentrypoint(Elf *elf) {
        GElf_Phdr phdr;
        gelf_getphdr(elf, 0, &phdr);
        return(phdr.p_vaddr - phdr.p_offset);
}

void listsections(Elf *elf) {
        char *name;
        size_t shstrndx;
        Elf_Scn *scn = NULL;
        GElf_Shdr shdr;

        if(elf_getshstrndx(elf, &shstrndx)) {
                errx(EX_SOFTWARE, "getshstrndx failed: %s", elf_errmsg(-1));
        }

        printf("Index\tOffset\t\tSize (bytes)\tPermissions\tSection\n");
        printf("-----\t-----------\t------------\t-----------\t-------\n");
        while ((scn = elf_nextscn(elf, scn)) != NULL) {
                if (gelf_getshdr(scn, &shdr) != &shdr)
                        errx(EX_SOFTWARE, "getshdr failed: %s.", elf_errmsg(-1));
                if (!(name = elf_strptr(elf, shstrndx, shdr.sh_name)))
                        errx(EX_SOFTWARE, "elf_strptr failed: %s.", elf_errmsg(-1));
                printf("%04jd\t0x%08x\t", (uintmax_t) elf_ndxscn(scn), (uint32_t)shdr.sh_offset);
                printf("%d\t\t", (uint32_t)shdr.sh_size);
                if(shdr.sh_flags & PF_R)
                        printf("r");
                else
                        printf("-");
                if(shdr.sh_flags & PF_W)
                        printf("w");
                else
                        printf("-");
                if(shdr.sh_flags & PF_X)
                        printf("x");
                else
                        printf("-");

                printf("\t\t%s\n", name);
        }
        printf("\n");
}

void getsectioninfo(char *section, Elf *elf) {
        char *name;
        size_t shstrndx;
        Elf_Scn *scn = NULL;
        GElf_Shdr shdr;

        if(elf_getshstrndx(elf, &shstrndx)) {
                errx(EX_SOFTWARE, "getshstrndx failed: %s", elf_errmsg(-1));
        }
        while ((scn = elf_nextscn(elf, scn)) != NULL) {
                if (gelf_getshdr(scn, &shdr) != &shdr)
                        errx(EX_SOFTWARE, "getshdr failed: %s.", elf_errmsg(-1));
                if (!(name = elf_strptr(elf, shstrndx, shdr.sh_name)))
                        errx(EX_SOFTWARE, "elf_strptr failed: %s.", elf_errmsg(-1));

                if(!strcmp(name, section)) {
                        si.offset = shdr.sh_offset;
                        si.size = shdr.sh_size;
                }
        }
}

int process(char *section, int bytes, Elf *elf) {
        uint32_t entry;
        unsigned char *image, *buf;
        struct stat s;
        int i;

        if(fstat(fd, &s) == -1) {
                perror("fstat");
                return(1);
        }

        entry = getentrypoint(elf);
        getsectioninfo(section, elf);

        if(!si.offset && !si.size) {
                fprintf(stderr, "Section '%s' does not exist.\n", section);
                return(1);
        }

        if((int)(image = (unsigned char *)mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0)) == -1) { // fd is global
                fprintf(stderr, "Unable to map anonymous memory: %s\n", strerror(errno));
                return(1);
        }

        for(i=0; i<si.size; i++) {
                buf = image;
                x86_disasm_range(buf, entry, si.offset+i, bytes, x86dis_manual_print, NULL);
                memset(outbuf, 0, sizeof(outbuf));
        }

        return(0);
}

int analyze_process(char *section, Elf *elf) {
        uint32_t entry;
        unsigned char *image, *buf;
        struct stat s;
        int i, t;

        if(fstat(fd, &s) == -1) {
                perror("fstat");
                return(1);
        }

        entry = getentrypoint(elf);
        getsectioninfo(section, elf);

        if(!si.offset && !si.size) {
                fprintf(stderr, "Section '%s' does not exist.\n", section);
                return(1);
        }

        if((int)(image = (unsigned char *)mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0)) == -1) { // fd is global
                fprintf(stderr, "Unable to map anonymous memory: %s\n", strerror(errno));
                return(1);
        }

        for(i=0; i<si.size; i++) {
                for(t=MAX_SIZE; t>0; t--) {
                        buf = image;
                        if(t > si.size)
                                t = si.size;

                        x86_disasm_range(buf, entry, si.offset+i, t, x86dis_manual_print, NULL);
                        memset(outbuf, 0, sizeof(outbuf));
                }
        }

        return(0);
}

// this function was initially ripped from x86dis.c
// however its going to be heavily modified and work
// in a very _gross_ way.
void x86dis_manual_print( x86_insn_t *insn, void *arg ) {
        char line[4096];
        int i;

        if ( x86_format_insn(insn, line, 4096, att_syntax) <= 0 ) {
                return;
        }

        sprintf(outbuf, "%s%08X", outbuf, (uint32_t)insn->addr);
        for ( i = 0; i < 10; i++ ) {
                if ( i < insn->size ) {
                        sprintf(outbuf, "%s %02X", outbuf, insn->bytes[i]);
                } else {
                        strcat(outbuf, "   ");
                }
        }

        if(!strcmp(line, "ret\t")) {
                sprintf(outbuf, "%s\t%s\n\n", outbuf, line);
                printf("%s", outbuf);
                memset(outbuf, 0, sizeof(outbuf));
        } else {
                sprintf(outbuf, "%s\t%s\n", outbuf, line);
        }
}

void usage(char *p) {
        printf("usage: %s <-f file> <[-m analyze [-l]] [-m process <-s section> <-b bytes>]>\n\n", p);
        printf("Hikaru operates in two modes, either enalyze mode or process mode.\n");
        printf("Both modes take a -f argument which defines a file to process.  \n\n");
        printf("Analyze mode has a single optional argument, -l, which will list \n");
        printf("the elf header sections available for processing.  If Analyze mode \n");
        printf("is run without the -l flag, it will analyze all executable sections \n");
        printf("of the target binary displaying all instructions up to MAX_SIZE.\n\n");
        printf("Process mode is similar to analyze, however it takes a specific \n");
        printf("section to process and the size of the instruction to analyze in bytes.\n\n");
        printf("As of this release, invalid operands are not displayed, however if a \n");
        printf("instruction sequence contains a ret and still has invalid operands \n");
        printf("before the ret instruction, all valid instructions up to the ret will \n");
        printf("be displayed.\n\n");
        exit(0);
}