基于kvm-qemu使用rust开发一个命令行cheat engine
main
mod ch_3_value_scanner;
mod ch_3_ptr_map;
mod ch_3_disasm;
mod ch_3_sigmaker;
use std::collections::BTreeSet;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::thread;
use std::time::Instant;
use iced_x86::Decoder;
use scan_fmt::scan_fmt_some;
use simplelog::{LevelFilter, TermLogger, Config, TerminalMode};
use memflow::connector::ConnectorInventory;
use memflow::{Address, CachedMemoryAccess, OsProcessInfo, PartialResultExt, size, VirtualMemory};
use memflow_win32::{Kernel, Win32Process};
use crate::ch_3_ptr_map::PtrMAp;
use crate::ch_3_value_scanner::ValueScanner;
use ch_3_disasm::DisAsm;
use memflow_win32::{Error, Result};
use crate::ch_3_sigmaker::Sigmaker;
const PAGE_SIZE: usize = 4096;
/// ## 修改cli2 程序, 改变其堆上 u64 的内存
fn main() -> Result<()> {
TermLogger::init(LevelFilter::Info, Config::default(), TerminalMode::Mixed).expect("log 初始化失败");
unsafe {
let inventory = ConnectorInventory::scan();
println!("所有的链接器: {:?}", inventory.available_connectors());
let connect = inventory.create_connector_default("qemu_procfs")?;
// 获取kernel相关信息, 运行这里会下载pdb工具, 会很慢(好像也用过不到, 我这里改源码直接return了)
let mut kernel = Kernel::builder(connect)
.build_page_cache(|mem, arch| { // 开启页缓存, 好像没啥差别 和下面默认缓存设置相比
CachedMemoryAccess::builder(mem)
.arch(arch)
.build()
.unwrap()
})
// .build_default_caches()
.build()?;
// 获得所有进程
let process_info = kernel.process_info_list()?;
// 遍历所有进程的信息
for process in process_info.iter() {
println!("{:?} {:?}", process.pid, process.name)
}
// 得到指定进程
let mut process = kernel.process("cli_2.exe")?;
// let mut process = kernel.process("League of Legends.exe")?;
// {
// // 每 16m 字节合并为一块内存区域
// let mem_map = process.virt_mem.virt_page_map(size::mb(16));
//
// for (addr, size) in mem_map.iter() {
// println!("block addr {addr:x}, size: {size:x}");
// }
// }
// 一个反编译汇编器
let mut dis_asm = DisAsm::default();
// 一个指针关系表
let mut ptr_map = PtrMAp::default();
// 创建一个扫描器
let mut scanner = ValueScanner::default();
let mut type_name = String::new();
let mut buf_len = 0;
loop {
let line = get_line();
let mut tokens = line.trim().splitn(2, " ");
let (cmd, args) = (tokens.next().unwrap_or(""), tokens.next().unwrap_or(""));
match cmd {
"q" => break,
"p" => {
scanner.print_matches(&mut process.virt_mem, buf_len, &type_name);
println!("一共有 {} 个: ", scanner.matches_iter().len());
},
"a" => {
if let (Some(addr)) = scan_fmt_some!(args, "{x}", [hex u64]) {
scanner.matches.push(Address::from(addr));
};
}
"r" => {
buf_len = 0;
type_name = String::new();
ptr_map.reset();
dis_asm.reset();
scanner.reset();
}
"pm" => {
ptr_map.reset();
ptr_map.create_map(&mut process.virt_mem, process.proc_info.proc_arch.size_addr());
// let addr = Address::from(0x1bae1bba350_u64);
// let addr = Address::from(0x7ff6ae531040_u64);
// ptr_map.walk_down_range(addr, 0, 16, 0, 5);
}
"g" => {
dis_asm.reset();
dis_asm.collect(&mut process);
println!("全局变量一共: {:x}", dis_asm.map.len());
}
"s" => {
// 得到 s 的反汇编代码
if let Some(addr) = scan_fmt_some!(args, "{x}", [hex u64]) {
match Sigmaker::find_sigs(&mut process, &dis_asm, addr.into()) {
Ok(sigs) => {
println!("Found signatures:");
for sig in sigs {
println!("{}", sig);
}
}
Err(e) => println!("sigmaker error {}", e),
}
} else {
println!("请输入: s{{addr}}");
}
}
"os" => { // off scan, 使用 ptr_map 得到 扫描仪扫到的指针 的调用链
if let (Some(use_di), Some(lrange), Some(urange), Some(max_depth), filter_args) = scan_fmt_some!(args, "{} {} {} {} {x}", String, usize, usize, usize, [hex u64]) {
// 先把 所有使用的指针收集起来
if ptr_map.map.is_empty() {
ptr_map.create_map(&mut process.virt_mem, process.proc_info.proc_arch.size_addr());
}
let st = Instant::now();
let matches = if use_di == "y" {
if dis_asm.map.is_empty() {
dis_asm.collect(&mut process);
}
// 去重得到全局变量列表
// let set_global_addrs = &dis_asm.map.values().copied().collect::<BTreeSet<Address>>().iter().copied().collect::<Vec<Address>>();
// 得到 全局变量内 指针的调用链, 然后找到scanner内匹配的
let tmp_matches = ptr_map.find_matches_addrs(lrange, urange, max_depth, scanner.matches_iter(), &dis_asm.globals);
println!("os matchs 找到: {}", tmp_matches.len());
tmp_matches
} else {
// 得到 当前程序所有的 指针的调用链, 然后找到scanner内匹配的
let tmp_matches = ptr_map.find_matches(lrange, urange, max_depth, scanner.matches_iter());
println!("os matchs 找到: {}", tmp_matches.len());
tmp_matches
};
let end = st.elapsed().as_millis();
for (m, offsets) in matches.iter() {
// 如果只对 传进来的指定的基地址 感兴趣
if let Some(filter) = filter_args {
if let Some((f, _)) = offsets.first() {
if f.as_u64() != filter {
continue
}
}
}
for (start, off) in offsets {
print!("{start:x} + ({off}) => ")
}
println!("{m:x}")
}
println!("找到: {} 用时: {end}ms", matches.len())
} else{
println!("请输入正确的值: os y/[n] lrange urange max_depth")
}
}
"w" => {
match write_value(args, &type_name, scanner.matches_iter(), &mut process.virt_mem) {
Ok(o) => {
println!("修改成功");
}
Err(e) => {
println!("修改失败: {e}");
}
}
}
_ => {
if let Some((buf, tp)) = parse_input(cmd, args) {
type_name = tp;
buf_len = buf.len();
scanner.scan(&mut process.virt_mem, &buf);
println!("一共有 {} 个: ", scanner.matches_iter().len());
// scanner.print_matches(&mut process.virt_mem, buf_len, &type_name);
} else {
println!("无法解析")
}
}
};
}
}
Ok(())
}
// 得到标准输入
fn get_line() -> String {
let mut out = String::new();
std::io::stdin().read_line(&mut out);
out
}
fn async_get_line() -> Receiver<String> {
let (tx, rx) = mpsc::channel();
thread::spawn(move || tx.send(get_line()));
rx
}
// 解析输入的 参数: i64 123
fn parse_input(type_name: &str, value: &str) -> Option<(Box<[u8]>, String)>{
let buf = match type_name {
"str" => Box::from(value.as_bytes()),
"str_utf16" => { // utf16 特殊处理这是windows使用的文本格式
let mut out = vec![];
for v in value.encode_utf16() {
out.extend(v.to_ne_bytes().iter().copied())
}
out.into_boxed_slice()
},
"i64" => Box::from(value.parse::<i64>().unwrap().to_ne_bytes()),
"u64" => Box::from(value.parse::<u64>().unwrap().to_ne_bytes()),
"i32" => Box::from(value.parse::<i32>().unwrap().to_ne_bytes()),
"u32" => Box::from(value.parse::<u32>().unwrap().to_ne_bytes()),
"i16" => Box::from(value.parse::<i16>().unwrap().to_ne_bytes()),
"u16" => Box::from(value.parse::<u16>().unwrap().to_ne_bytes()),
"i8" => Box::from(value.parse::<i8>().unwrap().to_ne_bytes()),
"u8" => Box::from(value.parse::<u8>().unwrap().to_ne_bytes()),
"f64" => Box::from(value.parse::<f64>().unwrap().to_ne_bytes()),
"f32" => Box::from(value.parse::<f32>().unwrap().to_ne_bytes()),
_ => return None,
};
Some((buf, type_name.to_string()))
}
pub fn write_value(args: &str, type_name: &str, matches: &[Address], mut virt_mem: impl VirtualMemory) -> Result<()> {
let usage = Error::Other("usage: w {{idx/*}} {{o/c}} {{value}}");
if matches.is_empty() {
return Err(Error::Other("matches是空的"));
}
let mut words = args.splitn(3, " ");
let (idx, mode, value) = (words.next().ok_or(usage)?, words.next().ok_or(usage)?, words.next().ok_or(usage)?);
let (skip, take) = if idx == "*" {
(0, matches.len())
} else {
(idx.parse::<usize>().expect("解析idx失败"), 1)
};
// 解析要写入的值
let (v, _) = parse_input(type_name, value).ok_or(Error::Other("解析输入type_name value失败"))?;
let rx = async_get_line();
if mode == "o" {
println!("修改中~");
} else if mode == "c" {
println!("修改中~ 按确定键停止");
} else {
return Err(Error::Other("错误的修改模式"));
};
loop {
for m in matches.iter().skip(skip).take(take) {
virt_mem.virt_write_raw(*m, &v).data_part();
}
// 如果只修改一次
if mode == "o" {
break
} else {
// 如果键盘输入, 则停止
if mode == "c" {
if let Ok(_) = rx.try_recv() {
break
}
}
}
}
Ok(())
}
disasm
use std::cmp::{max, min};
use std::collections::BTreeMap;
use memflow::{Address, PartialResultExt, size, VirtualMemory};
use memflow_win32::Win32Process;
use pelite::PeFile;
use iced_x86::{Decoder, DecoderOptions};
#[derive(Default)]
pub struct DisAsm {
// 保存了全局变量相关
pub map: BTreeMap<Address, Address>, // 数据转移指令的ip地址, 数据转移操作的目的地址(其实就是指令地址+偏移地址)
pub inverse_map: BTreeMap<Address, Vec<Address>>, // 一个全局变量可能被多个 ip的指令使用 保存所以需要一个vec
pub globals: Vec<Address> // 保存所有的全局变量, 其实就是不重复的 self.map 中的value
}
impl DisAsm {
pub fn reset(&mut self) {
self.map.clear();
self.inverse_map.clear();
self.globals.clear();
}
/// 扫描全局变量, 扫描模块的每个部分, 找到可执行的代码段, 扫描这些段, 反汇编每一条指令, 保证不会对一行指令反汇编两次, 也不会再某些指令中间开始反汇编
///
pub fn collect(&mut self, process:&mut Win32Process<impl VirtualMemory>) {
self.reset();
// 得到进程所有的模块
let modules = process.module_list().unwrap();
// 数据标头的buffer , 128kb足够
let mut image_buffer = vec![0; size::kb(128)];
for module in modules.iter() {
// 读取模块的标头的字节数据
process.virt_mem.virt_read_raw_into(module.base, &mut image_buffer).data_part();
// 解析为模块头
let pefile = PeFile::from_bytes(&image_buffer).expect("pe头解析失败");
// 解析模块所有的段
for section in pefile.section_headers().iter() {
// 过滤掉不符合的一些特征的段, 然后扫描整个段内容
if section.Characteristics & 0x20 != 0 {
// 段开始位置, 就是模块在内存中的基础位置 加上 当前段在模块中的开始位置
let section_start_addr = module.base.as_u64() + section.VirtualAddress as u64;
// 段结束位置, 就是开始位置 + 段大小
let section_end_addr = section_start_addr + section.VirtualSize as u64;
// 扫描段每次扫描16mb
let mut cur_scanner_addr = 0; // 当前扫描的结束位置
let mut chunk_buffer = vec![0; size::mb(16) + 32]; // 预留32个字节给指令
for chunk_start in (section_start_addr..section_end_addr).step_by(size::mb(16)) {
// 防止越界(这里找指令的边界, 而不是切块的边界)
let chunk_start = max(chunk_start, cur_scanner_addr); // 把当前 16mb内容的开始地址, 和 当前扫描的位置比较找最大的 (有什么用?)(可能是找到跨页的指令?比如 一个指令由于切块, 被分成了两个块中?, 还有就是chunk_buffer, 预留了一些字节, 可能会导致开头的指令重叠取出, 被多次扫描)
let chunk_end = min(section_end_addr, chunk_start + size::mb(16) as u64); // 如果不够16mb, 就有多少读取多少, 而不是取16mb
// 读取内存数据
process.virt_mem.virt_read_raw_into(chunk_start.into(), &mut chunk_buffer).data_part();
// 创建解码器
let mut decoder = Decoder::new(process.proc_info.proc_arch.bits().into(), &chunk_buffer, DecoderOptions::NONE);
// 设置解码器的ip
decoder.set_ip(chunk_start);
for (ip, addr) in decoder
.iter()
.filter(|i| i.ip() < chunk_end) // 必须小于结束地址
.inspect(|i| cur_scanner_addr = i.ip() + i.len() as u64) // 每次推进 当前已经扫描的位置
.filter(|i| i.is_ip_rel_memory_operand()) // 检查内存操作数是否与RIP/EIP相关 得到相对操作数的指令(也就是使用当前ip的 相对地址)
.filter(|i| i.near_branch_target() == 0) // 不是分支调用语句
.map(|i| (i.ip(), i.ip_rel_memory_address())) // (ip, 操作数相对于EIP或RIP寄存器的相对内存操作数,如果该操作数是相对于EIP或RIP寄存器的相对内存操作数,则返回64位的绝对地址)
{
self.map.insert(ip.into(), addr.into());
};
};
}
}
}
// 保存反向指针
for (k, v) in self.map.iter() {
let addrs = self.inverse_map.entry(*v).or_default();
addrs.push(*k);
}
// 保存所有ip
self.globals = self.inverse_map.keys().copied().collect();
println!("DisAsm size: {}", self.map.len())
}
}
ptr_map
use std::cmp::Ordering;
use std::collections::{BTreeMap};
use std::ops::Bound::Included;
use memflow::{Address, PartialResultExt, size, VirtualMemory};
#[derive(Default)]
pub struct PtrMAp {
pub map: BTreeMap<Address, Address>, //正向的 指针所在的位置 -> 指针指向的实际的值 用来存放当前进程中每个指针的映射
pub inverse_map: BTreeMap<Address, Vec<Address>>, // 反向指针, 一个地址可能被多个地址保存所以需要一个vec
pub pointers: Vec<Address> // 保存了所有的非终点的指针, 其实就是 self.map中不重复的key
}
impl PtrMAp {
pub fn reset(&mut self) {
self.map.clear();
self.inverse_map.clear();
self.pointers.clear();
}
// 循环所有的内存块, 找到符合指针类型的数值, 并做出一个k,v映射
pub fn create_map<T: VirtualMemory>(&mut self, virt_mem: &mut T, addr_size: usize) {
self.reset();
let mem_map = virt_mem.virt_page_map_range(size::mb(16), Address::NULL, (1_usize << 48).into());
// 循环所有的块
let mut buf = vec![0; size::kb(64) + addr_size - 1];
for (block_addr, block_size) in mem_map.iter() {
// 循环块内内存(每次循环64k)
for offset in (0..*block_size).step_by(size::kb(64)) {
// 如果读到数据
if let Ok(_) = virt_mem.virt_read_raw_into(*block_addr + offset, &mut buf).data_part() {
// 循环64k 内部的 数据
for (o, data) in buf.windows(addr_size).enumerate(){
let addr = *block_addr + offset + o;
let out_addr = Address::from(u64::from_le_bytes(data.try_into().unwrap()));
// 查看 out_addr 是否是一个内存地址(是否在映射的内存块当中)
if let Ok(_) = mem_map
.binary_search_by(|&(a, u)| {
if a <= out_addr && out_addr <= a + u {
Ordering::Equal
} else {
a.cmp(&out_addr)
}
}) {
self.map.insert(addr, out_addr);
};
}
}
}
}
// 保存反向指针
for (k, v) in self.map.iter() {
let addrs = self.inverse_map.entry(*v).or_default();
addrs.push(*k);
}
// 保存所有非终止点指针
self.pointers = self.map.keys().copied().collect();
}
// 从一个地址addr中递归的查找指针映射表, 直到找到 start_points 搜索列表中符合的指针地址
fn walk_down_range(&self, addr: Address, lrange: usize, urange: usize, max_levels: usize, level: usize, start_points: &[Address], out: &mut Vec<(Address, Vec<(Address, isize)>)>, (final_addrs, stack): (Address, &mut Vec<(Address, isize)>)) {
// 从指针地址, 往下找实际保存的value, 如果这个value 的 范围, 还是指针, 则继续往下找 一直找到 max_levels层
let min = Address::from(addr.as_u64().saturating_sub(urange as _)); // 想要的addr 可能并没有被直接保存, 而是 指针+偏移, 这里减去 up_range, 是因为 计算的 上一级指针的范围, 在平坦内存中, 这个范围 是在指定addr 的 左侧
let max = Address::from(addr.as_u64().saturating_add(lrange as _));
// addr 如果如果已经在start_points 上级指针的附近, 会把 进入下面的循环, 把附近指针, 都添加到out中
// 指向addr的指针列表, 二分查找到 min和max范围中间的所有指针
let idx = start_points.binary_search(&min).unwrap_or_else(|x| x);
// 得到 上方 min的idx 到 <= max 的地方 这个范围区间
let mut iter = start_points.iter().skip(idx).copied().take_while(|&v| v <= max);
// 把 addr 附近的指针的 和信息都保存起来
for e in iter {
let off = signed_diff(addr, e); // addr附近的e指针 的字节距离
let mut cloned = stack.clone(); // 得到 已经保存的调用栈
cloned.push((e, off)); // 把当前 e 和 距离的字节 记录下信息 (push到了最后一个)
cloned.reverse(); // 翻转一下, 把 最后添加的, 基地址 调换到 第一个, 第一次添加的addr附近的放到最后一个
out.push((final_addrs, cloned)); // 把 由基地址=>addr 指针调用链, 添加到out, 键就是 外部传入的 终止点指针
}
// // 只保存最接近的信息, 找到最接近的指针保存,
// let mut m = iter.next();
// for e in iter {
// let off = signed_diff(addr, e).abs();
// if off < signed_diff(addr, m.unwrap()).abs() { // 如果当前偏移, 小于 上次循环的地址偏移
// m = Some(e); // 保存当前偏移
// }
// }
// if let Some(e) = m {
// let off = signed_diff(addr, e);
// let mut cloned = stack.clone();
// cloned.push((e, off));
// cloned.reverse();
// out.push((final_addrs, cloned));
// }
// 查找b树所有的指针, 指定基础范围, 并且不能超过最大递归深度
if level < max_levels {
for (k, vec) in self.inverse_map.range((Included(&min), Included(&max))) { // 循环的 k就是addr附近的被指向的值, vec就是指向k值的指针列表
let off = signed_diff(addr, *k ); // 在范围内的指针k, 和addr差了具体多少字节
stack.push((*k, off)); // 压栈
for v in vec { // 循环内递归, 又把属于 k值的 上级指针 vec逐个, 传入当前函数, 向上查找 上级的上级指针
self.walk_down_range(*v, lrange, urange, max_levels, level + 1, start_points, out, (final_addrs, stack));
}
stack.pop(); // 每次循环 k 之后, 还原栈
};
}
}
// 传进来 scanner扫描到的地址, 递归的寻找指针调用链, 从所有的非终点的指针
pub fn find_matches(&self, lrange: usize, urange: usize, max_depth: usize, search_for: &Vec<Address>) -> Vec<(Address, Vec<(Address, isize)>)> {
// 从当前所有的指针映射map中 构造调用链, 然后得到 search_for 内的指针的调用链
self.find_matches_addrs(lrange, urange, max_depth, search_for, &self.pointers)
}
// search_for 中保存了 最终的需要的值的 地址,
pub fn find_matches_addrs(&self, lrange: usize, urange: usize, max_depth: usize, search_for: &Vec<Address>, entry_points: &Vec<Address>) -> Vec<(Address, Vec<(Address, isize)>)> {
let mut matches = vec![];
// 循环search_for 终止点的指针, 反向查找 entry_points中保存的基地址
let mut points = entry_points.clone();
points.sort();
for m in search_for.iter() {
self.walk_down_range(*m, lrange, urange, max_depth, 1, &points, &mut matches, (*m, &mut vec![])); // tmp是外部传入的一个栈, 内部需要递归调用
}
matches
}
}
// 计算两个地址之间的差值, a是否大于b, 是否小于b
pub fn signed_diff(a: Address, b: Address) -> isize {
a.as_u64()
.checked_sub(b.as_u64())
.map(|a| a as isize)
.unwrap_or_else(|| -((b - a) as isize))
}
sigmaker
// use iced_x86::{Code, ConstantOffsets, Decoder, DecoderOptions, Instruction, OpKind, Register};
// use pelite::PeFile;
// use memflow::{Address, size, VirtualMemory, VirtualReadData};
// use memflow_win32::Win32Process;
// use memflow_win32::error::*;
// use crate::ch_3_disasm::DisAsm;
//
// const MAX_SIG_LEN: usize = 128;
//
// pub struct SigState<'a> {
// start_ip:Address,
// buf: &'a [u8; MAX_SIG_LEN], // 对ip后面128字节数据的引用
// decoder: Decoder<'a>, // 对应128字节数据的解码器, 所以和buf有一样的生命周期
// instrs: Vec<(Instruction, ConstantOffsets)>, //
// mask: Vec<u8>,
// }
//
// impl SigState<'_> {
// // 添加一条指令
// pub fn add_single_instr(&mut self) -> bool {
// // 如果不能继续解析
// if self.decoder.can_decode() {
// return false;
// }
// // 解码下一条指令,
// let instr = self.decoder.decode();
//
// // 检查指令有效
// if instr.code() == Code::INVALID {
// false
// } else {
// // 添加掩码
// let constant_offsets = self.decoder.get_constant_offsets(&instr); //当前指令相关信息
// for _ in 0..instr.len() {
// self.mask.push(0xff)
// };
// let mask_len = self.mask.len();
// let instr_mask =&mut self.mask[(mask_len - instr.len())..]; // 当前指令掩码的在self.mask中的引用
// Self::mask_instr(&instr, &constant_offsets, instr_mask); // 传递当前指令,当前指令的常量偏移 和当前指令的掩码的引用,
// self.instrs.push((instr, constant_offsets));
// true
// }
// }
// fn mask_instr(instr: &Instruction, offsets: &ConstantOffsets, mask: &mut [u8]) {
// //参数3个: 当前指令,当前指令的常量偏移, 当前指令的掩码的引用(等下直接修改引用就修改了self.mask)
// // 它的作用是根据给定的指令 (Instruction) 和位移信息 (ConstantOffsets) 修改一个掩码数组 (mask),以便在汇编指令处理中忽略特定部分的指令
//
// // 检查指令中是否包含特定基础寄存器
// if let Register::EIP
// | Register::RIP
// | Register::ES
// | Register::CS
// | Register::SS
// | Register::DS
// | Register::FS
// | Register::GS
// | Register::None = instr.memory_base()
// {
// Self::mask_mem(offsets, mask);
// };
//
// // 检查指令中的第一个操作数是否是特定分支类型
// if let Ok(OpKind::NearBranch16)
// | Ok(OpKind::NearBranch32)
// | Ok(OpKind::NearBranch64)
// | Ok(OpKind::FarBranch16)
// | Ok(OpKind::FarBranch32) = instr.try_op_kind(0)
// {
// // 如果是特定分支类型,调用 mask_branch 函数来修改分支部分的掩码
// Self::mask_branch(&offsets, mask, 1);
// }
// }
//
// // 将 掩码mask数组 中与位移有关的位设置为无效, 以便在某些操作中忽略位移部分
// fn mask_mem(offsets: &ConstantOffsets, mask: &mut [u8]) {
// // 这个条件语句检查 ConstantOffsets 结构体中是否包含位移信息。如果包含位移信息,说明汇编指令中有位移部分需要被处理
// if offsets.has_displacement() {
// // 这行代码从 ConstantOffsets 结构体中获取位移的偏移量(offset)。偏移量表示位移在掩码中的起始位置
// let off = offsets.displacement_offset();
// // 这行代码获取位移的大小(size)。大小表示位移在掩码中占用的字节数。
// let size = offsets.displacement_size();
// // 遍历掩码数组中的每个字节。enumerate() 方法用于同时获取字节的索引 i 和字节的可变引用 b
// for (i, b) in mask.iter_mut().enumerate() {
// // 检查当前字节的索引 i 是否在位移范围内。如果是的话,将当前字节的值设置为 0,表示该字节的位是无效的
// if i >= off && i < off + size {
// *b = 0;
// }
// }
// }
// }
//
// fn mask_branch(offsets: &ConstantOffsets, mask: &mut [u8], unmasked_branch_size: usize) {
// // 检查位移信息中是否包含立即数
// if offsets.has_immediate() {
// let off = offsets.immediate_offset(); // 获取立即数的偏移量
// let size = offsets.immediate_size(); // 获取立即数的大小
//
// // 如果立即数的大小大于未经掩码的分支部分大小
// if size > unmasked_branch_size {
// // 遍历掩码数组中的每个字节
// for (i, b) in mask.iter_mut().enumerate() {
// // 如果当前字节的索引位于立即数范围内,则将该字节的值设置为 0,表示无效
// if i >= off && i < off + size {
// *b = 0;
// }
// }
// }
// }
// }
//
// }
//
// #[derive(Default)]
// pub struct Sigmaker {}
//
// impl Sigmaker {
// // 从可执行代码段, 得到指定全局变量
// pub fn find_sigs(process: &mut Win32Process<impl VirtualMemory>, disasm: &DisAsm, target_global_addr:Address) -> Result<Vec<String>> {
// // 得到指定 全局变量:ip列表 的映射
// let ip_addrs = disasm
// .inverse_map
// .get(&target_global_addr)
// .ok_or(Error::Other("没有找到ip列表"))?;
//
// // 寻找全局变量所在的模块
// let module = process
// .module_list()?
// .iter()
// .find(|x| x.base <= target_global_addr && target_global_addr < x.base + x.size)
// .ok_or(Error::Other("没有找到模块"))?
// .clone();
//
// // 读取模块头
// let mut image_buffer = vec![0; size::kb(128)];
//
// // 读取模块的标头的字节数据
// process.virt_mem.virt_read_raw_into(module.base, &mut image_buffer).data_part()?;
//
// // 解析为模块头
// let pefile = PeFile::from_bytes(&image_buffer).expect("pe头解析失败");
//
// // 找到所有段在内存中的位置, 和大小
// let mut ranges = Vec::new(); // 保存了段在模块中的位置, 和段的大小
// for section in pefile.section_headers().iter() {
// // 过滤掉不符合的一些特征的段, 然后扫描整个段内容
// if section.Characteristics & 0x20 != 0 {
// ranges.push((Address::from(module.base+(section.VirtualAddress as usize)), section.VirtualSize as usize));
// }
// }
//
// // 存放了 好多buffer的列表, 保存了每个 ip 对应的 128字节的内存数据, 一共 ip_addrs.len() 个 128字节数据
// // 这些数据有对全局变量操作相关的特征
// let mut bufs = ip_addrs
// .iter()
// .map(|a| (*a, [0; MAX_SIG_LEN]))
// .collect::<Vec<(Address, [u8; MAX_SIG_LEN])>>();
//
// // 保存了 ip_addrs 中每个ip 后的128字节数据
// let mut read_list_info:Vec<_> = bufs
// .iter_mut()
// .map(|(addr, buf)| VirtualReadData(*addr, buf))
// .collect();
//
// // 一次读取全部的 ip_addrs 中的每个ip后的128字节数据, 通过引用读取到 bufs中了
// process.virt_mem.virt_read_raw_list(&mut read_list_info).data_part()?;
//
// let bitness = process.proc_info.proc_arch.bits().into();
// let mut states = bufs
// .iter()
// .map(|(start_ip, buf)| {
// let mut decoder = Decoder::new(bitness, buf, DecoderOptions::NONE);
// decoder.set_ip(start_ip.as_u64()); // 设置解码器的初始地址, 这个初始地址是相较于程序的绝对地址
// SigState {
// start_ip: *start_ip,
// buf,
// decoder,
// instrs: Vec::new(),
// mask:Vec::new()
// }
// })
// .collect::<Vec<_>>();
//
// let mut out = vec![];
// loop {
// let mut added = false;
// for s in states.iter_mut() {
// if s.add_single_instr() {
// added = true;
// }
// }
//
// if !added || Self::has_unique_matches(&states, &mut process.virt_mem, &ranges, &mut out)? {
// break;
// }
//
// }
// Ok(out)
// }
//
// fn has_unique_matches( states: &[SigState], virt_mem: &mut impl VirtualMemory, ranges: &[(Address, usize)], out: &mut Vec<String>) -> Result<bool>{
// let mut sigs: Vec<_> = states
// .iter()
// .map(|s| (s.start_ip, s.buf, &s.mask, 0))
// .collect();
//
// // 找到所有段在内存中的位置, 和大小
// let mut buffer = vec![0; size::kb(64) + MAX_SIG_LEN - 1];
//
// // 循环 读取 段内的内存, 每次读取64k
// for (addr, size) in ranges.iter() {
// // 读取成功, 现在应该在64k的空间内继续查找
// for off in (0..*size).step_by(size::kb(64)) {
// let addr = *addr + off; // 地址应该是 段基础地址 + 当前段内读取的64k偏移
// virt_mem.virt_read_raw_into(addr, &mut buffer).data_part()?;
//
// // 滚动64k, 每次读128字节
// for (off, w) in buffer.windows(MAX_SIG_LEN).enumerate() {
// let addr = addr + off;
// for (start_ip, bytes, mask, dup_matches) in sigs.iter_mut() {
// let win_iter = w.iter().zip(mask.iter()).map(|(&w, &m)| w & m);
// let bytes_iter = bytes.iter().zip(mask.iter()).map(|(&w, &m)| w & m);
// if win_iter.eq(bytes_iter) && addr != *start_ip {
// *dup_matches += 1;
// }
// }
// }
//
// }
// }
// let mut has_unique = false;
//
// for (_, buf, mask, dup_matches) in sigs {
// if dup_matches == 0 {
// has_unique = true;
// out.push(Self::bytes_to_string(buf, mask));
// }
// }
//
// Ok(has_unique)
// }
//
// fn bytes_to_string(bytes: &[u8], mask: &[u8]) -> String {
// // 使用 zip 方法将字节数组和掩码数组中的对应字节配对
// // 对每个配对进行映射操作
// let result: Vec<String> = bytes
// .iter()
// .zip(mask.iter())
// .map(|(&b, &m)| {
// // 如果掩码为 0,则将该字节替换为 "?"
// // 否则,使用格式化字符串将字节转换为十六进制表示
// if m == 0 {
// "?".to_string()
// } else {
// format!("{:02X}", b)
// }
// })
// .collect(); // 将映射结果收集到一个字符串向量中
//
// // 使用 join 方法将字符串向量中的元素用空格连接成一个单独的字符串
// result.join(" ")
// }
//
// }
use memflow::mem::{VirtualMemory, VirtualReadData};
use memflow::types::{size, Address};
use memflow_win32::error::*;
use memflow_win32::win32::Win32Process;
use iced_x86::{Code, ConstantOffsets, Decoder, DecoderOptions, Instruction, OpKind, Register};
use pelite::PeFile;
use crate::ch_3_disasm::DisAsm;
const MAX_SIG_LENGTH: usize = 128;
struct Sigstate<'a> {
start_ip: Address,
buf: &'a [u8; MAX_SIG_LENGTH],
decoder: Decoder<'a>,
instrs: Vec<(Instruction, ConstantOffsets)>,
mask: Vec<u8>,
}
impl Sigstate<'_> {
fn add_single_instr(&mut self) -> bool {
if !self.decoder.can_decode() {
return false;
}
let instr = self.decoder.decode();
if instr.code() == Code::INVALID {
false
} else {
let constant_offsets = self.decoder.get_constant_offsets(&instr);
self.mask.extend((0..instr.len()).map(|_| 0xff));
let mask_len = self.mask.len();
let instr_mask = &mut self.mask[(mask_len - instr.len())..];
Self::mask_instr(&instr, &constant_offsets, instr_mask);
self.instrs.push((instr, constant_offsets));
true
}
}
fn mask_instr(instr: &Instruction, offsets: &ConstantOffsets, mask: &mut [u8]) {
if let Register::EIP
| Register::RIP
| Register::ES
| Register::CS
| Register::SS
| Register::DS
| Register::FS
| Register::GS
| Register::None = instr.memory_base()
{
Self::mask_mem(offsets, mask);
}
if let Ok(OpKind::NearBranch16)
| Ok(OpKind::NearBranch32)
| Ok(OpKind::NearBranch64)
| Ok(OpKind::FarBranch16)
| Ok(OpKind::FarBranch32) = instr.try_op_kind(0)
{
Self::mask_branch(&offsets, mask, 1);
}
}
fn mask_branch(offsets: &ConstantOffsets, mask: &mut [u8], unmasked_branch_size: usize) {
if offsets.has_immediate() {
let off = offsets.immediate_offset();
let size = offsets.immediate_size();
if size > unmasked_branch_size {
for (i, b) in mask.iter_mut().enumerate() {
if i >= off && i < off + size {
*b = 0;
}
}
}
}
}
fn mask_mem(offsets: &ConstantOffsets, mask: &mut [u8]) {
if offsets.has_displacement() {
let off = offsets.displacement_offset();
let size = offsets.displacement_size();
for (i, b) in mask.iter_mut().enumerate() {
if i >= off && i < off + size {
*b = 0;
}
}
}
}
}
#[derive(Default)]
pub struct Sigmaker {}
impl Sigmaker {
fn has_unique_matches(
states: &[Sigstate],
virt_mem: &mut impl VirtualMemory,
ranges: &[(Address, usize)],
out: &mut Vec<String>,
) -> Result<bool> {
let mut sigs: Vec<_> = states
.iter()
.map(|s| (s.start_ip, s.buf, &s.mask, 0))
.collect();
const CHUNK_SIZE: usize = size::kb(4);
let mut buf = vec![0; CHUNK_SIZE + MAX_SIG_LENGTH - 1];
for &(addr, size) in ranges {
for off in (0..size).step_by(CHUNK_SIZE) {
let addr = addr + off;
virt_mem
.virt_read_raw_into(addr, buf.as_mut_slice())
.data_part()?;
for (off, w) in buf.windows(MAX_SIG_LENGTH).enumerate() {
let addr = addr + off;
for (start_ip, bytes, mask, dup_matches) in sigs.iter_mut() {
let win_iter = w.iter().zip(mask.iter()).map(|(&w, &m)| w & m);
let bytes_iter = bytes.iter().zip(mask.iter()).map(|(&w, &m)| w & m);
if win_iter.eq(bytes_iter) && addr != *start_ip {
*dup_matches += 1;
}
}
}
}
}
let mut has_unique = false;
for (_, buf, mask, dup_matches) in sigs {
if dup_matches == 0 {
has_unique = true;
out.push(Self::bytes_to_string(buf, mask));
}
}
Ok(has_unique)
}
fn bytes_to_string(bytes: &[u8], mask: &[u8]) -> String {
bytes
.iter()
.zip(mask.iter())
.map(|(&b, &m)| {
if m == 0 {
"?".to_string()
} else {
format!("{:02X}", b)
}
})
.collect::<Vec<_>>()
.join(" ")
}
pub fn find_sigs(
process: &mut Win32Process<impl VirtualMemory>,
disasm: &DisAsm,
target_global: Address,
) -> Result<Vec<String>> {
let addrs = disasm
.inverse_map
.get(&target_global)
.ok_or(Error::Other("Invalid global variable"))?;
let module = process
.module_list()?
.into_iter()
.find(|m| m.base <= target_global && m.base + m.size > target_global)
.ok_or(Error::Other("Could not find target module"))?;
let mut image = vec![0; size::kb(128)];
process
.virt_mem
.virt_read_raw_into(module.base, &mut image)
.data_part()?;
let pefile =
PeFile::from_bytes(&image).map_err(|_| Error::Other("Failed to parse header"))?;
const IMAGE_SCN_CNT_CODE: u32 = 0x20;
let ranges: Vec<(Address, usize)> = pefile
.section_headers()
.iter()
.filter(|s| (s.Characteristics & IMAGE_SCN_CNT_CODE) != 0)
.map(|s| {
(
module.base + s.VirtualAddress as usize,
s.VirtualSize as usize,
)
})
.collect();
let mut bufs: Vec<(Address, [u8; MAX_SIG_LENGTH])> =
addrs.iter().map(|&a| (a, [0; MAX_SIG_LENGTH])).collect();
let mut read_list: Vec<_> = bufs
.iter_mut()
.map(|(a, b)| VirtualReadData(*a, b))
.collect();
process
.virt_mem
.virt_read_raw_list(&mut read_list)
.data_part()?;
let bitness = process.proc_info.proc_arch.bits().into();
let mut states: Vec<_> = bufs
.iter()
.map(|(start_ip, buf)| {
let mut decoder = Decoder::new(bitness, buf, DecoderOptions::NONE);
decoder.set_ip(start_ip.as_u64());
Sigstate {
start_ip: *start_ip,
buf,
decoder,
instrs: vec![],
mask: vec![],
}
})
.collect();
let mut out = vec![];
loop {
let mut added = false;
for s in states.iter_mut() {
if s.add_single_instr() {
added = true;
}
}
if !added
|| Self::has_unique_matches(&states, &mut process.virt_mem, &ranges, &mut out)?
{
break;
}
}
Ok(out)
}
}
scanner
use std::io::Read;
use memflow::{Address, PartialResultExt, size, VirtualMemory};
// 一个扫描仪
#[derive(Default)]
pub struct ValueScanner {
pub matches: Vec<Address>, // 存放了 符合 data 数据的内存地址
// mem_maps: Vec<(Address, usize)>
}
impl ValueScanner{
pub fn reset(&mut self) -> &mut Self {
self.matches.clear();
// self.mem_maps.clear();
self
}
pub fn scan<T: VirtualMemory>(&mut self, virt_mem: &mut T, data: &[u8]) -> &mut Self{
if self.matches.is_empty() {
let mut buffer = vec![0; size::kb(64) + data.len() - 1];
// 合并低范围的内存, 限定合并空间, 减少将来需要扫描的范围
let mut mem_maps = virt_mem.virt_page_map_range(size::mb(16), Address::NULL, (1_usize << 47).into());
// 循环所有的内存块
for (block_addr, block_size) in mem_maps.iter(){
// 循环 读取块内内存, 每次读取64k
for offset in (0..*block_size).step_by(size::kb(64)) {
if let Ok(_) = virt_mem.virt_read_raw_into(*block_addr + offset, &mut buffer).data_part() {
// 读取成功, 现在应该在64k的空间内继续查找
for (o, buf) in buffer.windows(data.len()).enumerate() {
if buf == data {
let addr = *block_addr + offset + o;
self.matches.push(addr);
}
}
}
}
}
} else {
let mut new_matches = vec![];
// 从已有扫描出来的继续扫描
let mut buffer = vec![0; data.len()];
for addr in self.matches.iter() {
virt_mem.virt_read_raw_into(*addr, &mut buffer).data_part().unwrap();
if &buffer == data {
new_matches.push(*addr)
}
}
self.matches = new_matches;
}
self
}
// 返回一个匹配规则迭代器
pub fn matches_iter(&self) -> &Vec<Address> {
&self.matches
}
// 循环所有匹配的地址, 打印
pub fn print_matches<T: VirtualMemory>(&self, virt_mem: &mut T, buf_len:usize, type_name: &str) {
// let buf = Vec::new();
for addr in self.matches.iter(){
let buf = virt_mem.virt_read_raw(*addr, buf_len).data_part().unwrap();
match type_name {
"i64" => {
let value = i64::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"u64" => {
let value = u64::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"i32" => {
let value = i32::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"u32" => {
let value = u32::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"i16" => {
let value = i16::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"u16" => {
let value = u16::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"i8" => {
let value = i8::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"u8" => {
let value = u8::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"f64" => {
let value = f64::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"f32" => {
let value = f32::from_ne_bytes(buf.try_into().unwrap());
println!("addr: {addr}, value: {value}")
}
"str" => {
println!("ddr: {addr}, {}", String::from_utf8_lossy(&buf));
}
"str_utf16" => {
let mut v = vec![];
for w in buf.chunks_exact(2) {
let u = u16::from_ne_bytes(w.try_into().unwrap());
v.push(u);
}
let s = String::from_utf16_lossy(&v);
println!("addr: {addr}, value: {s}")
}
_ => {
println!("无效的类型: {}", type_name);
}
}
}
}
}