方格社区-淘金信息差

 找回密码
 立即注册

记录一次破解闪照|为啥现在闪照都破解不了了

[复制链接]
发表于 2022-4-9 17:20:28 | 显示全部楼层 |阅读模式
本帖最后由 不温卜火 于 2022-4-9 17:31 编辑


10001.jpg
妈了个巴子的,白瞎我两天时间,气死
闪照储存位置
    首先得找到闪照的文件储存的位置,这个过程相当繁琐,不再列举,直接把缓存的储存位置贴在下面

/storage/emulated/0/Android/data/com.tencent.mobileqq/Tencent/MobileQQ/chatpic/chatimg/


    在这个路径的文件夹下,有许多的子文件夹,子文件夹中的文件,就是闪照图片加密后的文件了
读取加密文件
    以十六进制读取加密后的文件,可以看到字节流以“ENCRYPT:”开头

    推己及人,如果是我开发这个程序,绝对会在读取加密文件的数据流时,在代码中判断与”ENCRYPT:”.getBytes(“UTF-8”)相同的流,那么也就是说在smali代码中很大可能通过”ENCRYPT:”这个字符串找到相应的解密方法
    果然,在com.tencent.mobileqq.utils.DESUtils类中找到了它,顺便发现了闪照文件的加密方式,DES加密
    那么可以推断,查看闪照时,程序读取加密文件的字节流并删除掉”ENCRYPT:”,然后通过Key进行DES解密,先编写DES解密方法吧
DES解密和加密
    DES是一种过时的加密方式,不够安全,不过用在这里好像恰到好处。。。
    解密方法,只要向方法中传入需要解密的文件路径和解密的Key即可
  1. /**
  2.      * DES加密文件解密
  3.      * @param filePath 加密文件的路径
  4.      * @param key 密钥
  5.      */
  6.     private static void decrypt(String filePath,Key key) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
  7.         File fromFile = new File(filePath); //已经加密的文件
  8.         File toFile = new File(filePath + "_decrypt.png"); //解密的文件的路径

  9.         Cipher cipher = Cipher.getInstance("DES");
  10.         cipher.init(Cipher.DECRYPT_MODE,key); //使用密钥解密

  11.         //加密文件文件头字节流
  12.         byte[] encryptBytes = "ENCRYPT:".getBytes("UTF-8");

  13.         if (fromFile.exists()){
  14.             InputStream inputStream = new FileInputStream(fromFile);
  15.             OutputStream outputStream = new FileOutputStream(toFile);

  16.             CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream,cipher);
  17.             byte[] buffer = new byte[1024];
  18.             int len;
  19.             int i = 0;
  20.             while ((len = inputStream.read(buffer)) >= 0){
  21. //                输出流中跳过加密文件头
  22.                 if (i == 0){
  23.                     cipherOutputStream.write(buffer, encryptBytes.length, len-encryptBytes.length);
  24.                 }
  25.                 else {
  26.                     cipherOutputStream.write(buffer, 0, len);
  27.                 }
  28.                 i++;
  29.             }
  30.             inputStream.close();
  31.             outputStream.close();
  32.             cipherOutputStream.close();
  33.         }
  34.         else {
  35.             System.out.println("文件不存在");
  36.         }
  37.     }
复制代码


    加密方法,有解密就顺手把加密放出来吧

  1. /**
  2. * DES加密文件
  3. * @param filePath 需要加密的文件的路径
  4. * @param key 密钥
  5. */
  6. private static void encrypt(String filePath,Key key) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
  7.     File fromFile = new File(filePath); //未加密的文件
  8.     File toFile = new File(filePath + "_encrypt"); //加密的文件

  9.     Cipher cipher = Cipher.getInstance("DES");
  10.     cipher.init(Cipher.ENCRYPT_MODE,key); //使用密钥加密

  11.     if (fromFile.exists()){
  12.         InputStream inputStream = new FileInputStream(fromFile);
  13.         OutputStream outputStream = new FileOutputStream(toFile);
  14.         CipherInputStream cipherInputStream = new CipherInputStream(inputStream,cipher);
  15.         byte[] buffer = new byte[1024];
  16.         int len;
  17.         while ((len = cipherInputStream.read(buffer)) > 0) {
  18.             outputStream.write(buffer, 0, len);
  19.         }

  20.         inputStream.close();
  21.         outputStream.close();
  22.         cipherInputStream.close();
  23.     }
  24.     else {
  25.         System.out.println("文件不存在");
  26.     }
  27. }
