Skip to content
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

Proposal: Loosen Import Redirection (1809+) restrictions for Desktop Application use #126

Open
riverar opened this issue Jul 23, 2020 · 11 comments
Assignees
Labels
area-Win32 Support for Win32 packaged and non-packaged apps feature

Comments

@riverar
Copy link
Contributor

riverar commented Jul 23, 2020

Proposal: Loosen Import Redirection (1809+) restrictions for Desktop Application use

Summary

Import Redirection, a feature introduced in Windows 10 version 1809, enables packaged application developers to "redirect" API imports to other implementations (whether they be private or elsewhere in the OS) without modifications to the application, its dependencies, or any related source code.

Use of this feature is currently restricted to packaged applications. This proposal seeks to loosen this restriction and allow this feature to be used with desktop applications.

Rationale

Allowing unpackaged desktop applications to utilize Import Redirection has several benefits, for example:

  • Puts the responsibility of loading redirection DLLs and the act of redirecting APIs onto the Windows Loader, eliminating the need for injectors and/or loaders.

  • Provides a simple (and documented) model for redirecting APIs, bringing a level of order to chaos.

    Real world example: Microsoft Edge / Chromium use runtime import table patching to redirect APIs at runtime, to, for example, handle the lack of gdi32, user32, and other modules not present on Windows 10X. Presumably, all this code could be replaced with a Import Redirection DLL.

  • Shortens the hook development/test loop considerably as all the hooking logic is automatically handled by Windows.

  • Aligns the features more closely with LD_PRELOAD, a popular feature used on other platforms.

Additional rationale was pared out per request but can be found in this item's edit history.

Scope

Capability Priority
Desktop application can be configured to use an Import Redirection DLL without modification to its files or dependencies on disk Must
Desktop application Import Redirection can redirect all APIs without restriction Must
Desktop application Import Redirection works without administrative privileges Should
Desktop application Import Redirection mark processes as redirected for easy identification Should
Desktop application Import Redirection avoids using the Windows Registry as a mechanism to configure this behavior Should
Desktop application Import Redirection is tied to a Limited Access Feature Could
Desktop application Import Redirection redirects APIs with module-level granularity Could
Desktop application Import Redirection DLLs, marked as optional, are skipped if failure occurs Could
Desktop application Import Redirection supports multiple DLLs and applies redirects in configured order Could
Desktop application Import Redirection DLL signature is checked for validity Could
Desktop application Import Redirection supports explicit specification of DLL architecture Could
Desktop application Import Redirection ignores, by default, applications signed by Microsoft Could
Desktop application Import Redirection DLL requires packaging/installation Won't
Desktop application Import Redirection uses DLL Search Order or PATH to locate its configuration Won't

Important Notes

Recommend syncing with App Lifecycle proposal due to its desktop application manifestation proposal. If implemented, the aforementioned desktop application manfiest could be extended to contain Import Redirection configuration parameters as well.

Sample usage

  1. User creates a msix manifest for a desktop app (fabrikam.exe)

    <?xml version="1.0" encoding="utf-8"?>
      <Identity Name="FabrikamDesktopApplication" ... />
      <ImportRedirectionTables architecture="x86">
        <Module name="app.exe"> <!-- Note: Module-level granularity is a Could feature -->
          <File>ContosoRedirections.dll</File>
          <File>NorthwindRedirections.dll</File>
        </Module>
        <Module name="dependency.dll">
          <!-- Note: Optional flag is a Could feature -->
          <File Optional="true">ADatumRedirections.dll</File>
        </Module>
      </ImportRedirectionTables>
  2. This manifest is saved to disk as fabrikam.exe.msixmanifest, located side-by-side with fabrikam.exe.

@ghost ghost added the needs-triage label Jul 23, 2020
@jonwis jonwis added the area-Win32 Support for Win32 packaged and non-packaged apps label Jul 23, 2020
@ptorr-msft
Copy link
Contributor

