在com/dangdang/reader/e/a目录下有个叫a.smali的文件,里面有个叫a的方法,我把代码全都贴出来
1 .method public static a([B[B)[B 2 .locals 5 3 4 new-instance v0, Lorg/bouncycastle/jce/provider/BouncyCastleProvider; 5 6 invoke-direct {v0}, Lorg/bouncycastle/jce/provider/BouncyCastleProvider;-><init>()V 7 8 invoke-static {v0}, Ljava/security/Security;->addProvider(Ljava/security/Provider;)I 9 10 new-instance v0, Ljavax/crypto/spec/SecretKeySpec; 11 12 const-string v1, "AES" 13 14 invoke-direct {v0, p0, v1}, Ljavax/crypto/spec/SecretKeySpec;-><init>([BLjava/lang/String;)V 15 16 const-string v1, "AES/CBC/PKCS7Padding" 17 18 const-string v2, "BC" 19 20 invoke-static {v1, v2}, Ljavax/crypto/Cipher;->getInstance(Ljava/lang/String;Ljava/lang/String;)Ljavax/crypto/Cipher; 21 22 move-result-object v1 23 24 const/4 v2, 0x2 25 26 new-instance v3, Ljavax/crypto/spec/IvParameterSpec; 27 28 sget-object v4, Lcom/dangdang/reader/e/a/a;->a:[B 29 30 invoke-direct {v3, v4}, Ljavax/crypto/spec/IvParameterSpec;-><init>([B)V 31 32 invoke-virtual {v1, v2, v0, v3}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V 33 34 invoke-virtual {v1, p1}, Ljavax/crypto/Cipher;->doFinal([B)[B 35 36 move-result-object v0 37 38 return-object v0 39 .end method
上述smali代码翻译成源代码大致上是这样的:
1 public static byte[] a(byte[] p1, byte[] p2) 2 { 3 Security.addProvider(new BouncyCastleProvider()); 4 SecretKeySpec localSecretKeySpec = new SecretKeySpec(p1, "AES"); 5 Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC"); 6 localCipher.init(2, localSecretKeySpec, new IvParameterSpec(a)); 7 return localCipher.doFinal(p2); 8 }
其中第6行有个数字2,经过查资料,得知
也就是说2代表DECRY_MODE,即采用AES算法解密。其中两个参数p1代表对称秘钥,p2代表要解密的密文。
我们可以试着动态调试,用Log的方式,将秘钥打印出来。
修改上面的com/dangdang/reader/e/a下的a.smali文件,在第3行附近加入以下代码:
1 const-string v0, "bupt" 2 new-instance v1, Ljava/lang/String; 3 const-string v2, "UTF-8" 4 invoke-direct {v1,p0,v2}, Ljava/lang/String;-><init>([BLjava/lang/String;)V 5 invoke-static {v0, v1}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
稍微解释一下:将byte[]数组转为String,然后调用Log.e()方法将秘钥打印出来。在logcat里查看显示的是乱码,如下图
导出来发现只有104位,开头的空格和结尾的回车去掉了。
疯了,不知道哪边错了。
接下来,我们用grep 命令查找哪个地方调用了上述a方法,发现com/dangdang/reader/epubreader/b/ 目录下有个1.smali。
1 private byte[] loadResource(String paramString, boolean paramBoolean, byte[] paramArrayOfByte) 2 { 3 if (!this.mEncrypted) 4 paramBoolean = false;
5 com.dangdang.reader.c.a.c("loadResource().." + paramString + "(" + paramBoolean + ")"); 6 byte[] arrayOfByte1; 7 //省略了try catch等代码 8 FileInputStream localFileInputStream = new FileInputStream(paramString); 9 ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream(); 10 byte[] arrayOfByte3 = new byte[1024]; 11 for (int i = localFileInputStream.read(arrayOfByte3); i != -1; i = localFileInputStream.read(arrayOfByte3)) 12 localByteArrayOutputStream.write(arrayOfByte3, 0, i); 13 localFileInputStream.close(); 14 localByteArrayOutputStream.flush(); 15 localByteArrayOutputStream.close(); 16 byte[] arrayOfByte4 = localByteArrayOutputStream.toByteArray(); 17 arrayOfByte1 = arrayOfByte4; 18 if (arrayOfByte1 == null) 19 return arrayOfByte1;
。。。。。。。
byte[] arrayOfByte2 = com.dangdang.reader.e.a.a.a(paramArrayOfByte, arrayOfByte1);
return arrayOfByte2;
}
再结合我们上节破解出的log日志,对照着看
loadResource()../mnt/sdcard/dangdang/undefine/readbook/1900284177/1900284177/OPS/Preface3.xhtml(true)
- 第一个参数paramString 是sd卡上的电子书路径
- 第二个参数paramBoolean为true表明这是解密
- 第三个参数paramArrayOfByte代表对称秘钥
======================
参考破解"当当"DRM版权保护的电子书中的介绍,找到sd目录里的book_decode_key文件,对其进行Base64解码,得到128位AES秘钥。
1 String str = readFileToString(new File("book_decode_key"), "UTF-8"); 2 byte[] aesKey = Base64.decode(str); 3 StringBuffer sb = new StringBuffer(); 4 for(byte b: aesKey){ 5 sb.append(String.format("%x ", b)); 6 } 7 System.out.println(sb);
a0 e8 59 55 32 61 13 98 cd 20 bf b2 97 e2 a0 4a
下一步呢??