Skip to content

Conversation

the-rectifier
Copy link
Contributor

@the-rectifier the-rectifier commented May 23, 2025

A plugin that dumps the stack for each (running) thread.

It grabs the current RSP from the TrapFrame _ETHREAD->_KTHREAD->_KTRAP_FRAME.Rsp, and the Stack base from _ETHREAD->_KTHREAD->_TEB.NT_TIB.StackBase

This could (and probably should) be part of another plugin like memmap, but I figured I would submit it for review and then change it.

Edit: No issues with the _NT_TIB object, see referenced 'issue'

@eve-mem
Copy link
Contributor

eve-mem commented May 23, 2025

It might be worth making an issue for the dt() problem. Perhaps i broke something with #1748

@SolitudePy
Copy link
Contributor

maybe this plugin can be enhanced to list the actual callstack?

Copy link
Member

@ikelos ikelos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks reasonable, but from a code perspective makes me cry when people use volatility to scalpel their way to some data and then use a machete to hack it raw from the layer! 5:P

# Stack Base is at NT_TIB + 8
# https://github.com/wine-mirror/wine/blob/master/include/winternl.h#L494
# https://github.com/wine-mirror/wine/blob/master/include/winnt.h#L2445
stack_base = struct.unpack(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to avoid reading bytes directly. Either make a struct or at least instantiate a new object of the right type for windows on it. Having to 0 index the results of unpack basically throws away all the extra goodness we provide through the volatility model (keeping track of the type, the offset, which layer it came from, etc).

The entire volatility framework is a giant parser, so if you're resorting to struct.unpack yourself please consider if there's no other way of doing it (or if you're doing it so often efficiency comes into question, which ones per thread, isn't the case here).


if self.config["dump"]:
fname = f"{proc.UniqueProcessId}.{thread.Cid.UniqueThread}.dmp"
stack = proc_layer.read(offset=thread_rsp, length=stack_size)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a reasonable use of read, because it's a big chunk of unstructured (or unknown structure) data, that you just want to write out as a big blob and aren't going to use again inside volatility.

@the-rectifier
Copy link
Contributor Author

Hello @ikelos, thank you for all the pointers (and apologies for the tears). I will make the code more modular and also create the NT_TIB object rather than reading an address of it directly from the layer.

Would migrating (a part of) this over to extensions.ETHREAD make sense? I was thinking of a get_stack() method that this plugin will call on each constructed _ETHREAD object:

for thread in active_thread_list:
	stack = thread.get_stack()

@the-rectifier
Copy link
Contributor Author

@SolitudePy, I think this is a nice addition, but I'm afraid I don't have the time to debug a process and find a way to follow the call stack, at least not right now. Maybe I will revisit it when my program is a bit lighter, but feel free to contribute if you want!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants