1. 程式人生 > >域名劫持原理及實現

域名劫持原理及實現

目錄

1.從輸入URL到頁面載入發生了什麼

總體來說分為以下幾個過程:

1.1 DNS解析

1.2 TCP連線

1.3傳送HTTP請求

它主要發生在客戶端。傳送HTTP請求的過程就是構建HTTP請求報文並通過TCP協議中傳送到伺服器指定埠(HTTP協議80/8080,HTTPS協議443)HTTP請求報文是由三部分組成:請求行,請求報頭和請求正文。

1.4伺服器處理請求並返回HTTP報文

後端從在固定的埠接收到TCP報文開始,這一部分對應於程式語言中的socket。它會對TCP連線進行處理,對HTTP協議進行解析,並按照報文格式進一步封裝成HTTPRequest物件,供上層使用。這一部分工作一般是由

Web伺服器去進行,HTTP響應報文也是由三部分組成:狀態碼,響應報頭和響應報文。

HTTP狀態碼由三個十進位制數字組成,第一個十進位制數字定義了狀態碼的型別,後兩個數字沒有分類的作用。HTTP狀態碼共分為5種類型:

1** 資訊,伺服器收到請求,需要請求者繼續執行操作

2** 成功,操作被成功接收並處理

3** 重定向,需要進一步的操作以完成請求

4** 客戶端錯誤,請求包含語法錯誤或無法完成請求

5** 伺服器錯誤,伺服器在處理請求的過程中發生了錯誤



1.5瀏覽器解析渲染頁面

瀏覽器在收到HTML,CSS,JS檔案後,它是如何把頁面呈現到螢幕上的?下圖對應的就是WebKit(一個開源的瀏覽器引擎)渲染的過程。


瀏覽器是一個邊解析邊渲染的過程。首先瀏覽器解析HTML檔案構建DOM樹,然後解析CSS檔案構建渲染樹,等到渲染樹構建完成後,瀏覽器開始佈局渲染樹並將其繪製到螢幕上。


JS的解析是由瀏覽器中的JS解析引擎完成的。JS的執行機制就可以看做是一個主執行緒加上一個任務佇列(taskqueue)。同步任務就是放在主執行緒上執行的任務,非同步任務是放在任務佇列中的任務。所有的同步任務在主執行緒上執行,形成一個執行棧;非同步任務有了執行結果就會在任務佇列中放置一個事件;指令碼執行時先依次執行執行棧,然後會從任務佇列裡提取事件,執行任務佇列中的任務,這個過程是不斷重複的,所以又叫做事件迴圈(Eventloop)。但是當文件載入過程中遇到

JS檔案,HTML文件會掛起渲染過程,不僅要等到文件中JS檔案載入完畢還要等待解析執行完畢,才會繼續HTML的渲染過程。原因是因為JS有可能修改DOM結構,這就意味著JS執行完成前,後續所有資源的下載是沒有必要的。


1.6連線結束

2. dns劫持

當用戶輸入一個URL時,想要能夠訪問我們的路由器管理介面首先就需要將改URLDNS解析到路由器的web伺服器地址上,這時候,我們需要dns劫持。Dns劫持中主要用到一個開源的軟體-dnsmasq

首先我們利用dnsmasq將自己的工作站配置為一個能夠解析開發域名的server,解析的ip地址設定為工作站的ip地址。利用dnsmasq建立了一個dnsmapping table,將www.baidu.com的域名解析為路由器管理介面地址192.168.2.1。這時候,通過www.baidu.com訪問時會轉跳至192.168.2.1


3. url重定向

url重定向的實現可以在前端實現或是後端實現。這時候web伺服器需要將指定的html返回給客戶端,比如我們的快速嚮導頁面或是首頁頁面。這就需要重新定向使用者輸入的url

3.1.前端實現

3.1.1 html頁面跳轉方式

可以使用htmlmeta標籤實現頁面的跳轉。


metahtml語言head區的一個輔助性標籤。meta標籤共有兩個屬性,它們分別是http-equiv屬性和name屬性,不同的屬性又有不同的引數值,這些不同的引數值就實現了不同的網頁功能。

http-equiv屬性:相當於http的檔案頭作用,它可以向瀏覽器傳回一些有用的資訊,以幫助正確和精確地顯示網頁內容,與之對應的屬性值為contentcontent中的內容其實就是各個引數的變數值。

<metahttp-equiv="引數"content="引數變數值">

3.1.2 JS頁面跳轉方式

1.使用window.location.href= "newurl"


也可以用window.location= "newurl"

2. 使用window.navigate

<script>

