1.字节码出现类名与包名冲突
使用这个工具:jarjar工具
- 创建一个rule文件,如rule.txt,内容如下:
rule 原包名.** 目标包名.@1
例如:
rule a.b.c.** x.y.z.@1
rule a.b.c.** a.b.c.@1
然后运行jarjar工具,将要修改的jar包old.jar
按照rule.txt的规则产生新的jar包new.jar
java -jar jarjar-1.4.jar process rule.txt old.jar new.jar
2. 利用javaassist修改对应方法
- 添加依赖
sourceCompatibility = 1.8
dependencies {
compile 'io.reactivex.rxjava2:rxjava:2.1.16'
compile fileTree('libs/javassist.jar')
}
- 导入类
public class Clazz {
private String JAR_DIR = "";
private String JAR_NAME = "";
private String PKGNAME = "";
private String CLASSNAME = "";
private Clazz() {
}
public interface Builder {
Clazz CLAZZ = new Clazz();
}
public interface Presenter {
void onHack(CtClass ctClass) throws Exception;
}
public static Clazz initialize(String libDir, String libName) {
Clazz clazz = Builder.CLAZZ;
clazz.JAR_DIR = libDir;
clazz.JAR_NAME = libName;
return clazz;
}
@SuppressWarnings("ResultOfMethodCallIgnored")
public void hackClass(String pkgName, String className, Presenter presenter) {
PKGNAME = pkgName;
CLASSNAME = className;
// jar包路径
// class位置
// 输入class路径, jar uvf命令替换jar
// String command = String.format("cd %s; jar uvf %s %s", JAR_DIR, JAR_NAME, clazzPath);
// String[] cmd = {"/bin/sh", "-c", command};
Observable.create((ObservableOnSubscribe<String>) emitter -> {
// jar包路径
String jar = getJarPath();
File file = new File(jar);
if (file.exists()) {
emitter.onNext(jar);
emitter.onComplete();
} else {
emitter.onError(new FileNotFoundException(jar + " NOT FOUND!"));
}
}).map(jarPath -> {
ClassPool classPool = ClassPool.getDefault();
classPool.insertClassPath(jarPath);
// class位置
String clazz = getClazzName();
CtClass ctClass = classPool.get(clazz);
if (presenter != null) {
presenter.onHack(ctClass);
}
return ctClass.toBytecode();
}).map(bytes -> {
String clazzPath = getClazzPath();
File dstFile = new File(clazzPath);
if (!dstFile.getParentFile().exists()) {
dstFile.getParentFile().mkdirs();
}
FileOutputStream output = new FileOutputStream(dstFile);
output.write(bytes);
output.flush();
return dstFile.getAbsolutePath();
}).map(savePath -> { // 输入class路径, jar uvf命令替换jar
String clazzPath = getClazzPath();
// String command = String.format("cd %s; jar uvf %s %s", JAR_DIR, JAR_NAME, clazzPath);
// String[] cmd = {"/bin/sh", "-c", command};
String command = String.format("jar uvf %s %s", getJarPath(), clazzPath);
return exec(command);
}).subscribe(status -> {
if (status == 0) {
String command = String.format("rm -rf %s", getClazzRootPath());
Runtime.getRuntime().exec(command);
}
System.out.printf("status: %d\t%s\n", status, Thread.currentThread().getName());
});
}
private String getJarPath() {
return String.format("%s/%s", JAR_DIR, JAR_NAME);
}
private String getClazzRootPath() {
String rootPath;
int index = PKGNAME.indexOf(".");
if (index > 0) {
rootPath = PKGNAME.substring(0, index);
} else {
rootPath = getClazzPath();
}
return rootPath;
}
private String getClazzPath() {
String clazzPath;
if (PKGNAME.isEmpty()) {
clazzPath = String.format("%s.class", CLASSNAME);
} else {
clazzPath = String.format("%s/%s.class", PKGNAME.replace(".", "/"), CLASSNAME);
}
return clazzPath;
}
private String getClazzName() {
String clazzName;
if (PKGNAME.isEmpty()) {
clazzName = CLASSNAME;
} else {
clazzName = String.format("%s.%s", PKGNAME, CLASSNAME);
}
return clazzName;
}
public static int exec(String... commands) throws IOException, InterruptedException {
int status = 1;
if (commands == null) {
return status;
}
File wd = new File(".");
Process proc = Runtime.getRuntime().exec("/bin/bash", null);
if (proc != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
for (String command : commands) {
out.println(command);
}
out.println("exit");
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
status = proc.waitFor();
in.close();
out.close();
proc.destroy();
}
return status;
}
public static int exec2(String... commands) throws IOException, InterruptedException {
int status = 1;
if (commands == null) {
return status;
}
String command = String.join(";", commands);
String[] cmd = {"/bin/sh", "-c", command};
Process proc = Runtime.getRuntime().exec(cmd);
if (proc != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
status = proc.waitFor();
in.close();
proc.destroy();
}
return status;
}
public static String readContent(String path) throws IOException, NotFoundException {
File file = new File(path);
if (!file.exists()) {
throw new NotFoundException("Not Found: " + file.getAbsolutePath());
}
BufferedReader in = new BufferedReader(new FileReader(file));
String line;
StringBuilder buffer = new StringBuilder();
while ((line = in.readLine()) != null) {
buffer.append(line).append("\n");
}
in.close();
return buffer.toString();
}
}
- 开始hook(写一个测试方法,修改charles.jar)
// dir: jar包目录
// charles.jar: 要修改的jar包名
Clazz.initialize("dir", "charles.jar")
.hackClass("com.xk72.charles", "oFTR", ctClass -> {
CtMethod ctMethod = ctClass.getDeclaredMethod("lktV", null);
ctMethod.setBody("{return \"Crack by me!\";}");
});
3. 还原原包包名
按方法1修改即可。
END
- PS:
// 如果方法体过大,采用读文本的形式setBody
String content = Clazz.readContent("method.txt");
ctMethod.setBody(content);
method.txt
内容如下:
{
return "Crack by me!";
}