Showing posts with label Python. Show all posts
Showing posts with label Python. Show all posts

Friday, February 21, 2025

OneDrive Microsoft.FileUsageSync.db

I recently started to look into the Microsoft.FileUsageSync.db. The database can be found in %localappdata%\Microsoft\OneDrive\ListSync\Business<1-9>\settings. It is not documented in OneDrive Evolution because it only appears in OneDrive for Business. OneDrive Evolution's data is collected from personal only. It's not known what version this database first appeared in. Just like Microsoft.ListSync.db, this database is used by Microsoft.SharePoint.exe but is not related to the Offline Mode for web feature that I am aware of. There is some interesting data in the recent_files_formatted_spo table. The FormattedValue column holds JSON data that isn't the prettiest to look at.

To make the data easier to read, I wrote the following script to convert the JSON data into CSV format.

import sqlite3
import pandas as pd
import json

db_path = "Microsoft.FileUsageSync.db"

conn = sqlite3.connect(db_path)

query = "SELECT FormattedValue FROM recent_files_formatted_spo"

df = pd.read_sql_query(query, conn)

conn.close()


def parse_json(value):
    try:
        value = value.encode().decode('unicode_escape')

        return json.loads(value)
    except Exception as e:
        print("JSON Parse Error:", e)
        return None


df_parsed = df["FormattedValue"].apply(parse_json)

df_expanded = pd.json_normalize(df_parsed.dropna())

df_expanded.to_csv('output.csv', index=False, encoding='utf-8')

So what type of data does this table hold? Unfortunately, I cannot show you the data because I don't have a development environment so I'll do my best to explain what I found.

To give you an idea, when the data is parsed out, we have the following headers:
file.Id, file.@odata.id, file.FileModifiedTime, file.LastModifiedDateTime, file.FileCreatedTime, file.FileExtension, file.FileSize, file.StorageProviderContext, file.IsEmptyCopy, file.SharePointItem.SiteId, file.SharePointItem.WebId, file.SharePointItem.ListId, file.SharePointItem.UniqueId, file.ItemProperties.Shared.LastSharedWithMailboxOwnerByDisplayName, file.ItemProperties.Shared.LastSharedWithMailboxOwnerBySmtp, file.ItemProperties.Shared.LastSharedWithMailboxOwnerDateTime, file.ItemProperties.Shared.SubjectProperty, file.ItemProperties.Shared.AttachmentItemReferenceId, file.ItemProperties.Shared.AttachmentReferenceId, file.ItemProperties.Shared.ImmutableFileItemReferenceId, file.ItemProperties.AggregatedActivities.LastUserActivityDateTime, file.ItemProperties.AggregatedActivities.LastModifiedDateTime, file.ItemProperties.AggregatedActivities.MailboxOwnerTopInsights, file.ItemProperties.AggregatedActivities.IsHidden, file.ItemProperties.SemanticProperties.Title, file.UserRelationship.LastSharedDateTime, file.Visualization.Title, file.Visualization.AccessUrl, file.Visualization.Type, file.AllExtensions.SharingHistory.Instances, file.FileName, file.SharePointOnlineFacetStatus, file.Document.Title, file.WorkingSetId, activity.message_format, activity.type, activity.users, activity.timestamp, activity.extended_info.subject, file.UserRelationship.LastSharedById, file.Document.Author, file.SharePointItem.ModifiedBy, file.PrimaryItemLocation, file.SharePointItem.ContentClass, file.SharePointItem.SitePath, file.ItemProperties.Default.SiteTemplateId, activity.extended_info.sharing_medium, file.Visualization.ContainerTitle, file.Visualization.ContainerUrl, file.Visualization.PreviewImageUrl, file.FileOwner, file.SharePointItem.ContentTypeId, file.SharePointItem.ListItemId, file.SharePointItem.DocId, file.SharePointItem.ModifiedByDisplayName, file.SharePointItem.FileUrl, file.SharePointItem.ParentId, file.ItemProperties.Default.AuthorOWSUSER, file.ItemProperties.Default.EditorOWSUSER, file.ItemProperties.Default.DocumentLink, file.ItemProperties.AggregatedActivities.MailboxOwnerHistograms, file.ItemProperties.ClientAccessByMailboxOwner.LastAccessDateTime, file.ItemProperties.SemanticProperties.Url, file.ItemProperties.SemanticProperties.ContainerName, file.ItemProperties.SemanticProperties.ContainerUrl, file.UserRelationship.FrequentlyUsedSiteWeight, file.UserRelationship.LastAccessDateTime, file.ItemProperties.Default.ProgID, file.ItemProperties.Shared.TeamsMessageThreadId, file.ItemProperties.Direct.ColorHex, file.UserRelationship.LastModifiedDateTime, file.ItemProperties.Default.RecordingStartDateTime, file.ItemProperties.Default.RecordingEndDateTime, file.ItemProperties.Default.MeetingOrganizerId, file.ItemProperties.Default.MeetingICalUid, file.ItemProperties.Default.BaseType, file.ItemProperties.Default.ListTemplateTypeId, file.ItemProperties.Default.ListIcon, file.ItemProperties.Default.ListColor, activity.extended_info.navigation_id

