tomcat原理刨析之手写tomcat

渗透技巧 2年前 (2022) admin
508 0 0

tomcat源码剖析

socket编程

tomcat其实就是一个网络请求处理程序,如果需要重写,我们需要通过socket编程进行接受数据与发送数据。

InetAddress类

getLocalHost  #获取本机InetAddress对象
getByName  #根据指定主机名/域名获取ip地址对象
getHostName  #获取inetAddress对象的主机名
getHostAddress  #获取InetAddress对象的地址

 

package com.socke.demo;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
System.out.println(“*******************************”);

//根据主机名/域名获取uo地址对象
InetAddress host1 = InetAddress.getByName(“DESKTOP-MSCGOJK”);
System.out.println(host1);
System.out.println(“*******************************”);
InetAddress host2 = InetAddress.getByName(“www.baidu.com”);
System.out.println(host2);

//获取InetAddress对象的主机名
String hostName = host2.getHostName();
System.out.println(hostName);
System.out.println(“*******************************”);

//获取InetAddress对象的地址
String hostAddress = host2.getHostAddress();
System.out.println(hostName);
System.out.println(“*******************************”);
}

}

结果

DESKTOP-MSCGOJK/192.168.0.108
*******************************
DESKTOP-MSCGOJK/192.168.0.108
*******************************
www.baidu.com/39.156.66.14
www.baidu.com
*******************************
www.baidu.com
*******************************

Process finished with exit code 0

 

ServerSocket类

构造方法
ServerSocket(int port)  #创建绑定到特定端口的服务器套接字
ServerSocket(int port, int backlog)  #用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号
ServerSocket(int port, int backlog, InetAddress address)  #使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
ServerSocket()  #创建非绑定服务器套接字

常用方法
getLocalPort()  #返回此套接字在其上侦听的端口
accept()  #侦听并接受到此套接字的连接
setSoTimeout(int timeout)  #通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位
bind(SocketAddress host, int backlog)  #将 ServerSocket 绑定到特定地址(IP 地址和端口号)

 

package com.socke.demo;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServerTcpDemo {
public static void main(String[] args) throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.有链接,则返回Socket对象
System.out.println(“socket=” + socket.getClass());
}

}

 

开启程序,并通过浏览器服务127.0.0.1:9999

tomcat原理刨析之手写tomcat

可以看到这里控制台返回了数据,证明链接成功,因此我们可以找到浏览器其实就是一个客户端,重写tomcat的第一步我们就已经算是成功了,及与浏览器交互

Socket类

构造方法
Socket(String host, int port)  #创建一个流套接字并将其连接到指定主机上的指定端口号
Socket(InetAddress host, int port)  #创建一个流套接字并将其连接到指定 IP 地址的指定端口号
Socket(String host, int port, InetAddress localAddress, int localPort)  #创建一个套接字并将其连接到指定远程主机上的指定远程端口
Socket(InetAddress host, int port, InetAddress localAddress, int localPort)  #创建一个套接字并将其连接到指定远程地址上的指定远程端口
Socket()  #通过系统默认类型的 SocketImpl 创建未连接套接字

常用方法
connect(SocketAddress host, int timeout)  #将此套接字连接到服务器,并指定一个超时值
getInetAddress()  # 返回套接字连接的地址
getPort()  #返回此套接字连接到的远程端口
getLocalPort()  #返回此套接字绑定到的本地端口
getRemoteSocketAddress()  #返回此套接字连接的端点的地址,如果未连接则返回 null
getInputStream()  #返回此套接字的输入流
getOutputStream()  #返回此套接字的输出流
close()  #关闭此套接字

 

建议的客户端

package com.socke.demo;

import java.io.IOException;
import java.net.Socket;

public class SocketClientTcpDemo {
public static void main(String[] args) throws IOException {
//1.连接服务端(ip,端口)
Socket socket = new Socket(“127.0.0.1”,9999);
//2.返送数据
socket.getOutputStream().write(“6666”.getBytes());
//3.关闭socket
socket.close();
}

}

 

