吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 58705|回复: 285
收起左侧

[原创] FXXXXRXXXXX 9.0 注册分析

    [复制链接]
benzcomp 发表于 2018-11-11 16:52
本帖最后由 ubuntu 于 2019-6-6 18:32 编辑

先说结论:
1、授权文件采用RSA加密,理论上是无法破解,要想破解只能替换密钥。但是,验证过程使用了解密后的明码比较来验证注册码的合法性,所以,理论上可以通过修改验证函数,输入明码license格式文件,跳过RSA加解密过程;
2、FineReport9为了保证安全,软件自定义了类加载器,设计了100个类加载器,只有反编译所有的加载器才能知道授权内容,过程大致如下:通过com.fr.plugin.bridge. Start推出com.fr.plugin.bridge._7c269529_9bce_43a5_95db_b44008bf984f,然后通过com.fr.plugin.bridge._7c269529_9bce_43a5_95db_b44008bf984f推出下一个加载器,一直往下推,总共有100个。
3、授权核心代码采用全部存储在com.fr.plugin.A目录下,后缀名为classx,这些文件实际上是加密的class文件,这些文件解密出来class文件不是授权类,是一个描述授权核心类的内文件,这些字符通过javassist工具动态生成类;
4、修改验证函数所在类com.fr.plugin.A.H.D,然后,反向加密成classx文件,替换同名文件。
分析步骤:
1、因为FineReport9的加密方式完全改变了,所以,8的方式不管用了。反编fr-core-9.0.jar,搜license可以在fr-core-9.0.jar!\com\fr\regist\FRCoreContext.class中找到关于license的函数
[Java] 纯文本查看 复制代码
  static
  {
    LISTENERS = new CopyOnWriteArrayList();
    PluginStartup.start();
    refresh();
    addShutDownHook();
  }

PluginStartup.start();这一句是真正调用license相关函数的入口,跟到fr-core-9.0.jar!\com\fr\plugin\manage\PluginStartup.class,内容如下:
[Java] 纯文本查看 复制代码
package com.fr.plugin.manage;

public abstract class PluginStartup
{
  private static final String START_LOADER_FACTORY_NAME = "com.fr.plugin.bridge.Start";
  private static final int STATE_NONE = 0;
  private static final int STATE_STARTING = 1;
  private static final int STATE_STATED = 2;
  private static int state = 0;
  
  public static synchronized void start()
  {
    if (state != 0) {
      return;
    }
    state = 1;
    try
    {
      initEncryptedBridge();
    }
    catch (Throwable localThrowable1)
    {
      try
      {
        Class.forName("com.fr.plugin.bridge.FinePluginBridge");
      }
      catch (Throwable localThrowable2)
      {
        localThrowable2.printStackTrace();
      }
    }
    state = 2;
  }
  
  private static void initEncryptedBridge()
    throws Exception
  {
    EngineClassLoaderFactory localEngineClassLoaderFactory = (EngineClassLoaderFactory)Class.forName("com.fr.plugin.bridge.Start").newInstance();
    ClassLoader localClassLoader = localEngineClassLoaderFactory.create();
    Class.forName("com.fr.plugin.bridge.FinePluginBridge", true, localClassLoader);
  }
}

可以看出来,加载了com.fr.plugin.bridge.Start,fr-core-9.0.jar!\com\fr\plugin\bridge\Start.class:
[Java] 纯文本查看 复制代码
public class Start
  extends ClassLoader
  implements EngineClassLoaderFactory
{
  private static final String NAME = "com.fr.plugin.bridge._ef95c0db_2556_4e47_a048_ea1b1a9c89a3";
  private static final String CLASS_PATH = "com/fr/plugin/bridge/_ef95c0db_2556_4e47_a048_ea1b1a9c89a3.classx";
  
  public Start()
  {
    super(GeneralContext.class.getClassLoader());
  }
  
  public ClassLoader create()
    throws Exception
  {
    return decryptFactory().create();
  }
  
  private EngineClassLoaderFactory decryptFactory()
    throws Exception
  {
    byte[] arrayOfByte1 = PluginBaseUtils.inputStream2Bytes(super.getResourceAsStream("com/fr/plugin/bridge/_ef95c0db_2556_4e47_a048_ea1b1a9c89a3.classx"));
    try
    {
      Field localField = Unsafe.class.getDeclaredField("theUnsafe");
      localField.setAccessible(true);
      Unsafe localUnsafe = (Unsafe)localField.get(null);
      
      byte[] arrayOfByte2 = _8ba75880_050f_43a5_9789_4afd24c549ef().doFinal(arrayOfByte1);
      Class localClass = localUnsafe.defineClass("com.fr.plugin.bridge._ef95c0db_2556_4e47_a048_ea1b1a9c89a3", arrayOfByte2, 0, arrayOfByte2.length, this, null);
      
      return (EngineClassLoaderFactory)localClass.newInstance();
    }
    catch (Throwable localThrowable)
    {
      throw new ClassNotFoundException("");
    }
  }
  
  private Cipher _8ba75880_050f_43a5_9789_4afd24c549ef()
    throws Exception
  {
    Cipher localCipher = Cipher.getInstance("AES");
    
    byte[] arrayOfByte1 = { -83, 120, 8, 6, -104, 96, -80, -98, 109, 56, -117, 118, 30, 18, -119, 7 };
    
    byte[] arrayOfByte2 = arrayOfByte1;
    
    SecretKeySpec localSecretKeySpec = new SecretKeySpec(arrayOfByte2, "AES");
    
    localCipher.init(2, localSecretKeySpec);
    
    return localCipher;
  }
}

