Merge pull request #2 from Microsoft/fixup_docs
Fix links to point to the correct headers
This commit is contained in:
Коммит
66320afc50
|
@ -2,7 +2,7 @@
|
||||||
The only two requirements are the two required dll exports: `ShimInitialize` and `ShimUninitialize`. Other than that, the shim is relatively free to do whatever it wants.
|
The only two requirements are the two required dll exports: `ShimInitialize` and `ShimUninitialize`. Other than that, the shim is relatively free to do whatever it wants.
|
||||||
|
|
||||||
## Shim Loading
|
## Shim Loading
|
||||||
The shim loading process is described in more detail [here](ShimRuntime/readme.md#Shim-Loading), but in short, when the process starts up, the Shim Runtime will enumerate the set of dlls configured for the current process, loading them before calling `ShimInitialize` within a Detours transaction. Typical shim behavior is to call `ShimRegister` for each function it wishes to detour at this time. This process can be somewhat automated by using the `DECLARE_SHIM` and `DECLARE_STRING_SHIM` macros. For example, consider the declarations for the `GetFileAttributes` functions:
|
The shim loading process is described in more detail [here](ShimRuntime/readme.md#shim-loading), but in short, when the process starts up, the Shim Runtime will enumerate the set of dlls configured for the current process, loading them before calling `ShimInitialize` within a Detours transaction. Typical shim behavior is to call `ShimRegister` for each function it wishes to detour at this time. This process can be somewhat automated by using the `DECLARE_SHIM` and `DECLARE_STRING_SHIM` macros. For example, consider the declarations for the `GetFileAttributes` functions:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
DWORD WINAPI GetFileAttributesA(LPCSTR fileName);
|
DWORD WINAPI GetFileAttributesA(LPCSTR fileName);
|
||||||
|
|
|
@ -93,7 +93,7 @@ In this example, the configuration is directing the File Redirection Shim to red
|
||||||
In reality, most applications will only require redirecting access to either (1) the package path, or (2) a named known folder.
|
In reality, most applications will only require redirecting access to either (1) the package path, or (2) a named known folder.
|
||||||
|
|
||||||
## Redirected Paths
|
## Redirected Paths
|
||||||
Determining whether or not to redirect a path, and determining what that redirected path is, is a multi-step process. The first step in this process is to "normalize" the path. In essence, this primarily just involves expanding this path out to an absolute path (via `GetFullPathName`). It does _not_ perform any canonicalization; see the section on [Limitations](#Limitations) for more information. Once the path is normalized, it is "de-virtualized." This involves mapping paths under the different package-relative `VFS` directories to their virtualized equivalent. E.g. a path under the `VFS\Windows` folder under the package path would get translated to the equivalent path under the expanded `FOLDERID_Windows` path. This is to ensure that references to the same file get redirected to the same location. Next, this path is compared to the set of configured paths. If the path "starts with" the configured path, then the remainder of the path is comopared to the configured regex pattern(s). If the remainder of the path matches the pattern, then the redirection kicks in. As a concrete example, consider the following scenario:
|
Determining whether or not to redirect a path, and determining what that redirected path is, is a multi-step process. The first step in this process is to "normalize" the path. In essence, this primarily just involves expanding this path out to an absolute path (via `GetFullPathName`). It does _not_ perform any canonicalization; see the section on [Limitations](#limitations) for more information. Once the path is normalized, it is "de-virtualized." This involves mapping paths under the different package-relative `VFS` directories to their virtualized equivalent. E.g. a path under the `VFS\Windows` folder under the package path would get translated to the equivalent path under the expanded `FOLDERID_Windows` path. This is to ensure that references to the same file get redirected to the same location. Next, this path is compared to the set of configured paths. If the path "starts with" the configured path, then the remainder of the path is comopared to the configured regex pattern(s). If the remainder of the path matches the pattern, then the redirection kicks in. As a concrete example, consider the following scenario:
|
||||||
|
|
||||||
> * The application makes an attempt to create the file `log.txt`
|
> * The application makes an attempt to create the file `log.txt`
|
||||||
> * The normalized path is `C:\Program Files\WindowsApps\Contoso.App_1.0.0.0_x64__wgeqdkkx372wm\VFS\ProgramFilesX64\Contoso\App\log.txt`
|
> * The normalized path is `C:\Program Files\WindowsApps\Contoso.App_1.0.0.0_x64__wgeqdkkx372wm\VFS\ProgramFilesX64\Contoso\App\log.txt`
|
||||||
|
|
|
@ -40,7 +40,7 @@ The Shim Runtime then calls `ShimInitialize` within a Detours transaction, faili
|
||||||
|
|
||||||
> **IMPORTANT: The exported names must _exactly_ match `ShimInitialize` and `ShimUninitialize`. This isn't automatic when using `__declspec(dllexport)` due to the "mangling" performed for 32-bit binaries**
|
> **IMPORTANT: The exported names must _exactly_ match `ShimInitialize` and `ShimUninitialize`. This isn't automatic when using `__declspec(dllexport)` due to the "mangling" performed for 32-bit binaries**
|
||||||
|
|
||||||
> TIP: In most cases you can leverage the `SHIM_DEFINE_EXPORTS` macro to define/export these functions for you with the correct names. See [here](../Authoring.md#Shim-Loading) for more information
|
> TIP: In most cases you can leverage the `SHIM_DEFINE_EXPORTS` macro to define/export these functions for you with the correct names. See [here](../Authoring.md#shim-loading) for more information
|
||||||
|
|
||||||
## Runtime Requirements
|
## Runtime Requirements
|
||||||
As a part of its initialization, the Shim Runtime queries information about its environment that it then caches for later use. A few examples include parsing the `config.json`, caching the path to the package root, and caching the package name, among a couple other things. If any of these steps fail, e.g. because something is not present/cannot be found or any other failure, then the Shim Runtime dll will fail to load, which likely means that the process fails to start. Note that this implies the requirement that the application be running with package identity. There have been past conversations on adding support for a "debug" mode that works around this restriction (e.g. by using a fake package name, executable directory as the package root, etc.), but its benefit is questionable and has not yet been implemented.
|
As a part of its initialization, the Shim Runtime queries information about its environment that it then caches for later use. A few examples include parsing the `config.json`, caching the path to the package root, and caching the package name, among a couple other things. If any of these steps fail, e.g. because something is not present/cannot be found or any other failure, then the Shim Runtime dll will fail to load, which likely means that the process fails to start. Note that this implies the requirement that the application be running with package identity. There have been past conversations on adding support for a "debug" mode that works around this restriction (e.g. by using a fake package name, executable directory as the package root, etc.), but its benefit is questionable and has not yet been implemented.
|
||||||
|
|
|
@ -3,10 +3,10 @@ The Package Support Framework makes some assumptions about which files are prese
|
||||||
|
|
||||||
| File Name | Requirements |
|
| File Name | Requirements |
|
||||||
| --------- | ------------ |
|
| --------- | ------------ |
|
||||||
| ShimLauncher32.exe<br>ShimLauncher64.exe | This is the entry point to the application that appears in the AppxManifest. There is no naming requirement imposed on it and the only path requirement is that it be able to find ShimRuntimeXX.dll in its dll search path. In fact, you can relatively easily replace this executable with your own if you wish. In general, it is suggested that you match this executable's architecture with that of the target executable to avoid unnecessary extra work. You can find more information on [MSDN](https://docs.microsoft.com/windows/uwp/porting/package-support-framework#Create-a-configuration-file) |
|
| ShimLauncher32.exe<br>ShimLauncher64.exe | This is the entry point to the application that appears in the AppxManifest. There is no naming requirement imposed on it and the only path requirement is that it be able to find ShimRuntimeXX.dll in its dll search path. In fact, you can relatively easily replace this executable with your own if you wish. In general, it is suggested that you match this executable's architecture with that of the target executable to avoid unnecessary extra work. You can find more information on [MSDN](https://docs.microsoft.com/windows/uwp/porting/package-support-framework#create-a-configuration-file) |
|
||||||
| ShimRuntime32.dll<br>ShimRuntime64.exe | This _must_ be named either `ShimRuntime32.dll` or `ShimRuntime64.dll` (depending on architecture), and _must_ be located at the package root. For more information on this dll, you can find its documentation [here](ShimRuntime/readme.md) |
|
| ShimRuntime32.dll<br>ShimRuntime64.exe | This _must_ be named either `ShimRuntime32.dll` or `ShimRuntime64.dll` (depending on architecture), and _must_ be located at the package root. For more information on this dll, you can find its documentation [here](ShimRuntime/readme.md) |
|
||||||
| ShimRunDll32.exe<br>ShimRunDll64.exe | Its presence is only required if cross-architecture launches are a possibility. Otherwise, it _must_ be named either `ShimRunDll32.exe` or `ShimRunDll64.exe` (depending on architecture), and _must_ be located at the package root. For more information on this executable, you can find its documentation [here](ShimRunDll/readme.md) |
|
| ShimRunDll32.exe<br>ShimRunDll64.exe | Its presence is only required if cross-architecture launches are a possibility. Otherwise, it _must_ be named either `ShimRunDll32.exe` or `ShimRunDll64.exe` (depending on architecture), and _must_ be located at the package root. For more information on this executable, you can find its documentation [here](ShimRunDll/readme.md) |
|
||||||
| config.json | The configuration file _must_ be named `config.json` and _must_ be located at the package root. For more information, see [the documentation on MSDN](https://docs.microsoft.com/windows/uwp/porting/package-support-framework#Create-a-configuration-file) |
|
| config.json | The configuration file _must_ be named `config.json` and _must_ be located at the package root. For more information, see [the documentation on MSDN](https://docs.microsoft.com/windows/uwp/porting/package-support-framework#create-a-configuration-file) |
|
||||||
| Shim dlls | There is no naming or path requirement for the individual shim dlls, although they must also be able to find `ShimRuntimeXX.dll` in their dll search paths. It is also suggested that the name end with either `32` or `64` (more information can be found [here](ShimRuntime/readme.md#Shim-Loading)) |
|
| Shim dlls | There is no naming or path requirement for the individual shim dlls, although they must also be able to find `ShimRuntimeXX.dll` in their dll search paths. It is also suggested that the name end with either `32` or `64` (more information can be found [here](ShimRuntime/readme.md#shim-loading)) |
|
||||||
|
|
||||||
In general, it's probably safest/easiest to place all Package Support Framework related files and binaries directly under the package root.
|
In general, it's probably safest/easiest to place all Package Support Framework related files and binaries directly under the package root.
|
||||||
|
|
|
@ -21,9 +21,9 @@ The `tests.sln` solution file in the parent directory should reference all test
|
||||||
### Creating Appx-es
|
### Creating Appx-es
|
||||||
The restrictions that Desktop Bridge places on applications is only fully realized when installed through the "normal" appx install path. I.e. attempting to register the package in development mode - e.g. through `Add-AppxPackage -Register` - while useful for quick and dirty tests, won't necessarily reflect reality. For example, the filesystem ACLs that prevent apps from being able to write to their install path won't be present unless installed normally. Therefore, all tests are set up with the expectation that testing will occur after genarating and installing an appx.
|
The restrictions that Desktop Bridge places on applications is only fully realized when installed through the "normal" appx install path. I.e. attempting to register the package in development mode - e.g. through `Add-AppxPackage -Register` - while useful for quick and dirty tests, won't necessarily reflect reality. For example, the filesystem ACLs that prevent apps from being able to write to their install path won't be present unless installed normally. Therefore, all tests are set up with the expectation that testing will occur after genarating and installing an appx.
|
||||||
|
|
||||||
The `MakeAppx.ps1` script is useful for automating this process. In essence, it will create an appx, placing it under the `Appx` folder, generate a certificate using the `CreateCert.ps1` script in the `signing` directory (if necessary), and sign the appx using that certificate. Note that this script requires that both `makeappx.exe` and `signtool.exe` - both a part of the Windows SDK - be a part of the PATH. Typically the easiset way to satisfy this requirement is by running powershell from a Visual Studio developer command prompt. See the [Project Layout](#Project-Layout) section for more information on the prerequisites for directory layout. Run `Get-Help MakeAppx.ps1` from a PowerShell command window for more detailed information.
|
The `MakeAppx.ps1` script is useful for automating this process. In essence, it will create an appx, placing it under the `Appx` folder, generate a certificate using the `CreateCert.ps1` script in the `signing` directory (if necessary), and sign the appx using that certificate. Note that this script requires that both `makeappx.exe` and `signtool.exe` - both a part of the Windows SDK - be a part of the PATH. Typically the easiset way to satisfy this requirement is by running powershell from a Visual Studio developer command prompt. See the [Project Layout](#project-layout) section for more information on the prerequisites for directory layout. Run `Get-Help MakeAppx.ps1` from a PowerShell command window for more detailed information.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
As mentioned in the [Project Layout](#Project-Layout) section, each scenario will have multiple entry points: one or more for un-shimmed scenarios and one or more for shimmed scenarios. The un-shimmed versions should fail most, if not all tests when run whereas the shimmed variants should pass all tests when run. The name of the application in the start menu will indicate which variant is which.
|
As mentioned in the [Project Layout](#project-layout) section, each scenario will have multiple entry points: one or more for un-shimmed scenarios and one or more for shimmed scenarios. The un-shimmed versions should fail most, if not all tests when run whereas the shimmed variants should pass all tests when run. The name of the application in the start menu will indicate which variant is which.
|
||||||
|
|
||||||
In order to execute a test, follow the above for [creating an appx](#Creating-Appx-es) for the scenario(s) you wish to test and install the appx, e.g. by using [PowerShell's `Add-AppxPackage` cmdlet](https://docs.microsoft.com/en-us/powershell/module/appx/add-appxpackage?view=win10-ps), or by double clicking on the appx and using the [App Installer](https://www.microsoft.com/en-us/store/p/app-installer/9nblggh4nns1?activetab=pivot%3aoverviewtab). Once installed, multiple entries will be added to your start menu - one for each entry point in the manifest. Launch the shimmed version to validate that there are no runtime errors. If you are making modifications to a test, or if you are authoring a new test, it is also good to launch the un-shimmed version to validate that the executable fails at runtime (i.e. the shims are actually doing something).
|
In order to execute a test, follow the above for [creating an appx](#creating-appx-es) for the scenario(s) you wish to test and install the appx, e.g. by using [PowerShell's `Add-AppxPackage` cmdlet](https://docs.microsoft.com/en-us/powershell/module/appx/add-appxpackage?view=win10-ps), or by double clicking on the appx and using the [App Installer](https://www.microsoft.com/en-us/store/p/app-installer/9nblggh4nns1?activetab=pivot%3aoverviewtab). Once installed, multiple entries will be added to your start menu - one for each entry point in the manifest. Launch the shimmed version to validate that there are no runtime errors. If you are making modifications to a test, or if you are authoring a new test, it is also good to launch the un-shimmed version to validate that the executable fails at runtime (i.e. the shims are actually doing something).
|
||||||
|
|
|
@ -37,7 +37,7 @@ The shim can be configured to trace calls in a variety of ways and in a variety
|
||||||
| -------- | ----------- |
|
| -------- | ----------- |
|
||||||
| `traceMethod` | Defines the method of tracing. This is expected to be a value of type `string`. Allowed values are:<br>`printf` - Uses `printf` (i.e. console output) for tracing.<br>`outputDebugString` - Uses `OutputDebugString` for tracing. This is the default. |
|
| `traceMethod` | Defines the method of tracing. This is expected to be a value of type `string`. Allowed values are:<br>`printf` - Uses `printf` (i.e. console output) for tracing.<br>`outputDebugString` - Uses `OutputDebugString` for tracing. This is the default. |
|
||||||
| `waitForDebugger` | Specifies whether or not to hold the process until a debugger is attached in the `DLL_PROCESS_ATTACH` callback. This is expected to be a value of type `boolean`. The default value is `false`. This option is most useful when `traceMethod` is set to `outputDebugString`. |
|
| `waitForDebugger` | Specifies whether or not to hold the process until a debugger is attached in the `DLL_PROCESS_ATTACH` callback. This is expected to be a value of type `boolean`. The default value is `false`. This option is most useful when `traceMethod` is set to `outputDebugString`. |
|
||||||
| `traceFunctionEntry` | Specifies whether or not to trace function entry. This is useful when trying to reason about function call order and composition since functions are logged in the reverse order (see [Log Ordering](#Log-Ordering) for more information). This is expected to be a value of type `boolean`. The default value is `false`. Note that this logging is done independent of function success/failure and the `traceLevels` configuration since success/failure is not known at function entry. |
|
| `traceFunctionEntry` | Specifies whether or not to trace function entry. This is useful when trying to reason about function call order and composition since functions are logged in the reverse order (see [Log Ordering](#log-ordering) for more information). This is expected to be a value of type `boolean`. The default value is `false`. Note that this logging is done independent of function success/failure and the `traceLevels` configuration since success/failure is not known at function entry. |
|
||||||
| `traceCallingModule` | Defines whether or not to include the calling module in the output. This is expected to be a value of type `boolean`. The default value is `true`. This is potentially useful for identifying possible risks of recursion (one API implemented using another). There's no real harm with leaving this option always enabled, but can help reduce output noise when turned off. |
|
| `traceCallingModule` | Defines whether or not to include the calling module in the output. This is expected to be a value of type `boolean`. The default value is `true`. This is potentially useful for identifying possible risks of recursion (one API implemented using another). There's no real harm with leaving this option always enabled, but can help reduce output noise when turned off. |
|
||||||
| `ignoreDllLoad` | Specifies whether or not to ignore calls to `NtCreateFile` for dlls. This is expected to be a value of type `boolean`. The default value is `true`. |
|
| `ignoreDllLoad` | Specifies whether or not to ignore calls to `NtCreateFile` for dlls. This is expected to be a value of type `boolean`. The default value is `true`. |
|
||||||
| `traceLevels` | Used to determine whether or not a function call should get logged, based off function result. E.g. you can configure calls to always get logged, only logged for unexpected failures, or logged for any failure. This is expected to be a value of type `object`. The format is described in more detail below |
|
| `traceLevels` | Used to determine whether or not a function call should get logged, based off function result. E.g. you can configure calls to always get logged, only logged for unexpected failures, or logged for any failure. This is expected to be a value of type `object`. The format is described in more detail below |
|
||||||
|
@ -63,7 +63,7 @@ And the values specify which function results to trace, based off the degree of
|
||||||
| `unexpectedFailures` | Logs only failures that are not considered to be "expected" - such as "file not found", "buffer overflow", etc. |
|
| `unexpectedFailures` | Logs only failures that are not considered to be "expected" - such as "file not found", "buffer overflow", etc. |
|
||||||
| `ignore` | Does not log output for any function call, regardless of success/failure |
|
| `ignore` | Does not log output for any function call, regardless of success/failure |
|
||||||
|
|
||||||
The configuration that's best to use will depend on the scenario. For example, you likely don't want to use a `traceMethod` of `printf` unless the target application is a console application. E.g. the test applications in this project are mostly console applications, however most "real world" applications probably are not. Similarly, a value of `unexpectedFailures` for the default trace level may be a reasonable starting place to reduce noise, but this isn't always an indication of issue(s) due to the previously mentioned [Limitations](#Limitations).
|
The configuration that's best to use will depend on the scenario. For example, you likely don't want to use a `traceMethod` of `printf` unless the target application is a console application. E.g. the test applications in this project are mostly console applications, however most "real world" applications probably are not. Similarly, a value of `unexpectedFailures` for the default trace level may be a reasonable starting place to reduce noise, but this isn't always an indication of issue(s) due to the previously mentioned [Limitations](#limitations).
|
||||||
|
|
||||||
## Log Ordering
|
## Log Ordering
|
||||||
Since the majority purpose of this shim is to identify API call failures, tracing must be done _after_ the invocation of the implementation function returns. This means that if a single function is written in terms of one or more other functions, then they will appear in reverse order in the output. E.g. `CreateFile` is written in terms of `NtCreateFile`, so if both functions are shimmed, then you will see output for the call to `NtCreateFile` _before_ the output for the call to `CreateFile`.
|
Since the majority purpose of this shim is to identify API call failures, tracing must be done _after_ the invocation of the implementation function returns. This means that if a single function is written in terms of one or more other functions, then they will appear in reverse order in the output. E.g. `CreateFile` is written in terms of `NtCreateFile`, so if both functions are shimmed, then you will see output for the call to `NtCreateFile` _before_ the output for the call to `CreateFile`.
|
||||||
|
|
Загрузка…
Ссылка в новой задаче