And the day has finally come (if you’re thinking … free donuts for everyone? Nope, sorry) but something just as good … NRT is on Github!
To be fair, it’s only bare-bones at this point, but logging is working and database initialization is well on it’s way. If you want to git clone the repo (that just sounds cool), head over to Github. You can scan a client by updating the following line near the end of NRT.py:
scan_host("127.0.0.1") # The IP of the host to scan
127.0.0.1 will scan your local machine (if you didn’t know that already).
I try to comment almost every line, so that someone with little coding experience can get a good idea of what’s going on. Which leads me to an important topic … code formatting (yay …).
I know there are lots of opinions about how code should be formatted and how it should flow. The nice thing about Python (and one reason I like it) is that it enforces basic formatting hygiene. I use PyCharm and it has a nifty feature called Reformat Code (Top menu bar > Code > Reformat Code … or just Ctrl+Alt+L), which I use all the time to keep things consistent.
The goal is to make my code easy to read, so I prefer to use long variable names, this way I can describe what’s being stored in the variable. One of my great frustrations is seeing a variable called “s” or “c” (or something like that) and then trying to figure out what it actually does. Remember, computers don’t care how long a variable’s name is, so please use human-readable words to describe its function. (end rant)
I also like to list out exactly how my code flows. So all of my functions will appear under
if __name__ == '__main__':
In the order they’ll be executed (with very few exceptions) that way anyone can look and see what’s happening. No need to spend hours chasing down a bunch of functions as you worm your way through the program.
Lastly, I prefer to write out my logic, even if there’s a way to make the code shorter (or when PyCharm gives me helpful shortcut suggestions). My aim is readability, not code length, so … no lambda expressions. A small example, from W3Schools, about lambdas. This is not very readable …
x = lambda a: a + 10 print(x(5))
This is (even a non-programmer can probably understand what’s going on here) …
def add_ten(x): return x + 10 print(add_ten(5))
With that said (and I know there will be people who disagree … and that’s okay) I’m very happy with my coding style. It’s proven itself over time … I can go back to code I haven’t touched in years and remember exactly what it does in a minute or two. Now … let’s move on to the code.
Current NRT Code
I plan to include the full code, at it’s current state, in each subsequent blog post … for those who don’t like Github:
# Install imports using: pip install -r requirements.txt import inspect import sqlite3 import nmap import logging import logging.handlers log_filename = "NetReconToolkit.log" # The filename of the log file. TODO Set log_filename based on settings.txt log_rotate_handler = logging.handlers.RotatingFileHandler(log_filename, maxBytes=100000, backupCount=5) # Enable log file rotation if it exceeds maxBytes, keeping 5 total backup files. TODO Set maxBytes and backupCount via settings.txt logging.basicConfig(format='%(asctime)s >> %(message)s', handlers=[log_rotate_handler], level=logging.DEBUG) # Instantiate root logger. TODO Set logging level through settings.txt logging.info('-----##### Starting NRT #####-----') def database_connection(): current_function_name = inspect.getframeinfo(inspect.currentframe()).function # Get the name of the current function for logging purposes logging.info('%s - Entering function', current_function_name) database_name = "NetReconToolkit.db" sqlite3_connection = sqlite3.connect(database_name) # Create database connection using database_name variable. sqlite3_connection.row_factory = sqlite3.Row # Enable to provide index-based and case-sensitive name-based access to columns (https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.row_factory). def scan_host(ip_to_scan): current_function_name = inspect.getframeinfo(inspect.currentframe()).function # Get the name of the current function for logging purposes logging.info('%s - Entering function', current_function_name) print("Scanning: " + ip_to_scan) # Display the host we're currently scanning. logging.info('%s - Scanning: %s', current_function_name, ip_to_scan) nmap_ps = nmap.PortScanner() # Create our instance of python-nmap. nmap_ps.scan(hosts=ip_to_scan, arguments='-sV -Pn -p22-445') # Set our host to scan and arguments (sV - Service scan, Pn - don't ping, but assume the port is up, p22-445 - only scan ports 22 through 445). for host in nmap_ps.all_hosts(): # Iterate through all scanned hosts. print(nmap_ps[host].hostname()) # Display the hostname for each host. for protocol in nmap_ps[host].all_protocols(): # Iterate through all protocols (TCP, UDP, etc.) for one host. protocol_keys = nmap_ps[host][protocol].keys() # Get a dictionary containing all port numbers (22, 80, 445, etc.) for one protocol. for port in protocol_keys: # Iterate through each port in the protocol_keys dictionary. print(port, "-", nmap_ps[host][protocol][port]['state']) # Display each port and it's associated state. if __name__ == '__main__': current_function_name = inspect.getframeinfo(inspect.currentframe()).function # Get the name of the current function for logging purposes logging.info('%s - Entering function', "Main") scan_host("127.0.0.1") # The IP of the host to scan logging.info('-----##### Ending NRT #####-----')