修改混淆jar中类方法

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!";
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343