首页
社区
课程
招聘
[分享]zkm解析加密字符串的动态方式
发表于: 2021-4-29 13:31 11276

[分享]zkm解析加密字符串的动态方式

2021-4-29 13:31
11276


概念代码,希望大大家都可以参与修改,代码不是通用,只是提供一个思路,欢迎讨论。



https://gitee.com/googlewell/zkm


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (5)
雪    币: 3985
活跃值: (5680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2

通过代码自动修改后的字节码,只修改了main方法,只剩一个INVOKEDYNAMIC没有替换,因为前面有标签,可以用recaf手动移动一下指令位置就可以识别到了,复杂的代码移动一下会导致栈不平衡,所以没有自动修改。再就是修改后添加了许多NOP空操作指令,因为修改指令会remove一些指令,导致list变化,有的指令会被跳过,所以就加上NOP保持list不会缩减,或者后期考虑旧指令不变,将指令都存入一个新的指令列表来解决。虽然不太完美,每次解析一个方法,但是自动化修改也省去了很大一部分手动解析指令的工作,效率提高了。

上传的附件:
2021-4-29 17:57
0
雪    币: 3985
活跃值: (5680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

批量处理的zkm的invokedynamic命令。


处理过程

首先设置代理类,输出方法的第一个invokedynamic的key,因为一个方法内的都是一致的,这里用到StringBuilder去字符拼接,一起输出,防止输出被多线程打乱。

for (MethodNode method : methods) {
    InsnList srcList = method.instructions;

    for (int i = 0; i < srcList.size(); i++) {//遍历指令
        //查找invokedynamic指令
        if (srcList.get(i) instanceof InvokeDynamicInsnNode) {
            if (srcList.get(i - 1) instanceof VarInsnNode && srcList.get(i - 1).getOpcode() == Opcodes.LLOAD) {//判断是不是LLOAD
                VarInsnNode varInsnNode = (VarInsnNode) srcList.get(i - 1);

                //创建替换指令
                InsnList tmpInsnList = new InsnList();

                //组合成一条字符串输出,以免被打乱
                tmpInsnList.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"));
                tmpInsnList.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuilder"));
                tmpInsnList.add(new InsnNode(Opcodes.DUP));
                tmpInsnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,"java/lang/StringBuilder","<init>","()V",false));
                tmpInsnList.add(new LdcInsnNode(className + ":" + method.name + ":" + method.desc + ":"));
                tmpInsnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,"java/lang/StringBuilder","append","(Ljava/lang/String;)Ljava/lang/StringBuilder;",false));
                tmpInsnList.add(new VarInsnNode(Opcodes.LLOAD, varInsnNode.var));
                tmpInsnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,"java/lang/StringBuilder","append","(J)Ljava/lang/StringBuilder;",false));
                tmpInsnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,"java/lang/StringBuilder","toString","()Ljava/lang/String;",false));
                tmpInsnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"));

                srcList.insertBefore(srcList.get(i), tmpInsnList);
            }
        }
    }

    method.visitEnd();
}


然后通过代理运行zkm,点击需要解析的按钮和功能。这里只进行了混淆和保存。

java -javaagent:asm.jar=dynamic+com.zelix.+ -jar ZKM.jar > long.txt

然后处理字符,去重,228M文件得到166kb,446个类2719个方法。文件内容如下

com/zelix/ZKM:main:([Ljava/lang/String;)V:46979434589134

首先遍历类,通过类中的方法名和方法描述去处理,key可以查出,这样就可以批量处理了。


批量处理结果在附件,只是触发响应功能的类,其他没有运行加载的类和方法不会处理。只是去掉大部门invokedynamic的方法和加密字符串,有些被其他指令分割的没有处理。有的方法可能在recaf报类型不符,因为recaf严格匹配类名,没有判断调用者或者参数是不是其父类或者子类,实际类型应该是符合的。这个是批量处理的结果,应该会存在一些错误,仅供参考。

上传的附件:
2021-5-3 12:46
0
雪    币: 3985
活跃值: (5680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4

项目基于在zkm15的进行了更新,把以前零星代码都整理进来了,基本可以解析大部分字符串和方法了,并对相应指令进行转换存到class文件了。大的类转换时间可能有点长,毕竟需要和arthas进行交互,调试的分析类和方法的提示去掉了,只保留了写入到文件的提示,可以耐心等待,class写入到classes文件夹下。


执行的命令可以自己在arthas客户端输入history命令进行查看,可以用方向键执行进行查看结果,或者复制进行执行


反编译效果不是很好,推荐还是用recaf进行查看

这只是个思路,其他用zkm进行混淆的,也可以分析套用。

2021-12-26 10:03
0
雪    币: 3985
活跃值: (5680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5

新的解析思路,半自动化解析混淆方法和字符串,只需要取触发相应功能,后续就会自动解析。这种只适用于可以触发的功能,如果那种无法触发的,比如程序启功开始的一些功能,就需要编写agent去拦截关键参数了,就是最开始zkm那种方式了。

2022-1-18 16:03
1
雪    币: 3985
活跃值: (5680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
写了一个recaf的插件,实现加载类,然后再recaf修改字节码,修改完进行热更新,方便了一些操作。可以修改字节码了,就可以随意插入代码了,输出调试,打印调用堆栈,勉强算一个字节码级别的调试工具吧。

代码开源,开源地址在视频简介中。

https://www.bilibili.com/video/BV1Hr4y167jw/
2022-2-13 11:08
0
游客
登录 | 注册 方可回帖
返回
//