11

ネットワーク カードのデバイス インスタンス IDが与えられた場合、その MAC アドレスを知りたいです。内蔵 Intel ギガビット カードのシステムでのデバイス インスタンス ID の例:

PCI\VEN_8086&DEV_10CC&SUBSYS_00008086&REV_00\3&33FD14CA&0&C8

これまでのところ、私が使用したアルゴリズムは次のように機能します。

  1. SetupDiGetClassDevsで呼び出しDIGCF_DEVICEINTERFACEます。
  2. を呼び出しSetupDiEnumDeviceInfoて、返されたデバイスを で取得しSP_DEVINFO_DATAます。
  3. で呼び出しSetupDiEnumDeviceInterfacesGUID_NDIS_LAN_CLASS、デバイス インターフェイスを取得します。
  4. SetupDiGetDeviceInterfaceDetailこの返されたデバイス インターフェイスを呼び出します。これにより、デバイス パスが文字列として取得されます。 \\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}
  5. この時点で、ネットワーク カード ドライバーのインターフェイスへのアドレスを取得しています。CreateFile#4 の結果を使用して開きます。
  6. と OID を呼び出しDeviceIoControlて、MAC アドレスを取得します。IOCTL_NDIS_QUERY_GLOBAL_STATSOID_802_3_PERMANENT_ADDRESS

これは通常は機能し、非常に多くのマシンで正常に使用されています。DeviceIoControlただし、手順 6の要求に適切に応答しないネットワーク ドライバーを備えたごく少数のマシンが存在するようです。ネットワークカードドライバーを最新に更新した後でも問題は解決しません。これらは、新しい Windows 7 ベースのコンピューターです。具体的には、DeviceIoControl正常に完了しますが、MAC アドレスを含む予想される 6 バイトではなく、0 バイトを返します。

手がかりはMSDNページにあるようですIOCTL_NDIS_QUERY_GLOBAL_STATS

この IOCTL は、以降のオペレーティング システムのリリースでは非推奨になります。ミニポート ドライバー情報を照会するには、WMI インターフェイスを使用する必要があります。詳細については、「WMI の NDIS サポート」を参照してください。

-- おそらく、新しいネットワーク カード ドライバは、この IOCTL を実装していないのでしょうか?

では、どうすればこれを機能させることができますか?私のアプローチに見落としがあり、少し間違ったことをしている可能性はありますか? それとも、もっと別のアプローチを取る必要がありますか? いくつかの代替アプローチには、次のものが含まれているようです。

  • クエリWin32_NetworkAdapterWMI クラス: 必要な情報を提供しますが、パフォーマンスがひどいために拒否されました。ローカル コンピューターの MAC アドレスを取得するための Win32_NetworkAdapter WMI クラスの高速置換を参照してください。
  • Query MSNdis_EthernetPermanentAddressWMI class: WMI の代替と思われるものでIOCTL_NDIS_QUERY_GLOBAL_STATS、ドライバーから直接 OID を照会します。これは、厄介なネットワーク ドライバーで動作します。残念ながら、返されたクラス インスタンスは、MAC アドレスと、のInstanceNameようなローカライズされた文字列であるのみを提供しますIntel(R) 82567LM-2 Gigabit Network Connection。クエリを実行すると、をなどMSNdis_EnumerateAdapterに関連付けるリストが生成されます。からプラグアンドプレイ デバイス インスタンス ID ( )に移動する方法がわかりません。InstanceNameDeviceName\DEVICE\{28FD5409-15BD-4C06-B62F-004D3A06F852}DeviceNamePCI\VEN_8086......
  • GetAdaptersAddressesor GetAdaptersInfo(非推奨) を呼び出します。戻り値で見つけることができる唯一のローカライズされていない識別子はアダプター名です。これは、WMI NDIS クラスによって返されるもの{28FD5409-15BD-4C06-B62F-004D3A06F852}と同じような文字列です。DeviceName繰り返しますが、それをデバイス インスタンス ID に関連付ける方法がわかりません。TCP/IP プロトコルが構成されていないアダプターなど、100% の時間でも動作するかどうかはわかりません。
  • NetBIOS 方式: 特定のプロトコルをカードに設定する必要があるため、100% は機能しません。一般的にはハックっぽいようで、とにかく私が知っているデバイス インスタンス ID に関連付ける方法ではありません。私はこのアプローチを拒否します。
  • UUID 生成方法: ここでは詳しく説明しない理由により拒否されました。

