Skip to content

Commit e26e947

Browse files
committed
更新版本至1.6.0
1 parent 73fdfed commit e26e947

File tree

5 files changed

+256
-30
lines changed

5 files changed

+256
-30
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@ lazy_static = "1.5.0"
1313
regex = "1.10.6"
1414
reqwest = { version = "0.12.7" ,features = ["socks"]}
1515
serde = { version = "1.0.210", features = ["derive"] }
16+
serde_json = "1.0.134"
1617
tokio = { version = "1.40.0", features = ["full"] }
18+
once_cell = "1.19.0"
19+
base64 = "0.22.1"
20+
murmur3 = "0.5.2"
21+
url = "2.5.4"
1722

1823
[profile.release]
1924
lto = true # 启用链路时间优化

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
# 说明
22
利用Rust编写的高效URL测活工具,主要特点快速、批量、轻量,支持异步。
33

4+
# 功能介绍
5+
| 功能 | 描述 |
6+
|:---:|:------------------------------:|
7+
| 资产测活 | 对目标资产(域名、IP、URL) 进行测活 |
8+
| 支持代理 |支持多种代理,包括 HTTP、HTTPS 和 SOCKS 代理 |
9+
|状态码过滤| 对 HTTP 响应的状态码进行过滤,默认只保留200 |
10+
|指定路径| 支持指定路径进行测活,默认为根目录 |
11+
|指纹识别| 采用开源指纹库识别网站CMS |
12+
|高并发| 支持高并发请求、支持异步 |
13+
14+
415
# 用法
516
## 帮助信息
617
```text
@@ -16,7 +27,7 @@ Options:
1627
-c, --status-code <STATUS_CODE> Display the specified status code [default: 200]
1728
-p, --path <PATH> Designated path scan [default: ]
1829
-x, --proxy <PROXY> Supported Proxy socks5, http, and https, Example: -x socks5://127.0.0.1:1080
19-
-o, --output <OUTPUT> Output is an csv document, Example: -o result.csv
30+
-o, --output <OUTPUT> Output is an csv document, Example: -o result.csv
2031
-h, --help Print help (see more with '--help')
2132
-V, --version Print version
2233
```
@@ -28,6 +39,7 @@ Options:
2839
* -c --status-code 显示指定的状态码,默认200,可以输入多个,用逗号隔开,如200,403
2940
* -p --path 指定扫描路径,默认为空,不指定,如 -p admin
3041
* -x --proxy 支持代理,目前支持socks5,http,https,如:-x socks5://127.0.0.1:1080
42+
* -o --output 输出为csv文件,如:-o result.csv
3143
* -h --help 显示帮助信息
3244
* -V --version 显示版本信息
3345

