- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 598
- Reaction score
- 7
Sick of getting clapped by HWID bans because the anti-cheat is querying the miniport block directly? Registry-only spoofing is for scripts and teenagers. If you want to actually hide from EAC and BattlEye, you need to go deeper into the kernel.
I got my hands on this logic that handles MAC spoofing the right way — not by hooking OIDs (which is a massive detection vector), but by scanning the NDIS_MINIPORT_BLOCK and NIC driver device extensions to replace the MAC bytes in-place.
The Technical Concept
Most modern anti-cheats don't just ask Windows for the MAC. They query OID_802_3_PERMANENT_ADDRESS and OID_802_3_CURRENT_ADDRESS which often bypass simple registry overrides. This code scans the memory of ndis.sys and specific hardware drivers for the original physical MAC pattern and nukes it with a random locally-administered address.
Core Implementation
Below is the kernel-mode implementation for scanning and replacing the MAC bytes. Note how it walks the lower device stack to ensure no cached copies of the MAC remain in hardware-specific structures.
Usage Notes
This is a solid base for anyone building a permanent or temporary HWID spoofer. Just keep in mind that modifying memory inside ndis.sys might be flagged if the anti-cheat starts integrity checking these structures, though most currently only focus on code integrity (CR0/PatchGuard stuff).
Anyone tested this on the latest EAC build to see if they're checking miniport block integrity yet?
I got my hands on this logic that handles MAC spoofing the right way — not by hooking OIDs (which is a massive detection vector), but by scanning the NDIS_MINIPORT_BLOCK and NIC driver device extensions to replace the MAC bytes in-place.
The Technical Concept
Most modern anti-cheats don't just ask Windows for the MAC. They query OID_802_3_PERMANENT_ADDRESS and OID_802_3_CURRENT_ADDRESS which often bypass simple registry overrides. This code scans the memory of ndis.sys and specific hardware drivers for the original physical MAC pattern and nukes it with a random locally-administered address.
- ndis.sys (Core NDIS structures)
- ndisuio.sys (User I/O)
- Intel Drivers (e1iexpress, e1dexpress, e1rexpress, igc)
- Realtek Drivers (rt640x64, rt68cx21, rtwlane)
- Broadcom & Mellanox (bnxt, b57nd60a, mlx4eth63)
- Virtual environments (VMware vmxnet3ndis6, Hyper-V netvsc)
Core Implementation
Below is the kernel-mode implementation for scanning and replacing the MAC bytes. Note how it walks the lower device stack to ensure no cached copies of the MAC remain in hardware-specific structures.
Code:
// - NDIS OID_802_3_PERMANENT_ADDRESS → reads from miniport block
// - NDIS OID_802_3_CURRENT_ADDRESS → reads from miniport block
// - Registry NetworkAddress → used as override at driver init
//
// NDIS_MINIPORT_BLOCK stores CurrentAddress and PermanentAddress.
// We don't rely on exact offsets — we scan for the original MAC bytes
// across the entire miniport block and all NIC driver device extensions.
//
// approach:
// 1. Read real MACs from NIC class registry keys
// 2. Generate locally-administered random MACs
// 3. Scan ndis.sys + all NIC driver device extensions for original
// MAC bytes (both in NDIS_MINIPORT_BLOCK and hardware-specific structs)
// 4. Replace in-place (immediate effect on all OID queries)
constexpr ULONG MAX_NICS = 8;
inline UCHAR g_OMac[MAX_NICS][6] = {};
inline UCHAR g_SMac[MAX_NICS][6] = {};
inline ULONG g_NicCnt = 0;
// Read 6-byte MAC from a registry hex string (with or without delimiters)
inline BOOLEAN ParseMacHex(const char* str, UCHAR out[6])
{
ULONG parsed = 0;
for (ULONG c = 0; str[c] && parsed < 6; ) {
while (str[c] == '-' || str[c] == ':') c++;
if (!str[c]) break;
UCHAR hi = 0, lo = 0;
char ch = str[c];
if (ch >= '0' && ch <= '9') hi = (UCHAR)(ch - '0');
else if (ch >= 'A' && ch <= 'F') hi = (UCHAR)(ch - 'A' + 10);
else if (ch >= 'a' && ch <= 'f') hi = (UCHAR)(ch - 'a' + 10);
c++;
ch = str[c];
if (ch >= '0' && ch <= '9') lo = (UCHAR)(ch - '0');
else if (ch >= 'A' && ch <= 'F') lo = (UCHAR)(ch - 'A' + 10);
else if (ch >= 'a' && ch <= 'f') lo = (UCHAR)(ch - 'a' + 10);
c++;
out[parsed++] = (hi << 4) | lo;
}
// Reject zero MACs and broadcast MACs
if (parsed != 6) return FALSE;
BOOLEAN allZero = TRUE, allFF = TRUE;
for (int i = 0; i < 6; i++) { if (out[i]) allZero = FALSE; if (out[i] != 0xFF) allFF = FALSE; }
return !allZero && !allFF;
}
inline void CollectNicMacs()
{
g_NicCnt = 0;
const WCHAR* base = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control"
L"\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
for (ULONG idx = 0; idx < 32 && g_NicCnt < MAX_NICS; idx++)
{
WCHAR instPath[300];
RtlStringCchPrintfW(instPath, 300, L"%ws\\%04lu", base, idx);
// Read DriverDesc to skip virtual NICs
HANDLE hk = nullptr; UNICODE_STRING key; OBJECT_ATTRIBUTES oa;
RtlInitUnicodeString(&key, instPath);
InitializeObjectAttributes(&oa, &key, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, nullptr, nullptr);
if (!NT_SUCCESS(ZwOpenKey(&hk, KEY_READ, &oa))) continue;
WCHAR desc[128] = {};
UCHAR tmp[512] = {}; ULONG rlen = 0; UNICODE_STRING vn;
RtlInitUnicodeString(&vn, L"DriverDesc");
if (NT_SUCCESS(ZwQueryValueKey(hk, &vn, KeyValueFullInformation, tmp, sizeof(tmp), &rlen))) {
auto info = (PKEY_VALUE_FULL_INFORMATION)tmp;
if (info->Type == REG_SZ && info->DataLength > 0)
RtlCopyMemory(desc, (PUCHAR)info + info->DataOffset, min(info->DataLength, sizeof(desc)-2));
}
ZwClose(hk);
if (wcsstr(desc, L"Virtual") || wcsstr(desc, L"VPN") || wcsstr(desc, L"Loopback") ||
wcsstr(desc, L"TAP") || wcsstr(desc, L"WAN") || wcsstr(desc, L"Tunnel") ||
wcsstr(desc, L"Bluetooth") || desc[0] == 0)
continue;
// Read original MAC
char macStr[32] = {};
UCHAR origMac[6] = {};
BOOLEAN gotMac = FALSE;
// Source 1: "NetworkAddress" (current override or original)
if (!gotMac && NT_SUCCESS(RegGetSzA(instPath, L"NetworkAddress", macStr, 32)))
gotMac = ParseMacHex(macStr, origMac);
// Source 2: "OriginalNetworkAddress"
if (!gotMac) {
RtlZeroMemory(macStr, sizeof(macStr));
if (NT_SUCCESS(RegGetSzA(instPath, L"OriginalNetworkAddress", macStr, 32)))
gotMac = ParseMacHex(macStr, origMac);
}
if (!gotMac) continue;
RtlCopyMemory(g_OMac[g_NicCnt], origMac, 6);
// Generate spoofed MAC (locally administered, unicast)
for (int b = 0; b < 6; b++) g_SMac[g_NicCnt][b] = RandByte();
g_SMac[g_NicCnt][0] &= 0xFE; // unicast
g_SMac[g_NicCnt][0] |= 0x02; // locally-administered
// Registry Layer: set NetworkAddress for this adapter
WCHAR newMac[16];
RtlStringCchPrintfW(newMac, 16, L"%02X%02X%02X%02X%02X%02X",
g_SMac[g_NicCnt][0], g_SMac[g_NicCnt][1], g_SMac[g_NicCnt][2],
g_SMac[g_NicCnt][3], g_SMac[g_NicCnt][4], g_SMac[g_NicCnt][5]);
RegSetSz(instPath, L"NetworkAddress", newMac);
g_NicCnt++;
}
}
inline void ScanDriverNics(PCWSTR driverPath)
{
UNICODE_STRING name; RtlInitUnicodeString(&name, driverPath);
PDRIVER_OBJECT drv = nullptr;
if (!NT_SUCCESS(ObReferenceObjectByName(&name, OBJ_CASE_INSENSITIVE,
nullptr, 0, *IoDriverObjectType, KernelMode, nullptr, (PVOID*)&drv)))
return;
if (!drv) return;
PDEVICE_OBJECT dev = drv->DeviceObject;
while (dev)
{
if (!MmIsAddressValid(dev)) break;
if (dev->DeviceExtension && MmIsAddressValid(dev->DeviceExtension))
{
for (ULONG n = 0; n < g_NicCnt; n++)
{
// Replace 6-byte MAC pattern in extension memory
// Covers CurrentAddress, PermanentAddress, and any cached copies
MemReplace(dev->DeviceExtension, 0x4000,
g_OMac[n], 6, g_SMac[n], 6, '\0');
}
}
// Walk lower device stack too
PDEVICE_OBJECT lower = IoGetLowerDeviceObject(dev);
ULONG depth = 0;
while (lower && depth < 10) {
if (!MmIsAddressValid(lower)) break;
if (lower->DeviceExtension && MmIsAddressValid(lower->DeviceExtension)) {
for (ULONG n = 0; n < g_NicCnt; n++)
MemReplace(lower->DeviceExtension, 0x4000,
g_OMac[n], 6, g_SMac[n], 6, '\0');
}
PDEVICE_OBJECT next = IoGetLowerDeviceObject(lower);
ObDereferenceObject(lower);
lower = next; depth++;
}
if (lower) ObDereferenceObject(lower);
dev = dev->NextDevice;
}
ObDereferenceObject(drv);
}
inline void SpoofNics()
{
CollectNicMacs();
if (!g_NicCnt) return;
const WCHAR* drivers[] = {
L"\\Driver\\ndis", // NDIS core — NDIS_MINIPORT_BLOCK structures
L"\\Driver\\ndisuio", // NDIS user I/O
L"\\Driver\\e1iexpress", // Intel 1GbE
L"\\Driver\\e1dexpress", // Intel desktop NIC
L"\\Driver\\e1rexpress", // Intel server NIC
L"\\Driver\\igc", // Intel I225/I226
L"\\Driver\\rt640x64", // Realtek RTL8111/8168
L"\\Driver\\rt68cx21", // Realtek 2.5GbE
L"\\Driver\\rtwlane", // Realtek WiFi
L"\\Driver\\bnxt", // Broadcom NetXtreme
L"\\Driver\\b57nd60a", // Broadcom 57xx
L"\\Driver\\mlx4eth63", // Mellanox
L"\\Driver\\vmxnet3ndis6", // VMware (for dev/testing)
L"\\Driver\\netvsc", // Hyper-V
};
for (ULONG i = 0; i < ARRAYSIZE(drivers); i++)
ScanDriverNics(drivers[i]);
}
Usage Notes
- The CollectNicMacs function handles cleaning out virtual adapters, VPNs, and loopbacks to avoid breaking your local network stack.
- The spoofed MAC is generated as "locally administered" (0x02 bit set) which is standard for software-assigned addresses.
- Replacement is immediate — once the scan finishes, any OID query should return the new spoofed data.
This is a solid base for anyone building a permanent or temporary HWID spoofer. Just keep in mind that modifying memory inside ndis.sys might be flagged if the anti-cheat starts integrity checking these structures, though most currently only focus on code integrity (CR0/PatchGuard stuff).
Anyone tested this on the latest EAC build to see if they're checking miniport block integrity yet?