结论:
在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); } } } }