c++ - 在 Boost ASIO 中,如何设置源 IP 地址以模拟另一台服务器的 IP 地址?

标签 c++ tcp boost-asio impersonation arp

我有一个基于 Boost ASIO 的 C++ 服务器程序,我希望能够将 TCP 使用的源 IP 地址设置为另一台服务器的源 IP 地址。我知道可以读取源 IP 地址和目标 IP 地址,但大概也可以设置它们?

大概如果我在 C++ 代码中设置了“错误”的源 IP 地址,将会与网络堆栈进行一些交互。即使 C++ 代码是正确的,网络堆栈不会在退出时重新设置源 IP 地址吗?正确的方法是编写 C++ ASIO 代码来选择特定的虚拟网络接口(interface)吗?一个配置了“错误的”静态 IP 地址?我之前已经看到这是一种控制源 IP 地址的方法。这是我需要做的吗?

我想知道这会带来什么后果。将两台机器配置为相同的静态 IP 地址可能会导致“正常”服务器完全停止工作,这很糟糕。

我可以使用我的服务器的 Windows 和 Linux 端口,以防建议的代码在一个操作系统上运行而不在另一个操作系统上运行。我目前倾向于使用 Kali Linux,因为我可以“欺骗”主服务器并有效地将其关闭一段时间。

最佳答案

通过手动构建网络和传输层 header ,然后将 header 和所需的有效负载发送到 raw socket,可以将源 IP 设置为出站数据的任意地址。 .使用原始套接字可能需要提升权限,或者可能被内核禁用或限制,例如在某些 Microsoft platforms 上.此外,由于 TCP 的三次握手和不可预测的序列号,伪造的 TCP 段的有效性超出了潜在的TCP reset attacks。 , 值得怀疑。

路由是一个不同的主题,取决于各种路由器和配置。例如,设备可能会执行导出过滤并丢弃带有设备无法验证的源地址的数据包。此外,IP 地址冲突的影响可能会有所不同,但通常会导致间歇性连接。


Boost.Asio 提供了一个 basic_raw_socket<Protocol> 需要符合 Protocol 类型的模板类型要求。例如,下面是开始或 raw协议(protocol):

struct raw
{
  typedef boost::asio::ip::basic_endpoint<raw> endpoint;
  int type() const     { return SOCK_RAW;    }
  int protocol() const { return IPPROTO_RAW; }
  int family() const   { return PF_INET;     }
};

boost::asio::basic_raw_socket<raw> socket;

在处理原始套接字时,困难通常不在于使用 Boost.Asio,而在于必须实现网络和传输线协议(protocol)。下面是一个完整的最小示例,我试图通过创建一个 raw 来使其尽可能简单。协议(protocol)和使用 basic_raw_socket发送 UDP 消息:

#include <algorithm>
#include <iostream>

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/cstdint.hpp>

/// @brief raw socket provides the protocol for raw socket.
class raw
{
public:
  ///@brief The type of a raw endpoint.
  typedef boost::asio::ip::basic_endpoint<raw> endpoint;

  ///@brief The raw socket type.
  typedef boost::asio::basic_raw_socket<raw> socket;

  ///@brief The raw resolver type.
  typedef boost::asio::ip::basic_resolver<raw> resolver;

  ///@brief Construct to represent the IPv4 RAW protocol.
  static raw v4()
  {
    return raw(IPPROTO_RAW, PF_INET);
  }

  ///@brief Construct to represent the IPv6 RAW protocol.
  static raw v6()
  {
    return raw(IPPROTO_RAW, PF_INET6);
  }

  ///@brief Default constructor.
  explicit raw()
    : protocol_(IPPROTO_RAW),
      family_(PF_INET)
  {}

  ///@brief Obtain an identifier for the type of the protocol.
  int type() const
  {
    return SOCK_RAW;
  }

  ///@brief Obtain an identifier for the protocol.
  int protocol() const
  {
    return protocol_;
  }

  ///@brief Obtain an identifier for the protocol family.
  int family() const
  {
    return family_;
  }

  ///@brief Compare two protocols for equality.
  friend bool operator==(const raw& p1, const raw& p2)
  {
    return p1.protocol_ == p2.protocol_ && p1.family_ == p2.family_;
  }

  /// Compare two protocols for inequality.
  friend bool operator!=(const raw& p1, const raw& p2)
  {
    return !(p1 == p2);
  }

private:
  explicit raw(int protocol_id, int protocol_family)
    : protocol_(protocol_id),
      family_(protocol_family)
  {}