It appears to hold information on files that are not necessarily in your OneDrive, but files that are shared from OneDrive. This can include files that were shared to you via email, Teams, and whiteboards to name a few.

Another interesting table is recommended_files. This table appears to hold a max of 20 files. One of the things that stood out to me was a description in the JSON data. The description is the first couple lines of the file so it could give us a good indication of what the file contains.

The last table I want to talk about is top_collaborators. This one holds information on people the user interacts with the most. We could potentially glean work relationships from this data.

The plan is to add this data into OneDriveExplorer once I can get it sorted out. Until then, use the script to explore this sure to be valuable forensic resource.

Monday, May 3, 2021

SEPparser Released

What is SEPparser?

SEPparser is a command line tool examine artifacts from Symantec Endpoint Protection (SEP). SEPparser can be ran against a single file, directory, dead box system (write-blocked hard drive of mounted collection), or for live response.


Capabilities

  • Parse settings for log files
  • Parse the following log files:
    • Security log
    • System log
    • Firewall Traffic log
    • Firewall Packet log
    • Application and Device Control log
    • AV Management plugin log
    • Daily AV logs
  • Extract packets from Firewall Packet log
  • Parse ccSubSDK database into csv reports
  • Extract potential binary blobs from ccSubSDK
  • Parse VBN files into csv reports
  • Extract quarantine data to file or hex dump
  • Preform hex dump of VBN for research

Using SEPparser

SEPparser can be ran on Windows or Linux. Running SEPparser by itself shows all the available options.



















There are quite a few options, but it is straight forward to use.


Basic usage

To run SEPparser, all you need to do is point it to a file (-f) or a directory (-d) and SEPparser will take care of the rest. Output will be stored in the current directory SEPparser is ran from. This can be changed using OUTPUT (-o) option.



















But what if we don't know the location of the Symantec files? The -d option can be used at the base directory and all files will be scanned from that path recursively. To speed up the process, or if we are using a script, we can use KAPE mode (-k) in conjunction with -d. What this does is SEPparser will only scan files in locations where SEP data is stored instead of every file.

If we want to append data to output files that SEPparser already created, the append (-a) option can be used.

Once SEPparser is finished a series of csv files will be created.













Time Zones

Some of the time stamps in SEP's data are in UTC and others are recorded in the time zone set on the device they came from. There are a couple of ways to get all the time stamps to be in UTC.  

  1. If the registrationInfo.xml file is found during the scan; the offset will be automatically applied.
  2. The -r option can be used to point to the location of the registrationInfo.xml file so the offset can be automatically applied.
  3. The -tz option can be used to manually enter a time zone offset.


Logging

SEPparser has a logging feature (-l) that can be used to save the console output to a log file. This can be useful to check for errors during parsing. If an error occurred, the -v option can be used to get a more verbose output of what went wrong.


Quarantine Files (VBN)

When it comes to quarantine files, SEPparser has some additional features that can be useful.  

SEPparser has the ability to extract (-e) the quarantined data or it can dump the data to the console in hex format with the -qd option.
















SEPparser can also produce a hex dump of the VBN itself. While SEPparser does a rather excellent job of parsing VBN's into the csv report, there is still data that it cannot. There are some parts of the VBN format that are unknown. The hex dump can help researchers to understand and figure out what these unknown parts of the file format mean.










SEPparser also contains hash-file (-hf) option. This can be used when parsing VBN files for reports. Because there can be extra data in the VBN file, the hash reported is not always the hash of the actual file. With the -hf option, SEPparser will record the MD5, SHA1, and SHA256 of the actual quarantined data.