复制代码

    获取密钥的方式,计算方式有很多种,我只不过是为了测试程序可行性临时写了一个
  1. /**
  2. * 根据参数生成密钥
  3. * @param encodeKey key字符串参数
  4. * @return key对象
  5. */
  6. private static Key getKey(String encodeKey){
  7.     KeyGenerator key = null;
  8.     try {
  9.         key = KeyGenerator.getInstance("DES");
  10.         key.init(new SecureRandom(encodeKey.getBytes()));
  11.     } catch (NoSuchAlgorithmException e) {
  12.         e.printStackTrace();
  13.     }
  14.     assert key != null;
  15.     return key.generateKey();
  16. }
复制代码




    万事具备,只欠解密的Key
获取Key对象
    通过这个输入流可以看到,它是在进行DES解密,并通过SecretKeySpec创建DES解密的Key对象;

    在this(a(paramArrayOfByte),”DES”)中,虽然不知道a(paramArrayOfByte)是什么玩意儿,传入一个byte[]再返回一个byte[],,,不过它一定是必要的

  1. public static byte[] a(byte[] paramArrayOfbyte) {
  2.   byte[] arrayOfByte = new byte[8];
  3.   if (8 > paramArrayOfbyte.length) {
  4.     System.arraycopy(paramArrayOfbyte, 0, arrayOfByte, 0, paramArrayOfbyte.length);
  5.   } else {
  6.     System.arraycopy(paramArrayOfbyte, 0, arrayOfByte, 0, 8);
  7.   }
  8.   return arrayOfByte;
  9. }
复制代码


    然后去找a(paramArrayOfByte)所用到的paramArrayOfByte,它是方法的第三个参数,向上查找,发现当前类中重载的a方法调用了它,继续向上查找
  1. public static void a(String str, String str2) {
  2.     try {
  3.         if (!a(str)) {
  4.             long currentTimeMillis = System.currentTimeMillis();
  5.             File file = new File(str);
  6.             long length = file.length() / 1024;
  7.             File file2 = new File(str + ".tmp");
  8.             if (file2.exists()) {
  9.                 file2.delete();
  10.             }
  11.             //调用
  12.             a(file, file2, str2.getBytes("UTF-8"));
  13.             FileUtils.copyFile(file2, file);
  14.             file2.delete();
  15.             if (QLog.isDevelopLevel()) {
  16.                 QLog.d("DESUtil", 4, "DES Encrypt filePath:" + str + ",key:" + str2 + ",costTime:" + (System.currentTimeMillis() - currentTimeMillis) + ",fileSize:" + length + "KB");
  17.             }
  18.         } else if (QLog.isDevelopLevel()) {
  19.             QLog.d("DESUtil", 2, "encrypt had encrypt,file:" + str);
  20.         }
  21.     } catch (UnsupportedEncodingException e) {
  22.         e.printStackTrace();
  23.     }
  24. }
复制代码



    在com.tencent.mobileqq.dating.HotChatFlashPicActivity的子类HotChatFlashPicActivity$5中调用了这个方法,不过依然没有拿到key的计算方式,继续向上查找

    在HotChatFlashPicActivity的A()方法中,发现返回了this.t,并且doOnCreate()中有this.t的赋值this.t = getIntent().getStringExtra(“md5”);,所以可以猜测,key的字符串参数是某字符串的md5加密值或者文件的md5唯一值,如果是前者还好,是后者就凉了

    然后又经过一系列查找。。。发现在com.tencent.mobileqq.activity.aio.item.FlashPicItemBuilder类中向其put了md5参数

    在这里插入log输出md5参数的值
  1. String paramString = paramMessageForPic.md5
  2. bundle.putString("md5", paramString);
  3. Log.d("xxin", paramString);
