当前位置 博文首页 > 文章内容

    心影:获取客户端Mac地址

    作者:shunshunshun18 栏目:未分类 时间:2021-11-28 10:12:44

    本站于2023年9月4日。收到“大连君*****咨询有限公司”通知
    说我们IIS7站长博客,有一篇博文用了他们的图片。
    要求我们给他们一张图片6000元。要不然法院告我们

    为避免不必要的麻烦,IIS7站长博客,全站内容图片下架、并积极应诉
    博文内容全部不再显示,请需要相关资讯的站长朋友到必应搜索。谢谢!

    另祝:版权碰瓷诈骗团伙,早日弃暗投明。

    相关新闻:借版权之名、行诈骗之实,周某因犯诈骗罪被判处有期徒刑十一年六个月

    叹!百花齐放的时代,渐行渐远!



    近期有个需求,需要获取客户端Mac地址作为白名单验证的依据。使用.net,B/S架构。先百度找了一些获取mac地址的方法,

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.IO;
    using System.Net;
    using System.Configuration;
    using System.Web;
    using System.Security.Cryptography;
    using System.Data;
    using System.Web.UI.WebControls;
    using System.Management;
    using System.Diagnostics;
    using System.Net.NetworkInformation;
    using System.Runtime.InteropServices;
    
    namespace Qianxun.Common
    {
        public class MacAddress
        {
            #region 获取mac地址的一些方法
            #region 1 通过IPConfig命令读取MAC地址(选用)
            /// <summary>
            /// 根据截取ipconfig /all命令的输出流获取网卡Mac
            /// </summary>
            /// <returns></returns>
            public static List<string> GetMacByIPConfig()
            {
                List<string> macs = new List<string>();
                try
                {
                    ProcessStartInfo startInfo = new ProcessStartInfo("ipconfig", "/all");
                    startInfo.UseShellExecute = false;
                    startInfo.RedirectStandardInput = true;
                    startInfo.RedirectStandardOutput = true;
                    startInfo.RedirectStandardError = true;
                    startInfo.CreateNoWindow = true;
                    Process p = Process.Start(startInfo);
                    // 截取输出流
                    StreamReader reader = p.StandardOutput;
                    string line = reader.ReadLine();
    
                    while (!reader.EndOfStream)
                    {
                        if (!string.IsNullOrEmpty(line))
                        {
                            line = line.Trim();
    
                            if (line.StartsWith("Physical Address") || line.StartsWith("物理地址"))
                            {
                                macs.Add(line.Substring(line.IndexOf(":") + 1, line.Length - line.IndexOf(":") - 1));
                                break;
                            }
                        }
    
                        line = reader.ReadLine();
                    }
    
                    // 等待程序执行完退出进程
                    p.WaitForExit();
                    p.Close();
                    reader.Close();
                }
                catch (Exception ex) { 
                
                }
                return macs;
            }
            #endregion
    
            #region 2 通过WMI读取MAC地址
            /// <summary>
            /// 通过WMI读取系统信息里的网卡MAC
            /// 该方法依赖WMI的系统服务,该服务一般不会被关闭;但如果系统服务缺失或者出现问题,该方法无法取得MAC地址。
            /// </summary>
            /// <returns></returns>
            public static List<string> GetMacByWMI()
            {
                List<string> macs = new List<string>();
                try
                {
                    string mac = "";
                    ManagementClass mc = new ManagementClass(" Win32_NetworkAdapterConfiguration ");
                    ManagementObjectCollection moc = mc.GetInstances();
                    foreach (ManagementObject mo in moc)
                    {
                        if ((bool)mo[" IPEnabled "])
                        {
                            mac = mo[" MacAddress "].ToString();
                            macs.Add(mac);
                        }
                    }
                    moc = null;
                    mc = null;
                }
                catch(Exception ex)
                {
                }
    
                return macs;
            }
            #endregion
    
            #region 3 通过NetworkInterface读取MAC地址:1)如果当前的网卡是禁用状态(硬件处于硬关闭状态),取不到该网卡的MAC地址,(您可以通过禁用网卡进行试验)。2)如果当前启用了多个网卡,最先返回的地址是最近启用的网络连接的信息
            // 返回描述本地计算机上的网络接口的对象(网络接口也称为网络适配器)。
            public static NetworkInterface[] NetCardInfo()
            {
                return NetworkInterface.GetAllNetworkInterfaces();
            }
    
            /// <summary>
            /// 通过NetworkInterface读取网卡Mac
            /// </summary>
            /// <returns></returns>
            public static List<string> GetMacByNetworkInterface()
            {
                List<string> macs = new List<string>();
                try
                {
                    NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
                    foreach (NetworkInterface ni in interfaces)
                    {
                        macs.Add(ni.GetPhysicalAddress().ToString());
                    }
                }
                catch (Exception ex)
                { 
                
                }
                return macs;
            }
            #endregion
            #region 4 通过SendARP读取MAC地址
            /// <summary>
            /// 通过SendARP获取网卡Mac
            /// 网络被禁用或未接入网络(如没插网线)时此方法失灵
            /// </summary>
            /// <param name="remoteIP"></param>
            /// <returns></returns>
            public static string GetMacBySendARP(string remoteIP)
            {
                StringBuilder macAddress = new StringBuilder();
    
                try
                {
                    Int32 remote = inet_addr(remoteIP);
    
                    Int64 macInfo = new Int64();
                    Int32 length = 6;
                    SendARP(remote, 0, ref macInfo, ref length);
    
                    string temp = Convert.ToString(macInfo, 16).PadLeft(12, '0').ToUpper();
    
                    int x = 12;
                    for (int i = 0; i < 6; i++)
                    {
                        if (i == 5)
                        {
                            macAddress.Append(temp.Substring(x - 2, 2));
                        }
                        else
                        {
                            macAddress.Append(temp.Substring(x - 2, 2) + " - ");
                        }
                        x -= 2;
                    }
    
                    return macAddress.ToString();
                }
                catch
                {
                    return macAddress.ToString();
                }
            }
    
            [DllImport(" Iphlpapi.dll ")]
            private static extern int SendARP(Int32 dest, Int32 host, ref Int64 mac, ref Int32 length);
            [DllImport(" Ws2_32.dll ")]
            private static extern Int32 inet_addr(string ip);
            #endregion
    
            #region 5 从注册表读取MAC地址
            //常规用户可通过读取注册表项Windows Genuine Advantage获取到物理网卡地址。
            //1)如果注册表项被修改,则无法取得该MAC地址
            //HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Genuine Advantage
            #endregion
            #endregion
        }
    }
    View Code

    测试时猛然发现多个客户端获取到的mac地址都是一样的,惊觉获取的是服务端的Mac地址,服务端语言获取服务端Mac,那么我需要获取客户端Mac,应该使用客户端语言,最先想到的是js(因为有碰到过客户端ip和服务端ip的问题,这里倒也能想清楚)。百度了一圈,mac地址需要ActiveX脚本支持,

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>ie浏览器获取客户端Mac地址</title>
    </head>
    
    <body>
    
        <object classid="CLSID:76A64158-CB41-11D1-8B02-00600806D9B6" id="locator" style="display: none; visibility: hidden"></object>
        <object classid="CLSID:75718C9A-F029-11d1-A1AC-00C04FB6C223" id="foo" style="display: none; visibility: hidden"></object>
        <form name="myForm">
            <br />
            MAC地址:<input type="text" name="macAddress">
        </form>
    </body>
    </html>
    
    <script language="javascript">
        var sMacAddr = "";
        var sIPAddr = "";
        var sDNSName = "";
        var service = locator.ConnectServer();
        service.Security_.ImpersonationLevel = 3;
        service.InstancesOfAsync(foo, 'Win32_NetworkAdapterConfiguration');
    </script>
    <script for="foo" event="OnObjectReady(objObject,objAsyncContext)" language="JScript">
        if (objObject.IPEnabled != null && objObject.IPEnabled != "undefined" && objObject.IPEnabled == true) {
            if (objObject.IPEnabled && objObject.IPAddress(0) != null && objObject.IPAddress(0) != "undefined")
                sIPAddr = objObject.IPAddress(0);
            if (objObject.MACAddress != null && objObject.MACAddress != "undefined")
                sMacAddr = objObject.MACAddress;
            if (objObject.DNSHostName != null && objObject.DNSHostName != "undefined")
                sDNSName = objObject.DNSHostName;
        }
    </script>
    
    <script for="foo" event="OnCompleted(hResult,pErrorObject, pAsyncContext)" language="JScript">
        myForm.macAddress.value = sMacAddr;
    </script>
    
    <script>
        //判断浏览器是否支持ActiveX控件
        //if (window.ActiveXObject) {
        //支持-通过ActiveXObject的一个新实例来创建XMLHttpRequest对象
        var WshShell = new ActiveXObject("WScript.Shell");
        //}
        //else {
        //    $("#tips").html("请使用ie并将本系统域名加入受信任的站点,并在internet选项-受信任的站点中开启ActiveX插件和脚本的相关权限");
        //}
    </script>
    View Code

    而这个东西仅支持ie,但是客户系统对ie兼容性不好,如果要调整,工作量巨大,那么就只能让其他浏览器比如chorme去兼容ActiveX。

    1.首先找到chorme for ActiveX扩展插件,试了好几个版本,也依然无法获取到mac地址,不知道是我的使用方式有问题,还是这个方法已经失效。(chorme已经升级到当前最新版本 96.0.4664.45(正式版本) (64 位))。

    2.之后找到IE Tab扩展插件,但是好像仅仅是在chorme上嵌入了一个ie内核,这样系统同样是不兼容的。

    3.然后找到了油猴,想着写一个脚本获取mac地址存入c盘的一个txt文本,然后系统去读取这个txt文件即可。奈何油猴写了个alert(‘hello world‘)也没生效,不知道是使用方法有误,还是相关机制阻止了这种毫无意义的打扰。想到另一个问题,如果是后端去保存文件,其实是保存到服务器,而后端读取文件也需要客户端进行上传。而客户端脚本是没有权限去操作文件直接保存到txt(有点木马的特性,奈何没有涉及到这一块的技术)。所以该方案暂时搁置。

    4.又找到一个插件PluginOK,但是是付费插件,原理应该是js调用c++去获取计算机物理信息,然后通过双向通信集成到系统。感觉有些大材小用,也没有这么多预算,暂时搁置,也没实测。这一痛点,成就了一家企业。

    提取问题的根本矛盾点:mac地址仅支持ie获取,系统不兼容ie,那么就只能分成两步,1.ie获取mac 2.使用chorme登录系统操作,这1和2之间需要产生一个联系来把1获取的数据传递给2,想到上次微信扫码登录的逻辑,使用了预登陆机制,联想到暴雪的安全令机制,有了些许思路:

    1.login页面创建一条预登陆记录存入数据库,且获取到id,在页面上拼接成安全令登录url

    2.用户使用ie登录前一步拼接而成的url获取到mac地址且根据入参id存入到对应的数据库记录中(用户的ie需要先进行配置:1.ie浏览器internet选项-安全-受信任的站点将系统域名加入受信任的站点 2.还是刚才的地方 自定义级别 将ActiveX控件和插件相关权限打开

    3.返回原来的login页面输入账号密码即可登录,正常逻辑登录之后,配合预登陆id(可以从数据库获取mac地址)可以进行mac地址白名单的匹配

    曲线救国,安全令增加一些交互效果,各处增加操作详细说明,倒也算勉强完成了需求

    bkbky