Thanks @riverar . Import Redirection was designed to be applicable for Win32 apps as well; the original implementation was scoped to MSIX (and focused on UWP) as a prioritization / simplification choice, but there is nothing technically stopping it working for other apps. Finding a "home" for the redirection metadata is the biggest missing piece.

One caveat: API Redirection does not work for everything; DLLs loaded early in process startup (like NTDLL) cannot be redirected since they are resolved before the redirection tables are loaded. And as currently implemented, redirection does not apply to system binaries; only binaries inside the app's package graph are subject to having their imports redirected. Without a package to consult, we'd need a way to explicitly specify which DLLs to redirect (possibly with wild-cards etc.).

Initial response to your questions:

Does the specification of redirection DLLs create any new security concerns?

For packaged apps (the current scenario) the answer is "no," since you can't tamper with the manifest or any of the DLLs without breaking the signature. For unpackaged apps, it's a new feature that would need a thorough threat analysis but my initial (brief) thinking is that it's no worse than existing attacks against unpackaged apps (exploiting weak ACLs, etc.).

Should redirection DLLs be signed?

Should they? Yes, since all DLLs should be signed :-). See below for related questions...

Should the application fail to launch if redirection DLLs are specified but cannot be found

Right now it's considered fatal because it's part of a package where presumably you're in complete control of everything and the redirection is assumed to be mandatory (there are no decent diagnostics for this failure, which something we should improve...). For unpackaged apps, where you might not be in control of everything (eg, due to plugins, different SKU options, etc.) it might be reasonable to have an "optional" flag in the manifest. Is this necessary for a "v1" version, or is it something we could add later if we see the need?

Should Import Redirection support Windows on ARM (CHPE) scenarios?

Is there a reason why you call this out specifically? In a vacuum, the answer should be "yes."

Should Import Redirection work for .NET applications?

It does today for .NET Native (since that uses IATs) but not for .NET Core (or CoreCLR, used for UWP debug builds) which rely on LoadLibrary / GetProcAddress to resolve DllImports. Including LL / GPA in API Redirection is a missing feature, and if we implemented that it would work for .NET apps, too. Perhaps though what you are really asking is "should there be a pure .NET way of doing this instead of having to write a native DLL"?

There are a few other things to think about; we would like the community's feedback on them.

  1. As noted above, API Redirection does not apply to system binaries today. Should it be an option to do so? Note this could cause all sorts of problems (customer takes a Windows Update and now their app crashes because your redirection implementation doesn't work for that use-case) and would be highly undesirable. But do you think it should it be an option?
  2. In addition to implicit redirection ("oh I found this manifest, let me load the DLL") we'd considered explicit redirection as well, e.g. a way to tell CreateProcess "...and here are the DLLs to search for redirection tables." Interesting? High or low priority?
  3. Currently, redirection applies to all app binaries; if you redirect Foo to Bar then every app call to Foo goes to Bar. We had initially considered a per-app-binary redirection, so you could redirect (say) all calls from Foo to Bar when made by mine.dll, but you could redirect Foo to OtherBar (or not redirect at all) for yours.dll. Interesting? High or low priority?
  4. With regards to signing, is it important to have an option to require signing the manifest file? And the redirection DLLs? Signed by whom? (Presumably the person writing the manifest / redirection DLL is not the original app author, so it probably isn't "the same" cert used to sign the app).
  5. More broadly we have heard from customers that they want to require that all DLLs loaded by their process are signed by <set of certificates>. This is not strictly a security feature (an attacker could simply patch the main EXE to turn off the "I require signing" bits) but is more about reliability; some apps don't want to "accidentally" load random DLLs, or have 3rd-party hooks injected into themselves, etc. If this is interesting, please consider opening another issue for it.

@riverar
Copy link
Contributor Author

riverar commented Jul 26, 2020

Should Import Redirection support Windows on ARM (CHPE) scenarios?
Is there a reason why you call this out specifically? In a vacuum, the answer should be "yes."

Just thinking users may want to provide a CHPE Redirection DLL for ARM users. Edge case for sure.

Should Import Redirection work for .NET applications?

It does today for .NET Native (since that uses IATs) but not for .NET Core (or CoreCLR, used for UWP debug builds) which rely on LoadLibrary / GetProcAddress to resolve DllImports. Including LL / GPA in API Redirection is a missing feature, and if we implemented that it would work for .NET apps, too. Perhaps though what you are really asking is "should there be a pure .NET way of doing this instead of having to write a native DLL"?

Actually, was thinking of the former case which you covered 👍 I think hooking .NET is out of scope and can be handled with CLR profiling.

There are a few other things to think about; we would like the community's feedback on them.

  1. As noted above, API Redirection does not apply to system binaries today. Should it be an option to do so? Note this could cause all sorts of problems (customer takes a Windows Update and now their app crashes because your redirection implementation doesn't work for that use-case) and would be highly undesirable. But do you think it should it be an option?

What's a "system binary" here? Are we talking about known DLLs (e.g. kernel32), apps (e.g. svchost), both/neither?

  1. In addition to implicit redirection ("oh I found this manifest, let me load the DLL") we'd considered explicit redirection as well, e.g. a way to tell CreateProcess "...and here are the DLLs to search for redirection tables." Interesting? High or low priority?

Interesting indeed, would need to be wired into ActivateApplication as well. Should it respect the appmodel sandbox, if present?

  1. Currently, redirection applies to all app binaries; if you redirect Foo to Bar then every app call to Foo goes to Bar. We had initially considered a per-app-binary redirection, so you could redirect (say) all calls from Foo to Bar when made by mine.dll, but you could redirect Foo to OtherBar (or not redirect at all) for yours.dll. Interesting? High or low priority?

Hm. It's my understanding users could roll out similar behavior via the existing __ShouldApplyRedirectionToFunction__ (untested). If so, I'm not sure there's much value to be had.

  1. With regards to signing, is it important to have an option to require signing the manifest file? And the redirection DLLs? Signed by whom? (Presumably the person writing the manifest / redirection DLL is not the original app author, so it probably isn't "the same" cert used to sign the app).

Hm, on second pass, I don't see a lot of value to be had with signing. Perhaps we toss that out. (Currently listed as a Could above.)

  1. More broadly we have heard from customers that they want to require that all DLLs loaded by their process are signed by <set of certificates>. This is not strictly a security feature (an attacker could simply patch the main EXE to turn off the "I require signing" bits) but is more about reliability; some apps don't want to "accidentally" load random DLLs, or have 3rd-party hooks injected into themselves, etc. If this is interesting, please consider opening another issue for it.

I don't think that'll ever truly work. Microsoft (e.g. PowerToys), NVIDIA (e.g. nvapi), and others inject all sorts of hooks into processes on the system for all sorts of valid reasons.

@ptorr-msft
Copy link
Contributor

What's a "system binary" here? Are we talking about known DLLs (e.g. kernel32), apps (e.g. svchost), both/neither?

In the current context, "system binary" meant anything not in the package. For unpackaged apps, since nothing is in a package :) the definition would need to be updated (eg, maybe everything in %system32%, or anything not in the app's directory & subdirectories, or some specific list of do-not-virtualize DLLs, etc.). At a minimum we likely need a speedbump to prevent accidental redirection of DLLs that could app instability or crashes. Have you ever needed to redirect an API for a DLL that wasn't "part of the app"?

Should it respect the appmodel sandbox, if present?

Redirection doesn't / can't affect the sandbox. It's basically just switching a pointer.

It's my understanding users could roll out similar behavior via the existing ShouldApplyRedirectionToFunction (untested)

That wouldn't let you do a different remapping though. (I was going to say "that's not documented" but then again none of this is very well documented...).

Microsoft (e.g. PowerToys), NVIDIA (e.g. nvapi), and others inject all sorts of hooks into processes on the system for all sorts of valid reasons.

Anyone who opts into this likely knows what they are doing and will test appropriately; whatever PowerToys is trying to inject isn't that essential (sorry Clint :)). Anyway, good to know you're not specifically interested in it.

@riverar
Copy link
Contributor Author

riverar commented Jul 28, 2020

What's a "system binary" here? Are we talking about known DLLs (e.g. kernel32), apps (e.g. svchost), both/neither?

In the current context, "system binary" meant anything not in the package. For unpackaged apps, since nothing is in a package :) the definition would need to be updated (eg, maybe everything in %system32%, or anything not in the app's directory & subdirectories, or some specific list of do-not-virtualize DLLs, etc.). At a minimum we likely need a speedbump to prevent accidental redirection of DLLs that could app instability or crashes. Have you ever needed to redirect an API for a DLL that wasn't "part of the app"?

I'm still struggling with the write up on this point so I'll try to restate it and we'll bang away on that.

  • Import Redirection must allow a user to redirect any/all APIs listed in the IAT of any manifested executable, no restrictions. Hooking APIs that would normally call an operating system DLL is critical to most appcompat work. (For example, for Myst, I had to fix up DirectInput!EnumDevices.)

  • Import Redirection (metadata) could be ignored for system executables, with system executables defined as executables owned (and signed?) by Microsoft (?). This feels especially rigid and error prone, am eager to hear your take on this.

It's my understanding users could roll out similar behavior via the existing ShouldApplyRedirectionToFunction (untested)

That wouldn't let you do a different remapping though. (I was going to say "that's not documented" but then again none of this is very well documented...).

If I understand correctly, the question is whether users want to redirect APIs at module-granularity or process-granularity. That's an interesting question.

So presumably, the metadata could appear as such:

<?xml version="1.0" encoding="utf-8"?>
  <Identity Name="DesktopApplication1" ... />
  <ImportRedirectionTables architecture="x86">
    <Module name="app.exe">
      <File>ContosoAppRedirections.dll</File>
      <File>NorthwindAppRedirections.dll</File>
    </Module>
    <Module name="dependency.dll">
      <File>ADatumAppRedirections.dll</File>
    </Module>
  </ImportRedirectionTables>
  ...

Module-level granularity sounds like a great stretch goal, I'd consider it a candidate for addition to proposal's scope as a Could item.

Administrative question: How/When do we go about revising the proposal as we continue the discussion?

@riverar
Copy link
Contributor Author

riverar commented Nov 6, 2020

Shameless bump. Can we bang on this some more @ptorr-msft?

@stevewri
Copy link
Contributor

@ptorr-msft has limited availability right now. @jonwis, is there anyone else who can engage?

@ptorr-msft
Copy link
Contributor

@riverar can you update the original proposal with context from the discussion, including proposed manifest changes (Win32 and MSIX)? Then we can generate internal work-item to track it. I also think you can drop a lot of the "rationale" if you like.

@riverar
Copy link
Contributor Author

riverar commented Jan 11, 2021

Updated the proposal and example with what we've discussed so far. I've also pared down the rationale as well.

Strongly recommend App Lifecycle #111 (currently in spec loop) at least carefully consider the design of their manifest to accommodate extensibility such as the one proposed here.

cc: @ptorr-msft

@riverar
Copy link
Contributor Author

riverar commented Oct 1, 2023

Ping to renew interest for 2023+

@riverar
Copy link
Contributor Author

riverar commented Oct 14, 2023

Understand everyone is busy and resources are, well, less than before.

@AdamBraden Is your assignment here still valid? Or can we hot potato this to another person? I still think this feature would be very well received! cc: @jonwis

@AdamBraden
Copy link

Thanks for the ping, Rafael. Let me work with @jonwis and others to see if we can find the right owner and find a way to prioritize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Win32 Support for Win32 packaged and non-packaged apps feature
Projects
None yet
Development

No branches or pull requests

7 participants