/**
Copyright (c) 2000 by Robert Graham (RobertGraham.com)
All rights reserved. Do not distribute. This program
is not fit for any purpose other than thinking about
its implication. Do not run; it will likely open a
security hole in your system or do other bad things.

http://www.robertgraham.com/src/soibten.c

SOIBTEN - A NetBIOS reflecter
v1.0

This is a subversive utility that plays
with the fact that Windows machines inadvertently
send NetBIOS NodeStatus queries when attempting
to resolve IP addresses into names. Instead of responding
to the query, this program refelcts it back
at the sender. Rather than them scanning you;
you scan them.

On a machine (not already running NetBIOS), simply
launch this in the background. It will reflect scans
back at people, and save the results in a file.

How I've compiled it:
Windows/MSDEV: cl soibten.c /link wsock32.lib
Solaris/SPARC/gcc: cl -o soibten soibten.c -lnsl -lsocket
Linux/gcc: cl -o soibten soibten.c

Note: On Windows, you need to unbind the NetBIOS/WINS stuff
from TCP/IP, or this module won't see the incoming packets.
Likewise, on UNIX if you are running SAMBA, you won't be
able to bind the port to 137. Also on UNIX, you need to
be root in order to open a port at 137.

The technique of reflecting NetBIOS scans pierces barriers
such as firewalls, NATs, and possibly even legal barriers.
Scan reflection has interesting philosophical 
implications.

Also note that since this demonstrates those philosophical
implications, this means that this code is likely protected
under the Ammendment 1 of the U.S. constituion, as ruled
by the Supreme Court in April 2000:
http://caselaw.findlaw.com/cgi-bin/getcase.pl?court=6th&navby=case&no=00a0117p
This brings up new philosophical questions: to what
extent is hacking code (scripts, viruses) protected
as free speech?
*/
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#if _MSC_VER
	/* Windows compilation, assuming everyone on Windows uses 
	 * Microsoft's compiler */
#	pragma warning(disable: 4057 4514 4201 4214 4115)
#	include <winsock.h>
	void sleep(int secs)
	{
		Sleep(secs*1000);
	}
#else
#	include <netinet/in.h>
#	include <sys/socket.h>
#	include <arpa/inet.h>
#	include <unistd.h>
#	define closesocket close
#endif



/** Just a raw copy/paste of the NetBIOS request.*/
static unsigned char nbt_request[] = {
	'R', 'G', /*NAME_TRN_ID*/
	0x00, 0x00, /*flags*/
	0x00, 0x01, /*question count*/
	0x00, 0x00, /*answer count*/
	0x00, 0x00, /*authority count*/
	0x00, 0x00, /*additional count*/
	0x20, 0x43, 0x4b, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 
	0x00, 0x21, /*type=netbios node status*/
	0x00, 0x01	/*address class=internet*/
};


/** Extract 16-bit endian-free */
int ex16(unsigned char *p)
{
	return p[0] * 256 + p[1];
}

/** Extract 32-bit endian-free */
int ex32(unsigned char *p)
{
	return (ex16(p) << 16) | ex16(p+2);
}


/**
 * Extracts/decodes a NetBIOS name from a packet. NetBIOS over TCP/IP
 * is based upon DNS. NetBIOS names are 8-bit binary whereas DNS
 * names are case-insenstive alnum + dash. Therefore, the name
 * is encoded whereby each NetBIOS nibble is added to the letter
 * 'A'.
 */
void extract_name(const unsigned char buf[], int offset, int length, char *outname, int maxoutlen)
{
	int i;
	int is_text = 1;
	int outlen = 0;

	outname[0] = '\0';

	/* Check remaining length */
	if (offset + 16 > length)
	{
		return;
	}

	/* for all NetBIOS characters*/
	for (i=0; i<16; i++)
	{
		char c = buf[offset+i];
		if (outlen+3 > maxoutlen)
			break;

		if (c == 0 && i != 15)
			c = ' ';

		if (i != 15 && isprint(c))
		{
			if (!is_text)
			{
				outname[outlen++] = '>';
				outname[outlen] = '\0';
				is_text = 1;
			}
			outname[outlen++] = c;
			outname[outlen] = '\0';
		}
		else
		{
			if (is_text)
			{
				outname[outlen++] = '<';
				outname[outlen] = '\0';
				is_text = 0;
			}
			outname[outlen++] = "0123456789ABCDEF"[((c>>4)&0xF)];
			outname[outlen++] = "0123456789ABCDEF"[((c>>0)&0xF)];
			outname[outlen] = '\0';
		}
	}

	/*Make sure we have a trailing '>' in case there
	 *was binary data at the end*/
	if (!is_text)
	{
		outname[outlen++] = '>';
		outname[outlen] = '\0';
		is_text = 1;
	}

	while (outlen > 0 && isspace(outname[outlen-1]))
	{
		outlen--;
		outname[outlen] = '\0';
	}
}

/**
 * Parse a response, presumably to one of our queries 
 */
