zoukankan      html  css  js  c++  java
  • ProGuard之——代码混淆

    ProGuard之——代码混淆

    2015年11月03日 21:39:40 阅读数:2756 标签: JavaProGuard 更多
    个人分类: JAVA
    所属专栏: Java
    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l1028386804/article/details/49622317

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/49622317

    1. 混淆器的选择

    ProGuard


    下载地址:http://proguard.sourceforge.net/index.html#downloads.html

    2. 优化策略

    因为公司项目都是 SSH 或者 SSJ 框架,涉及到非常多的配置文件,所以

    a.  必须保留实现Action类中的公有的,友好的,私有的属性和公有的方法

    b.  因为配置文件中的类名是一个完整的类名,如果经过处理后就有可能找不到这个类。

    c.  类中的属性是jsp页面所需要的,如果经过处理jsp页面就无法得到action中的数据。

    所以混淆器的配置需要根据 工程使用的技术、标准的编码规范以及pro配置编写人员对工程的深入理解,否则极易导致工程无法正常运行。

    ProGuard混淆时运行proguard.jar,并读取配置文件 ***.pro

    ProGuardUI 图形化界面不能完全满足需要,需要单独配置 pro文件,但通过图形界面配置可以很方便的查看配置内容。如图:


    3. 命令

    java -jar ProGuard4.8libproguard.jar @pathTrueCMS.pro

    4. 为混淆器增加内存

    使用 proguard进行混淆时,若项目比较大,经常会报 OutOfMemory错误,则需要增加混淆器的内存

    java -Xms256m -Xmx512m -jar proguardgui.jarconfiguration.pro
    注意:要在ProGuard目录的bin下运行这些命令

    5.参数简介

    参数

    1. -include {filename} 从给定的文件中读取配置参数
    2. -basedirectory {directoryname} 指定基础目录为以后相对的档案名称
    3. -injars {class_path} 指定要处理的应用程序jar,war,ear和目录
    4. -outjars {class_path} 指定处理完后要输出的jar,war,ear和目录的名称
    5. -libraryjars {classpath} 指定要处理的应用程序jar,war,ear和目录所需要的程序库文件
    6. -dontskipnonpubliclibraryclasses 指定不去忽略非公共的库类。
    7. -dontskipnonpubliclibraryclassmembers 指定不去忽略包可见的库类的成员。
    保留选项
    1. -keep {Modifier} {class_specification} 保护指定的类文件和类的成员
    2. -keepclassmembers {modifier} {class_specification} 保护指定类的成员,如果此类受到保护他们会保护的更好
    3. -keepclasseswithmembers {class_specification} 保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。
    4. -keepnames {class_specification} 保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除)
    5. -keepclassmembernames {class_specification} 保护指定的类的成员的名称(如果他们不会压缩步骤中删除)
    6. -keepclasseswithmembernames {class_specification} 保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)
    7. -printseeds {filename} 列出类和类的成员-keep选项的清单,标准输出到给定的文件
    压缩
    1. -dontshrink 不压缩输入的类文件
    2. -printusage {filename}
    3. -whyareyoukeeping {class_specification}
    优化
    1. -dontoptimize 不优化输入的类文件
    2. -assumenosideeffects {class_specification} 优化时假设指定的方法,没有任何副作用
    3. -allowaccessmodification 优化时允许访问并修改有修饰符的类和类的成员
    混淆
    1. -dontobfuscate 不混淆输入的类文件
    2. -printmapping {filename}
    3. -applymapping {filename} 重用映射增加混淆
    4. -obfuscationdictionary {filename} 使用给定文件中的关键字作为要混淆方法的名称
    5. -overloadaggressively 混淆时应用侵入式重载
    6. -useuniqueclassmembernames 确定统一的混淆类的成员名称来增加混淆
    7. -flattenpackagehierarchy {package_name} 重新包装所有重命名的包并放在给定的单一包中
    8. -repackageclass {package_name} 重新包装所有重命名的类文件中放在给定的单一包中
    9. -dontusemixedcaseclassnames 混淆时不会产生形形色色的类名
    10. -keepattributes {attribute_name,...} 保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses
    11. -renamesourcefileattribute {string} 设置源文件中给定的字符串常量

    6.详细说明

    A.     综述

    这是一个不应该在开源社区出现的东西,但它的的确确是一个开源的项目,正像它的名字一样,Proguard,即Program Guard(程序卫士),它代表了开源的相对面--代码保护。

    经过几天的摸索,终于对Proguard应用于实际项目有一些不成熟的见解。

    上面提过,对于Web工程,公司都是用了SSH/SSJ框架,这类框架中所有action/service/dao/domain以及包结构都固定写死在对应的配置文件中,这使得Proguard发挥的空间仅限于 service/dao内的方法、各部分的通用工具方法及util工具类,Proguard灵活配置的优势便显现出来了。

    B.    使用说明
    1.在Eclipse中选中需要混淆的工程,右键Exprot->选择jar文件导出备用
    2. 打开 proguard4.8lib 目录下的proguardgui.jar,在input/output 选项中增加输入文件,找到刚才导出的jar文件并确定,然后增加输出文件。

    3.在  中增加工程lib文件夹下所有的jar包,以及java jre


    4.Shrinking(压缩)中不设置任何配置 如图


      
    5.Obfuscation如下图配置


    其中 jss.txt 是我们提供给proguard 进行混淆的字符串,若为空,ProGuard会按小写英文字母a,b,c,d….顺序混淆。

    6.Optimization如图配置


    7.Information 如图配置


    Target中选择当前jdk版本

    8.在Process选项中选择, 将配置保存成 **.pro文件

    9.打开pro文件


    在配置后加入

    1. -keep class WebRoot.WEB-INFO.lib.* ---不混淆lib文件夹中所有jar
    2. -keep class * extends cn.com.trueway.platform.** {
    3. public <methods>;
    4. } #不混淆继承于platform的所有类
    其他配置请根据实际情况进行添加
    10.配置完成后在ProGuradUI中——>——>加载配置文件,——>

    即开始混淆,混淆完的文件会根据配置文件输出到指定位置。
    也可以使用命令行完成上述动作,且执行效率会有质的飞跃。
    命令为: 

    (java -Xms256m -Xmx512m –jar D:proguard4.8libproguard.jar@D: rm.pro)

    7. 注意要点

    A.在产品的开发过程中,应养成良好的代码习惯,将可重复利用的代码都封装成私有方法或工具类,便于代码维护及代码混淆的效果。
    B.    工程比较大时,请使用命令行方式混淆,效率会提高10倍以上。
    C.通配符:' ?“为任意单个字符,‘*’的任意数量的字符(不包扩分隔符),“**”为任意数量的字符,‘%’对于任何原始类型,“***”为任何类型,和“…“为任意数量的参数。
    D.    虽然ProGuard声称支持war包,但遗憾的是混淆完的war包会丢失所有class,所以一定需要war包的情况下需要先对jar包混淆,再将jar包内的class替换到war包内,可正常运行,未出现异常。
    E.配置文件中如下配置一定不能颠倒顺序,否则会无法生成outjar。
    -injars 'D:akpgin m.jar'
    -outjars 'D:akpgin mout.jar'
    F.需要注意web.xml中配置的filter, listener等需要被skip掉。
    G.ProGuardUI 可以看做是一个配置文件生成器,而不适合做混淆动作。
    H.一个完整的配置例子:

    *************************************************************************************************************************************************************************************

    1. -injars 'D:akpgin m.jar'
    2. -outjars 'D:akpgin mout.jar'
    3. -libraryjars 'D:eclipse_v34jdkjdk1.5.0_11jrelib t.jar'
    4. -libraryjars 'D:workspace mWebRootWEB-INFlibFastInfoset-1.2.7.jar'
    5. -libraryjars 'D:workspace mWebRootWEB-INFlibXmlSchema-1.4.5.jar'
    6. -libraryjars 'D:workspace mWebRootWEB-INFlibabdera-core-0.4.0-incubating.jar'
    7. -libraryjars 'D:workspace mWebRootWEB-INFlibabdera-extensions-json-0.4.0-incubating.jar'
    8. -libraryjars 'D:workspace mWebRootWEB-INFlibabdera-extensions-main-0.4.0-incubating.jar'
    9. -libraryjars 'D:workspace mWebRootWEB-INFlibabdera-i18n-0.4.0-incubating.jar'
    10. -libraryjars 'D:workspace mWebRootWEB-INFlibabdera-parser-0.4.0-incubating.jar'
    11. -libraryjars 'D:workspace mWebRootWEB-INFlibactivation.jar'
    12. -libraryjars 'D:workspace mWebRootWEB-INFlibant-1.6.5.jar'
    13. -libraryjars 'D:workspace mWebRootWEB-INFlibantlr-2.7.7.jar'
    14. -libraryjars 'D:workspace mWebRootWEB-INFlibaopalliance-1.0.jar'
    15. -libraryjars 'D:workspace mWebRootWEB-INFlibasm-2.2.3.jar'
    16. -libraryjars 'D:workspace mWebRootWEB-INFlibaspectjrt.jar'
    17. -libraryjars 'D:workspace mWebRootWEB-INFlibaspectjweaver.jar'
    18. -libraryjars 'D:workspace mWebRootWEB-INFlibaspriseTIFF.jar'
    19. -libraryjars 'D:workspace mWebRootWEB-INFlibaxiom-api-1.2.7.jar'
    20. -libraryjars 'D:workspace mWebRootWEB-INFlibaxiom-impl-1.2.7.jar'
    21. -libraryjars 'D:workspace mWebRootWEB-INFlibaseframework.jar'
    22. -libraryjars 'D:workspace mWebRootWEB-INFlibcprov-jdk15-1.43.jar'
    23. .
    24. .
    25. .
    26. -dontskipnonpubliclibraryclassmembers
    27. -target 1.5
    28. -dontshrink
    29. -dontoptimize
    30. -useuniqueclassmembernames
    31. -keeppackagenames
    32. -keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
    33. -keepparameternames
    34. -dontpreverify
    35. -dontwarn
    36. -ignorewarnings
    37. -keep class WebRoot.WEB-INFO.lib.*
    38. -keep class * extends cn.com.trueway.platform.** {
    39. public <methods>;
    40. }
    41. -keep class * extends org.springframework.orm.hibernate3.support.HibernateDaoSupport {
    42. public <methods>;
    43. }
    44. -keep class **.model.** {
    45. public private protected <fields>;
    46. public <methods>;
    47. }
    48. -keep class cn.com.trueway.resourceAllocation.application.domain.* {
    49. public private protected <fields>;
    50. public <methods>;
    51. }
    52. -keep class **.service.impl.* {
    53. public <methods>;
    54. }
    55. -keep class cn.com.trueway.platform.** {
    56. public private protected <fields>;
    57. public <methods>;
    58. }
    59. -keep class cn.com.trueway.resourceInterface.service.** {
    60. public private protected <fields>;
    61. public <methods>;
    62. }
    63. -keep class **.po.* {
    64. public private protected <fields>;
    65. public <methods>;
    66. }
    67. -keep class cn.com.trueway.sso.* {
    68. public private protected <fields>;
    69. public <methods>;
    70. }
    71. -keep class cn.com.trueway.startup.AdminFilter {
    72. public private protected <fields>;
    73. public <methods>;
    74. }
    75. # Keep names - Native method names. Keep all native class/method names.
    76. -keepclasseswithmembers,allowshrinking class * {
    77. native <methods>;
    78. }
    79. # Keep names - _class method names. Keep all .class method names. This may be
    80. # useful for libraries that will be obfuscated again with different obfuscators.
    81. -keepclassmembers,allowshrinking class * {
    82. java.lang.Class class$(java.lang.String);
    83. java.lang.Class class$(java.lang.String,boolean);
    84. }
    85. # Remove - System method calls. Remove all invocations of System
    86. # methods without side effects whose return values are not used.
    87. -assumenosideeffects public class java.lang.System {
    88. public static long currentTimeMillis();
    89. static java.lang.Class getCallerClass();
    90. public static int identityHashCode(java.lang.Object);
    91. public static java.lang.SecurityManager getSecurityManager();
    92. public static java.util.Properties getProperties();
    93. public static java.lang.String getProperty(java.lang.String);
    94. public static java.lang.String getenv(java.lang.String);
    95. public static java.lang.String mapLibraryName(java.lang.String);
    96. public static java.lang.String getProperty(java.lang.String,java.lang.String);
    97. }
    98. # Remove - Math method calls. Remove all invocations of Math
    99. # methods without side effects whose return values are not used.
    100. -assumenosideeffects public class java.lang.Math {
    101. public static double sin(double);
    102. public static double cos(double);
    103. public static double tan(double);
    104. public static double asin(double);
    105. public static double acos(double);
    106. public static double atan(double);
    107. public static double toRadians(double);
    108. public static double toDegrees(double);
    109. public static double exp(double);
    110. public static double log(double);
    111. public static double log10(double);
    112. public static double sqrt(double);
    113. public static double cbrt(double);
    114. public static double IEEEremainder(double,double);
    115. public static double ceil(double);
    116. public static double floor(double);
    117. public static double rint(double);
    118. public static double atan2(double,double);
    119. public static double pow(double,double);
    120. public static int round(float);
    121. public static long round(double);
    122. public static double random();
    123. public static int abs(int);
    124. public static long abs(long);
    125. public static float abs(float);
    126. public static double abs(double);
    127. public static int max(int,int);
    128. public static long max(long,long);
    129. public static float max(float,float);
    130. public static double max(double,double);
    131. public static int min(int,int);
    132. public static long min(long,long);
    133. public static float min(float,float);
    134. public static double min(double,double);
    135. public static double ulp(double);
    136. public static float ulp(float);
    137. public static double signum(double);
    138. public static float signum(float);
    139. public static double sinh(double);
    140. public static double cosh(double);
    141. public static double tanh(double);
    142. public static double hypot(double,double);
    143. public static double expm1(double);
    144. public static double log1p(double);
    145. }
    146. # Remove - Number method calls. Remove all invocations of Number
    147. # methods without side effects whose return values are not used.
    148. -assumenosideeffects public class java.lang.* extends java.lang.Number {
    149. public static java.lang.String toString(byte);
    150. public static java.lang.Byte valueOf(byte);
    151. public static byte parseByte(java.lang.String);
    152. public static byte parseByte(java.lang.String,int);
    153. public static java.lang.Byte valueOf(java.lang.String,int);
    154. public static java.lang.Byte valueOf(java.lang.String);
    155. public static java.lang.Byte decode(java.lang.String);
    156. public int compareTo(java.lang.Byte);
    157. public static java.lang.String toString(short);
    158. public static short parseShort(java.lang.String);
    159. public static short parseShort(java.lang.String,int);
    160. public static java.lang.Short valueOf(java.lang.String,int);
    161. public static java.lang.Short valueOf(java.lang.String);
    162. public static java.lang.Short valueOf(short);
    163. public static java.lang.Short decode(java.lang.String);
    164. public static short reverseBytes(short);
    165. public int compareTo(java.lang.Short);
    166. public static java.lang.String toString(int,int);
    167. public static java.lang.String toHexString(int);
    168. public static java.lang.String toOctalString(int);
    169. public static java.lang.String toBinaryString(int);
    170. public static java.lang.String toString(int);
    171. public static int parseInt(java.lang.String,int);
    172. public static int parseInt(java.lang.String);
    173. public static java.lang.Integer valueOf(java.lang.String,int);
    174. public static java.lang.Integer valueOf(java.lang.String);
    175. public static java.lang.Integer valueOf(int);
    176. public static java.lang.Integer getInteger(java.lang.String);
    177. public static java.lang.Integer getInteger(java.lang.String,int);
    178. public static java.lang.Integer getInteger(java.lang.String,java.lang.Integer);
    179. public static java.lang.Integer decode(java.lang.String);
    180. public static int highestOneBit(int);
    181. public static int lowestOneBit(int);
    182. public static int numberOfLeadingZeros(int);
    183. public static int numberOfTrailingZeros(int);
    184. public static int bitCount(int);
    185. public static int rotateLeft(int,int);
    186. public static int rotateRight(int,int);
    187. public static int reverse(int);
    188. public static int signum(int);
    189. public static int reverseBytes(int);
    190. public int compareTo(java.lang.Integer);
    191. public static java.lang.String toString(long,int);
    192. public static java.lang.String toHexString(long);
    193. public static java.lang.String toOctalString(long);
    194. public static java.lang.String toBinaryString(long);
    195. public static java.lang.String toString(long);
    196. public static long parseLong(java.lang.String,int);
    197. public static long parseLong(java.lang.String);
    198. public static java.lang.Long valueOf(java.lang.String,int);
    199. public static java.lang.Long valueOf(java.lang.String);
    200. public static java.lang.Long valueOf(long);
    201. public static java.lang.Long decode(java.lang.String);
    202. public static java.lang.Long getLong(java.lang.String);
    203. public static java.lang.Long getLong(java.lang.String,long);
    204. public static java.lang.Long getLong(java.lang.String,java.lang.Long);
    205. public static long highestOneBit(long);
    206. public static long lowestOneBit(long);
    207. public static int numberOfLeadingZeros(long);
    208. public static int numberOfTrailingZeros(long);
    209. public static int bitCount(long);
    210. public static long rotateLeft(long,int);
    211. public static long rotateRight(long,int);
    212. public static long reverse(long);
    213. public static int signum(long);
    214. public static long reverseBytes(long);
    215. public int compareTo(java.lang.Long);
    216. public static java.lang.String toString(float);
    217. public static java.lang.String toHexString(float);
    218. public static java.lang.Float valueOf(java.lang.String);
    219. public static java.lang.Float valueOf(float);
    220. public static float parseFloat(java.lang.String);
    221. public static boolean isNaN(float);
    222. public static boolean isInfinite(float);
    223. public static int floatToIntBits(float);
    224. public static int floatToRawIntBits(float);
    225. public static float intBitsToFloat(int);
    226. public static int compare(float,float);
    227. public boolean isNaN();
    228. public boolean isInfinite();
    229. public int compareTo(java.lang.Float);
    230. public static java.lang.String toString(double);
    231. public static java.lang.String toHexString(double);
    232. public static java.lang.Double valueOf(java.lang.String);
    233. public static java.lang.Double valueOf(double);
    234. public static double parseDouble(java.lang.String);
    235. public static boolean isNaN(double);
    236. public static boolean isInfinite(double);
    237. public static long doubleToLongBits(double);
    238. public static long doubleToRawLongBits(double);
    239. public static double longBitsToDouble(long);
    240. public static int compare(double,double);
    241. public boolean isNaN();
    242. public boolean isInfinite();
    243. public int compareTo(java.lang.Double);
    244. public <init>(byte);
    245. public <init>(short);
    246. public <init>(int);
    247. public <init>(long);
    248. public <init>(float);
    249. public <init>(double);
    250. public <init>(java.lang.String);
    251. public byte byteValue();
    252. public short shortValue();
    253. public int intValue();
    254. public long longValue();
    255. public float floatValue();
    256. public double doubleValue();
    257. public int compareTo(java.lang.Object);
    258. public boolean equals(java.lang.Object);
    259. public int hashCode();
    260. public java.lang.String toString();
    261. }
    262. # Remove - String method calls. Remove all invocations of String
    263. # methods without side effects whose return values are not used.
    264. -assumenosideeffects public class java.lang.String {
    265. public <init>();
    266. public <init>(byte[]);
    267. public <init>(byte[],int);
    268. public <init>(byte[],int,int);
    269. public <init>(byte[],int,int,int);
    270. public <init>(byte[],int,int,java.lang.String);
    271. public <init>(byte[],java.lang.String);
    272. public <init>(char[]);
    273. public <init>(char[],int,int);
    274. public <init>(java.lang.String);
    275. public <init>(java.lang.StringBuffer);
    276. public static java.lang.String copyValueOf(char[]);
    277. public static java.lang.String copyValueOf(char[],int,int);
    278. public static java.lang.String valueOf(boolean);
    279. public static java.lang.String valueOf(char);
    280. public static java.lang.String valueOf(char[]);
    281. public static java.lang.String valueOf(char[],int,int);
    282. public static java.lang.String valueOf(double);
    283. public static java.lang.String valueOf(float);
    284. public static java.lang.String valueOf(int);
    285. public static java.lang.String valueOf(java.lang.Object);
    286. public static java.lang.String valueOf(long);
    287. public boolean contentEquals(java.lang.StringBuffer);
    288. public boolean endsWith(java.lang.String);
    289. public boolean equalsIgnoreCase(java.lang.String);
    290. public boolean equals(java.lang.Object);
    291. public boolean matches(java.lang.String);
    292. public boolean regionMatches(boolean,int,java.lang.String,int,int);
    293. public boolean regionMatches(int,java.lang.String,int,int);
    294. public boolean startsWith(java.lang.String);
    295. public boolean startsWith(java.lang.String,int);
    296. public byte[] getBytes();
    297. public byte[] getBytes(java.lang.String);
    298. public char charAt(int);
    299. public char[] toCharArray();
    300. public int compareToIgnoreCase(java.lang.String);
    301. public int compareTo(java.lang.Object);
    302. public int compareTo(java.lang.String);
    303. public int hashCode();
    304. public int indexOf(int);
    305. public int indexOf(int,int);
    306. public int indexOf(java.lang.String);
    307. public int indexOf(java.lang.String,int);
    308. public int lastIndexOf(int);
    309. public int lastIndexOf(int,int);
    310. public int lastIndexOf(java.lang.String);
    311. public int lastIndexOf(java.lang.String,int);
    312. public int length();
    313. public java.lang.CharSequence subSequence(int,int);
    314. public java.lang.String concat(java.lang.String);
    315. public java.lang.String replaceAll(java.lang.String,java.lang.String);
    316. public java.lang.String replace(char,char);
    317. public java.lang.String replaceFirst(java.lang.String,java.lang.String);
    318. public java.lang.String[] split(java.lang.String);
    319. public java.lang.String[] split(java.lang.String,int);
    320. public java.lang.String substring(int);
    321. public java.lang.String substring(int,int);
    322. public java.lang.String toLowerCase();
    323. public java.lang.String toLowerCase(java.util.Locale);
    324. public java.lang.String toString();
    325. public java.lang.String toUpperCase();
    326. public java.lang.String toUpperCase(java.util.Locale);
    327. public java.lang.String trim();
    328. }
    329. # Remove - StringBuffer method calls. Remove all invocations of StringBuffer
    330. # methods without side effects whose return values are not used.
    331. -assumenosideeffects public class java.lang.StringBuffer {
    332. public <init>();
    333. public <init>(int);
    334. public <init>(java.lang.String);
    335. public <init>(java.lang.CharSequence);
    336. public java.lang.String toString();
    337. public char charAt(int);
    338. public int capacity();
    339. public int codePointAt(int);
    340. public int codePointBefore(int);
    341. public int indexOf(java.lang.String,int);
    342. public int lastIndexOf(java.lang.String);
    343. public int lastIndexOf(java.lang.String,int);
    344. public int length();
    345. public java.lang.String substring(int);
    346. public java.lang.String substring(int,int);
    347. }
    348. # Remove - StringBuilder method calls. Remove all invocations of StringBuilder
    349. # methods without side effects whose return values are not used.
    350. -assumenosideeffects public class java.lang.StringBuilder {
    351. public <init>();
    352. public <init>(int);
    353. public <init>(java.lang.String);
    354. public <init>(java.lang.CharSequence);
    355. public java.lang.String toString();
    356. public char charAt(int);
    357. public int capacity();
    358. public int codePointAt(int);
    359. public int codePointBefore(int);
    360. public int indexOf(java.lang.String,int);
    361. public int lastIndexOf(java.lang.String);
    362. public int lastIndexOf(java.lang.String,int);
    363. public int length();
    364. public java.lang.String substring(int);
    365. public java.lang.String substring(int,int);
    366. }
  • 相关阅读:
    javascript关闭弹出窗体时刷新父窗体和居中显示弹出窗
    iOS 开发人员不可缺少的75个工具
    JavaWeb学习笔记:Servlet
    Oracle SQL 查询优化.Part4
    高速集成支付宝支付步骤及注意事项(原创)
    算法题-注水问题
    《C专家编程》数组和指针并不同--多维数组
    jQuery源代码 框架分析
    Python中strip方法的妙用
    友盟社会化分享
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/9661610.html
Copyright © 2011-2022 走看看