不动jar文件，通过java agent来修改java类

辽宁省赛线下 awd，出了那么一道 java 题：

Copy
@PostMapping({"/cmd"})
@ResponseBody
public String cmd(@RequestParam String command) throws Exception {
Base64.getDecoder();
StringBuffer sb = new StringBuffer("");
String[] c = {"/bin/bash", "-c", command};
Process p = Runtime.getRuntime().exec(c);
CopyInputStream(p.getInputStream(), sb);
CopyInputStream(p.getErrorStream(), sb);
return sb.toString();
}

遗憾的是：只有 3 个队伍会修。而我们正因为不会修，屈居第四（还好 kali 上有个 jadx-gui，但是为什么 jadx-gui 只能看不能改啊！！）。

研究了一下，发现了 java agent 这个东西，可以弄出类似 hook 的效果（感觉不如。。xposed），但是感觉有很多坑，环境方面，所以记录一下。

首先最不重要的代码部分是从这里参考的，加了点针对函数重载的精确搜索（关于 java agent 的介绍也可以看这里）：https://www.cnblogs.com/rickiyang/p/11368932.html

Copy
package agent2;

import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import javassist.*;

public class PreMainAgent {

public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("agentArgs : " + agentArgs);
inst.addTransformer(new DefineTransformer(), true);
}

static class DefineTransformer implements ClassFileTransformer {

@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (!"com/example/ezjava/controller/EvilController".equals(className)) {
return null;
}
System.out.println("premain load Class:" + className);
try {
System.out.println("trying!");
final ClassPool classPool = ClassPool.getDefault();
final CtClass clazz = classPool.get("com.example.ezjava.controller.EvilController");
final CtClass str_class = classPool.get("java.lang.String");
System.out.println(str_class);
CtMethod cmd = clazz.getDeclaredMethod("cmd", new CtClass[]{str_class});
//这里对 java.util.Date.convertToAbbr() 方法进行了改写，在 return之前增加了一个 打印操作
String methodBody = "{return \"stupid!\";}";
System.out.println("Setting body!");
cmd.setBody(methodBody);

// 返回字节码，并且detachCtClass对象
byte[] byteCode = clazz.toBytecode();
//detach的意思是将内存中曾经被javassist加载过的Date对象移除，如果下次有需要在内存中找不到会重新走javassist加载
clazz.detach();
System.out.println("returning!");
return byteCode;
} catch (IOException | CannotCompileException | NotFoundException e) {
System.out.println("Error!");
System.out.println(e);
return null;
}
}
}
}

坑部分：

下载javassist.jar 。

然后他用的是 maven 来改 manifest，但是我一直没有找到把依赖打进包里的办法，就改用 ant 了。

IDE 我用的 netbeans。可以直接在 Libraries 上右键添加 jar。

修改 manifest 参考这个链接：https://www.javaxt.com/wiki/Tutorials/Netbeans/How_to_Add_Version_Information_to_a_Jar_File_with_Netbeans

但是，其中的这部分不太对

First, you must update your Netbeans "project.properties" file found in the "nbproject" directory. Add the following line to the file:

manifest.file=manifest.mf

应该是把project.properties里的manifest.file=manifest.mf改成manifest.file=MANIFEST.MF

manifest 里除了Premain-Class和Agent-Class以外还要再加两行（记得把他写的乱七八糟的属性都删掉）：

Copy
<attribute name="Can-Redefine-Classes" value="true"/>
<attribute name="Can-Retransform-Classes" value="true"/>

虽然 ant 也没把依赖打进包里，但他创建了个 lib 目录并在 manifest 里写入了 classpath 的属性，所以可以跑起来。

然后启动命令是java -javaagent:'/home/zfn/NetBeansProjects/agent2/dist/agent2.jar' -jar awd.jar

好