Android获取设备IP地址代码与分析,包含内外网筛选

简介

在Socket编制程序的时候,大家须求实时获取我们所须求的IP地址。举个例子在编排后门的时候,我们恐怕必要获得平价的外网IP或内网IP;一时候大家可能供给看清大家赢得的是还是不是是虚构机网卡,那时候就必要对每一张网卡上的特点进行辨认。以下我计算了一部分常用的拍卖方法供我们仿照效法。

参照他事他说加以考察资料:1.
领取网卡音讯情势
              2.
编造与物理网卡区分方法

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地址的目标,经过测量检验,结果也确实那样。

获取本机全体IP地址:

string name = Dns.GetHostName();
IPAddress[] ipadrlist = Dns.GetHostAddresses(name);

         这几个地点是带有全部网卡(虚构网卡)的ipv4和ipv6地址。

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("%ld\n", 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] = "\0";
    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;
}

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主页翻看和下载。

另,民用本事博客,同步更新,招待关切!转发请注解出处!文中若有啥错误希望大家钻探指正!

赢得本机全部IPV4地址:

string name = Dns.GetHostName();
IPAddress[] ipadrlist = Dns.GetHostAddresses(name);
foreach (IPAddress ipa in ipadrlist)
{
            if (ipa.AddressFamily == AddressFamily.InterNetwork)
            Console.Writeline(ipa.ToString());
}

        若要单单获取ipv4地址,能够用IPAdress.AddressFamily
属性判定:对于 IPv4,再次来到 InterNetwork;对于 IPv6,重回 InterNetworkV6。

       
然则尽管本机大概有多少个ipv4的地址,那什么样获得访谈暗许网关时使用的网卡IP呢。在CSDN论坛找到了大神的点子,用的是询问本机路由表。

获得本机正在利用的ipv4地址(访谈互连网的IP),可别小看,照旧有无尽急需思量的:
1.三个Computer有三个网卡,有线的、有线的、还应该有vmare设想的多少个网卡。
2.尽管唯有贰个网卡,然而该网卡配置了N个IP地址.个中还包括ipv6地址。

上边贴一个自个儿一直利用的方法,它通过查询本机路由表,获取访问暗中认可网关时使用的网卡IP。
用了2年了,屡试不爽。

      /// <summary>
        /// 获取当前使用的IP
        /// </summary>
        /// <returns></returns>
        public static string GetLocalIP()
        {
            string result = RunApp("route", "print",true);
            Match m = Regex.Match(result, @"0.0.0.0\s+0.0.0.0\s+(\d+.\d+.\d+.\d+)\s+(\d+.\d+.\d+.\d+)");
            if (m.Success)
            {
                return m.Groups[2].Value;
            }
            else
            {
                try
                {
                    System.Net.Sockets.TcpClient c = new System.Net.Sockets.TcpClient();
                    c.Connect("www.baidu.com", 80);
                    string ip = ((System.Net.IPEndPoint)c.Client.LocalEndPoint).Address.ToString();
                    c.Close();
                    return ip;
                }
                catch (Exception)
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// 获取本机主DNS
        /// </summary>
        /// <returns></returns>
        public static string GetPrimaryDNS()
        {
            string result = RunApp("nslookup", "",true);
            Match m = Regex.Match(result, @"\d+\.\d+\.\d+\.\d+");
            if (m.Success)
            {
                return m.Value;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 运行一个控制台程序并返回其输出参数。
        /// </summary>
        /// <param name="filename">程序名</param>
        /// <param name="arguments">输入参数</param>
        /// <returns></returns>
        public static string RunApp(string filename, string arguments,bool recordLog)
        {
            try
            {
                if (recordLog)
                {
                    Trace.WriteLine(filename + " " + arguments);
                }
                Process proc = new Process();
                proc.StartInfo.FileName = filename;
                proc.StartInfo.CreateNoWindow = true;
                proc.StartInfo.Arguments = arguments;
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.UseShellExecute = false;
                proc.Start();

                using (System.IO.StreamReader sr = new System.IO.StreamReader(proc.StandardOutput.BaseStream, Encoding.Default))
                {
                    //string txt = sr.ReadToEnd();
                    //sr.Close();
                    //if (recordLog)
                    //{
                    //    Trace.WriteLine(txt);
                    //}
                    //if (!proc.HasExited)
                    //{
                    //    proc.Kill();
                    //}
                    //上面标记的是原文,下面是我自己调试错误后自行修改的
                    Thread.Sleep(100);           //貌似调用系统的nslookup还未返回数据或者数据未编码完成,程序就已经跳过直接执行
                                                 //txt = sr.ReadToEnd()了,导致返回的数据为空,故睡眠令硬件反应
                    if (!proc.HasExited)         //在无参数调用nslookup后,可以继续输入命令继续操作,如果进程未停止就直接执行
                    {                            //txt = sr.ReadToEnd()程序就在等待输入,而且又无法输入,直接掐住无法继续运行
                        proc.Kill();
                    }
                    string txt = sr.ReadToEnd();
                    sr.Close();
                    if (recordLog)
                        Trace.WriteLine(txt);
                    return txt;
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
                return ex.Message;
            }
        }

大神代码源自帖子:

       

另有一种方法通过用ipconfig来收获:

private void GetIP()  
    {  
        Process cmd = new Process();  
        cmd.StartInfo.FileName = "ipconfig.exe";//设置程序名   
        cmd.StartInfo.Arguments = "/all";  //参数   
 //重定向标准输出   
        cmd.StartInfo.RedirectStandardOutput = true;  
        cmd.StartInfo.RedirectStandardInput = true;  
        cmd.StartInfo.UseShellExecute = false;  
        cmd.StartInfo.CreateNoWindow = true;//不显示窗口(控制台程序是黑屏)   
 //cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;//暂时不明白什么意思   
        /* 
 收集一下 有备无患 
        关于:ProcessWindowStyle.Hidden隐藏后如何再显示? 
        hwndWin32Host = Win32Native.FindWindow(null, win32Exinfo.windowsName); 
        Win32Native.ShowWindow(hwndWin32Host, 1);     //先FindWindow找到窗口后再ShowWindow 
        */  
        cmd.Start();  
        string info = cmd.StandardOutput.ReadToEnd();  
        cmd.WaitForExit();  
        cmd.Close();  
        textBox1.AppendText(info);  
    }

图片 1

此刻将在自身动手看怎么着截取了。可参照上边大神如何用正则表明式来同盟。