从这里开始,就是不停的使用AES解密下一个类,动态加载,再解密下一个,一共100个classx文件,每一个文件的解密密钥不同。直到第100个classx解密后就可以得到com/fr/plugin/A目录下所有classx文件的密钥。
2、得到密钥后把com.fr.plugin.A.M.A.C.D.classx解密得到class文件,反编:
[Java] 纯文本查看 复制代码
class DynaClass {
public static String D() {
                try {
                        return new String(new byte[] { (byte) 99, (byte) 111, (byte) 109, (byte) 46, (byte) 102, (byte) 114,
                                        (byte) 46, (byte) 108, (byte) 105, (byte) 99, (byte) 101, (byte) 110, (byte) 115, (byte) 101,
                                        (byte) 46, (byte) 115, (byte) 101, (byte) 108, (byte) 101, (byte) 99, (byte) 116, (byte) 111,
                                        (byte) 114, (byte) 46, (byte) 69, (byte) 110, (byte) 99, (byte) 114, (byte) 121, (byte) 112,
                                        (byte) 116, (byte) 101, (byte) 100, (byte) 76, (byte) 105, (byte) 99, (byte) 101, (byte) 110,
                                        (byte) 115, (byte) 101, (byte) 83, (byte) 101, (byte) 108, (byte) 101, (byte) 99, (byte) 116,
                                        (byte) 111, (byte) 114 }, "UTF-8");
                } catch (Throwable localThrowable) {
                }
                return "";
        }

        public static byte[] E() {
                byte[] arrayOfByte = new byte[0];
                arrayOfByte = A(arrayOfByte, getByteCode0());
                arrayOfByte = A(arrayOfByte, getByteCode1());
                arrayOfByte = A(arrayOfByte, getByteCode2());
                arrayOfByte = A(arrayOfByte, getByteCode3());
                arrayOfByte = A(arrayOfByte, getByteCode4());
                return arrayOfByte;
        }

        public String A() {
                return "dc1473c8b718c3f144d3f443517d2c44";
        }

        private static byte[] getByteCode0() {
                return new byte[] { (byte) -54,…… };
        }

        private static byte[] getByteCode1() {
                return new byte[] { (byte) 101, ……};
        }

        private static byte[] getByteCode2() {
                return new byte[] { (byte) -126, …… };
        }

        private static byte[] getByteCode3() {
                return new byte[] { (byte) 47, …… };
        }

        private static byte[] getByteCode4() {
                return new byte[] { (byte) 0, …… };
        }
}

通过byte字节数组拼接出com.fr.license.selector.EncryptedLicenseSelector.class文件
3、反编com.fr.license.selector.EncryptedLicenseSelector.class就到了真正的license验证入口:
[Java] 纯文本查看 复制代码
public abstract class EncryptedLicenseSelector
  extends AbstractLicenseSelector
{
  private static final String _0eca828a_77d6_428a_9a97_91c04b434f4a = "RSA";
  private static final int _9703cf65_25a0_49a0_91ed_738941a0738d = 128;
  private static final ExecutorService _2e5864b3_2147_4738_9fbf_edfc852e40ae = ;
  
  byte[] getBytes()
  {
    byte[] arrayOfByte = readRawBytes();
    return decrypt(arrayOfByte);        //改成 return arrayOfByte 就可以使用明码lic文件验证了。
  }
  
  private byte[] decrypt(byte[] paramArrayOfByte)
  {
    try
    {
      System.getProperties().setProperty("com.ibm.crypto.provider.DoRSATypeChecking", "false");
      X509EncodedKeySpec localX509EncodedKeySpec = new X509EncodedKeySpec(LicenseConstants.KEY);
      KeyFactory localKeyFactory = KeyFactory.getInstance("RSA");
      PublicKey localPublicKey = localKeyFactory.generatePublic(localX509EncodedKeySpec);
      Cipher localCipher = Cipher.getInstance("RSA");
      localCipher.init(2, localPublicKey);
      return dealFragment(paramArrayOfByte, localCipher);
    }
    catch (Throwable localThrowable)
    {
      _2e5864b3_2147_4738_9fbf_edfc852e40ae.execute(new EncryptedLicenseSelector.1(this));
      decryptFailed();
    }
    return new byte[0];
  }
  
  protected void decryptFailed()
  {
    FRLoggerFactory.getLogger().error("Read license failed.");
  }
  
  abstract byte[] readRawBytes();
  
  private static byte[] dealFragment(byte[] paramArrayOfByte, Cipher paramCipher)
    throws IllegalBlockSizeException, BadPaddingException
  {
    byte[] arrayOfByte1 = new byte[0];
    for (int i = 0; i < paramArrayOfByte.length; i += 128)
    {
      byte[] arrayOfByte2 = ArrayUtils.subarray(paramArrayOfByte, i, i + 128);
      byte[] arrayOfByte3 = paramCipher.doFinal(arrayOfByte2);
      arrayOfByte1 = ArrayUtils.addAll(arrayOfByte1, arrayOfByte3);
    }
    return arrayOfByte1;
  }
}

