mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-09-27 01:00:34 +03:00
network: NIC spoof matching tunnel (#912)
## Link to GitHub Issue or related Pull Request, if one exists #911 ## Description of change Add tunnelhost/tunnelclient modes to -nicspoof so two cabs can do LAN-style BPL matching over a real UDP path (including Wine), while keeping the fake overlay NIC. Offline spoof remains available as before. ## Testing DDR
This commit is contained in:
@@ -617,6 +617,7 @@ set(SOURCE_FILES ${SOURCE_FILES}
|
||||
hooks/icmphook_net.cpp
|
||||
hooks/icmphook_iphlpapi.cpp
|
||||
hooks/nicspoof.cpp
|
||||
hooks/nicspoof_tunnel.cpp
|
||||
hooks/powrprof.cpp
|
||||
#hooks/rom.cpp
|
||||
hooks/setupapihook.cpp
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "icmphook_net.h"
|
||||
|
||||
#include "hooks/nicspoof.h"
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
@@ -353,7 +354,11 @@ int WINAPI bind_hook_ws2(SOCKET s, const sockaddr *name, int namelen) {
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(g_mu);
|
||||
auto it = g_socks.find(s);
|
||||
if (it != g_socks.end() && name && namelen >= (int) sizeof(sockaddr_in)) {
|
||||
if (it != g_socks.end()) {
|
||||
if (!name || namelen < (int) sizeof(sockaddr_in)) {
|
||||
WSASetLastError(WSAEFAULT);
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
auto *in = reinterpret_cast<const sockaddr_in *>(name);
|
||||
if (in->sin_family != AF_INET) {
|
||||
WSASetLastError(WSAEAFNOSUPPORT);
|
||||
@@ -618,7 +623,10 @@ void install_icmphook_hooks() {
|
||||
}
|
||||
done = true;
|
||||
|
||||
const bool defer_divert = nicspoof_tunnel_enabled();
|
||||
bool ok = true;
|
||||
|
||||
// Always own socket creation so raw ICMP sockets become emulated.
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "socket",
|
||||
(void *) socket_hook, (void **) &socket_orig);
|
||||
@@ -628,37 +636,52 @@ void install_icmphook_hooks() {
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "WSASocketA",
|
||||
(void *) WSASocketA_hook, (void **) &WSASocketA_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "closesocket",
|
||||
(void *) closesocket_hook, (void **) &closesocket_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "bind",
|
||||
(void *) bind_hook_ws2, (void **) &bind_trampoline_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "sendto",
|
||||
(void *) sendto_hook, (void **) &sendto_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "recvfrom",
|
||||
(void *) recvfrom_hook, (void **) &recvfrom_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "WSASendTo",
|
||||
(void *) WSASendTo_hook, (void **) &WSASendTo_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "WSARecvFrom",
|
||||
(void *) WSARecvFrom_hook, (void **) &WSARecvFrom_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "ioctlsocket",
|
||||
(void *) ioctlsocket_hook, (void **) &ioctlsocket_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "setsockopt",
|
||||
(void *) setsockopt_hook, (void **) &setsockopt_orig);
|
||||
|
||||
if (!ok) {
|
||||
if (defer_divert) {
|
||||
// NIC tunnel already hooked bind/sendto/recvfrom/closesocket/ioctlsocket.
|
||||
// Tunnel hooks call icmphook_try_* for emulated ICMP sockets.
|
||||
log_info("network",
|
||||
"ICMP emulation: socket hooks installed; divert deferred to "
|
||||
"NIC tunnel (icmphook_try_*)");
|
||||
} else {
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "closesocket",
|
||||
(void *) closesocket_hook, (void **) &closesocket_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "bind",
|
||||
(void *) bind_hook_ws2, (void **) &bind_trampoline_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "sendto",
|
||||
(void *) sendto_hook, (void **) &sendto_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "recvfrom",
|
||||
(void *) recvfrom_hook, (void **) &recvfrom_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "WSASendTo",
|
||||
(void *) WSASendTo_hook, (void **) &WSASendTo_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "WSARecvFrom",
|
||||
(void *) WSARecvFrom_hook, (void **) &WSARecvFrom_orig);
|
||||
ok &= detour::trampoline_try(
|
||||
"ws2_32.dll", "ioctlsocket",
|
||||
(void *) ioctlsocket_hook, (void **) &ioctlsocket_orig);
|
||||
|
||||
if (!ok) {
|
||||
log_warning(
|
||||
"network",
|
||||
"ICMP emulation: one or more ws2_32 hooks failed to install");
|
||||
} else {
|
||||
log_info("network", "ICMP emulation hooks installed (raw ICMP sockets)");
|
||||
}
|
||||
}
|
||||
|
||||
if (defer_divert && !ok) {
|
||||
log_warning(
|
||||
"network",
|
||||
"ICMP emulation: one or more ws2_32 hooks failed to install");
|
||||
} else {
|
||||
log_info("network", "ICMP emulation hooks installed (raw ICMP sockets)");
|
||||
"ICMP emulation: one or more socket-creation hooks failed");
|
||||
}
|
||||
|
||||
g_installed.store(true, std::memory_order_release);
|
||||
@@ -686,6 +709,66 @@ bool icmphook_try_bind(SOCKET s, const struct sockaddr *name, int namelen, int *
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icmphook_try_sendto(SOCKET s, const char *buf, int len, int flags,
|
||||
const sockaddr *to, int tolen, int *out_result) {
|
||||
if (!icmphook_is_emulated_socket(s)) {
|
||||
return false;
|
||||
}
|
||||
*out_result = icmphook_internal::sendto_hook(s, buf, len, flags, to, tolen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icmphook_try_recvfrom(SOCKET s, char *buf, int len, int flags,
|
||||
sockaddr *from, int *fromlen, int *out_result) {
|
||||
if (!icmphook_is_emulated_socket(s)) {
|
||||
return false;
|
||||
}
|
||||
*out_result = icmphook_internal::recvfrom_hook(s, buf, len, flags, from, fromlen);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icmphook_try_WSASendTo(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
|
||||
LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const sockaddr *lpTo,
|
||||
int iTolen, LPWSAOVERLAPPED lpOverlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, int *out_result) {
|
||||
if (!icmphook_is_emulated_socket(s)) {
|
||||
return false;
|
||||
}
|
||||
*out_result = icmphook_internal::WSASendTo_hook(
|
||||
s, lpBuffers, dwBufferCount, lpNumberOfBytesSent, dwFlags, lpTo,
|
||||
iTolen, lpOverlapped, lpCompletionRoutine);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icmphook_try_WSARecvFrom(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
|
||||
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, sockaddr *lpFrom,
|
||||
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, int *out_result) {
|
||||
if (!icmphook_is_emulated_socket(s)) {
|
||||
return false;
|
||||
}
|
||||
*out_result = icmphook_internal::WSARecvFrom_hook(
|
||||
s, lpBuffers, dwBufferCount, lpNumberOfBytesRecvd, lpFlags, lpFrom,
|
||||
lpFromlen, lpOverlapped, lpCompletionRoutine);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icmphook_try_ioctlsocket(SOCKET s, long cmd, u_long *argp, int *out_result) {
|
||||
if (!icmphook_is_emulated_socket(s)) {
|
||||
return false;
|
||||
}
|
||||
*out_result = icmphook_internal::ioctlsocket_hook(s, cmd, argp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool icmphook_try_closesocket(SOCKET s, int *out_result) {
|
||||
if (!icmphook_is_emulated_socket(s)) {
|
||||
return false;
|
||||
}
|
||||
*out_result = icmphook_internal::closesocket_hook(s);
|
||||
return true;
|
||||
}
|
||||
|
||||
void icmphook_net_init() {
|
||||
icmphook_internal::install_icmphook_hooks();
|
||||
icmphook_iphlpapi_install();
|
||||
|
||||
@@ -6,8 +6,27 @@ bool icmphook_is_emulated_socket(SOCKET s);
|
||||
|
||||
/*!
|
||||
* Handle bind() for emulated ICMP sockets: records interface address and succeeds without kernel bind.
|
||||
* Returns true if this socket was handled (caller should return 0).
|
||||
* Returns true if this socket was handled (caller should return *out_result).
|
||||
*/
|
||||
bool icmphook_try_bind(SOCKET s, const struct sockaddr *name, int namelen, int *out_result);
|
||||
|
||||
/*!
|
||||
* Divert-path helpers for when NIC tunnel owns the MinHook slots on these APIs.
|
||||
* Return true if the socket is an emulated ICMP socket and was fully handled.
|
||||
*/
|
||||
bool icmphook_try_sendto(SOCKET s, const char *buf, int len, int flags,
|
||||
const sockaddr *to, int tolen, int *out_result);
|
||||
bool icmphook_try_recvfrom(SOCKET s, char *buf, int len, int flags,
|
||||
sockaddr *from, int *fromlen, int *out_result);
|
||||
bool icmphook_try_WSASendTo(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
|
||||
LPDWORD lpNumberOfBytesSent, DWORD dwFlags, const sockaddr *lpTo,
|
||||
int iTolen, LPWSAOVERLAPPED lpOverlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, int *out_result);
|
||||
bool icmphook_try_WSARecvFrom(SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount,
|
||||
LPDWORD lpNumberOfBytesRecvd, LPDWORD lpFlags, sockaddr *lpFrom,
|
||||
LPINT lpFromlen, LPWSAOVERLAPPED lpOverlapped,
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, int *out_result);
|
||||
bool icmphook_try_ioctlsocket(SOCKET s, long cmd, u_long *argp, int *out_result);
|
||||
bool icmphook_try_closesocket(SOCKET s, int *out_result);
|
||||
|
||||
void icmphook_net_init();
|
||||
|
||||
+131
-32
@@ -14,26 +14,32 @@
|
||||
|
||||
#include "util/detour.h"
|
||||
#include "util/logging.h"
|
||||
#include "hooks/nicspoof_tunnel.h"
|
||||
|
||||
namespace {
|
||||
constexpr uint32_t k_local_ip = 0x0A390302u; /* 10.57.3.2 */
|
||||
constexpr uint32_t k_gateway = 0x0A390301u; /* 10.57.3.1 */
|
||||
constexpr uint32_t k_dns = 0x0A390301u; /* 10.57.3.1 */
|
||||
constexpr uint32_t k_dhcp = 0x0A390301u; /* 10.57.3.1 */
|
||||
constexpr uint32_t k_mask = 0xFFFFFF00u; /* 255.255.255.0 */
|
||||
constexpr uint32_t k_subnet = 0x0A390300u;
|
||||
constexpr int k_prefix_len = 24;
|
||||
namespace nicspoof_detail {
|
||||
NicSpoofConfig g_cfg;
|
||||
|
||||
constexpr uint32_t k_default_local_ip = 0x0A64640Au; /* 10.100.100.10 */
|
||||
constexpr uint32_t k_gateway = 0x0A646401u; /* 10.100.100.1 */
|
||||
constexpr uint32_t k_dns1 = 0x0A02010Au; /* 10.2.1.10 */
|
||||
constexpr uint32_t k_dns2 = 0x0A02011Eu; /* 10.2.1.30 */
|
||||
constexpr uint32_t k_dhcp = 0xC0A80001u; /* 192.168.0.1 */
|
||||
constexpr uint32_t k_mask = 0xFF000000u; /* 255.0.0.0 */
|
||||
constexpr int k_prefix_len = 8;
|
||||
constexpr DWORD k_ifindex = 77;
|
||||
constexpr uint8_t k_fake_mac[6] = {0x12, 0x37, 0x13, 0x37, 0x13, 0x37};
|
||||
constexpr char k_hostname[] = "N1CSP00F";
|
||||
constexpr char k_domain[] = "local";
|
||||
constexpr char k_hostname_fqdn[] = "N1CSP00F.local";
|
||||
constexpr wchar_t k_domain_w[] = L"local";
|
||||
constexpr char k_hostname[] = "N1C5P00F";
|
||||
constexpr char k_domain[] = "sp2x";
|
||||
constexpr char k_hostname_fqdn[] = "N1C5P00F.sp2x";
|
||||
constexpr wchar_t k_domain_w[] = L"sp2x";
|
||||
constexpr char k_adapter_name[] = "{NICSPOOF-0880-0001-4250-4E6963537066}";
|
||||
constexpr char k_adapter_desc[] = "NicSpoof Virtual Ethernet";
|
||||
constexpr wchar_t k_friendly_name[] = L"NicSpoof";
|
||||
constexpr wchar_t k_adapter_desc_w[] = L"NicSpoof Virtual Ethernet";
|
||||
|
||||
uint32_t g_local_ip = k_default_local_ip;
|
||||
uint32_t g_subnet = k_default_local_ip & k_mask;
|
||||
|
||||
#define ALIGN_UP_PTR(p, a) \
|
||||
((BYTE *)(((ULONG_PTR)(p) + ((ULONG_PTR)(a) - 1)) & ~((ULONG_PTR)(a) - 1)))
|
||||
|
||||
@@ -125,7 +131,7 @@ DWORD WINAPI GetAdaptersInfo_hook(PIP_ADAPTER_INFO p, PULONG s) {
|
||||
memcpy(p->Address, k_fake_mac, 6);
|
||||
p->Type = MIB_IF_TYPE_ETHERNET;
|
||||
p->DhcpEnabled = 1;
|
||||
ip_to_str(k_local_ip, ip, sizeof(ip));
|
||||
ip_to_str(g_local_ip, ip, sizeof(ip));
|
||||
ip_to_str(k_gateway, gw, sizeof(gw));
|
||||
ip_to_str(k_dhcp, dhcp, sizeof(dhcp));
|
||||
strcpy(p->IpAddressList.IpAddress.String, ip);
|
||||
@@ -151,8 +157,11 @@ DWORD WINAPI GetAdaptersInfo_hook(PIP_ADAPTER_INFO p, PULONG s) {
|
||||
}
|
||||
|
||||
DWORD WINAPI GetNetworkParams_hook(PFIXED_INFO p, PULONG s) {
|
||||
ULONG need = static_cast<ULONG>(sizeof(FIXED_INFO));
|
||||
char dns[16];
|
||||
/* FIXED_INFO embeds the first IP_ADDR_STRING; second follows in the buffer. */
|
||||
ULONG need = static_cast<ULONG>(sizeof(FIXED_INFO) + sizeof(IP_ADDR_STRING));
|
||||
char dns1[16];
|
||||
char dns2[16];
|
||||
PIP_ADDR_STRING dns_second = nullptr;
|
||||
|
||||
if (!s) {
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@@ -164,18 +173,27 @@ DWORD WINAPI GetNetworkParams_hook(PFIXED_INFO p, PULONG s) {
|
||||
memset(p, 0, need);
|
||||
strncpy(p->HostName, k_hostname, sizeof(p->HostName) - 1);
|
||||
strncpy(p->DomainName, k_domain, sizeof(p->DomainName) - 1);
|
||||
ip_to_str(k_dns, dns, sizeof(dns));
|
||||
strcpy(p->DnsServerList.IpAddress.String, dns);
|
||||
ip_to_str(k_dns1, dns1, sizeof(dns1));
|
||||
ip_to_str(k_dns2, dns2, sizeof(dns2));
|
||||
strcpy(p->DnsServerList.IpAddress.String, dns1);
|
||||
p->DnsServerList.IpMask.String[0] = 0;
|
||||
p->DnsServerList.Context = 0;
|
||||
p->DnsServerList.Next = nullptr;
|
||||
dns_second = reinterpret_cast<PIP_ADDR_STRING>(
|
||||
reinterpret_cast<BYTE *>(p) + sizeof(FIXED_INFO));
|
||||
memset(dns_second, 0, sizeof(*dns_second));
|
||||
strcpy(dns_second->IpAddress.String, dns2);
|
||||
dns_second->IpMask.String[0] = 0;
|
||||
dns_second->Context = 0;
|
||||
dns_second->Next = nullptr;
|
||||
p->DnsServerList.Next = dns_second;
|
||||
p->CurrentDnsServer = &p->DnsServerList;
|
||||
p->NodeType = 1;
|
||||
p->EnableDns = 1;
|
||||
*s = need;
|
||||
if (g_log_params) {
|
||||
log_misc("network", "NIC spoof GetNetworkParams host={} domain={} dns={}",
|
||||
k_hostname, k_domain, dns);
|
||||
log_misc("network",
|
||||
"NIC spoof GetNetworkParams host={} domain={} dns={} dns2={}",
|
||||
k_hostname, k_domain, dns1, dns2);
|
||||
g_log_params = false;
|
||||
}
|
||||
return ERROR_SUCCESS;
|
||||
@@ -197,19 +215,21 @@ ULONG WINAPI GetAdaptersAddresses_hook(
|
||||
sizeof(void *) * 8 +
|
||||
sizeof(IP_ADAPTER_UNICAST_ADDRESS) +
|
||||
sizeof(IP_ADAPTER_PREFIX) +
|
||||
sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) +
|
||||
sizeof(IP_ADAPTER_DNS_SERVER_ADDRESS) * 2 +
|
||||
sizeof(IP_ADAPTER_GATEWAY_ADDRESS) +
|
||||
sizeof(sockaddr_in) * 4 + 64);
|
||||
sizeof(sockaddr_in) * 5 + 64);
|
||||
BYTE *blob = nullptr;
|
||||
PIP_ADAPTER_ADDRESSES a = nullptr;
|
||||
PIP_ADAPTER_UNICAST_ADDRESS u = nullptr;
|
||||
PIP_ADAPTER_PREFIX pref = nullptr;
|
||||
PIP_ADAPTER_GATEWAY_ADDRESS gw = nullptr;
|
||||
PIP_ADAPTER_DNS_SERVER_ADDRESS dns = nullptr;
|
||||
PIP_ADAPTER_DNS_SERVER_ADDRESS dns2 = nullptr;
|
||||
sockaddr_in *sa = nullptr;
|
||||
sockaddr_in *sm = nullptr;
|
||||
sockaddr_in *sg = nullptr;
|
||||
sockaddr_in *ds = nullptr;
|
||||
sockaddr_in *ds2 = nullptr;
|
||||
|
||||
(void)Reserved;
|
||||
(void)Flags;
|
||||
@@ -267,7 +287,7 @@ ULONG WINAPI GetAdaptersAddresses_hook(
|
||||
blob += sizeof(*sa);
|
||||
memset(sa, 0, sizeof(*sa));
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_addr.s_addr = htonl(k_local_ip);
|
||||
sa->sin_addr.s_addr = htonl(g_local_ip);
|
||||
u->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sa);
|
||||
u->Address.iSockaddrLength = sizeof(*sa);
|
||||
a->FirstUnicastAddress = u;
|
||||
@@ -282,7 +302,7 @@ ULONG WINAPI GetAdaptersAddresses_hook(
|
||||
blob += sizeof(*sm);
|
||||
memset(sm, 0, sizeof(*sm));
|
||||
sm->sin_family = AF_INET;
|
||||
sm->sin_addr.s_addr = htonl(k_subnet);
|
||||
sm->sin_addr.s_addr = htonl(g_subnet);
|
||||
pref->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(sm);
|
||||
pref->Address.iSockaddrLength = sizeof(*sm);
|
||||
a->FirstPrefix = pref;
|
||||
@@ -309,10 +329,23 @@ ULONG WINAPI GetAdaptersAddresses_hook(
|
||||
blob += sizeof(*ds);
|
||||
memset(ds, 0, sizeof(*ds));
|
||||
ds->sin_family = AF_INET;
|
||||
ds->sin_addr.s_addr = htonl(k_dns);
|
||||
ds->sin_addr.s_addr = htonl(k_dns1);
|
||||
dns->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(ds);
|
||||
dns->Address.iSockaddrLength = sizeof(*ds);
|
||||
dns->Next = nullptr;
|
||||
|
||||
blob = ALIGN_UP_PTR(blob, sizeof(void *));
|
||||
dns2 = reinterpret_cast<PIP_ADAPTER_DNS_SERVER_ADDRESS>(blob);
|
||||
blob += sizeof(*dns2);
|
||||
memset(dns2, 0, sizeof(*dns2));
|
||||
ds2 = reinterpret_cast<sockaddr_in *>(blob);
|
||||
blob += sizeof(*ds2);
|
||||
memset(ds2, 0, sizeof(*ds2));
|
||||
ds2->sin_family = AF_INET;
|
||||
ds2->sin_addr.s_addr = htonl(k_dns2);
|
||||
dns2->Address.lpSockaddr = reinterpret_cast<LPSOCKADDR>(ds2);
|
||||
dns2->Address.iSockaddrLength = sizeof(*ds2);
|
||||
dns2->Next = nullptr;
|
||||
dns->Next = dns2;
|
||||
a->FirstDnsServerAddress = dns;
|
||||
|
||||
a->Next = nullptr;
|
||||
@@ -404,7 +437,7 @@ INT WSAAPI getaddrinfo_hook(
|
||||
if (!pNodeName || !pNodeName[0] || name_is_local(pNodeName)) {
|
||||
if (pNodeName && (_stricmp(pNodeName, k_hostname) == 0 ||
|
||||
_stricmp(pNodeName, k_hostname_fqdn) == 0)) {
|
||||
ip = k_local_ip;
|
||||
ip = g_local_ip;
|
||||
} else {
|
||||
ip = 0x7F000001u;
|
||||
}
|
||||
@@ -412,7 +445,7 @@ INT WSAAPI getaddrinfo_hook(
|
||||
ip = parse_ipv4(pNodeName);
|
||||
} else {
|
||||
/* offline=1: map external names to local_ip */
|
||||
ip = k_local_ip;
|
||||
ip = g_local_ip;
|
||||
}
|
||||
|
||||
if (pServiceName && pServiceName[0]) {
|
||||
@@ -471,7 +504,7 @@ void install_nicspoof_hooks() {
|
||||
char ipstr[16];
|
||||
char maskstr[16];
|
||||
char gwstr[16];
|
||||
ip_to_str(k_local_ip, ipstr, sizeof(ipstr));
|
||||
ip_to_str(g_local_ip, ipstr, sizeof(ipstr));
|
||||
ip_to_str(k_mask, maskstr, sizeof(maskstr));
|
||||
ip_to_str(k_gateway, gwstr, sizeof(gwstr));
|
||||
log_info("network",
|
||||
@@ -508,7 +541,6 @@ void install_nicspoof_hooks() {
|
||||
(void *) freeaddrinfo_hook,
|
||||
(void **) &freeaddrinfo_orig);
|
||||
|
||||
/* FreeAddrInfoA is often the same address; only hook if distinct. */
|
||||
{
|
||||
HMODULE ws = GetModuleHandleA("ws2_32.dll");
|
||||
void *faa = nullptr;
|
||||
@@ -539,8 +571,75 @@ void install_nicspoof_hooks() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
bool ip_in_overlay_range(uint32_t ip) {
|
||||
const uint32_t net = k_gateway & k_mask;
|
||||
const uint32_t bcast = net | ~k_mask;
|
||||
if ((ip & k_mask) != net) {
|
||||
return false;
|
||||
}
|
||||
if (ip == net || ip == bcast || ip == k_gateway ||
|
||||
ip == k_dns1 || ip == k_dns2) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_impl() {
|
||||
if (g_cfg.mode == NicSpoofMode::Off) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_local_ip = g_cfg.local_ip ? g_cfg.local_ip : k_default_local_ip;
|
||||
if (!ip_in_overlay_range(g_local_ip)) {
|
||||
char bad[16];
|
||||
ip_to_str(g_local_ip, bad, sizeof(bad));
|
||||
log_warning("network",
|
||||
"NIC spoof: IP {} is outside 10.0.0.0/8 usable range; "
|
||||
"using 10.100.100.10",
|
||||
bad);
|
||||
g_local_ip = k_default_local_ip;
|
||||
}
|
||||
g_subnet = g_local_ip & k_mask;
|
||||
|
||||
const char *mode_str = "offline";
|
||||
if (g_cfg.mode == NicSpoofMode::TunnelHost) {
|
||||
mode_str = "tunnelhost";
|
||||
} else if (g_cfg.mode == NicSpoofMode::TunnelClient) {
|
||||
mode_str = "tunnelclient";
|
||||
}
|
||||
log_info("network", "NIC spoof mode={}", mode_str);
|
||||
|
||||
install_nicspoof_hooks();
|
||||
|
||||
if (g_cfg.mode == NicSpoofMode::TunnelHost ||
|
||||
g_cfg.mode == NicSpoofMode::TunnelClient) {
|
||||
nicspoof_tunnel_init(g_cfg);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nicspoof_detail
|
||||
|
||||
void nicspoof_configure(const NicSpoofConfig &cfg) {
|
||||
nicspoof_detail::g_cfg = cfg;
|
||||
}
|
||||
|
||||
bool nicspoof_tunnel_enabled() {
|
||||
return nicspoof_detail::g_cfg.mode == NicSpoofMode::TunnelHost ||
|
||||
nicspoof_detail::g_cfg.mode == NicSpoofMode::TunnelClient;
|
||||
}
|
||||
|
||||
uint32_t nicspoof_local_ip() {
|
||||
return nicspoof_detail::g_local_ip;
|
||||
}
|
||||
|
||||
uint32_t nicspoof_mask() {
|
||||
return nicspoof_detail::k_mask;
|
||||
}
|
||||
|
||||
uint32_t nicspoof_subnet() {
|
||||
return nicspoof_detail::g_subnet;
|
||||
}
|
||||
|
||||
void nicspoof_init() {
|
||||
install_nicspoof_hooks();
|
||||
nicspoof_detail::init_impl();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
enum class NicSpoofMode {
|
||||
Off,
|
||||
Offline,
|
||||
TunnelHost,
|
||||
TunnelClient,
|
||||
};
|
||||
|
||||
struct NicSpoofConfig {
|
||||
NicSpoofMode mode = NicSpoofMode::Off;
|
||||
uint32_t local_ip = 0x0A64640Au; /* 10.100.100.10 */
|
||||
uint16_t tunnel_port = 51820;
|
||||
std::string hub_host;
|
||||
};
|
||||
|
||||
void nicspoof_configure(const NicSpoofConfig &cfg);
|
||||
void nicspoof_init();
|
||||
|
||||
/*! True when mode is TunnelHost or TunnelClient (divert hooks own ws2 sendto/etc.). */
|
||||
bool nicspoof_tunnel_enabled();
|
||||
|
||||
uint32_t nicspoof_local_ip();
|
||||
uint32_t nicspoof_mask();
|
||||
uint32_t nicspoof_subnet();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "nicspoof.h"
|
||||
|
||||
void nicspoof_tunnel_init(const NicSpoofConfig &cfg);
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <shlwapi.h>
|
||||
#include <windows.h>
|
||||
@@ -258,7 +259,7 @@ int main_implementation(int argc, char *argv[]) {
|
||||
bool load_stubs = false;
|
||||
bool netfix_disable = false;
|
||||
bool icmphook_enable = false;
|
||||
bool nicspoof_enable = false;
|
||||
NicSpoofConfig nicspoof_cfg;
|
||||
bool lang_disable = false;
|
||||
std::string process_priority_str = "high";
|
||||
bool cardio_enabled = false;
|
||||
@@ -801,8 +802,43 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::EnableICMPHook].value_bool()) {
|
||||
icmphook_enable = true;
|
||||
}
|
||||
if (options[launcher::Options::EnableNICSpoof].value_bool()) {
|
||||
nicspoof_enable = true;
|
||||
if (options[launcher::Options::EnableNICSpoof].is_active()) {
|
||||
const auto &mode = options[launcher::Options::EnableNICSpoof].value_text();
|
||||
if (mode == "offline" || mode == "/ENABLED") {
|
||||
nicspoof_cfg.mode = NicSpoofMode::Offline;
|
||||
} else if (mode == "tunnelhost") {
|
||||
nicspoof_cfg.mode = NicSpoofMode::TunnelHost;
|
||||
} else if (mode == "tunnelclient") {
|
||||
nicspoof_cfg.mode = NicSpoofMode::TunnelClient;
|
||||
} else {
|
||||
log_warning("launcher", "unknown -nicspoof value '{}', ignoring", mode);
|
||||
}
|
||||
}
|
||||
if (nicspoof_cfg.mode != NicSpoofMode::Off) {
|
||||
if (options[launcher::Options::NICSpoofIP].is_active()) {
|
||||
unsigned a = 0, b = 0, c = 0, d = 0;
|
||||
char trail = 0;
|
||||
const auto &ip = options[launcher::Options::NICSpoofIP].value_text();
|
||||
if (sscanf(ip.c_str(), "%u.%u.%u.%u%c", &a, &b, &c, &d, &trail) == 4 &&
|
||||
a <= 255 && b <= 255 && c <= 255 && d <= 255) {
|
||||
nicspoof_cfg.local_ip = (a << 24) | (b << 16) | (c << 8) | d;
|
||||
} else {
|
||||
log_warning("launcher", "invalid -nicspoofip '{}'", ip);
|
||||
}
|
||||
}
|
||||
if (options[launcher::Options::NICSpoofHostRealIP].is_active()) {
|
||||
nicspoof_cfg.hub_host =
|
||||
options[launcher::Options::NICSpoofHostRealIP].value_text();
|
||||
}
|
||||
if (options[launcher::Options::NICSpoofPort].is_active()) {
|
||||
const uint32_t p =
|
||||
options[launcher::Options::NICSpoofPort].value_uint32();
|
||||
if (p == 0 || p > 65535) {
|
||||
log_warning("launcher", "invalid -nicspoofport {}", p);
|
||||
} else {
|
||||
nicspoof_cfg.tunnel_port = static_cast<uint16_t>(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options[launcher::Options::DisableACPHook].value_bool()) {
|
||||
lang_disable = true;
|
||||
@@ -2558,14 +2594,17 @@ int main_implementation(int argc, char *argv[]) {
|
||||
avs::core::load_dll();
|
||||
avs::ea3::load_dll();
|
||||
|
||||
// ICMP emulation (opt-in; before games open raw ICMP sockets)
|
||||
if (icmphook_enable) {
|
||||
icmphook_net_init();
|
||||
// NIC spoof / matching tunnel first so divert owns overlapping ws2_32
|
||||
// MinHook slots (bind/sendto/recvfrom/...). ICMP then installs only the
|
||||
// non-overlapping socket-creation hooks and is reached via icmphook_try_*.
|
||||
if (nicspoof_cfg.mode != NicSpoofMode::Off) {
|
||||
nicspoof_configure(nicspoof_cfg);
|
||||
nicspoof_init();
|
||||
}
|
||||
|
||||
// NIC spoof (opt-in; fake Ethernet with no real NIC)
|
||||
if (nicspoof_enable) {
|
||||
nicspoof_init();
|
||||
// ICMP emulation (opt-in; after tunnel so hooks do not collide)
|
||||
if (icmphook_enable) {
|
||||
icmphook_net_init();
|
||||
}
|
||||
|
||||
// net fix
|
||||
|
||||
@@ -3188,12 +3188,57 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = std::invoke([]()
|
||||
.category = "Network Dev",
|
||||
};
|
||||
c[launcher::Options::EnableNICSpoof] = {
|
||||
.title = "Enable NIC Spoof",
|
||||
.title = "NIC Spoof",
|
||||
.name = "nicspoof",
|
||||
.desc = "Report a fake Ethernet IPv4 adapter (10.57.3.2) so the game can "
|
||||
"boot when no real NIC is present. DNS names are mapped to that "
|
||||
"address. Does not create a real adapter.",
|
||||
.type = OptionType::Bool,
|
||||
.desc =
|
||||
"Use with -icmphook.\n\n"
|
||||
"offline: report a fake Ethernet IPv4 adapter and map DNS names to "
|
||||
"that address so the game can boot when no real NIC is present. "
|
||||
"Does not create a real adapter.\n\n"
|
||||
"tunnelhost: same fake NIC, plus listen for a matching tunnel "
|
||||
"from a peer.\n\n"
|
||||
"tunnelclient: same fake NIC, plus connect to a tunnel host. "
|
||||
"Set -nicspoofhostrealip.",
|
||||
.type = OptionType::Enum,
|
||||
.category = "Network Dev",
|
||||
.elements = {
|
||||
{"offline", ""},
|
||||
{"tunnelhost", ""},
|
||||
{"tunnelclient", ""},
|
||||
},
|
||||
};
|
||||
c[launcher::Options::NICSpoofIP] = {
|
||||
.title = "NIC Spoof IP",
|
||||
.name = "nicspoofip",
|
||||
.desc =
|
||||
"Overlay IPv4 for the fake adapter. Used when NIC Spoof is not Default. "
|
||||
"If unset, 10.100.100.10 is used.\n\n"
|
||||
"Must be on the same subnet as gateway 10.100.100.1 with mask "
|
||||
"255.0.0.0 (10.0.0.0/8). Examples: 10.100.100.10, 10.0.0.10. "
|
||||
"Do not use 10.0.0.0, 10.255.255.255, the gateway 10.100.100.1, "
|
||||
"or DNS 10.2.1.10 / 10.2.1.30. Host and client must use different IPs.",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "10.100.100.10",
|
||||
.category = "Network Dev",
|
||||
};
|
||||
c[launcher::Options::NICSpoofHostRealIP] = {
|
||||
.title = "NIC Spoof Tunnel Host",
|
||||
.name = "nicspoofhostrealip",
|
||||
.desc =
|
||||
"Real IP address or hostname of the tunnel host. Required for "
|
||||
"tunnel client mode. Not used for offline or tunnel host.",
|
||||
.type = OptionType::Text,
|
||||
.setting_name = "192.168.1.10",
|
||||
.category = "Network Dev",
|
||||
};
|
||||
c[launcher::Options::NICSpoofPort] = {
|
||||
.title = "NIC Spoof Tunnel Port",
|
||||
.name = "nicspoofport",
|
||||
.desc =
|
||||
"UDP port for the matching tunnel. Host listens on this port; "
|
||||
"client connects to it. If unset, 51820 is used.",
|
||||
.type = OptionType::Integer,
|
||||
.setting_name = "51820",
|
||||
.category = "Network Dev",
|
||||
};
|
||||
c[launcher::Options::AutoElevate] = {
|
||||
|
||||
@@ -317,6 +317,9 @@ namespace launcher {
|
||||
DisableHighResTimer,
|
||||
EnableICMPHook,
|
||||
EnableNICSpoof,
|
||||
NICSpoofIP,
|
||||
NICSpoofHostRealIP,
|
||||
NICSpoofPort,
|
||||
AutoElevate,
|
||||
CfgForceSoftwareRender,
|
||||
OBSWebSocketEnabled,
|
||||
|
||||
Reference in New Issue
Block a user