吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12409|回复: 124
收起左侧

[原创] FXXXXRXXXXX 11.0 注册分析

  [复制链接]
benzcomp 发表于 2022-5-2 19:18
本帖最后由 benzcomp 于 2023-4-23 18:58 编辑

本来不想再跟进FR的解密,前几天看到“FXXXXRXXXXX 10.0 另一破解思路”(https://www.52pojie.cn/thread-1117200-1-1.html)这篇文章,只是想以此做下尝试。
反向过程中发现,classx文件的加密又有所改变,以往发布的方法完全照做已无法轻松实现PJ,特此再做一下经验分享吧。
关于思路及关键文件分析请参考以前发布的文章https://www.52pojie.cn/thread-1012601-1-1.html,在此不再赘述。
总体原则:不对官方文件进行修改,减少不必要的人为麻烦,只采用外挂拦截或修改配置文件方式,动态PJ,尽量不影响在线升级。
以前的PJ方式就是,重写com.fr.license.selector.EncryptedLicenseSelector,修改解密函数decrypt(),返回注册明文,然后,使其在真正的类前加载实现的。
但是,好像是在3月份的某一个版本后,10.0和11.0的发布版本都对该方法的PJ进行了反制。
老版本:
[Java] 纯文本查看 复制代码
import com.fr.license.selector.AbstractLicenseSelector;
import com.fr.license.selector.EncryptedLicenseSelector;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.Hidden;

@Hidden
public abstract class EncryptedLicenseSelector extends AbstractLicenseSelector {
  byte[] getBytes() {
    byte[] arrayOfByte = readRawBytes();
    return decrypt(arrayOfByte);
  }
  
  protected void decryptFailed(Throwable paramThrowable) {
    FineLoggerFactory.getLogger().error("Read license failed." + paramThrowable.getMessage(), paramThrowable);
  }
  
  abstract byte[] readRawBytes();
  
  public byte[] decrypt(byte[] paramArrayOfbyte);
}

新版本:
[Java] 纯文本查看 复制代码
import com.fr.license.selector.AbstractLicenseSelector;
import com.fr.license.selector.EncryptedLicenseSelector;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.Hidden;

@Hidden
public abstract class EncryptedLicenseSelector extends AbstractLicenseSelector {
  byte[] getBytes() {
    byte[] arrayOfByte = readRawBytes();
    return decrypt(arrayOfByte);
  }
  
  protected void decryptFailed(Throwable paramThrowable) {
    FineLoggerFactory.getLogger().error("Read license failed." + paramThrowable.getMessage(), paramThrowable);
  }
  
  abstract byte[] readRawBytes();
  
  public native byte[] decrypt(byte[] paramArrayOfbyte);
}

唯一的区别是decrypt()添加了native 前缀,而且,其他方法的注册认证也不再调用getBytes(),而是改为直接调用decrypt(),所以,老方法就失效了,修改后的com.fr.license.selector.EncryptedLicenseSelector被提前加载后,系统启动会挂起其他模块对该native函数的调用过程。
那么,如何使原来的方法继续有效?“FXXXXRXXXXX 10.0 另一破解思路”一文提供了有效的思路:
仍然使用修改版的EncryptedLicenseSelector,然后,利用 Java Instrumentation 去拦截。
操作步骤:
1、构建com.fr.license.selector.EncryptedLicenseSelector
[Java] 纯文本查看 复制代码
import com.fr.license.selector.AbstractLicenseSelector;
import com.fr.license.selector.EncryptedLicenseSelector;
import com.fr.log.FineLoggerFactory;
import com.fr.stable.Hidden;

@Hidden
public abstract class EncryptedLicenseSelector extends AbstractLicenseSelector {
byte[] getBytes() {
byte[] arrayOfByte = readRawBytes();
return decrypt(arrayOfByte);
}

protected void decryptFailed(Throwable paramThrowable) {
FineLoggerFactory.getLogger().error("Read license failed." + paramThrowable.getMessage(), paramThrowable);
}

abstract byte[] readRawBytes();

public byte[] decrypt(byte[] paramArrayOfbyte);
}

构建生成com.fr.license.selector.EncryptedLicenseSelector.class文件,待用;
2、构建FineCrackAgent
借用2316361Teahome的代码,在此,一并表示感谢。
git clone 2316361 的分享 https://github.com/2316361/FineCrack
修改FineCrackAgent.java
[Java] 纯文本查看 复制代码
package crack;

import crack.transformer.MultiMethodTransformer;
import crack.transformer.SingleMethodTransformer;
import javassist.ClassPool;
import javassist.CtClass;

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;

public class FineCrackAgent {
    public static void agentmain(String args, Instrumentation inst) throws Exception {
        ClassPool pool = new ClassPool(true);
        CtClass cl = pool.get("java.lang.reflect.Modifier");
        cl.getDeclaredMethod("isNative").setBody("{ return true; }");
        inst.redefineClasses(new ClassDefinition[] {
                new ClassDefinition(Class.forName(cl.getName(), false, null), cl.toBytecode())
        });
        System.out.println(cl.getName() + " 替换完成!");
        Class<?>[] classes = inst.getAllLoadedClasses();
        for (Class<?> clazz : classes) {
            String name = clazz.getName();
            if (name.equals("com.fr.license.security.LicFileRegistry")) {
                inst.addTransformer(new SingleMethodTransformer(name, "check", 1, 2, new byte[]{4, -84}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
            if (name.equals("com.fr.license.entity.AbstractLicense")) {
                inst.addTransformer(new MultiMethodTransformer(name, "support", 1, 2, new byte[]{4, -84}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
            if (name.equals("com.fr.license.selector.EncryptedLicenseSelector")) {
                inst.addTransformer(new SingleMethodTransformer(name, "decrypt", 1, 2, new byte[]{43, -80}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
        }
    }
}

构建生成FineCrack-jar-with-dependencies.jar
3、加密com.fr.license.selector.EncryptedLicenseSelector.class
文章开始所说的classx文件加密方式的改变。
老版本是对class全文件进行RSA加密(具体加密方式参考9.0和10.0版本的分析),新版本加密改为:
a、只对文件每256字节的前24字节进行加密;
b、文件结尾不足256字节部分,超过24字节长度时,不加密;不足24字节时,加密。
按照该加密规则,完成两次加密后,打包成jar,放入fine-core-11.0.jar相同目录。
4、FineCrack-jar-with-dependencies.jar随便放到哪个目录
修改X:\FineReport_11.0\bin\designer.vmoptions  增加 -javaagent:X:\FineReport_11.0\FineCrack-jar-with-dependencies.jar
tomcat、resin等部署时,在JAVA_OPTS增加探针破解库路径,如:
JAVA_OPTS="$JAVA_OPTS -javaagent:/usr/local/tomcat8/webapps/webroot/WEB-INF/lib/FineCrack-jar-with-dependencies.jar"
5、注册文件fanruan.lic
格式没变,只要修改版本号即可
[XML] 纯文本查看 复制代码
{"VERSION":"11.0","DEADLINE":4102444799499,"CONCURRENCY":"0"}

====================================================================================
结合各位大神的思路,更新一下吧,不再需要自己构建com.fr.license.selector.EncryptedLicenseSelector.class
1、修改SingleMethodTransformer
[Java] 纯文本查看 复制代码
package crack.transformer;

import crack.AddConstFunction;
import javassist.bytecode.*;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;

public class SingleMethodTransformer extends CrackTransformer {

    public SingleMethodTransformer(String targetClassName, String targetMethodName, int stack, int locals, byte[] newCode, AddConstFunction function) {
        super(targetClassName, targetMethodName, stack, locals, newCode, function);
    }

    @Override
    protected byte[] modifyMethod(byte[] classfileBuffer, String methodName, int stack, int locals, byte[] newCode) throws Exception {
        DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(classfileBuffer));
        ClassFile classFile = new ClassFile(inputStream);
        inputStream.close();
        ConstPool constPool = classFile.getConstPool();

        if (this.function != null) {
            this.function.accept(constPool);
        }
        MethodInfo methodInfo = classFile.getMethod(methodName);
        methodInfo.setAccessFlags(methodInfo.getAccessFlags() & ~AccessFlag.NATIVE);
        CodeAttribute codeAttribute = new CodeAttribute(constPool, stack, locals, newCode, new ExceptionTable(constPool));
        methodInfo.setCodeAttribute(codeAttribute);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        DataOutputStream outputStream = new DataOutputStream(byteArrayOutputStream);
        classFile.write(outputStream);
        outputStream.close();
        byte[] result = byteArrayOutputStream.toByteArray();
        byteArrayOutputStream.close();
        return result;
    }
}

2、修改FineCrackAgent
[Java] 纯文本查看 复制代码
package crack;

import crack.transformer.MultiMethodTransformer;
import crack.transformer.SingleMethodTransformer;
import javassist.ClassPool;
import javassist.CtClass;

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;

public class FineCrackAgent {
    public static void agentmain(String args, Instrumentation inst) throws Exception {
        ClassPool pool = new ClassPool(true);
        CtClass cl = pool.get("java.lang.reflect.Modifier");
        cl.getDeclaredMethod("isNative").setBody("{ return true; }");
        inst.redefineClasses(new ClassDefinition(Class.forName(cl.getName(), false, null), cl.toBytecode()));
        System.out.println(cl.getName() + " 替换完成!");
        CtClass cl2 = pool.get("java.lang.reflect.Method");
        cl2.getDeclaredMethod("getModifiers").setBody("{ if (getName().equalsIgnoreCase(\"decrypt\")) { return modifiers | 0x100;} return modifiers;}");
        inst.redefineClasses(new ClassDefinition(Class.forName(cl2.getName(), false, null), cl2.toBytecode()));
        System.out.println(cl2.getName() + " 替换完成!");
        Class<?>[] classes = inst.getAllLoadedClasses();
        for (Class<?> clazz : classes) {
            String name = clazz.getName();
            if (name.equals("com.fr.license.selector.LicenseContext")) {
                inst.addTransformer(new SingleMethodTransformer(name, "stopLicense", 1, 1, new byte[]{-79}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
            if (name.equals("com.fr.license.security.LicFileRegistry")) {
                inst.addTransformer(new SingleMethodTransformer(name, "check", 1, 2, new byte[]{4, -84}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
            if (name.equals("com.fr.license.entity.AbstractLicense")) {
                inst.addTransformer(new MultiMethodTransformer(name, "support", 1, 2, new byte[]{4, -84}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
            if (name.equals("com.fr.license.selector.EncryptedLicenseSelector")) {
                inst.addTransformer(new SingleMethodTransformer(name, "decrypt", 1, 2, new byte[]{43, -80}, null), true);
                inst.retransformClasses(clazz);
                System.out.println(name + " 替换完成!");
            }
        }
    }
}

免费评分

参与人数 4威望 +1 吾爱币 +23 热心值 +4 收起 理由
gqdsc + 1 + 1 谢谢@Thanks!
yanghui02000 + 1 + 1 谢谢@Thanks!
AppMan + 1 + 1 用心讨论,共获提升!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

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

kaizceo8 发表于 2023-8-17 11:18
crosy 发表于 2023-7-11 18:08
服务器版似乎增加了对-javaagent注入的校验,只要加了就无法正常加载应用到webroot/decision。大大有解决的 ...

https://bbs.fanruan.com/wenda/question/3587.html 看看是不是
2316361 发表于 2023-4-17 10:26
不用加解密com.fr.license.selector.EncryptedLicenseSelector.class,直接用javaassist去掉native标识就可以了
image.png
solly 发表于 2023-2-8 15:51
本帖最后由 solly 于 2023-2-9 13:20 编辑

新版本对 java.lang.reflect.Method 也要处理一下:

[Java] 纯文本查看 复制代码
        CtClass cl2 = pool.get("java.lang.reflect.Method");
        cl2.getDeclaredMethod("getModifiers").setBody("{ if (getName().equalsIgnoreCase(\"decrypt\")) { return modifiers | 0x100;} return modifiers;}");
        inst.redefineClasses(new ClassDefinition[] {
                new ClassDefinition(Class.forName(cl2.getName(), false, null), cl2.toBytecode())
        });
        System.out.println(cl2.getName() + " 替换完成!");
AppMan 发表于 2022-8-19 15:54
solly 发表于 2022-8-19 09:32
那找到isnative 改掉。
另外,我是模拟加密锁,可能解密调用过程不一样吧,没有 isNative 这个问题。

fine-report-engine-11.0 这个好多类里混有大量的isNative
[Java] 纯文本查看 复制代码
 License license = FRCoreContext.getLicense();
    if (license == null)
      throw new RuntimeException("No TrialLicense or AuthorizedLicense."); 
    Method[] arrayOfMethod = license.getClass().getMethods();
    String[] arrayOfString = { "signature", "deadline", "companyName", "projectName", "templateEncryptionKey" };
    for (Method method : arrayOfMethod) {
      String str = method.getName();
      if (!str.equals("getJSONObject") && (str.startsWith("is") || str.startsWith("get") || str.startsWith("max") || ArrayUtils.contains((Object[])arrayOfString, str))) {
        int i = method.getModifiers();
        if (!Modifier.isNative(i))
          throw new RuntimeException("Illegal license object " + license.getClass().getName() + "."); 
      } 
    } 
 楼主| benzcomp 发表于 2022-5-2 19:24
关于修改rt.jar包下Modifier类的isNative()方法返回true的拦截操作!
因为我是与其他系统部署在同一个应用下,想不影响fr以外的系统对Modifier的调用,需要加个限制只是com.fr包路径下调用此方法才返回true。
想请教一下,如何做到
candyl6 发表于 2022-5-5 21:47
前排支持,大佬威武
chengsheng16 发表于 2022-5-6 03:14

谢谢大神,学习了。
ming1332236 发表于 2022-5-6 09:18
学习下,谢谢分享
cfsxy 发表于 2022-5-11 05:14
感谢分享
solly 发表于 2022-5-11 12:12
本帖最后由 solly 于 2022-5-12 13:02 编辑

从4月18日开始的版本,加密长度才开始变了,每256字节块加密前48字节,最后一块少于65字节不加密。
solly 发表于 2022-5-12 13:13
本帖最后由 solly 于 2022-5-12 13:19 编辑
benzcomp 发表于 2022-5-2 19:24
关于修改rt.jar包下Modifier类的isNative()方法返回true的拦截操作!
因为我是与其他系统部署在同一个应用 ...

给你一个参考:
[Java] 纯文本查看 复制代码
import java.io.IOException;
import java.net.ServerSocket;

public class Test {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
                Object s = "test";
                System.out.println(((Object)s).toString());
                ///
                showTrace();
        }
        
        public static void showTrace() {
                try {
                        throw new IOException("my exception!");
                } catch (Exception ex) {
                        java.lang.StackTraceElement[] traces = ex.getStackTrace();

                        for (StackTraceElement aTrace: traces) {
                                System.out.println(aTrace.toString()); /// 在这里检查包名、类名、方法名
                        }
                }
        }
}
zhh4250 发表于 2022-6-4 00:28

不太明白256位的描述

本帖最后由 zhh4250 于 2022-6-5 12:04 编辑

日志文件

日志文件


问题1:取256位字节时,是从第5位开始取后面所有字节256,还是直接从0开始取?(这个可以不用回答,自己试试也能出来)
问题2:关于异或运算的问题,请问首先是先进行文件最前八位异或运算,拿到结果后,再进行运算吗?

itanium 发表于 2022-7-7 17:10
同上,菜鸟很多还是不懂,要向大神们学习,10的可以搞定,希望我也能把11拿下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-12-22 19:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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