@@ -58,6 +70,6 @@ windfire -f urls.txt -o result.csv
5870
```
5971
## 默认打印信息
6072
```shell
61-
https://www.baidu.com [200] [百度一下,你就知道] [BWS/1.1] [https://www.baidu.com/] [414219]
73+
https://www.baidu.com [200] [百度一下,你就知道] [BWS/1.1] [https://www.baidu.com/] [414219] ["CMS"]
6274
```
63-
包括:起始地址(url)、状态码(status_code)、标题(title)、服务器(server)、跳转后地址(jump_url)、响应页面大小(content_length)
75+
包括:起始地址(url)、状态码(status_code)、标题(title)、服务器(server)、跳转后地址(jump_url)、响应页面大小(content_length)、指纹信息

src/httpclient.rs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
use crate::utils::{get_format_info, ScanInfo};
1+
use crate::{
2+
utils::{get_favicon_url, get_fofa_iconhash, get_format_info},
3+
FINGER_DATA,
4+
};
5+
use crossbeam::queue::SegQueue;
26
use reqwest::{header, Client};
7+
use serde::Serialize;
38
use std::time::Duration;
4-
use crossbeam::queue::SegQueue;
59

610
pub const USER_AGENT: &str =
711
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:128.0) Gecko/20100101 Firefox/128.0";
812

13+
#[derive(Debug, Serialize, Clone)]
14+
pub struct PrintInfo {
15+
pub url: String,
16+
pub status_code: u16,
17+
pub title: String,
18+
pub server: String,
19+
pub jump_url: String,
20+
pub content_length: usize,
21+
pub cms: Vec<String>,
22+
}
23+
924
// 创建http客户端
1025
pub fn create_http_client(timeout: usize, proxy: Option<String>) -> Client {
1126
let mut headers = header::HeaderMap::new();
@@ -63,7 +78,7 @@ pub async fn send_request(
6378
url: &str,
6479
u16_vec: Vec<u16>,
6580
path: &str,
66-
seg_queue: &SegQueue<ScanInfo>,
81+
seg_queue: &SegQueue<PrintInfo>,
6782
) -> Result<String, reqwest::Error> {
6883
// 解析URL,如果path为空,则默认为/,如果有值,则加上,还需要处理url有没有/
6984
let url = if path.is_empty() {
@@ -79,7 +94,7 @@ pub async fn send_request(
7994

8095
let response = client.get(url.as_str()).send().await?;
8196

82-
let scan_info = get_format_info(response,url);
97+
let scan_info = get_format_info(response, url);
8398
let scan_info = scan_info.await;
8499

85100
let url = scan_info.url;
@@ -88,19 +103,66 @@ pub async fn send_request(
88103
let content_length = scan_info.content_length;
89104
let server = scan_info.server;
90105
let jump_url = scan_info.jump_url;
106+
let body = scan_info.body;
107+
let header = scan_info.header;
108+
109+
// 处理url 变为 协议 + 域名 + 端口 + /favicon.ico
110+
let favicon_url = get_favicon_url(&url).unwrap_or_else(|_| "".to_string());
111+
112+
// 获取 icon
113+
let icon_hash = get_fofa_iconhash(favicon_url, client)
114+
.await
115+
.unwrap_or_else(|_| "".to_string());
116+
let mut cms_list: Vec<String> = Vec::new();
117+
118+
// 然后进行指纹匹配,如果能匹配到,则返回cms,遍历指纹
119+
for finger in &*FINGER_DATA {
120+
let method = finger.method.to_string();
121+
let location = finger.location.to_string();
122+
let keyword = finger.keyword.clone();
123+
124+
if method == "keyword" {
125+
// 说明为关键词匹配
126+
if location == "body" {
127+
// 说明是body匹配,keyword 词组都在 body中
128+
let all_found = keyword.iter().all(|s| body.contains(s));
129+
if all_found {
130+
// 说明匹配成功
131+
cms_list.push(finger.cms.to_string());
132+
}
133+
} else if location == "header" {
134+
// 说明是header匹配
135+
let all_found = keyword.iter().all(|s| header.contains(s));
136+
if all_found {
137+
// 说明匹配成功
138+
cms_list.push(finger.cms.to_string());
139+
}
140+
}
141+
} else if method == "faviconhash" {
142+
// 说明是faviconhash匹配
143+
if icon_hash == finger.keyword[0] {
144+
// 说明匹配成功
145+
cms_list.push(finger.cms.to_string());
146+
}
147+
}
148+
}
149+
150+
// 对 cms_list 进行去重
151+
cms_list.dedup();
91152

92153
if u16_vec.contains(&status_code) {
93-
seg_queue.push(ScanInfo {
154+
seg_queue.push(PrintInfo {
94155
url: url.to_string(),
95156
status_code,
96157
title: title.to_string(),
97158
server: server.to_string(),
98159
jump_url: jump_url.to_string(),
99160
content_length,
161+
cms: cms_list.clone(),
100162
});
101163
Ok(format!(
102-
"{} [{}] [{}] [{}] [{}] [{}]",
103-
url, status_code, title, server, jump_url, content_length
164+
"{} [{}] [{}] [{}] [{}] [{}] {:?}",
165+
url, status_code, title, server, jump_url, content_length, cms_list
104166
))
105167
} else {
106168
Ok("".to_string())

src/main.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
1-
use crate::httpclient::{create_http_client, send_request};
2-
use crate::utils::{queue_to_csv, read_file, ScanInfo};
1+
use crate::httpclient::{create_http_client, send_request, PrintInfo};
2+
use crate::utils::{queue_to_csv, read_file};
33
use clap::Parser;
4+
use crossbeam::queue::SegQueue;
45
use futures::future::join_all;
6+
use once_cell::sync::Lazy;
57
use reqwest::Client;
8+
use serde::Deserialize;
69
use std::sync::Arc;
7-
use crossbeam::queue::SegQueue;
810
use tokio::sync::Semaphore;
911
use tokio::task;
1012

1113
mod httpclient;
1214
mod utils;
1315

16+
#[derive(Debug, Deserialize)]
17+
pub struct Finger {
18+
cms: String,
19+
method: String,
20+
location: String,
21+
keyword: Vec<String>,
22+
}
23+
24+
// 使用 once_cell::Lazy 来延迟初始化静态变量
25+
static FINGER_DATA: Lazy<Vec<Finger>> = Lazy::new(|| {
26+
let json_data = include_str!("finger.json"); // 使用 include_str! 将文件嵌入到程序中
27+
serde_json::from_str(json_data).expect("Failed to parse JSON") // 解析 JSON 数据
28+
});
29+
1430
#[derive(Parser, Debug)]
1531
#[command(
1632
version = "1.5.0",
@@ -64,7 +80,7 @@ async fn main() {
6480

6581
let path = args.path;
6682
let proxy = args.proxy;
67-
let seg_queue: Arc<SegQueue<ScanInfo>> = Arc::new(SegQueue::new());
83+
let seg_queue: Arc<SegQueue<PrintInfo>> = Arc::new(SegQueue::new());
6884

6985
if let Some(url) = args.url {
7086
let client = create_http_client(args.timeout, proxy);
@@ -90,11 +106,12 @@ async fn main() {
90106
let semaphore = Arc::clone(&semaphore);
91107
let client: Client = client.clone();
92108
let u16_vec = u16_vec.clone();
93-
let path = path.clone();
109+
let path: String = path.clone();
94110
let seg_queue = Arc::clone(&seg_queue);
95111
futures.push(task::spawn(async move {
96112
let permit = semaphore.acquire().await.unwrap();
97-
let result = send_request(client, url.as_str(), u16_vec, &path, &seg_queue).await;
113+
let result =
114+
send_request(client, url.as_str(), u16_vec, &path, &seg_queue).await;
98115
match result {
99116
Ok(result) => {
100117
if result != "" {

0 commit comments

Comments
 (0)