You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows系统开启随机硬件地址后如何获取真实MAC地址

解决Windows 10/11开启随机MAC后获取真实硬件地址及客户端唯一识别问题

获取真实MAC地址的可行方法

通过WMI查询永久硬件地址

Windows的WMI服务中,Win32_NetworkAdapter类的PermanentAddress字段存储的是网卡出厂时的硬件MAC地址,不受随机MAC功能影响。需要过滤虚拟网卡(通过PhysicalAdapter属性判断是否为物理网卡),同时排除VPN、虚拟机等虚拟设备。

以下是C++实现示例:

#include <windows.h>
#include <wbemidl.h>
#include <iostream>
#pragma comment(lib, "wbemuuid.lib")

HRESULT GetRealMACAddresses() {
    HRESULT hres;
    IWbemLocator* pLoc = nullptr;
    IWbemServices* pSvc = nullptr;

    // 初始化COM环境
    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) return hres;

    hres = CoInitializeSecurity(
        nullptr, -1, nullptr, nullptr,
        RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE,
        nullptr, EOAC_NONE, nullptr
    );
    if (FAILED(hres)) { CoUninitialize(); return hres; }

    // 创建WMI定位器实例
    hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
    if (FAILED(hres)) { CoUninitialize(); return hres; }

    // 连接到ROOT\CIMV2命名空间
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"), nullptr, nullptr, 0, nullptr, 0, 0, &pSvc
    );
    if (FAILED(hres)) { pLoc->Release(); CoUninitialize(); return hres; }

    // 设置代理安全 blanket
    hres = CoSetProxyBlanket(
        pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
        nullptr, EOAC_NONE
    );
    if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return hres; }

    // 执行WQL查询,筛选有永久MAC的物理网卡
    IEnumWbemClassObject* pEnumerator = nullptr;
    hres = pSvc->ExecQuery(
        _bstr_t(L"WQL"),
        _bstr_t(L"SELECT PermanentAddress, PhysicalAdapter, Description FROM Win32_NetworkAdapter WHERE PermanentAddress IS NOT NULL"),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        nullptr, &pEnumerator
    );
    if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); return hres; }

    IWbemClassObject* pclsObj = nullptr;
    ULONG uReturn = 0;
    while (pEnumerator) {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
        if (0 == uReturn) break;

        // 获取永久MAC地址
        VARIANT vtMAC;
        VariantInit(&vtMAC);
        hr = pclsObj->Get(L"PermanentAddress", 0, &vtMAC, nullptr, nullptr);
        if (SUCCEEDED(hr) && vtMAC.vt == VT_BSTR && vtMAC.bstrVal != nullptr) {
            // 判断是否为物理网卡
            VARIANT vtPhysical;
            VariantInit(&vtPhysical);
            hr = pclsObj->Get(L"PhysicalAdapter", 0, &vtPhysical, nullptr, nullptr);
            if (SUCCEEDED(hr) && vtPhysical.boolVal == VARIANT_TRUE) {
                // 排除虚拟设备(如VPN、Hyper-V网卡)
                VARIANT vtDesc;
                VariantInit(&vtDesc);
                hr = pclsObj->Get(L"Description", 0, &vtDesc, nullptr, nullptr);
                if (SUCCEEDED(hr)) {
                    wchar_t* desc = vtDesc.bstrVal;
                    if (wcsstr(desc, L"VPN") == nullptr && wcsstr(desc, L"Hyper-V") == nullptr && wcsstr(desc, L"Virtual") == nullptr) {
                        wprintf(L"真实MAC地址: %s\n", vtMAC.bstrVal);
                    }
                    VariantClear(&vtDesc);
                }
            }
            VariantClear(&vtPhysical);
        }
        VariantClear(&vtMAC);
        pclsObj->Release();
    }

    // 清理资源
    pEnumerator->Release();
    pSvc->Release();
    pLoc->Release();
    CoUninitialize();
    return WBEM_S_NO_ERROR;
}

int main() {
    GetRealMACAddresses();
    return 0;
}

注意:该方法需要应用以管理员权限运行,否则可能无法访问部分WMI属性。

低成本客户端唯一识别替代方案

若无法稳定获取真实MAC(如部分设备权限限制),可结合多个稳定硬件标识生成兼容现有MAC格式的唯一ID,减少代码改动:

  1. 收集稳定硬件标识:主板序列号(Win32_BaseBoard.SerialNumber)、BIOS序列号(Win32_BIOS.SerialNumber)、CPU ID(Win32_Processor.ProcessorId)等;
  2. 将标识拼接后做哈希(如SHA-1),取前12字节格式化为XX-XX-XX-XX-XX-XX样式,完全兼容现有MAC使用逻辑;
  3. 将生成的ID存储在本地配置文件,首次生成后直接读取,避免重复计算。

示例实现思路:

#include <windows.h>
#include <string>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")

std::wstring GenerateCompatibleClientID() {
    // 1. 获取多个硬件标识(示例:主板+BIOS序列号)
    std::wstring hardwareInfo = GetMotherboardSerial() + L"-" + GetBIOSSerial();
    
    // 2. 计算SHA-1哈希
    BYTE hash[20];
    DWORD hashSize = sizeof(hash);
    HCRYPTPROV hProv;
    HCRYPTHASH hHash;
    if (CryptAcquireContext(&hProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) &&
        CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash) &&
        CryptHashData(hHash, (BYTE*)hardwareInfo.c_str(), hardwareInfo.size() * sizeof(wchar_t), 0) &&
        CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashSize, 0)) {
        // 3. 格式化为MAC样式
        std::wstring id;
        for (int i = 0; i < 12; i++) {
            wchar_t buf[3];
            swprintf_s(buf, L"%02X", hash[i]);
            id += buf;
            if (i != 11) id += L"-";
        }
        CryptDestroyHash(hHash);
        CryptReleaseContext(hProv, 0);
        return id;
    }
    // 哈希失败时的 fallback(临时方案)
    SYSTEMTIME st;
    GetSystemTime(&st);
    return std::to_wstring(st.wYear) + L"-" + std::to_wstring(st.wMonth) + L"-" + std::to_wstring(st.wDay) + L"-" + std::to_wstring(GetCurrentProcessId());
}

// 辅助函数:通过WMI获取主板序列号(实现参考前文MAC查询逻辑)
std::wstring GetMotherboardSerial() {
    return L"MB-SERIAL-12345";
}

// 辅助函数:通过WMI获取BIOS序列号
std::wstring GetBIOSSerial() {
    return L"BIOS-SERIAL-67890";
}

内容的提问来源于stack exchange,提问作者xiaoguo lu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 04:57:14