void siobten_parse_rsp(FILE *fpout, int ip, int length, unsigned char buf[])
{
	/* The offset of the first name in the packet is 57 bytes */
	static const int offset_of_first_name = 57;
	int offset = 0;
	int name = 0;
	int name_count = 0;

	/*
	The header looks like this:
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|         NAME_TRN_ID           |1|  0x0  |1|0|0|0|0 0|0|  0x0  |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|          0x0000               |           0x0001              |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	|          0x0000               |           0x0000              |
	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	*/
	if ( 
		ex16(buf+2) != 0x8400
		|| ex32(buf+4) != 0x00000001
		|| ex32(buf+ 8) != 0x00000000)
	{
		/* Doesn't match our expected header; don't know what this is*/
		return;
	}

	/* Jump to the first name to extract */
	if (length < offset_of_first_name)
	{
		/*packet too short*/
		return; 
	}
	else
		offset = offset_of_first_name;

	/* Get a count of the number of names in the packet */
	name_count = buf[offset-1];
	if (name_count < 1 || name_count > 57)
	{
		/* too many names */
		return;
	}

	/* Go through all the names in the packet */
	for (name=0; name<name_count && offset < length; name++, offset += 18)
	{
		char outname[64] = {0};
		int isgroup = buf[offset+16] & 0x80;

		extract_name(buf, offset, length, outname, sizeof(outname));
		if (name == 0)
		{
			fprintf(fpout, "--------------------------------------------\n");
			fprintf(fpout, "IP address = %d.%d.%d.%d\n", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, (ip>>0)&0xFF);
		}
		fprintf(fpout, "%-32s  %s\n", outname, isgroup ? "GROUP" : "UNIQUE");
	}

	if (name == name_count && offset + 6 <= length)
	{
		char hexaddr[13] = "";
		int j;
		for (j=0; j<6; j++)
		{
			unsigned char c = buf[offset+j];
			hexaddr[j*2] = "0123456789ABCDEF"[((c>>4)&0xF)];
			hexaddr[j*2+1] = "0123456789ABCDEF"[((c>>0)&0xF)];
		}
		if (memcmp(hexaddr, "000000000000", 12) != 0)
		{
			fprintf(fpout, "MAC Address = %s\n", hexaddr);
		}
	}

	fflush(fpout);
}

/**
 * Test to see if this is an NBT request
 */
int is_nbt_request(unsigned char buf[])
{
	if (memcmp(buf+4, nbt_request+4, 50-4) != 0)
		return 0;
	if (buf[2] != 0)
		return 0;
	return 1;
}

/**
 * Open a socket at UDP port 137 and listen for incoming
 * NetBIOS packets.
 */
void soibten(FILE *fpout, int ip, int port)
{
	int fd;
	int x;
	struct sockaddr_in sin = {0};
	char buf[512];

	/* Open the socket */
	fd = socket(PF_INET, SOCK_DGRAM, 0);
	if (fd == -1)
	{
		perror("socket(UDP)");
		return;
	}

	/* Bind to port 137 */
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = htonl(ip);
	sin.sin_port = htons((unsigned short)port);
	x = bind(fd, (struct sockaddr*)&sin, sizeof(sin));
	if (x != 0)
	{
		perror("bind(137)");
		closesocket(fd);
		return;
	}

	/* Sit in an endless loop waiting for incoming packets
	 * and responding to them */
	for (;;)
	{
		int bytes_received;
		int sizeof_sin = sizeof(sin);

		/* Block waiting for incoming request from Windoze user */
		bytes_received = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr*)&sin, &sizeof_sin);
		if (bytes_received <= 0)
		{
			sleep(1);
			continue;
		}

		/* Ignore incoming packets that aren't from port 137*/
		if (ntohs(sin.sin_port) != 137)
			continue;

		/* If not enough bytes, then ignore */
		if (bytes_received < 50)
		{
			fprintf(stderr, "siobten: recv(%d) %02d%02d%02d%02d\n", bytes_received,
					buf[0], buf[1], buf[2], buf[3] );
			continue;
		}

		/* Check to see if we have a response to one of our queries */
		if (buf[0] == 'R' && buf[1] == 'G')
		{
			if (buf[2] & 0x80)
				siobten_parse_rsp(fpout, ntohl(sin.sin_addr.s_addr), bytes_received, buf);
			else
				; /*maybe another query from this script? Dangerous infinite recursion here!*/
			continue;
		}

		/* See if we have a request */
		if (is_nbt_request(buf))
		{
			/* If it is a request, then reply with a request*/
			if (sendto(fd, nbt_request, 50, 0, (struct sockaddr*)&sin, sizeof_sin) != 50)
				perror("siobten: sendto()");

			continue;
		}

		

	}
}

int main(int argc, char *argv[])
{
	int i;
	int ip = 0;
	int port = 137;
	FILE *fpout = stdout;

#ifdef _MSC_VER
	WSADATA wsadata;
	WSAStartup(MAKEWORD(1,1), &wsadata);
#endif

	fprintf(stderr, "Usage:\n soibten <-pN> <-iIP> <-oFILE>\n");
	fprintf(stderr, "Bounces NetBIOS requests back to sender.\n");
	fprintf(stderr, "Listens on port N and address IP and saves output to FILE\n");
	fprintf(stderr, "Default: N=137, IP=0.0.0.0 (all), FILE=stdout\n");

	for (i = 1; i<argc; i++)
	{
		if (argv[i][0] != '-' || strlen(argv[i]) < 3)
		{
			fprintf(stderr, "Unknown option %s\n", argv[i]);
			continue;
		}
		switch (argv[i][1])
		{
		case 'p':
			port = atoi(argv[i]+2);
			break;
		case 'i':
			ip = ntohl(inet_addr(argv[i]+2));
			if (ip == -1)
			{
				fprintf(stderr, "Invalid IP address %s\n", argv[i]+2);
				return 1;
			}
			break;
		case 'o':
			fpout = fopen(argv[i]+2, "w");
			if (fpout == NULL)
			{
				perror(argv[i]+2);
				return 1;
			}
			break;
		default:
			fprintf(stderr, "Unknown option %s\n", argv[i]);
		}
	}

	/* Launch the listener */
	soibten(fpout, ip, port);

#ifdef _MSC_VER
	WSACleanup();
#endif
	return 0;
}
