From 7f5345ec7e6f43917f860bb8256280e96dbef369 Mon Sep 17 00:00:00 2001 From: Nick Anderson Date: Tue, 6 Dec 2016 14:25:08 -0800 Subject: [PATCH] Adding process_open_ports and listening_sockets virtual tables to Windows (#2760) --- osquery/CMakeLists.txt | 2 + .../{posix => }/listening_ports.cpp | 0 .../windows/process_open_sockets.cpp | 270 ++++++++++++++++++ .../tables/networking/windows/win_sockets.h | 54 ++++ specs/{posix => }/listening_ports.table | 0 specs/{posix => }/process_open_sockets.table | 0 6 files changed, 326 insertions(+) rename osquery/tables/networking/{posix => }/listening_ports.cpp (100%) create mode 100644 osquery/tables/networking/windows/process_open_sockets.cpp create mode 100644 osquery/tables/networking/windows/win_sockets.h rename specs/{posix => }/listening_ports.table (100%) rename specs/{posix => }/process_open_sockets.table (100%) diff --git a/osquery/CMakeLists.txt b/osquery/CMakeLists.txt index 93f2e677..e6c08210 100644 --- a/osquery/CMakeLists.txt +++ b/osquery/CMakeLists.txt @@ -36,6 +36,8 @@ else() endif() if(WINDOWS) + ADD_OSQUERY_LINK_CORE("ws2_32.lib") + ADD_OSQUERY_LINK_CORE("iphlpapi.lib") ADD_OSQUERY_LINK_CORE("netapi32.lib") ADD_OSQUERY_LINK_CORE("rpcrt4.lib") ADD_OSQUERY_LINK_CORE("shlwapi.lib") diff --git a/osquery/tables/networking/posix/listening_ports.cpp b/osquery/tables/networking/listening_ports.cpp similarity index 100% rename from osquery/tables/networking/posix/listening_ports.cpp rename to osquery/tables/networking/listening_ports.cpp diff --git a/osquery/tables/networking/windows/process_open_sockets.cpp b/osquery/tables/networking/windows/process_open_sockets.cpp new file mode 100644 index 00000000..2cfdc4dd --- /dev/null +++ b/osquery/tables/networking/windows/process_open_sockets.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#include +#include +#include +#include + +#include "win_sockets.h" + +namespace osquery { +namespace tables { + +WinSockets::WinSockets() { + auto pSockTable = allocateSocketTable(IPPROTO_TCP, AF_INET); + if (status_.ok()) { + tcpTable_ = static_cast(pSockTable); + } else { + TLOG << "Error allocating the TCP IPv4 socket table"; + return; + } + + pSockTable = allocateSocketTable(IPPROTO_TCP, AF_INET6); + if (status_.ok()) { + tcp6Table_ = static_cast(pSockTable); + } else { + TLOG << "Error allocating the TCP IPv6 socket table"; + return; + } + + pSockTable = allocateSocketTable(IPPROTO_UDP, AF_INET); + if (status_.ok()) { + udpTable_ = static_cast(pSockTable); + } else { + TLOG << "Error allocating the UDP IPv4 socket table"; + return; + } + + pSockTable = allocateSocketTable(IPPROTO_UDP, AF_INET6); + if (status_.ok()) { + udp6Table_ = static_cast(pSockTable); + } else { + TLOG << "Error allocating the UDP IPv6 socket table"; + return; + } +} + +WinSockets::~WinSockets() { + if (tcpTable_ != nullptr) { + free(tcpTable_); + tcpTable_ = nullptr; + } + if (tcp6Table_ != nullptr) { + free(tcp6Table_); + tcp6Table_ = nullptr; + } + if (udpTable_ != nullptr) { + free(udpTable_); + udpTable_ = nullptr; + } + if (udp6Table_ != nullptr) { + free(udp6Table_); + udp6Table_ = nullptr; + } +} + +void WinSockets::parseSocketTable(WinSockTableType sockType, + QueryData& results) { + unsigned int numEntries; + switch (sockType) { + case WinSockTableType::tcp: + numEntries = tcpTable_->dwNumEntries; + break; + case WinSockTableType::tcp6: + numEntries = tcp6Table_->dwNumEntries; + break; + case WinSockTableType::udp: + numEntries = udpTable_->dwNumEntries; + break; + case WinSockTableType::udp6: + numEntries = udp6Table_->dwNumEntries; + break; + default: + numEntries = 0; + break; + } + + for (size_t i = 0; i < numEntries; i++) { + Row r; + std::vector localAddr(128, 0x0); + std::vector remoteAddr(128, 0x0); + + auto proto = static_cast(sockType); + r["protocol"] = INTEGER(proto); + + switch (sockType) { + case WinSockTableType::tcp: { + auto tcpLocalAddr = tcpTable_->table[i].dwLocalAddr; + auto retVal = + InetNtopA(AF_INET, &tcpLocalAddr, localAddr.data(), localAddr.size()); + if (retVal == nullptr) { + TLOG << "Error converting network local address to string: " + << WSAGetLastError(); + } + r["local_port"] = + INTEGER(ntohs(static_cast(tcpTable_->table[i].dwLocalPort))); + auto tcpRemoteAddr = tcpTable_->table[i].dwRemoteAddr; + retVal = InetNtopA( + AF_INET, &tcpRemoteAddr, remoteAddr.data(), remoteAddr.size()); + if (retVal == nullptr) { + TLOG << "Error converting network remote address to string: " + << WSAGetLastError(); + } + r["remote_address"] = remoteAddr.data(); + r["remote_port"] = INTEGER( + ntohs(static_cast(tcpTable_->table[i].dwRemotePort))); + r["pid"] = INTEGER(tcpTable_->table[i].dwOwningPid); + r["family"] = INTEGER(AF_INET); + break; + } + + case WinSockTableType::tcp6: { + auto tcp6LocalAddr = tcp6Table_->table[i].ucLocalAddr; + auto retVal = InetNtopA( + AF_INET6, tcp6LocalAddr, localAddr.data(), localAddr.size()); + if (retVal == nullptr) { + TLOG << "Error converting network local address to string: " + << WSAGetLastError(); + } + r["local_port"] = INTEGER( + ntohs(static_cast(tcp6Table_->table[i].dwLocalPort))); + auto tcp6RemoteAddr = tcp6Table_->table[i].ucRemoteAddr; + retVal = InetNtopA( + AF_INET6, tcp6RemoteAddr, remoteAddr.data(), remoteAddr.size()); + if (retVal == nullptr) { + TLOG << "Error converting network remote address to string: " + << WSAGetLastError(); + } + r["remote_address"] = remoteAddr.data(); + r["remote_port"] = INTEGER( + ntohs(static_cast(tcp6Table_->table[i].dwRemotePort))); + r["pid"] = INTEGER(tcp6Table_->table[i].dwOwningPid); + r["family"] = INTEGER(AF_INET6); + break; + } + + case WinSockTableType::udp: { + auto udpLocalAddr = udpTable_->table[i].dwLocalAddr; + auto retVal = + InetNtopA(AF_INET, &udpLocalAddr, localAddr.data(), localAddr.size()); + if (retVal == nullptr) { + TLOG << "Error converting network local address to string: " + << WSAGetLastError(); + } + r["local_port"] = + INTEGER(ntohs(static_cast(udpTable_->table[i].dwLocalPort))); + r["remote_address"] = "0"; + r["remote_port"] = INTEGER(0); + r["pid"] = INTEGER(udpTable_->table[i].dwOwningPid); + r["family"] = INTEGER(AF_INET); + break; + } + + case WinSockTableType::udp6: { + auto udp6LocalAddr = udp6Table_->table[i].ucLocalAddr; + auto retVal = InetNtopA( + AF_INET6, udp6LocalAddr, localAddr.data(), localAddr.size()); + if (retVal == nullptr) { + TLOG << "Error converting network local address to string: " + << WSAGetLastError(); + } + r["local_port"] = INTEGER( + ntohs(static_cast(udp6Table_->table[i].dwLocalPort))); + r["remote_address"] = "0"; + r["remote_port"] = INTEGER(0); + r["pid"] = INTEGER(udp6Table_->table[i].dwOwningPid); + r["family"] = INTEGER(AF_INET6); + break; + } + default: + break; + } + + r["local_address"] = localAddr.data(); + results.push_back(r); + } +} + +void* WinSockets::allocateSocketTable(unsigned long protocol, + unsigned long family) { + unsigned long ret = 0; + unsigned long buffsize = 0; + void* pSockTable = nullptr; + + /// Allocate the TCP Socket Tables + if (protocol == IPPROTO_TCP) { + ret = GetExtendedTcpTable( + pSockTable, &buffsize, true, family, TCP_TABLE_OWNER_PID_ALL, 0); + if (ret == ERROR_INSUFFICIENT_BUFFER) { + pSockTable = static_cast(malloc(buffsize)); + if (pSockTable == nullptr) { + status_ = Status( + 1, "Unable to allocate sufficient memory for the TCP socket table"); + } + } + ret = GetExtendedTcpTable(pSockTable, + reinterpret_cast(&buffsize), + true, + family, + TCP_TABLE_OWNER_PID_ALL, + 0); + if (ret != NO_ERROR) { + status_ = Status(1, + "Error retrieving the socket table: ( " + + std::to_string(GetLastError()) + " )"); + } + } + /// Allocate the UDP Socket Tables + else { + ret = GetExtendedUdpTable(pSockTable, + reinterpret_cast(&buffsize), + true, + family, + UDP_TABLE_OWNER_PID, + 0); + if (ret == ERROR_INSUFFICIENT_BUFFER) { + pSockTable = static_cast(malloc(buffsize)); + if (pSockTable == nullptr) { + status_ = Status( + 1, "Unable to allocate sufficient memory for the UDP socket table"); + } + } + ret = GetExtendedUdpTable(pSockTable, + reinterpret_cast(&buffsize), + true, + family, + UDP_TABLE_OWNER_PID, + 0); + if (ret != NO_ERROR) { + status_ = Status(1, + "Error retrieving the socket table: ( " + + std::to_string(GetLastError()) + " )"); + } + } + return pSockTable; +} + +QueryData genOpenSockets(QueryContext& context) { + QueryData results; + WinSockets sockTable; + + sockTable.parseSocketTable(WinSockTableType::tcp, results); + + sockTable.parseSocketTable(WinSockTableType::tcp6, results); + + sockTable.parseSocketTable(WinSockTableType::udp, results); + + sockTable.parseSocketTable(WinSockTableType::udp6, results); + + return results; +} +} +} diff --git a/osquery/tables/networking/windows/win_sockets.h b/osquery/tables/networking/windows/win_sockets.h new file mode 100644 index 00000000..7e746d20 --- /dev/null +++ b/osquery/tables/networking/windows/win_sockets.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#pragma once + +#include +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include + +namespace osquery { +namespace tables { + +enum class WinSockTableType { tcp, tcp6, udp, udp6 }; + +class WinSockets : private boost::noncopyable { + public: + /// Retreives all of the socket table structures from the Windows API + WinSockets(); + + /// Ensures that all Socket tables have been deallocated + ~WinSockets(); + + /// Parses all of the socket entries and populates the results QueryData + void parseSocketTable(WinSockTableType sockType, QueryData& results); + + /// Returns the status of the Sockets Table + Status getStatus() const { + return status_; + }; + + private: + Status status_; + MIB_TCPTABLE_OWNER_PID* tcpTable_ = nullptr; + MIB_TCP6TABLE_OWNER_PID* tcp6Table_ = nullptr; + MIB_UDPTABLE_OWNER_PID* udpTable_ = nullptr; + MIB_UDP6TABLE_OWNER_PID* udp6Table_ = nullptr; + + /// Helper function to allocate a table based off of family and protocol + void* WinSockets::allocateSocketTable(unsigned long protocol, + unsigned long family); +}; +} +} diff --git a/specs/posix/listening_ports.table b/specs/listening_ports.table similarity index 100% rename from specs/posix/listening_ports.table rename to specs/listening_ports.table diff --git a/specs/posix/process_open_sockets.table b/specs/process_open_sockets.table similarity index 100% rename from specs/posix/process_open_sockets.table rename to specs/process_open_sockets.table