Tutorial: Finding Out What Notepad is Doing¶
In this chapter we will use Nitro to monitor Windows Notepad. While this might not be super interesting in itself, it demonstrates all the essential techniques that will enable you to use Nitro for tackling real-world challenges.
Getting a Connection¶
The first thing to do is to initialize Nitro and attach it to a virtual machine. For the purposes of this tutorial, we expect the VM to be already running but we could of course use libvirt APIs for automating setting up the environment. Additionally, Nitro obviously has to be in Python’s module search path for any of this to work.
from nitro.nitro import Nitro
with Nitro("Windows-VM", introspection=True) as nitro:
self.nitro.listener.set_traps(True)
Here, we have imported the Nitro
class and used it to connect to a
libvirt domain named “Windows-VM”. The optional introspection
argument
indicates that we wish Nitro to create us a suitable analysis back end.
Inside the with
statement, we enable traps. After the traps have been set,
we are ready to start listening for low-level NitroEvent
events from
the virtual machine.
So far, the code doesn’t do anything too interesting as after the traps have been set, Nitro exits gracefully as there is nothing more to do.
Letting the Events Flow¶
Next, lets extend the code to get some events from the target machine. We can
listen for events using Nitro
object’s listen()
method.
Internally, Nitro will use the Listener
it initialized for us.
from nitro.nitro import Nitro
with Nitro("Windows-VM", introspection=True) as nitro:
self.nitro.listener.set_traps(True)
for event in nitro.listen():
print(event.as_dict())
listen()
will give us a stream of NitroEvent
events.
Each event describes a state of the machine when a system call entry or exit
happened. Here, we simply print a representation of each event received. This
should quickly print a lot of data about things happening inside the VM:
{'cr3': '0x493bb000', 'direction': 'enter', 'rax': '0x33', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'exit', 'rax': '0x0', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'enter', 'rax': '0x16', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'exit', 'rax': '0x0', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'enter', 'rax': '0x16', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'exit', 'rax': '0x0', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'enter', 'rax': '0x18d', 'type': 'syscall', 'vcpu': 0}
{'cr3': '0x493bb000', 'direction': 'exit', 'rax': '0x0', 'type': 'syscall', 'vcpu': 0}
Each event line shows the most important properties of the event and, if we
wanted, we could access even more information through the event
object.
Understanding the Data¶
While having all this data is certainly interesting, it does little to help us in our mission to understand what Windows Notepad is doing while we use it to edit text. There is simply too much data with too little useful information for practical uses.
We can remedy the situation by calling in help the analysis back end that Nitro helpfully created for us. Using the Windows analysis back end, we can transform all the low-level events into something more useful.
from nitro.nitro import Nitro
from nitro.libvmi import LibvmiError
with Nitro("Windows-VM", introspection=True) as nitro:
self.nitro.listener.set_traps(True)
for event in nitro.listen():
try:
syscall = nitro.backend.process_event(event)
except LibvmiError:
print("Failed to analyze event :/")
else:
print(syscall.as_dict())
We now invoke back end’s process_event()
method for each
incoming event to see what they are about. The method returns us a
Syscall
event with all the associated information. It is worth noting
that digging around virtual machine’s memory for information about events is a
potentially challenging task that may result in an error. It is a good practice
to catch those. Here, if everything went well, we will print the analysis
result. This should result in something little more understandable.
{'event': {'cr3': '0x11b27000', 'type': 'syscall', 'direction': 'exit', 'vcpu': 0, 'rax': '0x0'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\mcbuilder.exe', 'command_line': 'C:\\Windows\\system32\\mcbuilder.exe', 'iswow64': False, 'create_time': '2017-10-05 02:23:15', 'pid': 1476, 'parent_pid': 1908, 'name': 'mcbuilder.exe'}, 'full_name': 'nt!NtCreateFile', 'name': 'NtCreateFile'}
{'event': {'cr3': '0x11b27000', 'type': 'syscall', 'direction': 'enter', 'vcpu': 0, 'rax': '0x47'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\mcbuilder.exe', 'command_line': 'C:\\Windows\\system32\\mcbuilder.exe', 'iswow64': False, 'create_time': '2017-10-05 02:23:15', 'pid': 1476, 'parent_pid': 1908, 'name': 'mcbuilder.exe'}, 'full_name': 'nt!NtCreateSection', 'name': 'NtCreateSection'}
{'event': {'cr3': '0x11b27000', 'type': 'syscall', 'direction': 'exit', 'vcpu': 0, 'rax': '0x0'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\mcbuilder.exe', 'command_line': 'C:\\Windows\\system32\\mcbuilder.exe', 'iswow64': False, 'create_time': '2017-10-05 02:23:15', 'pid': 1476, 'parent_pid': 1908, 'name': 'mcbuilder.exe'}, 'full_name': 'nt!NtCreateSection', 'name': 'NtCreateSection'}
{'event': {'cr3': '0x11b27000', 'type': 'syscall', 'direction': 'enter', 'vcpu': 0, 'rax': '0x25'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\mcbuilder.exe', 'command_line': 'C:\\Windows\\system32\\mcbuilder.exe', 'iswow64': False, 'create_time': '2017-10-05 02:23:15', 'pid': 1476, 'parent_pid': 1908, 'name': 'mcbuilder.exe'}, 'full_name': 'nt!NtMapViewOfSection', 'name': 'NtMapViewOfSection'}
{'event': {'cr3': '0x11b27000', 'type': 'syscall', 'direction': 'exit', 'vcpu': 0, 'rax': '0x0'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\mcbuilder.exe', 'command_line': 'C:\\Windows\\system32\\mcbuilder.exe', 'iswow64': False, 'create_time': '2017-10-05 02:23:15', 'pid': 1476, 'parent_pid': 1908, 'name': 'mcbuilder.exe'}, 'full_name': 'nt!NtMapViewOfSection', 'name': 'NtMapViewOfSection'}
{'event': {'cr3': '0x493bb000', 'type': 'syscall', 'direction': 'exit', 'vcpu': 0, 'rax': '0x102'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\svchost.exe', 'command_line': 'C:\\Windows\\system32\\svchost.exe -k netsvcs', 'iswow64': False, 'create_time': '2017-10-05 01:52:40', 'pid': 836, 'parent_pid': 452, 'name': 'svchost.exe'}, 'full_name': 'nt!NtWaitForSingleObject', 'name': 'NtWaitForSingleObject'}
{'event': {'cr3': '0x493bb000', 'type': 'syscall', 'direction': 'enter', 'vcpu': 0, 'rax': '0x5c'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\svchost.exe', 'command_line': 'C:\\Windows\\system32\\svchost.exe -k netsvcs', 'iswow64': False, 'create_time': '2017-10-05 01:52:40', 'pid': 836, 'parent_pid': 452, 'name': 'svchost.exe'}, 'full_name': 'nt!NtPowerInformation', 'name': 'NtPowerInformation'}
{'event': {'cr3': '0x493bb000', 'type': 'syscall', 'direction': 'exit', 'vcpu': 0, 'rax': '0x0'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\svchost.exe', 'command_line': 'C:\\Windows\\system32\\svchost.exe -k netsvcs', 'iswow64': False, 'create_time': '2017-10-05 01:52:40', 'pid': 836, 'parent_pid': 452, 'name': 'svchost.exe'}, 'full_name': 'nt!NtPowerInformation', 'name': 'NtPowerInformation'}
{'event': {'cr3': '0x493bb000', 'type': 'syscall', 'direction': 'enter', 'vcpu': 0, 'rax': '0x5c'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\svchost.exe', 'command_line': 'C:\\Windows\\system32\\svchost.exe -k netsvcs', 'iswow64': False, 'create_time': '2017-10-05 01:52:40', 'pid': 836, 'parent_pid': 452, 'name': 'svchost.exe'}, 'full_name': 'nt!NtPowerInformation', 'name': 'NtPowerInformation'}
{'event': {'cr3': '0x493bb000', 'type': 'syscall', 'direction': 'exit', 'vcpu': 0, 'rax': '0x0'}, 'process': {'path': '\\Device\\HarddiskVolume1\\Windows\\System32\\svchost.exe', 'command_line': 'C:\\Windows\\system32\\svchost.exe -k netsvcs', 'iswow64': False, 'create_time': '2017-10-05 01:52:40', 'pid': 836, 'parent_pid': 452, 'name': 'svchost.exe'}, 'full_name': 'nt!NtPowerInformation', 'name': 'NtPowerInformation'}
We can see that each event has been coupled with information about the process that caused it. We are already getting pretty close to our goal!
Looking for a Notepad¶
Now we have everything we need to spot the bits of data that interest us. In this case, we are interested in what Windows Notepad is doing when we open it.
from nitro.nitro import Nitro
from nitro.libvmi import LibvmiError
with Nitro("Windows-VM", introspection=True) as nitro:
self.nitro.listener.set_traps(True)
for event in nitro.listen():
try:
syscall = nitro.backend.process_event(event)
except LibvmiError:
print("Failed to analyze event :/")
else:
if syscall.process.name == "notepad.exe":
print(syscall.as_dict())
To find out Notepad related events we simply inspect the process property associated with the event.