The main feature of the 1.0.0 version of the Profiler is ready and thus it won’t take long for the new version to be released. This post serves as introduction to the topic of logic providers and can in no way cover all the ground.
Logic providers are a new type of extension quite similar to hooks: the callbacks are named the same. Their purpose however is different. Hooks are very powerful, but their purpose is to modify the behavior of generic scans. Logic providers, on the other hand, tell the scan engine which folders to scan, which files, etc.
Let’s take a look at a small logic provider. The ‘logicp.cfg’ entry:
[MissingSecFlags] label = Missing security flags descr = Perform a scan inside system and application directories searching for Portable Executables which lack certain security related flags. file = missingsecflags.py init = init scanning = scanning
And the ‘missingsecflags.py’ file:
def init(): from Pro.Core import proCoreContext s = proCoreContext().getSystem() s.addPath("C:\\Windows") s.addPath("C:\\Program Files") s.addPath("C:\\Program Files (x86)") return True def scanning(sp, ud): if sp.getObjectFormat() == "PE": obj = sp.getObject() # exclude .NET files if obj.DotNETDirectory().IsValid() == False: # check NX_COMPAT and DYNAMIC_BASE flags sp.include((obj.OptionalHeader().Num("DllCharacteristics") & 0x140) != 0x140) return sp.exclude()
Let’s now take a look at the home view in the Profiler.
As you can see, there’s an additional scan button which belongs to the logic provider we’ve just added. The icon can be customized from the cfg file by specifying an ‘icon’ field (the path is relative to the media folder).
So let’s take a closer look at the code above. When the user clicks the scan button, the init function of our logic provider will be called.
def init(): from Pro.Core import proCoreContext s = proCoreContext().getSystem() s.addPath("C:\\Windows") # ... return True
The init function calls getSystem (which returns a CommonSystem base class). This class can be used to initialize the scan engine. By default the system will be initialized to LocalSystem. A logic provider can even create its own system class and then set it with setSystem. As introduction it’s not useful to inspect all the methods in CommonSystem and every possible use, we leave that for future posts. In this simple case it’s not necessary to implement anything complex, we are performing a scan on the local system and so it’s enough to call the addPath method on the default class returned by getSystem.
The function then returns the True value. It can also return False and abort the scan operation. Any other value will be passed as user argument to other callbacks such as: scanning, scanned and end.
That’s a small difference between hooks and logic providers: the init function in hooks can’t abort a scan operation. Another difference is that while hooks don’t have mandatory callbacks, the init function is mandatory for logic providers, since, without it, nothing gets done. Logic providers start their own scanning operations, while hooks just attach to existing operations (even those created by logic providers).
The scanned function has the same syntax as in hooks and doesn’t require an additional explanation. The only thing worth mentioning is that hooks can be selectively called based on the file format (see formats field). This isn’t true for logic providers: their scanning/scanned callbacks will be called for every file. The logic providers API is recent and not written in stone, so it might very well be that in the future a filtering mechanism for formats will be provided for them as well.
As for the content of the scanned function, it just checks two security related flags inside of a Portable Executable and includes in the final report only those files which miss one or both flags.
The scan could be made more useful to check for specific things like COM modules which miss the ASRL flag and things like that.
Also the extension doesn’t really fully benefit from the advantages brought by logic providers: it could as well be implemented as a hook, perhaps it would be even better. In this case the only advantage it provides is a shortcut in the home view for the user.
An important aspect of logic providers is that the Profiler remembers which logic providers have been used to create a report and calls their rload callback when loading that report. The rload callback exists even for hooks, but for them it’s called in any case provided the hook is enabled. It’s important to remember that the identifying name for logic providers is the value contained between brackets in the cfg file. If it’s changed, the Profiler won’t be able to identify the logic provider and print an error message in the output view.
Since this version of the Profiler also exposes its internal SQLite implementation, it’s now possible to access the internal database (main.db):
from Pro.Core import proCoreContext db = proCoreContext().getReport().dataBase() # returns the internal SQLite handle, never to be closed!
Another useful method exposed by the Report class is retrieveFile:
c = proCoreContext().getReport().retrieveFile("C:\\somefile") # returns a NTContainer object
The retrieveFile method retrieves a file based on its name either from the project or from the disk.
Using all these features in conjunction a typical scenario for a logic provider would be:
- The init callback is called and initializes the scan engine.
- Information is collected either from the scanning or scanned callback, depending on the needs.
- The end callback stores the collected data in the main database via the provided SQLite API.
- The rload callback retrieves the collected data from the main database and creates one or more views to display it to the user.
As already mentioned this post covers only the basics and we’ll try to provide more useful examples in the future.