  int protocol_;
  int family_;
};

///@ brief Mockup ipv4_header for with no options.
//
//  IPv4 wire format:
//
//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-------+-------+-------+-------+-------+-------+-------+------+   ---
// |version|header |    type of    |    total length in bytes     |    ^
// |  (4)  | length|    service    |                              |    |
// +-------+-------+-------+-------+-------+-------+-------+------+    |
// |        identification         |flags|    fragment offset     |    |
// +-------+-------+-------+-------+-------+-------+-------+------+  20 bytes
// | time to live  |   protocol    |       header checksum        |    |
// +-------+-------+-------+-------+-------+-------+-------+------+    |
// |                      source IPv4 address                     |    |
// +-------+-------+-------+-------+-------+-------+-------+------+    |
// |                   destination IPv4 address                   |    v
// +-------+-------+-------+-------+-------+-------+-------+------+   ---
// /                       options (if any)                       /
// +-------+-------+-------+-------+-------+-------+-------+------+
class ipv4_header
{
public:
  ipv4_header() { std::fill(buffer_.begin(), buffer_.end(), 0); }

  void version(boost::uint8_t value) {
    buffer_[0] = (value << 4) | (buffer_[0] & 0x0F);
  }

  void header_length(boost::uint8_t value)
  {
    buffer_[0] = (value & 0x0F) | (buffer_[0] & 0xF0);
  }

  void type_of_service(boost::uint8_t value) { buffer_[1] = value; }
  void total_length(boost::uint16_t value) { encode16(2, value); }
  void identification(boost::uint16_t value) { encode16(4, value); }

  void dont_fragment(bool value)
  {
    buffer_[6] ^= (-value ^ buffer_[6]) & 0x40;
  }

  void more_fragments(bool value)
  {
    buffer_[6] ^= (-value ^ buffer_[6]) & 0x20;
  }

  void fragment_offset(boost::uint16_t value)
  {
     // Preserve flags.
     auto flags = static_cast<uint16_t>(buffer_[6] & 0xE0) << 8;
     encode16(6, (value & 0x1FFF) | flags);
  }

  void time_to_live(boost::uint8_t value) { buffer_[8] = value; }
  void protocol(boost::uint8_t value) { buffer_[9] = value; }
  void checksum(boost::uint16_t value) { encode16(10, value); }

  void source_address(boost::asio::ip::address_v4 value)
  {
    auto bytes = value.to_bytes();
    std::copy(bytes.begin(), bytes.end(), &buffer_[12]);
  }

  void destination_address(boost::asio::ip::address_v4 value)
  {
    auto bytes = value.to_bytes();
    std::copy(bytes.begin(), bytes.end(), &buffer_[16]);
  }

public:

  std::size_t size() const { return buffer_.size(); }

  const boost::array<uint8_t, 20>& data() const { return buffer_; }

private:

  void encode16(boost::uint8_t index, boost::uint16_t value)
  {
    buffer_[index] = (value >> 8) & 0xFF;
    buffer_[index + 1] = value & 0xFF;
  }

  boost::array<uint8_t, 20> buffer_;
};

void calculate_checksum(ipv4_header& header)
{
  // Zero out the checksum.
  header.checksum(0);

  // Checksum is the 16-bit one's complement of the one's complement sum of
  // all 16-bit words in the header.

  // Sum all 16-bit words.
  auto data = header.data();
  auto sum = std::accumulate<boost::uint16_t*, boost::uint32_t>(
    reinterpret_cast<boost::uint16_t*>(&data[0]),
    reinterpret_cast<boost::uint16_t*>(&data[0] + data.size()),
    0);

  // Fold 32-bit into 16-bits.
  while (sum >> 16)
  {
    sum = (sum & 0xFFFF) + (sum >> 16);
  }

  header.checksum(~sum);
}

///@brief Mockup IPv4 UDP header.
//
// UDP wire format:
//
//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-------+-------+-------+-------+-------+-------+-------+------+   ---
// |          source port          |      destination port        |    ^
// +-------+-------+-------+-------+-------+-------+-------+------+  8 bytes
// |            length             |          checksum            |    v
// +-------+-------+-------+-------+-------+-------+-------+------+   ---
// /                        data (if any)                         /
// +-------+-------+-------+-------+-------+-------+-------+------+
class udp_header
{
public:
  udp_header() { std::fill(buffer_.begin(), buffer_.end(), 0); }