ccSubSDK Database

SEPparser has an extract-blob (-eb) option that can be used when parsing the ccSubSDK database. With this option enabled, SEPparser will extract anything that could be an executable contained in the ccSubSDK database.















Packets

SEPparser has one more trick up its sleeve. When parsing the raw.log (packet log), SEPparser will extract the packets from the log into a text file. This text file can then be loaded into a tool, like Wireshark, to examine the packets. SEP only captures the headers and not the data associated with it.







































There is a public GitHub repository, located at https://github.com/Beercow/SEPparser, containing SEPparser and a wiki with the file formats for the SEP artifacts. KAPE also includes targets and modules for Symantec Endpoint Protection and SEPparser. If you find any errors or would like to contribute, issues/pull requests are always welcome. 

Wednesday, May 16, 2018

ProcDOT GeoIP plugin

Today I would like to introduce to you my first event handler plugin. The plugin is designed to run after you click on the refresh button in ProcDOT. You will need an Internet connection on first run because GeoIP needs to download the MindMax databases to get the location information on the IP address. The GeoIP information is then added to the details view on a server node.

Details view without GeoIP plugin

Details view with GeoIP plugin

There is a pretty interesting side effect that I happened to come across. The plugin also creates variables that you can call with other plugins.

Variables without plugin

Variables with plugin

Because of this discovery, I currently developing a clone of Christian's Server List plugin that includes the GeoIP information. 

GeoIP binaries can be found here for easy install.











Tuesday, July 18, 2017

ProcDOT plugin writing. Part 4 - Context Menu and CanBeVerified

Throughout this tutorial, we learned how to write a plugin for the the Main Menu. Writing a plugin for the context menu isn't any different. There are a couple of options available for the context menu I would like to touch on though to help make your plugin a little more professional.

Context Menu plugins allow us to get more granular with the data we are after. Let's say we want a context menu item that is only available when we right click on a server node. The plugin engine offers this feature though the CanBeVerified switch.

Before we continue, lets alter our  cmd_line plugin so it becomes a Context Menu Item instead of being in the Main Menu. Open the cmd_line.pdp file in an editor and change the type from MainMenuItem to ContextMenuItemForGraph. Your pdp file should now look like this:

Name = cmd
Author = <your name>
Description = Open cmd prompt from ProcDOT's Main Menu
Version = 1
Type = ContextMenuItemForGraph
Architecture = WindowsBatchScript
File = cmd_line.bat
Priority = 9
RunHidden = 1
RunExclusively = 1
CanOverrideOtherPlugins = 0
CanOverrideProcdot = 0

Restart ProcDOT and load your graph again. If we right click anywhere on the graph, we should see cmd in the context menu. What we are trying to accomplish though is having a plugin show up if we right click on a server node. Lets right click on a server node an see what we need to do to have this happen.


In the command prompt enter the following:

 set | find /i "procdot"

You will notice that there are a lot more variables to choose from. Remember, this type of plugin allows us to get granular with what we are doing.


Looking through our list of options, it looks like PROCDOTPLUGIN_CurrentNode_name would be a good candidate for what we are doing. Notice it is telling us that this is a server node. With this information, we can try to get our  plugin to only show up if we right click on a server node.

To do this, we first have to set the CanBeVerified switch to 1 in our pdp file (add CanBeVerified = 1 to the end of the file). Lets stop ProcDOT and create our verify plugin. We'll start by creating a new pdp file called verify.pdp with the following content (Notice it has the CanBeVerified switch).

Name = Verify
Author = <your name>
Description = verify test
Version = 1
Type = ContextMenuItemForGraph
Architecture = WindowsBatchScript
File = verify.bat
Priority = 9
RunHidden = 0
RunExclusively = 1
CanOverrideOtherPlugins = 0
CanOverrideProcdot = 0
CanBeVerified = 1

RunHidden has also been set to 0 so we can see additional output. We can clean this up after our plugin is complete. When we set the CanBeVerified switch, a new ProcDOT variable called PROCDOTPLUGIN_VerificationRun is created and its initial value is set to 1. If the criteria for the verification passes, (in our case, is it a server node), this value will be set to 0 indicating the verification passed. If not, the value will remain 1. This will be a little easier to explain if I give you the code for the plugin and go through each part. Create a file called verify.bat with the following content:

 @setlocal enabledelayedexpansion && python -x "%~f0" %* & exit /b !ERRORLEVEL!

