前言

前阵子我的一位朋友发来一份代码让我帮忙看看。具体就是所有的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启动脚本中发现了如下的参数:

嗯,果然是自定义加载器,可人家是通过Java agent实现的,而且是实现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;
}

解密:

记一次逆向分析解密还原Class文件的更多相关文章

  1. 某Android手游的lua源码逆向分析与还原

    近日分析某一款Android上面的手游,反编译后再起asset目录下可以看到加密过的脚本,lib目录下发现lua的so 初步怀疑其使用lua脚本实现的 解密函数定位 动态跟踪解密函数流程 静态分析解密 ...

  2. 还原win10任务管理器的内存dump功能之——程序逆向分析(待完成)

    逆向分析工作基本完成,笔记待完成.

  3. IM通信协议逆向分析、Wireshark自定义数据包格式解析插件编程学习

    相关学习资料 http://hi.baidu.com/hucyuansheng/item/bf2bfddefd1ee70ad68ed04d http://en.wikipedia.org/wiki/I ...

  4. C++反汇编与逆向分析技术揭秘

    C++反汇编-继承和多重继承   学无止尽,积土成山,积水成渊-<C++反汇编与逆向分析技术揭秘> 读书笔记 一.单类继承 在父类中声明为私有的成员,子类对象无法直接访问,但是在子类对象的 ...

  5. TI(德州仪器) TMS320C674x逆向分析之一

    一.声明 作者并不懂嵌入式开发,整个逆向流程都是根据自身逆向经验,一步一步摸索出来,有什么错误请批评指正,或者有更好的方法请不吝赐教.个人写作水平有限,文中会尽量把过程写清楚,有问题或是写的不清楚的地 ...

  6. 一文了解安卓APP逆向分析与保护机制

    "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问就不会知道."知物 ...

  7. PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第二讲,被除数是正数 除数非2的幂

    目录 一丶简介 二丶代码还原讲解 1.被除数无符号 除数非2的幂 2.被除数无符号 除数为特例7 三丶代码还原总结 一丶简介 上一篇博客说的除2的幂. 如果被除数是有符号的,那么会进行调整,并使用位操 ...

  8. SG Input 软件安全分析之逆向分析

    前言 通过本文介绍怎么对一个 windows 程序进行安全分析.分析的软件版本为 2018-10-9 , 所有相关文件的链接 链接:https://pan.baidu.com/s/1l6BuuL-HP ...

  9. [Android Security] Smali和逆向分析

    copy : https://blog.csdn.net/u012573920/article/details/44034397 1.Smali简介 Smali是Dalvik的寄存器语言,它与Java ...

  10. 逆向分析-IDA动态调试WanaCrypt0r的wcry.exe程序

    0x00 前言 2017年5月12日全球爆发大规模蠕虫勒索软件WanaCrypt0r感染事件,各大厂商对该软件做了深入分析,但针对初学者的分析教程还比较少,复现过程需要解决的问题有很多,而且没有文章具 ...

随机推荐

  1. Dirty-Pipe Linux内核提权漏洞(CVE-2022-0847)

    前言: 划水一波,哈哈,以后复现漏洞不再直接傻瓜无脑的走流程了,首先码字写加构思比较麻烦且写的不多还效率不高,现在就是当做见到了一个漏洞,在此记录一下这个漏洞,包括其来源,简单的描述,适用范围,以及其 ...

  2. AVR汇编(二):AVR架构介绍

    AVR汇编(二):AVR架构介绍 ATmega328P介绍 ATmega328P是Atmel公司(现Microchip公司)推出的一个基于AVR架构的高性能低功耗单片机,拥有32KB的Flash.1K ...

  3. WorkManager的用法

    一.WorkManager的作用 绝大部分应用程序都有后台执行任务的需求,根据需求的不同,Android为后台任务提供了多种解决方案,如JobShedule,Loader,Service等.如果这些a ...

  4. 三维模型OSGB格式轻量化顶点压缩主要技术方法分析

    三维模型OSGB格式轻量化顶点压缩主要技术方法分析 在三维模型应用中,轻量化处理是提高数据传输效率.减少渲染时间和优化用户体验的重要手段.而OSGB格式是一种常见的三维模型格式,在进行轻量化处理时,顶 ...

  5. 干掉 CRUD!这个API开发神器效率爆炸,无需定义MVC类!!

    简介 magic-api 能够只通过 UI 界面就能完成简单常用的接口开发,能够支持市面上多数的关系性数据库,甚至还支持非关系性数据库 MongoDB. 通过 magic-api 提供的 UI 界面完 ...

  6. 拯救“消失的她”——双系统grub完美恢复方案

    双系统grub意外消失怎么办? 不用重装系统.不用去维修店.不会丢数据,教你一招,完美恢复grub! 背景 我的电脑是windows和linux双系统,启动项使用的grub.某天准备切换linux时突 ...

  7. 《微服务架构设计》——Eventuate Tram框架订阅/消费模式源码解析

    Eventuate Tram框架官方文档: https://eventuate.io/docs/manual/eventuate-tram/latest/getting-started-eventua ...

  8. 文心一言 VS 讯飞星火 VS chatgpt (94)-- 算法导论9.2 3题

    三.用go语言,给出 RANDOMIZED-SELECT的一个基于循环的版本. 文心一言: 在循环中实现 RANDOMIZED-SELECT 的一个版本可以如下所示.这个版本使用 Go 语言编写,利用 ...

  9. strimzi实战之二:部署和消息功能初体验

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<strimzi实战>系列 ...

  10. WebApi中添加Jwt鉴权

    前言 JSON Web Token(JWT)是一个非常轻巧的规范.这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息.一个 JWT 实际上就是一个字符串,它由三部分组成,头部.载荷与签 ...