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

generate_pool_scan does not support scanning for objects outside of ntoskrnl #1504

Open
atcuno opened this issue Jan 1, 2025 · 3 comments
Open
Assignees

Comments

@atcuno
Copy link
Contributor

atcuno commented Jan 1, 2025

Until now, the generate_pool_scan function:

Was sufficient for all pool scanners as we were only scanning for objects inside the main kernel (ntoskrnl), which is what backs the required and default symbol table.

But now I ran into two blocking issues with this function as the last remaining major feature to get added to Vol3 for parity is the suite of Windows GUI plugins. The GUI plugins (windowstations, desktops, windows, etc.) are driven by the GUI subsystem, which is implemented in w32k.sys and not ntoskrnl. We find these data structures largely through pool scanning before then walking embedded lists. Also, (issue 2) is that each session has its own virtual address space (memory layer) within the kernel, which is not something the current function supports (scanning in the kernel layer, but creating the resulting objects in a different layer).

So in summary, I am stuck on this now on these two issues:

Issue 1:

The function accepts only one symbol table, which is expected to be for ntoskrnl as the function then looks up types and so on from the kernel itself:

type_map = handles.Handles.get_type_map(

The problem is that we need to also be able to send in the symbol table for symbols outside the kernel (win32k in this current case, but basically every driver uses pool allocations in some way). So this function needs to be made more general, but I am not sure the best way to do it.

We could rename the existing symbol_table parameter to be kernel_symbol_table and use it in the places we know we want the kernel to be referenced, such as the handle type map lookups. We could then accept another argument of object_symbol_table for where the objects being scanned for live. This could be a required argument, but then all existing scanners would need to send in the same value twice... OR it could be an optional argument and then the function checks if its set or not and defaults to the kernel if not. So either way the function will need to be updated to access and use two symbol tables or we can't support pool scanning outside of ntoskrnl, but we just need to decide the best way to do this.

Issue 2:

This is Win32k specific, but each GUI session has its own virtual address space (memory layer) and this is the layer where objects that are found through scanning need to be constructed.

Right now, the function always sets the memory layer as the kernel one since its the only one it has access to:

So, we could update the function to also take an optional memory layer on which to instantiate objects... otherwise we would force callers to recreate the objects on their own in the proper layer. If we going with forcing the recreation its fine, we would just need to document it and I would write the plugins to do that in the scanning loop.

@atcuno
Copy link
Contributor Author

atcuno commented Jan 1, 2025

I assigned this to @ikelos but if anyone else (@dgmcdona @superponible @iMHLv2 ) has strong opinions then please leave them here.

@ikelos
Copy link
Member

ikelos commented Jan 1, 2025

So, for issue 1, I'd write out a second function, keep it in the same file/plugin as the old one, but give it a second parameter that the first function defaults to, so the first function becomes a subset of the second, but the API is only added to:

Old function -> return new_function(kernel_table, kernel_table, ...)
New function -> Do what old function did, but with two tables

The names of the parameters should be chosen carefully to be as accurate as possible about their use (if they're not just for the kernel, then don't steer people down the kernel path by calling the variable kernel_something).

For issue 2, under what cases would the use of native_layer not be as good as recreating the object on the correct layer? The native layer should get passed to any substructures, so as long as the data for the structure was in contiguous pages of both, I don't see it making much of a difference? It's the reason we built the native_layer mechanism into the structures? If there's places where people incorrectly assumed they could just take vol.layer_name rather than vol.native_layer then we should fix those up. If you're saying that the native_layer isn't known within the function, I think issue 1 would resolve that too?

@atcuno
Copy link
Contributor Author

atcuno commented Jan 1, 2025

This makes sense thanks. Issue 2 isn't going to be as nice as I thought, but working on a unified way for the GUI scanners at least. Should have the PR for the first plugins later today or tomorrow depending how painful it ends up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants