首页 > 其他分享 >记一次逆向分析解密还原Class文件

记一次逆向分析解密还原Class文件

时间:2023-12-05 16:57:22浏览次数:41  
标签:逆向 src buffer dst 解密 int file Class size

前言

前阵子我的一位朋友发来一份代码让我帮忙看看。具体就是所有的jsp文件内容和大小都一样,漏洞挖掘无从下手。经过分析发现所有的Class都使用了自定义的加密工具加密,经过逆向分析,顺利解密,因而有了此文。

初步分析

文件内容如下所示:

其他文件亦如是:

接着在tomcat work目录找到了编译后的class文件:

但是没办法直接反编译,查看头信息发现都一样:

因此猜测一种可能性是Java层面实现的类加载器,类加载的时候进行动态解密操作。于是自己写了一个jsp文件上传到目标环境,首先访问一下这个jsp文件,让JVM加载至内存中。然后我们调用Class.forName再去加载该类,获取到该类的java.lang.Class对象实例,然后调用getClassLoader获取该类的加载器:

<%
try {
    out.println(Class.forName("org.apache.jsp.test_html").getClassLoader());
} catch(Throwable th) {
    th.printStackTrace(out);
}
%>

结果获取到的内容为tomcat实现的WebAppClassLoader,回溯父类加载器也没有发现自定义的实现。于是计划取巧,使用Arthas之类的工具attach到目标的JVM,去内存dump加载过的Class。然后发现无法attach,估计是目标JVM版本过低,为JDK 1.5。因而继续取巧,用动态调试调试,在ClassLoader#defineClass方法下断点争取将byte[]直接dump出来,可是也没有成功。

峰回路转

过了几天,后来回头重新去做分析。在tomcat启动脚本中发现了如下的参数:

嗯,果然是自定义加载器,通过实现JVMTI接口,并未在应用层使用Java代码去实现,而是直接用C++实现接口。具体的实现就在这个dll中:

经过对java agent的简单学习,了解相关参数和实现后,在ida中将相关的结构体还原代码如上图所示。这里有个ida使用技巧,分析C/C++代码最重要就是要了解关键的结构体功能,这相当于了解Java中类的定义和相关方法的含义。而JVMTI的SDK在JDK的安装目录中是开源的,我们可以用ida导入本地结构体的功能批量导入。导入的时候根据header文件的加载顺序依次复制到一个文件中,否则会有很多依赖缺失导致的报错。最终的头文件结构如下:

jni_md.h -> jni.h -> jvmti.h

然后需要将前边的include指令导入操作删除掉,导入ida:

顺利的话,将会提示如上图所示:

相关的错误信息也会在message窗口输出。可以用来定位错误。

接着开始我们的逆向之旅,经过一些分析之后还原出来的伪代码如图所示:

在第22行,这里一定要把数据的显示格式修改为hex,如上图,我们可以看到这里判断了Class文件的魔术头,因而猜测这里就是解密的操作了,我们跟进解密函数的具体实现(第29行 decryptClassBytes函数):

代码的实现很简单,根据随机数种子设置srand函数。然后循环调用rand()函数去和读取到的byte进行异或操作。最终返回异或结果。这里一度让我十分困惑,因为根据我对随机数的认识,至少这里应该会把随机数也存放在某个地方,这样将来解密才能正确执行。因此我自己写了一些代码来观察随机数的生成:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	srand(0x96F07);
	int i = rand();
	printf("rand number = %x\n", i);
	
	int c = rand();
	printf("rand number = %d\n", c);
	
	int n = rand();
	printf("rand number = %d\n", n);
	
	int k = rand();
	printf("rand number = %d\n", k);
   
   return 0;
}

找了好几个在线运行代码的站点,发现最终生成的结果居然是完全一样的!查询该函数之后发现了这样的一句话:

初始化随机种子,会提供一个种子,这个种子会对应一个随机数,如果使用相同的种子后面的 rand() 函数会出现一样的随机数。

结合前阵子JumpServer出现过的随机数问题,让我再次认识到这个问题的居然是这种方式!

因此我们的解密操作就很简单了:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

int main() {
    const int BUFFER_SIZE = 1;
    srand(0x96F07);
    char *src_file = "D:\\cms\\tomcat\\work\\Catalina\\localhost\\oa\\org\\apache\\jsp\\test_html.class";
    char *dst_file = "C:\\Users\\Administrator\\CLionProjects\\decrypt\\decrypt.class";

    FILE *p_src = fopen(src_file, "rb");
    if (p_src == NULL) {
        printf("src_file open failed");
        return 0;
    }
    FILE *p_dst = fopen(dst_file, "wb");
    if (p_dst == NULL) {
        printf("dst_file open failed");
        return 0;
    }
    // 判断文件大小 , 该结构体接收文件大小结果
    struct stat st = {0};
    stat(src_file, &st);
    // 计算缓冲区文件大小
    int buffer_size = st.st_size;
    if ( buffer_size > BUFFER_SIZE ) {
        buffer_size = BUFFER_SIZE;
    }
    char *buffer = malloc(buffer_size);
    char output[1];

    while ( !feof(p_src) ) {
        int res = fread(buffer, 1, buffer_size, p_src);
        *output = *buffer ^ rand();
        fwrite(output, 1, res, p_dst);
    }
    // 释放缓冲区内存
    free(buffer);
    fclose(p_src);
    fclose(p_dst);
    printf("Copy Success");
    return 0;
}

以上代码并没有成功,后来我用这个代码加密一个未加密过的class文件,发现和目标文件差了1个字节。也就是说解密操作是越过第一个字节开始的,在如上代码基础上,越过第一个字节即可:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

int main() {
    const int BUFFER_SIZE = 1;
    srand(0x96F07);
    char *src_file = "D:\\cms\\tomcat\\work\\Catalina\\localhost\\oa\\org\\apache\\jsp\\test_html.class";
    char *dst_file = "C:\\Users\\Administrator\\CLionProjects\\decrypt\\decrypt.class";

    FILE *p_src = fopen(src_file, "rb");
    if (p_src == NULL) {
        printf("src_file open failed");
        return 0;
    }
    FILE *p_dst = fopen(dst_file, "wb");
    if (p_dst == NULL) {
        printf("dst_file open failed");
        return 0;
    }
    // 判断文件大小 , 该结构体接收文件大小结果
    struct stat st = {0};
    stat(src_file, &st);
    // 计算缓冲区文件大小
    int buffer_size = st.st_size;
    if ( buffer_size > BUFFER_SIZE ) {
        buffer_size = BUFFER_SIZE;
    }
    char *buffer = malloc(buffer_size);
    char output[1];

    // 跳过最初的1字节
    if (fseek(p_src, 1, SEEK_SET) != 0) {
        printf("fseek error!\n");
        fclose(p_src);
        return 1;
    }

    while ( !feof(p_src) ) {
        int res = fread(buffer, 1, buffer_size, p_src);
        *output = *buffer ^ rand();
        fwrite(output, 1, res, p_dst);
    }
    // 释放缓冲区内存
    free(buffer);
    fclose(p_src);
    fclose(p_dst);
    printf("Copy Success");
    return 0;
}

解密:

标签:逆向,src,buffer,dst,解密,int,file,Class,size
From: https://www.cnblogs.com/magic-zero/p/17877618.html

相关文章

  • 使用mysqlbinlog恢复数据库(逆向或正向都可以)
    我确实喜欢写一些不一样的东西。如果我们在网上搜索“mysql恢复数据库”、“使用mysqlbinlog恢复数据库”、“mysql使用binlog恢复被删除的表”等,一般可以搜索到很多,但是仔细分析会发现,这许多的基本都在讲几件事:1、查看是否已开启在线日志,如果没有开启就开启2、查看日志文件位置3......
  • 解密Prompt系列20. LLM Agent之再谈RAG的召回多样性优化
    几个月前我们就聊过RAG的经典方案解密Prompt系列14.LLMAgent之搜索应用设计。前几天刚看完openAI在DevDay闭门会议上介绍的RAG相关的经验,有些新的感悟,借此机会再梳理下RAG相关的优化方案。推荐直接看原视频(外网)ASurveyofTechniquesforMaximizingLLMPerformanceRAG最关键......
  • 天堂之门(Heaven's Gate)逆向
    Heaven'sGate原理及POC通过在32位WoW进程中执行64位代码,实现静态反编译以及干扰对Win32Api的检测实现免杀。详见[原创]天堂之门(Heaven'sGate)C语言实现-软件逆向-看雪-安全社区|安全招聘|kanxue.com常见的打开天堂之门的代码块//convertx86tox646A33......
  • js:React中使用classnames实现按照条件将类名连接起来
    参考文档https://www.npmjs.com/package/classnameshttps://github.com/JedWatson/classnames安装npminstallclassnames示例importclassNamesfrom"classnames";//字符串合并console.log(classNames("foo","bar"));//foobar//对象合并c......
  • 【验证码逆向专栏】百某网数字九宫格验证码逆向分析
    声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均......
  • 【Android逆向】一些零碎的笔记
    *在/sdcard/下的文件无法执行,必须将其拷贝到其它位置执行,如/data/目录,/data/目录中是system分组,可以执行程序;*每个应用都会创建一个对应的应用用户,如:cn.abcpiano.pianist包名的应用,创建了一个u0_a147用户;* getpropro.product.cpu.abi ......
  • SRE Google运维解密 第一章
    译者序SRE是一群天生的怀疑论者,我们怀疑一切宣传起来"高大上"的技术,以及任何"神奇"的产品一一我们只想看具体的设计架构、实现细节,以及真实的监控图表。SRE在保障系统可靠性方面并没有什么万能药,有的只是这种极强的务实态度(pragmatic)。这种务实的态度决定了SRE会认真对......
  • class-dump 混淆加固、保护与优化原理
    ​ class-dump混淆加固、保护与优化原理进行逆向时,经常需要dump可执行文件的头文件,用以确定类信息和方法信息,为hook相关方法提供更加详细的数据.class-dump的主要用于检查存储在MachO文件的Objective-C中的运行时信息,为类,类别和协议生成声明信息,与tool-ov命令产生的信息相......
  • android开发aar包或者jar包出现类重复问题Caused by: java.lang.RuntimeException: Du
    如果是仓库依赖的方式直接使用exclude语句移除相同的依赖库即可,如下:implementation("org.java-websocket:Java-WebSocket:1.5.2"){excludegroup:'org.slf4j',module:'slf4j-api'//exclude掉websocket库依赖的slf4j库}但是如果是aar包或者jar包里面的类重复呢?这个......
  • JS逆向——某道翻译
    文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,如有侵权,请联系作者立即删除!目标网站:aHR0cHM6Ly9mYW55aS55b3VkYW8uY29tL2luZGV4Lmh0bWwjLw==一、定位接口使用Chrome浏览器,打开Network进行抓包输入要翻译的字符串:HelloWorld!页面没有重新加载,初步判断是Ajax......