-
-
Notifications
You must be signed in to change notification settings - Fork 2.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
Update image memory region processing to support 24H2 changes #3342
base: development
Are you sure you want to change the base?
Conversation
24H2 adds hotpatching, with hotpatchable modules having an extra page mapped at the end of their image region. We previously assumed that the image region will only contain the header and executable sections, that's no longer the case.
I spent a few days tracking down where an extra page in some image regions is coming from. That ultimately resulted in this post: https://ynwarcs.github.io/Win11-24H2-CFG. It's pretty long & technical so feel free to skim over it. Basically, there's now hotpatching on Windows 11 24H2, and most of system modules are marked as hotpatchable, which makes kernel code extend their image region by a single page. This page will store new CFG functions, presumably so that they can be hotpatched painlessly. x64dbg assumed that the image region only contains header + sections, which caused the linked bug. This fix handles that, but I made it a little more generic (i.e. we don't assume there's only going to be a single page, or even that the leftover size in the region is page-aligned), if you want me to make the checks more specific to this page, let me know. At the moment, there's only a single page but this could change in the future I guess. One more thing to note about this page is that it's seen as committed by the debugger, but is actually not committed by the OS, causing |
MEMPAGE imageExtensionPage = {}; | ||
imageExtensionPage.mbi.BaseAddress = (PVOID)(pageBase + totalSize); | ||
imageExtensionPage.mbi.RegionSize = pageSize - totalSize; | ||
sprintf_s(imageExtensionPage.info, "ImageExtension"); |
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.
"ImageExtension" is the name that shows up in kernel functions which deal with the extra page, hence why I named it this. If the format should be different, let's change it.
✅ Download x64dbg 1.0.1874 (commit e1eb85add9 by @ynwarcs) |
I think the solution should be to check for that flag + build number and insert those pages as fake pages earlier. This way other potential logic errors could be hidden. |
@JustasMasiulis gave me this paste: #ifndef MemoryImageExtensionInformation
#define MemoryImageExtensionInformation ((MEMORY_INFORMATION_CLASS)0xE)
#endif
struct MEMORY_IMAGE_EXTENSION_INFORMATION
{
ULONG ExtensionType; // MEMORY_IMAGE_EXTENSION_TYPE
ULONG Flags;
ULONG_PTR ExtensionImageBaseRva;
ULONG_PTR ExtensionSize;
}; for (ULONG ExtensionType = 0; ExtensionType < 4; ++ExtensionType)
{
MEMORY_IMAGE_EXTENSION_INFORMATION ExtensionInfo{};
ExtensionInfo.ExtensionType = ExtensionType;
ExtensionInfo.Flags = 0;
NTSTATUS Status = ZwQueryVirtualMemory(NtCurrentProcess(), ImageBase, MemoryImageExtensionInformation, &ExtensionInfo, sizeof(ExtensionInfo), nullptr);
if (!NT_SUCCESS(Status) || (ExtensionInfo.ExtensionSize == 0))
{
continue;
}
const auto ExtensionAddr = (char*)ImageBase + ExtensionInfo.ExtensionImageBaseRva;
const auto ExtensionEnd = ExtensionAddr + ExtensionInfo.ExtensionSize; |
Apologies for a late reply as I'd missed the activity here. The code above should be fine, but I think we should be using a handle to the debuggee process as the first argument to |
I also managed to reproduce the issue in a VM btw. Probably just checking the PE and build number is also enough 🤷♂️ |
Apologies once again for the delay, I barely found any time to look into this. I further debugged the conditions necessary for a module to have an extra page, and there are both process-wide conditions (for example, ACG being enabled means no hotpatching = no extra page for modules), and per-module conditions (for example, no extra page if the module doesn't have DVRT). Most conditions rely on undocumented behaviour & flags, such as DVRT or the CodeIntegrity field in the load configuration of the module. As such, I'm not sure it would be a good idea to implement detection based on these conditions, as they could silently change at any point and tracking them all down would require more effort. Unfortunately, there's not a single flag in the PE that we can rely on. Alternatively, it may be better to handle the extra page situation gracefully, but not commit to a single "explanation" for why a page is there. For example, instead of naming the extra memory "ImageExtension", we can name it "Unknown" to let the user know we're not sure what the memory corresponds to. The way I see it, there are only two possibilities for when we have extra memory at the end of the region - either it's this new page on newer Windows 11 builds, or something non-standard is happening, either by the program or by a driver on the system, in which case we're dealing with some level of obfuscation. In the first case, the extra page is useless for user-mode debugging anyway, so it doesn't matter how we present it to the user as they're not likely to do anything with it. In the second case, we can't do much better than just admitting we don't know what the memory is, anyway. I'll leave the decision in your hands. I can probably find some time to track down the precise conditions for a module to have an extra page, but I can't promise when that will be. |
|
24H2 adds hotpatching, with hotpatchable modules having an extra page mapped at the end of their image region. We previously assumed that the image region will only contain the header and executable sections, that's no longer the case.
Fixes #3338