C++ LibCurl實現(xiàn)Web指紋識別功能
Web指紋識別是一種通過分析Web應(yīng)用程序的特征和元數(shù)據(jù),以確定應(yīng)用程序所使用的技術(shù)棧和配置的技術(shù)。這項技術(shù)旨在識別Web服務(wù)器、Web應(yīng)用框架、后端數(shù)據(jù)庫、JavaScript庫等組件的版本和配置信息。通過分析HTTP響應(yīng)頭、HTML源代碼、JavaScript代碼、CSS文件等,可以獲取關(guān)于Web應(yīng)用程序的信息。指紋識別在信息搜集、滲透測試、安全審計等方面具有重要作用。有許多開源和商業(yè)工具可以用于執(zhí)行Web指紋識別,例如Wappalyzer、WebScarab、Nmap等。
Web指紋識別的主要目的包括:
- 技術(shù)識別: 了解Web應(yīng)用程序所使用的服務(wù)器軟件、框架、數(shù)據(jù)庫等技術(shù)。
- 版本檢測: 確定這些技術(shù)的具體版本,有助于判斷應(yīng)用程序是否存在已知的漏洞。
- 配置檢測: 獲取Web應(yīng)用程序的配置信息,包括安裝路徑、默認(rèn)文件、目錄結(jié)構(gòu)等。
- 漏洞分析: 通過已知漏洞與特定版本相關(guān)聯(lián),評估Web應(yīng)用程序的安全性。
指紋識別是滲透測試中常用的一項技術(shù),用于識別目標(biāo)Web
應(yīng)用程序所使用的框架、技術(shù)和配置。其中,通過計算特定頁面的哈希值進行指紋識別是一種常見的方法,主要通過以下步驟實現(xiàn):
- 1.利用CURL庫獲取頁面內(nèi)容: 使用LibCURL庫可以方便地獲取目標(biāo)網(wǎng)站的頁面內(nèi)容,將其讀入到
std::string
字符串中,以便后續(xù)處理。 - 2.MD5算法計算哈希值: 對獲取的頁面內(nèi)容進行MD5哈希計算,得到一個唯一的哈希值。MD5是一種常用的哈希算法,將任意長度的數(shù)據(jù)映射成128位的哈希值,通常以16進制表示。
- 3.比對預(yù)先計算的框架頁面哈希值: 預(yù)先計算一些特定頁面的哈希值,這些頁面通常是目標(biāo)框架中相對獨立且不經(jīng)常變動的頁面。將獲取到的頁面的哈希值與預(yù)先計算的哈希值進行比對。
- 4.框架識別: 如果哈希值匹配,則說明目標(biāo)頁面的框架很可能是預(yù)先定義的框架,從而實現(xiàn)對框架的識別。比對的過程可以使用簡單的相等比對,也可以設(shè)置一定的相似度閾值。
通過計算頁面哈希值進行指紋識別是一種有效的方式,特別是針對那些相對穩(wěn)定的頁面。首先我們利用LibCURL
庫將目標(biāo)頁面讀入到std::string
字符串中,然后調(diào)用MD5算法計算出該頁面的HASH值并比對,由于特定框架中總是有些頁面不會變動,我們則去校驗這些頁面的HASH值,即可實現(xiàn)對框架的識別。
LibCURL讀入頁面
當(dāng)我們需要獲取遠(yuǎn)程服務(wù)器上的網(wǎng)頁內(nèi)容時,使用C++編寫一個簡單的程序來實現(xiàn)這個目標(biāo)是非常有用的。在這個例子中,我們使用了libcurl
庫,在程序中引入libcurl
庫的頭文件,并使用#pragma comment
指令引入相關(guān)的庫文件。接下來,我們定義了一個回調(diào)函數(shù)WriteCallback
,該函數(shù)將獲取的數(shù)據(jù)追加到一個std::string
對象中。
主要的功能實現(xiàn)在GetUrlPageOfString
函數(shù)中。該函數(shù)接受一個URL
作為參數(shù),并使用libcurl
庫來執(zhí)行HTTP GET
請求。我們通過設(shè)置CURLOPT_URL
選項來指定URL
路徑,同時關(guān)閉了SSL
證書驗證以及啟用了重定向。我們還設(shè)置了一些超時選項,以確保在連接或接收數(shù)據(jù)時不會花費太長時間。通過調(diào)用curl_easy_perform
執(zhí)行請求,并通過回調(diào)函數(shù)將獲取到的數(shù)據(jù)存儲在read_buffer
中。最后,我們輸出接收到的數(shù)據(jù)的長度。
#define CURL_STATICLIB #define BUILDING_LIBCURL #include <iostream> #include <string> #include "curl/curl.h" #pragma comment (lib,"libcurl_a.lib") #pragma comment (lib,"wldap32.lib") #pragma comment (lib,"ws2_32.lib") #pragma comment (lib,"Crypt32.lib") using namespace std; // 存儲回調(diào)函數(shù) size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); return size * nmemb; } // 獲取數(shù)據(jù)并放入string中. std::string GetUrlPageOfString(std::string url) { std::string read_buffer; CURL *curl; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略證書檢查 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // 重定向 curl_easy_setopt(curl, CURLOPT_URL, url); // URL路徑 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); // 查找次數(shù),防止查找太深 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); // 連接超時 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3); // 接收數(shù)據(jù)時超時設(shè)置 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); curl_easy_perform(curl); curl_easy_cleanup(curl); return read_buffer; } return "None"; } int main(int argc, char *argv[]) { std::string urls = GetUrlPageOfString("https://www.baidu.com"); std::cout << "接收長度: " << urls.length() << " bytes" << std::endl; system("pause"); return 0; }
運行上述代碼將會輸出訪問特定主機所接收到的流量字節(jié)數(shù),如下圖所示;
LibCURL獲取狀態(tài)碼
在這個C++程序中,我們使用了libcurl
庫來獲取指定URL的HTTP狀態(tài)碼。首先,我們引入libcurl
庫的頭文件,并通過#pragma comment
指令引入相關(guān)的庫文件。然后,我們定義了一個靜態(tài)的回調(diào)函數(shù)not_output
,該函數(shù)用于屏蔽libcurl
的輸出。
接著,我們定義了GetStatus
函數(shù),該函數(shù)接受一個URL
作為參數(shù),并返回該URL對應(yīng)的HTTP狀態(tài)碼。在函數(shù)中,我們使用curl_easy_setopt
設(shè)置了一些選項,包括URL、寫數(shù)據(jù)的回調(diào)函數(shù)(這里我們使用not_output
屏蔽輸出),以及通過curl_easy_getinfo
獲取狀態(tài)碼。
在main函數(shù)中,我們調(diào)用GetStatus
函數(shù)并輸出獲取到的狀態(tài)碼。這個例子非常簡單,但展示了使用libcurl
庫獲取HTTP狀態(tài)碼的基本方法。
#define CURL_STATICLIB #define BUILDING_LIBCURL #include <iostream> #include <string> #include "curl/curl.h" #pragma comment (lib,"libcurl_a.lib") #pragma comment (lib,"wldap32.lib") #pragma comment (lib,"ws2_32.lib") #pragma comment (lib,"Crypt32.lib") using namespace std; // 屏蔽輸出 static size_t not_output(char *d, size_t n, size_t l, void *p){ return 0; } // 獲取狀態(tài)碼 long GetStatus(std::string url) { CURLcode return_code; long retcode = 0; return_code = curl_global_init(CURL_GLOBAL_WIN32); if (CURLE_OK != return_code) return 0; CURL *easy_handle = curl_easy_init(); if (NULL != easy_handle) { curl_easy_setopt(easy_handle, CURLOPT_URL, url); // 請求的網(wǎng)站 curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, not_output); // 設(shè)置回調(diào)函數(shù),屏蔽輸出 return_code = curl_easy_perform(easy_handle); // 執(zhí)行CURL return_code = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &retcode); if ((CURLE_OK == return_code) && retcode) { return retcode; } } curl_easy_cleanup(easy_handle); curl_global_cleanup(); return retcode; } int main(int argc, char *argv[]) { long ref = GetStatus("https://www.baidu.com/"); std::cout << "返回狀態(tài)碼: " << ref << std::endl; system("pause"); return 0; }
運行上述代碼可得到特定網(wǎng)址的狀態(tài)碼信息,圖中200表示訪問正常;
計算字符串Hash值
我們使用Boost
庫中的boost/crc.hpp
和boost/uuid/detail/md5.hpp
來計算CRC32和MD5值。首先,定義GetCrc32
函數(shù),該函數(shù)接受一個字符串作為輸入,使用Boost
庫中的crc_32_type
計算字符串的CRC32
值。
接著,我們定義了GetMd5
函數(shù),該函數(shù)接受一個字符數(shù)組和其大小作為輸入,使用Boost
庫中的boost::uuids::detail::md5
計算字符串的MD5值。在這個例子中,我們使用了Boost的md5實現(xiàn)。
在main函數(shù)中,我們創(chuàng)建了一個測試字符串"hello lyshark",并分別調(diào)用GetMd5和GetCrc32函數(shù)來計算其MD5和CRC32值。最后,我們輸出計算得到的MD5和CRC32值。
#define CURL_STATICLIB #define BUILDING_LIBCURL #include <iostream> #include <string> #include <boost/crc.hpp> #include <boost/uuid/detail/md5.hpp> #include <boost/algorithm/hex.hpp> using namespace std; using namespace boost; // 應(yīng)用于crc32 long GetCrc32(std::string sz_string) { long ref; crc_32_type crc32; cout << hex; crc32.process_bytes(sz_string.c_str(), sz_string.length()); return crc32.checksum(); } // 應(yīng)用于md5 std::string GetMd5(const char * const buffer, size_t buffer_size) { if (buffer == nullptr) return false; std::string str_md5; boost::uuids::detail::md5 boost_md5; boost_md5.process_bytes(buffer, buffer_size); boost::uuids::detail::md5::digest_type digest; boost_md5.get_digest(digest); const auto char_digest = reinterpret_cast<const char*>(&digest); str_md5.clear(); boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(str_md5)); return str_md5; } int main(int argc, char *argv[]) { std::string urls = "hello lyshark"; std::cout << "計算Hash: " << urls << std::endl; // 計算MD5 std::string str = GetMd5(urls.c_str(), urls.length()); std::cout << "計算 MD5: " << str << std::endl; // 計算CRC32 long crc = GetCrc32(urls.c_str()); std::cout << "計算 CRC32: " << crc << std::endl; system("pause"); return 0; }
通過計算可得到hello lyshark
字符串的CRC32與MD5特征碼,如下圖;
當(dāng)具備了hash值的計算后,我們只需要將上述兩個功能組合起來就可以實現(xiàn)提取特定頁面的特征碼,首先通過libcurl
庫完成對頁面的訪問,接著就是計算特征碼即可。
#define CURL_STATICLIB #define BUILDING_LIBCURL #include <iostream> #include <string> #include "curl/curl.h" #include <boost/crc.hpp> #include <boost/uuid/detail/md5.hpp> #include <boost/algorithm/hex.hpp> #pragma comment (lib,"libcurl_a.lib") #pragma comment (lib,"wldap32.lib") #pragma comment (lib,"ws2_32.lib") #pragma comment (lib,"Crypt32.lib") using namespace std; // 存儲回調(diào)函數(shù) size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); return size * nmemb; } // 獲取數(shù)據(jù)并放入string中. std::string GetUrlPageOfString(std::string url) { std::string read_buffer; CURL *curl; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略證書檢查 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // 重定向 curl_easy_setopt(curl, CURLOPT_URL, url); // URL路徑 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); // 查找次數(shù),防止查找太深 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); // 連接超時 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3); // 接收數(shù)據(jù)時超時設(shè)置 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); curl_easy_perform(curl); curl_easy_cleanup(curl); return read_buffer; } return "None"; } // 應(yīng)用于md5 std::string GetMd5(const char * const buffer, size_t buffer_size) { if (buffer == nullptr) return false; std::string str_md5; boost::uuids::detail::md5 boost_md5; boost_md5.process_bytes(buffer, buffer_size); boost::uuids::detail::md5::digest_type digest; boost_md5.get_digest(digest); const auto char_digest = reinterpret_cast<const char*>(&digest); str_md5.clear(); boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(str_md5)); return str_md5; } // 計算特定頁面MD5 std::string CalculationMD5(std::string url) { std::string page_string = GetUrlPageOfString(url); if (page_string != "None") { std::string page_md5 = GetMd5(page_string.c_str(), page_string.length()); std::cout << "[+] 計算頁面: " << url << std::endl; std::cout << "[+] 壓縮數(shù)據(jù): " << page_md5 << std::endl; return page_md5; } return "None"; } int main(int argc, char *argv[]) { std::string md5 = CalculationMD5("https://www.baidu.com"); system("pause"); return 0; }
上述代碼運行后,則可以計算出特定網(wǎng)站的MD5值,如下圖;
解析對比Hash值
指紋識別依賴于特征庫,如果需要實現(xiàn)自己的指紋識別工具則需要我么能自行去收集各類框架的特征庫,有了這些特征庫就可以定義一個如下所示的JSON文本,該文本中container
用于存儲框架類型,其次hash
則用于存放特征碼,最后的sub_url
則是識別路徑。
{ "data_base": [ { "container": "typecho", "hash": "04A40072CDB70B1BF54C96C6438678CB" ,"sub_url":"/index.php/about.html" }, { "container": "wordpress", "hash": "04A40072CBB70B1BF54C96C6438678CB" ,"sub_url":"/admin.php" }, { "container": "baidu", "hash": "EF3F1F8FBB7D1F545A75A83640FF0E9F" ,"sub_url":"/index.php" } ] }
接著就是解析這段JSON
文本,我們利用BOOST
提供的JSON
解析庫,首先解析出所有的鍵值對,將其全部讀入到定義的結(jié)構(gòu)體映射中,然后嘗試輸出看看,注意壓縮和解包格式必須對應(yīng)。
#include <iostream> #include <string> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> using namespace std; using namespace boost; using namespace boost::property_tree; // 定義映射字段 typedef struct { std::vector<std::string> container; std::vector<std::string> hash; std::vector<std::string> sub_url; }database_map; // 獲取文本中的JSON,放入自定義database_map std::vector<database_map> GetDataBase() { std::vector<database_map> ref; boost::property_tree::ptree ptr; boost::property_tree::read_json("database.json", ptr); if (ptr.count("data_base") == 1) { boost::property_tree::ptree p1, p2; p1 = ptr.get_child("data_base"); // 定義映射類型 std::vector<std::string> x, y, z; database_map maps; for (ptree::iterator it = p1.begin(); it != p1.end(); ++it) { // 讀取出json中的數(shù)據(jù) p2 = it->second; std::string container = p2.get<std::string>("container"); std::string hash = p2.get<std::string>("hash"); std::string sub_url = p2.get<std::string>("sub_url"); // 臨時存儲數(shù)據(jù) x.push_back(container); y.push_back(hash); z.push_back(sub_url); } // 打包結(jié)構(gòu)壓入ref中 maps.container = x; maps.hash = y; maps.sub_url = z; ref.push_back(maps); } return ref; } int main(int argc, char *argv[]) { std::vector<database_map> db_map = GetDataBase(); for (int x = 0; x < db_map.size(); x++) { // 依次將字典讀入內(nèi)存容器. database_map maps = db_map[x]; std::vector<std::string> container = maps.container; std::vector<std::string> hash = maps.hash; std::vector<std::string> sub_url = maps.sub_url; // 必須保證記錄數(shù)完全一致 if (container.size() != 0 && hash.size() != 0 && sub_url.size() != 0) { for (int x = 0; x < container.size(); x++) { std::cout << "容器類型: " << container[x] << std::endl; std::cout << "指紋: " << hash[x] << std::endl; std::cout << "根路徑: " << sub_url[x] << std::endl; std::cout << std::endl; } } } std::system("pause"); return 0; }
運行后則可以實現(xiàn)正常的json文檔解析,如下圖;
最后增加循環(huán)對比流程,這里我們以百度為例測試一下提取字段是否可以被解析。
#define CURL_STATICLIB #define BUILDING_LIBCURL #include <iostream> #include <string> #include "curl/curl.h" #include <boost/format.hpp> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> #include <boost/crc.hpp> #include <boost/uuid/detail/md5.hpp> #include <boost/algorithm/hex.hpp> #pragma comment (lib,"libcurl_a.lib") #pragma comment (lib,"wldap32.lib") #pragma comment (lib,"ws2_32.lib") #pragma comment (lib,"Crypt32.lib") using namespace std; using namespace boost; using namespace boost::property_tree; // 定義映射字段 typedef struct { std::vector<std::string> container; std::vector<std::string> hash; std::vector<std::string> sub_url; }database_map; // 存儲回調(diào)函數(shù) size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); return size * nmemb; } // 屏蔽輸出 static size_t not_output(char *d, size_t n, size_t l, void *p){ return 0; } // 獲取數(shù)據(jù)并放入string中. std::string GetUrlPageOfString(std::string url) { std::string read_buffer; CURL *curl; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略證書檢查 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // 重定向 curl_easy_setopt(curl, CURLOPT_URL, url); // URL路徑 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); // 查找次數(shù),防止查找太深 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); // 連接超時 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3); // 接收數(shù)據(jù)時超時設(shè)置 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); curl_easy_perform(curl); curl_easy_cleanup(curl); return read_buffer; } return "None"; } // 獲取狀態(tài)碼 long GetStatus(std::string url) { CURLcode return_code; long retcode = 0; return_code = curl_global_init(CURL_GLOBAL_WIN32); if (CURLE_OK != return_code) return 0; CURL *easy_handle = curl_easy_init(); if (NULL != easy_handle) { curl_easy_setopt(easy_handle, CURLOPT_URL, url); // 請求的網(wǎng)站 curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, not_output); // 設(shè)置回調(diào)函數(shù),屏蔽輸出 return_code = curl_easy_perform(easy_handle); // 執(zhí)行CURL return_code = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &retcode); if ((CURLE_OK == return_code) && retcode) { return retcode; } } curl_easy_cleanup(easy_handle); curl_global_cleanup(); return retcode; } // 應(yīng)用于md5 std::string GetMd5(const char * const buffer, size_t buffer_size) { if (buffer == nullptr) return false; std::string str_md5; boost::uuids::detail::md5 boost_md5; boost_md5.process_bytes(buffer, buffer_size); boost::uuids::detail::md5::digest_type digest; boost_md5.get_digest(digest); const auto char_digest = reinterpret_cast<const char*>(&digest); str_md5.clear(); boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(str_md5)); return str_md5; } // 獲取文本中的JSON,放入自定義database_map std::vector<database_map> GetDataBase() { std::vector<database_map> ref; boost::property_tree::ptree ptr; boost::property_tree::read_json("database.json", ptr); if (ptr.count("data_base") == 1) { boost::property_tree::ptree p1, p2; p1 = ptr.get_child("data_base"); // 定義映射類型 std::vector<std::string> x, y, z; database_map maps; for (ptree::iterator it = p1.begin(); it != p1.end(); ++it) { // 讀取出json中的數(shù)據(jù) p2 = it->second; std::string container = p2.get<std::string>("container"); std::string hash = p2.get<std::string>("hash"); std::string sub_url = p2.get<std::string>("sub_url"); // 臨時存儲數(shù)據(jù) x.push_back(container); y.push_back(hash); z.push_back(sub_url); } // 打包結(jié)構(gòu)壓入ref中 maps.container = x; maps.hash = y; maps.sub_url = z; ref.push_back(maps); } return ref; } int main(int argc, char *argv[]) { std::vector<database_map> db_map = GetDataBase(); for (int x = 0; x < db_map.size(); x++) { // 依次將字典讀入內(nèi)存容器. database_map maps = db_map[x]; std::vector<std::string> container = maps.container; std::vector<std::string> hash = maps.hash; std::vector<std::string> sub_url = maps.sub_url; // 必須保證記錄數(shù)完全一致 if (container.size() != 0 && hash.size() != 0 && sub_url.size() != 0) { for (int x = 0; x < container.size(); x++) { // 開始編寫掃描函數(shù) // 1.拼接字符串 std::string ur = "https://www.baidu.com"; std::string this_url = boost::str(boost::format("%s%s") %ur %sub_url[x]); // 2.判斷頁面是否存在 long ref_status = GetStatus(this_url); if (ref_status != 0 && ref_status == 200) { // 3.讀入頁面字符串,判斷是否成功 std::string read_page = GetUrlPageOfString(this_url); if (read_page != "None") { std::string check_md5 = GetMd5(read_page.c_str(),read_page.length()); std::cout << "[+] 頁面MD5: " << check_md5 << std::endl; std::cout << "[+] 數(shù)據(jù)庫: " << hash[x] << std::endl; // 4.比對MD5值是否相同 if (check_md5 == std::string(hash[x])) { std::cout << "[*] 診斷框架為: " << container[x] << std::endl; break; } } } } } } std::system("pause"); return 0; }
如下圖所示,說明對比通過,接著就可以增加命令行參數(shù)并使用了。
完整代碼總結(jié)
C++指紋識別助手程序,它使用了libcurl庫進行HTTP請求,通過比對頁面的MD5值與預(yù)先存儲在數(shù)據(jù)庫中的MD5值,從而識別目標(biāo)網(wǎng)站所使用的容器框架。
通過參數(shù)-u
用于識別一個網(wǎng)站是什么框架,使用-g
則是獲取當(dāng)前頁面指紋特征,如下圖;
#define CURL_STATICLIB #define BUILDING_LIBCURL #include <iostream> #include <string> #include "curl/curl.h" #include <boost/format.hpp> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> #include <boost/crc.hpp> #include <boost/uuid/detail/md5.hpp> #include <boost/algorithm/hex.hpp> #include <boost/program_options.hpp> #pragma comment (lib,"libcurl_a.lib") #pragma comment (lib,"wldap32.lib") #pragma comment (lib,"ws2_32.lib") #pragma comment (lib,"Crypt32.lib") using namespace std; using namespace boost; using namespace boost::property_tree; namespace opt = boost::program_options; // 定義映射字段 typedef struct { std::vector<std::string> container; std::vector<std::string> hash; std::vector<std::string> sub_url; }database_map; void ShowOpt() { fprintf(stderr, "# # # \n" "# # # \n" "# # # ##### ###### ###### # ### # ## \n" "# # # # # # # # ## # # \n" "# # # #### # # # # # ### \n" "# ##### # # # # ## # # # \n" "###### # ##### # # #### # # # ## \n\n" ); } // 存儲回調(diào)函數(shù) size_t WriteCallback(char *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); return size * nmemb; } // 屏蔽輸出 static size_t not_output(char *d, size_t n, size_t l, void *p){ return 0; } // 獲取數(shù)據(jù)并放入string中. std::string GetUrlPageOfString(std::string url) { std::string read_buffer; CURL *curl; curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略證書檢查 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); // 重定向 curl_easy_setopt(curl, CURLOPT_URL, url); // URL路徑 curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 1); // 查找次數(shù),防止查找太深 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); // 連接超時 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3); // 接收數(shù)據(jù)時超時設(shè)置 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &read_buffer); curl_easy_perform(curl); curl_easy_cleanup(curl); return read_buffer; } return "None"; } // 獲取狀態(tài)碼 long GetStatus(std::string url) { CURLcode return_code; long retcode = 0; return_code = curl_global_init(CURL_GLOBAL_WIN32); if (CURLE_OK != return_code) return 0; CURL *easy_handle = curl_easy_init(); if (NULL != easy_handle) { curl_easy_setopt(easy_handle, CURLOPT_URL, url); // 請求的網(wǎng)站 curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, not_output); // 設(shè)置回調(diào)函數(shù),屏蔽輸出 return_code = curl_easy_perform(easy_handle); // 執(zhí)行CURL return_code = curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &retcode); if ((CURLE_OK == return_code) && retcode) { return retcode; } } curl_easy_cleanup(easy_handle); curl_global_cleanup(); return retcode; } // 應(yīng)用于md5 std::string GetMd5(const char * const buffer, size_t buffer_size) { if (buffer == nullptr) return false; std::string str_md5; boost::uuids::detail::md5 boost_md5; boost_md5.process_bytes(buffer, buffer_size); boost::uuids::detail::md5::digest_type digest; boost_md5.get_digest(digest); const auto char_digest = reinterpret_cast<const char*>(&digest); str_md5.clear(); boost::algorithm::hex(char_digest, char_digest + sizeof(boost::uuids::detail::md5::digest_type), std::back_inserter(str_md5)); return str_md5; } // 獲取文本中的JSON,放入自定義database_map std::vector<database_map> GetDataBase() { std::vector<database_map> ref; boost::property_tree::ptree ptr; boost::property_tree::read_json("database.json", ptr); if (ptr.count("data_base") == 1) { boost::property_tree::ptree p1, p2; p1 = ptr.get_child("data_base"); // 定義映射類型 std::vector<std::string> x, y, z; database_map maps; for (ptree::iterator it = p1.begin(); it != p1.end(); ++it) { // 讀取出json中的數(shù)據(jù) p2 = it->second; std::string container = p2.get<std::string>("container"); std::string hash = p2.get<std::string>("hash"); std::string sub_url = p2.get<std::string>("sub_url"); // 臨時存儲數(shù)據(jù) x.push_back(container); y.push_back(hash); z.push_back(sub_url); } // 打包結(jié)構(gòu)壓入ref中 maps.container = x; maps.hash = y; maps.sub_url = z; ref.push_back(maps); } return ref; } // 掃描判斷容器類型 void ScanPage(std::string urls) { std::vector<database_map> db_map = GetDataBase(); for (int x = 0; x < db_map.size(); x++) { // 依次將字典讀入內(nèi)存容器. database_map maps = db_map[x]; std::vector<std::string> container = maps.container; std::vector<std::string> hash = maps.hash; std::vector<std::string> sub_url = maps.sub_url; // 必須保證記錄數(shù)完全一致 if (container.size() != 0 && hash.size() != 0 && sub_url.size() != 0) { for (int x = 0; x < container.size(); x++) { // 1.拼接字符串 std::string this_url = boost::str(boost::format("%s%s") % urls %sub_url[x]); // 2.判斷頁面是否存在 long ref_status = GetStatus(this_url); if (ref_status != 0 && ref_status == 200) { // 3.讀入頁面字符串,判斷是否成功 std::string read_page = GetUrlPageOfString(this_url); if (read_page != "None") { std::string check_md5 = GetMd5(read_page.c_str(), read_page.length()); std::cout << "[+] 頁面MD5: " << check_md5 << std::endl; std::cout << "[+] 數(shù)據(jù)庫: " << hash[x] << std::endl; // 4.比對MD5值是否相同 if (check_md5 == std::string(hash[x])) { std::cout << "[*] 診斷框架為: " << container[x] << std::endl; break; } } } } } } } int main(int argc, char *argv[]) { opt::options_description des_cmd("\n Usage: 容器識別助手 \n\n Options"); des_cmd.add_options() ("url,u", opt::value<std::string>(), "指定目標(biāo)URL地址") ("get,g", opt::value<std::string>(), "提取頁面指紋") ("help,h", "幫助菜單"); opt::variables_map virtual_map; try { opt::store(opt::parse_command_line(argc, argv, des_cmd), virtual_map); } catch (...){ return 0; } // 定義消息 opt::notify(virtual_map); // 無參數(shù)直接返回 if (virtual_map.empty()) { ShowOpt(); return 0; } // 幫助菜單 else if (virtual_map.count("help") || virtual_map.count("h")) { ShowOpt(); std::cout << des_cmd << std::endl; return 0; } else if (virtual_map.count("url")) { std::string address = virtual_map["url"].as<std::string>(); ScanPage(address); } else if (virtual_map.count("get")) { std::string address = virtual_map["get"].as<std::string>(); std::string read_page = GetUrlPageOfString(address); std::cout << "[+] 提取指紋: " << GetMd5(read_page.c_str(), read_page.length()) << std::endl; } else { std::cout << "參數(shù)錯誤" << std::endl; } return 0; std::system("pause"); return 0; }
以上就是C++ LibCurl實現(xiàn)Web指紋識別功能的詳細(xì)內(nèi)容,更多關(guān)于C++ LibCurl指紋識別的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++核心編程之占位參數(shù)和默認(rèn)參數(shù)
這篇文章主要介紹了C++核心編程之占位參數(shù)和默認(rèn)參數(shù),c++中函數(shù)的形參列表中的形參是可以有默認(rèn)值的,函數(shù)的形參列表里可以有占位參數(shù),用來占位,調(diào)用函數(shù)時必須填補位置。下面更多相關(guān)內(nèi)容的詳細(xì)介紹,需要的小伙伴可以參考一下2022-03-03C語言中的內(nèi)存管理之掌握動態(tài)分配的技巧(最新推薦)
在C語言編程中,內(nèi)存管理是一項至關(guān)重要的技能,它直接關(guān)系到程序的性能和穩(wěn)定性,特別是在處理大型數(shù)據(jù)集或需要靈活內(nèi)存布局的場景下,本文將深入探討C語言中的動態(tài)內(nèi)存分配技巧,幫助開發(fā)者更好地掌握這一核心技能2025-03-03C語言浮點型數(shù)據(jù)在內(nèi)存中的存儲方式詳解
任何數(shù)據(jù)在內(nèi)存中都是以二進制的形式存儲的,例如一個short型數(shù)據(jù)1156,其二進制表示形式為00000100 10000100,下面這篇文章主要給大家介紹了關(guān)于C語言浮點型數(shù)據(jù)在內(nèi)存中的存儲方式,需要的朋友可以參考下2023-03-03C++歸并法+快速排序?qū)崿F(xiàn)鏈表排序的方法
這篇文章主要介紹了C++歸并法+快速排序?qū)崿F(xiàn)鏈表排序的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04