benzcomp 发表于 2018-10-30 10:09

FineReport 8.0 注册分析

FineReport 8.0的注册使用RSA加密,关于RSA算法很容易搜到,破解思路就是,替换原版的公钥,用自己的RSA私钥加密格式化的注册信息,即可成功注册。
首先,加解密算法在FRCoreContext.class里,通过反编class文件可以得到FRCoreContext的源码,可以得到RSA的N、e,用他们就可以吧正版的license文件解密。
通过对FRCoreContext的分析可以知道,license的生成算法是:
1、license授权信息的HEX每32字节分成若干组;
2、每组前导插入7F;
3、各组做RSA加密运算;
4、各组之间插入RSA的N值,最终得到的HEX串就是注册文件的二进制内容。

license制作步骤:
1、按照格式编辑lic文件明文,如下格式:
{"BI_MAKER":"0","CONCURRENCY":"0","FS_USER":"0","PRICE":"","APPNAME":"","MACADDRESS":"","BI_USER":"0","FUNCTION":"265735294061","ISAFTER701":true,"MOBILE_FS_USER":"0","REPORTLETSCOUNT":"","APPCONTENT":"","DEADLINE":4102444799499,"VERSION":"8.0","reportletscount":"","PRINCIPAL":"","COMPANYNAME":"","UUID":"","MUTICONNECTION":"0","BI_MOBILE":"0","PROJECTNAME":"","KEY":""}
对于这段明文来说    "DEADLINE":4102444799499    (2100-1-1到1970-1-1的毫秒数)
                  "FUNCTION":"265735294061"(定制版对应的编号,可以在fr-core-8.0.jar中的FUNC.class找到定义)
保存成UTF-8编码格式的文本文件 lic.txt
2、用WinHEX打开lic.txt,复制HEX码
3、文件明码的HEX码每64个字符为一组分割,最前面加"7F"或"7E"、"7D"等,目的是让加密后的密文保证为256位字符,
4、RSA分组加密,加密后的密文要保证双字节数256个(手工做的话可以使用,大数计算器等程序)
5、每一组中间插入公钥(N),结尾不加!最终是一串6400位的字符。
6、用WinHEX粘贴ascii-hex,保存文件就是 FineReport.lic 了
7、用winrar打开fr-core-8.0.jar,拖出 com\fr\base\FRCoreContext.class
8、用UltraEdit找原版的N,用自己的 N 替换
9、再把FRCoreContext.class替换回去
10、替换fr-core-8.0.jar文件
11、FineReport.lic 放到 resources 目录下

