您的位置:澳门402永利com > 操作系统 > 带有内外网筛选,Android获取道具IP地址代码与剖

带有内外网筛选,Android获取道具IP地址代码与剖

发布时间:2019-09-23 20:45编辑:操作系统浏览(162)

    简介

    在Socket编程的时候,我们需要实时获取我们所需要的IP地址。例如在编写后门的时候,我们可能需要获得有效的外网IP或内网IP;有时候我们可能需要判断我们获取的是否是虚拟机网卡,这时候就需要对每一张网卡上的特征进行识别。以下笔者总结了一些常用的处理方法供大家参考。


    参考资料:1. 提取网卡信息方法
                  2. 虚拟与物理网卡区分方法

    一直以来,好像没有一段标准的代码能提供Android设备此刻的IP地址,究其原因,Android设备的网卡可能不只一个,如蜂窝网卡、WiFi网卡,而且同一个网卡也可能拥有不止一个IP地址。基于此,一个Android终端很有可能同时拥有多个IP地址(不只是同时拥有IPv4和IPv6地址),比如开启热点共享蜂窝网络的时候,蜂窝网卡拥有一个IPv4地址来访问外网,WiFi网卡拥有一个IPv4地址来作为内网的网关。

    C++代码样例

    网上比较流行的获取Android设备IP地址的代码有以下几种,下面我们来一一分析一下。

    1. 头文件(包含特征处理函数)

    /////////////////////////////////////////
    //
    // FileName : NetInfoProc.h
    // Creator : PeterZ
    // Date : 2018-6-21 23:50
    // Comment : 网卡信息筛选
    // Editor : Visual Studio 2017
    //
    /////////////////////////////////////////
    
    #pragma once
    
    #include <cstdio>
    #include <cstdlib>
    #include <iostream>
    #include <strsafe.h>
    #include <WinSock2.h>
    #include <Iphlpapi.h>
    #include <cstring>
    
    #pragma comment(lib,"Iphlpapi.lib")
    
    using namespace std;
    
    #define REG_ERROR -2
    #define NO_PCI -1
    #define IS_PCI 0
    
    
    /**
     * @brief 查看字符串中是否有指定特征串
     * @param source 指向源字符串的指针
     * @param target 指向目标字符串的指针
     */
    BOOL IsInString(LPCSTR source, LPCSTR target)
    {
        if (source == NULL && target == NULL)
        {
            return false;
        }
        const size_t targetLength = strlen(target);
        const size_t sourceLength = strlen(source);
    
        if (sourceLength >= targetLength)
        {
            for (int i = 0; i < strlen(source); i++)
            {
                if (i + targetLength > sourceLength)
                {
                    return false;
                }
                for (int j = 0; j < targetLength; j++)
                {
                    if (*(source + i + j) != *(target + j))
                    {
                        break;
                    }
                    if (j == targetLength - 1)
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
    
    /**
     * @brief 获取注册表数据
     * @param hRoot 根键
     * @param szSubKey 子键
     * @param szValueName 数据项名
     * @param szRegInfo 数据
     */
    BOOL GetRegInfo(HKEY hRoot, LPCTSTR szSubKey, LPCTSTR szValueName, LPSTR szRegInfo)
    {
        HKEY hKey;
        DWORD dwType = REG_SZ;
        DWORD dwLenData = strlen(szRegInfo);
        LONG lRes = RegCreateKeyEx(hRoot, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL);
        if (lRes != ERROR_SUCCESS)
        {
            if (lRes == 5)
            {
                printf("Please use Administrator Privilege !n");
            }
            else
            {
                printf("Get Register Info Error! Error Code is ");
                printf("%ldn", lRes);
            }
            RegCloseKey(hKey);
            RegCloseKey(hRoot);
            return false;
        }
        RegQueryValueEx(hKey, szValueName, 0, &dwType, NULL, &dwLenData);
        lRes = RegQueryValueEx(hKey, szValueName, 0, &dwType, (LPBYTE)szRegInfo, &dwLenData);
        if (lRes != ERROR_SUCCESS)
        {
            RegCloseKey(hKey);
            RegCloseKey(hRoot);
            return false;
        }
        RegCloseKey(hKey);
        RegCloseKey(hRoot);
        return true;
    }
    
    /**
     * @brief 验证注册信息是否是PCI物理网卡(需要以管理员权限运行程序)
     * @param pIpAdapterInfo 指向网卡数据的指针
     */
    int IsPCINetCard(const PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        //通过注册表特征去除非物理网卡
        CHAR szRegSubKey[255] = "SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\";
        CHAR szNetCardRegInfo[255] = "";
        StringCchCat(szRegSubKey, sizeof(szRegSubKey), pIpAdapterInfo->AdapterName);
        StringCchCat(szRegSubKey, sizeof(szRegSubKey), "\Connection");
        if (!GetRegInfo(HKEY_LOCAL_MACHINE, szRegSubKey, "PnPInstanceId", szNetCardRegInfo))
        {
            return REG_ERROR;
        }
        if (strncmp(szNetCardRegInfo, "PCI", 3) == 0) return IS_PCI;
        else return NO_PCI;
    
    }
    
    
    /**
     * @brief 验证是否是虚拟网卡
     * @param pIpAdapterInfo 指向网卡数据的指针
     */
    BOOL IsVirtualNetCard(const PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        //去除有特征名的虚拟网卡
        if (IsInString(strlwr(pIpAdapterInfo->Description), "virtual")) return true;
        //去除有MAC的虚拟网卡 vmware
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x05 && pIpAdapterInfo->Address[2] == 0x69) return true;
        //去除有MAC的虚拟网卡 vmware
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x0C && pIpAdapterInfo->Address[2] == 0x29) return true;
        //去除有MAC的虚拟网卡 vmware
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x50 && pIpAdapterInfo->Address[2] == 0x56) return true;
        //去除有MAC的虚拟网卡 vmware
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x1C && pIpAdapterInfo->Address[2] == 0x14) return true;
        //去除有MAC的虚拟网卡 parallels
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x1C && pIpAdapterInfo->Address[2] == 0x42) return true;
        //去除有MAC的虚拟网卡 microsoft virtual pc
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x03 && pIpAdapterInfo->Address[2] == 0xFF) return true;
        //去除有MAC的虚拟网卡 virtual iron
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x0F && pIpAdapterInfo->Address[2] == 0x4B) return true;
        //去除有MAC的虚拟网卡 red hat xen , oracle vm , xen source, novell xen
        if (pIpAdapterInfo->Address[0] == 0x00 && pIpAdapterInfo->Address[1] == 0x16 && pIpAdapterInfo->Address[2] == 0x3E) return true;
        //去除有MAC的虚拟网卡 virtualbox
        if (pIpAdapterInfo->Address[0] == 0x08 && pIpAdapterInfo->Address[1] == 0x00 && pIpAdapterInfo->Address[2] == 0x27) return true;
        return false;
    }
    
    
    /**
     * @brief 验证是否是0.0.0.0不可用IP
     * @param pIpAdapterInfo 指向网卡数据的指针
     */
    BOOL IsInvalidIp(const PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
        do
        {
            if (!strcmp(pIpAddrString->IpAddress.String, "0.0.0.0"))
            {
                return false;
            }
            if ((pIpAddrString = pIpAddrString->Next) == NULL)
            {
                return true;
            }
        } while (pIpAddrString);
        return true;
    }
    
    /**
    * @brief 验证是否是内网IP
    * @param pIpAdapterInfo 指向网卡数据的指针
    */
    BOOL IsIntranetIP(const PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
        do
        {
            if (strncmp(pIpAddrString->IpAddress.String, "10", 2) == 0 || (strncmp(pIpAddrString->IpAddress.String, "172.16", 6) > 0 && strncmp(pIpAddrString->IpAddress.String, "172.31", 6) < 0) || strncmp(pIpAddrString->IpAddress.String, "192.168", 7) == 0)
            {
                return true;
            }
            if ((pIpAddrString = pIpAddrString->Next) == NULL)
            {
                return false;
            }
        } while (pIpAddrString);
        return true;
    }
    

    1. 不可行的方法

    String ipAddress = Inet4Address.getLocalHost().getHostAddress()
    

    这个是Java提供的API,在Android上执行需要以下权限(经测试Android版本6.0.1的一部机器不需要该权限,比较纳闷,求解答)

    <uses-permission android:name="android.permission.INTERNET"/>
    

    此外,由于该方法使用了网络通信,因此不能在UI线程执行。

    该方法顾名思义是获取本地主机的IP地址,在某些Java平台上可以得到想要的结果,但是我截取了Android官方给出的关于该方法的部分说明如下:

    Returns an InetAddress for the local host if possible, or the loopback address otherwise. This method works by getting the hostname, performing a DNS lookup, and then taking the first returned address.
    Note that if the host doesn't have a hostname set – as Android devices typically don't – this method will effectively return the loopback address, albeit by getting the name localhost and then doing a lookup to translate that to 127.0.0.1.

    可以看出,一般在Android平台上,由于网络通信设备没有设置hostname,因此无法进行DNS检索得到其相应的IP地址,因此该方法会返回本地回环地址,即127.0.0.1,也就是说这个方法在Android平台上无法达到我们一般的获取本机IP地址的目的,经过测试,结果也确实如此。

    2. CPP文件(代码应用演示)

    /////////////////////////////////////////
    //
    // FileName : NetCardVer.cpp
    // Creator : PeterZ
    // Date : 2018-6-21 23:50
    // Comment : 网卡信息筛选
    // Editor : Visual Studio 2017
    //
    /////////////////////////////////////////
    
    #include "NetInfoProc.h"
    
    void Output1(PIP_ADAPTER_INFO pIpAdapterInfo); //结果输出1(正常结果)
    void Output2(PIP_ADAPTER_INFO pIpAdapterInfo); //结果输出2(删除虚拟网卡的结果)
    void Output3(PIP_ADAPTER_INFO pIpAdapterInfo); //结果输出3(去除非PCI物理网卡) >>需要以管理员权限运行程序<<
    void Output4(PIP_ADAPTER_INFO pIpAdapterInfo); //结果输出4(筛选内网网卡)
    
    //主函数
    int main(void)
    {
        PIP_ADAPTER_INFO pIpAdapterInfo = (PIP_ADAPTER_INFO)malloc(sizeof(IP_ADAPTER_INFO));
        unsigned long stSize = sizeof(IP_ADAPTER_INFO);
        int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
        if (ERROR_BUFFER_OVERFLOW == nRel/*GetAdaptersInfo参数传递的内存空间不足*/)
        {
            //free(pIpAdapterInfo);
            pIpAdapterInfo = (PIP_ADAPTER_INFO)realloc(pIpAdapterInfo, stSize);
            nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
        }
        if (ERROR_SUCCESS == nRel)
        {
            printf(">>>>>>>>> 正常结果 <<<<<<<<<<<nn");
            Output1(pIpAdapterInfo);
            printf("nn>>>>>>>>> 删除虚拟网卡的结果 <<<<<<<<<nn");
            Output2(pIpAdapterInfo);
            printf("nn>>>>>>>>> 去除非PCI物理网卡的结果 <<<<<<<<<nn");
            Output3(pIpAdapterInfo);
            printf("nn>>>>>>>>> 筛选内网网卡的结果 <<<<<<<<<nn");
            Output4(pIpAdapterInfo);
        }
        if (pIpAdapterInfo)
        {
            free(pIpAdapterInfo);
        }
        system("pause");
        return 0;
    }
    
    //结果输出1(正常结果)
    void Output1(PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        //可能有多网卡,因此通过循环去判断
        while (pIpAdapterInfo)
        {
            //输出信息
            cout << "网卡名称:" << pIpAdapterInfo->AdapterName << endl;
            cout << "网卡描述:" << pIpAdapterInfo->Description << endl;
            cout << "网卡MAC地址:" << pIpAdapterInfo->Address;
            for (UINT i = 0; i < pIpAdapterInfo->AddressLength; i++)
            {
                if (i == pIpAdapterInfo->AddressLength - 1)
                {
                    printf("%02xn", pIpAdapterInfo->Address[i]);
                }
                else
                {
                    printf("%02x-", pIpAdapterInfo->Address[i]);
                }
            }
            cout << "网卡IP地址如下:" << endl;
            IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
            //可能网卡有多IP,因此通过循环去判断
            do
            {
                cout << pIpAddrString->IpAddress.String << endl;
                pIpAddrString = pIpAddrString->Next;
            } while (pIpAddrString);
            pIpAdapterInfo = pIpAdapterInfo->Next;
            cout << "*****************************************************" << endl;
        }
        return;
    }
    
    //结果输出2(删除虚拟网卡的结果)
    void Output2(PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        //可能有多网卡,因此通过循环去判断
        while (pIpAdapterInfo)
        {
            //去除虚拟网卡IP
            if (IsVirtualNetCard(pIpAdapterInfo))
            {
                pIpAdapterInfo = pIpAdapterInfo->Next;
                continue;
            }
            //输出信息
            cout << "网卡名称:" << pIpAdapterInfo->AdapterName << endl;
            cout << "网卡描述:" << pIpAdapterInfo->Description << endl;
            cout << "网卡MAC地址:" << pIpAdapterInfo->Address;
            for (UINT i = 0; i < pIpAdapterInfo->AddressLength; i++)
            {
                if (i == pIpAdapterInfo->AddressLength - 1)
                {
                    printf("%02xn", pIpAdapterInfo->Address[i]);
                }
                else
                {
                    printf("%02x-", pIpAdapterInfo->Address[i]);
                }
            }
            cout << "网卡IP地址如下:" << endl;
            IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
            //可能网卡有多IP,因此通过循环去判断
            do
            {
                cout << pIpAddrString->IpAddress.String << endl;
                pIpAddrString = pIpAddrString->Next;
            } while (pIpAddrString);
            pIpAdapterInfo = pIpAdapterInfo->Next;
            cout << "*****************************************************" << endl;
        }
        return;
    }
    
    //结果输出3(去除非PCI物理网卡)
    void Output3(PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        //可能有多网卡,因此通过循环去判断
        while (pIpAdapterInfo)
        {
            //去除非PCI物理网卡
            if (IsPCINetCard(pIpAdapterInfo) != IS_PCI)
            {
                if (IsPCINetCard(pIpAdapterInfo) == REG_ERROR)
                {
                    printf("1n");
                    return;
                }
                pIpAdapterInfo = pIpAdapterInfo->Next;
                continue;
            }
            //输出信息
            cout << "网卡名称:" << pIpAdapterInfo->AdapterName << endl;
            cout << "网卡描述:" << pIpAdapterInfo->Description << endl;
            cout << "网卡MAC地址:" << pIpAdapterInfo->Address;
            for (UINT i = 0; i < pIpAdapterInfo->AddressLength; i++)
            {
                if (i == pIpAdapterInfo->AddressLength - 1)
                {
                    printf("%02xn", pIpAdapterInfo->Address[i]);
                }
                else
                {
                    printf("%02x-", pIpAdapterInfo->Address[i]);
                }
            }
            cout << "网卡IP地址如下:" << endl;
            IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
            //可能网卡有多IP,因此通过循环去判断
            do
            {
                cout << pIpAddrString->IpAddress.String << endl;
                pIpAddrString = pIpAddrString->Next;
            } while (pIpAddrString);
            pIpAdapterInfo = pIpAdapterInfo->Next;
            cout << "*****************************************************" << endl;
        }
        return;
    }
    
    //结果输出4(筛选内网网卡)
    void Output4(PIP_ADAPTER_INFO pIpAdapterInfo)
    {
        //可能有多网卡,因此通过循环去判断
        while (pIpAdapterInfo)
        {
            //筛选内网网卡
            if (!IsIntranetIP(pIpAdapterInfo))
            {
                pIpAdapterInfo = pIpAdapterInfo->Next;
                continue;
            }
            //输出信息
            cout << "网卡名称:" << pIpAdapterInfo->AdapterName << endl;
            cout << "网卡描述:" << pIpAdapterInfo->Description << endl;
            cout << "网卡MAC地址:" << pIpAdapterInfo->Address;
            for (UINT i = 0; i < pIpAdapterInfo->AddressLength; i++)
            {
                if (i == pIpAdapterInfo->AddressLength - 1)
                {
                    printf("%02xn", pIpAdapterInfo->Address[i]);
                }
                else
                {
                    printf("%02x-", pIpAdapterInfo->Address[i]);
                }
            }
            cout << "网卡IP地址如下:" << endl;
            IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
            //可能网卡有多IP,因此通过循环去判断
            do
            {
                cout << pIpAddrString->IpAddress.String << endl;
                pIpAddrString = pIpAddrString->Next;
            } while (pIpAddrString);
            pIpAdapterInfo = pIpAdapterInfo->Next;
            cout << "*****************************************************" << endl;
        }
        return;
    }
    

    2. 部分可行的方法

    WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
    int ipAddressInt = wm.getConnectionInfo().getIpAddress();
    String ipAddress = String.format(Locale.getDefault(), "%d.%d.%d.%d", (ipAddressInt & 0xff), (ipAddressInt >> 8 & 0xff), (ipAddressInt >> 16 & 0xff), (ipAddressInt >> 24 & 0xff));
    

    方法执行所需权限为:

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    

    需要说明的是,上述代码第二行返回的是一个int类型的值,如1795336384,它对应的十六进制值6b02a8c0每两位便对应IPv4地址的每一项(逆序,如c0转化为十进制为192)。

    经测试,通过该方法可以获得当前WiFi网络中Android设备的IPv4地址,但是显然,该方法是通过WifiManager获取当前网络连接下的IP地址的,因此它只局限于使用WiFi网络的情况,当使用蜂窝等其他网络设备时,该方法无效,会返回0值。另外,如果你是通过比较hacker的方式比如没有通过系统Framework层打开WiFi,而是自己通过Linux命令创建的WiFi网络,那么像这种Framework层提供的API也是不起作用的。

    3. 基本可行的方法

        public static String getIpAddressString() {
            try {
                for (Enumeration<NetworkInterface> enNetI = NetworkInterface
                        .getNetworkInterfaces(); enNetI.hasMoreElements(); ) {
                    NetworkInterface netI = enNetI.nextElement();
                    for (Enumeration<InetAddress> enumIpAddr = netI
                            .getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                        InetAddress inetAddress = enumIpAddr.nextElement();
                        if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
                            return inetAddress.getHostAddress();
                        }
                    }
                }
            } catch (SocketException e) {
                e.printStackTrace();
            }
            return "";
        }
    

    方法执行所需权限为:

    <uses-permission android:name="android.permission.INTERNET"/>
    

    这段代码不难理解,其实就是双重循环获取终端中所有网络接口的所有IP地址,然后返回第一个遇到的非本地回环的IPv4地址。这种方式可以很好的覆盖我们一般的需求。根据Android系统的运行机制,当WiFi网络开启时蜂窝网络会自动关闭,因此遍历到的第一个地址是WiFi网卡的IP地址;同样,当关闭WiFi网络,打开蜂窝网络时,遍历到的第一个地址是蜂窝网卡的IP地址。

    那么,为什么我叫这种方式为基本可行的方法呢,因为它返回的结果并不是百分百“正确”的,确切地说并不一定是开发人员想要的结果。比如当Android手机开启热点的时候,实际上是通过WiFi网卡共享其蜂窝网络,因此此时,WiFi网卡和蜂窝网卡分配了不同的IP地址,但由于蜂窝网卡对应的NetworkInterface对象出现的位置要先于WiFi网卡,因此该方法返回的实际上是蜂窝网卡的IP地址。如果想要始终获取WiFi网卡的IP地址可以在上述的两个循环间添加如下筛选代码:

    if (netI.getDisplayName().equals("wlan0") || netI.getDisplayName().equals("eth0"))
    

    其中"wlan0"和"eth0"为常见的WLAN网卡的DisplayName名称,绝大部分为"wlan0",比较老的机型可能会是"eth0"或其他。

    这里只是举了一个简单的例子,其实还有很多特殊的情况,比如开启USB网络共享的情况、开启网络代理的情况、之前提到的Hacker手段同时打开蜂窝网络和WiFi网络(非WiFi热点)的情况等等,这些网络环境下都会存在多IP的情况,因此该方法不一定完全适用了。

    正如文章开头所说,由于一个Android设备同一时刻可能不只有一个IP地址,因此可以说没有任何一段通用的代码能获取每个人心中想要获取的IP地址,重要的还是根据自己具体的需求来进行相应的代码修改,通过对获取的IP地址列表进行筛选来得到想要的结果。

    本文的讨论是围绕IPv4地址的,如果想要获取IPv6地址,Android API也提供了相应的类或方法,只需要在上述代码的基础上作出微小修改即可。

    最后附上在StackOverFlow上看到的关于IP地址筛选的总结,供大家参考。

    • Any address in the range 127.xxx.xxx.xxx is a "loopback" address. It is only visible to "this" host.
    • Any address in the range 192.168.xxx.xxx is a private (aka site local) IP address. These are reserved for use within an organization. The same applies to 10.xxx.xxx.xxx addresses, and 172.16.xxx.xxx through 172.31.xxx.xxx.
    • Addresses in the range 169.254.xxx.xxx are link local IP addresses. These are reserved for use on a single network segment.
    • Addresses in the range 224.xxx.xxx.xxx through 239.xxx.xxx.xxx are multicast addresses.
    • The address 255.255.255.255 is the broadcast address.
    • Anything else should be a valid public point-to-point IPv4 address.

    文中所有代码可以在个人github主页查看和下载。

    另,个人技术博客,同步更新,欢迎关注!转载请注明出处!文中若有什么错误希望大家探讨指正!

    本文由澳门402永利com发布于操作系统,转载请注明出处:带有内外网筛选,Android获取道具IP地址代码与剖

    关键词: