“JAVA” 标签页面:
-
Android中Java编写的APK使用apktool反编译并修改smali
今天装了某个游戏,游戏中有“商店”,可以用金币购买相关物品。而获得金币的途径,是用人民币购买(1元100只)或者安装该公司的其他应用(安装3款得一点点)。
这显然不科学啊!
当然,程序在我机子上,我们当然能够用科学的手段来对付不科学的游戏货币商城制度。
1、使用apktool进行反编译,得到smali的字节码
假设游戏安装文件为my_game.apk,我们将把它反编译到mygame目录下。
在这里下载apktool和对应的dependencies,把apktool和对应的dependencies加压缩到一起,并将该文件夹添加到path环境变量中。我在使用Windows系统,这时候apktool的解压文件夹有以下内容:
aapt.exe
apktool.bat
apktool.jar
然后对my_game.apk进行反编译,运行命令:
apktool d my_game.apk mygame
可以看到apktool将游戏反编译到mygame文件夹中。其中的smali就是对应的字节码。
2、修改smali字节码
关于smali字节码的寄存器可以参考这里,类型、函数和成员可以参考这里,操作符号可以参考这里。
当然,由于程序已经混淆,所以不能从文件名和包名中猜测相关信息。这时候有好几个办法定位到要修改的地方。如调试设断点,在这里有教程。还可以通过资源和字符串定位,这是我现在使用的方法,因为我的调试环境还没搭好……
我注意到,某一个与购买相关的按钮图片的id编号为“0×7f020093”,于是在smali中搜索,最后在某个文件夹,也就对应Java的某个包中找到g.smali,这个文件包含了字符串“0×7f020093”。然后,点击购买时,会提示“金币不足,无法购买”,这是文本信息。也就是说,可以在g.smali中搜索该字符串,或者字符串子串来进行程序上下文的定位。当然,直接搜索中文字符串是一无所获的,因为反编译器将中文以utf8的裸编码格式表现。这时候在g.smali中搜索“\u91d1\u5e01\u4e0d\u8db3,\u65e0\u6cd5\u8d2d\u4e70”,找到详细位置!具体代码片段如下:
sget-object v0, La/f/c;->s:La/a/b/g;
iget-object v9, v0, La/a/b/g;->j:Ld/a/b;
iget v10, p2, La/d/n;->f:I
iget v0, v9, Ld/a/b;->v:I
if-ge v0, v10, :cond_0
invoke-static {}, Lcom/gale/manager/f;->a()Lcom/gale/manager/f;
move-result-object v0
const-string v1, "\u91d1\u5e01\u4e0d\u8db3,\u65e0\u6cd5\u8d2d\u4e70"
invoke-virtual {v0, v1}, Lcom/gale/manager/f;->a(Ljava/lang/String;)V
:goto_0
return-void:cond_0
neg-int v0, v10iput v0, v9, Ld/a/b;->x:I
需要关注的是“if-ge”这句,意思是如果v0大于等于v10,则跳到cond_0执行,否则显示“金币不足,无法购买”并返回。
我们可以直接将“if-ge”改成“if-le”解决问题,不过这时金币会在购买后变负数,说不定会把游戏crash。所以,更好的修改方法如下:
sget-object v0, La/f/c;->s:La/a/b/g;
iget-object v9, v0, La/a/b/g;->j:Ld/a/b;
iget v10, p2, La/d/n;->f:I
iget v0, v9, Ld/a/b;->v:I
if-ge v0, v10, :cond_0
goto/16 :cond_0:goto_0
return-void:cond_0
move v0, v10iput v0, v9, Ld/a/b;->x:I
意思是,即使当前金币小于购买所需金币,也跳到条件成立的地方执行。另外,执行时会将玩家的金币数加上所需金币的相反数,即“neg-int v0, v10”,将其改为“move v0, v10”就可以每次都把玩家的金币数加上所需金币数。最终效果则是,即使金币不足也可以购买,并且越买钱越多。
3、打包、签名和安装修改后的apk
修改完了,就可以打包回apk了。执行以下命令:
apktool b mygame
在mygame目录下的dist在会看到打包好的apk。
当然,现在一般是无法安装的,因为apk还没有签名。下面就来签名。签名需要keystore文件,我已经有专用的keystore了,如果还没有,请参阅这里进行生成。
执行以下命令为重新编译的my_game.apk签名:
jarsigner -verbose -keystore creke.keystore my_game.apk Alias_name
最后,在安装到手机前,需要把手机中的已有版本先卸载,因为如果签名不同,是不能覆盖安装的,会提示“应用程序未安装”错误。
4、其他
Windows下的aapt似乎不能对非本地磁盘进行读写,比如我原来在内存盘中进行操作,apktool总是提示aapt出错,不能打包apk。后来放到本地磁盘中进行才一切正常。
最后说一句,好的游戏从来不会用奇怪的货币和商城来吸引玩家或者玩家的钱财。
-
PHP与JAVA使用AES128位加密通信
本来JAVA和JSP之间加密通信好好的,相同的函数,相同的处理,不会有其他大问题。不过有时候就是蛋疼啊,于是就有了PHP与JAVA间使用AES进行加密通信。
PHP的AES128位由mcrypt模块提供,称为MCRYPT_RIJNDAEL_128。
JAVA的AES默认就是128位的。
加密模式有好几种,不同的语言不同的库支持的情况不同。这里选择的是安全且通用的CBC模式。
至于padding,这是最头疼的问题,因为PHP的padding与Java的padding不一样。如果使用NoPadding,则默认又用不了CBC模式。所以,最好的解决方法是自己padding——在原文末尾加上若干个空格,使原文凑齐16的倍数的长度。当然,原文末尾也可能是空格结束啊,那怎么办?没办法,只有强制原文末尾加上一个换行。这样子,每次解密后,将最右边的换行以及其右边的空格裁剪掉,就得到原文了。
另外,为了兼容,在加密和解密时,需要将内容转换成16进制的字符数组。这样一来,即使加密/解密的内容不是普通文本,而是二进制数据,也可以轻松传送啦。
JAVA方面
初始化代码:
try { cipherEnc = Cipher.getInstance("AES/CBC/NoPadding"); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (NoSuchPaddingException ex) { ex.printStackTrace(); } try { cipherDec = Cipher.getInstance("AES/CBC/NoPadding"); } catch (NoSuchAlgorithmException ex) { ex.printStackTrace(); } catch (NoSuchPaddingException ex) { ex.printStackTrace(); } key = new SecretKeySpec(keyStr.getBytes(), "AES"); iv = new IvParameterSpec(ivStr.getBytes());
加密解密及其核心函数:
public static String padRight(String s, int n) { return String.format("%1$-" + n + "s", s); } public static String padLeft(String s, int n) { return String.format("%1$#" + n + "s", s); } public String encrypt(SecretKeySpec enc_key, IvParameterSpec enc_iv, String str){ byte[] ret = null; try { cipherEnc.init(Cipher.ENCRYPT_MODE, enc_key, enc_iv); ret = cipherEnc.doFinal(padRight(str, ((int)Math.ceil(str.length() / 16.0))*16).getBytes()); } catch (Exception ex) { ex.printStackTrace(); return null; } return byteArray2HexString(ret); } /* * str is Hex String */ public String decrypt(SecretKeySpec dec_key, IvParameterSpec dec_iv, String str){ byte[] ret = null; try { cipherDec.init(Cipher.DECRYPT_MODE, dec_key, dec_iv); ret = cipherDec.doFinal(hexString2ByteArray(str)); } catch (Exception ex) { ex.printStackTrace(); return null; } try { return new String(ret, "UTF-8"); } catch (UnsupportedEncodingException e) { return null; } } static final char[] HEX_CHAR_TABLE = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public static String byteArray2HexString(byte[] b) { if (b == null) { return null; } final StringBuilder hex = new StringBuilder(2 * b.length); for (final byte by : b) { hex.append(HEX_CHAR_TABLE[(by & 0xF0) >> 4]).append(HEX_CHAR_TABLE[(by & 0x0F)]); } return hex.toString(); } public static byte[] hexString2ByteArray(String s) { if (s == null) { return null; } byte high, low; int len = s.length() / 2; byte[] b = new byte[len]; for(int i=0, k=0; i<len; i++, k+=2) { high = (byte) (Character.digit(s.charAt(k), 16) & 0x0F); low = (byte) (Character.digit(s.charAt(k+1), 16) & 0x0F); b[i] = (byte) ((high<<4) | low); } return b; }
PHP方面
加密解密部分及其核心函数
static function encrypt($enc_key, $enc_iv, $data){ $pad = str_pad($data, ceil(strlen($data)/16.0)*16, " "); $method = MCRYPT_RIJNDAEL_128; $mode = MCRYPT_MODE_CBC; $td = mcrypt_module_open($method, '', $mode, ''); mcrypt_generic_init ( $td , $enc_key , $enc_iv); $encrypt = mcrypt_generic($td, $pad); mcrypt_generic_deinit($td); mcrypt_module_close($td); return bin2hex($encrypt); } static function decrypt($dec_key, $dec_iv, $data){ $method = MCRYPT_RIJNDAEL_128; $mode = MCRYPT_MODE_CBC; $td = mcrypt_module_open($method, '', $mode, ''); mcrypt_generic_init ( $td , $dec_key , $dec_iv); $decrypt = mdecrypt_generic($td, hex2bin($data)); mcrypt_generic_deinit($td); mcrypt_module_close($td); return $decrypt; } if(!function_exists('hex2bin')) { /** * Converts the hex representation of data to binary * * http://www.php.net/manual/en/function.hex2bin.php * * @param string $str Hexadecimal representation of data * * @return string Returns the binary representation of the given data */ function hex2bin($data) { return pack("H*" , $data); } }就是这样啦。什么?不懂怎么用?那就看看AES的文献,看看Java和PHP的文档再说吧。
-
JAVA中将PrivateKey和X509Certificate对象保存为OpenSSL等程序可用的标准格式
我将两个个对象存进了ca.cert中,第一个是CA的私钥,第二个是CA的证书。
现在需要将ca.cert中的CA私钥和证书读取出来,并保存为OpenSSL等程序可识别的标准格式。
以下是代码:
import java.io.*; import java.security.*; import java.security.spec.*; import java.security.cert.X509Certificate; import java.util.*; import sun.misc.BASE64Encoder; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.X509Extensions; import org.bouncycastle.jce.X509Principal; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.x509.X509V3CertificateGenerator; import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; public class ExportKeys { public static void main(String args[]) { X509Certificate caCert = null; PrivateKey caPriKey = null; PublicKey caPubKey = null; BASE64Encoder encoder = null; try { FileInputStream caCertFis = new FileInputStream("ca.cert"); ObjectInputStream caCertOis = new ObjectInputStream(caCertFis); caPriKey = (PrivateKey) caCertOis.readObject(); caCert = (X509Certificate) caCertOis.readObject(); caPubKey = caCert.getPublicKey(); caCertOis.close(); caCertFis.close(); } catch (Exception ex) { ex.printStackTrace(); } //导出私钥 try { encoder=new BASE64Encoder(); String encoded=encoder.encode(caPriKey.getEncoded()); FileWriter fw=new FileWriter("ca.key"); fw.write("-----BEGIN PRIVATE KEY-----\n"); fw.write(encoded); fw.write("\n"); fw.write("-----END PRIVATE KEY-----"); fw.close(); } catch (Exception ex) { ex.printStackTrace(); } //导出证书 try { encoder=new BASE64Encoder(); String encoded=encoder.encode(caCert.getEncoded()); FileWriter fw=new FileWriter("ca.crt"); fw.write("-----BEGIN CERTIFICATE-----\n"); fw.write(encoded); fw.write("\n"); fw.write("-----BEGIN CERTIFICATE-----"); fw.close(); } catch (Exception ex) { ex.printStackTrace(); } } }
程序需要使用bouncycastle库来操作X509证书(即CA证书),下载地址在这里。
编译命令和执行命令是:
编译:
javac -cp .;bcprov-ext-jdk15-145.jar ExportKeys.java
执行:
java -cp .;bcprov-ext-jdk15-145.jar ExportKeys
就是这样,完毕。
-
JAVA动态生成兼容浏览器的HTTPS(SSL)证书
JAVA自带的SSL以及X509库只能使用SSL证书,不能生成SSL证书。因此我们使用“Bouncy Castle”这个算法库来实现SSL证书的生成。然后使用X509KeyManager来构建符合浏览器规范的SSL证书链,以便自建代理服务器时,信任该CA就可以浏览HTTPS内容而不会有安全提醒。
浏览器生成的CA,使用X509的V3版。V3版和V1版的主要不同是可以生成扩展字段。详细信息可以参考Bouncy Castle的WIKI上面的解释。参考了burpsuite,发现CA的扩展字段需要Subject Key Identifier,再加上Basic Constraints声明是CA及最大中间CA数即可兼容大部分主流浏览器。
注意一点,就是Windows系统的证书验证似乎只检查公钥私钥,根证书使用相同的密钥对每次动态生成都可以通过信任CA的认证;而Firefox则严格很多,需要把CA保存下来,每次都使用同样的CA才能匹配信任列表中的对应CA。
生成密钥对:
KeyPairGenerator caKeyPairGen = KeyPairGenerator.getInstance("RSA", "BC"); caKeyPairGen.initialize(1024, new SecureRandom()); KeyPair keypair = caKeyPairGen.genKeyPair(); caPriKey = keypair.getPrivate(); caPubKey = keypair.getPublic();生成CA:
public static X509Certificate createAcIssuerCert( PublicKey pubKey, PrivateKey privKey) throws Exception { X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); // // signers name // String issuer = "CN=My CA, OU=My CA, O=My, L=My, ST=AMy, C=CN"; // // subjects name - the same as we are self signed. // String subject = issuer; // // create the certificate - version 3 // v3CertGen.setSerialNumber(BigInteger.valueOf(0x1234ABCDL)); v3CertGen.setIssuerDN(new X509Principal(issuer)); v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 30*aDay)); v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + 36500*aDay)); v3CertGen.setSubjectDN(new X509Principal(subject)); v3CertGen.setPublicKey(pubKey); v3CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption"); // Is a CA v3CertGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true)); v3CertGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(pubKey)); X509Certificate cert = v3CertGen.generateX509Certificate(privKey); cert.checkValidity(new Date()); cert.verify(pubKey); return cert; }
使用CA签发网站证书:
public static X509Certificate createClientCert( PublicKey pubKey, PrivateKey caPrivKey, PublicKey caPubKey, String host) throws Exception { X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator(); // // issuer // String issuer = "CN=My CA, OU=My CA, O=My, L=My, ST=My, C=CN"; // // subjects name table. // Hashtable attrs = new Hashtable(); Vector order = new Vector(); attrs.put(X509Principal.C, "CN"); attrs.put(X509Principal.O, "My"); attrs.put(X509Principal.OU, "My"); attrs.put(X509Principal.CN, host); order.addElement(X509Principal.C); order.addElement(X509Principal.O); order.addElement(X509Principal.OU); order.addElement(X509Principal.CN); // // create the certificate - version 3 // v3CertGen.reset(); v3CertGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); v3CertGen.setIssuerDN(new X509Principal(issuer)); v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 10*aDay)); v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + 3650*aDay)); v3CertGen.setSubjectDN(new X509Principal(order, attrs)); v3CertGen.setPublicKey(pubKey); v3CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption"); X509Certificate cert = v3CertGen.generateX509Certificate(caPrivKey); cert.checkValidity(new Date()); cert.verify(caPubKey); return cert; }
对应的X509KeyManager每次从host_port.cert文件中读取对应的网站证书,然后加上CA证书构成证书链返回:
public final class MyKeyManager implements X509KeyManager { private String entryname; private String port; private X509Certificate caCert; private X509Certificate clientCert; private PrivateKey privatekey; MyKeyManager(String host, String port, X509Certificate caCert) { this.port = port; this.entryname = host; this.caCert = caCert; try { String certFileName = host + "_" + port + ".cert"; FileInputStream caCertFis = new FileInputStream(certFileName); ObjectInputStream oos = new ObjectInputStream(caCertFis); privatekey = (PrivateKey) oos.readObject(); clientCert = (X509Certificate) oos.readObject(); oos.close(); caCertFis.close(); } catch (Exception ex) { ex.printStackTrace(); } } public String[] getClientAliases(String string, Principal[] prncpls) { throw new UnsupportedOperationException("Not supported yet."); } public String chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket) { throw new UnsupportedOperationException("Not supported yet."); } public String[] getServerAliases(String string, Principal[] prncpls) { return (new String[] { entryname }); } public String chooseServerAlias(String string, Principal[] prncpls, Socket socket) { return entryname; } public X509Certificate[] getCertificateChain(String string) { X509Certificate x509certificates[] = new X509Certificate[2]; x509certificates[0] = clientCert; x509certificates[1] = caCert; return x509certificates; } public PrivateKey getPrivateKey(String string) { return this.privatekey; } }
使用实例:
SSLContext sslcontext; sslcontext = SSLContext.getInstance("SSL"); X509KeyManager[] x509km = new X509KeyManager[]{ new AsanKeyManager(host , port , KeyMaker.getCAcert())}; sslcontext.init(x509km, null, null); SSLServerSocketFactory sslserversocketfactory = sslcontext.getServerSocketFactory();这样动态生成证书,只要在浏览器第一次浏览HTTPS内容时,信任该CA,以后就不会出现安全警告了。当然,真正的安全程度,靠的是你的代理程序的数据传输的安全性啦。
-
JAVA启航之旅(写给新手):安装配置IDE(Jcreator篇)
JAVA启航之旅(写给新手):安装配置IDE(Jcreator篇)
前言:
大家学习C++时,使用的一般是VC或者C-free。偶尔有火星人用TC,偶尔由高手用VS。其实它们都集成了开发套装(编译器、解释器及库类等)和开发环境(源代码编辑器)。但是学习JAVA时,事情就没那么简单了。你要分别配置两者。
这次,我们选择Jcreator作为IDE。针对新手。全文讲解比较详细。
Jcreator是最接近VC和C-free的IDE,相信大家如果只是应付一下学习,只是随便抄点程序,使用Jcreator是最佳选择,而且会很快上手。前言:请确定你的机器已经安装并配置好了JDK。如果没有,请参照本人写的《JAVA启航之旅:安装配置JDK(JAVA SE)》
一、下载并安装Jcreator
前往http://www.jcreator.com/download.htm下载Jcreator。然后,安装。大家也不要执迷于汉化版了,既然已经学到了JAVA,认命吧,用英文版吧。动一下手,动一下脑子也好,对不。二、配置Jcreator
首次启动Jcreator时,出现”Jcreator Setup Wizard”。建议在”User settings”中选择”Save settings in the installation directory”,以防重装系统时丢失配置。下一步来到”File Associations”,这是文件关联,一般默认即可。下一步来到”JDK Home Directory”,这里填写JDK的安装目录,不过一般都能够自动识别,不用管。下一步来到”JDK JavaDoc Directory”,这里填写JDK文档目录,一般没装,所以默认留空或自动识别,不用管,点击”Finish”三、注册Jcreator
Jcreator注册后才能成为专业版,专业版相对普通版多出的功能都是很实用的,比如代码提示等等。你可以去官方网站注册,费用是单用户89美元,教育用途用户35美元。显然大家都买不起,所以深感社会主义好的我们,可以直接填写注册码。在这里,如果你是Jcreator 4.5的用户,可以:在Help――Enter Registration Details里,在Name处填写”Made By HHJ”,在Key处填写”00000G-HHH22K-WTK2U6-RWFVDW-VAJMPC-EPM47D-JXA0U9-9F228N-PAK6ME”。如果你在看到本文后时光已经流逝了不知多少岁月(说不定这时MS已经倒闭了),如果你用的是比4.5更高版本,你就得上网新版本注册码了。四、配置Jcreator
基本不需要配置,说了够傻瓜的。想DIY的就调一下各个视图就OK了。讲完了。大家使用上次的HelloDate测试一下就行。
-
JAVA启航之旅(写给菜鸟):安装配置JDK(JAVA SE)
JAVA启航之旅(写给菜鸟):安装配置JDK(JAVA SE)
写给菜鸟。
一、安装JDK
到http://www.skycn.com/soft/52313.html下载Java SE Development Kit (JDK)。然后,安装。二、设置环境变量:
右击”我的电脑”――属性——高级――环境变量。
你可以更改”用户变量”部分,那么你的更改只能用于当前用户;你也可以更改”系统变量”部分,那么你的更改可以用于计算机所有用户。
单击”新建”,在”变量名”中输入”java_home”,在”变量值”中填入你的JDK的安装路径,默认为”C:\Program Files\Java\jdk1.6.0_12″是你的JDK默认安装位置,具体根据自己的电脑作修改。大家可以看见,这个变量描述了JDK的安装位置,可以便于下面的设置。
单击”新建”,”变量名”中输入”classpath”,在”变量值”中填入”.;%java_home%\lib;%java_home%\lib\tools.jar”。其中”.”表示当前目录,后面的不难理解了。
找到”path”变量,单击”编辑”,在”变量值”的最后加上”;%java_home%\bin”。如果找不到”path”变量就按照上面的方法新建path变量。
注意:以上操作不区分大小写。三、测试JDK
用记事本输入”************”之间的内容:
************
import java.util.*;public class HelloDate
{
public static void main(String[] args)
{
System.out.println(”Hello,it’s:”);
System.out.println(new Date());
}
}
************
单击”文件”――”保存”,在”文件类型”中选择”所有文件(*.*)”,然后在”文件名”中输入”HelloDate.java”,接着把保存地点选到D盘的根目录,最后单击保存。
然后单击”开始”――”运行”。输入”cmd”,回车。这步操作是打开”命令提示符”。
在那个黑乎乎,看着就觉得恐怖的界面中,输入”javac d:\HelloDate.java”。如果没什么不正确的提示,那么恭喜你,你可以继续输入“d:”,回车,跳转到C盘根目录,再输入”java HelloDate”。这时将会显示一句”Hello,it’s:”以及当前系统时间。
注意:以上操作区分大小写!到此为止,JDK安装配置完毕!有什么问题,欢迎交流!
-
我与JAVA(一)
我与JAVA(一)
终于学了JAVA,3天前开始。
选择JAVA,就等于选择不断学习。这句话没错。JAVA的语法、函数都在不断变化中,这也反映了JAVA的特点:灵活而有生命力。
JAVA应该是个不错的选择。跨平台,安全,而且应用广。
学到常量变量部分,遇到了一些难题,基本都解决了,感觉还行。计划月底学完基本知识,起码能学到网络编程(说不定编个挂Q工具,哈哈)。一定要实现目标!
时间脚印
| 一 | 二 | 三 | 四 | 五 | 六 | 日 |
|---|---|---|---|---|---|---|
| « 三 | ||||||
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | 31 | ||
最新文章
- ffmpeg/ffprobe查看音频视频文件信息并输出为json格式
- ffmpeg将音频或视频编码为AMR格式音频
- CentOS编译安装ffmpeg以及相关编码解码器
- CentOS编译安装新版gnutls
- MingW通过mingw-get的package管理切换回gcc 4.5.2
最近评论
- Bobhu 在 ffmpeg/ffprobe查看音频视频文件信息并输出为json格式 上的评论
- ApkTool反编译和重新打包 - iew3c 在 Android中Java编写的APK使用apktool反编译并修改smali 上的评论
- zaykl 在 为nginx配置https并自签名证书 上的评论
- cubieboard试用手记 | 斯巴达第二季 在 为nginx配置https并自签名证书 上的评论
- 给猫 在 一年的感动,终于终结――《Clannad Afterstory》完结纪念 上的评论