dll 源码_【技术分享】 | 一个JAVA内存马的源码分析

前言

偶然接触到了这样一个JAVA内存马,其作者也是冰蝎的作者,项目地址:

https://github.com/rebeyond/memShell

正好最近在接触JAVA,借此机会学习下大佬的代码,对自己的编程思路也有了一定的提升。当然笔者只是一个脚本小子,对代码接触不深,如果文中出现理解不当或是错误的情况,还望各位大佬不吝赐教:)

背景知识

Java Instrumentation

JAVA在SE5版本引入了Java Instrumentation,其包含在java.lang.instrument中。通过Instrumentation,我们可以构建独立于应用程序的代理端,而通过这个代理我们可以监控JVM状态以及修改类定义。而在SE6版本中,我们能够实现注入代码到运行时的JVM中。

Java Agent

前面提到的代理端也就是Java Agent,Java Agent是依附于JAVA应用程序并能对其字节码做修改的一项技术,不能独立运行。加载运行方式有两种:premain模式和attach模式,前者是在程序运行前加载,后者是在程序运行后加载,本文分析的这个木马是使用的后者。

项目作者在这篇文章中有个原理demo,感兴趣的可以看看。

分析

分析按由外及里、由简入难的顺序进行,即:用户可见功能模块、注入模块、代理模块、持久化模块。

用户可见模块分析

从项目README文件里可以看到这款木马有以下功能:

l  欢迎页

l  命令执行

l  反弹Shell

l  远程文件下载

l  文件操作

l  本地文件下载

l  文件上传

l  代理

l  菜刀连接

欢迎页

当我们获取目标服务器权限时,将木马上传至目标服务器,执行java -jar inject.jar password,即可进行进程注入获取一个隐藏进程会话。

 [+]OK.i find a jvm. [+]memeShell is injected.

此时我们去浏览器访问目标服务器即可调用目标模块。

4449d72823b2cbbe68d6e254a6813ceb.png

需要注意的是,这个木马和常规木马不同。常规木马需要去访问目标木马文件来执行命令,而这款木马访问任意URL都可以调用木马执行其模块。其原理是此木马向org.apache.catalina.core.ApplicationFilterChain类的internalDoFilter方法注入了自定义代码,这个JAVA类在HTTP请求调用栈的上方,可以相应我们的任意Request请求,因此我们可以用GET请求也可以用POST请求。

internalDoFilter方法的原型如下:

b10f6954f5f8956d4075eaa5364b60dd.png

换个角度来讲,这个类就是一个过滤器,而过滤器可以在客户端的请求访问后端资源之前,拦截这些请求。也可在服务器的响应发送回客户端之前,处理这些响应。也就是说用户的每一个Request请求都会经过过滤器,无论用户访问的资源是否存在,如何处理这些请求也是过滤器的核心所在。

命令执行

命令执行模块的核心就是调用Runtime.getRuntime().exec(cmd)方法来执行任意命令并获取返回结果。

f8c997298966e5e050b04c8cebc4cdc2.png

例如:

5ee8b5a4c5ed965de96cc3faaf5865b7.png

反弹Shell

反弹Shell主要依靠的是Socket通信,首先判断操作系统类型,再进行Socket连接以达到反弹Shell的目的。

85250438efc62dd264e45d4bc56d4c3b.png

例如:

ea259d07b0aa869ea876394e2791c155.png

远程文件下载

远程文件下载支持HTTPS,下面的代码片段为了排版美观我去掉了SSL认证代码。

ed2bfaa2a2b0f5d3af1c748fe6f7a926.png

文件操作

文件操作包含列文件、删除文件、查看文件、删除文件夹,都是一些很基础的文件操作代码,因此我们以查看文件为例。

4faa4136bc43e88871e8b4c2169b1e60.png

b2c22fe632a52879f46cfe3c18775211.png

本地文件下载

f3907c18a02bba323771e446044fe19d.png

文件上传

有普通上传方式,有Base64编码上传方式。

09d8141eaf76fe319c74a2e234ca8ac8.png

代理