import os
import sys
verify = os.getenv('PROCDOTPLUGIN_VerificationRun')
   
if os.getenv('PROCDOTPLUGIN_VerificationRun') == '0':
    pass
   
else:   
    if os.getenv('PROCDOTPLUGIN_CurrentNode_name')[:6] == 'SERVER':
        print 'PROCDOTPLUGIN_VerificationRun = ' + verify
        raw_input('Yes. This is a server node.')
        sys.exit(1)
    else:
        print 'PROCDOTPLUGIN_VerificationRun = ' + verify
        raw_input('No. This is not a server node')
        sys.exit(0)

def main():
    print 'PROCDOTPLUGIN_VerificationRun = ' + verify
    raw_input('Verification complete.')

if __name__ == '__main__':
    main()

Lets take a closer look at what is going on.

if os.getenv('PROCDOTPLUGIN_VerificationRun') == '0':
    pass

This part of the code is telling the plugin, that if everything is verified, to skip or "pass" everything else and go to the main function. Remember, PROCDOTPLUGIN_VerificationRun is initially set to 1, so we are going to have to create a condition to set it to 0. this is were the next part o f the code comes into play.

else:   
    if os.getenv('PROCDOTPLUGIN_CurrentNode_name')[:6] == 'SERVER':
        print 'PROCDOTPLUGIN_VerificationRun = ' + verify
        raw_input('Yes. This is a server node.')
        sys.exit(1)
    else:
        print 'PROCDOTPLUGIN_VerificationRun = ' + verify
        raw_input('No. This is not a server node')
        sys.exit(0)

This  part of the code is responsible for verifying a condition for our plugin. In the if statement, we are using  the PROCDOTPLUGIN_CurrentNode_name variable to check if we are right clicking on a server node. If this is true, the plugin sets the exit code to 1. This will tell ProcDOT to change PROCDOTPLUGIN_VerificationRun to 0. If it is not a server node, the plugin will run the else statement and set the exit code to 0, leaving PROCDOTPLUGIN_VerificationRun set to 1. The print and raw_input statements are there for our debugging purposes so we can see what the plugin is doing.

After doing this check, if it is a server node, ProcDOT will set  PROCDOTPLUGIN_VerificationRun to 0 and initialize our plugin. Our plugin can now run the rest of its code under main. Lets continue and see it in action. Start ProcDOT and  refresh your graph.

The first thing we are going to do is right click on a server node. When we do this, you should see the following command prompt come up:


This is the place in the code we are at now:

    if os.getenv('PROCDOTPLUGIN_CurrentNode_name')[:6] == 'SERVER':
        print 'PROCDOTPLUGIN_VerificationRun = ' + verify
        raw_input('Yes. This is a server node.')
        sys.exit(1)

From here, hit enter. You will see the same command prompt come up again (ProcDOT does a double check for some reason). Hit enter one more time and then you should see the verify entry in the context menu.


If we left click on Verify, we should be presented with the following command prompt:


This is the place in the code we are at now:

def main():
    print 'PROCDOTPLUGIN_VerificationRun = ' + verify
    raw_input('Verification complete.')

if __name__ == '__main__':
    main()

Hit enter to clear the command prompt. So far it seems to be working. Lets do one more test to make sure it only shows up when we click on a server node. Now, right click anywhere except for on a server node. You should see the following command prompt:





This is the place in the code we are now:

else:
        print 'PROCDOTPLUGIN_VerificationRun = ' + verify
        raw_input('No. This is not a server node')
        sys.exit(0)

Hit enter, and then hit enter again. If everything worked, Verify should not show up in the context menu:
As you can see, you can set conditions for when your plugin will show up in the context menu. This is not just for a server node, this can be applied to any conditions you want met. All that you need to do to give it some function is to add your code to the main function. I hope I did a good job of explaining how this works. It can seem a little confusing at first.

Tuesday, June 6, 2017

ProcDOT plugin writing. Part 3 - Creating a Main Menu plugin

In the last two posts (Part 1, Part 2), we created a simple plugin and explored some of the files that ProcDOT stores data in. We will now leverage both of these to create a plugin that lists the servers in the graph. This is not a tutorial on python but, I will try to explain some of the plugin to show where the information is coming from. Yes, I know, Christian has already made this plugin. But, by looking at this plugin, it helped me to figure some of this stuff out when I first started out writing plugins. If you remember from part 1, there was a reason I liked to use python. By rewriting this plugin in python, I only have to maintain one plugin (the serverslist plugin has a batch script for Windows and a bash script for Linux).

