如果我们希望在CrackLicenseTest运行时不重启该Java程序的情况下运行我们的破解程序就需要以Attach模式运行了。Attach模式需要知道我们运行的Java程序进程ID,通过Java虚拟机的进程注入方式实现可以将我们的Agent程序动态的注入到一个已在运行中的Java程序中。
    我们可以使用JDK自带的jps命令获取本机运行的所有的Java进程,如:

    1. [robert@192:~]$ jps -l
    2. 14608 org.jetbrains.jps.cmdline.Launcher
    3. 14931 org.jd.gui.OsxApp
    4. 1075
    5. 6809 org.jetbrains.idea.maven.server.RemoteMavenServer36
    6. 15820 com.anbai.sec.agent.CrackLicenseTest
    7. 15823 sun.tools.jps.Jps

    通过进程的名字com.anbai.sec.agent.CrackLicenseTest就可以找到我们需要注入的进程ID为15823。如果我们想要直接借助Java程序来获取所有的JVM进程也是可以的,使用com.sun.tools.attach.VirtualMachinelist方法即可获取本机所有运行的Java进程,如:

    1. List<VirtualMachineDescriptor> list = VirtualMachine.list();
    2. for (VirtualMachineDescriptor desc : list) {
    3. System.out.println("进程ID:" + desc.id() + ",进程名称:" + desc.displayName());
    4. }

    有了进程ID我们就可以使用Attach API注入Agent了,Attach Java进程注入示例代码如下:

    1. // Java进程ID
    2. String pid = args[0];
    3. // 设置Agent文件的绝对路径
    4. String agentPath = "/xxx/agent.jar";
    5. // 注入到JVM虚拟机进程
    6. VirtualMachine vm = VirtualMachine.attach(pid);
    7. // 注入Agent到目标JVM
    8. vm.loadAgent(agentPath);
    9. vm.detach();

    使用Attach模式启动Agent程序时需要使用到JDK目录下的lib/tools.jar,如果没有配置CLASS_PATH环境变量的话需要在运行Agent程序时添加-classpath $JAVA_HOME/lib/tools.jar参数,否则我们无法使用Attach API,如下:

    1. cd ~/IdeaProjects/javaweb-sec/javaweb-sec-source/javasec-agent
    2. java -classpath $JAVA_HOME/lib/tools.jar:target/javasec-agent.jar com.anbai.sec.agent.CrackLicenseAgent

    程序执行结果如下:
    6. Attach模式 - 图1
    当Attach成功后我们可以看到原来的进程输出结果也已经不在输出授权过期提示信息了,如下图:
    6. Attach模式 - 图2
    使用Attach模式需要特别的需要注意和Agent模式的区别,因为Attach是运行在Java程序启动后,所以我们需要修改的Java类很有可能已经被JVM加载了,而已加载的Java类是不会再被Agent处理的,这时候我们需要在Attach到目标进程后retransformClasses,让JVM重新该Java类,这样我们就可以使用Agent机制修改该类的字节码了。