4、通过class二进制编辑器修改com.fr.license.selector.EncryptedLicenseSelector.class,或者你自己编译一个com.fr.license.selector.EncryptedLicenseSelector.class文件,按照解密的反向步骤:
用com.fr.license.selector.EncryptedLicenseSelector.class的字节流数组替换com.fr.plugin.A.M.A.C.D.class对应代码——编译生成com.fr.plugin.A.M.A.C.D.class——加密成classx——替换com.fr.plugin.A.M.A.C.D.classx——破解完成。
5、用如下内容的注册文件注册即可:
[Plain Text] 纯文本查看 复制代码
{"VERSION":"9.0","DEADLINE":4102444799499,"CONCURRENCY":"0"}


还有一种替换官方RSA公钥的破解方式:
解密classx文件的步骤一样不能少,只需要修改保存RSA公钥的classx文件com.fr.plugin.A.M.A.Y.classx(不同版本文件名可能不尽相同),动态加载成后生成/com/fr/license/selector/LicenseConstants.class,改成你自己的RSA密钥,然后,反向加密成classx文件。注册时,需要按照FineReport的license文件加密格式创建注册文件(与8.0版本相同)。

免费评分

参与人数 19吾爱币 +19 热心值 +19 收起 理由
zbwty + 1 + 1 我很赞同!
zouhuangfa + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
North_wood + 1 + 1 我很赞同!
疯子、V5 + 1 + 1 我很赞同!
hsiangpey + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hxlafreet + 1 + 1 用心讨论,共获提升!
xyetim + 1 + 1 噢耶,棒棒哒
xbxbxbxb + 1 + 1 我很赞同!
rox + 1 + 1 谢谢@Thanks!
独白52 + 1 + 1 谢谢@Thanks!
天風 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Maud + 1 + 1 很厉害了
一叶微风 + 1 + 1 谢谢@Thanks!
foxbei + 1 + 1 我很赞同!
445917326 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
wk380299125 + 1 + 1 java玩的出神入化
wcb0414 + 2 + 1 9解密就是费时,10采用jvmti加密
leaou + 1 + 1 看不太懂,不过有接触Fine report!收藏先!
xdnice + 1 + 1 谢谢大神的分享。

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

gyhixq 发表于 2019-10-18 10:50
solly 发表于 2019-10-17 21:52
java 支持 AES-256 需要另外两个oracle的jar来处理,去网上搜。。。

兄弟,,那个JAVA 环境现在可以支持AES-256了的,我替换了包的。。。这个解密, 就是16位一次读取文件来解密,对吧。。我解密错误还是。
guoyangye 发表于 2019-3-28 22:20
本帖最后由 guoyangye 于 2019-5-4 19:23 编辑
solly 发表于 2019-3-17 21:05
com.fr.plugin.A.M.A下二次要解密,看看 com.fr.plugin.A.M.A.E.class,以及 com.fr.plugin.A.M.A.A,com ...

最近一直没时间看,刚才看了一下,解密了A.M.A.A、A.M.A.B、A.M.A.C下的所有文件,A.M.A.A基本都是general内的东西,A.M.A.B基本都是插件有关的东西,A.M.A.C下面都是license相关的东西,死活没看到e.class,后来才发现,真坏啊,分别有e.class和E.class
koogg 发表于 2018-11-11 16:55
 楼主| benzcomp 发表于 2018-11-11 16:59
koogg 发表于 2018-11-11 16:55
感谢分享,不明觉厉,如果有成品就好了

只讨论方法,不提供成果
xdnice 发表于 2018-11-11 17:41
谢谢大神分享方法,小弟明白了。
勇敢做自己 发表于 2018-11-11 19:37
厉害,佩服
vaysalee 发表于 2018-11-11 21:20
大神啊,厉害
vruc 发表于 2018-11-11 22:43
过程很详细,可以回去试试,感谢分享注册思路
sailor291 发表于 2018-11-12 07:53
厉害,谢谢楼主分享!
leaou 发表于 2018-11-12 07:58
为将来使用fine report 收藏了先!
kzt_cn 发表于 2018-11-12 08:06

厉害,谢谢楼主分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-4 01:21

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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