{"id":19060258,"url":"https://github.com/4l3x777/reflective_loader","last_synced_at":"2025-04-13T19:53:27.539Z","repository":{"id":219330248,"uuid":"748760786","full_name":"4l3x777/reflective_loader","owner":"4l3x777","description":"EXE PE x86_64 loader","archived":false,"fork":false,"pushed_at":"2024-03-05T16:52:39.000Z","size":1661,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T10:38:45.362Z","etag":null,"topics":["asm","reflective-pe-loaders","reverse-engineering","system-programming"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/4l3x777.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-01-26T17:51:52.000Z","updated_at":"2025-01-15T22:06:48.000Z","dependencies_parsed_at":"2025-01-02T12:34:06.038Z","dependency_job_id":null,"html_url":"https://github.com/4l3x777/reflective_loader","commit_stats":null,"previous_names":["4l3x777/reflective_loader"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4l3x777%2Freflective_loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4l3x777%2Freflective_loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4l3x777%2Freflective_loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4l3x777%2Freflective_loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4l3x777","download_url":"https://codeload.github.com/4l3x777/reflective_loader/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248774216,"owners_count":21159527,"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":["asm","reflective-pe-loaders","reverse-engineering","system-programming"],"created_at":"2024-11-09T00:14:09.983Z","updated_at":"2025-04-13T19:53:27.510Z","avatar_url":"https://github.com/4l3x777.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Reflective Loader\n\n## Задача - реализовать простейший PE Loader, который будет считывать с диска PE (EXE) и запускать его из памяти\n\n## Основная функция загрузчика\n\n```C++\nHANDLE ReflectiveLoader::native_reflective_execution() {\n    PIMAGE_DOS_HEADER           dos, doshost;\n    PIMAGE_NT_HEADERS           nt, nthost;\n    PIMAGE_SECTION_HEADER       sh;\n    PIMAGE_THUNK_DATA           oft, ft;\n    PIMAGE_IMPORT_BY_NAME       ibn;\n    PIMAGE_IMPORT_DESCRIPTOR    imp;\n    PIMAGE_DELAYLOAD_DESCRIPTOR del;\n    PIMAGE_EXPORT_DIRECTORY     exp;\n    PIMAGE_TLS_DIRECTORY        tls;\n    PIMAGE_TLS_CALLBACK* callbacks;\n    PIMAGE_RELOC                list;\n    PIMAGE_BASE_RELOCATION      ibr;\n    DWORD                       rva;\n    PDWORD                      adr;\n    PDWORD                      sym;\n    PWORD                       ord;\n    PBYTE                       ofs;\n    PCHAR                       str, name;\n    HMODULE                     dll;\n    ULONG_PTR                   ptr;\n    Start_t                     Start;              // EXE\n    LPVOID                      cs = NULL, base, host;\n    DWORD                       i, cnt;\n    HANDLE                      hThread;\n    WCHAR                       buf[MAX_PATH + 1];\n    DWORD                       size_of_img;\n\n    base = PE.data();\n    dos = (PIMAGE_DOS_HEADER)base;\n    nt = RVA2VA(PIMAGE_NT_HEADERS, base, dos-\u003ee_lfanew);\n\n    // before doing anything. check compatibility between exe/dll and host process.\n    host = GetModuleHandleA(NULL);\n    doshost = (PIMAGE_DOS_HEADER)host;\n    nthost = RVA2VA(PIMAGE_NT_HEADERS, host, doshost-\u003ee_lfanew);\n\n    if (nt-\u003eFileHeader.Machine != nthost-\u003eFileHeader.Machine) {\n        printf(\"Host process %08lx and file %08lx are not compatible...cannot load.\\n\",\n            nthost-\u003eFileHeader.Machine, nt-\u003eFileHeader.Machine);\n        return NULL;\n    }\n\n    printf(\"Allocating %\" PRIi32 \" (0x%\" PRIx32 \") bytes of RWX memory for file\\n\",\n        nt-\u003eOptionalHeader.SizeOfImage, nt-\u003eOptionalHeader.SizeOfImage);\n\n    cs = VirtualAlloc(\n        NULL, nt-\u003eOptionalHeader.SizeOfImage + 4096,\n        MEM_COMMIT | MEM_RESERVE,\n        PAGE_EXECUTE_READWRITE);\n\n    if (cs == NULL) return NULL;\n\n    printf(\"Copying Headers\\n\");\n    memcpy(cs, base, nt-\u003eOptionalHeader.SizeOfHeaders);\n\n    printf(\"Copying each section to RWX memory %p\\n\", cs);\n    sh = IMAGE_FIRST_SECTION(nt);\n\n    for (i = 0; i \u003c nt-\u003eFileHeader.NumberOfSections; i++) {\n        memcpy((PBYTE)cs + sh[i].VirtualAddress,\n            (PBYTE)base + sh[i].PointerToRawData,\n            sh[i].SizeOfRawData);\n    }\n\n    rva = nt-\u003eOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;\n\n    if (rva != 0) {\n        printf(\"Applying Relocations\\n\");\n\n        ibr = RVA2VA(PIMAGE_BASE_RELOCATION, cs, rva);\n        ofs = (PBYTE)cs - nt-\u003eOptionalHeader.ImageBase;\n\n        while (ibr-\u003eVirtualAddress != 0) {\n            list = (PIMAGE_RELOC)(ibr + 1);\n\n            while ((PBYTE)list != (PBYTE)ibr + ibr-\u003eSizeOfBlock) {\n                if (list-\u003etype == IMAGE_REL_TYPE) {\n                    *(ULONG_PTR*)((PBYTE)cs + ibr-\u003eVirtualAddress + list-\u003eoffset) += (ULONG_PTR)ofs;\n                }\n                else if (list-\u003etype != IMAGE_REL_BASED_ABSOLUTE) {\n                    printf(\"ERROR: Unrecognized Relocation type %08lx.\\n\", list-\u003etype);\n                    goto pe_cleanup;\n                }\n                list++;\n            }\n            ibr = (PIMAGE_BASE_RELOCATION)list;\n        }\n    }\n\n    rva = nt-\u003eOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;\n\n    if (rva != 0) {\n        printf(\"Processing the Import Table\\n\");\n\n        imp = RVA2VA(PIMAGE_IMPORT_DESCRIPTOR, cs, rva);\n\n        // For each DLL\n        for (; imp-\u003eName != 0; imp++) {\n            name = RVA2VA(PCHAR, cs, imp-\u003eName);\n\n            printf(\"Loading %s\\n\", name);\n            dll = LoadLibraryA(name);\n\n            // Resolve the API for this library\n            oft = RVA2VA(PIMAGE_THUNK_DATA, cs, imp-\u003eOriginalFirstThunk);\n            ft = RVA2VA(PIMAGE_THUNK_DATA, cs, imp-\u003eFirstThunk);\n\n            // For each API\n            for (;; oft++, ft++) {\n                // No API left?\n                if (oft-\u003eu1.AddressOfData == 0) break;\n\n                // Resolve by ordinal?\n                if (IMAGE_SNAP_BY_ORDINAL(oft-\u003eu1.Ordinal)) {\n                    ft-\u003eu1.Function = (ULONG_PTR)GetProcAddress(dll, (LPCSTR)IMAGE_ORDINAL(oft-\u003eu1.Ordinal));\n                }\n                else {\n                    // Resolve by name\n                    ibn = RVA2VA(PIMAGE_IMPORT_BY_NAME, cs, oft-\u003eu1.AddressOfData);\n                    ft-\u003eu1.Function = (ULONG_PTR)GetProcAddress(dll, ibn-\u003eName);\n                }\n            }\n        }\n    }\n\n    rva = nt-\u003eOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress;\n\n    if (rva != 0) {\n        printf(\"Processing Delayed Import Table\\n\");\n\n        del = RVA2VA(PIMAGE_DELAYLOAD_DESCRIPTOR, cs, rva);\n\n        // For each DLL\n        for (; del-\u003eDllNameRVA != 0; del++) {\n            name = RVA2VA(PCHAR, cs, del-\u003eDllNameRVA);\n\n            printf(\"Loading %s\\n\", name);\n            dll = LoadLibraryA(name);\n\n            if (dll == NULL) continue;\n\n            // Resolve the API for this library\n            oft = RVA2VA(PIMAGE_THUNK_DATA, cs, del-\u003eImportNameTableRVA);\n            ft = RVA2VA(PIMAGE_THUNK_DATA, cs, del-\u003eImportAddressTableRVA);\n\n            // For each API\n            for (;; oft++, ft++) {\n                // No API left?\n                if (oft-\u003eu1.AddressOfData == 0) break;\n\n                // Resolve by ordinal?\n                if (IMAGE_SNAP_BY_ORDINAL(oft-\u003eu1.Ordinal)) {\n                    ft-\u003eu1.Function = (ULONG_PTR)GetProcAddress(dll, (LPCSTR)IMAGE_ORDINAL(oft-\u003eu1.Ordinal));\n                }\n                else {\n                    // Resolve by name\n                    ibn = RVA2VA(PIMAGE_IMPORT_BY_NAME, cs, oft-\u003eu1.AddressOfData);\n                    ft-\u003eu1.Function = (ULONG_PTR)GetProcAddress(dll, ibn-\u003eName);\n                }\n            }\n        }\n    }\n\n    /**\n      Execute TLS callbacks. These are only called when the process starts, not when a thread begins, ends\n      or when the process ends. TLS is not fully supported.\n    */\n    rva = nt-\u003eOptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress;\n    if (rva != 0) {\n        printf(\"Processing TLS directory\\n\");\n\n        tls = RVA2VA(PIMAGE_TLS_DIRECTORY, cs, rva);\n\n        // address of callbacks is absolute. requires relocation information\n        callbacks = (PIMAGE_TLS_CALLBACK*)tls-\u003eAddressOfCallBacks;\n        printf(\"AddressOfCallBacks : %p\\n\", callbacks);\n\n        if (callbacks) {\n            while (*callbacks != NULL) {\n                // call function\n                printf(\"Calling %p\\n\", *callbacks);\n                (*callbacks)((LPVOID)cs, DLL_PROCESS_ATTACH, NULL);\n                callbacks++;\n            }\n        }\n    }\n\n    size_of_img = nt-\u003eOptionalHeader.SizeOfImage;\n    Start = RVA2VA(Start_t, cs, nt-\u003eOptionalHeader.AddressOfEntryPoint);\n\n    printf(\"Wiping Headers from memory\\n\");\n    memset(cs, 0, nt-\u003eOptionalHeader.SizeOfHeaders);\n    memset(base, 0, nt-\u003eOptionalHeader.SizeOfHeaders);\n\n    // Create a new thread for this process.\n    // Since we replaced exit-related API with RtlExitUserThread in IAT, once an exit-related API is called, the\n    // thread will simply terminate and return back here. Of course, this doesn't work\n    // if the exit-related API is resolved dynamically.\n    printf(\"Creating thread for entrypoint of EXE : %p\\n\\n\", (PVOID)Start);\n    hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Start, NULL, 0, NULL);\n\n    if (hThread != NULL) {\n        return hThread;\n    }\npe_cleanup:\n    // if memory allocated\n    if (cs != NULL) {\n        // release\n        printf(\"Releasing memory\\n\");\n        VirtualFree(cs, 0, MEM_DECOMMIT | MEM_RELEASE);\n    }\n```\n\n## Tests\n\n### Проверочный MessageBox ASM x86\n\n```C++\nformat PE GUI; at 0xfe0000\n\ninclude '..\\include\\win32ax.inc'\n\n.code\n\n  start:\n        invoke  MessageBox,HWND_DESKTOP,\"Hi! I'm the example program from .code!\",invoke GetCommandLine,MB_OK\n        db 4096 dup(0x90)\n        invoke  MessageBox,HWND_DESKTOP,\"Hi! I'm the example program from .code!\",invoke GetCommandLine,MB_OK\n        db 4096 dup(0x90)\n        invoke  MessageBox,HWND_DESKTOP,\"Hi! I'm the example program from .code!\",invoke GetCommandLine,MB_OK\n        jmp Start2\n\n.end start\n\nsection 'code2' readable writeable executable\n Start2:\n        invoke  MessageBox,HWND_DESKTOP,\"Hi! I'm the example program from code2!\",invoke GetCommandLine,MB_OK\n        invoke  ExitProcess,0\n\nsection '.reloc' fixups data readable discardable       ; needed for Win32s\n```\n\n### Проверочный MessageBox x86_64\n\n```C++\n#include \u003ciostream\u003e\n#include \u003cWindows.h\u003e\n\nint main(int argc, char* argv[]) {\n    MessageBoxA(\n        HWND_DESKTOP,\n        (LPCSTR)\"Hi! I'm the example program from .code!\",\n        (LPCSTR)GetCommandLineA(),\n        MB_OK\n    );\n\n    return 0;\n}\n```\n\n## bin\n\n+ содержит рефлективные загрузчики x86_64 и набор тестовых PE\n\n## Пример работы\n\n![alt text](/img/reflective_loader.gif)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4l3x777%2Freflective_loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4l3x777%2Freflective_loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4l3x777%2Freflective_loader/lists"}