I recently played around a little bit with volshell, as I had realized I do not have sufficient notes on how to use it. I’ll share the notes below.
Volshell uses virtual memory addresses, if you are searching or using offsets keep that in mind.
Starting volshell without entering the context of a process is pretty straigh forward.
Getting some help for the first usage can be done by the hh() command.
1234567891011121314151617181920212223242526
In [1]: hh()
Use addrspace() for Kernel/Virtual AS
Use addrspace().base for Physical AS
Use proc() to get the current process object
and proc().get_process_address_space() for the current process AS
and proc().get_load_modules() for the current process DLLs
addrspace() : Get the current kernel/virtual address space.
cc(offset=None, pid=None, name=None, physical=False) : Change current shell context.
db(address, length=128, space=None) : Print bytes as canonical hexdump.
dd(address, length=128, space=None) : Print dwords at address.
dis(address, length=128, space=None, mode=None) : Disassemble code at a given address.
dq(address, length=128, space=None) : Print qwords at address.
dt(objct, address=None, space=None, recursive=False, depth=0) : Describe an object or show type info.
find(needle, max=1, shift=0, skip=0, count=False, length=128) :
getmods() : Generator for kernel modules (scripting).
getprocs() : Generator of process objects (scripting).
hh(cmd=None) : Get help on a command.
list_entry(head, objname, offset=-1, fieldname=None, forward=True, space=None) : Traverse a _LIST_ENTRY.
modules() : Print loaded modules in a table view.
proc() : Get the current process object.
ps() : Print active processes in a table view.
sc() : Show the current context.
For help on a specific command, type 'hh(<command>)'
To list the processes you can run the ps() command within the interactive volshell prompt.
1234567
In [1]: ps()
Name PID PPID Offset
System 4 0 0x85c50958
smss.exe 280 4 0x86ecaa70
csrss.exe 412 404 0x86cfa540
wininit.exe 464 404 0x87d85d40
<snip>
This output gives you an overview about the name, the process id (PID), the parent process (PPID) and the offset of each process.
The PID or the name can be used to change the context to a specific process, more on this can be seen below. For now we will work with the offset only.
In order to make structures like the Proccess Environment Block (PCB) accessible, volatility has its own structures. To view one of those structures you can use the dt() command.
If you would like to understand what the _EPROCESS structure contains use:
As you can see above, at the offset of 0x1a8 in the _EPROCESS structure, the Process Environment Block (PEB) can be found. It is a pointer, pointing to a structure named _PEB.
If we would like to understand the _PEB structure, we can again use the dt() command.
If we would like to know more about the process parameters we can learn that from the _RTL_USER_PROCESS_PARAMETERS structure, which is part of the Process Environment Block. For example it contains the command line the process was started with.
You can use these defined structures as an overlay for every offset. Volshell will than use the given structure and fill it with data starting the offset provided. As memory is just bits and bytes, the overlay can be applied to addresses not representing the corresponding structure. Be warned to double check your offsets and validate the output.
In order to use a structure as an overlay at a specific offset you have to tell volshell the offset. You can do this by providing it as a second parameter to the dt() command: dt(“$STRUCTUR_NAME”, $HEXADECIMAL_OFFSET)
So lets start to dig down and see what the command line of the process cmd process was.
Fist step is to determine the offset by looking at the ps() command output
12345
In [1]: ps()
Name PID PPID Offset
<snip>
cmd.exe 208 1208 0x860f2578
<snip>
Next use the _EPROCESS structure as on overlay at the offset of the process
Given the output above, we can see that the Process Environment Block points to 2147348480. This value is in decimal and we will convert it into hex in a standard bash shell:
12
~$ printf'0x%x\n' 2147348480
0x7ffdf000
If we are not changing the context to the process we are investigating we might get the following error.
1234
In [7]: dt('_PEB',0x7FFDF000)
ERROR: could not instantiate object
Reason: Invalid Address 0x7FFDF000, instantiating _PEB
So it is time to switch context and apply the above command again:
Our ProcessParameters are located at 3674456, which is 0x381158 in hex. Using the _RTL_USER_PROCESS_PARAMETER overlay and calculated offset, we can already see the command line used to start the process.
So it seems we cannot apply a predefined structure to view the content of the environment variables. How about to dump them with the db() command?
Again we converted the decimal pointer value (in this case to Environemt) to hex.
In order to view the complete content we need to overwrite the default returned size of 1024. Lucky for us the size of the environment is part of the _RTL_USER_PROCESS_PARAMETER structure.
12
In [17]: db (0x3932d0, 2484)
In [18]: db (0x3932d0, 0x9b4)
Both will return the complete content of the environment variables for the given process. This first command supplies the length db() should read and return in decimal form, the second used hexadecimal.
If you do not want to deal with the offsets, there is a different way as well.
We start volshell again and directly jump into the context of the process by supplying the -p $PID parameter, in our example we jump to the process used above (PID 208).
We are now already in the context of the process with PID 208 and we can reference the structures via “self” for the selected process. Of cause you can also reference structures via “self” if you manually switched the context to the process you are investigating via cc(pid=$PID_OF_CHOICE).
As you have seen above, Environment is a pointer to data in memory not being parsed with an object overlay like the ProcessParameters for example. So running something like “dt(self._proc.Peb.ProcessParameters.Environment)” will cause an error. We have to use db to print the context, as we have already done above. From above we further know, we need to include the length wen want to read in order to print the whole environment variables.
By now we have successfully reviewed various data about a process and also printed out the hex representation of the process environment variable by using volshell.