木马里内嵌了一个reGeorg代理,因此我们可以直接使用这个木马实现代理转发,进一步渗透内网。

94e1f8fd132e5e0fac1dbbb572d2481d.png

核心源码就是根据reGeorg改的,网上也有分析文章,此处不再多赘述。

菜刀连接

菜刀模块的源码也是根据这个JSP菜刀源码改的,但是个人觉得挺有意思——方法命名全是AA、BB、CC这种,不明白原作者是为了混淆还是只是单纯的恶作剧。

e2e9cfbf4b124a79d163ca53723988ab.png

当然,以上的这些用户可见功能模块都建立在一个判断逻辑里,也就是需要正确输入我们所指定的密码。

  if (pass_the_world!=null&&pass_the_world.equals(net.rebeyond.memshell.Agent.password))

这些用户可见模块除了代理模块和菜刀模块都是一些常用的功能代码,简单易懂。紧接着我们深入分析其他模块。

注入模块分析

注入模块主要是用来遍历目标机器上的JVM实例并进行代码注入。前面提到我们有两种方式进行注入,premain和attach,这个木马里用的attach注入方式。

注入模块在运行的时候,会动态加载一个代理,也就是我们的代理模块。换句话说,代理模块会被注入模块注入到tomcat进程中。

71a56404abae545a7fed92c2762286e6.png

代理模块分析

在代理端被注入到JVM后,会自动运行agentmain方法。agentmain方法在获取用户输入的参数后,会遍历获取当前所有类,如果匹配到我们要注入的目标类,则查看当前的JVM配置是否支持类的重新定义,代码如下所示。

1eb341d52d51b1c093f7c38047c0a2ed.png

为避免默认端口号被更改初始化失败,木马会先获取当前工程的端口号,然后对本地的tomcat发起一起请求进行初始化。进行初始化的原因原作者也解释过,因为我们在运行木马后会将木马文件删除,因此需要在删除之前没有将木马写入内存。写入内存的方式有两种:依次加载需要的类、进行一次模拟访问,这款木马选择的后者。如下代码所示,会访问一次本地的tomcat服务,以达到将木马写入内存的目的。

f869cb6b5c55019ab9f801387b232127.png

在注入进程后,需要删除自身,但是我注意到删除的代码还比较多,原因是操作系统的不同,删除方式也不同,例如Windows下不能直接删除一个占用中的文件。因此首先需要判断操作系统类型,如果是Linux直接删除即可。

6b8ea3ba819fae8a9c01c81442f67d3a.png

如果是Windows,需要利用unlockFile这个方法来进行删除,unlockFile方法里使用了一个二进制文件——forceDelete.exe。例如当我们要删除代理端时,需要先用以下代码获取当前JVM进程PID。

  public static String getCurrentPid() {        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();        return runtimeMXBean.getName().split("@")[0];    }

然后利用命令执行的方式使用这个二进制文件强制删除占用的文件,并且将自身删除。

27bc229ab78770b7e70b5b2487588769.png

这个强制删除功能的二进制文件我们利用IDA进行简单分析,利用IDA打开后,手动调试恢复函数即可看到整个二进制文件代码逻辑。我这里就没有去恢复变量名,各位大佬凑合着看。

首先有一个判断逻辑:

  if ( !sub_411440 () || !sub_411590() )        ExitProcess(0);  

