对物理网卡,Android获取设备IP地址代码与分析

日期:2019-09-15编辑作者:系统操作

简介

在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值。另外,假诺您是经过相比骇客的法子比如未有通过系统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网络分享的景况、开启互联网代理的情事、从前提到的哈克er花招同时展开蜂窝互连网和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主页查阅和下载。

另,民用本事博客,同步更新,款待关切!转载请注解出处!文中若有何错误希望大家研商指正!

本文由今晚最快开奖现场直播发布于系统操作,转载请注明出处:对物理网卡,Android获取设备IP地址代码与分析

关键词:

工作站上截图,第10中学截取截图的6种办法

在Windows 10中截取截图的6种方式 简介   截图对于不同的目的很重要。它可以用于捕获笔记本电脑上的任何内容的截图...

详细>>

何以设置esxi的网卡与网络,VMware虚构机系统无法

1、环境 很多敌人安装了vmwareesxi后,不亮堂服务器上的网卡该怎么样设置以及怎么样运用,大家在此地来介绍一下v...

详细>>

结束普通方法无法结束的进程,定时启动和关闭

 使用ntsd程序 (即使设置过VS,在VS的设置目录下,举个例子“C:Program Files(x86)Debugging Tools for Windows(x86)ntsd.exe”,也足...

详细>>

x86汇编实现德文打字演练软件,系统机能调用W

一、实验目的 目的: 编写英语打字演习软件,综合复习字符输入和出示,置光标、开窗口、颜色设置等荧屏功能;精...

详细>>