black-hat-rust-chapter01

阅读笔记-第一章

Posted by 汤汤 on October 16, 2024

来自 black-hat-rust

写在前面

chapter01

1. 攻击阶段

侦察

目的:获取目标尽可能多的信息 如,开发者名字、互联网上的机器数量、运行的服务…

  • 被动
    • 使用公开数据
  • 主动
    • 直接扫描目标网络
利用
  • 0-day漏洞或非0-day漏洞
  • 社会工程
横向(lateral)移动
  • 目的:保持访问权限、获得更多资源和系统
  • 挑战:尽可能隐蔽
  • 工具:远程访问工具RATs
数据渗透

通过网络传输大量数据,很容易被发现

清理
  • logs
  • 临时文件
  • 钓鱼网站
  • 基础设施

2. 攻击者简介

hacker
exploit writer

武器化

developer
  • 自定义攻击工具(代理、credential dumpers等)
  • 使用公共、现有工具
系统管理员

一旦攻略“系统管理员”成功,即可保护攻击方的基础设施,也可帮助后期的利用横向移动阶段

分析者

解释攻击结果、分析目标的具体优先级

3. Rust与其他语言的区别

编程语言 问题
C,C++ 不够安全
python 慢;弱类型导致难以编写大型程序
Java 依赖较长的运行时间

4. Awesome Rust

Setup
安装rustup

借助rustup管理工具链
可执行rustup update进行升级更新

  • 安装方法
    • windows 下载地址
    • 类unix安装命令 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
      • 但是老报错👀,可能压缩包太大了,网络状况堪忧,推荐下一种方法
  • 官方链接找到适合自己操作系统的压缩包下载、解压,然后运行sudo ./install.sh进行安装
    • 安装包括编译器rustc\包管理器cargo\
  • 验证
    • 安装完成后,运行rustc --version
      • 这里的rustc是rust编辑器
      • 我的显示rustc 1.82.0 (f6e511eec 2024-10-15),安装成功啦🥂🥂🥂
cargo安装各类工具
  • 换国内源
    • /home/.cargo/config.toml中添加
[source.crates-io]
replace-with = 'ustc'

[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
  • 添加环境变量
    • cargo install rustfmt执行后,有一个warning:warning: be sure to add /home/lxh/.cargo/bin to your PATH to be able to run the installed binaries提示需要将bin加入环境变量
      • 查看当前环境变量echo $PATH
    • 方法1:export PATH=/home/lxh/.cargo/bin:$PATH
      • 缺点:一次性修改
    • 方法2:修改~/.bashrc文件,并终端执行$ source ~/.bashrc立即生效,且永久有效
    • 方法3:修改/etc/profile,同2
配置编辑器
  • 在vscode中装一个rust-analyzer插件,支持命令补全、代码检查
  • 待补充…

5. 小试牛刀

5.1 代码框架
  1. cargo new projectName1使用包管理器创建一个新项目。创建成功后项目目录如下所示:

    projectName1
    ├── Cargo.lock
    ├── Cargo.toml
    ├── src
    │   └── main.rs
    └── target
    
       + 其中,Cargo.toml中  
    
    [package]
    name = "projectName1"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
  2. toml文件中加上某些依赖,就能在代码中使用相应函数

5.2 示例代码分析
fn main()-> Result<(), Box<dyn Error>>{
    println!("Hello, world!");
    let args: Vec<String> = env::args().collect();

    if args.len() != 3 {
        println!("usage:");
        println!("sha1-cracker:<worklist.txt><sha1-hash>");
        return Ok(());
    }

    let hash_to_crack = args[2].trim();
    if hash_to_crack.len() != SHA1_HEX_STRING_LENGTH{
        return Err("sha1 hash is not valid".into());
    }

    // args[0]:程序路径;args[1]:程序的第一个参数
    // ?操作符用于传播错误
    let wordlist_file = File::open(&args[1])?;
    let reader = BufReader::new(&wordlist_file);

    // reader.lines()返回一个迭代器result<String, std::io::Error>
    for line in reader.lines(){
        // let line = line?.trim().to_string();
        // println!("{}",line);
        let line = line?;
        let common_passwd = line.trim();
        // 在toml中添加依赖,在这里使用
        if hash_to_crack == &hex::encode(sha1::Sha1::digest(common_passwd.as_bytes())){
            println!("password found: {}", &common_passwd);
            return Ok(());
        }
    }
    println!("password not found in wordlist");

    Ok(())
}
5.2.1函数返回值 Result<T,E>
// 枚举类型,其中T是泛型参数,存放方式为Ok(T)
// E表示发生错误时存入的错误值,存放方式为Err(E)
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Q:🤷🏻 🤷 🤷我怎么获得变量类型和函数返回值类型? A1:可查询标准库或第三方库函数⛔ A2:可借助VScode+rust-analyzer获得变量类型和函数返回类型✅ A3:分析报错信息✅

error[E0308]: mismatched types
  --> src/main.rs:18:16
   |
11 | fn main() {
   |          - expected `()` because of default return type
...
18 |         return Ok(());
   |                ^^^^^^ expected `()`, found `Result<(), _>`
   |
   = note: expected unit type `()`
                   found enum `Result<(), _>`
5.2.2 Box与dyn
// 截取部分示例代码
fn main()-> Result<(), Box<dyn Error>>{
  ...
}

struct Box<T, A = Global>,其中A是allocator; a pointer type that uniquely owns a heap allocaton of type T

  • 是什么?
    • Box是一个智能指针,指向堆分配的T类型的值。
    • Box<T>将值装箱使它能在堆上分配,当箱子离开作用域时,1️⃣会调用析构函数,2️⃣并销毁内部对象、3️⃣释放堆上内存。
    • 拆箱/解引用:*
  • 为什么?
    • 数据大小不确定时,使用堆分配避免栈溢出
    • 某些类型大小不确定,如递归数据结构
    • trait对象
      这里的trait和dyn是啥
5.2.3 Trait对象与dyn
  • 是什么?
    • trait类似于其它语言中的interface
    • dyn是一个关键字,
      • 使用dyn关键字,不在编译阶段静态确定,取而代之的是在运行阶段通过动态分发来调用trait方法
  • 静态分发与动态分发
    • 静态分发:在编译时确定方法调用的具体实现,使用泛型(❓什么是泛型)和impl Trait,不支持多态。
    • 动态分发:在运行时才确定,使用dyn Trait
// 这里举一个使用dyn T的例子
// 运行时根据“动物类型”动态决定“speak”方法
let animals: Vec<Box<dyn Animal>> = vec![Box::new(Dog), Box::new(Cat)];
for animal in animals {
    animal.speak();
}
5.2.4 泛型参数
  • 泛型参数
    • 在定义函数、结构体、枚举、方法等时,使用占位符表示任意类型
    • 使用trait约束来限制泛型参数的类型范围
// 泛型参数举例--结构体
struct Point<T> {
    x: T,
    y: T,
}
let int_point = Point { x: 5, y: 10 };

// 泛型参数举例--使用trait约束
// T: std::fmt::Display 表示T必须实现Display接口
fn print_value<T: std::fmt::Display>(value: T) {
    println!("{}", value);
}  
5.2.5 RAII(资源获取时初始化)
// 截取部分示例代码
let wordlist_file = File::open(&args[1])?;

❓ 文件操作只有open,没有close

RAII(resource acquisition is initialization)资源获取时初始化 在rust中,变量不仅指代相应的内存,也拥有资源(如下面例子中的文件资源)。

  • 当超出作用域时,对象会自动调用析构函数,释放内存
    • 这里的文件句柄wordlist_file的作用域为main函数,当main函数返回时,文件会自动关闭
5.2.6 Ok(())
  • statements-oriented language 声明式语言
    • return Ok(());以“;”结尾
  • expression-oriented language 表达式语言
    • Ok(())没有标点符号

6. For:初学者

  • All this stuff is really interesting and produced by very smart people, but it prevents you from getting things done.
  • 命令式语言+函数式语言
Rust的内存安全机制
所有权
借用
生命周期

lifetime
编译器需要知道引用的数据在内存中存活的时间,以保证引用(🤷🏻什么是引用)在有效范围内是安全的
如果编译器无法推断出引用的生命周期,则要求程序员显式提供生命周期注释

  • 生命周期注释
    • 用法:'+标识符,如'a,通常放在函数签名(🤷🏼什么是函数签名)或结构体的定义中

  1. 引用-Reference

    指向另一个变量数据的指针,允许在不移动数据所有权的情况下访问或使用数据,从而实现共享和安全的数据访问 核心是借用变量,而不改变变量的所有权

  • 不可变引用&
    • 只读访问数据,不能修改引用指向的数据
  • 可变引用&mut
    • 允许修改数据
    • 同一时间只能有一个可变引用,且不能与不可变引用同时存在
// 不可变引用和可变引用不能同时存在
fn main() {
    let mut s = String::from("hello");
    let r1 = &s;       // 不可变引用
    let r2 = &mut s;   // 编译错误:不可变引用和可变引用不能同时存在
    println!("{}, {}", r1, r2);
}
// 编译器使用 借用检查器borrow checker 防止出现悬垂引用
fn main() {
    let r;
    {
        let x = 5;
        r = &x; // 编译错误:x 的生命周期在此块结束时结束
    }
    println!("{}", r); // 如果编译通过,r 将指向无效的内存
}

  • 另外,Rust也支持函数传参引用、多线程引用
  1. 函数签名
    • 一般来说,包括:函数名、参数列表、返回类型
    • 作用:
    • 描述函数接口,方便被调用
    • 编译器用来验证类型是否匹配,以保证函数调用的类型安全 (🤷什么是类型安全) + Rust中还可能包括
    • 泛型参数
    • 生命周期注释
  2. 类型安全
    • Rust是静态类型语言,在编译时会进行类型检查。同时支持类型推断,因此不需要显示标注每个变量的类型。
    • 但是,不允许隐式的类型转换,必须显式的进行。

引用计数智能指针

生命周期注释增加了代码复杂性、降低了可读性 对一些共享(or not)、可变(or not)引用来说,使用生命周期注释相当麻烦 一种解决方案是:引用计数智能指针std::rc::Rc(reference counting)

use std::rc::Rc;

fn main() {
    let pointer = Rc::new(String::from("Hello, Rust!"));
    
    {
        // 这里调用Rc.clone()只会增加引用计数,而不需要深拷贝
        // 当引用计数为0时,才会清理数据
        let second_pointer = Rc::clone(&pointer); // 增加引用计数
        println!("{}", second_pointer); // 访问共享的 `String` 数据
    } // `second_pointer` 超出作用域,引用计数减少
    
    println!("{}", pointer); // 原始引用仍然有效
}

**引用计数** 天然的 存在**循环引用**的问题


  • Rc–单线程场景下实现引用计数
    use std::cell::{Refcell, RefMut};
    use std::rc::Rc;
    
  • Arc–多线程场景下实现引用计数
维护与更新
  • rustup 本地工具链
rustup self update
rustup update
  • rust fmt 代码格式化工具cargo fmt
  • clippy 检测可能导致错误的代码
rustup component add clippy
cargo clippy 
  • cargo update
    cargo update