Previous Entry Add to Memories Share Next Entry
Finding connections to a specific port with SystemTap
hack
siddhesh

Earlier in the week I was asked by someone if I know a way to monitor applications trying to connect to the telnet port. The obvious answer was netstat/lsof, but the problem was that this application would be up for merely seconds and he did not know when/where it started up. All he had was his telnetd log telling him about the connections. So I decided to go the SystemTap way and came up with this:

/* snoop.stp */
function sockaddr_to_port:long(sck:long)
%{
        short ret = 0;
        struct sockaddr *sock = (struct sockaddr *) (long) THIS->sck;

        memcpy(&ret, sock->sa_data+1, 1);
        memcpy(((char *)&ret)+1, sock->sa_data, 1);

        THIS->__retvalue = ret;
%}

global testport = 0

probe begin(0) {
        if (testport == 0) {
                printf("Usage staprun snoop.ko testport=<portnum>\n");
                exit();
        }
}

probe syscall.connect {
        port = sockaddr_to_port($uservaddr)

        if (port == testport) {
                printf ("%d: %s trying to connect to port %d\n", gettimeofday_s(), execname(), testport);
                printf ("\tPID: %d\n", pid());
                printf ("\tUID: %d\n", uid());
                printf ("\tEUID: %d\n", euid());
                printf ("\tParent: %s\n", pexecname());
                printf ("\tPPID: %d\n", ppid());
        }
}

So this really is a very simple tap on the connect syscall to be able to intercept all connect requests on the system. The sockaddr_to_port is a little more interesting. It takes the first two bytes of sockaddr->sa_data and swaps them to get them to the correct byte order to return as the port number. The reason for the swap is that network byte ordering is essentially big endian (The most significant byte first) while x86 computers are little endian. So the port number 23, at byte level would be seen on your computer as:

0x00170000

while in network byte order it is seen as:

0x00000017

Once we have the port number, the rest is easy pickings with SystemTap giving us easy functions to collect the executable name, pid, parent process name, etc. As for the date, one may easily convert it to human readable form using:

date -d @<my date in seconds>

Now that we have all the pieces in place, we can build the above script into a kernel module using:

stap -vvvg -r `uname -r` snoop.stp -m snoop

The output of this is the kernel module named snoop.ko. The -g signifies the "Guru mode". We need it since our function sockaddr_to_port is embedded C and we need to tell stap that we really know what we're doing. I like putting in a lot of v's in the first place so that I get build errors right away. This is essentially building a kernel module. The -r and -m are for kernel version number and module name respectively. Once built, you can deploy the module using:

staprun snoop.ko testport=23

This loads the module into the kernel and waits to print messages (whenever the program decides to connect to telnet) to standard output. If you look into the output of lsmod, you will find that snoop is a loaded module.

Packages you will need to implement all of this:

  • systemtap
  • systemtap-runtime
  • kernel-debuginfo
  • kernel-devel
  • kernel-debuginfo-common

If you're using the PAE kernel then you need the kernel-PAE-debuginfo and kernel-PAE-devel instead of kernel-debuginfo and kernel-devel. If you're only looking to deploy a binary systemtap module, you will only need systemtap-runtime. Yes, I can run a module built on one system, on another system provided they have the same kernel version and architecture. But be careful of what you run, always inspect the source to make sure you're not running anything malicious.


Its nice to see the caption "systemtap" In 2006 I had worked a little bit on that. May be hardly a week or 5 days on that, before joining my current firm. Nostalgia :)

Great SystemTap script!

This only works for local applications, though. This won't show incoming connections from other systems on the network. To see those connections, it will have to probe syscall.listen and syscall.accept, although it's probably easier to just use an iptables rule to log traffic on that port. Something like
iptables -I INPUT 1 -p tcp -m tcp --dport 23 -j LOG

Re: client side only

siddhesh

2010-05-07 02:52 am (UTC)

Yes, this was specifically for the client program, since if it is the server, I can easily find out with lsof/netstat since it will remain up to listen on the socket. The client is much more volatile. The intent was to find out which program was coming up repeatedly to access the telnet port. There is no way of knowing that.