复制代码




    当点击聊天记录中的闪照时,LogCat中输出了它的md5值“5AB73AA4439EB210F65D2115C887A191”

    通过这个md5,也就是key参数值,根据上面的思路,编写key获取方法
  1. /**
  2. * QQ闪照加密的KEY获取方式
  3. * @param md5 key参数值
  4. * @return key对象
  5. */
  6. private static Key getQQKey(String md5) throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException {
  7.     SecretKeySpec secretKeySpec = new SecretKeySpec(a(md5.getBytes("UTF-8")), "DES");
  8.     return secretKeySpec;
  9. }

  10. private static byte[] a(byte[] bytes) {
  11.     byte[] bytes1 = new byte[8];
  12.     if (8 > bytes.length){
  13.         System.arraycopy(bytes, 0, bytes1, 0, bytes.length);
  14.     }
  15.     else {
  16.         System.arraycopy(bytes, 0, bytes1, 0, 8);
  17.     }
  18.     return bytes1;
  19. }
复制代码




    调用key获取方法得到key,并使用key对加密的文件进行解密
  1. public static void main(String[] args) throws NoSuchPaddingException, IOException, NoSuchAlgorithmException, InvalidKeyException {
  2.     String md5 = "5AB73AA4439EB210F65D2115C887A191"; //原文件的md5值是DES加密文件时使用的密钥值,妈的,初步推测这个md5值从服务器获取
  3.     Key qqKey = getQQKey(md5);
  4.     decrypt("C:\\Users\\30335\\Desktop\\闪照加密\\Cache_-f5ed123ea2f6cdd_fp",qqKey); //解密
  5. }
复制代码




    目测解密成功
[

    打开看一下,确实没问题

    不过,如果多点几张不同的闪照,会发现md5值并不固定,所以它一定有一个计算方式:
md5值获取
    从上面知道,md5值是计算key对象用到的key参数值,key对象是解密闪照的密钥;md5值不固定,不同的图片的闪照有不同的md5值,但是同一张图片的闪照的md5值在任何情况下永远固定,不禁让人发起深思。。。这个md5不会是原图片文件签名的md5吧
    妈的,是的,这个md5值是验证文件唯一性的值,字节流不同的文件有不同的md5值,并不是通过其它什么计算得出,且这个md5值是在对方发送闪照时一并发送,作为闪照的接收端只能从服务器发送的数据中接收
    说简单直白点,要有原图的MD5,才能把加密后的图片解密成原图;只有原图,才能得到解密用的MD5;话说回来,都有原图了,我还解他妈的加密干什么
    当然,也可以直接问对方要原图MD5签名
    A:妹子可以发张照片看吗?
    B:[闪照]
    A:妹子可以提供下闪照原图的MD5签名吗,我解密下,谢谢。
    B:阴阳怪气什么啊,普信男真下头
    天无绝人之路,也可以像上面那样向原安装包中注入log,以使在点击闪照时在logcat中输出原图的md5签名,然后拿来解密闪照。。。



最后:简单点来说就是通过软件直接解密闪照是不可能的!除非修改QQ安装包。

回复

使用道具 举报

发表于 2022-4-9 17:32:18 来自手机 | 显示全部楼层
大佬啊     
回复 支持 反对

使用道具 举报

发表于 2022-4-9 19:14:59 来自手机 | 显示全部楼层
我直接小熊录屏,截图不就行了?
回复 支持 反对

使用道具 举报

发表于 2022-4-9 23:20:06 来自手机 | 显示全部楼层
999感冒灵 发表于 2022-4-9 19:14
我直接小熊录屏,截图不就行了?

这个可以无视闪照限制吗?我用华为或者苹果自带的录屏都说隐私界面不允许或者就是录出来空白
回复 支持 反对

使用道具 举报

发表于 2022-4-10 17:11:46 来自手机 | 显示全部楼层
999感冒灵 发表于 2022-4-9 19:14
我直接小熊录屏,截图不就行了?

截不了图,录屏出来也是黑屏的
回复 支持 反对

使用道具 举报

发表于 2022-4-12 01:37:26 来自手机 | 显示全部楼层
很强
回复 支持 反对

使用道具 举报

发表于 2022-4-12 06:29:34 来自手机 | 显示全部楼层
虽然看不懂,但是牛逼就对了
回复 支持 反对

使用道具 举报

发表于 2022-4-12 12:10:57 来自手机 | 显示全部楼层
大佬        
回复 支持 反对

使用道具 举报

发表于 2022-4-16 14:35:15 来自手机 | 显示全部楼层
技术流大佬,看完感觉我的java白学了
回复 支持 反对

使用道具 举报

发表于 2022-4-19 10:20:02 来自手机 | 显示全部楼层
真费劲下载个低版本的QQ不就好了
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表