带回显内容的服务端

package com.socke.demo;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServerTcpDemo {
public static void main(String[] args) throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.有链接,则返回Socket对象
System.out.println(“socket=” + socket.getClass());
//4.读取数据
InputStream inputStream = socket.getInputStream();
int readLen = 0;
byte[] buf = new byte[1024];
while((readLen = inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
//5.关闭资源
socket.close();
inputStream.close();
serverSocket.close();

}

}

启动服务,并利用浏览器服务127.0.0.1:9999

tomcat原理刨析之手写tomcat

可以看到返回的内容为一个http请求头,还请求头是由浏览器生成发送的,但是由于我们并没有,返回数据给浏览器因此,浏览器一直获取不到任何内容,处于加载状态,接下来就是,返回数据至浏览器上

自写tomcat之实现与浏览器交互

SocketServerTcpDemo

程序入口,只要用于实现多线程

package com.demo;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SocketServerTcpDemo {
public final static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(25,100,60,TimeUnit.SECONDS,new LinkedBlockingQueue());

public static void start() throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
while(true){
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.并行,线程池
threadPoolExecutor.execute(new SocketProcessor(socket));
}
}

public static void main(String[] args) throws IOException {
start();
}

}

 

SocketProcessor

socket进程任务,为socket数据发送与处理

package com.demo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.Socket;

