1. 程式人生 > >獲取DNS解析時間

獲取DNS解析時間

結論:

在6.0及一下的系統中,系統程式碼中沒有hook點,所有解析dns的方式都是通過呼叫靜態方法的方法完成的,所以6.0及以下系統是拿不到dns的解析時間的,但是在7.0及以上系統中 拿到了dns的解析時間

方法:

  • 在7.0手機嘗試 通過設定錯誤的host 找到系統解析dns的方法,如下圖

    發現系統進行dns解析的主要方式是利用InetAddress類中的方法getAllByName(String host)

    在這個方法中 我們驚奇的發現了一個變數impl,下面看看這個imp是個什麼

    太好了,居然是一個靜態物件,上面我們知道getAllByName方法內呼叫了imp的lookupAllHostAddr方法,下面這個lookupAllHostAddr方法

    繼續尋找,在lookupAllHostAddr方法中呼叫了lookupHostByName方法

    在lookupHostByName方法中 我們終於找到了系統解析dns的具體方式了,通過 InetAddress[] addresses = Libcore.os.android_getaddrinfo(host, hints, netId);這段程式碼拿到了InetAddress,這個就是解析dns的結果,為此我們只需要hook到imp這個靜態物件並動態代理到lookupAllHostAddr這個方法即可,具體程式碼如下:

    至此,在7.0及以上系統中 拿到了dns的解析時間
  • 在6.0及一下系統通過上面方式嘗試獲取dns報錯
    在6.0上一樣通過製造host異常,找到系統獲取dns的方法

    我們發現跟7.0的方法是一樣的getAllByName ,下面看看方法的具體實現

    居然是呼叫了靜態的方法getAllByNameImp,繼續看getAllByNameImp靜態方法怎麼寫的

    在getAllByNameImp方法中又是呼叫了靜態的方法lookupHostByName,繼續看

    完了 在6.0及一下的系統中,系統程式碼中沒有hook點,所有解析dns的方式都是通過呼叫靜態方法的方法完成的,所以6.0及以下系統是拿不到dns的解析時間的

hook部分程式碼:

package com.hello2mao.xlogging.internal.dns;

import android.util.Log;

import com.hello2mao.xlogging.internal.TransactionsCache;
import com.hello2mao.xlogging.internal.util.DnsData;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;

/**
 * Created by linyaokui on 18/9/12.
 */

public class DnsHook {
    private static final String TAG = "test_hook_dns";

    //呼叫此方法  hook到dns解析
    public static void hook() {
        try {
            //拿到InetAddress類
            Class<?> InetAddressClass = Class.forName("java.net.InetAddress");
            //拿到InetAddress的靜態屬性impl(InetAddressImpl型別)
            Field implField = InetAddressClass.getDeclaredField("impl");
            implField.setAccessible(true);
            //拿到InetAddressImpl物件
            Object implFieldValue = implField.get(null);
            //設定動態代理需要的InvocationHandler
            DnsInvocationHandler handler = new DnsInvocationHandler(implFieldValue);
            //拿到InetAddressImpl介面
            Class<?> InetAddressImplIntercept = Class.forName("java.net.InetAddressImpl");
            //設定impl的動態代理
            Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                    new Class<?>[]{InetAddressImplIntercept}, handler);
            //代理impl
            implField.set(null, proxy);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class DnsInvocationHandler implements InvocationHandler {

        private Object implValue;

        private DnsInvocationHandler(Object implValue) {
            this.implValue = implValue;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Log.v(TAG, "hook成功");
            Object result;
            try {
                if (method.getName().equals("lookupAllHostAddr")) {
                    //當拿到lookupAllHostAddr 方法時進行時間處理
                    //拿到nds解析的開始時間
                    long start = System.currentTimeMillis();
                    result = method.invoke(implValue, args);
                    //拿到dns解析的結束時間
                    long end = System.currentTimeMillis();
                    Log.v(TAG, (end - start) + "");
                    InetAddress[] address = (InetAddress[]) result;
                    //輸出dns解析的hostName和hostAddress
                    if (address != null && address.length > 0) {
                        for (int i = 0; i < address.length; i++) {
                            Log.v(TAG, i + "--" + address[i].getHostName());
                            Log.v(TAG, i + "--" + address[i].getHostAddress());
                        }
                    }
                    TransactionsCache.addDnsData(address[0].getHostName(),
                            new DnsData(start, end));
                    return result;
                } else {
                    return method.invoke(implValue, args);
                }
            } catch (Exception e) {
                return method.invoke(implValue, args);
            }
        }
    }
}