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
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project
DefaultTargets="Restore;Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionPath>$(MSBuildThisFileDirectory)NanaRun.slnx</SolutionPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionPath)">
<AdditionalProperties>Configuration=Debug;Platform=x86</AdditionalProperties>
</ProjectReference>
<ProjectReference Include="$(SolutionPath)">
<AdditionalProperties>Configuration=Release;Platform=x86</AdditionalProperties>
</ProjectReference>
<ProjectReference Include="$(SolutionPath)">
<AdditionalProperties>Configuration=Debug;Platform=x64</AdditionalProperties>
</ProjectReference>
<ProjectReference Include="$(SolutionPath)">
<AdditionalProperties>Configuration=Release;Platform=x64</AdditionalProperties>
</ProjectReference>
<ProjectReference Include="$(SolutionPath)">
<AdditionalProperties>Configuration=Debug;Platform=ARM64</AdditionalProperties>
</ProjectReference>
<ProjectReference Include="$(SolutionPath)">
<AdditionalProperties>Configuration=Release;Platform=ARM64</AdditionalProperties>
</ProjectReference>
</ItemGroup>
<Target Name="Restore">
<MSBuild
Projects="@(ProjectReference)"
Targets="Restore"
StopOnFirstFailure="True"
Properties="PreferredToolArchitecture=x64" />
</Target>
<Target Name="Build">
<MSBuild
Projects="@(ProjectReference)"
Targets="Build"
BuildInParallel="True"
StopOnFirstFailure="True"
Properties="PreferredToolArchitecture=x64" />
</Target>
</Project>
================================================
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
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<MileProjectOutputPath>$(MSBuildThisFileDirectory)Output\</MileProjectOutputPath>
</PropertyGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Build.props" />
</Project>
================================================
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: `<Major>.<Minor> <Tag>`
- Example: `9.0 Preview 1`
- Binary Version: `<Major>.<Minor>.<Build>.<Revision>`
- 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 <Windows.h>
#include <WtsApi32.h>
#pragma comment(lib, "WtsApi32.lib")
#include <Userenv.h>
#pragma comment(lib, "Userenv.lib")
#include <cstdint>
#include <cwchar>
#include <map>
#include <string>
#include <vector>
#include <Mile.Project.Version.h>
#include "resource.h"
#include <Mile.Helpers.CppBase.h>
namespace
{
void SplitCommandLineEx(
std::wstring const& CommandLine,
std::vector<std::wstring> const& OptionPrefixes,
std::vector<std::wstring> const& OptionParameterSeparators,
std::wstring& ApplicationName,
std::map<std::wstring, std::wstring>& 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<wchar_t*>(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<DWORD>(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<DWORD>(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<DWORD>(String.size()),
&NumberOfCharsWritten,
nullptr))
{
std::string CurrentCodePageString = Mile::ToString(
::GetConsoleOutputCP(),
String);
::WriteFile(
ConsoleOutputHandle,
CurrentCodePageString.c_str(),
static_cast<DWORD>(CurrentCodePageString.size()),
&NumberOfCharsWritten,
nullptr);
}
}
std::map<std::string, std::wstring> 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<std::string, std::wstring> 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<PTOKEN_PRIVILEGES>(
::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<PVOID*>(&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<std::string, std::wstring> 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<const char*>(ResourceInfo.Pointer),
ResourceInfo.Size));
}
std::wstring ApplicationName;
std::map<std::wstring, std::wstring> OptionsAndParameters;
std::wstring UnresolvedCommandLine;
::SplitCommandLineEx(
std::wstring(::GetCommandLineW()),
std::vector<std::wstring>{ L"-", L"/", L"--" },
std::vector<std::wstring>{ 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<LPWSTR>(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
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
================================================
FILE: MinSudo/MinSudo.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{99FA5072-6C05-4027-8C8B-EA3EC9BCF9E0}</ProjectGuid>
<RootNamespace>MinSudo</RootNamespace>
<MileProjectType>ConsoleApplication</MileProjectType>
<MileProjectManifestFile>MinSudo.manifest</MileProjectManifestFile>
<MileProjectEnableVCLTLSupport>true</MileProjectEnableVCLTLSupport>
<MileProjectUseProjectProperties>true</MileProjectUseProjectProperties>
<MileProjectCompanyName>M2-Team</MileProjectCompanyName>
<MileProjectFileDescription>MinSudo - Lightweight POSIX-style Sudo implementation for Windows</MileProjectFileDescription>
<MileProjectInternalName>MinSudo</MileProjectInternalName>
<MileProjectLegalCopyright>© M2-Team and Contributors. All rights reserved.</MileProjectLegalCopyright>
<MileProjectOriginalFilename>MinSudo.exe</MileProjectOriginalFilename>
<MileProjectProductName>NanaRun</MileProjectProductName>
<MileProjectVersion>1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0</MileProjectVersion>
<MileProjectVersionTag>Preview 3</MileProjectVersionTag>
<MileWindowsHelpersNoCppWinRTHelpers>true</MileWindowsHelpersNoCppWinRTHelpers>
</PropertyGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x86.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.ARM64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.Default.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.props" />
<Import Project="..\NanaRun.IconResource\NanaRun.IconResource.props" />
<ItemDefinitionGroup>
<ClCompile>
<EnableEnhancedInstructionSet Condition="'$(Platform)'=='Win32'">NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="MinSudo.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="MinSudo.manifest" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\en\Translations.md" />
<None Include="Resources\zh-Hans\Translations.md" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="MinSudo.rc" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mile.Windows.Helpers">
<Version>1.0.1171</Version>
</PackageReference>
</ItemGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.targets" />
</Project>
================================================
FILE: MinSudo/MinSudo.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="MinSudo.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="MinSudo.manifest" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Resources">
<UniqueIdentifier>{7b09b2fc-15c9-40d3-8c68-b33226a0b2cd}</UniqueIdentifier>
</Filter>
<Filter Include="Resources\en">
<UniqueIdentifier>{0e9f5c86-045b-493f-b15e-750b369fc678}</UniqueIdentifier>
</Filter>
<Filter Include="Resources\zh-Hans">
<UniqueIdentifier>{71ae93ee-4aa8-44b8-bc84-e680a9768fc5}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="Resources\en\Translations.md">
<Filter>Resources\en</Filter>
</None>
<None Include="Resources\zh-Hans\Translations.md">
<Filter>Resources\zh-Hans</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="MinSudo.rc" />
</ItemGroup>
</Project>
================================================
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 <cstdint>
#include <cwchar>
#include <string>
#include <vector>
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<std::string> ExcludedPrivileges;
std::vector<std::string> IncludedPrivileges;
MandatoryLabelType IntegrityLevel = MandatoryLabelType::Default;
bool InheritEnvironmentVariables = true;
std::vector<std::pair<std::string, std::string>> 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
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
================================================
FILE: NanaRun/NanaRun.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{3D1A07C8-17B2-4E5F-AAC1-16BE522BD4D7}</ProjectGuid>
<RootNamespace>NanaRun</RootNamespace>
<MileProjectType>ConsoleApplication</MileProjectType>
<MileProjectManifestFile>NanaRun.manifest</MileProjectManifestFile>
<MileProjectEnableVCLTLSupport>true</MileProjectEnableVCLTLSupport>
<MileProjectUseProjectProperties>true</MileProjectUseProjectProperties>
<MileProjectCompanyName>M2-Team</MileProjectCompanyName>
<MileProjectFileDescription>NanaRun (Console)</MileProjectFileDescription>
<MileProjectInternalName>NanaRun</MileProjectInternalName>
<MileProjectLegalCopyright>© M2-Team and Contributors. All rights reserved.</MileProjectLegalCopyright>
<MileProjectOriginalFilename>NanaRun.exe</MileProjectOriginalFilename>
<MileProjectProductName>NanaRun</MileProjectProductName>
<MileProjectVersion>1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0</MileProjectVersion>
<MileProjectVersionTag>Preview 3</MileProjectVersionTag>
<MileWindowsHelpersNoCppWinRTHelpers>true</MileWindowsHelpersNoCppWinRTHelpers>
</PropertyGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x86.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.ARM64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.Default.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.props" />
<Import Project="..\NanaRun.IconResource\NanaRun.IconResource.props" />
<ItemDefinitionGroup>
<ClCompile>
<EnableEnhancedInstructionSet Condition="'$(Platform)'=='Win32'">NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="NanaRun.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="NanaRun.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mile.Windows.Helpers">
<Version>1.0.1171</Version>
</PackageReference>
<PackageReference Include="Mile.Windows.Internal">
<Version>1.0.3550</Version>
</PackageReference>
</ItemGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.targets" />
</Project>
================================================
FILE: NanaRun/NanaRun.vcxproj.filters
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="NanaRun.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="NanaRun.manifest" />
</ItemGroup>
</Project>
================================================
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
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
PROJECT: NanaRun
FILE: NanaRun.IconResource.props
PURPOSE: Windows icon resource for NanaRun
LICENSE: The MIT License
MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com)
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IncludePath>$(MSBuildThisFileDirectory);$(IncludePath)</IncludePath>
</PropertyGroup>
<Target Name="NanaRunBuildIconResource" BeforeTargets="BeforeResourceCompile">
<ItemGroup>
<ResourceCompile Include="$(MSBuildThisFileDirectory)NanaRun.IconResource.rc" />
</ItemGroup>
</Target>
</Project>
================================================
FILE: NanaRun.slnx
================================================
<Solution>
<Configurations>
<Platform Name="ARM64" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="MinSudo/MinSudo.vcxproj" Id="99fa5072-6c05-4027-8c8b-ea3ec9bcf9e0" />
<Project Path="NanaRun/NanaRun.vcxproj" Id="3d1a07c8-17b2-4e5f-aac1-16be522bd4d7" />
<Project Path="SynthRdp/SynthRdp.vcxproj" Id="abfd05c2-4673-49ec-a23e-9ca53223eec2" />
<Project Path="VirtualSmb/VirtualSmb.vcxproj" Id="0a6fbd98-1210-4b36-a031-3e2d57e89de3" />
</Solution>
================================================
FILE: ReadMe.md
================================================
#  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] <Option1> <Option2> ...
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] <Value> - Set the specific configuration
with the specific value, or reset
the specific configuration if you
don't specify the value.
Configuration Keys:
DisableRemoteDesktop <True|False>
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 <True|False>
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 <True|False>
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 <True|False>
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 <Host>
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 <Port>
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 <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
#include <Mile.Helpers.CppBase.h>
#include <Mile.HyperV.VMBus.h>
#include <Mile.HyperV.Windows.VMBusPipe.h>
#include <Mile.Project.Version.h>
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<DWORD>(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<wchar_t*>(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<int>(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<SynthRdpServiceConnectionContext*>(
::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<DWORD>(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<DWORD>(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<DWORD>(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<DWORD>(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<DWORD>(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<wchar_t*>(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<std::uint16_t>(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<DWORD>((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<std::uint16_t>(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<std::string> 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<wchar_t*>(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] <Option1> <Option2> ...\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] <Value> - 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 <True|False>\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 <True|False>\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 <True|False>\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 <True|False>\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 <Host>\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 <Port>\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
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
================================================
FILE: SynthRdp/SynthRdp.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{ABFD05C2-4673-49EC-A23E-9CA53223EEC2}</ProjectGuid>
<RootNamespace>SynthRdp</RootNamespace>
<MileProjectType>ConsoleApplication</MileProjectType>
<MileProjectManifestFile>SynthRdp.manifest</MileProjectManifestFile>
<MileProjectEnableVCLTLSupport>true</MileProjectEnableVCLTLSupport>
<MileProjectUseProjectProperties>true</MileProjectUseProjectProperties>
<MileProjectCompanyName>M2-Team</MileProjectCompanyName>
<MileProjectFileDescription>Hyper-V Enhanced Session Proxy Service</MileProjectFileDescription>
<MileProjectInternalName>SynthRdp</MileProjectInternalName>
<MileProjectLegalCopyright>© M2-Team and Contributors. All rights reserved.</MileProjectLegalCopyright>
<MileProjectOriginalFilename>SynthRdp.exe</MileProjectOriginalFilename>
<MileProjectProductName>NanaRun</MileProjectProductName>
<MileProjectVersion>1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0</MileProjectVersion>
<MileProjectVersionTag>Preview 3</MileProjectVersionTag>
<MileHyperVEnableWindowsPlatformSupport>true</MileHyperVEnableWindowsPlatformSupport>
<WindowsTargetPlatformMinVersion>5.1.2600.0</WindowsTargetPlatformMinVersion>
<MileWindowsHelpersNoCppWinRTHelpers>true</MileWindowsHelpersNoCppWinRTHelpers>
</PropertyGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x86.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.ARM64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.Default.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.props" />
<Import Project="..\NanaRun.IconResource\NanaRun.IconResource.props" />
<ItemDefinitionGroup>
<ClCompile>
<EnableEnhancedInstructionSet Condition="'$(Platform)'=='Win32'">NoExtensions</EnableEnhancedInstructionSet>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="SynthRdp.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="SynthRdp.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mile.Windows.Helpers">
<Version>1.0.1171</Version>
</PackageReference>
<PackageReference Include="Mile.HyperV">
<Version>1.2.907</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<None Include="AutoRun.inf" />
<None Include="SynthRdp.iss" />
</ItemGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.targets" />
</Project>
================================================
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 <Mile.Internal.h>
#include <cstdio>
#include <cstring>
// 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 <typename RequestType>
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<RequestType*>(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<RequestType>(
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<RequestType>(
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<RequestType>(
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<RequestType>(
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<RequestType>(
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<RequestType>(
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<PLMR_BIND_UNBIND_TRANSPORT_REQUEST>(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
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
</assembly>
================================================
FILE: VirtualSmb/VirtualSmb.vcxproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{0A6FBD98-1210-4B36-A031-3E2D57E89DE3}</ProjectGuid>
<RootNamespace>VirtualSmb</RootNamespace>
<MileProjectType>ConsoleApplication</MileProjectType>
<MileProjectManifestFile>VirtualSmb.manifest</MileProjectManifestFile>
<MileProjectUseProjectProperties>true</MileProjectUseProjectProperties>
<MileProjectCompanyName>Project Mile</MileProjectCompanyName>
<MileProjectFileDescription>Hyper-V Virtual SMB Guest Utility</MileProjectFileDescription>
<MileProjectInternalName>VirtualSmb</MileProjectInternalName>
<MileProjectLegalCopyright>© M2-Team and Contributors. All rights reserved.</MileProjectLegalCopyright>
<MileProjectOriginalFilename>VirtualSmb.exe</MileProjectOriginalFilename>
<MileProjectProductName>NanaRun</MileProjectProductName>
<MileProjectVersion>1.0.$([System.DateTime]::Today.Subtract($([System.DateTime]::Parse('2024-05-01'))).TotalDays).0</MileProjectVersion>
<MileProjectVersionTag>Preview 3</MileProjectVersionTag>
<MileUniCrtDisableRuntimeDebuggingFeature>true</MileUniCrtDisableRuntimeDebuggingFeature>
<MileUniCrtEnableVcRuntimeWrapper>false</MileUniCrtEnableVcRuntimeWrapper>
<MileWindowsHelpersNoCppWinRTHelpers>true</MileWindowsHelpersNoCppWinRTHelpers>
</PropertyGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x86.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.x64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Platform.ARM64.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.Default.props" />
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.props" />
<Import Project="..\NanaRun.IconResource\NanaRun.IconResource.props" />
<ItemDefinitionGroup>
<ClCompile>
<RuntimeLibrary Condition="'$(Configuration)' == 'Debug'">MultiThreadedDebug</RuntimeLibrary>
<RuntimeLibrary Condition="'$(Configuration)' == 'Release'">MultiThreaded</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="VirtualSmb.cpp" />
</ItemGroup>
<ItemGroup>
<Manifest Include="VirtualSmb.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Mile.Windows.UniCrt">
<Version>1.2.410</Version>
</PackageReference>
<PackageReference Include="Mile.Windows.Internal">
<Version>1.0.3550</Version>
</PackageReference>
<PackageReference Include="Mile.Windows.Helpers">
<Version>1.0.1171</Version>
</PackageReference>
</ItemGroup>
<Import Sdk="Mile.Project.Configurations" Version="1.0.1917" Project="Mile.Project.Cpp.targets" />
</Project>
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
SYMBOL INDEX (53 symbols across 4 files) FILE: MinSudo/MinSudo.cpp function SplitCommandLineEx (line 34) | void SplitCommandLineEx( function GetCurrentProcessModulePath (line 139) | std::wstring GetCurrentProcessModulePath() function GetWorkingDirectory (line 148) | std::wstring GetWorkingDirectory() function WriteToConsole (line 157) | void WriteToConsole( function ParseStringDictionary (line 183) | std::map<std::string, std::wstring> ParseStringDictionary( function DWORD (line 242) | DWORD GetActiveSessionID() function BOOL (line 268) | BOOL CreateSystemToken( function BOOL (line 372) | BOOL OpenProcessTokenByProcessId( function BOOL (line 396) | BOOL OpenServiceProcessToken( function BOOL (line 417) | BOOL AdjustTokenPrivilegesSimple( function BOOL (line 464) | BOOL GetTokenInformationWithMemory( function BOOL (line 512) | BOOL AdjustTokenAllPrivileges( type TargetProcessTokenLevel (line 540) | enum class TargetProcessTokenLevel : std::uint32_t function BOOL (line 547) | BOOL SimpleCreateProcess( function main (line 804) | int main() FILE: NanaRun/NanaRun.cpp type AccessTokenSourceType (line 16) | enum class AccessTokenSourceType : std::int32_t type MandatoryLabelType (line 27) | enum class MandatoryLabelType : std::int32_t type ProcessPriorityType (line 39) | enum class ProcessPriorityType : std::int32_t type ShowWindowModeType (line 50) | enum class ShowWindowModeType : std::int32_t type EnvironmentConfiguration (line 59) | struct EnvironmentConfiguration function main (line 93) | int main() FILE: SynthRdp/SynthRdp.cpp function EXTERN_C (line 25) | EXTERN_C HANDLE WINAPI VmbusPipeClientTryOpenChannel( function SOCKET (line 53) | SOCKET SynthRdpConnectToServer() type SynthRdpServiceConnectionContext (line 153) | struct SynthRdpServiceConnectionContext function SynthRdpRedirectionWorker (line 159) | void SynthRdpRedirectionWorker( function DWORD (line 378) | DWORD SynthRdpMain() function GetCurrentProcessModulePath (line 528) | std::wstring GetCurrentProcessModulePath() function SynthRdpServiceHandler (line 544) | void WINAPI SynthRdpServiceHandler( function SynthRdpServiceMain (line 570) | void WINAPI SynthRdpServiceMain( function SynthRdpInstallService (line 600) | int SynthRdpInstallService() function SynthRdpUninstallService (line 665) | int SynthRdpUninstallService() function SynthRdpStartService (line 720) | int SynthRdpStartService() function SynthRdpStopService (line 742) | int SynthRdpStopService() function SynthRdpListConfigurations (line 764) | int SynthRdpListConfigurations() function SynthRdpUpdateConfiguration (line 904) | int SynthRdpUpdateConfiguration( function main (line 1088) | int main() FILE: VirtualSmb/VirtualSmb.cpp type _SMB2_INSTANCE_CONFIGURATION_17134 (line 20) | struct _SMB2_INSTANCE_CONFIGURATION_17134 type _SMB2_INSTANCE_CONFIGURATION_20348 (line 37) | struct _SMB2_INSTANCE_CONFIGURATION_20348 type _SMB2_INSTANCE_CONFIGURATION_25398 (line 55) | struct _SMB2_INSTANCE_CONFIGURATION_25398 type _SMB2_INSTANCE_CONFIGURATION (line 76) | struct _SMB2_INSTANCE_CONFIGURATION type _LMR_CONNECTION_PROPERTIES_10586 (line 97) | struct _LMR_CONNECTION_PROPERTIES_10586 type _LMR_CONNECTION_PROPERTIES_25398 (line 130) | struct _LMR_CONNECTION_PROPERTIES_25398 type _LMR_CONNECTION_PROPERTIES (line 166) | struct _LMR_CONNECTION_PROPERTIES type _LMR_START_INSTANCE_REQUEST_10586 (line 210) | struct _LMR_START_INSTANCE_REQUEST_10586 type _LMR_START_INSTANCE_REQUEST_17134 (line 225) | struct _LMR_START_INSTANCE_REQUEST_17134 type _LMR_START_INSTANCE_REQUEST_20348 (line 241) | struct _LMR_START_INSTANCE_REQUEST_20348 type _LMR_START_INSTANCE_REQUEST_25398 (line 257) | struct _LMR_START_INSTANCE_REQUEST_25398 type _LMR_START_INSTANCE_REQUEST_26100 (line 273) | struct _LMR_START_INSTANCE_REQUEST_26100 type _LMR_START_INSTANCE_REQUEST (line 289) | struct _LMR_START_INSTANCE_REQUEST type _LMR_TRANSPORT_TYPE (line 313) | enum _LMR_TRANSPORT_TYPE type _LMR_BIND_UNBIND_TRANSPORT_REQUEST (line 320) | struct _LMR_BIND_UNBIND_TRANSPORT_REQUEST function NTSTATUS (line 338) | NTSTATUS LanmanRedirectorStartInstance( function main (line 386) | int main()
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (187K chars).
[
{
"path": ".editorconfig",
"chars": 1107,
"preview": "## \n## PROJECT: Mouri Internal Library Essentials\n## FILE: .editorconfig\n## PURPOSE: The root .editorconfig"
},
{
"path": ".github/FUNDING.yml",
"chars": 251,
"preview": "patreon: MouriNaruto\ncustom: [\n \"https://paypal.me/MouriNaruto\",\n \"https://afdian.net/a/MouriNaruto\",\n \"https://githu"
},
{
"path": ".github/workflows/BuildBinaries.yml",
"chars": 850,
"preview": "name: Build Binaries\n\non:\n push:\n paths-ignore:\n - '.github/*'\n - '*.md'\n pull_request:\n paths-ignore"
},
{
"path": ".gitignore",
"chars": 7158,
"preview": "## \n## PROJECT: Mouri Internal Library Essentials\n## FILE: .gitignore\n## PURPOSE: The root .gitignore file f"
},
{
"path": "BuildAllTargets.cmd",
"chars": 791,
"preview": "@setlocal\n@echo off\n\nrem Change to the current folder.\ncd \"%~dp0\"\n\nrem Remove the output folder for a fresh compile.\nrd "
},
{
"path": "BuildAllTargets.proj",
"chars": 1682,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project \n DefaultTargets=\"Restore;Build\"\n xmlns=\"http://schemas.microsoft.com"
},
{
"path": "BuildInstallers.cmd",
"chars": 496,
"preview": "@setlocal\n@echo off\n\nrem Change to the current folder.\ncd \"%~dp0\"\n\nrem Remove the SynthRdp installation image output fol"
},
{
"path": "Directory.Build.props",
"chars": 284,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project>\n <PropertyGroup>\n <MileProjectOutputPath>$(MSBuildThisFileDirector"
},
{
"path": "Documents/People.md",
"chars": 388,
"preview": "# Relevant People\n\n## Notice\n\n- This list sort in alphabetical order.\n\n## Development Team\n\n- Kenji Mouri ([https://git"
},
{
"path": "Documents/ReleaseNotes.md",
"chars": 118,
"preview": "# NanaRun Release Notes\n\nFor preview versions, please read \n[NanaRun Preview Release Notes](ReleaseNotesPreview.md).\n"
},
{
"path": "Documents/ReleaseNotesPreview.md",
"chars": 893,
"preview": "# NanaRun Preview Release Notes\n\nFor stable versions, please read [NanaRun Release Notes](ReleaseNotes.md).\n\n**NanaRun "
},
{
"path": "Documents/Versioning.md",
"chars": 766,
"preview": "# NanaRun Versioning\n\nThis document applies to all versions of NanaRun.\n\n## Version Format\n\n- Simple Version: `<Major>."
},
{
"path": "License.md",
"chars": 21710,
"preview": "# NanaRun License\n\nFor giving the maximum respect for the upstream projects and following the \nphilosophy about open-so"
},
{
"path": "MinSudo/MinSudo.cpp",
"chars": 31254,
"preview": "/*\n * PROJECT: NanaRun\n * FILE: MinSudo.cpp\n * PURPOSE: Implementation for MinSudo\n *\n * LICENSE: The MI"
},
{
"path": "MinSudo/MinSudo.manifest",
"chars": 784,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-co"
},
{
"path": "MinSudo/MinSudo.vcxproj",
"chars": 2834,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msb"
},
{
"path": "MinSudo/MinSudo.vcxproj.filters",
"chars": 1085,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuil"
},
{
"path": "MinSudo/Resources/en/Translations.md",
"chars": 1612,
"preview": "```\n* PROJECT: NanaRun\n* FILE: Translations.md\n* PURPOSE: The English translation for MinSudo\n*\n* LICENSE: "
},
{
"path": "MinSudo/Resources/zh-Hans/Translations.md",
"chars": 1129,
"preview": "```\n* PROJECT: NanaRun\n* FILE: Translations.md\n* PURPOSE: The Chinese (Simplified) translation for MinSudo\n"
},
{
"path": "MinSudo/resource.h",
"chars": 416,
"preview": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ 生成的包含文件。\n// 供 MinSudo.rc 使用\n//\n#define IDR_TRANSLATIONS 10"
},
{
"path": "NanaRun/NanaRun.cpp",
"chars": 1995,
"preview": "/*\n * PROJECT: NanaRun\n * FILE: NanaRun.cpp\n * PURPOSE: Implementation for NanaRun\n *\n * LICENSE: The MI"
},
{
"path": "NanaRun/NanaRun.manifest",
"chars": 784,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-co"
},
{
"path": "NanaRun/NanaRun.vcxproj",
"chars": 2619,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msb"
},
{
"path": "NanaRun/NanaRun.vcxproj.filters",
"chars": 281,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuil"
},
{
"path": "NanaRun.IconResource/NanaRun.IconResource.h",
"chars": 255,
"preview": "/*\n * PROJECT: NanaRun\n * FILE: NanaRun.IconResource.h\n * PURPOSE: Windows icon resource for NanaRun\n *\n * "
},
{
"path": "NanaRun.IconResource/NanaRun.IconResource.props",
"chars": 667,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n PROJECT: NanaRun\n FILE: NanaRun.IconResource.props\n PURPOSE: "
},
{
"path": "NanaRun.slnx",
"chars": 504,
"preview": "<Solution>\n <Configurations>\n <Platform Name=\"ARM64\" />\n <Platform Name=\"x64\" />\n <Platform Name=\"x86\" />\n </"
},
{
"path": "ReadMe.md",
"chars": 6136,
"preview": "#  NanaRun\n\nApplication runtime environment customization utility\n\n## Development Status "
},
{
"path": "SynthRdp/AutoRun.inf",
"chars": 119,
"preview": "[autorun]\nopen=SynthRdpInstaller.exe\nicon=SynthRdpInstaller.exe\nlabel=Hyper-V Enhanced Session Proxy Service (SynthRdp)"
},
{
"path": "SynthRdp/SynthRdp.cpp",
"chars": 36745,
"preview": "/*\n * PROJECT: NanaRun\n * FILE: SynthRdp.cpp\n * PURPOSE: Implementation for Hyper-V Enhanced Session Proxy "
},
{
"path": "SynthRdp/SynthRdp.iss",
"chars": 3176,
"preview": "; -- 64BitThreeArch.iss --\n; Demonstrates how to install a program built for three different\n; architectures (x86, x64, "
},
{
"path": "SynthRdp/SynthRdp.manifest",
"chars": 572,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-co"
},
{
"path": "SynthRdp/SynthRdp.vcxproj",
"chars": 2908,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/ms"
},
{
"path": "Tools/Default.isl",
"chars": 20279,
"preview": "; *** Inno Setup version 6.1.0+ English messages ***\n;\n; To download user-contributed translations of this file, go to:"
},
{
"path": "VirtualSmb/VirtualSmb.cpp",
"chars": 21050,
"preview": "/*\n * PROJECT: SynthRdp\n * FILE: VirtualSmb.cpp\n * PURPOSE: Implementation for Hyper-V Virtual SMB Guest Ut"
},
{
"path": "VirtualSmb/VirtualSmb.manifest",
"chars": 324,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-co"
},
{
"path": "VirtualSmb/VirtualSmb.vcxproj",
"chars": 2952,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/ms"
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the M2Team/NanaRun GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (172.8 KB), approximately 42.4k tokens, and a symbol index with 53 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.