Files
PatchRequest 4b72c45c57 Add inter-task data chaining, work data in all 84 tasks, and COM/WMI category
Inter-task chaining: a 256-byte ScratchBuffer flows through all tasks in
a single run() call. Each task reads from it (blend_into) and writes back
results (absorb), creating a data-dependency chain where task N's output
influences task N+1's computation. An analyzer can no longer identify
individual tasks as isolated blocks.

Work data in all tasks: all 84 tasks now actively consume fed work data.
Filesystem tasks derive skip offsets and iteration biases from work data.
Registry tasks bias subkey/value enumeration counts. WinAPI tasks offset
starting indices for window/process/metric enumeration. Network tasks
select starting host indices and blend work data into response buffers.

New COM/WMI category (8 tasks): WQL queries against Win32_Process,
Win32_OperatingSystem, Win32_ComputerSystem, Win32_NetworkAdapterConfiguration,
Win32_LogicalDisk, Win32_Service, Win32_BIOS, and Win32_Processor. All
read-only. Uses COM automation with CoInitializeSecurity for reliable
initialization across apartment models.

84 tasks across 8 categories. All 182 tests pass.
2026-06-08 23:03:05 +02:00

318 lines
11 KiB
Rust

use crate::categories::Categories;
use crate::tasks::{ScratchBuffer, TaskDescriptor, TaskParams};
use crate::workdata::WorkData;
use rand::rngs::ThreadRng;
use rand::RngCore;
use std::hint::black_box;
use std::io::{Read, Write};
use std::net::{TcpStream, ToSocketAddrs, UdpSocket};
use std::os::windows::io::AsRawSocket;
#[link(name = "ws2_32")]
extern "system" {
fn setsockopt(s: usize, level: i32, optname: i32, optval: *const u8, optlen: i32) -> i32;
}
const SOL_SOCKET: i32 = 0xFFFF;
const SO_RCVTIMEO: i32 = 0x1006;
const SO_SNDTIMEO: i32 = 0x1005;
fn set_socket_timeouts(socket: &impl AsRawSocket, ms: u32) {
let raw = socket.as_raw_socket() as usize;
let val = ms.to_ne_bytes();
unsafe {
setsockopt(raw, SOL_SOCKET, SO_RCVTIMEO, val.as_ptr(), 4);
setsockopt(raw, SOL_SOCKET, SO_SNDTIMEO, val.as_ptr(), 4);
}
}
pub fn register() -> Vec<TaskDescriptor> {
vec![
TaskDescriptor {
name: "dns_lookups",
category: Categories::NETWORK,
func: dns_lookups,
},
TaskDescriptor {
name: "http_get",
category: Categories::NETWORK,
func: http_get,
},
TaskDescriptor {
name: "ntp_query",
category: Categories::NETWORK,
func: ntp_query,
},
TaskDescriptor {
name: "http_head_request",
category: Categories::NETWORK,
func: http_head_request,
},
TaskDescriptor {
name: "tcp_connect_probe",
category: Categories::NETWORK,
func: tcp_connect_probe,
},
TaskDescriptor {
name: "dns_varied_ports",
category: Categories::NETWORK,
func: dns_varied_ports,
},
TaskDescriptor {
name: "http_post_discard",
category: Categories::NETWORK,
func: http_post_discard,
},
]
}
fn dns_lookups(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let hosts = [
"google.com:80",
"microsoft.com:80",
"cloudflare.com:80",
"github.com:80",
"amazon.com:80",
"apple.com:80",
"mozilla.org:80",
"wikipedia.org:80",
"reddit.com:80",
"stackoverflow.com:80",
"twitter.com:80",
"linkedin.com:80",
"facebook.com:80",
"youtube.com:80",
"netflix.com:80",
"twitch.tv:80",
"spotify.com:80",
"dropbox.com:80",
"zoom.us:80",
"slack.com:80",
"discord.com:80",
"akamai.com:80",
"fastly.com:80",
"aws.amazon.com:80",
];
let start_idx = work.derive_usize(0) % hosts.len();
for i in 0..params.iterations.min(50) {
let host = hosts[(start_idx + i) % hosts.len()];
let _ = host.to_socket_addrs().map(|addrs| {
for addr in addrs {
let bytes = match addr {
std::net::SocketAddr::V4(v4) => v4.ip().octets().to_vec(),
std::net::SocketAddr::V6(v6) => v6.ip().octets().to_vec(),
};
scratch.absorb(&bytes);
black_box(addr);
}
});
}
}
fn http_get(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let targets: &[(&str, u16, &str)] = &[
("httpbin.org", 80, "/get"),
("ip-api.com", 80, "/json"),
("ifconfig.me", 80, "/ip"),
("api.ipify.org", 80, "/"),
("httpbin.org", 80, "/headers"),
("httpbin.org", 80, "/user-agent"),
("worldtimeapi.org", 80, "/api/ip"),
("httpbin.org", 80, "/ip"),
("icanhazip.com", 80, "/"),
("checkip.amazonaws.com", 80, "/"),
("api.myip.com", 80, "/"),
];
let start_idx = work.derive_usize(0) % targets.len();
for i in 0..params.call_depth.min(5) {
let &(host, port, path) = &targets[(start_idx + i) % targets.len()];
let addr = format!("{}:{}", host, port);
let stream = match TcpStream::connect(&*addr) {
Ok(s) => s,
Err(_) => continue,
};
set_socket_timeouts(&stream, 3000);
let request = format!(
"GET {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
path, host
);
let mut stream = stream;
if stream.write_all(request.as_bytes()).is_err() {
continue;
}
let mut response = vec![0u8; 4096];
let _ = stream.read(&mut response);
work.blend_into(&mut response);
scratch.blend_into(&mut response);
scratch.absorb(&response[..response.len().min(256)]);
black_box(&response);
}
}
fn ntp_query(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let servers = [
"pool.ntp.org:123",
"time.google.com:123",
"time.cloudflare.com:123",
"time.windows.com:123",
"time.apple.com:123",
"time.nist.gov:123",
"ntp.ubuntu.com:123",
];
let start_idx = work.derive_usize(0) % servers.len();
for i in 0..params.call_depth.min(5) {
let server = servers[(start_idx + i) % servers.len()];
let socket = match UdpSocket::bind("0.0.0.0:0") {
Ok(s) => s,
Err(_) => continue,
};
set_socket_timeouts(&socket, 3000);
let mut packet = [0u8; 48];
packet[0] = 0x1B;
if socket.send_to(&packet, server).is_err() {
continue;
}
let mut response = [0u8; 48];
let _ = socket.recv_from(&mut response);
scratch.absorb(&response);
black_box(&response);
}
}
fn http_head_request(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let targets: &[(&str, u16, &str)] = &[
("httpbin.org", 80, "/get"),
("httpbin.org", 80, "/headers"),
("httpbin.org", 80, "/ip"),
("httpbin.org", 80, "/user-agent"),
("ip-api.com", 80, "/json"),
("ifconfig.me", 80, "/ip"),
("worldtimeapi.org", 80, "/api/ip"),
("checkip.amazonaws.com", 80, "/"),
("icanhazip.com", 80, "/"),
("api.ipify.org", 80, "/"),
];
let start_idx = work.derive_usize(0) % targets.len();
for i in 0..params.call_depth.min(5) {
let &(host, port, path) = &targets[(start_idx + i) % targets.len()];
let addr = format!("{}:{}", host, port);
let stream = match TcpStream::connect(&*addr) {
Ok(s) => s,
Err(_) => continue,
};
set_socket_timeouts(&stream, 3000);
let request = format!(
"HEAD {} HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
path, host
);
let mut stream = stream;
if stream.write_all(request.as_bytes()).is_err() {
continue;
}
let mut response = vec![0u8; 2048];
let _ = stream.read(&mut response);
scratch.absorb(&response[..response.len().min(256)]);
black_box(&response);
}
}
fn tcp_connect_probe(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let targets: &[(&str, u16)] = &[
("google.com", 80),
("google.com", 443),
("cloudflare.com", 80),
("cloudflare.com", 443),
("github.com", 443),
("microsoft.com", 80),
("microsoft.com", 443),
("amazon.com", 443),
("1.1.1.1", 53),
("8.8.8.8", 53),
];
let start_idx = work.derive_usize(0) % targets.len();
for i in 0..params.iterations.min(10) {
let &(host, port) = &targets[(start_idx + i) % targets.len()];
let addr = format!("{}:{}", host, port);
if let Ok(stream) = TcpStream::connect(&*addr) {
set_socket_timeouts(&stream, 2000);
let marker = [host.as_bytes()[0], port as u8];
scratch.absorb(&marker);
black_box(&stream);
}
}
}
fn dns_varied_ports(params: &TaskParams, _rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let hosts = [
"example.com:21",
"example.com:22",
"example.com:25",
"example.com:53",
"example.com:110",
"example.com:143",
"example.com:443",
"example.com:993",
"example.com:8080",
"example.com:8443",
"example.org:80",
"example.org:443",
"example.net:80",
"example.net:25",
"example.net:8080",
];
let start_idx = work.derive_usize(0) % hosts.len();
for i in 0..params.iterations.min(40) {
let host = hosts[(start_idx + i) % hosts.len()];
let _ = host.to_socket_addrs().map(|addrs| {
for addr in addrs {
let bytes = match addr {
std::net::SocketAddr::V4(v4) => v4.ip().octets().to_vec(),
std::net::SocketAddr::V6(v6) => v6.ip().octets().to_vec(),
};
scratch.absorb(&bytes);
black_box(addr);
}
});
}
}
fn http_post_discard(params: &TaskParams, rng: &mut ThreadRng, work: &WorkData, scratch: &mut ScratchBuffer) {
let targets: &[(&str, u16, &str, &str)] = &[
("httpbin.org", 80, "/post", "POST"),
("httpbin.org", 80, "/anything", "POST"),
("httpbin.org", 80, "/put", "PUT"),
("httpbin.org", 80, "/anything", "PUT"),
("httpbin.org", 80, "/patch", "PATCH"),
];
let body_size = params.buffer_size.min(1024);
let mut body = vec![0u8; body_size];
rng.fill_bytes(&mut body);
work.blend_into(&mut body);
scratch.blend_into(&mut body);
let body_hex: String = body.iter().map(|b| format!("{:02x}", b)).collect();
let start_idx = work.derive_usize(0) % targets.len();
for i in 0..params.call_depth.min(5) {
let &(host, port, path, method) = &targets[(start_idx + i) % targets.len()];
let addr = format!("{}:{}", host, port);
let stream = match TcpStream::connect(&*addr) {
Ok(s) => s,
Err(_) => continue,
};
set_socket_timeouts(&stream, 3000);
let request = format!(
"{} {} HTTP/1.1\r\nHost: {}\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
method, path, host, body_hex.len(), body_hex
);
let mut stream = stream;
if stream.write_all(request.as_bytes()).is_err() {
continue;
}
let mut response = vec![0u8; 4096];
let _ = stream.read(&mut response);
scratch.absorb(&response[..response.len().min(256)]);
black_box(&response);
}
}