public class SocketProcessor implements Runnable{

Socket connection;

public SocketProcessor(Socket connection){
this.connection = connection;
}
public void run() {
byte[] requestBody = new byte[1024];
try {
connection.getInputStream().read(requestBody);
catch (IOException e) {
e.printStackTrace();
}
String requestString = new String(requestBody);
//调用http工厂类,处理http请求
HttpFactory httpFactory = new HttpFactory();
HttpServletRequest httpServletRequest = httpFactory.createRequest(requestString.getBytes());
//获取请求方法和url
System.out.println(httpServletRequest.getServletPath());
System.out.println(httpServletRequest.getMethod());

//发送响应
HttpServletResponse httpServletResponse = httpFactory.createResponse(connection);
try {
String content = “输入的uri为:”+httpServletRequest.getServletPath()+“输入的参数为”+httpServletRequest.getParameter(“name”);
httpServletResponse.getOutputStream().write(“HTTP/1.1 200 OKrn”.getBytes());
httpServletResponse.getOutputStream().write((“Content-type: text/plain;charset=utf-8rn”).getBytes());
httpServletResponse.getOutputStream().write((“Content-Length: “+(content.getBytes(“utf-8”).length)+“rnrn”).getBytes());
httpServletResponse.getOutputStream().write(content.getBytes());

catch (IOException e) {
e.printStackTrace();
}

}
}

 

HttpFactory

http工厂类,用于将socket数据进行封装成http数据,方便发送以及处理

package com.demo;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.security.Principal;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;

public class HttpFactory{
public HttpServletRequest createRequest(final byte[] requestBody){
return new HttpServletRequest() {
….
//获取请求方法
public String getMethod() {
String requestString = new String(requestBody);
return requestString.split(“rn”)[0].split(” “)[0];

}
….

//获取请求路径
public String getServletPath() {
String requestString = new String(requestBody);
return requestString.split(“rn”)[0].split(” “)[1];
}
….
};
//获取参数内容
public String getParameter(String s) {
String requestString = new String(requestBody);
String uri =  requestString.split(“rn”)[0].split(” “)[1];
System.out.println(uri);
String parameters = uri.split(“\?”)[1];
String[] keyValues = parameters.split(“&”);
Map<String,String> map = new HashMap<String, String>();
for(String str : keyValues){
System.out.println(str.split(“=”)[0]);
map.put(str.split(“=”)[0],str.split(“=”)[1]);
}
return map.get(s);
}

}

public HttpServletResponse createResponse(final Socket connect){
return new HttpServletResponse() {
….
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream(){
public void write(int b) throws IOException {
connect.getOutputStream().write(b);
}
};
}
….
};
}

效果

tomcat原理刨析之手写tomcat

这里由于需要重写的方法太多,在这里就只重写了,后面经常用到的方法

到目前为止我们的tomcat已经可以完整的获取,处理,发送http数据。

项目找寻

在前面我们已经解决了网络通信的问题,接下来就是对接项目,及通过url访问我们的serverlet

ProjectConfigBean

ProjectConfigBean,通过读取xml获取serverlet信息

package com.demo;

import jdk.internal.org.xml.sax.helpers.DefaultHandler;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.demo.BootStraper.work_space;

public class ProjectConfigBean extends DefaultHandler {

//Servlet 集合
Map<String,String> servlets = new HashMap<String, String>();

//Servlet 实例化
Map<String,Object> servletInstances = new HashMap<String, Object>();

//servlet 映射
Map<String,String> servletMapping = new HashMap<String, String>();

//项目
private String project;

//xml文件地址
private String xmlDir;

public ProjectConfigBean(String project) throws IOException, JDOMException {
//1.创建SAXBuilder对象
SAXBuilder saxBuilder = new SAXBuilder();
//2.创建输入流
InputStream is = new FileInputStream(new File(work_space+project+“/WEB-INF/web.xml”));
//3.将输入流加载到build中
Document document = saxBuilder.build(is);
//4.获取根节点
Element rootElement = document.getRootElement();
//5.获取子节点
List<Element> children = rootElement.getChildren();
for (Element child : children) {
//获取<servlet>中的标签
List<Element> childrenList = child.getChildren();
//判断servlet以及servlet-mapping
if(child.getName().equals(“servlet”)){
//将servlet添加至servlets
servlets.put(childrenList.get(0).getValue(),childrenList.get(1).getValue());

}
else if (child.getName().equals(“servlet-mapping”)){
//将servlet添加至servletMapping
servletMapping.put(childrenList.get(1).getValue(),childrenList.get(0).getValue());
}
}

}

public ProjectConfigBean loadXml() {
return this;
}

}

ProjectLoader

加载依赖以及class文件

package com.demo;

import org.jdom.JDOMException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Map;

public class ProjectLoader {
//项目地址
private String project;
//类加载器
URLClassLoader loader = null;

public ProjectLoader(String project) throws MalformedURLException {
this.project = project;

//创建一个存储需要加载的class和jar包的数组
ArrayList<URL> urls = new ArrayList<URL>();

//读取lib下的jar包列表
File libs = new File(BootStraper.work_space+“/”+project+“WEB-INF/lib”);
if(libs.exists()){
for (String lib: libs.list()){
urls.add(new URL(“file:”+BootStraper.work_space+“/”+project+“WEB-INF/lib/”+lib));
}
}
//读取classes文件列表
urls.add(new URL(“file:”+BootStraper.work_space+“/”+project+“WEB-INF/classes/”));

//加载项目的class和jar包
this.loader = new URLClassLoader(urls.toArray(urls.toArray(new URL[]{})));
}

//加载类
public ProjectLoader load(ProjectConfigBean projectConfigBean) throws IOException, JDOMException, ClassNotFoundException, IllegalAccessException, InstantiationException, ServletException {
//设置所有线程都使用刚创建的加载器
Thread.currentThread().setContextClassLoader(this.loader);
//遍历出所有的类
for (Map.Entry<String,String> entry:projectConfigBean.servlets.entrySet()){
//利用反射加载对象
Class<?> clazz = loader.loadClass(entry.getValue());
//实例化
Servlet servlet = (Servlet) clazz.newInstance();
//servlet初始化
servlet.init(new ServletConfig() {
public String getServletName() {
return null;
}

public ServletContext getServletContext() {
return null;
}

public String getInitParameter(String s) {
return null;
}

public Enumeration<String> getInitParameterNames() {
return null;
}
});
//保存对象,如果不保存在projectConfigBean中,创建的对象就会被销毁掉,前面做的所有都将是徒劳
projectConfigBean.servletInstances.put(entry.getKey(),servlet);
}
return this;
}
}

 

SocketProcessor

关联servlet和url

package com.demo;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.Socket;

public class SocketProcessor implements Runnable{

Socket connection;

public SocketProcessor(Socket connection){
this.connection = connection;
}
public void run() {
byte[] requestBody = new byte[1024];
try {
connection.getInputStream().read(requestBody);
catch (IOException e) {
e.printStackTrace();
}
String requestString = new String(requestBody);
//调用http工厂类,处理http请求
HttpFactory httpFactory = new HttpFactory();
HttpServletRequest httpServletRequest = httpFactory.createRequest(requestString.getBytes());
//获取请求方法和url
System.out.println(httpServletRequest.getServletPath());
System.out.println(httpServletRequest.getMethod());

//发送响应
HttpServletResponse httpServletResponse = httpFactory.createResponse(connection);

//获取请求路径,匹配servlet
try {
BootStraper bootStraper = new BootStraper();
// 1.处理/
String servletName = bootStraper.projectConfigBeans.get(“”).servletMapping.get(“/”);

//2.通过url加载
if(servletName == null){
servletName = bootStraper.projectConfigBeans.get(“”).servletMapping.get(httpServletRequest.getServletPath());
}
//3.如果没有找到
if (servletName == null){
try {
httpServletResponse.getOutputStream().write(“404”.getBytes());
catch (IOException e) {
e.printStackTrace();
}
}

//通过Servlet名称,获取对应的servlet实例
Servlet servlet = (Servlet)bootStraper.projectConfigBeans.get(“”).servletInstances.get(servletName);

//将httpServletRequest,httpServletResponse传入servlet
try {
servlet.service(httpServletRequest,httpServletResponse);
catch (ServletException e) {
e.printStackTrace();
catch (IOException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}

}
}

SocketServerTcpDemo

package com.demo;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SocketServerTcpDemo {
public final static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(25,100,60,TimeUnit.SECONDS,new LinkedBlockingQueue());

public static void start() throws IOException {
//1.在本机的9999端口监听,
ServerSocket serverSocket = new ServerSocket(9999);
while(true){
//2.接受数据,如果没有数据接受会进行阻塞
Socket socket = serverSocket.accept();
//3.并行,线程池
threadPoolExecutor.execute(new SocketProcessor(socket));
}
}

public static void main(String[] args) throws IOException {
start();
}
}

MyServlet

package com.naihe;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException 
{
String content = “这里是MyServlet,输入的uri为:”+req.getServletPath()+“输入的参数为”+req.getParameter(“name”);
resp.getOutputStream().write(“HTTP/1.1 200 OKrn”.getBytes());
resp.getOutputStream().write((“Content-type: text/plain;charset=utf-8rn”).getBytes());
resp.getOutputStream().write((“Content-Length: “+(content.getBytes(“utf-8”).length)+“rnrn”).getBytes());
resp.getOutputStream().write(content.getBytes());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException 
{
resp.getOutputStream().write(“666”.getBytes());
}
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

<!–注册Servlet–>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.naihe.MyServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>hello2</servlet-name>
<servlet-class>com.naihe.MyServlet</servlet-class>
</servlet>
<!–Servlet的请求路径–>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
</web-app>

tomcat原理刨析之手写tomcat

加下方wx,拉你一起进群学习

tomcat原理刨析之手写tomcat

往期推荐

ETW的攻与防

 

SEH异常之编译器原理探究

 

初探UAF漏洞

 

什么?你还不会webshell免杀?(三)

 

初探栈溢出

 

windows环境下的自保护探究

 

记一次内部红队渗透——定位张三

 

对抗无落地的shellcode注入

 

 

tomcat原理刨析之手写tomcat

原文始发于微信公众号(红队蓝军):tomcat原理刨析之手写tomcat

版权声明:admin 发表于 2022年7月21日 上午9:01。
转载请注明:tomcat原理刨析之手写tomcat | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...