跟进sub_411440()函数,根据其代码逻辑我们发现其实就是Change_access()函数,其代码逻辑如下:

  BOOL sub_411440()    {        HANDLE v0; // eax        struct _TOKEN_PRIVILEGES NewState; // [esp+D0h] [ebp-24h]        HANDLE TokenHandle; // [esp+E8h] [ebp-Ch]            NewState.PrivilegeCount = 1;        v0 = GetCurrentProcess();        if ( !OpenProcessToken(v0, 0x28u, &TokenHandle) )            return 0;        LookupPrivilegeValueW(0, L"SeDebugPrivilege", (PLUID)NewState.Privileges);        NewState.Privileges[0].Attributes = 2;        return AdjustTokenPrivileges(TokenHandle, 0, &NewState, 0x10u, 0, 0) != 0;    }

作用是获取当前进程信息并修改其权限。

再跟进sub_411590()函数,根据其代码逻辑推断是Get_Functions_address()函数

  BOOL sub_411590()    {        v0 = GetModuleHandleW(L"ntdll.dll");        dword_41713C = (int)GetProcAddress(v0, "ZwSuspendProcess");        v1 = GetModuleHandleW(L"ntdll.dll");        QueryInformationFille = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v1,  "ZwQueryInformationFile");        v2 = GetModuleHandleW(L"ntdll.dll");        QuerySystemInformation = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v2,  "ZwQuerySystemInformation");        v3 = GetModuleHandleW(L"ntdll.dll");        QueryObject = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v3, "ZwQueryObject");        v4 = GetModuleHandleW(L"ntdll.dll");        dword_417138 = (int)GetProcAddress(v4, "ZwResumeProcess");        v5 = GetModuleHandleW(L"ntdll.dll");  14.      QueryInformationPrecess = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD, _DWORD))GetProcAddress(v5, "ZwQueryInformationProcess");        return dword_41713C && QuerySystemInformation && QueryObject && dword_417138 && QueryInformationPrecess;    }  

其作用是加载ntdll.dll模块并从中获取ZwSuspendProcess、ZwQueryInformationFile、ZwQuerySystemInformation、ZwQueryObject、ZwResumeProcess、ZwQueryInformationProcess的函数地址。

还有一个sub_411DD0()函数,根据其代码逻辑推断是Find_SubStr()函数。因此此时整个二进制文件代码逻辑就清晰了。其核心代码如下:

  if ( QueryInformationFille(TargetHandle,&v13,FileInformation,528,9) >= 0 )2.  {        if ( Find_SubStr(FileInformation + 2, L"agent.jar") )        {            v4 = GetCurrentProcess();            if ( DuplicateHandle(hSourceProcessHandle, (HANDLE)v8, v4, &TargetHandle, 0, 0, 1u) )            {                CloseHandle(TargetHandle);                ExitProcess(0);            }        }    }  

我们梳理下整个二进制文件的运行逻辑:首先打开agent.jar进程,遍历该进程的所有句柄信息,通过DuplicateHandle()函数复制句柄到本地进程,关闭文件句柄,此时就能删除占用中的文件了。

本来DuplicateHandle()函数是用来创建新句柄的,但是我们可以利用这个特性来删除被占用的文件,巧妙的实现删除文件的功能。

持久化模块分析

持久化模块主要是用于tomcat服务重启后也能继续使用这款木马,也就是说,只要目标机器不重启,tomcat服务运行起来我们无需进行二次注入也能获取权限,其核心代码如下。

2c1735ecdf8226076c1811cef804eef9.png

主要的核心原理在于addShutdownHook钩子,JVM关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,JVM才会关闭。所以这些钩子可以在JVM关闭的时候进行内存清理、对象销毁等操作。当然这些只是一些“正规的操作”,我们可以设置一些“非法操作”,在JVM关闭的时候将我们已经注入内存的代码写入到文件,然后再调用startInject方法,startInject方法源码如下:

c081c4925c3e705d3842c87207aeece3.png

再次调用startInject方法后就达到了持久化的目的。

总结

我们再梳理下整个木马工作流程:

1.   获取到目标服务器权限,将Inject.jar和Agent.jar上传至服务器。

2.   执行java -jar Inject.jar password,开始注入Tomcat进程。

3.   注入模块寻找目标类。

4.   将代码注入到Tomcat进程。

5.   成功注入后删除自身。

6.   遇到Tomcat进程重启,将内存代码写入临时文件。

7.   再次注入Tomcat进程达到持久化目的。

注入的核心关键在于Servlet过滤器的internalDoFilter方法,因为所有的用户请求都会通过这个方法。

未来工作

