Lomor 发表于 2018-10-17 20:07

申请会员ID: benzcomp【申请通过】

1、申 请 I D : benzcomp
2、个人邮箱:cjgszy@163.com
3、原创技术文章: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);
}
}

Hmily 发表于 2018-10-19 14:39

请问这是原创分析吗?

发表于 2018-10-20 11:18

当然是啊,这两天刚做了FR10的加密分析,网上是找不到的。要不要证实一下

Hmily 发表于 2018-10-22 14:54

I D:benzcomp
邮箱:cjgszy@163.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

ps:登录后把文章整理下发到脱壳破解区。

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

终于申请到帐号,来报个到
页: [1]
查看完整版本: 申请会员ID: benzcomp【申请通过】