- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 617
- Реакции
- 16
Здарова, реверсеры. Пока большинство юзает паблик-спуферы, которые просто меняют одну ветку в реестре, античиты вроде EAC и BattlEye давно научились слать OID-запросы напрямую в минипорт. Обычный софт тут пасует, поэтому выкладываю базу для нормального спуфа на уровне ядра.
В чем суть метода
Мы не полагаемся на статические оффсеты (которые залетают в детект после каждого патча винды). Вместо этого драйвер сканирует
и расширения устройств (DeviceExtension) целевых сетевых драйверов на наличие байтов оригинального MAC-адреса. Как только паттерн найден — делаем замену прямо в памяти. Это убивает сразу двух зайцев:
и
начинают возвращать то, что нам нужно.
Логика работы:
Техническое ядро для реализации скана и подмены:
Нюансы и траблшутинг
Метод жесткий, но требует аккуратности. Если криво пропатчите память — словите BSOD с кодом
Перед использованием на основе убедитесь, что выключили Secure Boot и нормально скрыли сам драйвер-маппер, иначе прилетит по голове не за MAC, а за наличие неподписанного кода в системе. Сурс — отличная база для тех, кто пишет свой приватный спуфер и хочет закрыть вопрос с сетевыми картами раз и навсегда.
Кто будет допиливать под DMA — обратите внимание на типы памяти, иногда девайсы кэшируют мак в своих регистрах, там логика чуть сложнее.
В чем суть метода
Мы не полагаемся на статические оффсеты (которые залетают в детект после каждого патча винды). Вместо этого драйвер сканирует
Код:
ndis.sys
Код:
OID_802_3_PERMANENT_ADDRESS
Код:
OID_802_3_CURRENT_ADDRESS
Логика работы:
- Парсим реальные MAC-адреса из веток реестра сетевых классов.
- Генерируем рандомные локальные MAC-адреса (unicast/locally-administered).
- Проходимся по списку драйверов (Intel, Realtek, Broadcom и т.д.).
- Скан секций памяти на паттерны оригинального MAC.
- In-place замена байтов в структурах
и кастомных структурах вендоров.Код:
NDIS_MINIPORT_BLOCK
- ndis.sys / ndisuio.sys (Ядро)
- e1iexpress / e1dexpress / e1rexpress / igc (Intel)
- rt640x64 / rt68cx21 / rtwlane (Realtek)
- bnxt / b57nd60a (Broadcom)
- mlx4eth63 (Mellanox)
- vmxnet3ndis6 / netvsc (Виртуалки)
Техническое ядро для реализации скана и подмены:
Код:
// - 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]);
}
Нюансы и траблшутинг
Метод жесткий, но требует аккуратности. Если криво пропатчите память — словите BSOD с кодом
Код:
KMODE_EXCEPTION_NOT_HANDLED
Кто будет допиливать под DMA — обратите внимание на типы памяти, иногда девайсы кэшируют мак в своих регистрах, там логика чуть сложнее.