JSP服务器的内存注入,使得JAVA内存马的通用性得到提高。原作者也提到使得这个内存马通用性提高的关键就在于要寻找到“关键类”,Tomcat里使用的是Servlet的过滤器关键类,在其他JAVA容器我们也需要找到这样的一个关键类,这也是未来工作的重点。

7101c464fb8835b1c16bff73614263f3.png

安洵信息技术有限公司

www.i-soon.net

f398d08c7cb79bac86ad582a1dc5fdd2.png

以实力陪伴客户成长  使客户更加强大

400-066-5915

上海丨四川丨江苏 | 云南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ThunderSearch(闪电搜索器)_网络空间搜索引擎工具_信息收集

文章目录 ThunderSearch简介1 项目地址2 使用方式2.1 配置文件config.json说明2.2 构建和运行 3 使用式例 ThunderSearch简介 ThunderSearch(闪电搜索器)是一款使用多个(【支持Fofa、Shodan、Hunter、Zoomeye、360Quake网络空间搜索引擎】网络空间搜索引…

每个人都知道MVC…

从一个最近的博客中,您可能已经了解到我最近一直在进行一些采访,因为他们是针对Web应用程序开发人员的,所以我问的一个问题是“您能解释一下MVC模式是什么吗?”,值得称赞的是,每个候选人知道答案。 对于不认…

r语言ggplot2 多线图绘制图例_plotnine: Python版的ggplot2作图库

腾讯课堂 | Python网络爬虫与文本数据分析同样的基本作图任务,plotnine比matplotlib和seaborn代码量少,更美观。所以我又重新发一遍,大家可以先收藏起来,后面总有用到的时候~R语言的ggplot2绘图能力超强,python虽有mat…

单元和集成测试的代码覆盖率

我最近在一个宠物项目中着手构建自动化的UI(集成)测试以及普通的单元测试。 我想将所有这些集成到我的Maven构建中,并提供代码覆盖率报告,以便我可以了解测试覆盖率不足的区域。 我不仅发布了项目的源代码,还整理了一个…

python学生分布_Python数据分析实战之分布分析

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。 作者:严小样儿 分布分析法,一般是根据分析目的,将数据进行分组,研究各组别分布规律的一种分析方法。…

hls fifo_HLS优化方法DATAFLOW你用了吗

上期内容:异步跨时钟域电路该怎么约束DATAFLOW作为HLS的一种优化方法,对于改善吞吐率(Throughput)、降低延迟(Latency)非常有效。DATAFLOW的作用对象DATAFLOW可以作用于函数,也可以作用于for循环。如下图所示(图片来源Figure62, Figure 63, u…

在Hibernate,EhCache,Quartz,DBCP和Spring中启用JMX

继续使用JMX的过程(请参阅: 人类JMX ),我们将学习如何在一些流行的框架中启用JMX支持(通常是统计和监视功能)。 这些信息大部分都可以在项目的主页上找到,但是我决定在收集这些信息的同时&#…

400多万微信用户如何“变现”?凯叔说了五大秘诀与教训

凯叔,原名王凯,自媒体“凯叔讲故事”创始人,近日在狮享家班委会上做了分享,全是实实在在的实验性方法论。以下是王凯的分享内容,整理 / 垅青 我讲的主题叫“基于内容的MVP探索”,MVP是什么东西?…

使用模拟的单元测试–测试技术5

我的最后一个博客是有关测试代码方法的一系列博客中的第四篇,演示了如何创建使用存根对象隔离测试对象的单元测试。 今天的博客探讨了有时被视为对立的技术:使用模拟对象进行单元测试。 同样,我使用了从数据库检索地址的简单方案:…

多线程中的volatile和伪共享

伪共享 false sharing,顾名思义,“伪共享”就是“其实不是共享”。那什么是“共享”?多CPU同时访问同一块内存区域就是“共享”,就会产生冲突,需要控制协议来协调访问。会引起“共享”的最小内存区域大小就是一个cache…

C语言代码规范(一)缩进与换行

一、缩进的空格数为4个。最好配置代码编辑器将TAB键设置为空格替换&#xff0c;避免出现另一个编辑器打开时格式变乱的情况。 例如Notepad设置 KEIL设置 二、“{” 和 “}”各自独占一行。 不规范例子&#xff1a; for(i 0; i < student_num; i) { if((score[i] > 0…

armv7 cortex a系列编程手册_AWTK能为现代GUI编程带来何种改变?

AWTK是一个伸缩性极强的嵌入式图形框架&#xff0c;它的诞生会给GUI编程研发工程师带来哪些改变&#xff1f;AWTK是一个伸缩性极强的嵌入式图形框架&#xff0c;可在Cortex-M3这样低端的单片机上运行&#xff0c;也可以在Cortex-A7/A8/A9等处理器&#xff0c;甚至DSP以及X86处理…

为什么要编写单元测试–测试技巧8

我对最近在“您应该测试什么”上的博客有很多反应&#xff0c;有些人出于各种原因同意我的想法&#xff0c;另一些人则认为建议某些类可能不需要单元测试是非常危险的。 已经处理了什么测试&#xff0c;今天的博客涉及为什么要编写单元测试&#xff0c;而今天的示例代码是基于一…

c++ 多重背包状态转移方程_动态规划入门——详解经典问题零一背包

本文始发于个人公众号&#xff1a;TechFlow&#xff0c;原创不易&#xff0c;求个关注今天是周三算法与数据结构专题的第12篇文章&#xff0c;动态规划之零一背包问题。在之前的文章当中&#xff0c;我们一起探讨了二分、贪心、排序和搜索算法&#xff0c;今天我们来看另一个非…

python定义一个圆_Python-矩形和圆形

原博文 2019-11-11 12:34 − Exercise 15.1. 定义一个叫做Circle 类&#xff0c;类的属性是圆心 (center) 和半径 (radius) , 其中&#xff0c;圆心 (center) 是一个 Point 类&#xff0c;而半径 (radius) 是一个数字。 实例化一个圆心 (center) 为 (150, 100) &#xff0c;半…

STM32F1笔记(一)GPIO输出

GPIO&#xff1a;General Purpose Input Output &#xff08;通用输入/输出&#xff09;。 GPIO最经典应用&#xff1a;LED灯。 先看电路。声明&#xff1a;参考正点原子战舰开发板。 与LED串联的电阻称为限流电阻。 限流电阻计算公式&#xff1a;R(U-LED压降)/20ma。 U为LE…

dataframe转化为array_【Python专栏】12 种高效 Numpy 和 Pandas 函数为你加速分析

来源&#xff1a;机器之心编译&#xff1a;Jamin、杜伟、张倩我们都知道&#xff0c;Numpy 是 Python 环境下的扩展程序库&#xff0c;支持大量的维度数组和矩阵运算&#xff1b;Pandas 也是 Python 环境下的数据操作和分析软件包&#xff0c;以及强大的数据分析库。二者在日常…

具有GlassFish和一致性的高性能JPA –第1部分

您以前听说过连贯性吗&#xff1f; 大概是。 它是那些著名的内存网格解决方案之一&#xff0c;该解决方案承诺了超快的数据访问速度和对经常使用的数据的无限空间。 一些众所周知的竞争对手是Infinispan &#xff0c; Memcached和Terracotta Ehcache 。 它们都很棒&#xff0c;…

boost原理与sklearn源码_机器学习sklearn系列之决策树

一、 Sklearn库 Scikit learn 也简称 sklearn, 自2007年发布以来&#xff0c;scikit-learn已经成为Python重要的机器学习库了。支持包括分类、回归、降维和聚类四大机器学习算法。还包含了特征提取、数据处理和模型评估三大模块。sklearn是Scipy的扩展&#xff0c;建立在NumPy和…

STM32F1笔记(二)GPIO输入

STM32 GPIO输入的经典应用是按键。 先看电路。声明&#xff1a;参考正点原子战舰开发板。 在这里可以看到&#xff0c;KEY_UP按键是高电平有效的&#xff0c;即当按下该按键时&#xff0c;GPIO读到高电平。 KEY0/1/2是低电平有效的&#xff0c;即当按下该按键时&#xff0c;G…