-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement basic profile matching #11390
Conversation
wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, TRUE, static_cast<DWORD>(connectMessage->Descriptor.Process)) }; | ||
wil::unique_handle clientProcess{ OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, TRUE, static_cast<DWORD>(connectMessage->Descriptor.Process)) }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems unrelated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it? 😉
You need the VM_READ permission to read memory from the process... which is how we get the command line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this brings fear - do we always have permission to do that? This is conhost code so I don't want conhost to start magically failing to open handles.......
...... nevermind this is defterm code so if we fail to open the client with these permissions then we'll just fail the delegation entirely. That's fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...... nevermind this is defterm code
That's what got me hahaha
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't get PROCESS_VM_READ with a non-protected process at the same IL as us... someone's really changed how the process model on Windows works and this likely won't be the only problem we experience.
* Added unit tests * Fixed inverted ConnectionType comparison * Standardized PROCESS_BASIC_INFORMATION throughout the project * Use wil resource classes for safety * Give canonical() a namespace for clarity * Added exception handling for command line matching as command line normalization may throw * Finally introduced PCG into the project For now it's only used in a unit test though.
@miniksa If you can, it'd be awesome if you review the latest commit tomorrow, titled "An assortment of changes". It addresses a view points you made. |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm at 17/20 but the kid just woke up so figured I'd get you some feedback before I disappear this morning
src/inc/til/rand.h
Outdated
static const auto RtlGenRandom = []() { | ||
// The documentation states to use advapi32.dll, but technically | ||
// SystemFunction036 lives in cryptbase.dll since Windows 7. | ||
const auto cryptbase = LoadLibraryExW(L"cryptbase.dll", nullptr, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As always, there's wil helpers for this kind of thing:
wil::unique_hmodule hDComp{ LoadLibraryEx(L"Dcomp.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) };
RETURN_LAST_ERROR_IF(hDComp.get() == nullptr);
auto fn = GetProcAddressByFunctionDeclaration(hDComp.get(), DCompositionCreateSurfaceHandle);
RETURN_LAST_ERROR_IF(fn == nullptr);
return fn(GENERIC_ALL, nullptr, &_swapChainHandle);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wil::unique_hmodule
will unload the library when it exits the scope. Can I really do that while I still hold the proc address for later use? (I return the proc address and assign them to the static
variable.)
Furthermore I didn't use GetProcAddressByFunctionDeclaration
because there's no function declaration available for RtlGenRandom
. In order to use GetProcAddressByFunctionDeclaration
then I'd have to separately declare the type first. Would you prefer that anyways?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No you cannot let go of it if you're going to use it again later. You have leaked the count though by not holding the module also as a static. Probably not a huge deal, but it is a thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah DLL unloading should continue working shouldn’t it?
_inPipe{ hIn }, | ||
_outPipe{ hOut } | ||
{ | ||
THROW_IF_FAILED(ConptyPackPseudoConsole(hServerProcess, hRef, hSig, &_hPC)); | ||
if (_guid == guid{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose this means we won't be assigning defterms guids anymore, but they already wouldn't be able to have WT_SESSION in their env block so that doesn't really matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I shouldn't have committed that... I don't want to change unrelated code like that.
I'll revert this change. Although I'll drop the if condition, as the _guid
can't possibly be anything but guid{}
.
// Requires PROCESS_BASIC_INFORMATION | PROCESS_VM_READ privileges. | ||
winrt::hstring ConptyConnection::_commandlineFromProcess(HANDLE process) | ||
{ | ||
struct PROCESS_BASIC_INFORMATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't love documenting undocumented bits of the OS. We should try file deliverables internally to document these (since I can't imagine them changing)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened an internal PR.
|
||
// Yeah I know... Don't use "impl" stuff... But why do you make something _that_ useful private? :( | ||
// The hstring_builder allows us to create a hstring without intermediate copies. Neat! | ||
winrt::impl::hstring_builder commandline{ params.CommandLine.Length / 2u }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similarly, we should maybe file a issue on cppwinrt to move at least the builder out of impl
, because it does have value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened an issue: microsoft/cppwinrt#1035
// This test ensures CommandLineToArgvW doesn't change just to be sure. | ||
void TerminalSettingsTests::CommandLineToArgvW() | ||
{ | ||
pcg_engines::oneseq_dxsm_64_32 rng{ til::gen_random<uint64_t>() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh, so we're bringing in that whole random header just for this one test. This might make more sense as a fuzzed test, but ain't nobody got time for that in 1.12
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had written a fuzzer for til::rle
in the past that makes use of this RNG. With the header in place I can finally import that project. 🙂 It's also only a very minimalistic 45 LOC version of the library that I wrote. I wanted to just inline that into the til/rand.h
header, but this wouldn't have allowed me to properly credit the author with a cgmanifest.
src/inc/til/rand.h
Outdated
static const auto RtlGenRandom = []() { | ||
// The documentation states to use advapi32.dll, but technically | ||
// SystemFunction036 lives in cryptbase.dll since Windows 7. | ||
const auto cryptbase = LoadLibraryExW(L"cryptbase.dll", nullptr, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No you cannot let go of it if you're going to use it again later. You have leaked the count though by not holding the module also as a static. Probably not a huge deal, but it is a thing.
src/inc/til/rand.h
Outdated
|
||
namespace til | ||
{ | ||
void gen_random(void* data, uint32_t length) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did I miss where you actually use gen_random
and not use this just to import pcg_random.hpp
on your behalf?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is used in one of the tests to initialize the PCG RNG.
Hello @lhecker! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
big comments!
{ | ||
RtlGenRandomLoader() noexcept : | ||
// The documentation states to use advapi32.dll, but technically | ||
// SystemFunction036 lives in cryptbase.dll since Windows 7. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ideally we would have used the apiset 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Going back and forth between these two threads -- SystemFunction036 is hosted in api-ms-win-security-systemfunctions-l1-1-0
... but that apiset is only documented as containing SystemFunction036 after contract version win8
. I bet they could back-version that and be fine, but that's not likely to happen.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the apiset doesn't exist on win8, why shouldn't we have used advapi32.dll
itself? The loader will take care of finding the secret place where it actually lives.
Also, why did we have to delay load this? I have so many questions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to open a PR documenting the reasons behind this better.
// The documentation states to use advapi32.dll, but technically | ||
// SystemFunction036 lives in cryptbase.dll since Windows 7. | ||
module{ LoadLibraryExW(L"cryptbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) }, | ||
proc{ reinterpret_cast<decltype(proc)>(GetProcAddress(module.get(), "SystemFunction036")) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What on earth? RtlGenRandom is fully documented, but we're.. looking it up with a weird name in a private DLL? Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. It's a macro.
It does have a prototype in NTSecAPI.h, under this name. Could we really not have used it with the wil helper? It would resolve to SystemFunction036
, sure, but at least we wouldn't leak that name into our own code.
If not, could we not have stringified the content of the RtlGenRandom macro instead?
@@ -31,9 +31,7 @@ | |||
return Status; | |||
} | |||
|
|||
// This is the actual field name, but in the public SDK, it's named Reserved3. We need to pursue publishing the real name. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please file an internal bug on documenting this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel weird that we just threw our hands up and dumped a bunch of Reserved fields into the public eye 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation is now public: https://docs.microsoft.com/en-us/windows/win32/procthread/zwqueryinformationprocess#PROCESS_BASIC_INFORMATION
I'm going to copy the documentation over to the Nt variant soon.
@@ -265,14 +265,12 @@ static bool getWslNames(const wil::unique_hkey& wslRootKey, | |||
|
|||
std::wstring buffer; | |||
auto result = wil::AdaptFixedSizeToAllocatedResult<std::wstring, 256>(buffer, [&](PWSTR value, size_t valueLength, size_t* valueLengthNeededWithNull) -> HRESULT { | |||
auto length = static_cast<DWORD>(valueLength); | |||
auto length = gsl::narrow<DWORD>(valueLength * sizeof(wchar_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unrelated? but generally fine.
🎉 Handy links: |
This implements command line matching for
CascadiaSettings::GetProfileForArgs
.The command lines for all user profiles are resolved to absolute file paths,
argument quotes are standardized ("canonicalized") and the results are cached.
When
GetProfileForArgs
is called with a Commandline() value, we "canonicalize"the argument as well and find the profile that is the longest prefix.
If none could be found the default profile is returned.
PR Checklist
Validation Steps Performed
cmd.exe
tab in the store-version of WTstart cmd
--> A tab with the
cmd.exe
profile opensstart pwsh.exe
--> A tab with the PowerShell 7 profile opens
--> A tab with the PowerShell 7 profile opens
pwsh.exe
from there--> A tab with the PowerShell 7 profile opens