To build a better world !

          Android類動態(tài)加載技術

          轉(zhuǎn)載請注明出處:http://www.aygfsteel.com/zh-weir/archive/2011/10/29/362294.html 

          Android類動態(tài)加載技術

          Android應用開發(fā)在一般情況下,常規(guī)的開發(fā)方式和代碼架構就能滿足我們的普通需求。但是有些特殊問題,常常引發(fā)我們進一步的沉思。我們從沉思中產(chǎn)生頓悟,從而產(chǎn)生新的技術形式。

          如何開發(fā)一個可以自定義控件的Android應用?就像eclipse一樣,可以動態(tài)加載插件;如何讓Android應用執(zhí)行服務器上的不可預知的代碼?如何對Android應用加密,而只在執(zhí)行時自解密,從而防止被破解?……

          熟悉Java技術的朋友,可能意識到,我們需要使用類加載器靈活的加載執(zhí)行的類。這在Java里已經(jīng)算是一項比較成熟的技術了,但是在Android中,我們大多數(shù)人都還非常陌生。

          類加載機制

                 Dalvik虛擬機如同其他Java虛擬機一樣,在運行程序時首先需要將對應的類加載到內(nèi)存中。而在Java標準的虛擬機中,類加載可以從class文件中讀取,也可以是其他形式的二進制流。因此,我們常常利用這一點,在程序運行時手動加載Class,從而達到代碼動態(tài)加載執(zhí)行的目的。

                 然而Dalvik虛擬機畢竟不算是標準的Java虛擬機,因此在類加載機制上,它們有相同的地方,也有不同之處。我們必須區(qū)別對待。

                 例如,在使用標準Java虛擬機時,我們經(jīng)常自定義繼承自ClassLoader的類加載器。然后通過defineClass方法來從一個二進制流中加載Class。然而,這在Android里是行不通的,大家就沒必要走彎路了。參看源碼我們知道,AndroidClassLoaderdefineClass方法具體是調(diào)用VMClassLoaderdefineClass本地靜態(tài)方法。而這個本地方法除了拋出一個“UnsupportedOperationException”之外,什么都沒做,甚至連返回值都為空。

          static void Dalvik_java_lang_VMClassLoader_defineClass(const u4* args,

              JValue
          * pResult)

          {

              Object
          * loader = (Object*) args[0];

              StringObject
          * nameObj = (StringObject*) args[1];

              
          const u1* data = (const u1*) args[2];

              
          int offset = args[3];

              
          int len = args[4];

              Object
          * pd = (Object*) args[5];

              
          char* name = NULL;

           

              name 
          = dvmCreateCstrFromString(nameObj);

              LOGE(
          "ERROR: defineClass(%p, %s, %p, %d, %d, %p)\n",

                  loader, name, data, offset, len, pd);

              dvmThrowException(
          "Ljava/lang/UnsupportedOperationException;",

                  
          "can't load this type of class file");

           

              free(name);

              RETURN_VOID();

          }


          Dalvik
          虛擬機類加載機制

                 那如果在Dalvik虛擬機里,ClassLoader不好使,我們?nèi)绾螌崿F(xiàn)動態(tài)加載類呢?Android為我們從ClassLoader派生出了兩個類:DexClassLoaderPathClassLoader。其中需要特別說明的是PathClassLoader中一段被注釋掉的代碼:

          /* --this doesn't work in current version of Dalvik--

              if (data != null) {

                  System.out.println("--- Found class " + name

                      + " in zip[" + i + "] '" + mZips[i].getName() + "'");

                  int dotIndex = name.lastIndexOf('.');

                  if (dotIndex != -1) {

                      String packageName = name.substring(0, dotIndex);

                      synchronized (this) {

                          Package packageObj = getPackage(packageName);

                          if (packageObj == null) {

                              definePackage(packageName, null, null,

                                      null, null, null, null, null);

                          }

                      }

                  }

           

                  return defineClass(name, data, 0, data.length);

              }

          */



                 這從另一方面佐證了defineClass函數(shù)在Dalvik虛擬機里確實是被閹割了。而在這兩個繼承自ClassLoader的類加載器,本質(zhì)上是重載了ClassLoaderfindClass方法。在執(zhí)行loadClass時,我們可以參看ClassLoader部分源碼:

          protected Class<?> loadClass(String className, boolean resolve) 

          throws ClassNotFoundException {

          Class
          <?> clazz = findLoadedClass(className);

           

              
          if (clazz == null{

                  
          try {

                      clazz 
          = parent.loadClass(className, false);

                  }
           catch (ClassNotFoundException e) {

                      
          // Don't want to see this.

                  }


           

                  
          if (clazz == null{

                      clazz 
          = findClass(className);

                  }


              }


           

              
          return clazz;

          }


                 因此DexClassLoaderPathClassLoader都屬于符合雙親委派模型的類加載器(因為它們沒有重載loadClass方法)。也就是說,它們在加載一個類之前,回去檢查自己以及自己以上的類加載器是否已經(jīng)加載了這個類。如果已經(jīng)加載過了,就會直接將之返回,而不會重復加載。

                 DexClassLoaderPathClassLoader其實都是通過DexFile這個類來實現(xiàn)類加載的。這里需要順便提一下的是,Dalvik虛擬機識別的是dex文件,而不是class文件。因此,我們供類加載的文件也只能是dex文件,或者包含有dex文件的.apk.jar文件。

                  
          也許有人想到,既然DexFile可以直接加載類,那么我們?yōu)槭裁催€要使用ClassLoader的子類呢?DexFile在加載類時,具體是調(diào)用成員方法loadClass或者loadClassBinaryName。其中loadClassBinaryName需要將包含包名的類名中的”.”轉(zhuǎn)換為”/”。我們看一下loadClass代碼就清楚了:


          public Class loadClass(String name, ClassLoader loader) {

                  String slashName 
          = name.replace('.''/');

                  
          return loadClassBinaryName(slashName, loader);

          }

                 在這段代碼前有一段注釋,截取關鍵一部分就是說:If you are not calling this from a class loader, this is most likely not going to do what you want. Use {@link Class#forName(String)} instead. 這就是我們需要使用ClassLoader子類的原因。至于它是如何驗證是否是在ClassLoader中調(diào)用此方法的,我沒有研究,大家如果有興趣可以繼續(xù)深入下去。

                 有一個細節(jié),可能大家不容易注意到。PathClassLoader是通過構造函數(shù)new DexFile(path)來產(chǎn)生DexFile對象的;而DexClassLoader則是通過其靜態(tài)方法loadDexpath, outpath, 0)得到DexFile對象。這兩者的區(qū)別在于DexClassLoader需要提供一個可寫的outpath路徑,用來釋放.apk包或者.jar包中的dex文件。換個說法來說,就是PathClassLoader不能主動從zip包中釋放出dex,因此只支持直接操作dex格式文件,或者已經(jīng)安裝的apk(因為已經(jīng)安裝的apkcache中存在緩存的dex文件)。而DexClassLoader可以支持.apk.jar.dex文件,并且會在指定的outpath路徑釋放出dex文件。

                 另外,PathClassLoader在加載類時調(diào)用的是DexFileloadClassBinaryName,而DexClassLoader調(diào)用的是loadClass。因此,在使用PathClassLoader時類全名需要用”/”替換”.”。


          實際操作

                 這一部分比較簡單,因此我就不贅言了。只是簡單的說下。

                 可能使用到的工具都比較常規(guī):javacdx、eclipse等。其中dx工具最好是指明--no-strict,因為class文件的路徑可能不匹配。

                 加載好類后,通常我們可以通過Java反射機制來使用這個類。但是這樣效率相對不高,而且老用反射代碼也比較復雜凌亂。更好的做法是定義一個interface,并將這個interface寫進容器端。待加載的類,繼承自這個interface,并且有一個參數(shù)為空的構造函數(shù),以使我們能夠通過ClassnewInstance方法產(chǎn)生對象。然后將對象強制轉(zhuǎn)換為interface對象,于是就可以直接調(diào)用成員方法了。


          關于代碼加密的一些設想

                 最初設想將dex文件加密,然后通過JNI將解密代碼寫在Native層。解密之后直接傳上二進制流,再通過defineClass將類加載到內(nèi)存中。

                 現(xiàn)在也可以這樣做,但是由于不能直接使用defineClass,而必須傳文件路徑給dalvik虛擬機內(nèi)核,因此解密后的文件需要寫到磁盤上,增加了被破解的風險。

                 Dalvik虛擬機內(nèi)核僅支持從dex文件加載類的方式是不靈活的,由于沒有非常深入的研究內(nèi)核,我不能確定是Dalvik虛擬機本身不支持還是Android在移植時將其閹割了。不過相信Dalvik或者是Android開源項目都正在向能夠支持raw數(shù)據(jù)定義類方向努力。

                 我們可以在文檔中看到Google說:Jar or APK file with "classes.dex". (May expand this to include "raw DEX" in the future.);在AndroidDalvik源碼中我們也能看到RawDexFile的身影(不過沒有具體實現(xiàn))。

                 RawDexFile出來之前,我們都只能使用這種存在一定風險的加密方式。需要注意釋放的dex文件路徑及權限管理,另外,在加載完畢類之后,除非出于其他目的否則應該馬上刪除臨時的解密文件。


          轉(zhuǎn)載請注明出處:http://www.aygfsteel.com/zh-weir/archive/2011/10/29/362294.html 

          posted on 2011-10-29 21:51 zh.weir 閱讀(37958) 評論(25)  編輯  收藏 所屬分類: Android軟件安全

          評論

          # re: Android類動態(tài)加載技術 2011-11-30 13:25 android動態(tài)加載

          更好的做法是定義一個interface,并將這個interface寫進容器端
          >容器端做interface的強制類型轉(zhuǎn)換時出現(xiàn)異常,java.lang.ClassCastException:
          不知道您有沒有遇到?  回復  更多評論   

          # re: Android類動態(tài)加載技術 2011-11-30 20:52 zh-weir


          檢查一下,是否在Host端和Plugin端分別都定義了liangzijian這個接口。如果都定義了,且均被編譯進了字節(jié)碼文件中就會發(fā)生類似的問題。原因是 newInstance生成的對象實現(xiàn)的是Plugin端的liangzijian接口,而你想要轉(zhuǎn)化的是Host端的liangzijian接口。兩個接口雖然名字相同,甚至可能包名也一樣,但是它們是由不同的類加載器加載的,所以不同不能實現(xiàn)轉(zhuǎn)化。

          解決辦法是不要將liangzijian接口編譯進Plugin端的字節(jié)碼中。具體做法是導入liangzijian接口的jar庫時,不要使用添加外部jar庫,而應使用user library。后一種方式不會將庫中的類編譯進字節(jié)碼中,而是在運行時去動態(tài)鏈接。
            回復  更多評論   

          # re: Android類動態(tài)加載技術 2011-12-01 15:15 android動態(tài)加載

          @zh-weir
          問題解決了,需要注意兩點:
          1、Host端和Plugin端interface文件的包名需要一致
          2、jar文件打包時不要包含interface文件

          另外,DexClassLoader的最后一個參數(shù)使用getClassLoader()或者VMStack.getCallingClassLoader();  回復  更多評論   

          # re: Android類動態(tài)加載技術 2011-12-02 10:31 zephyranthes

          有點問題想請教樓主,之前使用dexclassloader想加載另一個apk的activity會出現(xiàn)activitynotfoundexception異常,似乎這種方式行不通.

          后來想到是不時只是寫一個jni的native activity,當apk運行時,會先運行這個jni so,然后該so生成解密的dex文件,再通過dexclassloader加載該dex中的activity?

          麻煩樓主解答,謝謝  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-02-17 23:59 passbye

          反復反復反復反復反復反復反復反復反復  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-02-18 00:01 pass

          請問對于(對Android應用加密,而只在執(zhí)行時自解密,從而防止被破解?)這句話該如何理解,是對本地的(已安裝好的)apk加密還是什么操作?請大家指教。謝謝,希望大家共同進步。  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-02-28 09:19 zh_weir

          @pass

          由于apk應用需要經(jīng)過Android系統(tǒng)的解析和安裝,因此是不能對它進行直接加密的哦。那樣會導致apk無法安裝或者無法運行。

          我的意思是將核心代碼抽取出來,作為二進制資源,由APK應用在運行時動態(tài)加載。  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-08 20:05 林風

          @android動態(tài)加載
          可否留個郵箱,交流一下,My Email --xk1411@139.com.  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-09 17:06 zh.weir

          @林風

          呵呵,其實右上方的公告里有寫。zh.weir@gmail.com 。

          歡迎交流!  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-26 10:04 ljj

          小弟現(xiàn)正開發(fā)一個這樣一個應用,這個應用做為主應用,只需要實現(xiàn)一些基本的功能就行,
          如果要擴展功能的話,就通過開發(fā)一些小插件來實現(xiàn)

          實現(xiàn)原理是這樣的:
          主應用(Host)定義一個接口:
          public abstract interface IExtension{
          void method1(WebView webview,....);
          void method2();


          public class AbstractExtension extends ContextWrapper implements IExtension{
          .....
          基本是對IExtension的空實現(xiàn)

          }


          這里應用反射來實例化一個插件:
          ClassLoader localExtensionClassLoader = context.createPackageContext(this.mPackageName,Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY).getClassLoader();
          ClassLoader localSystemClassLoader = OlivePackageManager.getSystemClassLoader();

          ClassLoaderWrapper localClassLoaderWrapper = new ClassLoaderWrapper(localExtensionClassLoader, localSystemClassLoader);

          String localClassName = String.valueOf(this.mPackageName) + ".Extension";
          Class<?> localClass = localClassLoaderWrapper.loadClass(localClassName);

          Constructor<?> localConstructor = localClass.getConstructor( new Class[]{Context.class} );
          localConstructor.setAccessible(true);

          Object instance = localConstructor.newInstance( new Object[]{context} );




          public class ClassLoaderWrapper extends ClassLoader{

          public ClassLoaderWrapper(ClassLoader extensionClassLoader, ClassLoader systemClassLoader) {
          super(systemClassLoader);
          this.mLoader = extensionClassLoader;
          }

          protected Class<?> findClass(String className) throws ClassNotFoundException {
          System.out.println("mLoader type === " + mLoader.toString());
          Class<?> localClass = null;

          if (localClass == null){
          //這個地方,就是這個地方,加載失敗
          localClass = this.mLoader.loadClass(className);
          }

          System.out.println("mLoader.loadClass============="+localClass);
          return localClass;
          }

          protected Class<?> loadClass(String className, boolean paramBoolean){
          Class<?> localClass = null;
          try {
          localClass = getParent().loadClass(className);
          } catch (ClassNotFoundException localClassNotFoundException) {
          localClass = findLoadedClass(className);

          if (localClass == null) {
          try {
          localClass = findClass(className);
          } catch (ClassNotFoundException e) {
          }
          }
          }

          return localClass;
          }




          在插件工程中:
          public class Extension extends AbstractExtension{
          ...實現(xiàn)IExtension接口


          這樣做的話,插件工程必須加入對主工程(Host)的引用



          思想是好的,但是問題出來了,我主應用的類加載器總是load不到插件工程的Extension類,
          但是如果我的插件工程里面不extends AbstractExtension 的話,是ok的,但是不繼承,就失去了意義。

          分析原因:
          public ClassLoaderWrapper(ClassLoader extensionClassLoader, ClassLoader systemClassLoader) {
          super(systemClassLoader);
          this.mLoader = extensionClassLoader;
          }
          這個構造函數(shù)里面,extensionClassLoader作為父類加載器,extensionClassLoader作為插件的類加載器,

          loadClass方法里面,先用systemClassLoader加載,應該是加載不到插件的Extension的,那么接下來就使用extensionClassLoader來加載,這個時候extensionClassLoader與插件的Extension是同一Context,理所當然能夠加載到,但是問題是Extension extends AbstractExtension,它肯定是加載不到AbstractExtension,導致插件的Extension也加載失敗

          這個問題困擾了我多日了,希望哪位給個意見,幫助分析一下,我應該怎么做?

            回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-26 15:09 zh-weir

          @ljj

          之前的評論有說:

          需要注意兩點:
          1、Host端和Plugin端interface文件的包名需要一致
          2、jar文件打包時不要包含interface文件

          另外,DexClassLoader的最后一個參數(shù)使用getClassLoader()或者VMStack.getCallingClassLoader();  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-26 15:39 ljj

          樓主,我用的是自定的classloader,沒用DexClassLoader,我現(xiàn)在就是要動態(tài)加載插件的
          類Class<?> localClass = localClassLoaderWrapper.loadClass(localClassName);但是在自定義的classloader中的@Override
          protected Class<?> findClass(String className)
          throws ClassNotFoundException {
          System.out.println("mLoader type === " + mLoader.toString());
          Class<?> localClass = null;
          if ((this.mLoader instanceof PathClassLoader)){
          //localClass = a(paramString);

          }
          if (localClass == null) {
          // 這個地方,就是這個地方,加載失敗
          localClass = this.mLoader.loadClass(className);
          }

          System.out.println("mLoader.loadClass=============" + localClass);
          return localClass;
          }

          我沒有用到jar包,謝謝  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-26 16:45 zh-weir

          @ljj

          沒有用到jar,那你是從哪個文件里load類?這點需要明確。個人認為,第一可能是沒有指明從哪個文件load類;第二是IExtension被不同的類加載器加載了兩次,這樣兩個IExtension雖然名字相同、包名相同,但是它們是不同的類。  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-26 17:25 ljj

          @zh-weir

          我是要加載插件中的類

          String localClassName = String.valueOf(packageName)+ ".Extension";


          其中ClassLoaderWrapper 是自定義的classloader
          IExtension只在主host定義的接口
          在主host基本是對IExtension的空實現(xiàn)

          在插件中具體實現(xiàn)

          ClassLoaderWrapper localClassLoaderWrapper = new ClassLoaderWrapper (localExtensionClassLoader , localSystemClassLoader );
          String localClassName = String.valueOf(this.c)+ ".Extension";
          Class<?> localClass = localClassLoaderWrapper.loadClass(localClassName);


            回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-26 17:37 zh.weir

          @ljj

          String localClassName = String.valueOf(this.c)+ ".Extension";
          Class<?> localClass = localClassLoaderWrapper.loadClass(localClassName);

          這是load插件中的類嗎?你的“插件”存在于哪?是安裝好的apk,還是未安裝的apk,還是jar或者dex文件?總需要一個路徑的。  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-27 08:17 ljj

          @zh-weir
          我的插件是已安裝好的apk,


          String localClassName = String.valueOf(this.c)+ ".Extension";
          Class<?> localClass = localClassLoaderWrapper.loadClass(localClassName);


          localClassName 是要去動態(tài)加載的插件中的類名,現(xiàn)在就是加載不上,
          自定義的classloader中

          @Override
          protected Class<?> findClass(String className)
          throws ClassNotFoundException {
          System.out.println("mLoader type === " + mLoader.toString());
          Class<?> localClass = null;
          if ((this.mLoader instanceof PathClassLoader)){
          //localClass = a(paramString);

          }
          if (localClass == null) {
          // 這個地方,就是這個地方,加載失敗
          localClass = this.mLoader.loadClass(className);
          }

          System.out.println("mLoader.loadClass=============" + localClass);
          return localClass;
          }  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-27 19:54 zh-weir

          又看了一下你的代碼。首先應該是AbstractExtension這個類沒有加載到。這個類,理論上應該是由Host端的類加載器加載好的。所以問題是,plugin端可能并沒有發(fā)現(xiàn)由host端加載好的AbstractExtension類。分析了可能的原因,覺得ClassLoader localSystemClassLoader = OlivePackageManager.getSystemClassLoader();這個Classloader可能并不是ClassLoader localExtensionClassLoader = context.createPackageContext(this.mPackageName,Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY).getClassLoader(); 這個ClassLoader的parent……  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-27 20:14 ljj

          我把插件工程 和主host 的代碼發(fā)到你郵箱了,幫看下吧,謝謝!  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-03-29 08:34 zh-weir

          不好意思,我可能周末才有時間看你的這份代碼哦……
          辦公室不讓收外部郵箱郵件,業(yè)余時間又少得可憐。  回復  更多評論   

          # re: Android類動態(tài)加載技術[未登錄] 2012-03-30 14:37 123

          @zh-weir
          按照這個方法我依然沒有實現(xiàn),我用的是PathClassLoader。  回復  更多評論   

          # re: Android類動態(tài)加載技術[未登錄] 2012-03-30 15:04 123

          @zh-weir
          你好,看了你的帖子的確讓我學到不少。

          但是我的實現(xiàn)依然存在問題。
          以下是我最近一個項目的需求:我要使我的App(容器端)中某個固定的View可以替換為其他開發(fā)者開發(fā)的任意自定義控件。目前利用PathClassLoader已經(jīng)實現(xiàn)了基本的替換功能。但是存在一個問題,跟上面幾位朋友討論的問題相似,在容器端我需要定義一些接口(目前是為了實現(xiàn)觀察者模式,當然以后可能提供更多的接口用以添加容器端和插件端的交互),在插件端的自定義控件可以實現(xiàn)這些接口,但是根據(jù)樓上提供的方法,依然存在ClassCastException的問題。

          以下是我實現(xiàn)的一些細節(jié),請LZ看看哪里有問題:
          1.我使用的是PathClassLoader,因為插件需要被安裝,所以不用DexClassLoader。
          2.PathClassLoader構造的時候使用的是getSystemClassLoader。
          3.利用Eclipse將接口導出到jar(我這里不太清楚是否需要用dx工具將jar里面的.class類型轉(zhuǎn)化,我目前的做法是沒有轉(zhuǎn)化),且不含源文件,插件端可以編譯通過。  回復  更多評論   

          # re: Android類動態(tài)加載技術 2012-05-15 19:45 briancol

          博主:請教個問題:
          Dalvik 在加載類時, 如果該類已被加載,就不會重新加載。有么有辦法讓dalvik強制重新加載?
          這個需求的原因是因為: android系統(tǒng)有些自帶類會有各種限制,所以我想通過寫一個和系統(tǒng)某個類同包名同類名的類,然后讓dalvik加載我的這個類,從而突破某些限制。
          博主有沒有什么思路?  回復  更多評論   

          # re: Android類動態(tài)加載技術 2013-01-23 14:45 菜鳥1234

          不是很明白,既然有最后解密的過程,自然就可以找到解密的算法和密鑰,再怎么動態(tài)加載類又有什么用呢?  回復  更多評論   

          # re: Android類動態(tài)加載技術 2013-05-08 11:28 chuan

          說理清晰,thx  回復  更多評論   

          # re: Android類動態(tài)加載技術 2013-07-05 14:36 卷起鋪蓋流浪

          @briancol
          我和你在做同樣的事情,如果我想動態(tài)替換報名類名都相同的一個類,那么這個類dex加載后會被apk中存在的那個類的實例覆蓋,沒有實現(xiàn)動態(tài)加載的意義,請問你找到解決方法了么?  回復  更多評論   

          公告

          大家好!歡迎光臨我的 Android 技術博客!



          本博客旨在交流與 Android 操作系統(tǒng)相關的各種技術及信息。

          博客內(nèi)的文章會盡量以開源的形式提供給大家,希望我們能相互交流,共同提高!

          有不足之處,請不吝賜教!

          我的郵箱:zh.weir@gmail.com
          我的新浪微博:@囧虎張建偉

           

          導航

          <2011年10月>
          2526272829301
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          統(tǒng)計

          留言簿(19)

          隨筆分類(24)

          隨筆檔案(18)

          文章檔案(1)

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 阿坝县| 竹溪县| 乾安县| 屯留县| 越西县| 荥经县| 休宁县| 玛曲县| 彝良县| 小金县| 镇雄县| 正宁县| 沈阳市| 昌邑市| 嘉祥县| 平度市| 丘北县| 木里| 临城县| 乐山市| 绩溪县| 栾城县| 靖西县| 额尔古纳市| 泸西县| 金溪县| 汽车| 武鸣县| 洛南县| 济源市| 皋兰县| 体育| 百色市| 托克托县| 永靖县| 安康市| 安国市| 达尔| 小金县| 崇州市| 纳雍县|