  void source_port(boost::uint16_t value)      { encode16(0, value); }
  void destination_port(boost::uint16_t value) { encode16(2, value); }
  void length(boost::uint16_t value)           { encode16(4, value); }
  void checksum(boost::uint16_t value)         { encode16(6, value); }

public:

  std::size_t size() const { return buffer_.size(); }

  const boost::array<uint8_t, 8>& data() const { return buffer_; }

private:

  void encode16(boost::uint8_t index, boost::uint16_t value)
  {
    buffer_[index] = (value >> 8) & 0xFF;
    buffer_[index + 1] = value & 0xFF;
  }

  boost::array<uint8_t, 8> buffer_;
};


int main()
{
  boost::asio::io_service io_service;

  // Create all I/O objects.
  boost::asio::ip::udp::socket receiver(io_service,
    boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0));
  boost::asio::basic_raw_socket<raw> sender(io_service,
    raw::endpoint(raw::v4(), 0));

  const auto receiver_endpoint = receiver.local_endpoint();

  // Craft a UDP message with a payload 'hello' coming from
  // 8.8.8.8:54321
  const boost::asio::ip::udp::endpoint spoofed_endpoint(
    boost::asio::ip::address_v4::from_string("8.8.8.8"),
    54321);
  const std::string payload = "hello";

  // Create the UDP header.
  udp_header udp;
  udp.source_port(spoofed_endpoint.port());
  udp.destination_port(receiver_endpoint.port());
  udp.length(udp.size() + payload.size()); // Header + Payload
  udp.checksum(0); // Optioanl for IPv4

  // Create the IPv4 header.
  ipv4_header ip;
  ip.version(4);                   // IPv4
  ip.header_length(ip.size() / 4); // 32-bit words
  ip.type_of_service(0);           // Differentiated service code point
  auto total_length = ip.size() + udp.size() + payload.size();
  ip.total_length(total_length); // Entire message.
  ip.identification(0);
  ip.dont_fragment(true);
  ip.more_fragments(false);
  ip.fragment_offset(0);
  ip.time_to_live(4);
  ip.source_address(spoofed_endpoint.address().to_v4());
  ip.destination_address(receiver_endpoint.address().to_v4());
  ip.protocol(IPPROTO_UDP);
  calculate_checksum(ip);

  // Gather up all the buffers and send through the raw socket.
  boost::array<boost::asio::const_buffer, 3> buffers = {{
    boost::asio::buffer(ip.data()),
    boost::asio::buffer(udp.data()),
    boost::asio::buffer(payload)
  }};
  auto bytes_transferred = sender.send_to(buffers,
    raw::endpoint(receiver_endpoint.address(), receiver_endpoint.port()));
  assert(bytes_transferred == total_length);

  // Read on the reciever.
  std::vector<char> buffer(payload.size(), '\0');
  boost::asio::ip::udp::endpoint sender_endpoint;
  bytes_transferred = receiver.receive_from(
      boost::asio::buffer(buffer), sender_endpoint);

  // Verify.
  assert(bytes_transferred == payload.size());
  assert(std::string(buffer.begin(), buffer.end()) == payload);
  assert(spoofed_endpoint == sender_endpoint);

  // Print endpoints.
  std::cout <<
    "Actual sender endpoint: " << sender.local_endpoint() << "\n"
    "Receiver endpoint: " << receiver.local_endpoint() << "\n"
    "Receiver's remote endpoint: " << sender_endpoint << std::endl;
}

输出:

$ sudo ./a.out
Actual sender endpoint: 0.0.0.0:255
Receiver endpoint: 0.0.0.0:44806
Receiver's remote endpoint: 8.8.8.8:54321

如输出所示,尽管真正的发送端点是 0.0.0.0:255 ,接收方认为发送方的端点是 8.8.8.8:54321 .

关于c++ - 在 Boost ASIO 中,如何设置源 IP 地址以模拟另一台服务器的 IP 地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28196466/

相关文章:

c++ - 解析函数中的命令行参数

c++ - 在 MSVC2013 中编译良好的代码似乎混淆了 MSVC2015

java - 在 linux 中记录键盘、鼠标 Activity

c++ - 链表中的段错误和 Linux 的调试选项

http - 如何在没有 HTTP 库的情况下发送 404 HTTP 响应?

Go 传输连接在 DNS 更改时保持事件状态

c++ - Mac 无法编译,Windows 和 Linux 可以 - Boost ASIO?

c++ - 同一 boost tcp 套接字对象上的多个连接

boost asio channel "no type named ' receive_cancelled_signature'"

c++ - 在不知道初始文件大小的情况下在 C++ 中创建压缩文件