Repository: M2Team/NanaRun Branch: main Commit: dfa9cccf850a Files: 41 Total size: 172.8 KB Directory structure: gitextract_8fdsknjd/ ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ └── BuildBinaries.yml ├── .gitignore ├── BuildAllTargets.cmd ├── BuildAllTargets.proj ├── BuildInstallers.cmd ├── Directory.Build.props ├── Documents/ │ ├── People.md │ ├── ReleaseNotes.md │ ├── ReleaseNotesPreview.md │ └── Versioning.md ├── License.md ├── MinSudo/ │ ├── MinSudo.cpp │ ├── MinSudo.manifest │ ├── MinSudo.rc │ ├── MinSudo.vcxproj │ ├── MinSudo.vcxproj.filters │ ├── Resources/ │ │ ├── en/ │ │ │ └── Translations.md │ │ └── zh-Hans/ │ │ └── Translations.md │ └── resource.h ├── NanaRun/ │ ├── NanaRun.cpp │ ├── NanaRun.manifest │ ├── NanaRun.vcxproj │ └── NanaRun.vcxproj.filters ├── NanaRun.IconResource/ │ ├── NanaRun.IconResource.h │ ├── NanaRun.IconResource.props │ └── NanaRun.IconResource.rc ├── NanaRun.slnx ├── ReadMe.md ├── SynthRdp/ │ ├── AutoRun.inf │ ├── SynthRdp.cpp │ ├── SynthRdp.iss │ ├── SynthRdp.manifest │ └── SynthRdp.vcxproj ├── Tools/ │ ├── Default.isl │ ├── Setup.e32 │ └── SetupLdr.e32 └── VirtualSmb/ ├── VirtualSmb.cpp ├── VirtualSmb.manifest └── VirtualSmb.vcxproj ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ ## ## PROJECT: Mouri Internal Library Essentials ## FILE: .editorconfig ## PURPOSE: The root .editorconfig file ## ## LICENSE: The MIT License ## ## MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) ## root = true [*] charset = utf-8-bom end_of_line = crlf [*.md] insert_final_newline = true [*.{c,c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,ixx,cppm,ipp,odl,idl,inl,ipp,tlh,tli}] indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true vc_generate_documentation_comments = doxygen_slash_star [*.{cs,csx,vb,vbx,asmx}] indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true [*.{js,json,xml,toml,xaml}] indent_style = space indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true [*.rc] charset = utf-16le [*.inf] charset = utf-16le [*.bat] charset = utf-8 insert_final_newline = true [*.sh] charset = utf-8 end_of_line = lf insert_final_newline = true [*.{asm,inc,s,nasm}] charset = utf-8 indent_style = space indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .github/FUNDING.yml ================================================ patreon: MouriNaruto custom: [ "https://paypal.me/MouriNaruto", "https://afdian.net/a/MouriNaruto", "https://github.com/MouriNaruto/MouriNaruto/tree/main/Sponsor#alipay", "https://github.com/MouriNaruto/MouriNaruto/tree/main/Sponsor#wechat" ] ================================================ FILE: .github/workflows/BuildBinaries.yml ================================================ name: Build Binaries on: push: paths-ignore: - '.github/*' - '*.md' pull_request: paths-ignore: - '.github/*' - '*.md' jobs: build: runs-on: windows-latest env: POWERSHELL_TELEMETRY_OPTOUT: 1 steps: - uses: actions/checkout@v4 with: submodules: 'recursive' - uses: microsoft/setup-msbuild@v2 - name: Clear local NuGet cache (workaround for failed restores on windows-latest) run: dotnet nuget locals all --clear - name: Build run: msbuild -m BuildAllTargets.proj - name: Prepare artifacts run: rm Output\Binaries\* -vb -Recurse -Force -Include *.exp, *.idb, *.ilk, *.iobj, *.ipdb, *.lastbuildstate, *.lib, *.obj, *.res, *.tlog - uses: actions/upload-artifact@v4 with: name: NanaRun_CI_Build path: Output\Binaries ================================================ FILE: .gitignore ================================================ ## ## PROJECT: Mouri Internal Library Essentials ## FILE: .gitignore ## PURPOSE: The root .gitignore file for Visual Studio C++ Project ## ## LICENSE: The MIT License ## ## MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) ## ## ## Ignore Mile.Project specific temporary files, build results, ## and files generated by popular Visual Studio add-ons. ## Output/ ## ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## # User-specific files *.rsuser *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Mono auto generated files mono_crash.* # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ [Ww][Ii][Nn]32/ [Aa][Rr][Mm]/ [Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ [Ll]ogs/ # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # Visual Studio 2017 auto generated files Generated\ Files/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUnit *.VisualState.xml TestResult.xml nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # Benchmark Results BenchmarkDotNet.Artifacts/ # .NET Core project.lock.json project.fragment.lock.json artifacts/ # ASP.NET Scaffolding ScaffoldingReadMe.txt # StyleCop StyleCopReport.xml # Files built by Visual Studio *_i.c *_p.c *_h.h *.ilk *.meta *.obj *.iobj *.pch *.pdb *.ipdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *_wpftmp.csproj *.log *.tlog *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # Visual Studio Trace Files *.e2e # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # AxoCover is a Code Coverage Tool .axoCover/* !.axoCover/settings.json # Coverlet is a free, cross platform Code Coverage Tool coverage*.json coverage*.xml coverage*.info # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # NuGet Symbol Packages *.snupkg # The packages folder can be ignored because of Package Restore **/[Pp]ackages/* # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt *.appx *.appxbundle *.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !?*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings orleans.codegen.cs # Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm ServiceFabricBackup/ *.rptproj.bak # SQL Server files *.mdf *.ldf *.ndf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings *.rptproj.rsuser *- [Bb]ackup.rdl *- [Bb]ackup ([0-9]).rdl *- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat node_modules/ # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) *.vbw # Visual Studio 6 auto-generated project file (contains which files were open etc.) *.vbp # Visual Studio 6 workspace and project file (working project files containing files to include in project) *.dsw *.dsp # Visual Studio 6 technical files *.ncb *.aps # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # CodeRush personal settings .cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/** # !tools/packages.config # Tabs Studio *.tss # Telerik's JustMock configuration file *.jmconfig # BizTalk build output *.btp.cs *.btm.cs *.odx.cs *.xsd.cs # OpenCover UI analysis results OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log *.binlog # NVidia Nsight GPU debugger configuration file *.nvuser # MFractors (Xamarin productivity tool) working folder .mfractor/ # Local History for Visual Studio .localhistory/ # Visual Studio History (VSHistory) files .vshistory/ # BeatPulse healthcheck temp database healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ # Fody - auto-generated XML schema FodyWeavers.xsd # VS Code files for those working on multiple tools .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.code-workspace # Local History for Visual Studio Code .history/ # Windows Installer files from build outputs *.cab *.msi *.msix *.msm *.msp # JetBrains Rider *.sln.iml ================================================ FILE: BuildAllTargets.cmd ================================================ @setlocal @echo off rem Change to the current folder. cd "%~dp0" rem Remove the output folder for a fresh compile. rd /s /q Output rem Initialize Visual Studio environment set VisualStudioInstallerFolder="%ProgramFiles(x86)%\Microsoft Visual Studio\Installer" if %PROCESSOR_ARCHITECTURE%==x86 set VisualStudioInstallerFolder="%ProgramFiles%\Microsoft Visual Studio\Installer" pushd %VisualStudioInstallerFolder% for /f "usebackq tokens=*" %%i in (`vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do ( set VisualStudioInstallDir=%%i ) popd call "%VisualStudioInstallDir%\VC\Auxiliary\Build\vcvarsall.bat" x86 rem Build all targets MSBuild -binaryLogger:Output\BuildAllTargets.binlog -m BuildAllTargets.proj @endlocal ================================================ FILE: BuildAllTargets.proj ================================================  $(MSBuildThisFileDirectory)NanaRun.slnx Configuration=Debug;Platform=x86 Configuration=Release;Platform=x86 Configuration=Debug;Platform=x64 Configuration=Release;Platform=x64 Configuration=Debug;Platform=ARM64 Configuration=Release;Platform=ARM64 ================================================ FILE: BuildInstallers.cmd ================================================ @setlocal @echo off rem Change to the current folder. cd "%~dp0" rem Remove the SynthRdp installation image output folder for a fresh compile. rd /s /q Output\SynthRdpInstallationImage rem Generate SynthRdp installer Tools\ISCC.exe SynthRdp\SynthRdp.iss rem Copy SynthRdp AutoRun.inf copy SynthRdp\AutoRun.inf Output\SynthRdpInstallationImage rem Generate SynthRdp installation image Tools\oscdimg.exe -u1 -udfver102 -lSynthRdp Output\SynthRdpInstallationImage Output\SynthRdp.iso @endlocal ================================================ FILE: Directory.Build.props ================================================  $(MSBuildThisFileDirectory)Output\ ================================================ FILE: Documents/People.md ================================================ # Relevant People ## Notice - This list sort in alphabetical order. ## Development Team - Kenji Mouri ([https://github.com/MouriNaruto](https://github.com/MouriNaruto)) ## Logo Designers - Shomnipotence ([https://github.com/Shomnipotence](https://github.com/Shomnipotence)) ## Special thanks - mingkuang ([https://github.com/mingkuang-Chuyu](https://github.com/mingkuang-Chuyu)) ================================================ FILE: Documents/ReleaseNotes.md ================================================ # NanaRun Release Notes For preview versions, please read [NanaRun Preview Release Notes](ReleaseNotesPreview.md). ================================================ FILE: Documents/ReleaseNotesPreview.md ================================================ # NanaRun Preview Release Notes For stable versions, please read [NanaRun Release Notes](ReleaseNotes.md). **NanaRun 1.0 Preview 3 (1.0.92.0)** - Introduce the SynthRdp tool. - Add short form command line options support for MinSudo. (Suggested by he3als.) - Update icon assets. (Designed by Shomnipotence.) - Adjust several implementations. **NanaRun 1.0 Preview 2 (1.0.18.0)** - Remove standard handle settings because child process will inherit the parent's console for MinSudo. - Update application icon. (Designed by Shomnipotence.) - Make sure ignores CTRL+C signals for MinSudo itself to solve unexcepted behaviors. - Fix current directory issue when put MinSudo into System32 folder. (Thanks to Slemoon.) - Add System and TrustedInstaller mode for MinSudo. - Add enable all privileges mode support for MinSudo. **NanaRun 1.0 Preview 1 (1.0.5.0)** - Implement MinSudo. ================================================ FILE: Documents/Versioning.md ================================================ # NanaRun Versioning This document applies to all versions of NanaRun. ## Version Format - Simple Version: `. ` - Example: `9.0 Preview 1` - Binary Version: `...` - Example: `9.0.2654.0` ## The rule for build and revision number The build number is the number of days since May 1, 2024 because that day is the 10th anniversary of the first version of NSudo is created and published. ~~The build number is the number of days since May 16, 2022 because the first version of NanaRun is created and published on that day.~~ The revision number is the number of releases releases in the day corresponding to the build number, and it counts from zero. So the first revision is 0 and the second revision is 1. ================================================ FILE: License.md ================================================ # NanaRun License For giving the maximum respect for the upstream projects and following the philosophy about open-source software from Kenji Mouri (MouriNaruto), the one of the M2-Team founders. The source code of NanaRun (not including the source code from third-party libraries) is distributed under the MIT License. The application file association icons of NanaRun (these contents are only in the `Assets` folder) are designed by Shomnipotence and authorized to the NanaRun project, and it is distributed under the CC BY-ND 4.0 License. The source code from the third-party libraries is distributed under the original license used in the third-party libraries. This permission notice shall be included in all copies or substantial portions of the Software. ### The philosophy about open-source software from Kenji Mouri (MouriNaruto) - The source code from the third-party projects should be distributed under their original licenses to give the maximum respect for the upstream projects. - Don't make your software open source if you don't want your source code or ideas used in proprietary software. Because they always have the way to cross restrictions if they really want to do, even you distributed your source code under the strictest copyleft license, they can use clean room to resolve it. - I prefer permissive licenses because using copyleft licenses will make you feel anxious in most cases because you always need to worry about someone using your source code in proprietary software. So, I choose to give the maximum respect to users, and I also hope people can try their best to treat others kindly. ### The MIT License ``` The MIT License (MIT) Copyright (c) M2-Team and Contributors. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` ### The CC BY-ND 4.0 License ``` Copyright (c) Shomnipotence and M2-Team. All rights reserved. This work is licensed under a Creative Commons Attribution-NoDerivatives 4.0 International License. Attribution-NoDerivatives 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-NoDerivatives 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. c. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. d. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. e. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. f. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. g. Licensor means the individual(s) or entity(ies) granting rights under this Public License. h. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. i. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. j. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce and reproduce, but not Share, Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material, You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database, provided You do not Share Adapted Material; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ``` ### The third-party libraries used in NanaRun - C++/WinRT, https://github.com/microsoft/cppwinrt - Mile.Detours, https://github.com/ProjectMile/Mile.Detours - Mile.Project.Windows, https://github.com/ProjectMile/Mile.Project.Windows - VC-LTL, https://github.com/Chuyu-Team/VC-LTL5 ================================================ FILE: MinSudo/MinSudo.cpp ================================================ /* * PROJECT: NanaRun * FILE: MinSudo.cpp * PURPOSE: Implementation for MinSudo * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) */ #include #include #pragma comment(lib, "WtsApi32.lib") #include #pragma comment(lib, "Userenv.lib") #include #include #include #include #include #include #include "resource.h" #include namespace { void SplitCommandLineEx( std::wstring const& CommandLine, std::vector const& OptionPrefixes, std::vector const& OptionParameterSeparators, std::wstring& ApplicationName, std::map& OptionsAndParameters, std::wstring& UnresolvedCommandLine) { ApplicationName.clear(); OptionsAndParameters.clear(); UnresolvedCommandLine.clear(); size_t arg_size = 0; for (auto& SplitArgument : Mile::SplitCommandLineWideString(CommandLine)) { // We need to process the application name at the beginning. if (ApplicationName.empty()) { // For getting the unresolved command line, we need to cumulate // length which including spaces. arg_size += SplitArgument.size() + 1; // Save ApplicationName = SplitArgument; } else { bool IsOption = false; size_t OptionPrefixLength = 0; for (auto& OptionPrefix : OptionPrefixes) { if (0 == _wcsnicmp( SplitArgument.c_str(), OptionPrefix.c_str(), OptionPrefix.size())) { IsOption = true; OptionPrefixLength = OptionPrefix.size(); } } if (IsOption) { // For getting the unresolved command line, we need to cumulate // length which including spaces. arg_size += SplitArgument.size() + 1; // Get the option name and parameter. wchar_t* OptionStart = &SplitArgument[0] + OptionPrefixLength; wchar_t* ParameterStart = nullptr; for (auto& OptionParameterSeparator : OptionParameterSeparators) { wchar_t* Result = wcsstr( OptionStart, OptionParameterSeparator.c_str()); if (nullptr == Result) { continue; } Result[0] = L'\0'; ParameterStart = Result + OptionParameterSeparator.size(); break; } // Save OptionsAndParameters[(OptionStart ? OptionStart : L"")] = (ParameterStart ? ParameterStart : L""); } else { // Get the approximate location of the unresolved command line. // We use "(arg_size - 1)" to ensure that the program path // without quotes can also correctly parse. wchar_t* search_start = const_cast(CommandLine.c_str()) + (arg_size - 1); // Get the unresolved command line. Search for the beginning of // the first parameter delimiter called space and exclude the // first space by adding 1 to the result. wchar_t* command = wcsstr(search_start, L" ") + 1; // Omit the space. (Thanks to wzzw.) while (command && *command == L' ') { ++command; } // Save if (command) { UnresolvedCommandLine = command; } break; } } } } std::wstring GetCurrentProcessModulePath() { // 32767 is the maximum path length without the terminating null character. std::wstring Path(32767, L'\0'); Path.resize(::GetModuleFileNameW( nullptr, &Path[0], static_cast(Path.size()))); return Path; } std::wstring GetWorkingDirectory() { // 32767 is the maximum path length without the terminating null character. std::wstring Path(32767, L'\0'); Path.resize(::GetCurrentDirectoryW( static_cast(Path.size()), &Path[0])); return Path; } void WriteToConsole( std::wstring_view const& String) { HANDLE ConsoleOutputHandle = ::GetStdHandle(STD_OUTPUT_HANDLE); DWORD NumberOfCharsWritten = 0; if (!::WriteConsoleW( ConsoleOutputHandle, String.data(), static_cast(String.size()), &NumberOfCharsWritten, nullptr)) { std::string CurrentCodePageString = Mile::ToString( ::GetConsoleOutputCP(), String); ::WriteFile( ConsoleOutputHandle, CurrentCodePageString.c_str(), static_cast(CurrentCodePageString.size()), &NumberOfCharsWritten, nullptr); } } std::map ParseStringDictionary( std::string_view const& Content) { constexpr std::string_view KeySeparator = "\r\n- "; constexpr std::string_view ValueStartSeparator = "\r\n```\r\n"; constexpr std::string_view ValueEndSeparator = "\r\n```"; std::map Result; if (Content.empty()) { return Result; } const char* Start = Content.data(); const char* End = Start + Content.size(); while (Start < End) { const char* KeyStart = std::strstr( Start, KeySeparator.data()); if (!KeyStart) { break; } KeyStart += KeySeparator.size(); const char* KeyEnd = std::strstr( KeyStart, ValueStartSeparator.data()); if (!KeyEnd) { break; } const char* ValueStart = KeyEnd + ValueStartSeparator.size(); const char* ValueEnd = std::strstr( ValueStart, ValueEndSeparator.data()); if (!ValueEnd) { break; } Start = ValueEnd + ValueEndSeparator.size(); Result.emplace(std::pair( std::string(KeyStart, KeyEnd - KeyStart), Mile::ToWideString( CP_UTF8, std::string(ValueStart, ValueEnd - ValueStart)))); } return Result; } DWORD GetActiveSessionID() { DWORD Count = 0; PWTS_SESSION_INFOW pSessionInfo = nullptr; if (::WTSEnumerateSessionsW( WTS_CURRENT_SERVER_HANDLE, 0, 1, &pSessionInfo, &Count)) { for (DWORD i = 0; i < Count; ++i) { if (pSessionInfo[i].State == WTS_CONNECTSTATE_CLASS::WTSActive) { return pSessionInfo[i].SessionId; } } ::WTSFreeMemory(pSessionInfo); } // We should return session 0 for the case that no active session. return 0; } BOOL CreateSystemToken( _In_ DWORD DesiredAccess, _Out_ PHANDLE TokenHandle) { // If the specified process is the System Idle Process (0x00000000), the // function fails and the last error code is ERROR_INVALID_PARAMETER. // So this is why 0 is the default value of dwLsassPID and dwWinLogonPID. // For fix the issue that @_kod0k and @DennyAmaro mentioned in // https://forums.mydigitallife.net/threads/59268/page-28#post-1672011 and // https://forums.mydigitallife.net/threads/59268/page-28#post-1674985. // Mile::CreateSystemToken will try to open the access token from lsass.exe // for maximum privileges in the access token, and try to open the access // token from winlogon.exe of current active session as fallback. // If no source process of SYSTEM access token can be found, the error code // will be HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER). DWORD dwLsassPID = 0; DWORD dwWinLogonPID = 0; PWTS_PROCESS_INFOW pProcesses = nullptr; DWORD dwProcessCount = 0; DWORD dwSessionID = ::GetActiveSessionID(); if (::WTSEnumerateProcessesW( WTS_CURRENT_SERVER_HANDLE, 0, 1, &pProcesses, &dwProcessCount)) { for (DWORD i = 0; i < dwProcessCount; ++i) { PWTS_PROCESS_INFOW pProcess = &pProcesses[i]; if ((!pProcess->pProcessName) || (!pProcess->pUserSid) || (!::IsWellKnownSid( pProcess->pUserSid, WELL_KNOWN_SID_TYPE::WinLocalSystemSid))) { continue; } if ((0 == dwLsassPID) && (0 == pProcess->SessionId) && (0 == ::_wcsicmp(L"lsass.exe", pProcess->pProcessName))) { dwLsassPID = pProcess->ProcessId; continue; } if ((0 == dwWinLogonPID) && (0 == dwSessionID || dwSessionID == pProcess->SessionId) && (0 == ::_wcsicmp(L"winlogon.exe", pProcess->pProcessName))) { dwWinLogonPID = pProcess->ProcessId; continue; } } ::WTSFreeMemory(pProcesses); } BOOL Result = FALSE; HANDLE SystemProcessHandle = nullptr; SystemProcessHandle = ::OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwLsassPID); if (!SystemProcessHandle) { SystemProcessHandle = ::OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwWinLogonPID); } if (SystemProcessHandle) { HANDLE SystemTokenHandle = nullptr; if (::OpenProcessToken( SystemProcessHandle, TOKEN_DUPLICATE, &SystemTokenHandle)) { Result = ::DuplicateTokenEx( SystemTokenHandle, DesiredAccess, nullptr, SecurityIdentification, TokenPrimary, TokenHandle); ::CloseHandle(SystemTokenHandle); } ::CloseHandle(SystemProcessHandle); } return Result; } BOOL OpenProcessTokenByProcessId( _In_ DWORD ProcessId, _In_ DWORD DesiredAccess, _Out_ PHANDLE TokenHandle) { BOOL Result = FALSE; HANDLE ProcessHandle = ::OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, ProcessId); if (ProcessHandle) { Result = ::OpenProcessToken( ProcessHandle, DesiredAccess, TokenHandle); ::CloseHandle(ProcessHandle); } return Result; } BOOL OpenServiceProcessToken( _In_ LPCWSTR ServiceName, _In_ DWORD DesiredAccess, _Out_ PHANDLE TokenHandle) { BOOL Result = FALSE; SERVICE_STATUS_PROCESS ServiceStatus; if (::MileStartService( ServiceName, &ServiceStatus)) { Result = ::OpenProcessTokenByProcessId( ServiceStatus.dwProcessId, DesiredAccess, TokenHandle); } return Result; } BOOL AdjustTokenPrivilegesSimple( _In_ HANDLE TokenHandle, _In_ PLUID_AND_ATTRIBUTES Privileges, _In_ DWORD PrivilegeCount) { BOOL Result = FALSE; if (Privileges && PrivilegeCount) { DWORD PrivilegesSize = sizeof(LUID_AND_ATTRIBUTES) * PrivilegeCount; DWORD TokenPrivilegesSize = PrivilegesSize + sizeof(DWORD); PTOKEN_PRIVILEGES TokenPrivileges = reinterpret_cast( ::MileAllocateMemory(TokenPrivilegesSize)); if (TokenPrivileges) { TokenPrivileges->PrivilegeCount = PrivilegeCount; std::memcpy( TokenPrivileges->Privileges, Privileges, PrivilegesSize); ::AdjustTokenPrivileges( TokenHandle, FALSE, TokenPrivileges, TokenPrivilegesSize, nullptr, nullptr); Result = (ERROR_SUCCESS == ::GetLastError()); ::MileFreeMemory(TokenPrivileges); } else { ::SetLastError(ERROR_NOT_ENOUGH_MEMORY); } } else { ::SetLastError(ERROR_INVALID_PARAMETER); } return Result; } BOOL GetTokenInformationWithMemory( _In_ HANDLE TokenHandle, _In_ TOKEN_INFORMATION_CLASS TokenInformationClass, _Out_ PVOID* OutputInformation) { if (!OutputInformation) { ::SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } *OutputInformation = nullptr; BOOL Result = FALSE; DWORD Length = 0; ::GetTokenInformation( TokenHandle, TokenInformationClass, nullptr, 0, &Length); if (ERROR_INSUFFICIENT_BUFFER == ::GetLastError()) { *OutputInformation = ::MileAllocateMemory(Length); if (*OutputInformation) { Result = ::GetTokenInformation( TokenHandle, TokenInformationClass, *OutputInformation, Length, &Length); if (!Result) { ::MileFreeMemory(*OutputInformation); *OutputInformation = nullptr; } } else { ::SetLastError(ERROR_NOT_ENOUGH_MEMORY); } } return Result; } BOOL AdjustTokenAllPrivileges( _In_ HANDLE TokenHandle, _In_ DWORD Attributes) { BOOL Result = FALSE; PTOKEN_PRIVILEGES pTokenPrivileges = nullptr; if (::GetTokenInformationWithMemory( TokenHandle, TokenPrivileges, reinterpret_cast(&pTokenPrivileges))) { for (DWORD i = 0; i < pTokenPrivileges->PrivilegeCount; ++i) { pTokenPrivileges->Privileges[i].Attributes = Attributes; } Result = ::AdjustTokenPrivilegesSimple( TokenHandle, pTokenPrivileges->Privileges, pTokenPrivileges->PrivilegeCount); ::MileFreeMemory(pTokenPrivileges); } return Result; } enum class TargetProcessTokenLevel : std::uint32_t { Standard = 0, System = 1, TrustedInstaller = 2, }; BOOL SimpleCreateProcess( _In_ TargetProcessTokenLevel TokenLevel, _In_ bool Privileged, _Inout_ LPWSTR lpCommandLine, _In_opt_ LPCWSTR lpCurrentDirectory, _In_ LPSTARTUPINFOW lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation) { BOOL Result = FALSE; DWORD Error = ERROR_SUCCESS; HANDLE CurrentProcessTokenHandle = INVALID_HANDLE_VALUE; HANDLE ImpersonatedCurrentProcessTokenHandle = INVALID_HANDLE_VALUE; LUID_AND_ATTRIBUTES RawPrivilege; HANDLE SystemTokenHandle = INVALID_HANDLE_VALUE; HANDLE ImpersonatedSystemTokenHandle = INVALID_HANDLE_VALUE; HANDLE TrustedInstallerTokenHandle = INVALID_HANDLE_VALUE; HANDLE TargetTokenHandle = INVALID_HANDLE_VALUE; LPVOID EnvironmentBlock = nullptr; auto Handler = Mile::ScopeExitTaskHandler([&]() { if (EnvironmentBlock) { ::DestroyEnvironmentBlock(EnvironmentBlock); } if (TargetTokenHandle != INVALID_HANDLE_VALUE) { ::CloseHandle(TargetTokenHandle); } if (TrustedInstallerTokenHandle != INVALID_HANDLE_VALUE) { ::CloseHandle(TrustedInstallerTokenHandle); } if (ImpersonatedSystemTokenHandle != INVALID_HANDLE_VALUE) { ::CloseHandle(ImpersonatedSystemTokenHandle); } if (SystemTokenHandle != INVALID_HANDLE_VALUE) { ::CloseHandle(SystemTokenHandle); } if (ImpersonatedCurrentProcessTokenHandle != INVALID_HANDLE_VALUE) { ::CloseHandle(ImpersonatedCurrentProcessTokenHandle); } if (CurrentProcessTokenHandle != INVALID_HANDLE_VALUE) { ::CloseHandle(CurrentProcessTokenHandle); } ::SetThreadToken(nullptr, nullptr); if (!Result) { ::SetLastError(Error); } }); if (!::OpenProcessToken( ::GetCurrentProcess(), MAXIMUM_ALLOWED, &CurrentProcessTokenHandle)) { Error = ::GetLastError(); return Result; } if (!::DuplicateTokenEx( CurrentProcessTokenHandle, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &ImpersonatedCurrentProcessTokenHandle)) { Error = ::GetLastError(); return Result; } if (!::LookupPrivilegeValueW( nullptr, SE_DEBUG_NAME, &RawPrivilege.Luid)) { Error = ::GetLastError(); return Result; } RawPrivilege.Attributes = SE_PRIVILEGE_ENABLED; if (!::AdjustTokenPrivilegesSimple( ImpersonatedCurrentProcessTokenHandle, &RawPrivilege, 1)) { Error = ::GetLastError(); return Result; } if (!::SetThreadToken( nullptr, ImpersonatedCurrentProcessTokenHandle)) { Error = ::GetLastError(); return Result; } if (!::CreateSystemToken( MAXIMUM_ALLOWED, &SystemTokenHandle)) { Error = ::GetLastError(); return Result; } if (!::DuplicateTokenEx( SystemTokenHandle, MAXIMUM_ALLOWED, nullptr, SecurityImpersonation, TokenImpersonation, &ImpersonatedSystemTokenHandle)) { Error = ::GetLastError(); return Result; } if (!::AdjustTokenAllPrivileges( ImpersonatedSystemTokenHandle, SE_PRIVILEGE_ENABLED)) { Error = ::GetLastError(); return Result; } if (!::SetThreadToken( nullptr, ImpersonatedSystemTokenHandle)) { Error = ::GetLastError(); return Result; } if (TargetProcessTokenLevel::Standard == TokenLevel) { if (!::DuplicateTokenEx( CurrentProcessTokenHandle, MAXIMUM_ALLOWED, nullptr, SecurityIdentification, TokenPrimary, &TargetTokenHandle)) { Error = ::GetLastError(); return Result; } } else if (TargetProcessTokenLevel::System == TokenLevel) { if (!::DuplicateTokenEx( SystemTokenHandle, MAXIMUM_ALLOWED, nullptr, SecurityIdentification, TokenPrimary, &TargetTokenHandle)) { Error = ::GetLastError(); return Result; } } else if (TargetProcessTokenLevel::TrustedInstaller == TokenLevel) { if (!::OpenServiceProcessToken( L"TrustedInstaller", MAXIMUM_ALLOWED, &TrustedInstallerTokenHandle)) { Error = ::GetLastError(); return Result; } if (!::DuplicateTokenEx( TrustedInstallerTokenHandle, MAXIMUM_ALLOWED, nullptr, SecurityIdentification, TokenPrimary, &TargetTokenHandle)) { Error = ::GetLastError(); return Result; } } else { Error = ERROR_INVALID_PARAMETER; return Result; } { DWORD SessionID = ::GetActiveSessionID(); if (!::SetTokenInformation( TargetTokenHandle, TokenSessionId, (PVOID)&SessionID, sizeof(DWORD))) { Error = ::GetLastError(); return Result; } } if (Privileged) { if (!::AdjustTokenAllPrivileges( TargetTokenHandle, SE_PRIVILEGE_ENABLED)) { Error = ::GetLastError(); return Result; } } if (!::CreateEnvironmentBlock( &EnvironmentBlock, CurrentProcessTokenHandle, TRUE)) { Error = ::GetLastError(); return Result; } Result = ::CreateProcessAsUserW( TargetTokenHandle, nullptr, lpCommandLine, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, EnvironmentBlock, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); return Result; } } int main() { // Fall back to English in unsupported environment. (Temporary Hack) // Reference: https://github.com/M2Team/NSudo/issues/56 switch (PRIMARYLANGID(::GetThreadUILanguage())) { case LANG_ENGLISH: case LANG_CHINESE: break; default: ::SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL)); break; } std::map StringDictionary; MILE_RESOURCE_INFO ResourceInfo = { 0 }; if (::MileLoadResource( &ResourceInfo, ::GetModuleHandleW(nullptr), L"Translations", MAKEINTRESOURCEW(IDR_TRANSLATIONS), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))) { StringDictionary = ::ParseStringDictionary(std::string_view( reinterpret_cast(ResourceInfo.Pointer), ResourceInfo.Size)); } std::wstring ApplicationName; std::map OptionsAndParameters; std::wstring UnresolvedCommandLine; ::SplitCommandLineEx( std::wstring(::GetCommandLineW()), std::vector{ L"-", L"/", L"--" }, std::vector{ L"=", L":" }, ApplicationName, OptionsAndParameters, UnresolvedCommandLine); bool NoLogo = false; bool Verbose = false; std::wstring WorkDir; TargetProcessTokenLevel TargetLevel = TargetProcessTokenLevel::Standard; bool Privileged = false; for (auto& Current : OptionsAndParameters) { if (0 == _wcsicmp(Current.first.c_str(), L"NoLogo") || 0 == _wcsicmp(Current.first.c_str(), L"NoL")) { NoLogo = true; } else if ( 0 == _wcsicmp(Current.first.c_str(), L"Verbose") || 0 == _wcsicmp(Current.first.c_str(), L"V")) { Verbose = true; } else if ( 0 == _wcsicmp(Current.first.c_str(), L"WorkDir") || 0 == _wcsicmp(Current.first.c_str(), L"WD")) { WorkDir = Current.second; } else if ( 0 == _wcsicmp(Current.first.c_str(), L"System") || 0 == _wcsicmp(Current.first.c_str(), L"S")) { TargetLevel = TargetProcessTokenLevel::System; } else if ( 0 == _wcsicmp(Current.first.c_str(), L"TrustedInstaller") || 0 == _wcsicmp(Current.first.c_str(), L"TI")) { TargetLevel = TargetProcessTokenLevel::TrustedInstaller; } else if ( 0 == _wcsicmp(Current.first.c_str(), L"Privileged") || 0 == _wcsicmp(Current.first.c_str(), L"P")) { Privileged = true; } } bool ShowHelp = false; bool ShowInvalidCommandLine = false; if (1 == OptionsAndParameters.size() && UnresolvedCommandLine.empty()) { auto Current = *OptionsAndParameters.begin(); if (0 == _wcsicmp(Current.first.c_str(), L"?") || 0 == _wcsicmp(Current.first.c_str(), L"H") || 0 == _wcsicmp(Current.first.c_str(), L"Help")) { ShowHelp = true; } else if ( 0 == _wcsicmp(Current.first.c_str(), L"Version") || 0 == _wcsicmp(Current.first.c_str(), L"Ver")) { ::WriteToConsole( L"MinSudo " MILE_PROJECT_VERSION_STRING L" (Build " MILE_PROJECT_MACRO_TO_STRING(MILE_PROJECT_VERSION_BUILD) L")" L"\r\n"); return 0; } else if (!( NoLogo || Verbose || !WorkDir.empty() || TargetLevel != TargetProcessTokenLevel::Standard || Privileged)) { ShowInvalidCommandLine = true; } } if (!NoLogo) { ::WriteToConsole( L"MinSudo " MILE_PROJECT_VERSION_STRING L" (Build " MILE_PROJECT_MACRO_TO_STRING(MILE_PROJECT_VERSION_BUILD) L")" L"\r\n" L"(c) M2-Team and Contributors. All rights reserved.\r\n" L"\r\n"); } if (ShowHelp) { ::WriteToConsole(StringDictionary["CommandLineHelp"]); return 0; } else if (ShowInvalidCommandLine) { ::WriteToConsole(StringDictionary["InvalidCommandLineError"]); return E_INVALIDARG; } ApplicationName = ::GetCurrentProcessModulePath(); if (UnresolvedCommandLine.empty()) { UnresolvedCommandLine = L"cmd.exe"; } if (Verbose) { std::wstring VerboseInformation; VerboseInformation += StringDictionary["CommandLineNotice"]; VerboseInformation += UnresolvedCommandLine; VerboseInformation += L"\r\n"; ::WriteToConsole(VerboseInformation.c_str()); } if (WorkDir.empty()) { WorkDir = std::wstring(::GetWorkingDirectory()); } if (L'\\' == WorkDir.back()) { WorkDir.pop_back(); } if (::MileIsCurrentProcessElevated()) { ::FreeConsole(); ::AttachConsole(ATTACH_PARENT_PROCESS); if (Verbose) { ::WriteToConsole(StringDictionary["Stage1Notice"]); } STARTUPINFOW StartupInfo = { 0 }; PROCESS_INFORMATION ProcessInformation = { 0 }; StartupInfo.cb = sizeof(STARTUPINFOW); if (::SimpleCreateProcess( TargetLevel, Privileged, const_cast(UnresolvedCommandLine.c_str()), WorkDir.c_str(), &StartupInfo, &ProcessInformation)) { // Make sure ignores CTRL+C signals after creating the child // process. Because that state is heritable, but we want to make // child process support CTRL+C. ::SetConsoleCtrlHandler(nullptr, TRUE); ::CloseHandle(ProcessInformation.hThread); ::WaitForSingleObjectEx(ProcessInformation.hProcess, INFINITE, FALSE); ::CloseHandle(ProcessInformation.hProcess); } else { ::WriteToConsole(StringDictionary["Stage1Failed"]); } } else { if (Verbose) { ::WriteToConsole(StringDictionary["Stage0Notice"]); } std::wstring TargetCommandLine = L"--NoLogo "; if (Verbose) { TargetCommandLine += L"--Verbose "; } TargetCommandLine += L"--WorkDir=\""; TargetCommandLine += WorkDir; TargetCommandLine += L"\" "; if (TargetLevel == TargetProcessTokenLevel::System) { TargetCommandLine += L"--System "; } else if (TargetLevel == TargetProcessTokenLevel::TrustedInstaller) { TargetCommandLine += L"--TrustedInstaller "; } if (Privileged) { TargetCommandLine += L"--Privileged "; } TargetCommandLine += UnresolvedCommandLine; SHELLEXECUTEINFOW Information = { 0 }; Information.cbSize = sizeof(SHELLEXECUTEINFOW); Information.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; Information.lpVerb = L"runas"; Information.lpFile = ApplicationName.c_str(); Information.lpParameters = TargetCommandLine.c_str(); if (::ShellExecuteExW(&Information)) { // Make sure ignores CTRL+C signals after creating the child // process. Because that state is heritable, but we want to make // child process support CTRL+C. ::SetConsoleCtrlHandler(nullptr, TRUE); ::WaitForSingleObjectEx(Information.hProcess, INFINITE, FALSE); ::CloseHandle(Information.hProcess); } else { ::WriteToConsole(StringDictionary["Stage0Failed"]); } } return 0; } ================================================ FILE: MinSudo/MinSudo.manifest ================================================  ================================================ FILE: MinSudo/MinSudo.vcxproj ================================================ {99FA5072-6C05-4027-8C8B-EA3EC9BCF9E0} MinSudo ConsoleApplication MinSudo.manifest true true M2-Team MinSudo - Lightweight POSIX-style Sudo implementation for Windows MinSudo © M2-Team and Contributors. All rights reserved. MinSudo.exe NanaRun 1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0 Preview 3 true NoExtensions 1.0.1171 ================================================ FILE: MinSudo/MinSudo.vcxproj.filters ================================================  {7b09b2fc-15c9-40d3-8c68-b33226a0b2cd} {0e9f5c86-045b-493f-b15e-750b369fc678} {71ae93ee-4aa8-44b8-bc84-e680a9768fc5} Resources\en Resources\zh-Hans ================================================ FILE: MinSudo/Resources/en/Translations.md ================================================ ``` * PROJECT: NanaRun * FILE: Translations.md * PURPOSE: The English translation for MinSudo * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) ``` - InvalidCommandLineError ``` [Error] Invalid command line parameters. (Use '/?', '-H' or '--Help' option for usage.) ``` - CommandLineNotice ``` [Info] Target Command Line: ``` - Stage0Notice ``` [Info] Enter the Stage 0 (non-elevated). ``` - Stage0Failed ``` [Error] ShellExecuteExW failed. ``` - Stage1Notice ``` [Info] Enter the Stage 1 (elevated). ``` - Stage1Failed ``` [Error] CreateProcessW failed. ``` - CommandLineHelp ``` Format: MinSudo [Options] Command Options: --NoLogo, -NoL Suppress copyright message. --Verbose, -V Show detailed information. --WorkDir=[Path], -WD=[Path] Set working directory. --System, -S Run as System instead of Administrator. --TrustedInstaller, -TI Run as TrustedInstaller instead of Administrator. --Privileged, -P Enable all privileges. --Version, -Ver Show version information. /?, -H, --Help Show this content. Notes: - All command options are case-insensitive. - MinSudo will execute "cmd.exe" if you don't specify another command. - You can use the "/" or "--" override "-" and use the "=" override ":" in the command line parameters. For example, "/Option:Value" and "-Option=Value" are equivalent. Example: If you want to run "whoami /all" as elevated in the non-elevated Console, and you don't want to show version information of MinSudo. > MinSudo --NoLogo whoami /all ``` ================================================ FILE: MinSudo/Resources/zh-Hans/Translations.md ================================================ ``` * PROJECT: NanaRun * FILE: Translations.md * PURPOSE: The Chinese (Simplified) translation for MinSudo * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) ``` - InvalidCommandLineError ``` [错误] 无效的命令行参数。 (使用 '/?', '-H' 或 '--Help' 选项获取用法。) ``` - CommandLineNotice ``` [信息] 目标命令行: ``` - Stage0Notice ``` [信息] 进入 0 阶段 (未提权). ``` - Stage0Failed ``` [信息] ShellExecuteExW 调用失败。 ``` - Stage1Notice ``` [信息] 进入 1 阶段 (已提权). ``` - Stage1Failed ``` [错误] CreateProcessW 调用失败。 ``` - CommandLineHelp ``` 格式: MinSudo [选项] 命令 选项: --NoLogo, -NoL 隐藏版权信息。 --Verbose, -V 显示详细信息。 --WorkDir=[路径], -WD=[路径] 设置工作目录。 --System, -S 使用 System 而不是管理员执行。 --TrustedInstaller, -TI 使用 TrustedInstaller 而不是管理员执行。 --Privileged, -P 启用全部特权。 --Version, -Ver 显示版本信息。 /?, -H, --Help 显示该内容。 提示: - 所有命令选项不区分大小写。 - 如果你不指定其他命令,MinSudo 将执行 "cmd.exe"。 - 可以在命令行参数中使用 "/" 或 "--" 代替 "-" 和使用 "=" 代替 ":"。例如 "/Option:Value" 和 "-Option=Value" 是等价的。 用例: 如果你想在未提权的控制台下运行提权的 "whoami /all",并且你不希望 MinSudo 显示 版本信息。 > MinSudo --NoLogo whoami /all ``` ================================================ FILE: MinSudo/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 MinSudo.rc 使用 // #define IDR_TRANSLATIONS 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 104 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: NanaRun/NanaRun.cpp ================================================ /* * PROJECT: NanaRun * FILE: NanaRun.cpp * PURPOSE: Implementation for NanaRun * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) */ #include #include #include #include enum class AccessTokenSourceType : std::int32_t { CurrentProcess = 0, Process = 1, System = 2, CurrentSession = 3, Session = 4, Service = 5, User = 6, }; enum class MandatoryLabelType : std::int32_t { Default = 0, Untrusted = 1, Low = 2, Medium = 3, MediumPlus = 4, High = 5, System = 6, ProtectedProcess = 7, }; enum class ProcessPriorityType : std::int32_t { Default = 0, Idle = 1, BelowNormal = 2, Normal = 3, AboveNormal = 4, High = 5, RealTime = 6, }; enum class ShowWindowModeType : std::int32_t { Default = 0, Show = 1, Hide = 2, Maximize = 3, Minimize = 4 }; struct EnvironmentConfiguration { AccessTokenSourceType AccessTokenSource = AccessTokenSourceType::CurrentProcess; std::uint32_t Process = 0; std::uint32_t Session = 0; std::string ServiceName; std::string UserName; std::string UserPassword; bool UseLinkedAccessToken = false; bool UseLuaAccessToken = false; bool EnableAllPrivileges = false; bool RemoveAllPrivileges = false; std::vector ExcludedPrivileges; std::vector IncludedPrivileges; MandatoryLabelType IntegrityLevel = MandatoryLabelType::Default; bool InheritEnvironmentVariables = true; std::vector> EnvironmentVariables; ProcessPriorityType ProcessPriority = ProcessPriorityType::Default; ShowWindowModeType ShowWindowMode = ShowWindowModeType::Default; bool WaitForExit = false; std::string CurrentDirectory; bool UseCurrentConsole = false; }; int main() { std::wprintf(L"Hello World! - NanaRun (Console)"); return 0; } ================================================ FILE: NanaRun/NanaRun.manifest ================================================  ================================================ FILE: NanaRun/NanaRun.vcxproj ================================================ {3D1A07C8-17B2-4E5F-AAC1-16BE522BD4D7} NanaRun ConsoleApplication NanaRun.manifest true true M2-Team NanaRun (Console) NanaRun © M2-Team and Contributors. All rights reserved. NanaRun.exe NanaRun 1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0 Preview 3 true NoExtensions 1.0.1171 1.0.3550 ================================================ FILE: NanaRun/NanaRun.vcxproj.filters ================================================  ================================================ FILE: NanaRun.IconResource/NanaRun.IconResource.h ================================================ /* * PROJECT: NanaRun * FILE: NanaRun.IconResource.h * PURPOSE: Windows icon resource for NanaRun * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) */ #define IDI_NANARUN 101 ================================================ FILE: NanaRun.IconResource/NanaRun.IconResource.props ================================================  $(MSBuildThisFileDirectory);$(IncludePath) ================================================ FILE: NanaRun.slnx ================================================ ================================================ FILE: ReadMe.md ================================================ # ![NanaRun](Assets/NanaRun.png) NanaRun Application runtime environment customization utility ## Development Status of Components - [x] MinSudo - [x] SynthRdp - [ ] NanaEAM - [ ] NanaKit - [ ] NanaRun (Console) - [ ] NanaRun (SDK) ## System Requirements - MinSudo - Supported OS: Windows Vista RTM (Build 6000.16386) or later - Supported Platforms: x86 (32-bit and 64-bit) and ARM (64-bit) - SynthRdp - Supported OS: Windows XP RTM (Build 2600) or later - Supported Platforms: x86 (32-bit and 64-bit) and ARM (64-bit) ## MinSudo MinSudo is a lightweight POSIX-style Sudo implementation for Windows. It makes user possible to use elevated console apps in non-elevated consoles. For safety, the implementation uses the UAC for elevation and don't support credential cache. It also don't use homemade Windows service and any IPC infrastructures. Here is the usage. ``` Format: MinSudo [Options] Command Options: --NoLogo, -NoL Suppress copyright message. --Verbose, -V Show detailed information. --WorkDir=[Path], -WD=[Path] Set working directory. --System, -S Run as System instead of Administrator. --TrustedInstaller, -TI Run as TrustedInstaller instead of Administrator. --Privileged, -P Enable all privileges. --Version, -Ver Show version information. /?, -H, --Help Show this content. Notes: - All command options are case-insensitive. - MinSudo will execute "cmd.exe" if you don't specify another command. - You can use the "/" or "--" override "-" and use the "=" override ":" in the command line parameters. For example, "/Option:Value" and "-Option=Value" are equivalent. Example: If you want to run "whoami /all" as elevated in the non-elevated Console, and you don't want to show version information of MinSudo. > MinSudo --NoLogo whoami /all ``` ## SynthRdp SynthRdp is a Hyper-V Enhanced Session Proxy Service for Windows, which can redirect RDP over VMBus pipe to any RDP over TCP socket, because the Hyper-V Enhanced Session is actually the RDP connection over the VMBus pipe. ### Features - Make Hyper-V virtual machines with Windows 8 and earlier guest OSes support Hyper-V Enhanced Session mode. - Act as a proxy service to make Hyper-V virtual machines with non-Windows guest OSes support Hyper-V Enhanced Session mode. ### Usage (Quick Start) - Move SynthRdp.exe to "%SystemDrive%\Windows\System32" folder in your virtual machine which needs to use SynthRdp. The platform of SynthRdp.exe needs to be the same as the virtual machine guest OS. - Open the Command Prompt as Administrator and execute the following commands. ``` SynthRdp Install SynthRdp Config Set DisableRemoteDesktop False SynthRdp Config Set EnableUserAuthentication False SynthRdp Config Set DisableBlankPassword False ``` ### Usage (Detailed) ``` Format: SynthRdp [Command] ... Commands: Help - Show this content. Install - Install SynthRdp service. Uninstall - Uninstall SynthRdp service. Start - Start SynthRdp service. Stop - Stop SynthRdp service. Config List - List all configurations related to SynthRdp. Config Set [Key] - Set the specific configuration with the specific value, or reset the specific configuration if you don't specify the value. Configuration Keys: DisableRemoteDesktop Set False to enable the remote desktop for this virtual machine. The default setting is True. Remote Desktop is necessary for the Hyper-V Enhanced Session because it is actually the RDP connection over the VMBus pipe. EnableUserAuthentication Set False to allow connections without Network Level Authentication. The default setting is True. Set this configuration option False is necessary for using the Hyper-V Enhanced Session via the SynthRdp service. DisableBlankPassword Set False to enable the usage of logging on this virtual machine as the account with the blank password via remote desktop. The default setting is True. Set this configuration option False will make you use the Hyper-V Enhanced Session via the SynthRdp service happier, but compromise the security. OverrideSystemImplementation Set True to use the Hyper-V Enhanced Session via the SynthRdp service on Windows 8.1 / Server 2012 R2 or later guests which have the built-in Hyper-V Enhanced Session support for this virtual machine. The default setting is False. You can set this configuration option True if you want to use your current virtual machine as the Hyper-V Enhanced Session proxy server. You need to reboot your virtual machine for applying this configuration option change. ServerHost Set the server host for the remote desktop connection you want to use in the Hyper-V Enhanced Session. The default setting is 127.0.0.1. ServerPort Set the server port for the remote desktop connection you want to use in the Hyper-V Enhanced Session. The default setting is 3389. Notes: - All command options are case-insensitive. - SynthRdp will run as a console application instead of service if you don't specify another command. Examples: SynthRdp Install SynthRdp Uninstall SynthRdp Start SynthRdp Stop SynthRdp Config List SynthRdp Config Set DisableRemoteDesktop False SynthRdp Config Set EnableUserAuthentication False SynthRdp Config Set DisableBlankPassword False SynthRdp Config Set OverrideSystemImplementation False SynthRdp Config Set ServerHost 127.0.0.1 SynthRdp Config Set ServerPort 3389 SynthRdp Config Set DisableRemoteDesktop SynthRdp Config Set EnableUserAuthentication SynthRdp Config Set DisableBlankPassword SynthRdp Config Set OverrideSystemImplementation SynthRdp Config Set ServerHost SynthRdp Config Set ServerPort ``` ### Suggestions - Users should use the SynthRdp to connect to Windows Vista or later guests for decent user experiences. ================================================ FILE: SynthRdp/AutoRun.inf ================================================ [autorun] open=SynthRdpInstaller.exe icon=SynthRdpInstaller.exe label=Hyper-V Enhanced Session Proxy Service (SynthRdp) ================================================ FILE: SynthRdp/SynthRdp.cpp ================================================ /* * PROJECT: NanaRun * FILE: SynthRdp.cpp * PURPOSE: Implementation for Hyper-V Enhanced Session Proxy Service * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) */ #define _WINSOCKAPI_ #include #include #include #pragma comment(lib, "Ws2_32.lib") #include #include #include #include EXTERN_C HANDLE WINAPI VmbusPipeClientTryOpenChannel( _In_ LPCGUID InterfaceType, _In_ LPCGUID InterfaceInstance, _In_ DWORD TimeoutInMsec, _In_ DWORD OpenMode) { VMBUS_PIPE_CHANNEL_INFO ChannelInfo = { 0 }; if (::VmbusPipeClientWaitChannel( InterfaceType, InterfaceInstance, TimeoutInMsec, &ChannelInfo)) { return ::VmbusPipeClientOpenChannel( &ChannelInfo, OpenMode); } return INVALID_HANDLE_VALUE; } namespace { static SERVICE_STATUS_HANDLE volatile g_ServiceStatusHandle = nullptr; static bool volatile g_ServiceIsRunning = true; static bool volatile g_InteractiveMode = false; } SOCKET SynthRdpConnectToServer() { SOCKET Result = INVALID_SOCKET; std::string ServerHost = "127.0.0.1"; { std::wstring Buffer(32767, L'\0'); DWORD Length = static_cast(Buffer.size()); if (ERROR_SUCCESS == ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" L"SynthRdp\\Configurations", L"ServerHost", RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY, nullptr, const_cast(Buffer.c_str()), &Length)) { Buffer.resize(std::wcslen(Buffer.c_str())); ServerHost = Mile::ToString(CP_UTF8, Buffer); } } std::string ServerPort = "3389"; { DWORD Data = 0; DWORD Length = sizeof(DWORD); if (ERROR_SUCCESS == ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" L"SynthRdp\\Configurations", L"ServerPort", RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &Data, &Length)) { ServerPort = Mile::FormatString("%hu", Data); } } int LastError = 0; addrinfo AddressHints = { 0 }; AddressHints.ai_family = AF_INET; AddressHints.ai_socktype = SOCK_STREAM; AddressHints.ai_protocol = IPPROTO_TCP; addrinfo* AddressInfo = nullptr; LastError = ::getaddrinfo( ServerHost.c_str(), ServerPort.c_str(), &AddressHints, &AddressInfo); if (0 == LastError) { for (addrinfo* Current = AddressInfo; nullptr != Current; Current = Current->ai_next) { SOCKET Socket = ::WSASocketW( Current->ai_family, Current->ai_socktype, Current->ai_protocol, nullptr, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == Socket) { LastError = ::WSAGetLastError(); continue; } if (SOCKET_ERROR != ::WSAConnect( Socket, Current->ai_addr, static_cast(Current->ai_addrlen), nullptr, nullptr, nullptr, nullptr)) { Result = Socket; break; } LastError = ::WSAGetLastError(); ::closesocket(Socket); } ::freeaddrinfo(AddressInfo); } if (INVALID_SOCKET == Result && 0 != LastError) { ::WSASetLastError(LastError); } return Result; } struct SynthRdpServiceConnectionContext { std::uint8_t SendBuffer[16384]; std::uint8_t RecvBuffer[16384]; }; void SynthRdpRedirectionWorker( _In_ HANDLE PipeHandle) { SOCKET Socket = INVALID_SOCKET; SynthRdpServiceConnectionContext* Context = nullptr; do { Socket = ::SynthRdpConnectToServer(); if (Socket == INVALID_SOCKET) { if (g_InteractiveMode) { std::printf( "[Error] SynthRdpConnectToServer failed (%d).\n", ::WSAGetLastError()); } break; } Context = reinterpret_cast( ::MileAllocateMemory(sizeof(SynthRdpServiceConnectionContext))); if (!Context) { if (g_InteractiveMode) { std::printf( "[Error] MileAllocateMemory failed.\n"); } break; } // X.224 Connection Request PDU (Patched) { DWORD NumberOfBytesRead = 0; if (!::MileReadFile( PipeHandle, Context->SendBuffer, static_cast(sizeof(Context->SendBuffer)), &NumberOfBytesRead)) { if (g_InteractiveMode) { std::printf( "[Error] MileReadFile failed (%d).\n", ::GetLastError()); } break; } // Set requestedProtocols to PROTOCOL_RDP (0x00000000). Context->SendBuffer[15] = 0x00; if (g_InteractiveMode) { std::wprintf( L"[Info] MileSocketSend: %d Bytes.\n", NumberOfBytesRead); } DWORD NumberOfBytesSent = 0; DWORD Flags = 0; if (!::MileSocketSend( Socket, Context->SendBuffer, NumberOfBytesRead, &NumberOfBytesSent, Flags)) { if (g_InteractiveMode) { std::printf( "[Error] MileSocketSend failed (%d).\n", ::WSAGetLastError()); } break; } } bool volatile ShouldRunning = true; HANDLE Vmbus2TcpThread = Mile::CreateThread([&]() { for (; ShouldRunning && g_ServiceIsRunning;) { DWORD NumberOfBytesRead = 0; if (!::MileReadFile( PipeHandle, Context->SendBuffer, static_cast(sizeof(Context->SendBuffer)), &NumberOfBytesRead)) { if (g_InteractiveMode) { std::printf( "[Error] MileReadFile failed (%d).\n", ::GetLastError()); } break; } if (g_InteractiveMode) { std::printf( "[Info] MileSocketSend: %d Bytes.\n", NumberOfBytesRead); } DWORD NumberOfBytesSent = 0; DWORD Flags = 0; if (!::MileSocketSend( Socket, Context->SendBuffer, NumberOfBytesRead, &NumberOfBytesSent, Flags)) { if (g_InteractiveMode) { std::printf( "[Error] MileSocketSend failed (%d).\n", ::WSAGetLastError()); } break; } } }); HANDLE Tcp2VmbusThread = Mile::CreateThread([&]() { for (; ShouldRunning && g_ServiceIsRunning;) { DWORD NumberOfBytesRecvd = 0; DWORD Flags = MSG_PARTIAL; if (!::MileSocketRecv( Socket, Context->RecvBuffer, static_cast(sizeof(Context->RecvBuffer)), &NumberOfBytesRecvd, &Flags)) { if (g_InteractiveMode) { std::printf( "[Error] MileSocketRecv failed (%d).\n", ::WSAGetLastError()); } break; } if (g_InteractiveMode) { std::printf( "[Info] MileWriteFile: %d Bytes.\n", NumberOfBytesRecvd); } DWORD NumberOfBytesWritten = 0; if (!::MileWriteFile( PipeHandle, Context->RecvBuffer, NumberOfBytesRecvd, &NumberOfBytesWritten)) { if (g_InteractiveMode) { std::printf( "[Error] MileWriteFile failed (%d).\n", ::GetLastError()); } break; } } }); for (;;) { if (WAIT_TIMEOUT != ::WaitForSingleObject( Vmbus2TcpThread, 100)) { ShouldRunning = false; break; } if (WAIT_TIMEOUT != ::WaitForSingleObject( Tcp2VmbusThread, 100)) { ShouldRunning = false; break; } } if (Vmbus2TcpThread) { ::CloseHandle(Vmbus2TcpThread); Vmbus2TcpThread = nullptr; } if (Tcp2VmbusThread) { ::CloseHandle(Tcp2VmbusThread); Tcp2VmbusThread = nullptr; } } while (false); if (Context) { ::MileFreeMemory(Context); } if (Socket != INVALID_SOCKET) { ::closesocket(Socket); } } DWORD SynthRdpMain() { WSADATA WSAData = { 0 }; { int WSAError = ::WSAStartup(MAKEWORD(2, 2), &WSAData); if (NO_ERROR != WSAError) { if (g_InteractiveMode) { std::printf( "[Error] WSAStartup failed (%d).\n", WSAError); } return WSAError; } } DWORD Error = ERROR_SUCCESS; HANDLE ControlChannelHandle = INVALID_HANDLE_VALUE; do { ControlChannelHandle = ::VmbusPipeClientTryOpenChannel( &SYNTHRDP_CONTROL_CLASS_ID, &SYNTHRDP_CONTROL_INSTANCE_ID, INFINITE, FILE_FLAG_OVERLAPPED); if (INVALID_HANDLE_VALUE == ControlChannelHandle) { Error = ::GetLastError(); if (g_InteractiveMode) { std::printf( "[Error] VmbusPipeClientTryOpenChannel failed (%d).\n", Error); } break; } SYNTHRDP_VERSION_REQUEST_MESSAGE Request; std::memset( &Request, 0, sizeof(SYNTHRDP_VERSION_REQUEST_MESSAGE)); Request.Header.Type = SynthrdpVersionRequest; Request.Header.Size = 0; Request.Version.AsDWORD = SYNTHRDP_VERSION_WINBLUE; Request.Reserved = 0; DWORD NumberOfBytesWritten = 0; if (!::MileWriteFile( ControlChannelHandle, &Request, sizeof(Request), &NumberOfBytesWritten)) { Error = ::GetLastError(); if (g_InteractiveMode) { std::printf( "[Error] MileWriteFile failed (%d).\n", Error); } break; } if (sizeof(SYNTHRDP_VERSION_REQUEST_MESSAGE) != NumberOfBytesWritten) { Error = ERROR_INVALID_DATA; if (g_InteractiveMode) { std::printf( "[Error] SYNTHRDP_VERSION_REQUEST_MESSAGE Invalid.\n"); } break; } SYNTHRDP_VERSION_RESPONSE_MESSAGE Response; std::memset( &Response, 0, sizeof(SYNTHRDP_VERSION_RESPONSE_MESSAGE)); DWORD NumberOfBytesRead = 0; if (!::MileReadFile( ControlChannelHandle, &Response, sizeof(Response), &NumberOfBytesRead)) { Error = ::GetLastError(); if (g_InteractiveMode) { std::printf( "[Error] MileReadFile failed (%d).\n", Error); } break; } if (sizeof(SYNTHRDP_VERSION_RESPONSE_MESSAGE) != NumberOfBytesRead || SYNTHRDP_TRUE_WITH_VERSION_EXCHANGE != Response.IsAccepted) { Error = ERROR_INVALID_DATA; if (g_InteractiveMode) { std::printf( "[Error] SYNTHRDP_VERSION_RESPONSE_MESSAGE Invalid.\n"); } break; } GUID Instances[] = { SYNTHRDP_DATA_INSTANCE_ID_1, SYNTHRDP_DATA_INSTANCE_ID_2, SYNTHRDP_DATA_INSTANCE_ID_3, SYNTHRDP_DATA_INSTANCE_ID_4, SYNTHRDP_DATA_INSTANCE_ID_5 }; for (size_t i = 0; g_ServiceIsRunning; ++i) { HANDLE DataChannelHandle = ::VmbusPipeClientTryOpenChannel( &SYNTHRDP_DATA_CLASS_ID, &Instances[i % (sizeof(Instances) / sizeof(*Instances))], 50, FILE_FLAG_OVERLAPPED); if (INVALID_HANDLE_VALUE == DataChannelHandle) { continue; } ::SynthRdpRedirectionWorker(DataChannelHandle); ::CloseHandle(DataChannelHandle); } } while (false); if (INVALID_HANDLE_VALUE != ControlChannelHandle) { ::CloseHandle(ControlChannelHandle); } ::WSACleanup(); return Error; } namespace { std::wstring GetCurrentProcessModulePath() { // 32767 is the maximum path length without the terminating null // character. std::wstring Path(32767, L'\0'); Path.resize(::GetModuleFileNameW( nullptr, &Path[0], static_cast(Path.size()))); return Path; } static std::wstring g_ServiceName = L"SynthRdp"; static std::wstring g_DisplayName = L"Hyper-V Enhanced Session Proxy Service"; } void WINAPI SynthRdpServiceHandler( _In_ DWORD dwControl) { switch (dwControl) { case SERVICE_CONTROL_STOP: { SERVICE_STATUS ServiceStatus = { 0 }; ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS; ServiceStatus.dwServiceSpecificExitCode = 0; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwWaitHint = 0; ::SetServiceStatus(g_ServiceStatusHandle, &ServiceStatus); g_ServiceIsRunning = false; break; } default: break; } } void WINAPI SynthRdpServiceMain( _In_ DWORD dwNumServicesArgs, _In_ LPWSTR* lpServiceArgVectors) { UNREFERENCED_PARAMETER(dwNumServicesArgs); UNREFERENCED_PARAMETER(lpServiceArgVectors); g_ServiceStatusHandle = ::RegisterServiceCtrlHandlerW( g_ServiceName.c_str(), ::SynthRdpServiceHandler); if (g_ServiceStatusHandle) { SERVICE_STATUS ServiceStatus = { 0 }; ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState = SERVICE_RUNNING; ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; ServiceStatus.dwWin32ExitCode = ERROR_SUCCESS; ServiceStatus.dwServiceSpecificExitCode = 0; ServiceStatus.dwCheckPoint = 0; ServiceStatus.dwWaitHint = 0; if (::SetServiceStatus(g_ServiceStatusHandle, &ServiceStatus)) { ServiceStatus.dwCurrentState = SERVICE_STOPPED; ServiceStatus.dwControlsAccepted = 0; ServiceStatus.dwWin32ExitCode = ::SynthRdpMain(); ::SetServiceStatus(g_ServiceStatusHandle, &ServiceStatus); } } } int SynthRdpInstallService() { DWORD Error = ERROR_SUCCESS; std::wstring ServiceBinaryPath = ::GetCurrentProcessModulePath() + L" Service"; SC_HANDLE ServiceControlManagerHandle = ::OpenSCManagerW( nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); if (ServiceControlManagerHandle) { SC_HANDLE ServiceHandle = ::CreateServiceW( ServiceControlManagerHandle, g_ServiceName.c_str(), g_DisplayName.c_str(), SERVICE_QUERY_STATUS | SERVICE_START, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, ServiceBinaryPath.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr); if (ServiceHandle) { SERVICE_STATUS_PROCESS ServiceStatus = { 0 }; if (!::MileStartServiceByHandle( ServiceHandle, 0, nullptr, &ServiceStatus)) { Error = ::GetLastError(); } ::CloseServiceHandle(ServiceHandle); } else { Error = ::GetLastError(); } ::CloseServiceHandle(ServiceControlManagerHandle); } else { Error = ::GetLastError(); } if (ERROR_SUCCESS == Error) { std::printf("[Success] SynthRdpInstallService\n"); } else { std::printf("[Error] SynthRdpInstallService (%d)\n", Error); } return Error; } int SynthRdpUninstallService() { DWORD Error = ERROR_SUCCESS; SC_HANDLE ServiceControlManagerHandle = ::OpenSCManagerW( nullptr, nullptr, SC_MANAGER_CONNECT); if (ServiceControlManagerHandle) { SC_HANDLE ServiceHandle = ::OpenServiceW( ServiceControlManagerHandle, g_ServiceName.c_str(), SERVICE_QUERY_STATUS | SERVICE_STOP | DELETE); if (ServiceHandle) { SERVICE_STATUS_PROCESS ServiceStatus = { 0 }; if (::MileStopServiceByHandle(ServiceHandle, &ServiceStatus)) { if (!::DeleteService(ServiceHandle)) { Error = ::GetLastError(); } } else { Error = ::GetLastError(); } ::CloseServiceHandle(ServiceHandle); } else { Error = ::GetLastError(); } ::CloseServiceHandle(ServiceControlManagerHandle); } else { Error = ::GetLastError(); } if (ERROR_SUCCESS == Error) { std::printf("[Success] SynthRdpUninstallService\n"); } else { std::printf("[Error] SynthRdpUninstallService (%d)\n", Error); } return Error; } int SynthRdpStartService() { DWORD Error = ERROR_SUCCESS; SERVICE_STATUS_PROCESS ServiceStatus = { 0 }; if (!::MileStartService(g_ServiceName.c_str(), &ServiceStatus)) { Error = ::GetLastError(); } if (ERROR_SUCCESS == Error) { std::printf("[Success] SynthRdpStartService\n"); } else { std::printf("[Error] SynthRdpStartService (%d)\n", Error); } return Error; } int SynthRdpStopService() { DWORD Error = ERROR_SUCCESS; SERVICE_STATUS_PROCESS ServiceStatus = { 0 }; if (!::MileStopService(g_ServiceName.c_str(), &ServiceStatus)) { Error = ::GetLastError(); } if (ERROR_SUCCESS == Error) { std::printf("[Success] SynthRdpStopService\n"); } else { std::printf("[Error] SynthRdpStopService (%d)\n", Error); } return Error; } int SynthRdpListConfigurations() { DWORD Error = ERROR_SUCCESS; DWORD Data = 0; DWORD Length = 0; bool DisableRemoteDesktop = false; { Data = 0; Length = sizeof(DWORD); Error = ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server", L"fDenyTSConnections", RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &Data, &Length); if (ERROR_SUCCESS == Error) { DisableRemoteDesktop = Data; } } bool EnableUserAuthentication = true; { Data = 0; Length = sizeof(DWORD); Error = ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\" L"WinStations\\RDP-Tcp", L"UserAuthentication", RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &Data, &Length); if (ERROR_SUCCESS == Error) { EnableUserAuthentication = Data; } } bool DisableBlankPassword = false; { Data = 0; Length = sizeof(DWORD); Error = ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Lsa", L"LimitBlankPasswordUse", RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &Data, &Length); if (ERROR_SUCCESS == Error) { DisableBlankPassword = Data; } } bool OverrideSystemImplementation = false; { Data = 0; Length = sizeof(DWORD); Error = ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Virtual Machine\\Guest", L"DisableEnhancedSessionConsoleConnection", RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &Data, &Length); if (ERROR_SUCCESS == Error) { OverrideSystemImplementation = Data; } } std::string ServerHost = "127.0.0.1"; { std::wstring Buffer(32767, L'\0'); Length = static_cast(Buffer.size()); Error = ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" L"SynthRdp\\Configurations", L"ServerHost", RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY, nullptr, const_cast(Buffer.c_str()), &Length); if (ERROR_SUCCESS == Error) { Buffer.resize(std::wcslen(Buffer.c_str())); ServerHost = Mile::ToString(CP_UTF8, Buffer); } } std::uint16_t ServerPort = 3389; { Data = 0; Length = sizeof(DWORD); Error = ::RegGetValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" L"SynthRdp\\Configurations", L"ServerPort", RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr, &Data, &Length); if (ERROR_SUCCESS == Error) { ServerPort = static_cast(Data); } } std::printf( "Configurations:\n" "\n" "DisableRemoteDesktop: %s\n" "EnableUserAuthentication: %s\n" "DisableBlankPassword: %s\n" "OverrideSystemImplementation: %s\n" "ServerHost: %s\n" "ServerPort: %hu\n" "\n", DisableRemoteDesktop ? "True" : "False", EnableUserAuthentication ? "True" : "False", DisableBlankPassword ? "True" : "False", OverrideSystemImplementation ? "True" : "False", ServerHost.c_str(), ServerPort); return Error; } int SynthRdpUpdateConfiguration( std::string const& Key, std::string const& Value) { DWORD Error = ERROR_SUCCESS; if (0 == ::_stricmp(Key.c_str(), "DisableRemoteDesktop")) { DWORD Data = 1; if (Value.empty() || 0 == ::_stricmp(Value.c_str(), "True")) { // Use the default value. } else if (0 == ::_stricmp(Value.c_str(), "False")) { Data = 0; } else { Error = ERROR_INVALID_PARAMETER; } if (ERROR_SUCCESS == Error) { Error = ::RegSetKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server", L"fDenyTSConnections", REG_DWORD, &Data, sizeof(DWORD)); } } else if (0 == ::_stricmp(Key.c_str(), "EnableUserAuthentication")) { DWORD Data = 1; if (Value.empty() || 0 == ::_stricmp(Value.c_str(), "True")) { // Use the default value. } else if (0 == ::_stricmp(Value.c_str(), "False")) { Data = 0; } else { Error = ERROR_INVALID_PARAMETER; } if (ERROR_SUCCESS == Error) { Error = ::RegSetKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\" L"WinStations\\RDP-Tcp", L"UserAuthentication", REG_DWORD, &Data, sizeof(DWORD)); } } else if (0 == ::_stricmp(Key.c_str(), "DisableBlankPassword")) { DWORD Data = 1; if (Value.empty() || 0 == ::_stricmp(Value.c_str(), "True")) { // Use the default value. } else if (0 == ::_stricmp(Value.c_str(), "False")) { Data = 0; } else { Error = ERROR_INVALID_PARAMETER; } if (ERROR_SUCCESS == Error) { Error = ::RegSetKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Lsa", L"LimitBlankPasswordUse", REG_DWORD, &Data, sizeof(DWORD)); } } else if (0 == ::_stricmp(Key.c_str(), "OverrideSystemImplementation")) { if (Value.empty() || 0 == ::_stricmp(Value.c_str(), "False")) { Error = ::RegDeleteKeyValueW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Virtual Machine\\Guest", L"DisableEnhancedSessionConsoleConnection"); } else if (0 == ::_stricmp(Value.c_str(), "True")) { DWORD Data = 1; Error = ::RegSetKeyValueW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Virtual Machine\\Guest", L"DisableEnhancedSessionConsoleConnection", REG_DWORD, &Data, sizeof(DWORD)); } else { Error = ERROR_INVALID_PARAMETER; } } else if (0 == ::_stricmp(Key.c_str(), "ServerHost")) { if (Value.empty()) { Error = ::RegDeleteKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" L"SynthRdp\\Configurations", L"ServerHost"); } else { std::wstring ServerHost = Mile::ToWideString(CP_UTF8, Value); Error = ::RegSetKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\" L"SynthRdp\\Configurations", L"ServerHost", REG_SZ, ServerHost.c_str(), static_cast((ServerHost.size() + 1) * sizeof(wchar_t))); } } else if (0 == ::_stricmp(Key.c_str(), "ServerPort")) { if (Value.empty()) { Error = ::RegDeleteKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\SynthRdp\\Configurations", L"ServerPort"); } else { DWORD Data = static_cast(Mile::ToUInt32(Value)); Error = ::RegSetKeyValueW( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\SynthRdp\\Configurations", L"ServerPort", REG_DWORD, &Data, sizeof(DWORD)); } } else { Error = ERROR_INVALID_PARAMETER; } if (ERROR_SUCCESS == Error) { std::printf( "[Success] SynthRdpUpdateConfiguration %s\n", Key.c_str()); } else { std::printf( "[Error] SynthRdpUpdateConfiguration %s (%d)\n", Key.c_str(), Error); } return Error; } int main() { ::std::printf( "SynthRdp " MILE_PROJECT_VERSION_UTF8_STRING " (Build " MILE_PROJECT_MACRO_TO_UTF8_STRING(MILE_PROJECT_VERSION_BUILD) ")" "\n" "(c) M2-Team and Contributors. All rights reserved.\n" "\n"); std::vector Arguments = Mile::SplitCommandLineString( Mile::ToString(CP_UTF8, ::GetCommandLineW())); bool NeedParse = (Arguments.size() > 1); if (!NeedParse) { g_InteractiveMode = true; std::printf( "[Info] SynthRdp will run as a console application instead of " "service.\n" "[Info] Use \"SynthRdp Help\" for more commands.\n" "\n"); return ::SynthRdpMain(); } int Result = 0; bool ParseError = false; bool ShowHelp = false; if (0 == ::_stricmp(Arguments[1].c_str(), "Help")) { ShowHelp = true; } else if (0 == ::_stricmp(Arguments[1].c_str(), "Service")) { ::FreeConsole(); SERVICE_TABLE_ENTRYW ServiceStartTable[] = { { const_cast(g_ServiceName.c_str()), ::SynthRdpServiceMain } }; return ::StartServiceCtrlDispatcherW(ServiceStartTable) ? ERROR_SUCCESS : ::GetLastError(); } else if (0 == ::_stricmp(Arguments[1].c_str(), "Install")) { Result = ::SynthRdpInstallService(); } else if (0 == ::_stricmp(Arguments[1].c_str(), "Uninstall")) { Result = ::SynthRdpUninstallService(); } else if (0 == ::_stricmp(Arguments[1].c_str(), "Start")) { Result = ::SynthRdpStartService(); } else if (0 == ::_stricmp(Arguments[1].c_str(), "Stop")) { Result = ::SynthRdpStopService(); } else if (0 == ::_stricmp(Arguments[1].c_str(), "Config")) { ParseError = !(Arguments.size() > 2); if (!ParseError) { if (0 == ::_stricmp(Arguments[2].c_str(), "List")) { Result = ::SynthRdpListConfigurations(); } else if (0 == ::_stricmp(Arguments[2].c_str(), "Set")) { ParseError = !(Arguments.size() > 3); if (!ParseError) { Result = ::SynthRdpUpdateConfiguration( Arguments[3], (Arguments.size() > 4) ? Arguments[4] : std::string()); } } } } else { ParseError = true; } if (ParseError) { std::printf( "[Error] Unrecognized command.\n" "\n"); } if (ParseError || ShowHelp) { std::printf( "Format: SynthRdp [Command] ...\n" "\n" "Commands:\n" "\n" " Help - Show this content.\n" "\n" " Install - Install SynthRdp service.\n" " Uninstall - Uninstall SynthRdp service.\n" " Start - Start SynthRdp service.\n" " Stop - Stop SynthRdp service.\n" "\n" " Config List - List all configurations related to SynthRdp.\n" " Config Set [Key] - Set the specific configuration\n" " with the specific value, or reset\n" " the specific configuration if you\n" " don't specify the value.\n" "\n" "Configuration Keys:\n" "\n" " DisableRemoteDesktop \n" " Set False to enable the remote desktop for this virtual\n" " machine. The default setting is True. Remote Desktop is\n" " necessary for the Hyper-V Enhanced Session because it is\n" " actually the RDP connection over the VMBus pipe.\n" " EnableUserAuthentication \n" " Set False to allow connections without Network Level\n" " Authentication. The default setting is True. Set this\n" " configuration option False is necessary for using the\n" " Hyper-V Enhanced Session via the SynthRdp service.\n" " DisableBlankPassword \n" " Set False to enable the usage of logging on this virtual\n" " machine as the account with the blank password via remote\n" " desktop. The default setting is True. Set this\n" " configuration option False will make you use the Hyper-V\n" " Enhanced Session via the SynthRdp service happier, but\n" " compromise the security.\n" " OverrideSystemImplementation \n" " Set True to use the Hyper-V Enhanced Session via the\n" " SynthRdp service on Windows 8.1 / Server 2012 R2 or later\n" " guests which have the built-in Hyper-V Enhanced Session\n" " support for this virtual machine. The default setting is\n" " False. You can set this configuration option True if you\n" " want to use your current virtual machine as the Hyper-V\n" " Enhanced Session proxy server. You need to reboot your\n" " virtual machine for applying this configuration option\n" " change.\n" " ServerHost \n" " Set the server host for the remote desktop connection you\n" " want to use in the Hyper-V Enhanced Session. The default\n" " setting is 127.0.0.1.\n" " ServerPort \n" " Set the server port for the remote desktop connection you\n" " want to use in the Hyper-V Enhanced Session. The default\n" " setting is 3389.\n" "\n" "Notes:\n" " - All command options are case-insensitive.\n" " - SynthRdp will run as a console application instead of\n" " service if you don't specify another command.\n" "\n" "Examples:\n" "\n" " SynthRdp Install\n" " SynthRdp Uninstall\n" " SynthRdp Start\n" " SynthRdp Stop\n" "\n" " SynthRdp Config List\n" "\n" " SynthRdp Config Set DisableRemoteDesktop False\n" " SynthRdp Config Set EnableUserAuthentication False\n" " SynthRdp Config Set DisableBlankPassword False\n" " SynthRdp Config Set OverrideSystemImplementation False\n" " SynthRdp Config Set ServerHost 127.0.0.1\n" " SynthRdp Config Set ServerPort 3389\n" "\n" " SynthRdp Config Set DisableRemoteDesktop\n" " SynthRdp Config Set EnableUserAuthentication\n" " SynthRdp Config Set DisableBlankPassword\n" " SynthRdp Config Set OverrideSystemImplementation\n" " SynthRdp Config Set ServerHost\n" " SynthRdp Config Set ServerPort\n" "\n"); } return Result; } ================================================ FILE: SynthRdp/SynthRdp.iss ================================================ ; -- 64BitThreeArch.iss -- ; Demonstrates how to install a program built for three different ; architectures (x86, x64, Arm64) using a single installer. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING .ISS SCRIPT FILES! [Setup] AppName=Hyper-V Enhanced Session Proxy Service (SynthRdp) AppVersion=1.0.92.0 AppPublisher=M2-Team AppPublisherURL=https://github.com/M2Team/NanaRun WizardStyle=modern DefaultDirName={autopf}\M2-Team\NanaRun DefaultGroupName=NanaRun UninstallDisplayIcon={app}\SynthRdp.exe Compression=lzma2 SolidCompression=yes OutputDir=..\Output\SynthRdpInstallationImage ; "ArchitecturesInstallIn64BitMode=x64compatible or arm64" instructs ; Setup to use "64-bit install mode" on x64-compatible systems and ; Arm64 systems, meaning Setup should use the native 64-bit Program ; Files directory and the 64-bit view of the registry. On all other ; OS architectures (e.g., 32-bit x86), Setup will use "32-bit ; install mode". ArchitecturesInstallIn64BitMode=x64compatible or arm64 SetupLogging=yes OutputBaseFilename=SynthRdpInstaller [Files] ; In order of preference, we want to install: ; - Arm64 binaries on Arm64 systems ; - else, x64 binaries on x64-compatible systems ; - else, x86 binaries ; Place all Arm64-specific files here, using 'Check: PreferArm64Files' on each entry. Source: "..\Output\Binaries\Release\ARM64\SynthRdp.exe"; DestDir: "{sysnative}"; DestName: "SynthRdp.exe"; Check: PreferArm64Files ; Place all x64-specific files here, using 'Check: PreferX64Files' on each entry. ; Only the first entry should include the 'solidbreak' flag. Source: "..\Output\Binaries\Release\x64\SynthRdp.exe"; DestDir: "{sysnative}"; DestName: "SynthRdp.exe"; Check: PreferX64Files; Flags: solidbreak ; Place all x86-specific files here, using 'Check: PreferX86Files' on each entry. ; Only the first entry should include the 'solidbreak' flag. Source: "..\Output\Binaries\Release\Win32\SynthRdp.exe"; DestDir: "{sysnative}"; DestName: "SynthRdp.exe"; Check: PreferX86Files; Flags: solidbreak ; Place all common files here. ; Only the first entry should include the 'solidbreak' flag. ;Source: "MyProg.chm"; DestDir: "{app}"; Flags: solidbreak ;Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme [Code] function PreferArm64Files: Boolean; begin Result := IsArm64; end; function PreferX64Files: Boolean; begin Result := not PreferArm64Files and IsX64Compatible; end; function PreferX86Files: Boolean; begin Result := not PreferArm64Files and not PreferX64Files; end; [Run] Filename: "{sysnative}\SynthRdp.exe"; Parameters: "Uninstall"; Flags: runhidden logoutput Filename: "{sysnative}\SynthRdp.exe"; Parameters: "Install"; Flags: runhidden logoutput Filename: "{sysnative}\SynthRdp.exe"; Parameters: "Config Set DisableRemoteDesktop False"; Flags: runhidden logoutput Filename: "{sysnative}\SynthRdp.exe"; Parameters: "Config Set EnableUserAuthentication False"; Flags: runhidden logoutput Filename: "{sysnative}\SynthRdp.exe"; Parameters: "Config Set DisableBlankPassword False"; Flags: runhidden logoutput [UninstallRun] Filename: "{sysnative}\SynthRdp.exe"; Parameters: "Uninstall"; Flags: runhidden logoutput; RunOnceId: RemoveService ================================================ FILE: SynthRdp/SynthRdp.manifest ================================================  ================================================ FILE: SynthRdp/SynthRdp.vcxproj ================================================  {ABFD05C2-4673-49EC-A23E-9CA53223EEC2} SynthRdp ConsoleApplication SynthRdp.manifest true true M2-Team Hyper-V Enhanced Session Proxy Service SynthRdp © M2-Team and Contributors. All rights reserved. SynthRdp.exe NanaRun 1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0 Preview 3 true 5.1.2600.0 true NoExtensions 1.0.1171 1.2.907 ================================================ FILE: Tools/Default.isl ================================================ ; *** Inno Setup version 6.1.0+ English messages *** ; ; To download user-contributed translations of this file, go to: ; https://jrsoftware.org/files/istrans/ ; ; Note: When translating this text, do not add periods (.) to the end of ; messages that didn't have them already, because on those messages Inno ; Setup adds the periods automatically (appending a period would result in ; two periods being displayed). [LangOptions] ; The following three entries are very important. Be sure to read and ; understand the '[LangOptions] section' topic in the help file. LanguageName=English LanguageID=$0409 LanguageCodePage=0 ; If the language you are translating to requires special font faces or ; sizes, uncomment any of the following entries and change them accordingly. ;DialogFontName= ;DialogFontSize=8 ;WelcomeFontName=Verdana ;WelcomeFontSize=12 ;TitleFontName=Arial ;TitleFontSize=29 ;CopyrightFontName=Arial ;CopyrightFontSize=8 [Messages] ; *** Application titles SetupAppTitle=Setup SetupWindowTitle=Setup - %1 UninstallAppTitle=Uninstall UninstallAppFullTitle=%1 Uninstall ; *** Misc. common InformationTitle=Information ConfirmTitle=Confirm ErrorTitle=Error ; *** SetupLdr messages SetupLdrStartupMessage=This will install %1. Do you wish to continue? LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted HelpTextNote= ; *** Startup error messages LastErrorMessage=%1.%n%nError %2: %3 SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program. SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program. SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program. InvalidParameter=An invalid parameter was passed on the command line:%n%n%1 SetupAlreadyRunning=Setup is already running. WindowsVersionNotSupported=This program does not support the version of Windows your computer is running. WindowsServicePackRequired=This program requires %1 Service Pack %2 or later. NotOnThisPlatform=This program will not run on %1. OnlyOnThisPlatform=This program must be run on %1. OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1 WinVersionTooLowError=This program requires %1 version %2 or later. WinVersionTooHighError=This program cannot be installed on %1 version %2 or later. AdminPrivilegesRequired=You must be logged in as an administrator when installing this program. PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program. SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. ; *** Startup questions PrivilegesRequiredOverrideTitle=Select Setup Install Mode PrivilegesRequiredOverrideInstruction=Select install mode PrivilegesRequiredOverrideText1=%1 can be installed for all users (requires administrative privileges), or for you only. PrivilegesRequiredOverrideText2=%1 can be installed for you only, or for all users (requires administrative privileges). PrivilegesRequiredOverrideAllUsers=Install for &all users PrivilegesRequiredOverrideAllUsersRecommended=Install for &all users (recommended) PrivilegesRequiredOverrideCurrentUser=Install for &me only PrivilegesRequiredOverrideCurrentUserRecommended=Install for &me only (recommended) ; *** Misc. errors ErrorCreatingDir=Setup was unable to create the directory "%1" ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files ; *** Setup common messages ExitSetupTitle=Exit Setup ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup? AboutSetupMenuItem=&About Setup... AboutSetupTitle=About Setup AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4 AboutSetupNote= TranslatorNote= ; *** Buttons ButtonBack=< &Back ButtonNext=&Next > ButtonInstall=&Install ButtonOK=OK ButtonCancel=Cancel ButtonYes=&Yes ButtonYesToAll=Yes to &All ButtonNo=&No ButtonNoToAll=N&o to All ButtonFinish=&Finish ButtonBrowse=&Browse... ButtonWizardBrowse=B&rowse... ButtonNewFolder=&Make New Folder ; *** "Select Language" dialog messages SelectLanguageTitle=Select Setup Language SelectLanguageLabel=Select the language to use during the installation. ; *** Common wizard text ClickNext=Click Next to continue, or Cancel to exit Setup. BeveledLabel= BrowseDialogTitle=Browse For Folder BrowseDialogLabel=Select a folder in the list below, then click OK. NewFolderName=New Folder ; *** "Welcome" wizard page WelcomeLabel1=Welcome to the [name] Setup Wizard WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing. ; *** "Password" wizard page WizardPassword=Password PasswordLabel1=This installation is password protected. PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive. PasswordEditLabel=&Password: IncorrectPassword=The password you entered is not correct. Please try again. ; *** "License Agreement" wizard page WizardLicense=License Agreement LicenseLabel=Please read the following important information before continuing. LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation. LicenseAccepted=I &accept the agreement LicenseNotAccepted=I &do not accept the agreement ; *** "Information" wizard pages WizardInfoBefore=Information InfoBeforeLabel=Please read the following important information before continuing. InfoBeforeClickLabel=When you are ready to continue with Setup, click Next. WizardInfoAfter=Information InfoAfterLabel=Please read the following important information before continuing. InfoAfterClickLabel=When you are ready to continue with Setup, click Next. ; *** "User Information" wizard page WizardUserInfo=User Information UserInfoDesc=Please enter your information. UserInfoName=&User Name: UserInfoOrg=&Organization: UserInfoSerial=&Serial Number: UserInfoNameRequired=You must enter a name. ; *** "Select Destination Location" wizard page WizardSelectDir=Select Destination Location SelectDirDesc=Where should [name] be installed? SelectDirLabel3=Setup will install [name] into the following folder. SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. DiskSpaceGBLabel=At least [gb] GB of free disk space is required. DiskSpaceMBLabel=At least [mb] MB of free disk space is required. CannotInstallToNetworkDrive=Setup cannot install to a network drive. CannotInstallToUNCPath=Setup cannot install to a UNC path. InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another. DiskSpaceWarningTitle=Not Enough Disk Space DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway? DirNameTooLong=The folder name or path is too long. InvalidDirName=The folder name is not valid. BadDirName32=Folder names cannot include any of the following characters:%n%n%1 DirExistsTitle=Folder Exists DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway? DirDoesntExistTitle=Folder Does Not Exist DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created? ; *** "Select Components" wizard page WizardSelectComponents=Select Components SelectComponentsDesc=Which components should be installed? SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue. FullInstallation=Full installation ; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) CompactInstallation=Compact installation CustomInstallation=Custom installation NoUninstallWarningTitle=Components Exist NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway? ComponentSize1=%1 KB ComponentSize2=%1 MB ComponentsDiskSpaceGBLabel=Current selection requires at least [gb] GB of disk space. ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space. ; *** "Select Additional Tasks" wizard page WizardSelectTasks=Select Additional Tasks SelectTasksDesc=Which additional tasks should be performed? SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next. ; *** "Select Start Menu Folder" wizard page WizardSelectProgramGroup=Select Start Menu Folder SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts? SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder. SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. MustEnterGroupName=You must enter a folder name. GroupNameTooLong=The folder name or path is too long. InvalidGroupName=The folder name is not valid. BadGroupName=The folder name cannot include any of the following characters:%n%n%1 NoProgramGroupCheck2=&Don't create a Start Menu folder ; *** "Ready to Install" wizard page WizardReady=Ready to Install ReadyLabel1=Setup is now ready to begin installing [name] on your computer. ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings. ReadyLabel2b=Click Install to continue with the installation. ReadyMemoUserInfo=User information: ReadyMemoDir=Destination location: ReadyMemoType=Setup type: ReadyMemoComponents=Selected components: ReadyMemoGroup=Start Menu folder: ReadyMemoTasks=Additional tasks: ; *** TDownloadWizardPage wizard page and DownloadTemporaryFile DownloadingLabel=Downloading additional files... ButtonStopDownload=&Stop download StopDownload=Are you sure you want to stop the download? ErrorDownloadAborted=Download aborted ErrorDownloadFailed=Download failed: %1 %2 ErrorDownloadSizeFailed=Getting size failed: %1 %2 ErrorFileHash1=File hash failed: %1 ErrorFileHash2=Invalid file hash: expected %1, found %2 ErrorProgress=Invalid progress: %1 of %2 ErrorFileSize=Invalid file size: expected %1, found %2 ; *** "Preparing to Install" wizard page WizardPreparing=Preparing to Install PreparingDesc=Setup is preparing to install [name] on your computer. PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name]. CannotContinue=Setup cannot continue. Please click Cancel to exit. ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications. CloseApplications=&Automatically close the applications DontCloseApplications=&Do not close the applications ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing. PrepareToInstallNeedsRestart=Setup must restart your computer. After restarting your computer, run Setup again to complete the installation of [name].%n%nWould you like to restart now? ; *** "Installing" wizard page WizardInstalling=Installing InstallingLabel=Please wait while Setup installs [name] on your computer. ; *** "Setup Completed" wizard page FinishedHeadingLabel=Completing the [name] Setup Wizard FinishedLabelNoIcons=Setup has finished installing [name] on your computer. FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed shortcuts. ClickFinish=Click Finish to exit Setup. FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now? FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now? ShowReadmeCheck=Yes, I would like to view the README file YesRadio=&Yes, restart the computer now NoRadio=&No, I will restart the computer later ; used for example as 'Run MyProg.exe' RunEntryExec=Run %1 ; used for example as 'View Readme.txt' RunEntryShellExec=View %1 ; *** "Setup Needs the Next Disk" stuff ChangeDiskTitle=Setup Needs the Next Disk SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse. PathLabel=&Path: FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder. SelectDirectoryLabel=Please specify the location of the next disk. ; *** Installation phase messages SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again. AbortRetryIgnoreSelectAction=Select action AbortRetryIgnoreRetry=&Try again AbortRetryIgnoreIgnore=&Ignore the error and continue AbortRetryIgnoreCancel=Cancel installation ; *** Installation status messages StatusClosingApplications=Closing applications... StatusCreateDirs=Creating directories... StatusExtractFiles=Extracting files... StatusCreateIcons=Creating shortcuts... StatusCreateIniEntries=Creating INI entries... StatusCreateRegistryEntries=Creating registry entries... StatusRegisterFiles=Registering files... StatusSavingUninstall=Saving uninstall information... StatusRunProgram=Finishing installation... StatusRestartingApplications=Restarting applications... StatusRollback=Rolling back changes... ; *** Misc. errors ErrorInternal2=Internal error: %1 ErrorFunctionFailedNoCode=%1 failed ErrorFunctionFailed=%1 failed; code %2 ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3 ErrorExecutingProgram=Unable to execute file:%n%1 ; *** Registry errors ErrorRegOpenKey=Error opening registry key:%n%1\%2 ErrorRegCreateKey=Error creating registry key:%n%1\%2 ErrorRegWriteKey=Error writing to registry key:%n%1\%2 ; *** INI errors ErrorIniEntry=Error creating INI entry in file "%1". ; *** File copying errors FileAbortRetryIgnoreSkipNotRecommended=&Skip this file (not recommended) FileAbortRetryIgnoreIgnoreNotRecommended=&Ignore the error and continue (not recommended) SourceIsCorrupted=The source file is corrupted SourceDoesntExist=The source file "%1" does not exist ExistingFileReadOnly2=The existing file could not be replaced because it is marked read-only. ExistingFileReadOnlyRetry=&Remove the read-only attribute and try again ExistingFileReadOnlyKeepExisting=&Keep the existing file ErrorReadingExistingDest=An error occurred while trying to read the existing file: FileExistsSelectAction=Select action FileExists2=The file already exists. FileExistsOverwriteExisting=&Overwrite the existing file FileExistsKeepExisting=&Keep the existing file FileExistsOverwriteOrKeepAll=&Do this for the next conflicts ExistingFileNewerSelectAction=Select action ExistingFileNewer2=The existing file is newer than the one Setup is trying to install. ExistingFileNewerOverwriteExisting=&Overwrite the existing file ExistingFileNewerKeepExisting=&Keep the existing file (recommended) ExistingFileNewerOverwriteOrKeepAll=&Do this for the next conflicts ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file: ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: ErrorReadingSource=An error occurred while trying to read the source file: ErrorCopying=An error occurred while trying to copy a file: ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: ErrorRestartReplace=RestartReplace failed: ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: ErrorRegisterServer=Unable to register the DLL/OCX: %1 ErrorRegSvr32Failed=RegSvr32 failed with exit code %1 ErrorRegisterTypeLib=Unable to register the type library: %1 ; *** Uninstall display name markings ; used for example as 'My Program (32-bit)' UninstallDisplayNameMark=%1 (%2) ; used for example as 'My Program (32-bit, All users)' UninstallDisplayNameMarks=%1 (%2, %3) UninstallDisplayNameMark32Bit=32-bit UninstallDisplayNameMark64Bit=64-bit UninstallDisplayNameMarkAllUsers=All users UninstallDisplayNameMarkCurrentUser=Current user ; *** Post-installation errors ErrorOpeningReadme=An error occurred while trying to open the README file. ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually. ; *** Uninstaller messages UninstallNotFound=File "%1" does not exist. Cannot uninstall. UninstallOpenError=File "%1" could not be opened. Cannot uninstall UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components? UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows. OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges. UninstallStatusLabel=Please wait while %1 is removed from your computer. UninstalledAll=%1 was successfully removed from your computer. UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually. UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now? UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall ; *** Uninstallation phase messages ConfirmDeleteSharedFileTitle=Remove Shared File? ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm. SharedFileNameLabel=File name: SharedFileLocationLabel=Location: WizardUninstalling=Uninstall Status StatusUninstalling=Uninstalling %1... ; *** Shutdown block reasons ShutdownBlockReasonInstallingApp=Installing %1. ShutdownBlockReasonUninstallingApp=Uninstalling %1. ; The custom messages below aren't used by Setup itself, but if you make ; use of them in your scripts, you'll want to translate them. [CustomMessages] NameAndVersion=%1 version %2 AdditionalIcons=Additional shortcuts: CreateDesktopIcon=Create a &desktop shortcut CreateQuickLaunchIcon=Create a &Quick Launch shortcut ProgramOnTheWeb=%1 on the Web UninstallProgram=Uninstall %1 LaunchProgram=Launch %1 AssocFileExtension=&Associate %1 with the %2 file extension AssocingFileExtension=Associating %1 with the %2 file extension... AutoStartProgramGroupDescription=Startup: AutoStartProgram=Automatically start %1 AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway? ================================================ FILE: VirtualSmb/VirtualSmb.cpp ================================================ /* * PROJECT: SynthRdp * FILE: VirtualSmb.cpp * PURPOSE: Implementation for Hyper-V Virtual SMB Guest Utility * * LICENSE: The MIT License * * MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com) */ #include #include #include // Reference: https://github.com/microsoft/hcsshim // /blob/ed5784127999cfd4c08c254626cc964cb20d7948 // /internal/gcs-sidecar/vsmb.go typedef struct _SMB2_INSTANCE_CONFIGURATION_17134 { UINT32 DormantDirectoryTimeout; UINT32 DormantFileTimeout; UINT32 DormantFileLimit; UINT32 FileInfoCacheLifetime; UINT32 FileNotFoundCacheLifetime; UINT32 DirectoryCacheLifetime; UINT32 FileInfoCacheEntriesMax; UINT32 FileNotFoundCacheEntriesMax; UINT32 DirectoryCacheEntriesMax; UINT32 DirectoryCacheSizeMax; UINT8 RequireSecuritySignature; UINT8 RequireEncryption; UINT8 Padding[2]; } SMB2_INSTANCE_CONFIGURATION_17134, *PSMB2_INSTANCE_CONFIGURATION_17134; typedef struct _SMB2_INSTANCE_CONFIGURATION_20348 { UINT32 DormantDirectoryTimeout; UINT32 DormantFileTimeout; UINT32 DormantFileLimit; UINT32 FileInfoCacheLifetime; UINT32 FileNotFoundCacheLifetime; UINT32 DirectoryCacheLifetime; UINT32 FileInfoCacheEntriesMax; UINT32 FileNotFoundCacheEntriesMax; UINT32 DirectoryCacheEntriesMax; UINT32 DirectoryCacheSizeMax; UINT32 ReadAheadGranularity; UINT8 RequireSecuritySignature; UINT8 RequireEncryption; UINT8 Padding[2]; } SMB2_INSTANCE_CONFIGURATION_20348, *PSMB2_INSTANCE_CONFIGURATION_20348; typedef struct _SMB2_INSTANCE_CONFIGURATION_25398 { UINT32 DormantDirectoryTimeout; UINT32 DormantFileTimeout; UINT32 DormantFileLimit; UINT32 FileInfoCacheLifetime; UINT32 FileNotFoundCacheLifetime; UINT32 DirectoryCacheLifetime; UINT32 FileInfoCacheEntriesMax; UINT32 FileNotFoundCacheEntriesMax; UINT32 DirectoryCacheEntriesMax; UINT32 DirectoryCacheSizeMax; UINT32 ReadAheadGranularity; UINT32 VolumeFeatureSupportCacheLifetime; UINT32 VolumeFeatureSupportCacheEntriesMax; UINT8 RequireSecuritySignature; UINT8 RequireEncryption; UINT8 Padding[2]; } SMB2_INSTANCE_CONFIGURATION_25398, *PSMB2_INSTANCE_CONFIGURATION_25398; // Since 26100.xxxx typedef struct _SMB2_INSTANCE_CONFIGURATION { UINT32 DormantDirectoryTimeout; UINT32 DormantFileTimeout; UINT32 DormantFileLimit; UINT32 FileInfoCacheLifetime; UINT32 FileNotFoundCacheLifetime; UINT32 DirectoryCacheLifetime; UINT32 FileInfoCacheEntriesMax; UINT32 FileNotFoundCacheEntriesMax; UINT32 DirectoryCacheEntriesMax; UINT32 DirectoryCacheSizeMax; UINT32 ReadAheadGranularity; UINT32 VolumeFeatureSupportCacheLifetime; UINT32 VolumeFeatureSupportCacheEntriesMax; UINT32 FileAbeStatusCacheLifetime; UINT8 RequireSecuritySignature; UINT8 RequireEncryption; UINT8 Padding[2]; } SMB2_INSTANCE_CONFIGURATION, *PSMB2_INSTANCE_CONFIGURATION; typedef struct _LMR_CONNECTION_PROPERTIES_10586 { union { UINT8 Value; struct { UINT8 StatusCodeFiltering : 1; // Symbol UINT8 FileInfoCache : 1; // Symbol UINT8 FileNotFoundCache : 1; // Symbol UINT8 DirectoryCache : 1; // Symbol UINT8 Leasing : 1; // Symbol UINT8 SuppressRenames : 1; // Symbol UINT8 ForceMultiChannel : 1; // Symbol UINT8 ForceKeepalive : 1; // Symbol } Fields; } Flags1; union { UINT8 Value; struct { UINT8 DisableBandwidthThrottling : 1; // Symbol UINT8 Reserved : 7; } Fields; } Flags2; UINT8 Padding[2]; UINT32 SessionTimeoutInterval; UINT32 CAHandleKeepaliveInterval; UINT32 NonCAHandleKeepaliveInterval; UINT32 ActiveIOKeepaliveInterval; } LMR_CONNECTION_PROPERTIES_10586, *PLMR_CONNECTION_PROPERTIES_10586; typedef struct _LMR_CONNECTION_PROPERTIES_25398 { union { UINT8 Value; struct { UINT8 StatusCodeFiltering : 1; // Symbol UINT8 FileInfoCache : 1; // Symbol UINT8 FileNotFoundCache : 1; // Symbol UINT8 DirectoryCache : 1; // Symbol UINT8 Leasing : 1; // Symbol UINT8 SuppressRenames : 1; // Symbol UINT8 ForceMultiChannel : 1; // Symbol UINT8 ForceKeepalive : 1; // Symbol } Fields; } Flags1; union { UINT8 Value; struct { UINT8 DisableBandwidthThrottling : 1; // Symbol UINT8 Reserved : 7; } Fields; } Flags2; UINT8 Padding[2]; UINT32 SessionTimeoutInterval; UINT32 CAHandleKeepaliveInterval; UINT32 NonCAHandleKeepaliveInterval; UINT32 ActiveIOKeepaliveInterval; UINT32 DisableRdma; UINT32 ConnectionCountPerRdmaInterface; } LMR_CONNECTION_PROPERTIES_25398, *PLMR_CONNECTION_PROPERTIES_25398; // Since 26100.1 typedef struct _LMR_CONNECTION_PROPERTIES { union { UINT8 Value; struct { UINT8 StatusCodeFiltering : 1; // Symbol UINT8 FileInfoCache : 1; // Symbol UINT8 FileNotFoundCache : 1; // Symbol UINT8 DirectoryCache : 1; // Symbol UINT8 Leasing : 1; // Symbol UINT8 SuppressRenames : 1; // Symbol UINT8 ForceMultiChannel : 1; // Symbol UINT8 ForceKeepalive : 1; // Symbol } Fields; } Flags1; union { UINT8 Value; struct { UINT8 DisableBandwidthThrottling : 1; // Symbol UINT8 Reserved : 7; } Fields; } Flags2; UINT8 Padding[2]; UINT32 SessionTimeoutInterval; UINT32 CAHandleKeepaliveInterval; UINT32 NonCAHandleKeepaliveInterval; UINT32 ActiveIOKeepaliveInterval; UINT32 DisableRdma; UINT32 ConnectionCountPerRdmaInterface; UINT16 AlternateTCPPort; UINT16 AlternateQuicPort; UINT16 AlternateRdmaPort; UINT8 Padding2[2]; } LMR_CONNECTION_PROPERTIES, *PLMR_CONNECTION_PROPERTIES; #define LMR_INSTANCE_FLAG_REGISTER_FILESYSTEM 0x2 #define LMR_INSTANCE_FLAG_USE_CUSTOM_TRANSPORTS 0x4 #define LMR_INSTANCE_FLAG_ALLOW_GUEST_AUTH 0x8 #define LMR_INSTANCE_FLAG_SUPPORTS_DIRECTMAPPED_IO 0x10 typedef struct _LMR_START_INSTANCE_REQUEST_10586 { UINT32 StructureSize; UINT32 IoTimeout; UINT32 IoRetryCount; UINT16 Flags; // LMR_INSTANCE_FLAG_* UINT16 AlternatePort; // Symbol UINT32 Reserved1; LMR_CONNECTION_PROPERTIES_10586 DefaultConnectionProperties; UINT8 InstanceId; UINT8 Reserved2; UINT16 DeviceNameLength; WCHAR DeviceName[ANYSIZE_ARRAY]; // Symbol } LMR_START_INSTANCE_REQUEST_10586, *PLMR_START_INSTANCE_REQUEST_10586; typedef struct _LMR_START_INSTANCE_REQUEST_17134 { UINT32 StructureSize; UINT32 IoTimeout; UINT32 IoRetryCount; UINT16 Flags; // LMR_INSTANCE_FLAG_* UINT16 AlternatePort; // Symbol UINT32 Reserved1; SMB2_INSTANCE_CONFIGURATION_17134 InstanceConfig; LMR_CONNECTION_PROPERTIES_10586 DefaultConnectionProperties; UINT8 InstanceId; UINT8 Reserved2; UINT16 DeviceNameLength; WCHAR DeviceName[ANYSIZE_ARRAY]; // Symbol } LMR_START_INSTANCE_REQUEST_17134, *PLMR_START_INSTANCE_REQUEST_17134; typedef struct _LMR_START_INSTANCE_REQUEST_20348 { UINT32 StructureSize; UINT32 IoTimeout; UINT32 IoRetryCount; UINT16 Flags; // LMR_INSTANCE_FLAG_* UINT16 AlternatePort; // Symbol UINT32 Reserved1; SMB2_INSTANCE_CONFIGURATION_20348 InstanceConfig; LMR_CONNECTION_PROPERTIES_10586 DefaultConnectionProperties; UINT8 InstanceId; UINT8 Reserved2; UINT16 DeviceNameLength; WCHAR DeviceName[ANYSIZE_ARRAY]; // Symbol } LMR_START_INSTANCE_REQUEST_20348, *PLMR_START_INSTANCE_REQUEST_20348; typedef struct _LMR_START_INSTANCE_REQUEST_25398 { UINT32 StructureSize; UINT32 IoTimeout; UINT32 IoRetryCount; UINT16 Flags; // LMR_INSTANCE_FLAG_* UINT16 AlternatePort; // Symbol UINT32 Reserved1; SMB2_INSTANCE_CONFIGURATION_25398 InstanceConfig; LMR_CONNECTION_PROPERTIES_25398 DefaultConnectionProperties; UINT8 InstanceId; UINT8 Reserved2; UINT16 DeviceNameLength; WCHAR DeviceName[ANYSIZE_ARRAY]; // Symbol } LMR_START_INSTANCE_REQUEST_25398, *PLMR_START_INSTANCE_REQUEST_25398; typedef struct _LMR_START_INSTANCE_REQUEST_26100 { UINT32 StructureSize; UINT32 IoTimeout; UINT32 IoRetryCount; UINT16 Flags; // LMR_INSTANCE_FLAG_* UINT16 AlternatePort; // Symbol UINT32 Reserved1; SMB2_INSTANCE_CONFIGURATION_25398 InstanceConfig; LMR_CONNECTION_PROPERTIES DefaultConnectionProperties; UINT8 InstanceId; UINT8 Reserved2; UINT16 DeviceNameLength; WCHAR DeviceName[ANYSIZE_ARRAY]; // Symbol } LMR_START_INSTANCE_REQUEST_26100, *PLMR_START_INSTANCE_REQUEST_26100; typedef struct _LMR_START_INSTANCE_REQUEST { UINT32 StructureSize; UINT32 IoTimeout; UINT32 IoRetryCount; UINT16 Flags; // LMR_INSTANCE_FLAG_* UINT16 AlternatePort; // Symbol UINT32 Reserved1; SMB2_INSTANCE_CONFIGURATION InstanceConfig; LMR_CONNECTION_PROPERTIES DefaultConnectionProperties; UINT8 InstanceId; UINT8 Reserved2; UINT16 DeviceNameLength; WCHAR DeviceName[ANYSIZE_ARRAY]; // Symbol } LMR_START_INSTANCE_REQUEST, *PLMR_START_INSTANCE_REQUEST; #ifndef FSCTL_LMR_START_INSTANCE #define FSCTL_LMR_START_INSTANCE CTL_CODE( \ FILE_DEVICE_NETWORK_FILE_SYSTEM, \ 232, \ METHOD_BUFFERED, \ FILE_ANY_ACCESS) #endif // !FSCTL_LMR_START_INSTANCE typedef enum _LMR_TRANSPORT_TYPE { SmbCeTransportTypeTdi = 0x0, SmbCeTransportTypeTcpIp = 0x1, SmbCeTransportTypeVmbus = 0x2, } LMR_TRANSPORT_TYPE, *PLMR_TRANSPORT_TYPE; typedef struct _LMR_BIND_UNBIND_TRANSPORT_REQUEST { UINT16 StructureSize; UINT16 Flags; LMR_TRANSPORT_TYPE Type; UINT TransportIdLength; WCHAR TransportId[ANYSIZE_ARRAY]; } LMR_BIND_UNBIND_TRANSPORT_REQUEST, *PLMR_BIND_UNBIND_TRANSPORT_REQUEST; #ifndef FSCTL_LMR_BIND_TO_TRANSPORT #define FSCTL_LMR_BIND_TO_TRANSPORT CTL_CODE( \ FILE_DEVICE_NETWORK_FILE_SYSTEM, \ 108, \ METHOD_BUFFERED, \ FILE_ANY_ACCESS) #endif // !FSCTL_LMR_BIND_TO_TRANSPORT template NTSTATUS LanmanRedirectorStartInstance( _In_ HANDLE Handle) { const WCHAR DeviceName[] = L"\\Device\\vmsmb"; const std::size_t DeviceNameLength = (sizeof(DeviceName) / sizeof(WCHAR)) - 1; const std::size_t RequestBufferSize = FIELD_OFFSET(RequestType, DeviceName) + sizeof(WCHAR) * DeviceNameLength; UINT8 RequestBuffer[RequestBufferSize] = {}; RequestType* Request = reinterpret_cast(RequestBuffer); Request->StructureSize = FIELD_OFFSET(LMR_START_INSTANCE_REQUEST, DeviceName); Request->IoTimeout = 30; Request->IoRetryCount = 3; Request->Flags = LMR_INSTANCE_FLAG_REGISTER_FILESYSTEM | LMR_INSTANCE_FLAG_USE_CUSTOM_TRANSPORTS | LMR_INSTANCE_FLAG_ALLOW_GUEST_AUTH | LMR_INSTANCE_FLAG_SUPPORTS_DIRECTMAPPED_IO; Request->Reserved1 = 0; Request->DefaultConnectionProperties.Flags1.Value = 0x1F; Request->DefaultConnectionProperties.SessionTimeoutInterval = 55; Request->DefaultConnectionProperties.CAHandleKeepaliveInterval = 10; Request->DefaultConnectionProperties.NonCAHandleKeepaliveInterval = 30; Request->DefaultConnectionProperties.ActiveIOKeepaliveInterval = 30; Request->InstanceId = 1; Request->DeviceNameLength = DeviceNameLength * sizeof(WCHAR); std::memcpy( Request->DeviceName, DeviceName, sizeof(DeviceName) - sizeof(WCHAR)); IO_STATUS_BLOCK IoStatusBlock = {}; return ::NtFsControlFile( Handle, nullptr, nullptr, nullptr, &IoStatusBlock, FSCTL_LMR_START_INSTANCE, RequestBuffer, RequestBufferSize, nullptr, 0); } int main() { NTSTATUS Status = STATUS_SUCCESS; HANDLE LanmanRedirectorHandle = INVALID_HANDLE_VALUE; { UNICODE_STRING LanmanRedirectorDevicePath = RTL_CONSTANT_STRING( L"\\Device\\LanmanRedirector"); OBJECT_ATTRIBUTES ObjectAttributes = {}; ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); ObjectAttributes.ObjectName = &LanmanRedirectorDevicePath; ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE; IO_STATUS_BLOCK IoStatusBlock = {}; Status = ::NtCreateFile( &LanmanRedirectorHandle, FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0, nullptr, 0); } if (NT_SUCCESS(Status)) { do { // 120: 26100.xxxx 28000 { using RequestType = LMR_START_INSTANCE_REQUEST; Status = ::LanmanRedirectorStartInstance( LanmanRedirectorHandle); if (NT_SUCCESS(Status) || STATUS_OBJECT_NAME_COLLISION == Status) { break; } std::printf( "%s with %s failed: 0x%08X, trying to fallback...\n", "LanmanRedirectorStartInstance", "LMR_START_INSTANCE_REQUEST", Status); } // 116: 26100 { using RequestType = LMR_START_INSTANCE_REQUEST_26100; Status = ::LanmanRedirectorStartInstance( LanmanRedirectorHandle); if (NT_SUCCESS(Status) || STATUS_OBJECT_NAME_COLLISION == Status) { break; } std::printf( "%s with %s failed: 0x%08X, trying to fallback...\n", "LanmanRedirectorStartInstance", "LMR_START_INSTANCE_REQUEST_26100", Status); } // 108: 25398 { using RequestType = LMR_START_INSTANCE_REQUEST_25398; Status = ::LanmanRedirectorStartInstance( LanmanRedirectorHandle); if (NT_SUCCESS(Status) || STATUS_OBJECT_NAME_COLLISION == Status) { break; } std::printf( "%s with %s failed: 0x%08X, trying to fallback...\n", "LanmanRedirectorStartInstance", "LMR_START_INSTANCE_REQUEST_25398", Status); } // 92: 20348 22000 22621 { using RequestType = LMR_START_INSTANCE_REQUEST_20348; Status = ::LanmanRedirectorStartInstance( LanmanRedirectorHandle); if (NT_SUCCESS(Status) || STATUS_OBJECT_NAME_COLLISION == Status) { break; } std::printf( "%s with %s failed: 0x%08X, trying to fallback...\n", "LanmanRedirectorStartInstance", "LMR_START_INSTANCE_REQUEST_20348", Status); } // 88: 17134 17763 18362 19041 { using RequestType = LMR_START_INSTANCE_REQUEST_17134; Status = ::LanmanRedirectorStartInstance( LanmanRedirectorHandle); if (NT_SUCCESS(Status) || STATUS_OBJECT_NAME_COLLISION == Status) { break; } std::printf( "%s with %s failed: 0x%08X, trying to fallback...\n", "LanmanRedirectorStartInstance", "LMR_START_INSTANCE_REQUEST_17134", Status); } // 44: 10586 14393 15063 16299 { using RequestType = LMR_START_INSTANCE_REQUEST_10586; Status = ::LanmanRedirectorStartInstance( LanmanRedirectorHandle); if (NT_SUCCESS(Status) || STATUS_OBJECT_NAME_COLLISION == Status) { break; } std::printf( "%s with %s failed: 0x%08X, trying to fallback...\n", "LanmanRedirectorStartInstance", "LMR_START_INSTANCE_REQUEST_10586", Status); } // 0: 10240 and earlier { Status = STATUS_NOT_SUPPORTED; } } while (false); if (NT_SUCCESS(Status)) { std::printf("SMB redirector instance started successfully.\n"); } else if (STATUS_OBJECT_NAME_COLLISION == Status) { std::printf( "SMB redirector instance already started.\n"); } else { std::printf( "LanmanRedirectorStartInstance failed with all known request " "structures, cannot start SMB redirector instance."); } ::CloseHandle(LanmanRedirectorHandle); } HANDLE VmSmbHandle = INVALID_HANDLE_VALUE; { UNICODE_STRING VmSmbDevicePath = RTL_CONSTANT_STRING( L"\\Device\\vmsmb"); OBJECT_ATTRIBUTES ObjectAttributes = {}; ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); ObjectAttributes.ObjectName = &VmSmbDevicePath; ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE; IO_STATUS_BLOCK IoStatusBlock = {}; Status = ::NtCreateFile( &VmSmbHandle, FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0, nullptr, 0); } if (NT_SUCCESS(Status)) { const WCHAR DeviceName[] = L"\\Device\\VMBus\\{4d12e519-17a0-4ae4-8eaa-5270fc6abdb7}-{dcc079ae-60ba-4d07-847c-3493609c0870}-0000"; const std::size_t DeviceNameLength = (sizeof(DeviceName) / sizeof(WCHAR)) - 1; const std::size_t RequestBufferSize = FIELD_OFFSET(LMR_BIND_UNBIND_TRANSPORT_REQUEST, TransportId) + sizeof(WCHAR) * DeviceNameLength; UINT8 RequestBuffer[RequestBufferSize] = {}; PLMR_BIND_UNBIND_TRANSPORT_REQUEST Request = reinterpret_cast(RequestBuffer); Request->StructureSize = sizeof(LMR_BIND_UNBIND_TRANSPORT_REQUEST); Request->Flags = 0; Request->Type = SmbCeTransportTypeVmbus; Request->TransportIdLength = DeviceNameLength * sizeof(WCHAR); std::memcpy( Request->TransportId, DeviceName, sizeof(DeviceName) - sizeof(WCHAR)); IO_STATUS_BLOCK IoStatusBlock = {}; Status = ::NtFsControlFile( VmSmbHandle, nullptr, nullptr, nullptr, &IoStatusBlock, FSCTL_LMR_BIND_TO_TRANSPORT, RequestBuffer, RequestBufferSize, nullptr, 0); if (NT_SUCCESS(Status)) { std::printf("VMBUS transport bound to SMB redirector instance.\n"); } else { std::printf( "Failed to bind VMBUS transport to SMB redirector instance: " "0x%08X\n", Status); } } { HANDLE Handle = INVALID_HANDLE_VALUE; { UNICODE_STRING VmSmbDevicePath = RTL_CONSTANT_STRING( L"\\Device\\vmsmb\\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\\NanaBox.HostDrivers"); OBJECT_ATTRIBUTES ObjectAttributes = {}; ObjectAttributes.Length = sizeof(OBJECT_ATTRIBUTES); ObjectAttributes.ObjectName = &VmSmbDevicePath; ObjectAttributes.Attributes = OBJ_CASE_INSENSITIVE; IO_STATUS_BLOCK IoStatusBlock = {}; Status = ::NtCreateFile( &Handle, FILE_LIST_DIRECTORY | FILE_TRAVERSE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0, nullptr, 0); } } std::getchar(); return 0; } ================================================ FILE: VirtualSmb/VirtualSmb.manifest ================================================  ================================================ FILE: VirtualSmb/VirtualSmb.vcxproj ================================================  {0A6FBD98-1210-4B36-A031-3E2D57E89DE3} VirtualSmb ConsoleApplication VirtualSmb.manifest true Project Mile Hyper-V Virtual SMB Guest Utility VirtualSmb © M2-Team and Contributors. All rights reserved. VirtualSmb.exe NanaRun 1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0 Preview 3 true false true MultiThreadedDebug MultiThreaded 1.2.410 1.0.3550 1.0.1171