デバイス インスタンス ID からカードの "GUID" を取得する方法を見つけることができれば、残りの 2 つの方法のいずれかでうまくいくようです。しかし、私はまだ方法を理解していません。それ以外の場合は、WMI NDIS アプローチが最も有望に思えます。

ネットワーク カードと MAC アドレスのリストを取得するのは簡単で、いくつかの方法があります。デバイス インスタンス ID に関連付けることができる高速な方法でそれを行うのは明らかに難しいです...

編集: 誰かに役立つ場合のIOCTL呼び出しのサンプルコード(リークされたhFileハンドルは無視してください):

HANDLE hFile = CreateFile(dosDevice.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
    DWORD err = GetLastError();
    wcout << "GetMACAddress: CreateFile on " << dosDevice << " failed." << endl;
    return MACAddress();
}
BYTE address[6];
DWORD oid = OID_802_3_PERMANENT_ADDRESS, returned = 0;
//this fails too: DWORD oid = OID_802_3_CURRENT_ADDRESS, returned = 0;
if (!DeviceIoControl(hFile, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof(oid), address, 6, &returned, NULL)) {
    DWORD err = GetLastError();
    wcout << "GetMACAddress: DeviceIoControl on " << dosDevice << " failed." << endl;
    return MACAddress();
}
if (returned != 6) {
    wcout << "GetMACAddress: invalid address length of " << returned << "." << endl;
    return MACAddress();
}

コードは失敗し、次のように出力されます。

GetMACAddress: invalid address length of 0.

そのため、DeviceIoControl は成功を示すゼロ以外を返しますが、その後はゼロ バイトを返します。

4

3 に答える 3

4

これを行う1つの方法は次のとおりです。

  1. 構造体GetAdaptersAddressesのリストを取得するために呼び出すIP_ADAPTER_ADDRESSES
  2. 各アダプターを繰り返し処理し、AdapterNameフィールドからGUIDを取得します(この動作が保証されているかどうかはわかりませんが、システム内のすべてのアダプターにはGUIDがあり、ドキュメントにAdapterNameは永続的であると記載されています)
  3. 各アダプターについて、(存在する場合)からレジストリキーを読み取ります(ここHKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\<the adapter GUID>\Connection\PnPInstanceIDからこのアイデアを入手しました。Googleでそのキーを検索すると、十分に文書化されているようであるため、変更される可能性はありません)
  4. このキーから、アダプタのデバイスIDを取得します(次のようなものPCI\VEN_14E4&DEV_16B1&SUBSYS_96B11849&REV_10\4&2B8260C3&0&00E4) 。
  5. 一致するものが見つかるまで、アダプターごとにこれを実行します。試合が終わったら、に戻ってフィールドIP_ADAPTER_ADDRESSESを見てくださいPhysicalAddress
  6. ビールを買う(オプション)

何かをする方法が百万もなければ、それはWindowsではないでしょう!

于 2012-09-27T17:26:36.463 に答える
2

私は を使っSetupDiGetDeviceRegistryPropertyて読んでしまいましたSPDRP_FRIENDLYNAME。それが見つからない場合は、SPDRP_DEVICEDESC代わりに読みます。最終的に、これは「VirtualBox Host-Only Ethernet Adapter #2」のような文字列を取得します。MSNdis_EthernetPermanentAddress次に、これを WMI NDIS クラス ( WMI クラス)の InstanceName プロパティと照合します。複数のアダプターが同じドライバーを共有している場合 (つまり、「#2」、「#3」など)、両方のプロパティを読み取る必要があります。アダプターが 1 つしかSPDRP_FRIENDLYNAMEない場合は使用できませんが、複数ある場合は使用できません。SPDRP_FRIENDLYNAMEそれらを区別するために必要です。

