请问一下论坛里面研究FR的大神,有没有发现这款软件有什么暗装呢?发出来一起交流一下呗。
前些天看了论坛一位大神的帖子https://www.52pojie.cn/thread-1012601-1-1.html,试着弄了一下,真的被FR10的加密给弄崩溃了
于是想到了一个新思路,是否可以在它加载完关键类之后,从jvm里面取出字节码修改后再替换回去呢?
分享一下我的思路吧
启动FR,使用HSDB工具拿到三个类:
1、com.fr.license.selector.EncryptedLicenseSelector
2、com.fr.license.security.LicFileRegistry
3、com.fr.license.entity.FineLicense
企业微信截图_20200227124109.png
需要修改以下几个关键的地方:
1、com.fr.license.selector.EncryptedLicenseSelector的decrypt方法:
[Java] 纯文本查看 复制代码
public abstract class EncryptedLicenseSelector extends AbstractLicenseSelector { byte[] getBytes() { byte[] var1 = this.readRawBytes(); return this.decrypt(var1); } private byte[] decrypt(byte[] var1) { // 修改为 return var1; try { System.getProperties().setProperty("com.ibm.crypto.provider.DoRSATypeChecking", "false"); return (new CommonDecryptChain()).decrypt(var1); } catch (Throwable var3) { this.decryptFailed(var3); return new byte[0]; } } protected void decryptFailed(Throwable var1) { FineLoggerFactory.getLogger().error("Read license failed." + var1.getMessage(), var1); } abstract byte[] readRawBytes();}
其实修改这一个地方就够了,另外两处修改可用于方便懒人。
2、com.fr.license.security.LicFileRegistry的check方法:
[Java] 纯文本查看 复制代码
public class LicFileRegistry { private static Map<Integer, LicFileChecker> _2c75dfba_fdee_47fa_8cf4_74897f6d7e9a = new ConcurrentHashMap(5); private LicFileRegistry() { } static { register(new LicFileCheckerImpl()); } public static void register(LicFileChecker var0) { _2c75dfba_fdee_47fa_8cf4_74897f6d7e9a.put(var0.getTypeCode(), var0); } public static boolean check(InputStream var0, Integer var1) { // 修改为 return true; LicFileChecker var2 = _2c75dfba_fdee_47fa_8cf4_74897f6d7e9a.get(var1); return var2 != null && var2.check(var0); }}
修改这里主要是破解lic上传注册。
3、com.fr.license.entity.FineLicense的两个support方法:
[Java] 纯文本查看 复制代码
public class FineLicense extends AbstractLicense { public FineLicense(JSONObject var1) { super(var1); } public String getString(String var1, String var2) { return super.getInnerString(var1, var2); } public boolean support(PluginMarker var1) { // 修改为 return true; if (var1 == null) { return false; } else { try { JSONObject var2 = this.getJSONObject(); if (var2.has(LicenseItem.Plugin.getKey())) { JSONArray var3 = var2.getJSONArray(LicenseItem.Plugin.getKey()); for(int var5 = 0; var5 < var3.length(); ++var5) { String[] var4 = var3.getString(var5).split(","); if (var4.length >= 2 && var1.getPluginID().equals(var4[0])) { return true; } } } } catch (Exception var6) { FineLoggerFactory.getLogger().error(var6.getMessage(), var6); } return false; } } public boolean support(FunctionPoint var1) { // 修改为 return true; String var2 = this.getString(LicenseItem.Function.getKey(), ""); if (StringUtils.isNotBlank(var2)) { BigInteger var3 = new BigInteger(String.valueOf(var2)); return var3.and(BigInteger.ONE.shiftLeft(var1.getMarker())).compareTo(BigInteger.ZERO) == 1; } else { return true; } }
主要用于破解收费插件。
修改之后的代码可自行编译为class字节码,使用javaagent替换,不过不推荐。因为每次小更新部分类的成员变量名都会随机变化,使得破解失效,
因此建议拿到加载后的字节码自行进行修改并使用javaagent替换,以第一个关键类为例,代码如下:
[Java] 纯文本查看 复制代码
public class FxxxAgent { public static void agentmain(String args, Instrumentation inst) throws Exception { Class<?>[] classes = inst.getAllLoadedClasses(); for (Class<?> clazz : classes) { if (clazz.getName().equals("com.fr.license.selector.EncryptedLicenseSelector")) { inst.addTransformer((loader, className, classBeingRedefined, protectionDomain, classfileBuffer) -> { if (className.equals("com/fr/license/selector/EncryptedLicenseSelector")) { try { DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(classfileBuffer)); ClassFile classFile = new ClassFile(inputStream); inputStream.close(); ConstPool constPool = classFile.getConstPool(); MethodInfo methodInfo = classFile.getMethod("decrypt"); CodeAttribute codeAttribute = new CodeAttribute(constPool, 1, 2, new byte[]{43, -80}, 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; } catch (Exception e) { e.printStackTrace(); return classfileBuffer; } } else { return classfileBuffer; } }); inst.retransformClasses(clazz); } } }}
在manifest.sf文件中加上一行:Agent-Class: 刚刚编辑的类名,打包成jar文件备用。
为了使这些代码可以运行起来,需要找个地方写上一些自己的代码,我找的是fine-core-10.0.jar下的com.fr.runtime.FineRuntime这个类,
在initEncryptedBridge方法的最下面加上了自己的代码:
[Java] 纯文本查看 复制代码
private static void initEncryptedBridge() throws Exception { ClassLoader var0 = (ClassLoader) Class.forName(START_LOADER_FACTORY_NAME).newInstance(); Class.forName("com.fr.plugin.bridge.FinePluginBridge", true, var0); String name = ManagementFactory.getRuntimeMXBean().getName(); String pid = name.substring(0, name.indexOf('@')); VirtualMachine virtualMachine = VirtualMachine.attach(pid); virtualMachine.loadAgent("D:/agent.jar", null); // 这里的路径即为刚刚打的jar包路径 virtualMachine.detach(); }
用winrar打开fr-core-10.0.jar,将编译好的FineRuntime.class替换,或者单独打成jar包,保证文件名能排在fr-core-10.0.jar的前面,放在WEB-INF\lib目录里
用如下内容的注册文件注册即可
[XML] 纯文本查看 复制代码
{"VERSION":"10.0","DEADLINE":"253402271999000","CONCURRENCY":"0"}
最后把自己的代码分享给大家吧。
https://github.com/2316361/FineCrack