ExplorerPatcher includes a mechanism which allows the user to load
a single DLL, named `ep_extra.dll` and placed in `C:\Windows` when
`explorer.exe` loads. This was long requested by some users who wanted
to perform their own code/initializations/hooks when `explorer` started.
In the mean time, I thought `ep_extra.dll` would work nicely as a
loader for any number of modules. I could've included this functionality
in the main ExplorerPatcher code, but I decided to take it up as a
challenge instead and offer a robust implementation without changing
ExplorerPatcher's main code.
Thus, when using this as `ep_extra.dll`, it will also load DLLs that
match the `ep_extra_*.dll` pattern. These DLLs must export a function
called `setup` which the loader will execute on the thread that is
loading the DLLs. Although not currently checked, return 0 from this
function if your initialization succeeded, or some error code when it
fails.
What's up with the assembly and shell codes and weird threads that I
create? Well, I realized I kind of did a mistake when coding
ExplorerPatcher, in that I should have loaded and executed `ep_extra`
on a seprate thread, not in `explorer`'s main thread. But since I said
I'd do this challenge without changing EP, this was my solution
towards having this `ep_extra` loader do its work, load the other DLLs,
if any, and then unload itself from memory safely and (almost) cleanly
without disturbing the main application right after it does its job.
This way, you can update it seamlessly when `explorer` is running,
which is much more convenient than having to kill `explorer`, replace
the DLL, and then manually reload `explorer`. I don't know if this is
the best way, but it is the way I thought about when realizing that I
cannot call `FreeLibrary` simply, since the "next line" (which would
have been a "return") is well outside of valid memory at that point.
`FreeLibraryAndExitThread` also can't be used since I do not want to
exit `explorer`'s main thread which the loader's function is called on.
Impact: A recent bug report on the Mozilla Firefox issue tracker
(https://bugzilla.mozilla.org/show_bug.cgi?id=1798707) identifies a
crash in the Firefox browser caused by an invalid memory access
performed by ExplorerPatcher (https://crash-stats.mozilla.org/signature/?signature=explorerpatcher.amd64.dll%20|%20%3Cunknown%20in%20Windows.UI.FileExplorer.dll%3E%20|%20explorerpatcher.amd64.dll%20|%20RtlpFindEntry%20|%20RtlpAllocateHeap%20|%20RtlpAllocateHeapInternal%20|%20explorerpatcher.amd64.dll%20|%20RtlDosApplyFileIsolationRedirection_Ustr%20|%20LdrpApplyFileNameRed...&date=%3E%3D2022-11-02T20%3A44%3A00.000Z&date=%3C2022-11-16T20%3A44%3A00.000Z).
This might happen only when the "Register as shell extension: option
is used, and ExplorerPatcher is injected in other processes. Testing
was unable to reproduce the issue, but looking on the crash logs it
was determined that it likely happens in "VnPatchDelayIAT", where
the memory is patched regardless of whether the protection level
actually succeeded changing or not. The call is suspected to fail
when certain antivirus solutions are used, although a clear test case
with this scenario could not be determined.
Also, code review determined that a race condition might happen in both
"VnPatchIAT" and "VnPatchDelayIAT", where some other thread might
unload the module while the code works with it, attempting to patch the
requested function.
Description: The issue has been addressed by improved checks and
ensuring the module is not unloaded while the functions work with it.
The program only attempts to patch the memory if the previous
protection change call actually succeeded. Additionally, the module
reference count is increased prior to working with it when attempting
the patch, in order to prevent other threads from successfully
unloading it. The proposed changes should harden the code against
unexpected behavior and should address the crashes experienced when
the code runs in other processes, including Firefox.
Impact: The actual layout setting from Windows Settings -
Personalization - Start is ignored and the "Default" layout setting is
always used when displaying the Windows 11 Start menu on 22621-based
builds when the setting to disable the "Recommended" section is NOT
used in ExplorerPatcher Properties.
Description: The issue has been addressed by remembering the current/
previous setting and using that when setting the height of the pinned
area when the setting to disable the "Recommended" section is NOT
used in ExplorerPatcher Properties, as opposed to the previous behavior
where a hardcoded value was being used.
Impact: Failure to check against a NULL value and dereferencing it
leads to a crash in `explorer.exe` with fault offset 0xfc69 on
22621-based OS builds. This happens when right clicking certain system
tray icons, like "Epic Games Launcher" when using the Windows 11
taskbar.
Description: The issue has been addressed with improved checks: a check
against NULL values is performed before attempting to work with the
data the variables might point to.
The final Windows 11 22H2 that is shipped to users is OS build 22621.
Unfortunately, I updated EP some time ago to 22622. This creates a
discrepancy between the OS build most people have and the version of
EP that they might have installed. Furthermore, I currently test EP
against OS build 22621. To make things more clear, the next version of
EP will have the major build number set to 22621.
In order to have EP use version 22621 from now on and not prevent
updating from EP 22622... to EP 22621..., I devised this workaround
where the EP version is kept to 22622 as far as updates are concerned,
but the internal build number of EP really is set to 22621.
In the future, if we want to bump the EP version to 22622 again, a
regular change in `version.h` will have the setup patcher insert a `!`
as the first character in the hash part of the version string, which
tells the updater to display that version as a real 22622 build and not
artificially decrement it to 22621.
The only slight inconvenience is that, when the next update arrives,
people will be told they upgrade to EP 22622... still, but they will
receive an EP 22621... build. But at least the updater will work and
detect the new version. From there on, subsequent versions will be
shown as 22621 in the notification if the build number for that version
really is 22621.