如上方式替换文件的方法,有一个缺点,每次升级fr-core-8.0.jar都会被替换为新版本,还需要再次操作第7-11步,后来想到一个不影响升级的办法:
把fr-core-8.0.jar复制一份,并且重命名为cr_FRCoreContext.jar,文件名可以随意命名,只要保证文件名升序排序,在fr-core-8.0.jar之前就行。
用winrar打开cr_FRCoreContext.jar,把cr_FRCoreContext.jar\com\fr\base\FRCoreContext.class以外的所有文件都删掉,然后用替换N的FRCoreContext.class覆盖同名文件。
最后,把cr_FRCoreContext.jar放到\WebReport\WEB-INF\lib目录里,只要能排在fr-core-8.0.jar前面就行了。
===========================================================================================
FRCoreContext源码:
public class FRCoreContext
{
public static final ThreadLocal TMAP = new ThreadLocal();
private static byte[] lic_bytes = null;
private static byte[] lock_bytes = null;
private static final long ONE_YEAR_MILLISECOND = 31536000000L;
private static final int MAX_DIGIT = 255;
private static boolean onlinePassed = true;
private static String;
private static final String uuid = UUID.randomUUID().toString();
private static final long ONLINE_CHECK_TIME_DELAY = 0L;
private static final long ONLINE_CHECK_TIME_PERIOD = 10800000L;
private static Timer ONLINE_CHECK_TIMER;
private static int failCount;
private static boolean shouldFireLicChange = false;
private static final int MAX_FAIL_COUNT = 8;
private static final BigInteger N = new BigInteger("61103299352066102812915201580370346997919089893149305765565972348630053713717591736527153881172892494135635969333391530396986735629281282430026953431657619628355730192943385620088393498664105803897708601718035436482482749378713844253725606147581454234307387984660050507963063894825237808748868429675256901161");//RSA的N
private static final BigInteger D = new BigInteger("65537");

……
    private static String byte2hex(byte[] paramArrayOfByte)    //byte转hex码,paramArrayOfByte输入license文件
{
    StringBuffer localStringBuffer = new StringBuffer();
    String str = "";
    for (int i = 0; i < paramArrayOfByte.length; i++)
    {
      str = Integer.toHexString(paramArrayOfByte & 0xFF);
      if (str.length() == 1) {
      localStringBuffer.append('0');
      }
      localStringBuffer.append(str);
    }
    return localStringBuffer.toString().toUpperCase();
}

private static void decode(String paramString, OutputStream paramOutputStream)//解密函数
{
    String[] arrayOfString = paramString.split(byte2hex(N.toByteArray()));      //删除license中插入的 N ,还原出有效注册信息的加密内容
    try
    {
      for (int i = 0; i < arrayOfString.length; i++) {
      paramOutputStream.write(tinyDecode(hex2byte(arrayOfString)));      //删掉密文数组的第一个元素,然后tinyDecode执行RSA解密,得到license明文
      }                                                                              
    }
    catch (IOException localIOException)
    {
      FRContext.getLogger().error(localIOException.getMessage(), localIOException);
    }
}
……
private static byte[] hex2byte(String paramString)    //hex转byte
{
    if (paramString == null) {
      return null;
    }
    int i = paramString.length();
    if (i % 2 == 1) {                                                //每一段加密后的密文必须保证双字节数256个,而要保证双字节靠每一段明文前面插入的7F、7E等来控制,反正最后都是要截掉的,插入的字符就是控制密文长度的作用
      return null;
    }
    byte[] arrayOfByte = new byte;
    for (int j = 0; j != i / 2; j++) {
      arrayOfByte = ((byte)Integer.parseInt(paramString.substring(j * 2, j * 2 + 2), 16));
    }
    return arrayOfByte;
}


private static byte[] tinyDecode(byte[] paramArrayOfByte)    //RSA解密算法
{
    paramArrayOfByte = new BigInteger(paramArrayOfByte).modPow(D, N).toByteArray();
    return ArrayUtils.subarray(paramArrayOfByte, 1, paramArrayOfByte.length);
}
}

guoki 发表于 2018-11-16 09:47

benzcomp 发表于 2018-11-9 19:20
10已搞定
谁有V2 Conference 7.0的版本,可以找我来换

楼主 可以说下10的思路吗? 我现在使用 instrument + javassist 去把 类加载器 加载的隐藏类 dump出来 ..
这些加载文件好像还加密的,难道真的要去反编译 fineassist.dll文件吗?

benzcomp 发表于 2018-11-11 16:58

xdnice 发表于 2018-11-10 22:23
请大神提供下思路,我现在找不到dealFragment这个方法,不知道怎么入手了。

看这个吧
FineReport 9.0 注册分析
https://www.52pojie.cn/thread-819149-1-1.html
(出处: 吾爱破解论坛)

redapple2015 发表于 2018-10-30 10:34

这个软件好像开发挺快的,现在到10了吗?

cnitren 发表于 2018-10-30 10:40

这个牛,不过能力有限,还是看不太懂!

zxpr 发表于 2018-10-30 10:57

学习以后,以后说不定会用到呢

xie83544109 发表于 2018-10-30 10:58

{:1_927:}
多谢楼主分享哟
收徒不,有偿的

明晓林 发表于 2018-10-30 13:58

老铁很稳啊,不过8.0用户貌似不多,大部分还在7

andyzgs 发表于 2018-10-30 14:06

mark一下,以后肯定用的着。。

coolfire1983 发表于 2018-10-30 16:14

学习了 ,对我来说 有点复杂啊!

i-ii 发表于 2018-10-30 21:55

好东西啊,谢谢楼主分享

0531chuxin 发表于 2018-10-30 21:59

感觉挺厉害的啊
页: [1] 2 3 4
查看完整版本: FineReport 8.0 注册分析