From 8650cf86673564ff74ee97781f6fe8d9d87b52c0 Mon Sep 17 00:00:00 2001 From: angryziber Date: Fri, 9 Nov 2007 18:24:50 +0000 Subject: [PATCH] NetBIOSInfoFetcher implemented git-svn-id: https://ipscan.svn.sourceforge.net/svnroot/ipscan/trunk@233 375186e5-ef17-0410-b0b6-91563547dcda --- TODO | 6 +- dictionary.txt | 1 + resources/Labels.txt | 2 + .../azib/ipscan/config/ComponentRegistry.java | 2 + .../ipscan/fetchers/NetBIOSInfoFetcher.java | 146 ++++++++++++++++++ .../fetchers/NetBIOSInfoFetcherTest.java | 53 +++++++ 6 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java create mode 100644 test/net/azib/ipscan/fetchers/NetBIOSInfoFetcherTest.java diff --git a/TODO b/TODO index 84b29136..ce4d8d83 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ Before 3.0 beta: * export/import of settings (profiles?) -* NetBIOS fetchers (like in 2.x) Before 3.0: @@ -13,14 +12,13 @@ Before 3.0: * startup as root option * compile librocksaw for mac * preferences & select fetchers buttons in the toolbar +* plugin loader Later: -* preferences profiles (maybe even save with favorites) +* preferences profiles (tied to favorites?) * offer installation on Windows * free text (advanced) feeder -* comments fetcher * saving and restoring of results together with all options -* plugin loader * advanced find (firefox-like) * use jpcap for raw packet injection and ARP scanning diff --git a/dictionary.txt b/dictionary.txt index ed642880..02a8b781 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -28,3 +28,4 @@ clipboard ctrl compiz placeholder +firewall diff --git a/resources/Labels.txt b/resources/Labels.txt index aa72fdda..34be26d7 100755 --- a/resources/Labels.txt +++ b/resources/Labels.txt @@ -184,6 +184,8 @@ fetcher.comment=Comments fetcher.comment.info=Allows writing of comments for each host.\nThe comments are persisted and always shown then the host is scanned. fetcher.webDetect=Web detect fetcher.webDetect.info=Detects the web server software name and version, if possible.\n\nWorks by sending a HEAD request and reading the Server HTTP header from the response. +fetcher.netbios=NetBIOS Info +fetcher.netbios.info=Retrieves the NetBIOS information about Windows machines.\n\nThe response has the following format:\nDOMAIN\\USER@COMPUTER [MAC]\n\nWhere:\nDOMAIN - Windows domain or workgroup\nUSER - currently logged in user\nCOMPUTER - Windows computer name (may be different from DNS name)\nSome parts may be absent, depending on the response.\n\nNote that this won't work with machines that have firewall enabled (which are most modern installations).\nThis fetcher is provided mostly for feature-compatibility with version 2.x. fetcher.value.ms=\u00A0ms fetcher.value.notAvailable=[n/a] fetcher.value.notScanned=[n/s] diff --git a/src/net/azib/ipscan/config/ComponentRegistry.java b/src/net/azib/ipscan/config/ComponentRegistry.java index 1efe34c4..d4b23588 100755 --- a/src/net/azib/ipscan/config/ComponentRegistry.java +++ b/src/net/azib/ipscan/config/ComponentRegistry.java @@ -22,6 +22,7 @@ import net.azib.ipscan.fetchers.FetcherRegistryImpl; import net.azib.ipscan.fetchers.FilteredPortsFetcher; import net.azib.ipscan.fetchers.HostnameFetcher; import net.azib.ipscan.fetchers.IPFetcher; +import net.azib.ipscan.fetchers.NetBIOSInfoFetcher; import net.azib.ipscan.fetchers.PingFetcher; import net.azib.ipscan.fetchers.PingTTLFetcher; import net.azib.ipscan.fetchers.PortsFetcher; @@ -97,6 +98,7 @@ public class ComponentRegistry { container.registerComponentImplementation(FilteredPortsFetcher.class); container.registerComponentImplementation(WebDetectFetcher.class); container.registerComponentImplementation(CommentFetcher.class); + container.registerComponentImplementation(NetBIOSInfoFetcher.class); container.registerComponentImplementation(PingerRegistry.class, PingerRegistryImpl.class); container.registerComponentImplementation(ScanningResultList.class); diff --git a/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java b/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java new file mode 100644 index 00000000..d7b4c2bb --- /dev/null +++ b/src/net/azib/ipscan/fetchers/NetBIOSInfoFetcher.java @@ -0,0 +1,146 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ + +package net.azib.ipscan.fetchers; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.SocketException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import net.azib.ipscan.config.GlobalConfig; +import net.azib.ipscan.config.LoggerFactory; +import net.azib.ipscan.core.ScanningSubject; + +/** + * NetBIOSInfoFetcher - gathers NetBIOS info about Windows machines. + * Provided for feature-compatibility with version 2.x + * + * @author Anton Keks + */ +public class NetBIOSInfoFetcher implements Fetcher { + + private static final Logger LOG = LoggerFactory.getLogger(); + + private static final int NETBIOS_UDP_PORT = 137; + private static final byte[] REQUEST_DATA = {(byte)0xA2, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x01}; + + private static final int RESPONSE_BASE_LEN = 57; + private static final int RESPONSE_NAME_LEN = 15; + private static final int RESPONSE_NAME_BLOCK_LEN = 18; + + private static final int GROUP_NAME_FLAG = 128; + private static final int NAME_TYPE_DOMAIN = 0x00; + private static final int NAME_TYPE_MESSENGER = 0x03; + + private GlobalConfig config; + + public NetBIOSInfoFetcher(GlobalConfig config) { + this.config = config; + } + + public String getLabel() { + return "fetcher.netbios"; + } + + public Object scan(ScanningSubject subject) { + DatagramSocket socket = null; + try { + socket = new DatagramSocket(); + socket.setSoTimeout(config.pingTimeout); + + socket.connect(subject.getAddress(), NETBIOS_UDP_PORT); + socket.send(new DatagramPacket(REQUEST_DATA, REQUEST_DATA.length)); + + byte[] response = new byte[1024]; + DatagramPacket responsePacket = new DatagramPacket(response, response.length); + socket.receive(responsePacket); + + if (responsePacket.getLength() < RESPONSE_BASE_LEN) { + // response was too short for some reason + return null; + } + + int nameCount = response[RESPONSE_BASE_LEN-1] & 0xFF; + if (responsePacket.getLength() < RESPONSE_BASE_LEN + RESPONSE_NAME_BLOCK_LEN * (nameCount-1)) { + // data was truncated or something is wrong + return null; + } + + return extractNames(response, nameCount); + } + catch (SocketException e) { + // this includes PortUnreachableException and SocketTimeoutException + return null; + } + catch (Exception e) { + // bugs? + LOG.log(Level.WARNING, null, e); + return null; + } + finally { + if (socket != null) { + socket.close(); + } + } + } + + static String extractNames(byte[] response, int nameCount) { + String computerName = name(response, 0); + + String groupName = null; + for (int i = 1; i < nameCount; i++) { + if (nameType(response, i) == NAME_TYPE_DOMAIN && (nameFlag(response, i) & GROUP_NAME_FLAG) > 0) { + groupName = name(response, i); + break; + } + } + + String userName = null; + for (int i = nameCount - 1; i > 0; i--) { + if (nameType(response, i) == NAME_TYPE_MESSENGER) { + userName = name(response, i); + break; + } + } + + String macAddress = String.format("%02X-%02X-%02X-%02X-%02X-%02X", + nameByte(response, nameCount, 0), nameByte(response, nameCount, 1), + nameByte(response, nameCount, 2), nameByte(response, nameCount, 3), + nameByte(response, nameCount, 4), nameByte(response, nameCount, 5)); + + return (groupName != null ? groupName + "\\" : "") + + (userName != null ? userName + "@" : "") + + computerName + " [" + macAddress + "]"; + } + + private static String name(byte[] response, int i) { + // as we have no idea in what encoding are the received names, + // assume that local default encoding matches the remote one (they are on the same LAN most probably) + return new String(response, RESPONSE_BASE_LEN + RESPONSE_NAME_BLOCK_LEN * i, RESPONSE_NAME_LEN).trim(); + } + + private static int nameByte(byte[] response, int i, int n) { + return response[RESPONSE_BASE_LEN + RESPONSE_NAME_BLOCK_LEN * i + n] & 0xFF; + } + + private static int nameFlag(byte[] response, int i) { + return response[RESPONSE_BASE_LEN + RESPONSE_NAME_BLOCK_LEN * i + RESPONSE_NAME_LEN + 1] & 0xFF + + (response[RESPONSE_BASE_LEN + RESPONSE_NAME_BLOCK_LEN * i + RESPONSE_NAME_LEN + 2] & 0xFF) * 0xFF; + } + + private static int nameType(byte[] response, int i) { + return response[RESPONSE_BASE_LEN + RESPONSE_NAME_BLOCK_LEN * i + RESPONSE_NAME_LEN] & 0xFF; + } + + public void init() { + } + + public void cleanup() { + } + +} diff --git a/test/net/azib/ipscan/fetchers/NetBIOSInfoFetcherTest.java b/test/net/azib/ipscan/fetchers/NetBIOSInfoFetcherTest.java new file mode 100644 index 00000000..4cb52771 --- /dev/null +++ b/test/net/azib/ipscan/fetchers/NetBIOSInfoFetcherTest.java @@ -0,0 +1,53 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ + +package net.azib.ipscan.fetchers; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * NetBIOSInfoFetcherTest + * + * @author Anton Keks + */ +public class NetBIOSInfoFetcherTest { + @Test + public void extractNamesNoUserNoGroup() throws Exception { + byte[] response = ("01234567890123456789012345678901234567890123456789012345\u0001" + + "ComputerName XYY" + + "\u00DE\u00AD\u00BE\u00EF\u0000\u0000 XYY" + ).getBytes("ISO-8859-1"); + assertEquals("ComputerName [DE-AD-BE-EF-00-00]", NetBIOSInfoFetcher.extractNames(response, 1)); + } + + @Test + public void extractNamesNoUserWithGroup() throws Exception { + byte[] response = ("01234567890123456789012345678901234567890123456789012345\u0002" + + "ComputerName XYY" + + "GroupName \u0000\u0080\u0000" + + "\u0001\u0002\u0003\u0004\u0005\u0006 XYY" + ).getBytes("ISO-8859-1"); + assertEquals("GroupName\\ComputerName [01-02-03-04-05-06]", NetBIOSInfoFetcher.extractNames(response, 2)); + } + + @Test + public void extractNamesWithUserAndGroup() throws Exception { + byte[] response = ("01234567890123456789012345678901234567890123456789012345\u0007" + + "ComputerName XYY" + + "SomeName X\u007F\u0000" + + "SomeName X\u0085\u0000" + + "GroupName \u0000\u0085\u0000" + + "WrongUserName \u0003YY" + + "UserName \u0003YY" + + "SomeName XYY" + + "\u00DE\u00AD\u00BE\u00EF\u0000\u0000 XYY" + ).getBytes("ISO-8859-1"); + assertEquals("GroupName\\UserName@ComputerName [DE-AD-BE-EF-00-00]", NetBIOSInfoFetcher.extractNames(response, 7)); + } + +}