A Totally Useless Port Scanner To Teach You Some Python15 Jul 2010
I would like to present you a quick post to demonstrate the power of Python in a succinct little port scanner. The time taken from idea to finished product was about three minutes.
First an example of the program. Everything (hosts to check and the port range) is hard-coded into the script.
Not too powerful and not too quick, but it does the job.
Now let’s go through the code and explain each line. As a boss of mine used to say “break it down ‘Barney style’”
This is our “hashbang” line. Please refer to wikipedia on this one as its a little out of scope.
from socket import socket
Python, being “batteries included”, is doing a lot of sophisticated work for us behind the scenes: opening and closing ports, managing memory, etc. Some super smart people have bundled up all of their expert work into packages and modules. For all of their hard work, all they ask of you is that you nicely call upon their code before you use it.
HOSTS = ['192.168.1.1', '192.168.1.2']
PORT_RANGE = [1,100]
As you might guess from this section, this is where we are configuring our variables. All caps is not necessary, I just chose it to stand out. Note that both variables (
PORT_RANGE) are lists. A list is just a container with separate compartments for holding different pieces of data. The cool thing about lists is that they provide us a way to iterate (or walk-through) each piece (element) of the data.
for host in HOSTS:
This is pretty easy, all it says is iterate over each element in HOSTS. Every time you hit a new element set host to that value then execute all the code below, once you reach the end, start back at the top, setting host to the next value.
By the way, the indentation in Python is important. Python looks “down the page”, everything it sees that is indented at least one tab, it knows is part of the for loop. At the end of the loop, the blank space tells it to stop looking. These levels of loops (or functions, or conditions) and their indented commands can be embedded within each other, as seen in our example.
t1 = time.time()
We are setting the variable t1 to the current time. This way we have a reference point to figure out how long the scan took. Note again that since we are in a for loop, once we are finished, all of these variables get “reset” when we do our next iteration.
print "Testing %s" % host
In this line we are telling python to print whatever is enclosed in the double-quotes to the string. The ‘%s’ tells python, “hey man I’m going to print something here, not sure what it is, but I can tell it will be a string”. The ‘%’ just says that everything after this shouldn’t be printed, but it should be sent back to the print statement. If you have more than one ‘%s’ (or other types, see http://diveintopython.org/native_data_types/formatting_strings.html) you need to do it a little different. It might look something like this:
print “This is the first variable: %s, this is the second: %s” % (one, two)
Now we are getting into the meat of the program, the actual commands that do the port testing.
for port in range(PORT_RANGE,PORT_RANGE+1):
You remember our for command, right? In this situation we are using the range _command rather than a pre-built list. Effectively, _range builds this for us. We simply give it the first port and the last port plus one.
s = socket()
This command simply makes s an instance of whatever socket() is. In this case socket() is a “socket object”, which is a bundle of that wonderful “batteries included” code we get from importing at the top. Now, instead of saying
socket.something(), we can say
s.something. The advantage of this is that we can also have
r = socket() and when we call on r it lives in its own little world, unaffected by whatever s is up to.
if not s.connect_ex((host, port)):
This might be the most complex part of our program. But once we break it down, you will see it is just a few easy concepts chained together. Let’s start from the inside out: what are host and port?
In the first pass through our code, host would be set to 192.168.1.1 and port would be 1. Makes sense right? The host and port are enclosed in a structure called a “tuple”. A tuple is very similar to our lists from earlier, it is a little lighter-weight. This gives us a handy way to send the host, port pair as one object.
Where is this object going? We are sending this as input to the connect_ex, this is a function which is part of the socket that we have set to s. Okay, so if you have this all straight (s is a reference to the socket_code pulled in from our import, we are feeding the tuple of (host, port) to the _connect_ex function) you should be asking yourself, “What the heck does connect_ex do?”. I will do better then tell you, I will show you.
The wonderful thing about Python is the amazing depth of documentation. So lets say we want to know what a socket does? We simply look it up in the Python documentation. Starting at http://docs.python.org/ we see there are many categories of documentation. What we are interested in now are the “batteries” that are included with Python, also known as the “Standard Library”. We find that here. Also as you grow more curious about the basic of Python, there is wonderful documentation on the syntax and pieces that make up the Python language.
Okay, so what does socket_ex do? Lets look it up in the documentation. As you can see socket_ex takes an address, which is our
(host,port). The function will try to connect to the host and port specified and report if it was successful or not. If the connection is successful it will return
True, otherwise it will return
Next we see the
if part of the statement. All this is saying is if the result of
socket_ex is 0 (which is a successful connection) execute the commands below. The reason for using 0 (or other error codes if the connection is not successful) is because the socket functions mirror the underlying C code, which is rather low-level. Feel free to read it over.
Now we see that if the connection was successful we are going to print out a statement. The only thing new here is the \t - this tells print to insert a “tab”.
You will see the next line:
t2 = time.time() has jumped back a few levels of indentation. If you are unfamiliar with Python’s rules on indentation or just need to brush up, feel free to check out the Python Style Guide. What we are doing here is “jumping” out of the if statement and continuing our normal program flow. Now that we have tested all of the ports for the host, we are going to save the current time and in the next line
print "Took %0.3f sec" % (t2 - t1) we are displaying how long the execution took.
You will notice that the print statement we are using a slightly different syntax than before. We are simply printing the time as a float with three significant digits. Again, review the print formatting if you would like to see all of the various ways to format your output.
And that’s it. The loop will repeat with the next host in our list and continue the operation again.
Admittedly, this is a very simple port scanner, meant as an instructional device. We make no attempts to verify or do any error checking. We use none of the tricks the “big boys” use (nmaps half-open SYN scans, etc.). But even for a simplistic scanner, this can be very useful. I will be the first one to say I am no Python expert. Some of the explanations may be inaccurate and undoubtedly there are better ways of doing this. Hopefully this demonstrates, that even with incomplete knowledge of Python, you can rapidly turn your ideas into working code.
I look forward to seeing your comments, questions and corrections. What awesome tools have you thrown together in Python, or your favorite programming language?