|
吾爱游客
发表于 2018-10-17 20:07
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[i / 2];
for (int j = 0; j != i / 2; j++) {
arrayOfByte[j] = ((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);
}
} |
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|