We'll start out by firing up ProcDOT and generate our graph.Looking at the graph, I have eight different server nodes (yours might be different). Make note of the servers listed.


Right click on one of the server nodes and select details.




Looking at the details for a server node, we can see there are five different keys and values. With this information, we can start to build our plugin.Go to the Plugin menu and launch the cmd plugin. So, we now know the information we are after is in the details file. If we type set | find /i "procdot" in the cmd prompt, we can see the variable that we will have to call is PROCDOTPLUGIN_GraphFileDetails. If we want to display output, we will also have to call PROCDOTPLUGIN_ResultCSV or PROCDOTPLUGIN_ResultXML because we are going to create a table. We will create our plugin output with PROCDOTPLUGIN_ResultCSV for this example.

From here, drop into a python shell by typing python and hit enter.


We will need to import os into the python shell so we can get our ProcDOT variables and assign them in our plugin.After that, we will create some variables for our key data we are after.


Next we will open the details file in python and search it, line by line, for the Domain keys. For every hit we get on Domain, we'll have python print it out.


Hmmm. Looks like we are getting back the Domain key and then some. Looking at the format of the file, we can split the lines apart with a space. This will split the line into three parts. We can go back and search the first part for Domain and try it again.


Now we are getting somewhere! But there is another problem. Not every server node has a Domain tied to it. If we look back, we can see that we can also search for the IP-Address. I we assign these to our variables, we can print these together.


Looks good. We can now identify a server node by either its Domain or IP-Address. Wait a minute though. My graph had only eight server nodes in it. Lets list out the rest of our keys and see if we can narrow this list down.


So, looking at the output, we can see that the server nodes that are in the graph also contain an entry in the  RelevantBecauseOfProcmonLines key. And the server nodes that are marked yes in the OnlyInPCAP key are also in the graph. Lets parse this out a little more and strip out what we don't before we write our actual plugin. We'll reset some of our variables and finish this up.


Nice! We can now write our plugin. In order for our output to come out right, we will have to refer to the ProcDOT documentation to make sure the result csv is properly formatted.



So looking at this, the first line of the file contains the headers surrounded in quotation marks and separated by commas. The next line is the column widths, then finally our data. We are only going to have headers for the Domain and the IP-Address. We'll add some style by marking the server nodes that are only in the pcap with blue lettering. With our plan in place, we can create the plugin and the pdp file.

server.pdp

Name = server
Author = <your name>
Description = Open cmd prompt from ProcDOT's Main Menu
Version = 1
Type = MainMenuItem
Architecture = WindowsBatchScript
File = server.bat
Priority = 9
RunHidden = 1
RunExclusively = 1
CanOverrideOtherPlugins = 0
CanOverrideProcdot = 0

server.bat

@setlocal enabledelayedexpansion && python -x "%~f0" %* & exit /b !ERRORLEVEL!
import os

def main():

    data = os.getenv('PROCDOTPLUGIN_GraphFileDetails')
    out = os.getenv('PROCDOTPLUGIN_ResultCSV')
    outfile =open(out, 'w')
    domain = None
    ip = None
    onlyinpcap = None
    procmon = None

    outfile.write('"Domain","IP-Address"\n')
    outfile.write('"*","*"\n')

    with open(data) as f:
        for line in f:
            c = line.split(' ', 2)
            if c[0] == 'Domain':
                domain = ''.join(c[2:]).strip()
            if c[0] =='IP-Address':
                ip = ''.join(c[2:]).strip()
            if c[0] == 'OnlyInPCAP':
                onlyinpcap = ''.join(c[2:]).strip()
            if c[0] == 'RelevantBecauseOfProcmonLines':
                procmon = ''.join(c[2:]).strip()
                if domain != ip:
                    if procmon != '':
                        outfile.write('"' +domain + '","' + ip + '"\n')
                    if onlyinpcap == 'Yes':
                        outfile.write('{{color:blue}}' + '"' + domain + '","' + ip + '"\n')
                           
if __name__ == '__main__':
    main()

Save these to your plugin folder restart ProcDOT and generate a graph. From the Plugin menu, select server and you should see the results.



