浅析dir-815服务架构(一)
D-Link DIR-815栈溢出漏洞,现已是经典的入门实战案例。笔者在复现的过程中,总觉得只是一知半解,阅读源代码时,不知为何会有这些操作。因此,笔者希望能够真正明白该路由器的web服务原理,在以后分析其他路由器时,也能够有所经验。
请求网页时发生了什么
当我们输入一个URL后,浏览器就会渲染出一个页面,那么这个页面,以及其中的内容是怎么来的,我们不难从网络上查到以下历程。
- 浏览器通过DNS把域名解析成对应的IP地址。
- 浏览器和服务器建立连接(TCP/TP三次握手)。
- 浏览器向服务器发送HTTP请求。
- 服务器接受到请求并返回HTTP响应。
- 浏览器解析渲染页面。
第一个http请求报文
当我们输入一个URL,浏览器就会向通过DNS解析得到的ip地址发送第一个http请求。这里笔者以打开百度为例,使用brupsuite拦截了第一个请求报文。
GET / HTTP/1.1
Host: www.baidu.com
Cookie: ***
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Linux"
Upgrade-Insecure-Requests: 1
User-Agent: ***
Accept: text/html,application/xhtml+xml,application/xml; \
q=0.9,image/avif,image/webp,image/apng,*/*; \
q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
可以看出这个报文使用GET请求,请求资源为根 /,主机为www.baidu.com,没有其他什么特别的内容。
资源文件的获取
接下来笔者的一个疑问是,我们浏览器在打开页面时,是获取了一堆(包括.css,.js在内的)资源文件,然后渲染的页面,那么这些文件是如何获取、何时获取的呢?
当服务器接到我们GET请求的http报文后,会向我们发回一个http应答。这个应答包含响应头和主体内容,主体内容为html格式。
接下来浏览器就会尝试解析该html,构建DOM树,在浏览器解析的过程中,如果遇到link、script、img等标签,就会向服务器发送HTTP请求获取相应的资源文件。
当资源就位后,浏览器就会根据渲染树,绘制页面内容到屏幕上。
php
浏览器完成页面的渲染离不开html、css、js这前端三剑客的相互配合,即便在vue、react等前端框架盛行的当下,浏览器也需要这些框架导出的html、css、js文件。DIR-815路由器,使用php与cgi完成业务逻辑。这里简单介绍一下php的原理。
php语法
PHP 脚本以 结束:
<?php
function functionName()
{
echo "Hello World!";
}
?>
<!DOCTYPE html>
<html>
<body>
<h1>My first PHP page</h1>
<?php
echo "Hello World!";
?>
</body>
</html>
可以简单的理解php文件就是在html文件的基础上加了一些动态的内容,这些php的内容可以加在html中间,也可以在其前后。当客户端请求php文件后,php文件会根据请求的参数输出不同的内容,客户端得到回应是不含php语句的。
<!DOCTYPE html>
<html>
<body>
<h1>My first PHP page</h1>
Hello World!
</body>
</html>
ajax
分析固件导出的文件系统,我们可以发现对hedwig.cgi请求,在文件/htdocs/web/js/comm.js
中,是通过ajax实现的。
function COMM_CallHedwig(xml, resultCallback)
{
var ajaxObj = GetAjaxObj("setData");
ajaxObj.createRequest();
ajaxObj.onCallback =
function (xml)
{
ajaxObj.release();
resultCallback(xml);
}
ajaxObj.setHeader("Content-Type", "text/xml");
ajaxObj.sendRequest("hedwig.cgi", xml.XDoc);
}
ajax并不是一种编程语言,它是web2.0引入的一种写在js中,不需要刷新整个页面就可以更新页面。它允许js向服务器发送http报文,根据回复更新网页的部分信息。
ajax使用
我们知道js可以给一个按钮绑定一个回调函数,下面就是一个使用ajax作为回调函数的例子。
function showHint(str)
{
var xmlhttp;
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","/try/ajax/gethint.php?q="+str,true);
xmlhttp.send();
}
ajax使用XMLHttpRequest()
来实现,首先编写了一个onreadystatechange
回调函数作为获得服务器相应后的处理方式,这里即将ID为txtHint
元素的内容,修改为了xmlhttp
的responseText
。xmlhttp.open
设置了请求内容,xmlhttp.send()
则发起了请求。
ajax在dir-815中的实现
function COMM_CallHedwig(xml, resultCallback)
{
var ajaxObj = GetAjaxObj("setData");
ajaxObj.createRequest();
ajaxObj.onCallback =
function (xml)
{
ajaxObj.release();
resultCallback(xml);
}
ajaxObj.setHeader("Content-Type", "text/xml");
ajaxObj.sendRequest("hedwig.cgi", xml.XDoc);
}
COMM_CallHedwig
即可作为一个回调函数,只不过它将获取xml响应后的操作,抽象了出来,可以由其他模块调用时填入。这里的GetAjaxObj()
函数,会返回对应传入参数的HTTPClient()
对象,如果之前没有,没有就会新创建一个。ajaxObj.createRequest()
创建了一个XMLHttpRequest()
对象,接下来将定义onCallback
,在release
后,调用resultCallback(xml)
,使用COMM_CallHedwig(xml, resultCallback)
传入的对象。ajaxObj.setHeader
设置header,最后ajaxObj.sendRequest("hedwig.cgi", xml.XDoc)
发送请求。
我们接下来看一下HTTPClient()
对象,这里我省去部分内容,只保留上述结果使用了的部分。
function HTTPClient(){}
HTTPClient.prototype =
{
debug: false,
__httpRequest : null,
requestMethod : "POST",
requestAsyn : true,
returnXml : true,
__header : null,
onSend : null,
onCallback : null,
onError : function (msg){......},
__callback : function(){
......
this.onCallback,
......
},
createRequest : function()
{
try
{
this.__httpRequest = new XMLHttpRequest();
}
catch (e){......}
},
sendRequest : function(requestUrl, payload)
{
if (!this.__httpRequest) this.createRequest();
var self = this;
this.__httpRequest.onreadystatechange = function() {self.__callback();}
if (!requestUrl)
{
this.onError("Error : Invalid request URL.");
return;
}
this.__httpRequest.open(this.requestMethod, requestUrl, this.requestAsyn);
if (this.__header)
{
for (var i = 0; i < this.__header.length; i+=1)
{
if (this.__header[i].value != "")
this.__httpRequest.setRequestHeader(this.__header[i].name, this.__header[i].value);
}
}
if (this.requestMethod == "GET" || this.requestMethod == "get")
this.__httpRequest.send(null);
else
{
if (!payload)
{
this.onError("Error : Invalid payload for POST.");
return;
}
this.__httpRequest.send(payload);
}
},
getResponseHeader : function(header){......},
getAllResponseHeaders : function(){......},
setHeader : function(header, value)
{
if (header && value)
{
if (!this.__header) this.__header = new Array();
var tmpHeader = new Object();
tmpHeader.name = header;
tmpHeader.value = value;
this.__header[ this.__header.length ] = tmpHeader;
}
},
clearHeader : function (header){......}
clearAllHeaders : function(){......}
release : function(){......}
};
我们看sendRequest("hedwig.cgi", xml.XDoc)
传入参数的处理方式。其中"hedwig.cgi"
在this.__httpRequest.open(this.requestMethod, requestUrl, this.requestAsyn)
也就是通过XMLHttpRequest()
的open使用,HTTPClien
默认使用POST方法,因此最终调用this.__httpRequest.send(payload)
使用xml.XDoc
。XMLHttpRequest()
是浏览器内置的可调用对象,最终会向服务器发送http报文,我们不用去这个层面分析。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类