ローカライズされた文字列のように見えるものを比較しているため、この方法は少し緊張します。また、私が行っていることが常に機能することを保証するドキュメントが見つかりませんでした。残念ながら、動作することが文書化されているより良い方法も見つかりませんでした。

他のいくつかの代替方法には、文書化されていないレジストリの場所に足を踏み入れることが含まれます。1 つは spencercw の方法で、もう 1 つは、 のSPDRP_DRIVER下にあるサブキーの名前であるを読み取ることHKLM\SYSTEM\CurrentControlSet\Control\Classです。ドライバー キーの下で、クラスのプロパティにLinkage\Export一致する可能性があると思われる値を探します。しかし、これらの値が合法的に一致する可能性があると述べているドキュメントは見つかりませんでした。さらに、私が見つけた唯一のドキュメントは Win2000 レジストリ リファレンスからのもので、アプリケーションはそれに依存すべきではないと明示的に述べていました。DeviceNameMSNdis_EnumerateAdapterLinkage\Export

別の方法は、最初の質問であるステップ 4:「SetupDiGetDeviceInterfaceDetailこの返されたデバイス インターフェイスについて」を確認することです。デバイス インターフェイス パスは、実際にはデバイス パスを再構築するために使用できます。デバイス インターフェイス パスから開始します: \\?\pci#ven_8086&dev_10cc&subsys_00008086&rev_00#3&33fd14ca&0&c8#{ad498944-762f-11d0-8dcb-00c04fc3358c}\{28fd5409-15bd-4c06-b62f-004d3a06f852}. 次に、最後のスラッシュの前にあるものをすべて削除して、: を残します{28fd5409-15bd-4c06-b62f-004d3a06f852}。最後に、\Device\この文字列の先頭に追加し、WMI NDIS クラスと照合します。ただし、これも文書化されておらず、デバイス インターフェイス パスの実装の詳細に依存しているようです。

結局、私が調査した他の方法には、文書化されていない独自の複雑さがあり、少なくともSPDRP_FRIENDLYNAME/SPDRP_DEVICEDESC文字列の一致と同じくらい深刻に聞こえました。そこで、私はより単純な方法を選択しました。それは、これらの文字列を WMI NDIS クラスと照合するだけでした。

于 2012-10-01T15:37:17.410 に答える
2

現在のMACアドレスではなく永続的なMACアドレスを取得しようとしたため、何らかのDRM、インベントリ、または分類システムを実装するためにMACアドレスを取得したいと思います。

管理上重畳された MAC アドレス(つまり、「強制された」MAC アドレス)さえあることを忘れているようです。
一部のドライバーでは、[詳細設定] タブの下の [デバイス プロパティ] ページからこれを行うことができます (例: 私の Marvell ネットワーク アダプターでは、これを行うことができます)。 )。

ただし、すべてレジストリ値:HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddressで終わりREG_SZます。ここでは、「01020304abcd」の形式で、元の MAC アドレスとは異なる MAC アドレスを設定できます (6 バイト、:区切り記号や0xプレフィックスなしのプレーンな 16 進数)。設定後、マシンを再起動すると、電源投入時に新しい MAC アドレスが有効になります。

