优势: Rust没有GC 效率和C++一样快 本贴子主要用于观摩和学习调用windows api,禁止用于某些用途
已测试游戏(cf) 注意关闭防火墙
目标 - 扫描屏幕敌人出现红色的名字,达到自动开枪的目的。 效果 - 爆破模式和狙击枪使用的时候 瞬间秒杀敌人。程序的反应时间为1秒/60fps=0.01秒。这性能保证了 只要准心略过敌人身体,永远都是你先开第一枪
具体步骤: 1屏幕截BitMap图片数据(本教程屏幕尺寸为1920*1080.具体屏幕尺寸需要自行测量),找图找色,找名字颜色 2根据扫描到的RGB颜色值矩阵找红色+黑色描边名字 3模拟鼠标按下和抬起
用到的库:
winapi-rs
用到的windows API:
mouse_event //模拟鼠标点击
CreateCompatibleBitmap //创建位图
SelectObject
BitBlt
GetDIBits //取得屏幕图片
DeleteDC
ReleaseDC //释放句柄
DeleteObject
#[cfg(windows)]
extern crate winapi;
use std::ffi::CString;
use std::io::{Bytes, Error};
use std::ptr::null_mut;
use std::thread::sleep;
use std::time::SystemTime;
use winapi::_core::mem::size_of;
use winapi::_core::str::Chars;
use winapi::_core::time::Duration;
use winapi::ctypes::{c_char, c_void};
use winapi::shared::minwindef::BYTE;
use winapi::shared::windef::{HBITMAP, HBITMAP__, HGDIOBJ, HWND, POINT, RECT, SIZE};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::wingdi;
use winapi::um::wingdi::{BI_RGB, BitBlt, BITMAPINFO, BITMAPINFOHEADER, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, DIB_RGB_COLORS, GetBValue, GetDIBits, GetGValue, GetPixel, GetRValue, GetTextColor, RGBQUAD, SelectObject, SRCCOPY};
use winapi::um::winuser::{GetCursorPos, GetDC, GetDesktopWindow, GetTopWindow, GetWindowDC, GetWindowRect, mouse_event, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, ReleaseDC};
use crate::util::{click_mouse_event, click_send_input, is_red, pixel_to_rgb, rgb_is_black, rgb_is_red, write_file};
pub mod util;
/// step: 步长
#[cfg(windows)]
unsafe fn find_color(left: u32, top: u32, right: u32, bottom: u32, step: usize) -> bool {
let h_screen_dc = GetDC(null_mut());//获得屏幕的HDC
let mem_dc = CreateCompatibleDC(h_screen_dc);//创建一个内存中的DC
let mut rect: RECT = RECT {
left: left as i32,
top: top as i32,
right: right as i32,
bottom: bottom as i32,
};
//获取屏幕尺寸
// GetWindowRect(h_desk_top_wnd, &mut rect);
let mut screensize = SIZE { cx: 0, cy: 0 };
screensize.cx = rect.right - rect.left;
screensize.cy = rect.bottom - rect.top;
let mut h_bitmap: HBITMAP;
h_bitmap = CreateCompatibleBitmap(h_screen_dc, screensize.cx, screensize.cy);
let h_old_bmp = SelectObject(mem_dc, h_bitmap as HGDIOBJ);
let bit_blt_success = BitBlt(mem_dc, 0, 0, screensize.cx, screensize.cy, h_screen_dc, rect.left, rect.top, SRCCOPY);
if bit_blt_success as i32 == 0 {
//注意释放资源
DeleteDC(h_screen_dc);
DeleteDC(mem_dc);
ReleaseDC(h_screen_dc as HWND, mem_dc);
ReleaseDC(null_mut(), h_screen_dc);
DeleteObject(mem_dc as HGDIOBJ);
DeleteObject(h_old_bmp);
DeleteObject(h_bitmap as HGDIOBJ);
return false;
}
let mut bit_info: BITMAPINFO = BITMAPINFO {
bmiHeader: BITMAPINFOHEADER {
biSize: size_of::<BITMAPINFOHEADER>() as u32,
biWidth: screensize.cx,
biHeight: screensize.cy,
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB,
biSizeImage: 0,
biXPelsPerMeter: 0,
biYPelsPerMeter: 0,
biClrUsed: 0,
biClrImportant: 0,
},
bmiColors: [RGBQUAD {
rgbBlue: 0,
rgbGreen: 0,
rgbRed: 0,
rgbReserved: 0,
}; 1],
};
let mut is_find_color = false;
let mut result = 0;
//第一次调用GetDIBits获得图片的大小
result = GetDIBits(mem_dc, h_bitmap, 0, screensize.cy as u32, null_mut(), &mut bit_info, DIB_RGB_COLORS);
if result != 0 {
//第二次调用GetDIBits取图片流数据
// 位图信息及调色板大小
let info_size = bit_info.bmiHeader.biSize + bit_info.bmiHeader.biClrUsed * size_of::<RGBQUAD>() as u32;
//缓冲区大小
let size: usize = bit_info.bmiHeader.biSizeImage as usize + info_size as usize;
//缓存
let mut buffer = vec![0u8; size];
let ptr = buffer.as_mut_ptr().cast();
result = GetDIBits(mem_dc, h_bitmap, 0, screensize.cy as u32, ptr, &mut bit_info, DIB_RGB_COLORS);
if result == 0 {
println!("2设置图片信息出错");
is_find_color = false;
}else{
let mut have_black = false;
let mut last_i = 0;
let len = size / 4;
for i in 0..len {
if step != 0 && i % step != 0 {
continue;
}
let rv = buffer[(size - i * 4) - 2] as i32;
let gv = buffer[(size - i * 4) - 3] as i32;
let bv = buffer[(size - i * 4) - 4] as i32;
if rgb_is_black(rv, gv, bv) {
have_black = true;
last_i = i;
}
if rgb_is_red(rv, gv, bv) && have_black && last_i + 1 == i {
//println!("find red r:{},g:{},b:{}", rv, gv, bv);
is_find_color = true;
break;
}
}
}
} else {
let e = GetLastError();
println!("1设置图片信息出错,code: {}", e);
}
//gc
DeleteDC(h_screen_dc);
DeleteDC(mem_dc);
ReleaseDC(h_screen_dc as HWND, mem_dc);
ReleaseDC(null_mut(), h_screen_dc);
DeleteObject(mem_dc as HGDIOBJ);
DeleteObject(h_old_bmp);
DeleteObject(h_bitmap as HGDIOBJ);
return is_find_color;
}
#[cfg(windows)]
unsafe fn print_message() {
// let pixel= GetPixel(null_mut(),0,512);
// println!("{}",pixel);
let mut p = POINT {
x: 0,
y: 0,
};
let hDeskTopWnd = GetDesktopWindow();//获得屏幕的HWND
let hScreenDC = GetDC(hDeskTopWnd);//获得屏幕的HDC
loop {
GetCursorPos(&mut p);
println!("point: {},{}", &p.x, &p.y);
let pixel = GetPixel(hScreenDC, p.x, p.y);
println!("pixel: {}", pixel);
pixel_to_rgb(pixel as u32);
sleep(Duration::from_secs(1));
}
}
unsafe fn loop_find_color() {
loop {
find_color(0, 0, 10, 100, 0);
}
}
unsafe fn loop_find_cf_color() {
println!("done");
loop {
//let find = find_color(0, 0, 10, 100, 0);;
let find = find_color(918, 570, 918 + 100, 570 + 57, 0);
if find {
click_mouse_event(0, 0);
println!("click");
}
}
}
fn main() {
println!("now start!");
//bench_rate();
unsafe { loop_find_cf_color(); }
}
封装util.rs
use std::fs::File;
use std::io::Write;
use std::thread::sleep;
use std::time::Duration;
use winapi::_core::mem::size_of;
use winapi::um::winuser::{GetCursorPos, GetDC, INPUT, INPUT_MOUSE, INPUT_u, mouse_event, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MOVE, MOUSEINPUT, SendInput};
pub unsafe fn click_send_input(dx: u32, dy: u32) {
let mut input = INPUT { type_: INPUT_MOUSE, u: INPUT_u::default() };
let mi = input.u.mi_mut();
mi.dx = 0;
mi.dy = 0;
mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
SendInput(1, &mut input, size_of::<INPUT>() as i32);
sleep(Duration::from_millis(50));
let mut input = INPUT { type_: INPUT_MOUSE, u: INPUT_u::default() };
let mi = input.u.mi_mut();
mi.dx = 0;
mi.dy = 0;
mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(1, &mut input, size_of::<INPUT>() as i32);
sleep(Duration::from_millis(100));
}
pub unsafe fn click_mouse_event(dx: u32, dy: u32) {
mouse_event(MOUSEEVENTF_LEFTDOWN, dx, dy, 0, 0);
sleep(Duration::from_millis(50));
mouse_event(MOUSEEVENTF_LEFTUP, dx, dy, 0, 0);
}
pub fn pixel_to_rgb(pixel: u32) -> (i32, i32, i32) {
let rv = pixel & 0xFF;
let gv = (pixel & 0xFF00) / 256;
let bv = (pixel & 0xFF0000) / 65536;
let rv = rv as i32;
let gv = gv as i32;
let bv = bv as i32;
//mouse::click(p.x as u32,p.y as u32);
rgb_is_red(rv, gv, bv);
return (rv, gv, bv);
}
pub fn rgb_is_red(rv: i32, gv: i32, bv: i32) -> bool {
if rv >= 140 && gv <= 70 && bv <= 70 {
// println!("Is Red r:{},g:{},b:{}", rv, gv, bv);
return true;
} else {
// println!("Not Red R_:{},G_:{},B_:{}", rv, gv, bv);
return false;
}
}
pub fn rgb_is_black(rv: i32, gv: i32, bv: i32) -> bool {
if rv == 0 && gv == 0 && bv== 0 {
// println!("Is Red r:{},g:{},b:{}", rv, gv, bv);
return true;
} else {
// println!("Not Red R_:{},G_:{},B_:{}", rv, gv, bv);
return false;
}
}
pub fn is_red(rv: i32, gv: i32, bv: i32, r_diff: i32, gb_diff: i32) -> bool {
let rg: i32 = (rv - gv);
let rb: i32 = (rv - bv);
let gb: i32 = (gv - bv);
if rg.abs() >= r_diff && rb.abs() >= r_diff && gb.abs() <= gb_diff {
return true;
}
return false;
}
cargo.toml
[package]
name = "cf"
version = "0.1.0"
authors = ["zhuxiujia <zhuxiujia@qq.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi","impl-default","winuser","wingdi","windef","minwindef"] }
最后,留给大家一个扩展思考🤔
1 我们基于屏幕扫描的策略,其实消耗了 系统的带宽,剪切板资源,影响显卡FPS刷新率。
有木有更加高效的办法呢?
2 我们自动开枪可以设置线程sleep,模拟放开鼠标压枪。那么能否使用移动鼠标压枪呢?
3 我们对仗当然是喜欢一枪爆头,那么有什么高效的办法锁定敌人头部呢?
欢迎评论
1
共 9 条评论, 1 页
评论区
写评论有没有不用winapi的方法
虽然看不懂, 但是很摩拜, 弱弱问下, 文章开头的应该不是系统防火墙吧。。。
内挂容易被封,技术要求也高。思路是不错。 如果一定要外挂的思路,其实可以走opencv快速截屏,opencv依赖显卡速度会干上去很多
--
👇
phper-chen: 楼主你有测试的具体游戏吗 比如单机游戏 周末想试试 更高效的估计就是内挂了 拦截运行时敌人的坐标数据?这个就不用扫描图像帧了
cf知道吗,测试过腾讯的cf
👇
phper-chen: 楼主你有测试的具体游戏吗 比如单机游戏 周末想试试 更高效的估计就是内挂了 拦截运行时敌人的坐标数据?这个就不用扫描图像帧了
楼主你有测试的具体游戏吗 比如单机游戏 周末想试试 更高效的估计就是内挂了 拦截运行时敌人的坐标数据?这个就不用扫描图像帧了
cargo就依赖一个 winapi 其他啥也没依赖
--
👇
songlinshu: 大神cargo.toml看一下呢?
大神cargo.toml看一下呢?
牛逼啊
相当可以啊 这样用技术才算好玩呢