window.navigate("http://www.csdn.net");

</script>

3.2.後端實現

後端實現主要是通過響應頭中的http響應location欄位,令客戶端重定向至指定URL

資料互動過程

3.2.1 http訊息

HTTP是基於客戶端/服務端(C/S)的架構模型,通過一個可靠的連結來交換資訊,是一個無狀態的請求/響應協議。

一個HTTP"客戶端"是一個應用程式(Web瀏覽器或其他任何客戶端),通過連線到伺服器達到向伺服器傳送一個或多個HTTP的請求的目的。一個HTTP"伺服器"同樣也是一個應用程式(通常是一個Web服務,如ApacheWeb伺服器或IIS伺服器等),通過接收客戶端的請求並向客戶端傳送HTTP響應資料。

HTTP協議的請求和響應都是一段按一定規則組織起來的文字,其請求的頭部包括請求行(請求方式method、請求的路徑path、協議版本protocol),請求頭標(一系列keyvalue形式組織的文字行),空行(分隔請求頭部與資料)和請求資料。

1. 客戶端請求

客戶端傳送一個HTTP請求到伺服器的請求訊息包括以下格式:請求行(requestline)、請求頭部(header)、空行和請求資料四個部分組成。


2. 伺服器響應訊息

HTTP響應也由四個部分組成,分別是:狀態行、訊息報頭、空行和響應正文。


3.2.2 http訊息原始碼分析

1 客戶端請求解析

客服端的請求處理其實就是將請求拆解,分解各個欄位,提取出header中的資訊。

首先,uhttpd會將收到的請求存放在一個buffer中。在uhttpd中有一個狀態機來處理http請求


這三個狀態分別用來處理客服端請求中的請求行(requestline)、請求頭部(header)、請求資料。


Uhttpd中預設的需要獲取的http請求包括以下欄位:


1.CLIENT_STATE_INIT

狀態-處理請求行,在Init狀態中,呼叫staticbool client_init_cb(struct client *cl, char *buf, int len)函式來method,url, version


獲取成功後將狀態變為CLIENT_STATE_HEADER

2. CLIENT_STATE_HEADER

狀態--處理請求頭部,呼叫staticbool client_header_cb(struct client *cl, char *buf, intlen)函式來解析requestheader

解析的方式就是staticbool client_header_cb(struct client *cl, char *buf, intlen);函式中通過/r/n作為標誌將buffer中的資料一行一行讀入。然後將每一行資料通過“:”為標誌存到結構體中傳入staticvoid client_parse_header(struct client *cl, char *data);函式中來獲取檔案頭。

這兩個函式中將buffer中的httpheader按照字串解析的方式取出有用資訊,存放到client結構體中。當buffer中全部解析完成之後狀態切換到CLIENT_STATE_DATA

3. CLIENT_STATE_DATA

處理請求資料,呼叫函式voidclient_poll_post_data(struct client *cl)

沒看明白……大概是按照content-length取出資料。╮(╯_╰)╭

2 伺服器響應訊息處理

Uhttpd在完成CLIENT_STATE_HEADER處理的時候會呼叫uh_handle_request(cl)函式來處理客戶端的請求。


響應主要是處理url並返回狀態碼。響應的處理主要在file.c檔案中進行處理。簡單的就是講url當做是相對於www資料夾的檔案路徑來查詢檔案。比如p.to/cgi-bin,其中“/cgi-bin”就會進入file.c檔案處理。在www資料夾下尋找cgi-bin

file.c檔案的函式入口在voiduh_handle_request(struct client *cl)


在這個函式中呼叫staticbool __handle_file_request(struct client *cl, char *url)來處理請求;

其中又呼叫了static struct path_info *uh_path_lookup(struct client*cl, const char *url)函式來尋找路徑。

其中,在uh_path_lookup()函式中,當url訪問的是一個目錄,但是url中沒有“/”的時候會轉跳到302,將url加上“/”


這裡面的path_phys[docroot_len]為根目錄,K2中就是www資料夾,預設將重定向到根資料夾中。

p.query ? "?" : "",

p.query ? p.query : "");用來提取query資訊,也就是url中的查詢資訊。

我們可以在這裡通過location欄位對url進行重定向。


附錄

1幾個重要的結構體

存放客戶端資料的結構體client.


其中,uh_addr結構體


可以用來表示一個32位的IPv4地址


得到local_addr,就是我們的lanip


存放http請求和響應的欄位

2配置引數讀入

Uhttpd的引數位於uhttpd.config檔案中。在main.cmain函式中通過while迴圈讀入配置引數;



函式中設定預設初始值。