Success! We made a plugin to display all the servers in the graph. Before we conclude, the cmd plugin can be used to troubleshoot our plugin. Lets say we made a typo in our plugin and no results were returned. We can't see any of the error messages to see what happened. To test this out, open the server.bat file and change domain = None to dmain = None and save the file. If we run the server plugin again our results come back empty.


We don't know why because we cannot see the errors. Close out the results and launch the cmd plugin from the Plugin menu. From this command prompt, we can run the server plugin manually. Type server.bat in the command prompt and hit enter.


From the output, we can see the error UnboundLocalError: local variable 'domain' referenced before assignment. If we edit server.bat back to domain = None and save again, we can run the plugin manually and see that there are no more errors. Pretty neat!


Our simple cmd plugin turns out to be rather useful for writing and troubleshooting plugins. Now that we have a way to better develop our plugins, we will create a plugin for the context menu in the next tutorial.

Tuesday, May 30, 2017

ProcDOT plugin writing. Part 1 - Creating your first plugin.

ProcDOT is a malware analysis tool created by Christian Wojner (CERT.at - CERT Austria). The tool is designed to correlate Procmon logs and PCAP data. ProcDOT takes this data and lets you visualize the information in a graph loaded with useful information. It also contains a simple, yet powerful plugin engine designed to help analysts extend the capabilities of ProcDOT. More information can be found at the ProcDOT website.

I decided to write these tutorials to help others with the creation of plugins for ProcDOT. My hope is that these tutorials will help with some of the frustration and issues I have had when creating plugins. For these tutorials, we need to have our system setup properly.

Most, if not all, of my plugins are written in python. Why python? I wanted to have a language that could be used on both Windows and Linux so I did not have to write separate plugins for each. This also helps with maintaining plugins because if you can get it to work on one system, it should work on the other, for the most part.

Another thing to note. I also try to stick with LinuxShellScript and WindowsBatchScript for the architecture. This is so I don't need to create different pdp files for 32 and 64 bit systems.

I'm not going to go into how to setup ProcDOT. If I have to explain that then you probably shouldn't be reading this yet.

For these tutorials, I will be using Windows, ProcDOT 1.2 [Build 55u], and python2.7. You can download python2.7 here. When installing python, make sure the executable is in the system path. You need to be able to call python from the command line for these tutorials to work.

In this post, we will create our first plugin. As we go through other tutorials, this plugin will come in handy for troubleshooting and plugin development.
 
ProcDOT's plugins consist of two parts:
  • The plugin descriptor file
  • The plugin itself
The plugin descriptor file contains the information that ProcDOT needs to be able to handle the plugin. Simply put, it is a configuration file for the plugin itself. The descriptor file must have the extension ".pdp" (Note: it must be lower case on a linux system or it will not be read).

The following table lists the keynames and their uses for the descriptor file:

Keyname Description Possible values Supported plugin types
Name Specifies the name of the plugin. [Ascii] all
Author Specifies the name of the author. [Ascii] all
Description Specifies a description for the plugin. [Ascii] all
Version Specifies the version of the plugin. [Ascii] all
Type Specifies the type of the plugin.
Currently 3 types are supported:
- ContextMenuItemForGraph ... The plugin is available through the context menu that popps up when the right mouse button is pressed while hovering the canvas area of the graph.
- EventHandler ... The plugin is automatically registered (and therefore called) as an eventhandler for the given event constant(s) via "Event = " or "Events = ".
- MainMenuItem ... The plugin is available through the main menu item "Plugins". Currently there can exist 9 main menu item plugins in maximum.
ContextMenuItemForGraph
EventHandler
MainMenuItem
all
Architecture Specifies the architecture the plugin was developed for. Linux32BitExe
Linux32BitSo
Linux64BitExe
Linux64BitSo
LinuxShellScript
Windows32BitDll
Windows32BitExe
Windows64BitDll
Windows64BitExe
WindowsBatchScript
all
File Specifies the file to be used as the actual plugin. [Filename] all
Priority Specifies the position of the actual plugin in case of multiple matching plugins from 1 to 9 with 1 being the highest priority and 9 being the lowest. 1 - 9 all
RunHidden Tells ProcDOT to run the actual plugin hidden hennce preventing any windows from popping up. (1 = True, 0 = False) 1 / 0 all
RunExclusively Tells ProcDOT to not run any other non-exclusive plugin in parallel to this one. (1 = True, 0 = False) 1 / 0 all
CanOverrideOtherPlugins Specifies that if the actual plugin exits with a return code of 1 any other plugin being in the queue waiting to run will be skipped. (1 = True, 0 = False) 1 / 0 all
CanOverrideProcDot Specifies that if the actual plugin exits with a return code of 1 any following builtin functionality of ProcDOT will be skipped. (1 = True, 0 = False) 1 / 0 all
CanBeVerified Specifies that this plugin can also be called in verification mode (Environmentvariable PROCDOTPLUGIN_VerificationRun) to decide if it's able to handle the hovered node or situation. (1 = True, 0 = False)
The actual plugin needs to exit with a return code of 1 to signal that it's able to properly handle an according "real" call.
1 / 0 all
Event
or
Events
Specifies the event (or events seperated with colons) for which the actual plugin shall be registered for as an event handler. BeforeLeftClickOnGraph
BeforeLeftDblClickOnGraph
BeforeMiddleClickOnGraph
BeforeMiddleDblClickOnGraph
BeforeRightClickOnGraph
BeforeRightDblClickOnGraph
AfterLeftClickOnGraph
AfterLeftDblClickOnGraph
AfterMiddleClickOnGraph
AfterMiddleDblClickOnGraph
AfterRightClickOnGraph
AfterRightDblClickOnGraph
BeforeRefresh
AfterRefresh
BeforeProcmonButtonClick
AfterProcmonButtonClick
BeforeWindumpButtonClick
AfterWindumpButtonClick
BeforeLauncherButtonClick
AfterLauncherButtonClick
BeforeSwitchToFrameMode
AfterSwitchToFrameMode
BeforeSwitchToNormalMode
AfterSwitchToNormalMode
BeforeTimelineFirstFrame
AfterTimelineFirstFrame
BeforeTimelinePreviousFrame
AfterTimelinePreviousFrame
BeforeTimelineNextFrame
AfterTimelineNextFrame
BeforeTimelineLastFrame
AfterTimelineLastFrame
BeforeTimelinePlayAnimation
AfterTimelinePlayAnimation
BeforeTimelineStopAnimation
AfterTimelineStopAnimation
EventHandler
Figure 1. From ProcDOT's documentation.

Let's begin by making a plugin for the Main Menu that opens the Windows command prompt. Although this plugin will not be very useful right now, it will be in later posts to help write other plugins. Back to the descriptor file.

Create a file named cmd_line.pdp in the ProcDOT plugin folder. If you do not have this folder, it can be created in the same folder the ProcDOT executable is located in. Inside this file we will place the following:

Name = cmd
Author = <your name>
Description = Open cmd prompt from ProcDOT's Main Menu
Version = 1
Type = MainMenuItem
Architecture = WindowsBatchScript
File = cmd_line.bat
Priority = 9
RunHidden = 0
RunExclusively = 1
CanOverrideOtherPlugins = 0
CanOverrideProcdot = 0

Now that we have our descriptor file, we can start to create our plugin. Notice in the descriptor file, File = cmd_line.bat. This will be our plugin. In the plugin directory, create a file called cmd_line.bat and place the following inside:

@setlocal enabledelayedexpansion && python -x "%~f0" %* & exit /b !ERRORLEVEL!
#!/usr/bin/env python

import os

os.system("start /wait cmd /K")

Lets break down what what this plugin will do:

Because we are calling our plugin in a batch file, the first line, @setlocal enabledelayedexpansion && python -x "%~f0" %* & exit /b !ERRORLEVEL!, calls python and executes the rest of the script through the python interpreter. This is so we do not have to associate .py files with python itself.

The rest of the lines are our python code to open the command prompt.

Lets fire up ProcDOT and see if our new plugin works.
When we click on the Plugins menu, there should be an entry called cmd.



Lets double check to make sure there are no errors in out plugin. We can do this by clicking on the Plugins-Manager. If there are not any errors, your screen should look like the one below.


We can also view the details of the plugin by clicking on the plugin from the Plugins-Manager menu.



Time to load up a pcap and a procmon trace to see if everything is working with the plugin Once the graph is generated, open the Plugins menu and select cmd. If everything worked you should have a command prompt open on your screen. If we type set | find /i "procdot" into this cmd prompt, we should see a list of ProcDOT variables to work with.



Success! We have created our first plugin.

In the upcoming post, we will use this plugin to create and troubleshoot other plugins.