{"id":24281026,"url":"https://github.com/xmegz/clrhost","last_synced_at":"2025-09-25T01:30:32.552Z","repository":{"id":81511031,"uuid":"282132837","full_name":"xmegz/clrhost","owner":"xmegz","description":".Net core crl application host start","archived":false,"fork":false,"pushed_at":"2025-01-02T16:16:49.000Z","size":11790,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-02T17:22:46.190Z","etag":null,"topics":["clr","dotnet","host","windows"],"latest_commit_sha":null,"homepage":"","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/xmegz.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":"2020-07-24T05:40:04.000Z","updated_at":"2025-01-02T16:16:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"3022656d-d5fb-4a68-b3bb-5368d59c6fde","html_url":"https://github.com/xmegz/clrhost","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmegz%2Fclrhost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmegz%2Fclrhost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmegz%2Fclrhost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xmegz%2Fclrhost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xmegz","download_url":"https://codeload.github.com/xmegz/clrhost/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234142670,"owners_count":18786022,"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":["clr","dotnet","host","windows"],"created_at":"2025-01-16T02:51:27.692Z","updated_at":"2025-09-25T01:30:31.463Z","avatar_url":"https://github.com/xmegz.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How to Embed .NET Core DLL into a Custom C++ Host Loader\n\n## Concept\nLoad .NET project DLLs directly from PE resources or ELF sections and initialize CoreCLR from these embedded resources.\n\n## Why better than single file publish ?\n\n**True Memory-Based Operation**\nThe resource injection technique allows embedded .NET DLLs to be loaded directly from memory without relying on the file system.\n\n**Greater Flexibility**\nInjected DLLs are placed in custom sections or embedded as resources within the host binary, enabling completely customizable loading, management, and execution.\n\n**Enhanced Security**\nThe injection method allows resources to be encrypted, reducing the chances of reverse engineering and manipulation.\n\n**Improved Startup Performance**\nThe native binary directly invokes CoreCLR from memory and loads the injected DLLs as resources, significantly reducing startup time.\n\n**Easier Diagnostics and Debugging**\nThe host code retains complete control over .NET runtime initialization, providing more precise diagnostic tools and logging capabilities.\n\n**Conclusion**\nWhile Single File Publish is straightforward and suitable for many use cases, bundling .NET DLLs at the end of the native binary comes with several limitations and potential issues. The presented resource injection technique addresses these challenges by offering greater flexibility, security, and performance.\n\n## Project Structure\n* **AppHostLinux** - Linux build project for the .NET Core startup (C++)\n* **AppHostWindows** - Windows build project for the .NET Core startup (C++)\n* **AppHostShared** - Shared source code for the .NET Core startup (C++).\n* **Hello** - A .NET Core project compiled into ''Hello.dll'', which is embedded as a resource/section into the AppHost binary (C#).\n* **InjectResourceLinux** - Injects 'Hello.dll' into 'AppHostLinux.Out' as an ELF section (C#).\n* **InjectResourceWindows** - Injects 'Hello.dll' into 'AppHostWindows.Exe' as a PE resource (C#).\n\n\n## Build instuctions\n\n\n### Prerequisites\n* Operating System: Windows 11\n* .NET SDK: Version 8.0 (64-bit)\n* Linux Environment: WSL with Ubuntu 20.04 LTS\n* IDE: Visual Studio Community 2022\n\n### Installing Dependencies in WSL\nRun the following commands in your WSL terminal to install dotnet-sdk and a build tools\n```bash\nwget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb\nsudo dpkg -i packages-microsoft-prod.deb\nrm packages-microsoft-prod.deb\nsudo apt-get update \u0026\u0026 sudo apt-get install -y dotnet-sdk-8.0\n\nsudo apt install -y openssh-server build-essential gdb rsync make zip\nwget https://github.com/microsoft/CMake/releases/download/v3.19.4268486/cmake-3.19.4268486-MSVC_2-Linux-x64.sh\nchmod +x cmake-3.19.4268486-MSVC_2-Linux-x64.sh\n./cmake-3.19.4268486-MSVC_2-Linux-x64.sh\n```\n\n### Clone repository\n\n```bash\ngit clone https://github.com/xmegz/clrhost.git\n```\n\n### Compile\n* Open the solution in 'Visual Studio 2022'.\n* Build the 'InjectResourceWindows' project in 64-bit 'Release' mode.\n* Run 'InjectResourceWindows' to generate the Windows-hosted executable.\n* Build the 'InjectResourceLinux' project in 64-bit 'Release' mode.\n* Run 'InjectResourceLinux' to generate the Linux-hosted executable.\n\n## Execution Instructions\n\n**Windows**\nExecute the generated binary with arguments from the 'out' folder:\n```powershell\nclrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\u003eHello.full.exe 1 2 3 4\n```\n\nSample Output:\n```powershell\n[INFO] Apphost [Jan  2 2025 19:07:05]\n[INFO] CoreCLR path:c:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\8.0.3\\coreclr.dll\n[INFO] CoreCLR pointer:0xd6720000\n[INFO] Error writer setted\n[INFO] Initialize OK\n[INFO] Create delegate OK\n[INFO] Call assembly load addr:0x00418cf0 size:9728\n[INFO] Assembly load ret - 0x00000000\n[INFO] Create delegate OK\n[INFO] Delegate entryPoint OK\n[INFO] Call entryPoint delegate...\ninfo: Hello.Program[0]\n      Ctor Hello, Version=1.0.9133.34412, Culture=neutral, PublicKeyToken=null\ninfo: Hello.Program[0]\n      Start 2025. 01. 02. 19:07:34\ninfo: Hello.Program[0]\n      Args: 1,2,3,4\ninfo: Hello.Program[0]\n      APP_PATHS : C:\\Projects\\PadarCom\\Source\\clrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\\\ninfo: Hello.Program[0]\n      APP_NAME : APPHOST\ninfo: Hello.Program[0]\n      APPBASE : C:\\Projects\\PadarCom\\Source\\clrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\\\ninfo: Hello.Program[0]\n      HOST_RUNTIME_CONTRACT : 0x7ff635369078\ninfo: Hello.Program[0]\n      NATIVE_DLL_SEARCH_DIRECTORIES : C:\\Projects\\PadarCom\\Source\\clrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\\runtimes\\win-x64\\native\\;C:\\Projects\\PadarCom\\Source\\clrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\\runtimes\\win\\lib\\netcoreapp3.0\\;C:\\Projects\\PadarCom\\Source\\clrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\\runtimes\\win\\lib\\netstandard2.0\\;c:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\8.0.3\\;c:\\Program Files\\dotnet\\shared\\Microsoft.AspNetCore.App\\8.0.3\\;C:\\Projects\\PadarCom\\Source\\clrhost\\InjectResourceWindows\\bin\\Release\\net8.0\\out\\;\ninfo: Hello.Data[0]\n      Test Json Serializer {\"Id\":0,\"Name\":\"Name\"}\ninfo: Hello.Program[0]\n      End 2025. 01. 02. 19:07:34\n[INFO] Shutdown exitCode:11\n```\n\n**Linux**\nRun the executable from your WSL terminal from the 'out' folder:\n``` bash\nasus@Asus:/mnt/c/clrhost/InjectResourceLinux/bin/Release/net8.0/out$ ./Hello.full.exe 1 2 3 4\n```\n\nSample Output:\n``` bash\n[INFO] Apphost [Jan  2 2025 19:07:00]\n[INFO] CoreCLR path:/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.3/libcoreclr.so\n[INFO] CoreCLR pointer:0xdba67680\n[INFO] Error writer setted\n[INFO] Initialize OK\n[INFO] Create delegate OK\n[INFO] Call assembly load addr:0xdbb2baa0 size:9728\n[INFO] Assembly load ret - 0x00000000\n[INFO] Create delegate OK\n[INFO] Delegate entryPoint OK\n[INFO] Call entryPoint delegate...\ninfo: Hello.Program[0]\n      Ctor Hello, Version=1.0.9133.34412, Culture=neutral, PublicKeyToken=null\ninfo: Hello.Program[0]\n      Start 01/02/2025 19:07:26\ninfo: Hello.Program[0]\n      Args: 1,2,3,4\ninfo: Hello.Program[0]\n      APP_PATHS : /mnt/c/Projects/PadarCom/Source/clrhost/InjectResourceLinux/bin/Release/net8.0/out/\ninfo: Hello.Program[0]\n      APP_NAME : APPHOST\ninfo: Hello.Program[0]\n      APPBASE : /mnt/c/Projects/PadarCom/Source/clrhost/InjectResourceLinux/bin/Release/net8.0/out/\ninfo: Hello.Program[0]\n      HOST_RUNTIME_CONTRACT : 0x5639da2a0020\ninfo: Hello.Program[0]\n      NATIVE_DLL_SEARCH_DIRECTORIES : /mnt/c/Projects/PadarCom/Source/clrhost/InjectResourceLinux/bin/Release/net8.0/out/runtimes/linux-x64/native/:/mnt/c/Projects/PadarCom/Source/clrhost/InjectResourceLinux/bin/Release/net8.0/out/runtimes/linux/lib/netstandard2.0/:/mnt/c/Projects/PadarCom/Source/clrhost/InjectResourceLinux/bin/Release/net8.0/out/runtimes/unix/lib/netcoreapp3.0/:/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.3/:/usr/share/dotnet/shared/Microsoft.AspNetCore.App/8.0.3/:/mnt/c/Projects/PadarCom/Source/clrhost/InjectResourceLinux/bin/Release/net8.0/out/:\ninfo: Hello.Data[0]\n      Test Json Serializer {\"Id\":0,\"Name\":\"Name\"}\ninfo: Hello.Program[0]\n      End 01/02/2025 19:07:26\n[INFO] Shutdown exitCode:11\n```\n\n# How it made ?\n\n## Main repository\nhttps://github.com/dotnet/runtime\n\nhttps://mattwarren.org/2017/03/23/Hitchhikers-Guide-to-the-CoreCLR-Source-Code/\n\n## Tutorials\nhttps://learn.microsoft.com/en-us/dotnet/core/tutorials/netcore-hosting\n\n## Design\nhttps://github.com/dotnet/runtime/blob/main/docs/design/features/native-hosting.md\n\n## APIs for hosting and resolve net runtime\nhttps://github.com/dotnet/runtime/blob/main/src/native/corehost/coreclr_delegates.h\nhttps://github.com/dotnet/runtime/blob/main/src/native/corehost/coreclr_resolver.h\nhttps://github.com/dotnet/runtime/blob/main/src/native/corehost/nethost/nethost.h\nhttps://github.com/dotnet/runtime/blob/main/src/native/corehost/hostfxr.h\n\n## Sample Host Example Project\nhttps://github.com/dotnet/samples/blob/main/core/hosting/src/NativeHost/nativehost.cpp\n\n## AssemblyLoadBytes trace\n\nhttps://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs\n\n```dotnet\n    public static unsafe int LoadAssemblyBytes(byte* assembly, nint assemblyByteLength, byte* symbols, nint symbolsByteLength, IntPtr loadContext, IntPtr reserved)\n        {\n            if (!IsSupported)\n                return HostFeatureDisabled;\n\n            try\n            {\n                ArgumentNullException.ThrowIfNull(assembly);\n                ArgumentOutOfRangeException.ThrowIfNegativeOrZero(assemblyByteLength);\n                ArgumentOutOfRangeException.ThrowIfGreaterThan(assemblyByteLength, int.MaxValue);\n                ArgumentOutOfRangeException.ThrowIfNotEqual(loadContext, IntPtr.Zero);\n                ArgumentOutOfRangeException.ThrowIfNotEqual(reserved, IntPtr.Zero);\n\n                ReadOnlySpan\u003cbyte\u003e assemblySpan = new ReadOnlySpan\u003cbyte\u003e(assembly, (int)assemblyByteLength);\n                ReadOnlySpan\u003cbyte\u003e symbolsSpan = default;\n                if (symbols != null \u0026\u0026 symbolsByteLength \u003e 0)\n                {\n                    symbolsSpan = new ReadOnlySpan\u003cbyte\u003e(symbols, (int)symbolsByteLength);\n                }\n\n                LoadAssemblyBytesLocal(assemblySpan, symbolsSpan);\n            }\n            catch (Exception e)\n            {\n                return e.HResult;\n            }\n\n            return 0;\n\n            [UnconditionalSuppressMessage(\"ReflectionAnalysis\", \"IL2026:RequiresUnreferencedCode\",\n                Justification = \"The same feature switch applies to GetFunctionPointer and this function. We rely on the warning from GetFunctionPointer.\")]\n            static void LoadAssemblyBytesLocal(ReadOnlySpan\u003cbyte\u003e assemblyBytes, ReadOnlySpan\u003cbyte\u003e symbolsBytes) =\u003e AssemblyLoadContext.Default.InternalLoad(assemblyBytes, symbolsBytes);\n        }\n```\n\n\nhttps://github.com/dotnet/runtime/blob/main/src/native/corehost/hostpolicy/hostpolicy.cpp\n\n```cpp\n  case coreclr_delegate_type::load_assembly_bytes:\n            return coreclr-\u003ecreate_delegate(\n                \"System.Private.CoreLib\",\n                \"Internal.Runtime.InteropServices.ComponentActivator\",\n                \"LoadAssemblyBytes\",\n                delegate);\n```\n\nhttps://github.com/dotnet/runtime/blob/main/src/native/corehost/hostpolicy/coreclr.cpp\n\n```cpp\npal::hresult_t coreclr_t::create_delegate(\n    const char* entryPointAssemblyName,\n    const char* entryPointTypeName,\n    const char* entryPointMethodName,\n    void** delegate)\n{\n    assert(coreclr_contract.coreclr_execute_assembly != nullptr);\n\n    return coreclr_contract.coreclr_create_delegate(\n        _host_handle,\n        _domain_id,\n        entryPointAssemblyName,\n        entryPointTypeName,\n        entryPointMethodName,\n        delegate);\n}\n```\n\n## Corecrl dll exports interface\n\n```dos\ndumpbin /exports coreclr.dll\n```\n\n```console\nMicrosoft (R) COFF/PE Dumper Version 14.39.33520.0\nCopyright (C) Microsoft Corporation.  All rights reserved.\n\n\nDump of file coreclr.dll\n\nFile Type: DLL\n\n  Section contains the following exports for coreclr.dll\n\n    00000000 characteristics\n    FFFFFFFF time date stamp\n        0.00 version\n           2 ordinal base\n          11 number of functions\n          11 number of names\n\n    ordinal hint RVA      name\n\n          3    0 0049CDD0 CLRJitAttachState\n          4    1 002135D0 GetCLRRuntimeHost\n          5    2 0010A000 MetaDataGetDispenser\n          6    3 003B6C60 coreclr_create_delegate\n          7    4 00117690 coreclr_execute_assembly\n          8    5 00117390 coreclr_initialize\n          9    6 001572E0 coreclr_set_error_writer\n         10    7 003B6EA0 coreclr_shutdown\n         11    8 0014BAE0 coreclr_shutdown_2\n          2    9 00488740 g_CLREngineMetrics\n         12    A 004051C0 g_dacTable\n```\n\n## Native entry point construct\n\nhttps://github.com/smx-smx/EzDotnet\n\n```csharp\nnamespace ManagedSample\n{\n\tpublic class Program {\n\t  private static void NativeEntryPoint(int argc, IntPtr argv)\n        {\n            static string[] MarshalArgv(int argc, IntPtr argv)\n            {\n                string[] args = new string[argc];\n\n                for (int i = 0; i \u003c argc; i++, argv += IntPtr.Size)\n                    args[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(argv));\n\n                return args;\n            }\n\n            string[] args = MarshalArgv(argc, argv);\n\n            Main(args);\n        }\n\n\t\tpublic static void Main(string[] args){\n\t\t\tConsole.WriteLine(\"Hello, World\");\n\t\t}\n\t}\n}\n```\n\n## Hosting links\n\nHost Runtime information\nhttps://github.com/dotnet/runtime/blob/main/docs/design/features/host-runtime-information.md\n\nHost Traceing\nhttps://github.com/dotnet/runtime/blob/main/docs/design/features/host-tracing.md\n\nHosting Layer Apis (hostpolicy, hostfxr, coreclr)\nhttps://github.com/dotnet/runtime/blob/main/docs/design/features/hosting-layer-apis.md\n\nHost Error codes\nhttps://github.com/dotnet/runtime/blob/main/docs/design/features/host-error-codes.md\n\n\n## Inject Resource Windows\n\nInject Windows Resource to file\nhttps://github.com/dotnet/runtime/tree/main/src/coreclr/tools/InjectResource\n\n## Inject Resource Linux\n\nObjcopy test\n```bash\nobjcopy --add-section .sname=Hello.dll AppHostLinux.out New.out\nobjcopy --add-section .sname=Hello.dll --set-section-flags .sname=noload,readonly AppHostLinux.out New.out\n```\n\nUsefull links\nhttps://stackoverflow.com/questions/7370407/get-the-start-and-end-address-of-text-section-in-an-executable\n\nhttps://github.com/mattst88/build-id\n\nhttps://stackoverflow.com/questions/10863510/getting-the-sh-name-member-in-a-section-header-elf-file\n\nTest obj scan\n```bash\nmcedit dump_shdr.c\n```\n\n```c\n#include \u003csys/stat.h\u003e\n#include \u003csys/mman.h\u003e\n#include \u003celf.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cstdio.h\u003e\n#include \u003cfcntl.h\u003e\n\n\nint print_shdr(const char *const fname, size_t size) {\n  int fd = open(fname, O_RDONLY);\n  char *p = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);\n\n  Elf64_Ehdr *ehdr = (Elf64_Ehdr*)p;\n  Elf64_Shdr *shdr = (Elf64_Shdr *)(p + ehdr-\u003ee_shoff);\n  int shnum = ehdr-\u003ee_shnum;\n\n  Elf64_Shdr *sh_strtab = \u0026shdr[ehdr-\u003ee_shstrndx];\n  const char *const sh_strtab_p = p + sh_strtab-\u003esh_offset;\n\n  for (int i = 0; i \u003c shnum; ++i) {\n    const char* sname = sh_strtab_p + shdr[i].sh_name;\n\n    if (!strcmp(sname,\".sname\"))\n    {\n        printf(\"%2d: %4d '%s' %4lu %4lu\\n\", i, shdr[i].sh_name,\n               sname, shdr[i].sh_offset,shdr[i].sh_size);\n\n        printf(\"dump\\n\");\n        const char* data = p + shdr[i].sh_offset;\n        long size = shdr[i].sh_size;\n        while (size--)\n        {\n            putchar(*data);\n            data++;\n        }\n        printf(\"\\ndump\\n\");\n    }\n  }\n\n  return 0;\n}\n\nint main(int argc, char *argv[])\n{\n  struct stat st;\n  const char *fname = \"/proc/self/exe\";\n\n  if (argc \u003e 1)\n    fname = argv[1];\n\n  if (stat(fname, \u0026st) != 0) {\n    perror(\"stat\");\n    return 1;\n  }\n  return print_shdr(fname, st.st_size);\n}\n```\n\n```\ngcc dump_shdr.c\nobjcopy --add-section .sname=dump_shdr.c --set-section-flags .sname=noload,readonly a.out new.out\n./new.out\n```\n\n```\n objcopy --add-section .idr_rcdata1=Hello.dll --set-section-flags .idr_rcdata1=noload,readonly AppHostLinux.out new.out\n```\n\nWindows crosstool for Linux X64\nhttps://docs.unrealengine.com/4.26/en-US/SharingAndReleasing/Linux/GettingStarted/\n\n## Bundle makeing\n\nSingle File App\nhttps://learn.microsoft.com/en-us/dotnet/core/deploying/single-file/overview?tabs=cli\n\nSingle File App Host Design\nhttps://github.com/dotnet/designs/blob/main/accepted/2020/single-file/design.md\n\nSingle File Extract\nhttps://github.com/dotnet/designs/blob/main/accepted/2020/single-file/extract.md\n\nBundler\nhttps://github.com/dotnet/designs/blob/main/accepted/2020/single-file/bundler.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxmegz%2Fclrhost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxmegz%2Fclrhost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxmegz%2Fclrhost/lists"}