XMind原来是这样被破解的...
bigegpt 2025-05-28 15:33 7 浏览
有没有想过,XMind是如何被破解的?那么今天我们就来看看javaassit这项技术,其实在你接触的很多其他工具中这个工具早就被广泛使用了
javaassit
我们知道,java是一门面向对象的编程语言,更是一门面向切面的编程语言,正是这个特性,让Java更加地灵活。
可能你写过基于Spring AOP的代码,其原理都是基于JDK动态代理或者CGLIB来实现,其局限性在于我们只能以方法作为连接点,来实现基于方法执行过程的代理。
你可还知道更厉害的代理工具:AspectJ、javaassit,这些都是基于字节码,属于更底层,但是功能更强大的代理。
知识点
- ASM
通过指令修改class字节码,主要基于ClassReader结合JVM指令集直接操作字节码,Cglib即是通过该技术实现。
- JavaAssit
基于org.javassist:javassist类库提供的CtPool工具类对字节码进行修改
- Instrumentation
JVM提供的一个可以修改已加载类的类库,通过编写java代码即可完成对字节码的修改
- JavaAgent
JVM加载类之前与JVM运行时,基于JavaAssit、Instrumentation实现字节码修改并加载到JVM
应用场景
- IDE的调试功能,例如 Eclipse、IntelliJ IDEA
- 热部署功能,例如 JRebel、XRebel、spring-loaded
- 线上诊断工具,例如 Btrace、Greys,还有阿里的 Arthas
- 性能分析工具,例如 Visual VM、JConsole、TProfiler等
- 全链路性能检测工具,例如 Skywalking、Pinpoint等
示例
下面我们基于javaagent以及运行时Attach的模式看下javaassit如何实现目标类的代理的:
基于javaagent
- 编写代理类
方法签名固定,方法名为premain,参数分别对应args(不是数组)以及Instrumentation
public class JavaAgent {
private static final String TARGET_CLASS_NAME = "com.sucl.blog.javaassit.Target";
public static void premain(String args, Instrumentation instrumentation){
AgentHelper.create(TARGET_CLASS_NAME).proxy(args, instrumentation);
}
}
- 打包代理类
这里我们借助maven插件maven-shade-plugin,主要是为了打包时修改/META-INF/MANIFEST.MF文件,需要加上Premain-Class这项
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Premain-Class>com.sucl.blog.agent.JavaAgent</Premain-Class>
<Agent-Class>com.sucl.blog.agent.AttachAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
- 编写测试类
目的很简单,每隔3秒打印当前时间
public class JavaAgentMain {
public static void main(String[] args) throws InterruptedException {
Target target = new Target();
while (true) {
target.print(new Date());
TimeUnit.SECONDS.sleep(3);
}
}
}
@Slf4j
class Target {
public void print(Object obj) {
log.info("打印内容:{}", obj);
}
}
- 配置代理
如何让我们编写的代理生效,这里提供两种方法:
- 当你使用IDEA启动时,可以在Config Configurations中通过配置VM OPTION,添加如下内容:
-javaagent:/your_jar_path/agent.jar=param=value
- 当你使用java命令启动时:
java -javaagent:/path/agent.jar=param=value -jar xxx.jar
- 测试
执行测试类main方法,你可以看到,在打印时间前后,分别会打印“开始执行方法:print”,“结束执行方法:print”,这也是我们代理类实现的功能。
>>> 开始执行方法:print
14:46:09.457 [main] INFO com.sucl.blog.javaassit.Target - 打印内容:Fri Mar 10 14:46:09 CST 2023
>>> 结束执行方法:print
基于Attach
- 编写代理类
方法签名固定,方法名为attachmain,参数分别对应args(不是数组)以及Instrumentation; 和上面的相比唯一的不同是方法名称。
public class AttachAgent {
private static final String TARGET_CLASS_NAME = "com.sucl.blog.javaassit.Target";
public static void agentmain(String args, Instrumentation instrumentation){
System.out.println(String.format(">>> agentmain starting, args: %s",args));
AgentHelper.create(TARGET_CLASS_NAME).proxy(args, instrumentation);
System.out.println(String.format(">>> agentmain finished"));
}
}
- 打包代理类
同样借助插件maven-shade-plugin,主要是为了打包时修改/META-INF/MANIFEST.MF文件,需要加上Agent-Class这项
<!-- 省略 ...-->
<Agent-Class>com.sucl.blog.agent.AttachAgent</Agent-Class>
<!-- 省略 ...-->
注意,这里我们使用了ClassPool、CtClass、CtMethod相关的类,记得在pom.xml中引入对应的依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
- 编写测试类
测试类完全一样,由于启动代理织入的方式不一样,因此分为两个类
public class AttachAgentMain {
public static void main(String[] args) throws InterruptedException {
Target target = new Target();
while (true) {
target.print(new Date());
TimeUnit.SECONDS.sleep(3);
}
}
}
- 执行代理
如何将编写的代码(AttachAgent)织入到目标类完成对目标类(Target)方法的代理?
这里我们需要用到jdk中的tool.jar,你可以在测试模块中添加下面的依赖:
<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
如何在运行时进行代理织入:
public class AttachAgentTests {
private static String JAR_PATH = AttachAgentTests.class.getClassLoader().getResource("").getPath().replace("test-classes/","")+"agent.jar";
@Test
public void attachAgent() throws Exception {
String pid = findPid(KEY); // 通过jps命令找到AttachAgentMain执行的pid
VirtualMachine virtualMachine = VirtualMachine.attach(pid);
virtualMachine.loadAgent(JAR_PATH.substring(1));
virtualMachine.detach();
}
}
- 测试
- a. 先执行测试代码(AttachAgentMain.java),此时每间隔3秒会打印当前时间。
- b. 执行代理织入方法(AttachAgentTests#attachAgent)
- c. 观察测试代码输出结果,你会会发现此时每次打印时间前后都会有“开始执行方法:print”,“结束执行方法:print”
AgentHelper
public class AgentHelper {
private String targetClassName;
private AgentHelper(String targetClassName) {
this.targetClassName = targetClassName;
}
public static AgentHelper create(String targetClassName){
AgentHelper agentHelper = new AgentHelper(targetClassName);
return agentHelper;
}
public void proxy(String args, Instrumentation instrumentation){
Class targetClass = obtainTargetClass(instrumentation);
try {
instrumentation.addTransformer(new SimpleTransformer(targetClassName), true);
instrumentation.retransformClasses(targetClass); //
} catch (Exception e) {
System.out.println(String.format(">>> agentmain failure, error: %s: %s", e.getClass().getName(),e.getLocalizedMessage()));
e.printStackTrace();
}
}
private Class obtainTargetClass(Instrumentation instrumentation) {
Class targetClass = null;
for (Class loadedClass : instrumentation.getAllLoadedClasses()) {
if(targetClassName.equals(loadedClass.getName())){
targetClass = loadedClass;
}
}
if(targetClass == null){
try {
// 无法加载
targetClass = Class.forName(targetClassName);
} catch (ClassNotFoundException e) {
System.out.println(String.format(">>> Class [%s] not found", targetClassName));
}
}
return targetClass;
}
public static class SimpleTransformer implements ClassFileTransformer {
private String targetClassName;
public SimpleTransformer(String targetClassName) {
this.targetClassName = targetClassName;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if(!className.equals(targetClassName.replaceAll("\\.","/"))){
return null;
}
ClassPool classPool = ClassPool.getDefault();
System.out.println(String.format("+++++ 代理类名:%s", className));
try {
CtClass ctClass = classPool.get(className.replace("/","."));
CtMethod[] ctMethods = ctClass.getDeclaredMethods();
for (CtMethod ctMethod : ctMethods) { // 所有类方法
ctMethod.insertBefore(String.format("{System.out.println(\">>> 开始执行方法:%s\");}",ctMethod.getName()));
ctMethod.insertAfter(String.format("{System.out.println(\">>> 结束执行方法:%s\");}",ctMethod.getName()));
}
return ctClass.toBytecode();
} catch (NotFoundException | CannotCompileException | IOException e) {
System.out.println(String.format("+++++ 代理出错:%s",e.getMessage()));
e.printStackTrace();
}
return classfileBuffer;
}
}
}
通过上面的例子可以看到,两种方式的比对如下:
对比JavaAgentAttachAgent/META-INF/MANIFEST.MFPremain-ClassAgent-Class代理类方法名称premainattachmain代理入口VM配置:-javaagentJVM attach进程ID代理时机JVM加载字节码时程序运行时作用Java桌面程序Web应用
原理
代理可以发送在编译时,类加载时或者是运行时。
这里你要清楚,java程序的入口是main方法,不管是普通程序(比如桌面应用、可执行jar)或是Web应用(在Web容器中运行的基于Servlet的应用)
以javaagent为例,是在执行main方法前对已经加载到JVM的类进行修改,从而实现对目标类的代理,这里的修改是在字节码层面的,当然我们可以基于ASM工具库来实现,但是门槛太高。
基于Instrumentation可以与编写java代码一样,实现修改字节码来
ClassPool:保存CtClass的池子,通过classPool.get(类全路径名)来获取CtClass CtClass:编译时类信息,它是一个class文件在代码中的抽象表现形式 CtMethod:对应类中的方法 CtField:对应类中的属性、变量
XMind
还记得XMind8的破解之法吗?
是不是需要在XMind.ini文件中插入这样一段:-javaagent:.../XMindCrack.jar 要是你打开这个jar,你会看到这样的内容:
首先你需要知道其原理,是通过/plugins/net.xmind.verify.jar中提供的方法LicenseVerifier#doCheckLicenseKeyBlacklisted来进行身份校验
我们是不是只用修改License的校验方法doCheckLicenseKeyBlacklisted,忽略其校验过程并直接返回true就完事了?当然截图中就是这样做的,如果你想看懂那几行代码,可能你先要去学习ASM相关的知识。
InsnList insnList = methodNode.instructions;
insnList.clear();
insnList.add((AbstractInsnNode)new InsnNode(4));
insnList.add((AbstractInsnNode)new InsnNode(172));
methodNode.exceptions.clear();
methodNode.visitEnd();
以上代码其实就是讲方法体清除,并写入“return true”
结束语
通过示例了解javaassit如何实现代对目标类的代理。是不是觉得java应用程序都能被修改,那不是太不安全了?所以,你觉得呢...
原文链接;
https://mp.weixin.qq.com/s/4sLA3WVJ3sRup0yU4q0Tdw
相关推荐
- 悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)
-
新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...
- 高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源
-
凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...
- 微服务架构实战:商家管理后台与sso设计,SSO客户端设计
-
SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...
- 还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑
-
在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...
- Seata源码—6.Seata AT模式的数据源代理二
-
大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...
- 30分钟了解K8S(30分钟了解微积分)
-
微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...
- SpringBoot条件化配置(@Conditional)全面解析与实战指南
-
一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...
- 一招解决所有依赖冲突(克服依赖)
-
背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...
- 你读过Mybatis的源码?说说它用到了几种设计模式
-
学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...
- golang对接阿里云私有Bucket上传图片、授权访问图片
-
1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...
- spring中的资源的加载(spring加载原理)
-
最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...
- Android资源使用(android资源文件)
-
Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...
- 如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)
-
深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...
- @Autowired与@Resource原理知识点详解
-
springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...
- java的redis连接工具篇(java redis client)
-
在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)