私はたまたま 2 つの Marvell 統合 NIC と NETGEAR USB WiFi NIC を搭載したマザーボードを持っています。Marvell は MAC アドレスの変更をサポートしています。NetworkAddressレジストリに値を設定すると、ドライバのプロパティ ページにも新しい値が表示され、再起動しなくてもすぐに有効になります (デバイス プロパティから変更した場合)。ページ)。以下は、さまざまな方法で MAC アドレスを読み取った結果です。

  • GetAdaptersInfo: 新しい MAC アドレス
  • IOCTL_NDIS_QUERY_GLOBAL_STATS: 元の MAC アドレス
  • MSNdis_EthernetPermanentAddress: 元の MAC アドレス

NetworkAddressNETGEAR USB WiFi NIC のレジストリに値を追加してみました。結果は次のとおりです。

  • GetAdaptersInfo: 新しい MAC アドレス
  • IOCTL_NDIS_QUERY_GLOBAL_STATS: 新しい MAC アドレス
  • MSNdis_EthernetPermanentAddress: 新しい MAC アドレス

元の MAC アドレスはなくなりました。

したがって、「悪意のある」ユーザーにだまされないようにするために、常にHKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}\xxxx\NetworkAddressレジストリ値を確認する必要があります。それが設定されている場合、さまざまな方法を使用して何を提示するかを決定するのはドライバーの実装次第であるため、そのネットワークアダプターをまったく信頼しない方がよいと思います。

そのレジストリ キーに到達するための背景:

HKLM\SYSTEM\CurrentControlSet\Class キー
に関する Microsoft ドキュメント そのページの Microsoft ドキュメントによると、

セットアップ クラスの GUID を使用して名前が付けられた各クラスのサブキーがあります。

したがって、{4D36E972-E325-11CE-BFC1-08002BE10318}サブキーを選択します (別名GUID_DEVCLASS_NET、 で定義され<devguid.h>、さらにここに文書化されています)

繰り返しますが、Microsoft のドキュメントによると、

各クラス サブキーには、システムにインストールされているそのクラスの各デバイス インスタンスのソフトウェア キー (またはドライバー キー) と呼ばれる他のサブキーが含まれています。これらのソフトウェア キーはそれぞれ、10 進数の 4 桁の序数値であるデバイス インスタンス ID を使用して名前が付けられますxxxxの部分は、0 から始まる正の整数の 4 文字のテキスト表現です

したがって、サブキーを 0000、0001、0002 から、システム内のネットワーク アダプターの数までトラバースできます。
ドキュメントはここで終わりです。さまざまなレジストリ値などに関する他のドキュメントは見つかりませんでした。

GetAdaptersInfo()ただし、これらの各サブキーには、 、MSNdis_EthernetPermanentAddress、 、およびデバイス インスタンス ID ワールドをリンクするのに役立つ REG_SZ 値がありますWin32_NetworkAdapter(これが質問の答えです)。

レジストリ値は次のとおりです。

  • DeviceInstanceID: その値は当然のことながら、デバイス インスタンス ID です。
  • NetCfgInstanceId: その値は、によって返される構造体のAdapterNameメンバーです。また、 WMI クラスのメンバーでもあります。IP_ADAPTER_INFOGetAdaptersInfo()GUIDWin32_NetworkAdapter
  • 1 つを忘れないでくださいNetworkAddress: 有効な MAC アドレスがここに存在する場合、ドライバーGetAdaptersInfo()それを、MSNdis_EthernetPermanentAddress、およびIOCTL_NDIS_QUERY_GLOBAL_STATS!で使用中の MAC アドレスとして報告することがあります。

次に、既に述べたように、MSNdis_EthernetPermanentAddressWMI クラスと「世界」の残りの部分との間の唯一の接続は、そのInstanceNameメンバーによるものです。によって返される構造体のDescriptionメンバーに関連付けることができます。ローカライズされた名前かもしれませんが、システムに固有のようです (私の 2 つの統合された Marvell NIC の場合、2 番目の NIC には名前に「#2」が追加されています)。IP_ADAPTER_INFOGetAdaptersInfo()

最後の注意:

前述のとおり、ユーザーは WMI を無効にすることを選択できます...

于 2019-07-11T12:20:31.723 に答える