/*
 * unbzImage
 *
 * Coge un bzImage y extrae la imagen comprimida del kernel
 * 
 * Jose.Carlos.Luna@gmail.com / dreyer 2005
 *
 * Saludos a la gente de !dSR
 *
 * Compilar: gcc -o unbzimage unzImage.c -lbfd
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <bfd.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>


#define START_ADDR 0xc0100000

#define COMMANDS "SECTIONS { .text : { *(.data) } }"


void muere(const char * str, ...)
{
    va_list args;
    va_start(args, str);
    fprintf(stderr,"[-] ");
    vfprintf(stderr, str, args);
    fprintf(stderr,"\n");
    exit(1);
}

// Obtenido de: /usr/src/linux/lib/inflate.c
char gzvalidbytes[3]={0x1f,0x8b,0x8};
char gzvalidbytes2[3]={0x1f,0x9e,0x8};

// Basado en dress.c del toolkit fenris de Michal Zawleski
void unnm(char *elf,char *mapfile) {
    struct bfd_section* s;
    bfd *ibfd,*obfd;
    FILE *fd;
    char type[2],name[200];
    unsigned int addr;
    int symtop=0,i=0;
    void *acopy;
    asymbol *ptrs[50000];

    fd=fopen(mapfile,"r");
    if(fd==NULL) 
	    muere("No se pudo abrir %s",mapfile);

    bfd_init();
    ibfd = bfd_openr(elf,0);
    if (!ibfd) muere("bfd_openr() on source file");
    obfd = bfd_openw("kernel-dressed.elf","i586-pc-linux-gnulibc1");
    if (!obfd) muere("bfd_openw() on destination file");
    if (!bfd_check_format_matches(ibfd, bfd_object, 0)) 
	    muere("input ELF format problem");
    bfd_set_format (obfd, bfd_get_format(ibfd));
    bfd_set_start_address (obfd, bfd_get_start_address(ibfd));
    bfd_set_file_flags(obfd,(bfd_get_file_flags(ibfd) & bfd_applicable_file_flags(obfd)));
    bfd_set_arch_mach(obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));

    s=ibfd->sections;
    while(s) {
        struct bfd_section* os;
	      
        os=bfd_make_section_anyway(obfd,bfd_section_name(ibfd,s));
	if (!os) muere("can't create new section");

	bfd_set_section_size(obfd, os, bfd_section_size(ibfd,s));
	bfd_set_section_vma(obfd, os, bfd_section_vma (ibfd, s));

	// Esto es un truco sucio, pero no se si es posible con el ld
	// que genera el elf manipular las opciones de las secciones
	// Y si no lo hacemos al usar gdb no funcionara correctamente.
	
	bfd_set_section_flags(obfd, os,
		        (strcmp(s->name,".text"))?bfd_get_section_flags(ibfd,s):
			SEC_ALLOC|SEC_HAS_CONTENTS|SEC_LOAD|SEC_CODE);

        os->lma = s->lma;
        os->entsize=s->entsize;
        s->output_section = os;
    	s->output_offset = 0;
    	bfd_copy_private_section_data(ibfd, s, obfd, os);
     	s=s->next;
    }

    while(!feof(fd)) {
        asymbol *news;
	fscanf(fd,"%x %s %s\n",&addr,type,name);
	news = bfd_make_empty_symbol(obfd);
	news->name = strdup(name);
	news->section = bfd_make_section_old_way(obfd,".text");
	news->flags = BSF_LOCAL;
	news->value = addr-START_ADDR;
	ptrs[symtop++]=news;
    }

     ptrs[symtop]=0;
     acopy=malloc(sizeof(ptrs));
     memcpy(acopy,ptrs,sizeof(ptrs));

    if (!bfd_set_symtab(obfd, acopy, symtop)) muere("bfd_set_symtab failed"); 

    s=ibfd->sections;
    while (s) {
        int siz;
	    siz = s->size;
	if (siz>=0)
	    if (bfd_get_section_flags(ibfd, s) & SEC_HAS_CONTENTS) {
	        void* memhunk = malloc(siz);
		if (!memhunk) muere("malloc failed");
		if (!bfd_get_section_contents(ibfd, s, memhunk, 0, siz)) muere("get_section contents failed");
		if (!bfd_set_section_contents(obfd, s->output_section, memhunk, 0, siz)) muere("set_section_contents failed");
		free (memhunk);
	     }
         s=s->next;
     }
     bfd_copy_private_bfd_data (ibfd, obfd);

     bfd_close(ibfd);
     bfd_close(obfd);
}


int main(int argc,char *argv[]) {
    int fd,fdw,skip,rbytes,idx=0,found=0,totalbytes;
    char *membuf,buf[500];
    unsigned char sect;
    struct stat sb;
    FILE *ldscript;


    printf("[+] unbzImage - Obtiene una imagen del kernel no comprimida\n");
    printf("\033[1;31mluna@aditel.org\033[0m / dreyer 2005\n");

    if(argc<2) {
        printf("Uso: %s bzImage [<System.map.file>]\n",argv[0]);
	exit(1);
    }

    if (stat(argv[1], &sb)) 
        muere("No se pudo hacer stat de %s",argv[1]);

    fd=open(argv[1],O_RDONLY,0);

    if(fd<0) 
        muere("No se pudo abrir %s",argv[1]);

    
    // obtenido de: /usr/src/linux/arch/i386/boot/tools/build.c
    
    if(lseek(fd,497,SEEK_SET) != 497) 
	    muere("Ha fallado lseek");

    //leemos el numero de sectores que ocupa el setup
    //
    read(fd,&sect,1);
    printf("[+] Setup ocupa %d sectores\n",sect);
    skip=512+512*sect; // Hay que saltar el bootsector + setup

    if(lseek(fd,skip,SEEK_SET) != skip) 
	    muere("Ha fallado lseek");

    membuf=(void *)malloc(sb.st_size-skip+1);

    while((rbytes=read(fd,&buf,500))>0) {
        memcpy(&membuf[idx],buf,rbytes);
	idx+=rbytes;
    }

    //Buscamos por el inicio de la cabecera gzip
    
    totalbytes=idx;
    idx=0;

    while(found<sizeof(gzvalidbytes) && idx<totalbytes) {
        if(membuf[idx]==gzvalidbytes[found] ||
	   membuf[idx]==gzvalidbytes2[found] ) 
		found++; 
	else
		found=0;
	idx++;
    }

    if(idx>=totalbytes)
	    muere("Cabecera gzip no encontrada");

    idx-=3;

    // Es posible que encontremos un falso positivo
    // solucion como ejercicio al lector ;)
    
    printf("[+] Encontrada posible cabecera gzip en offset %d\n",idx);
    printf("[+] Volcando en kernel.bin.gz\n");

    fdw=open("kernel.bin.gz",O_WRONLY|O_CREAT|O_TRUNC,0600);
    if(fdw<0) 
        muere("No se pudo escribir kernel.bin.gz");

    printf("[+] Escribiendo %d bytes en kernel.bin.gz\n",totalbytes-idx);
    while(idx<totalbytes-1) {
	    int howmany;
	    howmany=(idx+500>totalbytes-1)?totalbytes-idx:500;
	    write(fdw,&membuf[idx],howmany);
	    idx+=howmany;
    }
    close(fd);
    close(fdw);
    printf("[+] Descomprimiendo imagen -> kernel.bin \n");

    system("gunzip kernel.bin.gz"); 

    printf("[+] Convirtiendo imagen binaria a ejecutable ELF -> kernel.elf \n");


    ldscript=fopen("ld.scr","w");
    fprintf(ldscript,COMMANDS);
    fclose(ldscript);

    if(system("ld -s -o kernel.elf -b binary kernel.bin  -Ttext 0xc0100000 --oformat elf32-i386 -T ld.scr")) 
        muere("Error generando elf");

    if(argc>2) {
      printf("[+] Aņadiendo simbolos de %s -> kernel-dressed.elf\n",argv[2]);
      unnm("kernel.elf",argv[2]);
    }

    printf("[+] end :) \n");

}

