SysNOP is a windows system driver that filters calls from user mode to kernel mode. It hooks the Sysenter/Syscall and Int 2E addresses to temporarily reroute all user to kernel mode access through it's filter function. This allows SysNOP to use a filter rule set to allow or deny system calls based on stack content and caller's process state. What does this mean exactly? SysNOP can check the arguments on the stack that is passed to the system call, the thread or process ID, the Kernel Process name and even pointers on the stack to user mode data.
It can also temporarily unlink a process from the kernel's process linked list to hide a process from standard enumeration in user mode, as well as hiding and showing visible windows for processes. This means you can hide the fact that the process is running from other applications that search for the process - like some malicious code that searches for your virus scanner and ends the process before doing other things to your system. The drawback to this is that your system will bluescreen if you or anyone else close the application before it's added back to the process list. (Made visible again.) Eventually I will add in the ability to have the program automatically redisplayed when the process is closed, but I haven't gotten to it yet.
Normally, systems are secure with proper access controls. But what happens when you have a service that is being provided has a vulnerability that allows for exploitation and privelege escalation? It is likely that your system will be compromised, even with the best of access controls, not because you were complacent, but because the person who wrote the software wasn't as thorough as they should have been, or assumed that the vulnerability wouldn't be found due to being difficult to accomplish. And worse, you could then have a system with a rootkit that has dug in to your system and hidden itself well, or blocked you from being able to remove it easily without taking the system down for some time to clean it. Or you are logged in as an admin account and you go to a web page to download new drivers or searching for answers to some software or hardware issue, and while you are navigating, an exploit in some other unrelated program allows your system to be compromised.
It gives you control of system calls to kernel mode. It helps to keep problems in user mode out of the kernel, or slow down/stop user mode problems regardless of the access controls the system thinks the caller has. Take filtering "ntLoadDriver" as an example. It is relatively easy to make a list of drivers that are allowed to be loaded through user mode - loading these up into a list of allowed calls, and blocking the rest means that it makes it is more difficult for the hostile application to get to kernel mode. It is not a fix to every possible entry, but it does make the most common entry difficult. Or filtering "ntLoadDriver" by calling application name, based on kernel's process name, then filtering "ntCreateProcessEx" to disallow launching applications with names that match the specific processes from spots other than the accepted location. These are two easy examples of methods of system protection. Since you can filter any system call, you can control most important things the system can do. Another example: "SetWindowsHookEx" allows processes to hook into messages for other applications. It also allows hostile programs to inject DLLs into other user mode applications that are currently running. Filtering this call to block global, system-wide hooks is extremely easy, and stops applications from breaking into other running user mode applications. There will be more examples of how this works later.
Yes, if it is used in a hostile or careless manner, it can cause problems with a system. It can also open up other security holes if it is not used in a secure manner. This applies to all applications, but in particular, SysNOP, since it can access kernel-mode data. The difficulty lies in that in order to control it, it requires interaction. So the system would already have to be compromised, at least partially. Then they would have to break into SysNOP to disable it or subvert it, or gain enough access priveleges to stop it in a manner that would not halt the system. Obviously, if they were able to do this, they would already be able to halt the system in other ways. It is still in development, and every effort will be made to make sure it will be more secure than the rest of the system around it, if at all possible.
The other way it can be detrimental is if someone uses it in a manner that either crashes a system, or uses it on a system in a hostile manner. It is not designed to be used this way, but with most good tools, they can be used in multiple ways, that may or may not be desirable. From the security aspect of it, there is one built in protection for stability and security. If SysNOP is set to auto-start, and there are rules saved in the registry that are automatically applied on startup, after a couple starts and stops the driver will disable them if the driver does not stay active for a couple minutes. So if you reboot and let the driver start up a few times in rapid succession, either intentionally, or due to a bad rule, it will automatically start up with the filtering disabled. Why was this done? If someone has used it maliciously, or used it in a manner that causes system insecurity, it stops temporarily so it can be cleaned off, or the rules can be fixed. It is also done so that if someone has broken in remotely, it will make it more noticeable if they are rebooting to try to disable it, as well as forcing them to break their connection and be forced to try to get in again.
These are the assembly instructions that transfer control on a Windows machine to kernel mode in order to carry out base system functions. It is this method that transfers to Ring 0 (kernel mode) so it can access the base system and do all the dirty work of running the system. While in Ring 3 (user mode) applications are protected and limited so they cannot do as much damage to the system, and follow the rules of the system. While applications can still do harm, without getting into the Kernel, their damage is limited in what they can do. No direct memory or drive access, no direct process context access, no direct hardware access and no direct control over process execution. Having a tool that protects and controls that access is more than handy, if you prefer to have full control over your system. If you are familiar with other rootkit detection utilites, or the Windows Kernel, you have seen the calls available in the SSDT. The System Service Discriptor Tables have links to every kernel mode function that can be called through these instructions. When passed the correct variables on the stack, the specific system call is performed and the action completes, and returns success or error.
Instead of rewriting every jump location in the SSDTs, the call is filtered before the jump is made. This makes the filter installation safer since it is not necessary to rewrite pages of memory in the kernel, but also safer since checking, and disabling it is very fast. It also gives an easy method of blocking a system call by passing an invalid system call number, before the decision of where to jump is made. Thanks to how Windows sets up the call jumps, the call returns properly, with all errors set and process proceeds normally, and there is no chance that the blocked call will actually make it to the system call desired.
First, you need to decide what call(s) you are intending on filtering. All call filters are run for a specific system call only. There are several reasons for this.
The first (and main) reason is that SysNOP keeps a bitmask of the system calls being actively filtered and checks this before entering the main filtering process. This way, the actual increase on system call processing is increased by only a very small number of clock cycles - the number of instructions is very insignificant compared to the preprocessing that is done by windows for system call setup and handling.
The second is that when you do checking or modification of the system calls, the stack that is passed will almost always be specific to that system call alone. So checking stack arguments and modifying variables will usually not be a correct course of action. Additionally, you can usually do global style blocking using specific calls to kill the calling process.
Once you decide what call you intend on filtering, you can set up match rules to either allow the chain to continue, or to stop by blocking, allowing, or jumping. It is similar to an iptables style chain processing. Each rule in the chain will match or skip to the next if no match. There are several checks that can be performed so you can ensure you are processing the desired request before filtering. You can skip matching and process every call, or match based on process ID, thread ID, process name, or value at a specific point on the passed stack. You can compare the values to some of the above, or a specific hex value, bitmask or even a unicode string. You can also specify that the rule match inversely so it will apply on failure instead of success.
Once the rule is destined to be applied, the result can be for the call to be accepted or blocked - which stops processing the specific system call and allows or blocks the call. Alternately, flow control can be applied so it will skip rules or jump to a specific rule position. It can also modify the call by altering the stack variables by setting them directly, or masking them in different ways.
Using this, global rules can be set up to control the system and make sure things proceed how you want. Please see the example rule creation section for how to make basic rules.
"SysNOPControl.exe" is a standard executable that loads the device driver "SysNOP.sys" into memory. As a device driver, it has access to kernel memory and can access kernel data structures and the control registers needed to reroute execution flow through it's process before proceeding to the normal windows process.
When you register and load the SysNOP driver, it starts running and initializes all of it's variables in the space allocated for it in the Windows Kernel. It then waits for commands from the user-space control application (SysNOPControl.exe) in order to perform it's various commands. Using the control application, you can define rules for any system call then load them to the driver, which copies the rules into the driver's active rule set. After they are loaded, you start filtering which attaches to the system's kernel call hooks and starts processing the rules as the system calls flow through.
Using the control application, you can see any rules tagged with the "log" target when you hit the "List Data" button. This also shows you other statistics for the driver including the current location of the system call functions as the system can see it, the count of total kernel mode switches made so far in the current filtering invocation, the number of memory exceptions generated by rules trying to follow memory pointers, the number of rules loaded, the number of times the filter processing function has actually been called to filter a call, and then the list of log rule entries follows that. Everything that is output to the main log window is stored to the maximum length of the window's buffer, and can be copied out if saving is desired.
You can monitor the operation and modify the running state of the driver using the control, but the control is entirely unnecessary for standalone operation of the driver. Once rules are loaded, the control can be minimized or closed and the driver will continue processing. Also, if the driver's active rules are loaded, and you register the drivers rules, it will save them to the registry. The other options are to start filtering on driver load, and to start on boot. Using all of these together you can have the driver begin running at startup, load the registry saved rules, then begin filtering user-mode calls.
No. A system can run fine without it. But there are always situations where it would be benificial for anyone running a computer, even when not used for security purposes. For security, it's just one more hurdle to make a system difficult to break into or gain extra access to, especially effective for automated hacks that cannot work around things not proceeding as they believe they should. A good amount of hostile code will stop in it's tracks if suddenly the system call it's trying to make does not return successfully. Also, it can be used to improve certain aspects of system stability. An example that I like to use is SetWindowsHookEx - it allows a program to hook it's DLL into other applications. This may not seem like a big deal, but if the DLL isn't careful with how it operates, it can crash or damage the other running process without leaving a trace that it was causing the damage. It also makes it difficult from a debugging standpoint to track down errors since the code of the program isn't responsible for the actual cause of the crash. I recently had a similar situation with a program that I was using - the hook DLL was causing a very intermittent crash in another totally separate application that I was using. Possibly it was the copyright protection in the program that was detecting the DLL and causing issues, but it was easily resolved by setting the global flag in SetWindowsHookEx to the thread ID of the calling process instead. This effectively localized DLL to only the calling application's thread, which handled the problem without the program even knowing about it, allowing both to run on the system without causing the crash. In this case, the hook wasn't important in the operation of the program. Of course, sometimes you cannot do that, but it is easy to write exception rules for programs to be able to still call global hooks when needed.
Since the driver is what does all the work, and since drivers cannot have user interfaces themselves, everything is controlled with the program SysNOPControl.exe. SysNOPControl (the control program) does all the work taking your directions. It handles registration and loading of the system driver, control while running and management of rules.
Using the control program for driver control requires administrator access. Without Administrators group membership, or using the Administrator account, you will not be able to control the driver.
System drivers require registration by windows in order to load the necessary data into the registry so the service loader can load the driver properly. This includes the location and certain specific pieces of data about the driver. Once the control program is open, the button "Register Service" asks the service manager to register the driver. The service manager loads all the necessary information into the registry so the driver can be started. The "Unregister Service" button does the opposite. It requests that the service manager remove the data from the system. If the service is still running, it does not terminate it.
"Start Service" is the button that actually has the service manager start the driver running. Once it is started, the driver is loaded into memory and sits idle until it receives commands. When it starts, it reads information about the system and gets the system ready for processing rules. At this time, no system calls are being filtered. "Stop Service" requests that the driver unload and remove itself from memory. It will release all resources held and stop all processing, attempting to leave the system in the same state as it was started in.
To actually begin rule processing, you hit the "Start Syscall Replace" button. This makes the system jump to SysNOP.sys before processing system calls by replacing the address of the system call function in the processor with it's own address. At this time, all calls are rerouted through SysNOP before being sent to the Windows kernel for standard processing. Again, there is a button to stop the system call filtering.
Once these steps are done, the driver is actively processing, but does not contain any rules. You can see the activity by hitting "List Data" button. This writes the current processing data to the log window. The data that is listed contains the virtual addresses of the system call pointers, how many times the pointers have been hit, the number of memory exceptions, number of rules loaded, times the filter has triggered and lastly the list of rules. Before the system call pointer has been rewritten, you will see that it points to the Windows Kernel's memory range. Afterwards (after "Start Syscall Replace" has been hit) it will point to SysNOP's memory range.
Once the control program is running, from the main window, click the "Rule Creator" button in the bottom left corner, or use the menu option. You have the option to select all the rule preferences from the right hand side of the window, control the rule positions from the bottom of the window and add rules from the bottom right. To add a new rule, you need to fill in some fields.
For each flavor of Windows, there is a set of system calls and each call is uniquely identified by a number that will only be the same for that version of windows. As an example, you have Windows XP where the system call ntCreateFile is 0025h and on Windows 2k the same call is 0020h. In our case, we will use Windows XP as an example, and ntCreateFile for the call to work with, so we enter 25 into the Syscall Number box. You can also click the ? box next to the entry - this will open up a dialog that will attempt to load a saved version of the first system call list if it can properly determine the version of windows you are using. Double clicking on any call will input the call number into the field. You can also see the variables that are passed to the functions by user mode. This list was generated from Metasploit's system call list. (www.metasploit.com)
For Check Type, we can select several different ways of checking info about the call that will be filtered. For our example, we will assume we don't know anything about locations of data on the stack that is passed to the system call. All data given to a system call is passed as variables on the stack, and we are going to use some of that data to block the call so the file cannot be opened. Since stack data is usually aligned on 4 bytes, you can use the "Location Search" to automatically fill in the first 64 stack positions for testing where your data may be. Before we do that though, we need to enter what we want to compare to. It can be several different value types, but in this case, we want to search for a string. So select "String" in the "Compare To" box and then type "no_open_me_filename" in to the box below. Then, set the "Alter Type" to "Log". This will make it so no action is taken, but it will display a tally of the number of times the rule has been triggered when you "List Data" on the main window. Then hit "Location Search" and hit OK on the pop-up. You will see the rules load into the list display. They are now loaded into the temporary memory of the control program, but not yet loaded into the driver.
Close the Rule Creator window and hit "List Data" to check if the driver is running. If you get an error message saying it could not open device control, then the driver is not running. In this case, to start it, clikc "Register Service" then "Start Service" and now when you hit "List Data" you should get a bunch of data. Once you are at that point, hit "Load rules to driver" to load the rules from the temporary memory in the control program into the driver's active rule set, then hit OK. The log area should display "Rules received: 64" - the rules are now in the driver's memory, but they are not running yet. Next, hit "Start Syscall Replace" to start filtering system calls. At this point, the rules are running. Now it will log any stack positions of ntCreateFile that contain the text "no_open_me_filename" and allow us to see which positions they are.
Now, create a new file on the desktop and name it "no_open_me_filename.txt" and then open it and save some text into it and then reopen it several times, counting the number of times you open it. Then go back to the control program and hit "List Data" and scroll up to the top of the count list. The rules with hits nearest the top of the list are usually the best bet for the right stack position to use, as long as they match the number of times you opened the file. There are times when it is better to use a stack position lower in the list, or times when using a position that has the most hits rather than the exact number of hits, but I will discuss that later. In my case, the stack position that matched was rule 13.
Open up the Rule Creator window again and then select rule 13. Hit "Edit Rule" and all the information for that rule will be copied into the editing boxes on the right. Change "Log" to "Block". This alter target will attempt to deny the system call if the match is successful. Then hit "Add Rule" to add it to the bottom of the rule list. Then hit "Load To Driver" to load them to the driver's active rules. Since it is already filtering calls, the rules will take effect immediately. Try to open the file up and you will get an error message and you will not be able to see any of the text that you saved earlier, since the file was not opened. Then to clean things up, open the Rule Creator window again, and select the rule 64 (the block rule that was just added) and edit it so you have it on the right side. Then hit "Clear All Rules" to clear out the list then "Add Rule" to add the rule back. Now the rule is all by itself, and you can have as many variations on it as you have space for rules.
Any ntCreateFile call can be filtered in this way to stop a file from being created or opened. Also keep in mind however that there is more than one way to open a file. You can also call ntOpenFile to open a file, so in order to stop that, you would have to do the same thing for that call. Remember that stack data positions are often different for every different call number, and sometimes stack data beneath the standard arguments that are passed to the system call are not always going to be in the same position - however often they are. Only the actual arguments to the call are ensured to be the same, which are usually only the first few stack elements.
There is also another, more elegant way to do the same thing without blocking the call outright. You can use another alter type to mask off the bits of the ACCESS_MASK bitmap in the stack so the file cannot be read, written, deleted or executed, basically denying access to it without blocking the call.
There are more examples of things you can do, I will highlight some of them: Block driver loading except certain drivers or loading by certain programs. Block system hooks, local or global, or convert hooks to local when a global hook is requested. Block access to a file or directory. Block access to a registry key or branch. Make files or registry keys non-executable or read-only for select paths. Block or modify process or thread creation or destruction. Block file deletion for specific files or paths.
Here are examples of things that you cannot do easily: You cannot hide files or registry entries - there is no system call wrapper, so when a directory query is made to the system you cannot block the return call from the kernel, or alter it. It is not really a needed functionality for the program since normally there is no good reason to try to trick userspace in this way unless you are trying to be malicious. No way to run programs when certain system calls are made. No way to send data received in a system call over the network, or to another process. Cannot edit random memory locations in a program, check kernel memory for strings or alter kernel memory space, besides normal program behavior.
Some of them are not necessarily impossible, but not easily accomplished with this program. If you want rootkit functionality, this is not the utility you are looking for.
The exception handler in the program keeps a running tally of the number of exceptions that are triggered in system space while processing system calls. Normally this count is 0, but when you use string functions, sometimes following the string pointers will result in memory exceptions when probing the memory to see if it is readable. This is normal. Other times page faults are handled due to attempting to read invalid memory areas when a string points to a valid area, but ends up in an invalid page while reading the string. In most cases this isn't really a problem at all, but the more exceptions, just like the more rules processed for each call, the slower execution proceeds. If you create a rule that checks a string location in memory, or something too far down the stack, it can generate a lot of memory exceptions. Although the rule is skipped, it means the exception handling functions have to execute which creates extra overhead. It's best when you have rules that generate lots of exceptions to try to find ways to minimize the number of exceptions generated. For instance, if you have several rules that search for the same string repeatedly, instead, have one search that skips over the rules if it does not match.
From the control program, after the program is registered, you use the menu command, "Driver Service->Enable Boot Start" which changes the registry entry to start the service on system start. If "Enable Filter on driver load" is selected, then the driver will automatically read the registry and start processing the rules saved there after the driver starts. Once you are happy with the rules you have in place, and feel confident that they are performing as you want them to, you should set a password so the rules cannot be read and the state of the driver cannot be altered without entering the password (or disabling the driver).
Once the driver is loaded you can set up authentication. This allows the driver to be locked and unloadable, as well as disabling all direct control of the driver except for authenticating. First you hit the Authenticate button. A dialog will pop up giving you the option to select the type of authentication. There are two types of authentication, driver mode and user space mode. The driver mode authentication will attempt to attach to the keyboard driver device and intercept the password as it is being typed, and use that for authentication. User space authentication just allows entry into a text box and forwards that on to the driver for authentication. The downside to user space authentication is that a keylogger could capture the keystrokes, there are several ways to read the data out of the window using the message system, and the messages themselves could be intercepted. An advantage to driver auth is that you can ensure that the authentication is done on the system itself since the keys have to be hit on the keyboard device. The main drawbacks to driver auth are that you cannot use it over the network if you administer the system remotely, and that not all devices will be compatible with driver auth. PnP device stacks are not supported at this time - you need to use a PS/2 keyboard for hardware auth. The driver does not currently implement a USB device filter that loads on the USB device's stack since it is not as easy and reversible. Since the design of SysNOP is that it completely reverses any changes to the active system on unload, having a filter driver stay persistent for USB stacks would not comply.
After you have chosen the method you prefer, proceed to enter a password. For driver mode auth you hit F1 then start typing your password. Any key you type is considered a valid character for authentication. So the space bar is a unique key, the shift key, the F-keys and even number pad keys will identify as keystrokes. Your password could be the shift key 10 times if you wish, and it would treat it as 10 characters entered. The enter key will finalize the entry, and the Esc key will abort it. For user space auth you just enter your password and hit enter or click the "Activate" button.
Once you have entered the password, it is enabled and the driver is now locked. In order to access anything you must authenticate, which requires entering the password again using the same method you did previously. Once you do that, it ensures you can enter the password, and the driver is unlocked for that process and thread. If you close the control program, and reopen it, or open a second instance of the program, it will not accept control. The driver will automatically lock if the control program is inactive for a few minutes. This resets the driver to the active but unauthenticated state which will allow authentication again from other processes or threads. If the driver is in the authenticated state and you enter the password again, it will disable authentication for the current instance of the driver. While active and unauthenticated, you cannot disable or stop the driver. In order to stop it, you must be authenticated.
Once you have activated authentication and enabled it, you can register the auth hash to the registry, which will cause the driver to automatically lock itself once loaded. "IO Controls->Register Auth" is the menu command, but be sure you are comfortable typing you password in before you do this. Until you do this, you can always reboot or stop the driver to clear out the current password. After this is done, the current password is saved for future invocations of the driver. While authenticated, you can always delete the auth hash from the registry by using the menu command to delete it, or you can reenable auth with a different password and save it to overwrite the previously stored password. Note that although you can disable the filtering rules by restarting repeatedly or disabling the driver and reloading it, this will not disable the authentication. This means that the driver will still run and require auth to control, but the rules will be inactive - this will allow you to reauthenticate and alter rules if you cause a system crash unintentionally (and have the rules loaded to the registry). This also means that if someone else was to disable the filtering by that means, it would not allow them to easily rewrite the password or change the rules through the control interface.
Remember to unregister authentication when upgrading versions. The password hashing algorithm may change as I might occasionally adjust it to provide better hashing characteristics. It is currently an (mostly) effective one-way hash function - I am also testing collision resistance which is not really an issue in the current usage of this program. It does not use hashing for anything except making the password more difficult to retrieve by a hostile application than the other methods it might resort to in order to disable the driver. Of course, while a very good cryptanalyst may be able to glean some information from the hash, recovering the password, or finding a password that will result in the saved hash is unlikely. And if it was cracked, it would certainly take far more work than the other ways of disabling the driver - considering it is quite easy to turn off filtering if you are physically at the machine.
Using the control program requires administrator access. Without Administrator group membership, you will not be able to authenticate or control the driver. Having Administrator access does not allow unauthorized access to the SysNOP driver, as the assumption is made that the Administrator account may be compromised. Of course, if you don't set up rules to restrict access to the registry, the driver can be disabled by an administrator account at any time. This does require rebooting since the driver will not automatically unload by registry modification, but the auto-startup can be turned off. It is best if the password used for the Administrator account does not match the SysNOP password - for better protection.
I wrote this a little while ago, primarily for my own use to protect my systems from different things that there were no good solutions for. I have used it to effectively stop several things, including PDF file exploits in a reader application, the meterpreter loading itself, some viruses that use dll injection and some browser exploits, either through routine activity or intentional testing. There are many ways to use it in personal and server situations. From kernel mode, no application in user mode can outright kill it, nor can it be disabled, or interrupted - limiting a malicious application's ability to disable it. It does not scan for specific programs, it just runs rules similar to how iptables does. It is simple, and was designed so it would not cause significant slowdown of a system. Global rules are very easy to implement. It's essentially a simple kernel firewall.
I know you probably thought, "my virus protection will stop anything, it's a good solution!" While virus scanners are the ideal solution for most generic viruses and the slew of hostile code that can be recognized, as most good pentesters, hackers, and security experts will tell you: they are not the end-all to system protection. Most exploit code can be packed or re-encoded a dosen ways to avoid detection, assuming the code would even get detected in the first place - this is especially effective for targeted exploit attacks, unlike automated viruses and malware. Virustotal.com has a great multi-engine scanner that can illustrate this if you need proof, and have some known-hostile code to throw at it. Having extra protection for your system, and an extra line of defense for the Kernel never hurts. SysNOP also protects the Sysenter EIP Model Specific Register from modification while it's in use and filtering, as well as Int 2E in the IDT.
I actually made this for myself, in response to several exploits in applications that were designed to enter through (at the time) unpatched vulnerabilities, elevate their priveleges then rootkit a machine in order to steal information. I was a little irritated by it, and the fact that even after being reported, code took time to get updated in the virus databases. I thought I should write something I can apply to my machines to give another layer of generic global protection, segregated from Window's normal security mechanisms, so even if the system gets completely compromised, there is still something that can provide some System and Kernel protection. Even if the exploit is custom, a good set of global system rules can limit a system service's exploitable potential. Limiting the most harmful system calls, or illogical calls is always useful. Examples: Does iexplore.exe need NtLoadDriver? Does "peedeeeffreader.exe" really need NtCreateProcessEx, NtDebugActiveProcess or NtOpenProcess in most situations? Specific rules are easy to write, as well as global rules.