每月 Shaarli

一月内的所有链接,汇聚在一个页面上。

January, 2025

全国互联网安全管理服务平台

网站备案专用

分享

lpv4.cn,qqdns.top,ukweb.qqdns.top

Debian 12 / Ubuntu 24.04 安装 Docker 以及 Docker Compose 教程 - 烧饼博客

本文将指导如何在 Debian 12 和 Ubuntu 24.04 下安装 Docker 以及 Docker Compose。

Note: Rust机器码及注册机

注册机

//Cargo.toml

[dependencies]
rsa = "0.9.7"
rand = "0.8.5"
sha2 = "0.10.8"
pkcs1 = { version = "0.7.5", features = ["pem"] }
hex = "0.4.3"

// main.rs

use rand::rngs::OsRng;
use rsa::pkcs1::{
    DecodeRsaPrivateKey, DecodeRsaPublicKey, EncodeRsaPrivateKey, EncodeRsaPublicKey, LineEnding,
};
use rsa::signature::SignatureEncoding;
use rsa::signature::{Signer, Verifier};
use rsa::{pkcs1v15::SigningKey, pkcs1v15::VerifyingKey, RsaPrivateKey, RsaPublicKey};
use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::{self, Read, Write};

/// 生成 RSA 公私钥并保存到文件
fn generate_keys() -> io::Result<()> {
    let mut rng = OsRng;

    // 生成 2048 位私钥
    let private_key = RsaPrivateKey::new(&mut rng, 2048).expect("密钥生成失败");
    let public_key = RsaPublicKey::from(&private_key);

    // 保存私钥到文件
    let private_key_pem = private_key
        .to_pkcs1_pem(LineEnding::LF)
        .expect("私钥编码失败: 无法将私钥编码为 PKCS#1 格式");
    let mut private_key_file = File::create("private_key.pem")?;
    private_key_file.write_all(private_key_pem.as_bytes())?;

    // 保存公钥到文件
    let public_key_pem = public_key
        .to_pkcs1_pem(LineEnding::LF)
        .expect("公钥编码失败: 无法将公钥编码为 PKCS#1 格式");
    let mut public_key_file = File::create("public_key.pem")?;
    public_key_file.write_all(public_key_pem.as_bytes())?;

    println!("密钥已生成并保存到 'private_key.pem' 和 'public_key.pem' 文件中。");
    Ok(())
}

/// 根据机器码生成授权文件
fn generate_license() -> io::Result<()> {
    // 让用户输入机器码
    println!("请输入机器码:");
    let mut machine_code = String::new();
    io::stdin().read_line(&mut machine_code)?;
    let machine_code = machine_code.trim();
    if machine_code.is_empty() || !machine_code.chars().all(|c| c.is_ascii_alphanumeric()) {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "机器码格式无效: 应仅包含字母和数字",
        ));
    }

    // 从文件中读取私钥
    let private_key_pem = std::fs::read_to_string("private_key.pem")?;
    let private_key = RsaPrivateKey::from_pkcs1_pem(&private_key_pem).expect("无效的私钥");

    // 计算机器码的哈希
    let mut hasher = Sha256::new();
    hasher.update(machine_code);
    let hashed_machine_code = hasher.finalize();

    // 使用私钥对哈希签名
    let signing_key = SigningKey::<Sha256>::new_unprefixed(private_key);
    let signature = signing_key.sign(&hashed_machine_code);

    // 将机器码和签名保存到授权文件
    let license_filename = format!("{}_license.dat", machine_code);
    let mut license_file = File::create(&license_filename)?;
    if license_file.metadata().is_err() {
        return Err(io::Error::new(io::ErrorKind::Other, "无法创建授权文件"));
    }
    writeln!(license_file, "{}", machine_code)?; // 保存机器码并换行
    writeln!(license_file, "{}", hex::encode(signature.to_bytes()))?; // 保存签名为十六进制字符串

    println!("授权文件 '{}' 已创建。", license_filename);
    Ok(())
}

/// 批量生成授权文件
fn batch_generate_licenses() -> io::Result<()> {
    // 读取 licenses.txt 文件
    let mut file = match File::open("licenses.txt") {
        Ok(f) => f,
        Err(_) => {
            println!("未找到 'licenses.txt' 文件,请创建该文件,每行输入一个机器码。");
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "缺少 licenses.txt 文件",
            ));
        }
    };
    let mut content = String::new();
    file.read_to_string(&mut content)?;

    // 逐行处理机器码
    for machine_code in content.lines() {
        let machine_code = machine_code.trim();
        if machine_code.is_empty() || !machine_code.chars().all(|c| c.is_ascii_alphanumeric()) {
            eprintln!("跳过无效机器码: {}", machine_code);
            continue;
        }

        // 从文件中读取私钥
        let private_key_pem = std::fs::read_to_string("private_key.pem")?;
        let private_key = RsaPrivateKey::from_pkcs1_pem(&private_key_pem).expect("无效的私钥");

        // 计算机器码的哈希
        let mut hasher = Sha256::new();
        hasher.update(machine_code);
        let hashed_machine_code = hasher.finalize();

        // 使用私钥对哈希签名
        let signing_key = SigningKey::<Sha256>::new_unprefixed(private_key);
        let signature = signing_key.sign(&hashed_machine_code);

        // 将机器码和签名保存到授权文件
        let license_filename = format!("{}_license.dat", machine_code);
        let mut license_file = File::create(&license_filename)?;
        if license_file.metadata().is_err() {
            eprintln!("无法创建授权文件: {}", license_filename);
            continue;
        }
        writeln!(license_file, "{}", machine_code)?; // 保存机器码并换行
        writeln!(license_file, "{}", hex::encode(signature.to_bytes()))?; // 保存签名为十六进制字符串

        println!("授权文件 '{}' 已创建。", license_filename);
    }

    Ok(())
}

/// 使用公钥验证授权文件
fn verify_license() -> io::Result<()> {
    // 让用户输入机器码
    println!("请输入机器码:");
    let mut machine_code = String::new();
    io::stdin().read_line(&mut machine_code)?;
    let machine_code = machine_code.trim();
    if machine_code.is_empty() || !machine_code.chars().all(|c| c.is_ascii_alphanumeric()) {
        return Err(io::Error::new(
            io::ErrorKind::InvalidInput,
            "机器码格式无效: 应仅包含字母和数字",
        ));
    }

    let license_filename = format!("{}_license.dat", machine_code);

    // 检查授权文件是否存在
    if !std::path::Path::new(&license_filename).exists() {
        return Err(io::Error::new(io::ErrorKind::NotFound, "授权文件不存在"));
    }

    // 从文件中读取公钥
    let public_key_pem = std::fs::read_to_string("public_key.pem")?;
    let public_key = RsaPublicKey::from_pkcs1_pem(&public_key_pem)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "公钥读取失败或格式无效"))?;

    // 读取授权文件
    let mut license_file = File::open(&license_filename)?;
    let mut content = String::new();
    license_file.read_to_string(&mut content)?;

    // 按行解析授权文件内容
    let mut lines = content.lines();
    let machine_code_in_file = lines
        .next()
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "授权文件中缺少机器码"))?;
    let signature_hex = lines
        .next()
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "授权文件中缺少签名"))?;
    let signature_bytes = hex::decode(signature_hex)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "签名格式无效"))?;

    // 验证机器码匹配
    if machine_code != machine_code_in_file {
        return Err(io::Error::new(io::ErrorKind::InvalidData, "机器码不匹配"));
    }

    // 计算机器码的哈希
    let mut hasher = Sha256::new();
    hasher.update(machine_code);
    let hashed_machine_code = hasher.finalize();

    // 验证签名
    let verifying_key = VerifyingKey::<Sha256>::new_unprefixed(public_key);
    let signature = rsa::pkcs1v15::Signature::try_from(signature_bytes.as_slice())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "签名无效"))?;
    verifying_key
        .verify(&hashed_machine_code, &signature)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "授权验证失败"))?;

    println!("授权验证成功!");
    Ok(())
}

fn main() {
    loop {
        println!("请选择功能:");
        println!("1. 生成 RSA 密钥");
        println!("2. 为机器码生成授权文件");
        println!("3. 批量生成授权文件");
        println!("4. 验证授权文件");
        println!("5. 退出");

        let mut choice = String::new();
        io::stdin().read_line(&mut choice).expect("读取输入失败");
        let choice = choice.trim();

        match choice {
            "1" => {
                if let Err(e) = generate_keys() {
                    eprintln!("密钥生成失败:{}", e);
                }
            }
            "2" => {
                if let Err(e) = generate_license() {
                    eprintln!("授权文件生成失败:{}", e);
                }
            }
            "3" => {
                if let Err(e) = batch_generate_licenses() {
                    eprintln!("批量授权文件生成失败:{}", e);
                }
            }
            "4" => {
                if let Err(e) = verify_license() {
                    eprintln!("授权验证失败:{}", e);
                }
            }
            "5" => {
                println!("退出程序。");
                break;
            }
            _ => println!("无效的选项,请重新选择。"),
        }
    }
}

机器码

//Cargo.toml

winapi = { version = "0.3.9", features = ["fileapi", "sysinfoapi", "winnt"] }
sha2 = "0.10.8"
rsa = "0.9.7"
reqwest = { version = "0.12.12", features = ["blocking"] }
hex = "0.4.3"
cipher = { version = "0.4.4", features = ["block-padding"] }
aes = "0.8.4"
cbc = "0.1.2"

// 模块 register_app.rs

extern crate winapi;

use std::ffi::OsString;
use std::os::windows::ffi::OsStrExt;
use std::process::Command;
use winapi::um::fileapi::GetVolumeInformationW;

use aes::Aes128;
use cipher::{block_padding::Pkcs7, BlockEncryptMut, KeyIvInit};
use sha2::{Digest, Sha256};

use reqwest::blocking::get;
use rsa::pkcs1::DecodeRsaPublicKey;
use rsa::signature::Verifier;
use rsa::{pkcs1v15::VerifyingKey, RsaPublicKey};
use std::path::Path;
use std::{
    fs,
    io::{self, Read},
};

// 类型定义:AES加密模式
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;

/// 更新 public_key 文件
pub fn update_public_key() -> Result<String, io::Error> {
    // 远程公钥的URL
    let remote_url = "http://dl.r1.cccxx.cc//file/register/public_key.pem";

    // 步骤1:尝试从远程URL获取公钥
    let public_key = match get(remote_url) {
        Ok(response) => {
            if response.status().is_success() {
                response.text().ok()
            } else {
                None
            }
        }
        Err(_) => None,
    };

    // 如果成功且非空,请将其保存到本地文件并返回
    if let Some(key) = public_key {
        if !key.trim().is_empty() {
            fs::write("public_key.pem", &key).map_err(|e| {
                io::Error::new(io::ErrorKind::Other, format!("保存公钥失败: {}", e))
            })?;
            return Ok(key);
        }
    }

    // 步骤2:如果远程获取失败,请检查本地文件
    if let Ok(public_key_pem) = fs::read_to_string("public_key.pem") {
        if let Ok(_) = RsaPublicKey::from_pkcs1_pem(&public_key_pem) {
            return Ok(public_key_pem);
        } else {
            return Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "公钥读取失败或格式无效",
            ));
        }
    }

    // 步骤3:使用内置公钥作为回退
    let built_in_public_key = r"-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAox+Lis9OWXQIPAf9lvQvtRIhiTKZi1wOgv6JBHcKqFFSzCEUVFSE
I+8jOH386HxT7e8UpIuuUqC5wuMKfYwF5Ohn/IaegtPVklRxD0fewK6Uhflcwhmn
FeL+AtPk0FRxqd11SY50t1DOPY1H23RICa9+QuSHZ5aecnRtqjIricDfCC175mKm
NOZu8pXFFxKAYqVtky2BrVtEKmp7qyklSzhdtS88SOuN5foUdEoPppx3WGlUwV8X
6vUcHgYIOcHgYOWMqMqA+yFBjLlIXXDjBr7WvQke2jFlQ2C4M0kcxeyJSpXakIF/
Ot+QM3XQHk8nAoZ4USHqWFNSpFTZ8tr1sQIDAQAB
-----END RSA PUBLIC KEY-----";

    // 如果其他选项不起作用,请将内置公钥保存到本地文件
    fs::write("public_key.pem", built_in_public_key)
        .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("保存内置公钥失败: {}", e)))?;

    Ok(built_in_public_key.to_string())
}

/// 验证授权文件
pub fn verify_license() -> Result<(), io::Error> {
    // 获取机器码
    let machine_code = generate_machine_code();

    // 授权文件名
    let license_filename = format!("{}_license.dat", machine_code);

    // 检查授权文件是否存在
    if !Path::new(&license_filename).exists() {
        return Err(io::Error::new(io::ErrorKind::NotFound, "授权文件不存在"));
    }

    // 读取公钥
    let public_key_pem = fs::read_to_string("public_key.pem")?;
    let public_key = RsaPublicKey::from_pkcs1_pem(&public_key_pem)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "公钥格式无效"))?;

    // 读取授权文件
    let mut license_file = fs::File::open(&license_filename)?;
    let mut content = String::new();
    license_file.read_to_string(&mut content)?;

    // 按行解析授权文件内容
    let mut lines = content.lines();
    let machine_code_in_file = lines
        .next()
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "授权文件中缺少机器码"))?;
    let signature_hex = lines
        .next()
        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "授权文件中缺少签名"))?;
    let signature_bytes = hex::decode(signature_hex)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "签名格式无效"))?;

    // 验证机器码匹配
    if machine_code != machine_code_in_file {
        return Err(io::Error::new(io::ErrorKind::InvalidData, "机器码不匹配"));
    }

    // 计算机器码的哈希
    let mut hasher = Sha256::new();
    hasher.update(machine_code);
    let hashed_machine_code = hasher.finalize();

    // 验证签名
    let verifying_key = VerifyingKey::<Sha256>::new_unprefixed(public_key);
    let signature = rsa::pkcs1v15::Signature::try_from(signature_bytes.as_slice())
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "签名无效"))?;
    verifying_key
        .verify(&hashed_machine_code, &signature)
        .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "授权验证失败"))?;

    println!("授权验证成功!");
    Ok(())
}

// 获取硬盘序列号
fn get_disk_serial() -> Option<String> {
    let mut volume_name = [0u16; 1024];
    let mut volume_serial_number: u32 = 0;
    let mut file_system_name = [0u16; 1024];
    let mut file_system_flags: u32 = 0;

    let c_drive = "C:\\";
    let c_drive_wide: Vec<u16> = OsString::from(c_drive)
        .encode_wide()
        .chain(std::iter::once(0))
        .collect();

    unsafe {
        let result = GetVolumeInformationW(
            c_drive_wide.as_ptr(),
            volume_name.as_mut_ptr(),
            volume_name.len() as u32,
            &mut volume_serial_number,
            std::ptr::null_mut(),
            &mut file_system_flags,
            file_system_name.as_mut_ptr(),
            file_system_name.len() as u32,
        );

        if result != 0 {
            Some(format!("{:X}", volume_serial_number))
        } else {
            None
        }
    }
}

// 通用 WMIC 获取信息函数
fn get_wmic_info(args: &[&str]) -> Option<String> {
    match Command::new("wmic").args(args).output() {
        Ok(output) => {
            let result = String::from_utf8_lossy(&output.stdout);
            let lines: Vec<&str> = result.lines().collect();
            if lines.len() > 1 {
                Some(lines[1].trim().to_string())
            } else {
                eprintln!("意外的输出格式: {:?}", result);
                None
            }
        }
        Err(e) => {
            eprintln!("执行WMIC命令失败: {}", e);
            None
        }
    }
}

// 获取 CPU 信息
fn get_cpu_info() -> Option<String> {
    get_wmic_info(&["cpu", "get", "ProcessorId"])
}

// 获取主板序列号
fn get_motherboard_serial() -> Option<String> {
    get_wmic_info(&["baseboard", "get", "SerialNumber"])
}

// 获取 BIOS 序列号
fn get_bios_serial() -> Option<String> {
    get_wmic_info(&["bios", "get", "SerialNumber"])
}

// 获取 BIOS 制造商
fn get_bios_manufacturer() -> Option<String> {
    get_wmic_info(&["bios", "get", "Manufacturer"])
}

// 获取 SMBIOS BIOS 版本
fn get_smbios_bios_version() -> Option<String> {
    get_wmic_info(&["bios", "get", "SMBIOSBIOSVersion"])
}

// AES加密
fn aes_encrypt(data: &str, key: &[u8], iv: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    // println!("Key length: {}, IV length: {}", key.len(), iv.len());
    if key.len() != 16 || iv.len() != 16 {
        return Err("密钥或IV长度无效".into());
    }

    let cipher: cbc::Encryptor<Aes128> = match Aes128CbcEnc::new_from_slices(key, iv) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("密码初始化失败: {:?}", e);
            return Err("AES参数无效".into());
        }
    };

    let mut buffer = data.as_bytes().to_vec();

    // println!("Buffer before padding   : {:?}", buffer);

    let block_size = 16;
    let padding_len = block_size - (buffer.len() % block_size);
    let padding_len = if padding_len == 0 {
        block_size
    } else {
        padding_len
    };

    // 填充数据
    buffer.extend(vec![padding_len as u8; padding_len]);

    // 调整缓冲区长度,确保有足够的空间
    let buffer_len = buffer.len();
    buffer.resize(buffer_len + block_size, 0); // 预留一个块的空间

    // println!("Buffer before encryption: {:?}", buffer);
    // println!("Buffer length: {}", buffer.len());

    assert_eq!(buffer_len % block_size, 0, "缓冲区长度必须是块大小的倍数");

    // 加密
    match cipher.encrypt_padded_mut::<Pkcs7>(&mut buffer, buffer_len) {
        Ok(ciphertext) => Ok(ciphertext.to_vec()), // 加密成功,返回加密后的缓冲区
        Err(e) => {
            // 加密失败,打印错误信息
            eprintln!("加密过程失败: {:?}", e);
            Err("加密失败".into())
        }
    }
}

// 生成机器码
pub fn generate_machine_code() -> String {
    let disk_serial = get_disk_serial().unwrap_or_else(|| "UnknownDisk".to_string());
    let cpu_info = get_cpu_info().unwrap_or_else(|| "UnknownCPU".to_string());
    let motherboard_serial =
        get_motherboard_serial().unwrap_or_else(|| "UnknownMotherboard".to_string());
    let bios_serial = get_bios_serial().unwrap_or_else(|| "UnknownBIOSSerial".to_string());
    let bios_manufacturer =
        get_bios_manufacturer().unwrap_or_else(|| "UnknownManufacturer".to_string());
    let smbios_bios_version =
        get_smbios_bios_version().unwrap_or_else(|| "UnknownSMBIOSVersion".to_string());

    // 打印各个部分的信息
    // println!("Disk Serial: {}", disk_serial);
    // println!("CPU Info: {}", cpu_info);
    // println!("Motherboard Serial: {}", motherboard_serial);
    // println!("BIOS Serial: {}", bios_serial);
    // println!("BIOS Manufacturer: {}", bios_manufacturer);
    // println!("SMBIOS BIOS Version: {}", smbios_bios_version);

    // 拼接机器码
    let machine_code = format!(
        "{}-{}-{}-{}-{}-{}",
        disk_serial,
        cpu_info,
        motherboard_serial,
        bios_serial,
        bios_manufacturer,
        smbios_bios_version
    );

    // AES加密密钥和IV(需固定长度:16字节)
    let key = b"jlZp6FubWOiNo7jG"; // 示例密钥
    let iv = b"mOUZhQhMc0MO6Vno"; // 示例IV

    // AES加密机器码
    let encrypted_code = match aes_encrypt(&machine_code, key, iv) {
        Ok(code) => code,
        Err(e) => {
            eprintln!("加密失败: {}", e);
            return "加密错误".to_string(); // 或者返回一个默认值
        }
    };
    // 生成哈希值
    let mut hasher = Sha256::new();
    hasher.update(&encrypted_code);
    let hash_result = hasher.finalize();

    // 返回加密后的哈希值
    format!("{:X}", hash_result)
}

/// 控制流程主函数
pub fn run() {
    // 打印本机机器码
    let machine_code = generate_machine_code();
    println!("机器码: {}", machine_code);
    // 更新公钥
    match update_public_key() {
        Ok(_) => println!("验证授权……"),
        Err(e) => {
            eprintln!("初始化授权失败: {}", e);
            std::process::exit(1);
        }
    }

    // 验证授权
    if let Err(e) = verify_license() {
        eprintln!("授权验证失败: {},程序退出", e);
        eprintln!("请访问: xxx.com 获取更多消息");
        std::process::exit(0);
    }

    println!("启动程序……");
}

//main.rs

mod register_app;
use register_app::run;

fn main() {
    run();
    println!("Hello, world!");
}
Note: AlpineLinux国内镜像源:/etc/apk/repositories

vi /etc/apk/repositories

#/media/cdrom/apks
https://mirrors.nju.edu.cn/alpine/latest-stable/main/
https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/main/
https://mirrors.nju.edu.cn/alpine/latest-stable/community/
https://mirrors.tuna.tsinghua.edu.cn/alpine/latest-stable/community/

https://mirrors.nju.edu.cn/alpine/v3.21/main/
https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/main/
https://mirrors.nju.edu.cn/alpine/v3.21/community/
https://mirrors.tuna.tsinghua.edu.cn/alpine/v3.21/community/

https://mirrors.nju.edu.cn/alpine/edge/main/
https://mirrors.tuna.tsinghua.edu.cn/alpine/edge/main/
https://mirrors.nju.edu.cn/alpine/edge/community/
https://mirrors.tuna.tsinghua.edu.cn/alpine/edge/community/

https://mirrors.nju.edu.cn/alpine/edge/testing/
https://mirrors.tuna.tsinghua.edu.cn/alpine/edge/testing/
prettytable-rs
豆包 MarsCode - 编程助手

基于豆包大模型,提供智能 AI IDE 和 AI 编程助手,带给你全新的编码体验。AI IDE 提供开箱即用的开发环境,AI 编程助手提供代码生成、代码解释、单测生成和问题修复等功能,支持上百种编程语言和主流开发环境。

空痕博客

一个个人开发者的学习记录

数字宝库

数字宝库致力于打造高质量资源百宝箱,为广大用户提供国内外优秀的软件、工具、插件、模板、素材,在这里您能找到涉及各行各业,种类繁多、流行前沿的数字宝藏

Note: 在Alpine Linux 中配置 IP 地址

配置 DHCP
编辑 /etc/network/interfaces 文件,确保以下内容存在:

auto eth0
iface eth0 inet dhcp

重新启动网络接口:

/etc/init.d/networking restart
或者
ifup eth0

设置固定 IP 地址

编辑 /etc/network/interfaces 文件,添加如下配置:

auto eth0
iface eth0 inet static
    address 192.168.1.100
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 8.8.8.8 1.1.1.1

重新启动网络接口:

/etc/init.d/networking restart
或者
ifup eth0
或者
rc-service networking restart

验证配置:

ip a
Note: LVM将多个物理磁盘合并为一个逻辑卷的常用方法

以下是如何在 Alpine Linux 上使用 LVM 将您的三个磁盘(sda、sdb、sdc)合并为一个大卷的详细步骤

安装 LVM 工具:

apk add lvm2

为每个磁盘创建一个物理卷(Physical Volume, PV):

清除磁盘上的现有分区

wipefs -a /dev/sda
wipefs -a /dev/sdb
wipefs -a /dev/sdc

创建物理卷:

pvcreate /dev/sda
pvcreate /dev/sdb
pvcreate /dev/sdc

验证物理卷创建:

pvs

创建卷组(Volume Group, VG):

vgcreate vg0 /dev/sda /dev/sdb /dev/sdc

验证卷组创建:

vgs

创建逻辑卷(Logical Volume, LV):

lvcreate -n lv0 -l 100%FREE vg0

验证逻辑卷创建:

lvs

格式化逻辑卷:

mkfs.ext4 /dev/vg0/lv0

挂载逻辑卷:

mkdir /mnt/bigdisk
mount /dev/vg0/lv0 /mnt/bigdisk

验证挂载:

df -h | grep /mnt/bigdisk

自动挂载(可选):
编辑 /etc/fstab 文件并添加以下行:

/dev/vg0/lv0  /mnt/bigdisk  ext4  defaults  0  2

可以使用以下命令测试 fstab 配置

mount -a

扩展逻辑卷:
添加新磁盘(假设为 /dev/sdd):

pvcreate /dev/sdd
vgextend vg0 /dev/sdd

扩展逻辑卷:

lvextend -l +100%FREE /dev/vg0/lv0

调整文件系统大小(对于 ext4):

resize2fs /dev/vg0/lv0

创建快照:

lvcreate -L 10G -s -n lv0_snapshot /dev/vg0/lv0

查看 LVM 状态:

vgdisplay
lvdisplay
pvdisplay
国内无法访问下载 Docker 镜像的多种解决方案 - 清~幽殇

简介2023 年 5 月, hub.docker.com  "不知" 何种原因国内均无法正常访问了。当时只是官网不能访问,但不影响 pull 镜像。2024年6月7日,GFW正式DNS污染+SN...

Note: 如何设计安全的软件注册与防破解机制

如何设计安全的软件注册与防破解机制

在软件开发中,设计一个安全的注册与验证机制是防止非法使用和破解的重要步骤。本教程详细介绍了从机器码生成到防破解设计的全流程。


1. 机器码的生成

机器码通常是根据用户设备的硬件信息生成的,用于唯一标识设备。例如:

  • CPU 序列号
  • 硬盘序列号
  • 网络 MAC 地址

生成的机器码可以采用哈希算法将这些硬件信息加密,确保其唯一性。


2. 机器码和授权码绑定

授权码是对机器码的加密签名,用于验证合法性。生成授权码的方法通常包括:

  • HMAC(对称加密)
    • 使用私钥和机器码生成签名。
  • RSA(非对称加密)
    • 用私钥对机器码进行签名,软件通过公钥验证。

3. 软件注册逻辑

软件注册的核心逻辑分为两部分:

  1. 注册机生成授权文件。

    • 注册机使用私钥对机器码签名。
    • 授权文件存储机器码和签名。
  2. 软件验证授权文件。

    • 软件读取授权文件,提取机器码和签名。
    • 使用公钥验证签名是否匹配机器码。

4. 公钥和私钥机制

为了提高安全性,建议使用非对称加密机制:

  • 私钥:仅存储在注册机中,用于生成签名。
  • 公钥:嵌入软件中,用于验证签名。

这样,即使破解者获得了公钥,也无法伪造合法的授权文件。


5. 防破解设计

为了防止破解者绕过注册逻辑,可以采取以下措施:

5.1 深度绑定功能与授权验证

将验证逻辑深度嵌入软件的核心功能,分散验证点。

5.2 代码混淆和加壳保护

  • 使用代码混淆工具(如 ProGuard)。
  • 对可执行文件进行加壳(如 Themida)。

5.3 校验自我完整性

验证软件自身文件的哈希值,防止被篡改。

5.4 动态授权验证

动态生成校验数据,与授权文件结合验证。

5.5 硬件绑定

将授权文件绑定到设备硬件信息。

5.6 环境检测

检测是否运行在调试器或虚拟机中,发现异常环境时终止运行。


6. 组合方案实例

一个完整的设计可以包括:

  1. 注册机使用非对称加密生成授权文件。
  2. 授权文件绑定机器码和硬件信息。
  3. 软件通过公钥验证签名,同时检测文件完整性。
  4. 代码加壳保护,并动态校验授权。

通过多种技术组合,可以显著增加破解难度。


总结

本教程详细讲解了如何设计一个安全的软件注册系统,从机器码生成到防破解设计。虽然无法完全杜绝破解,但可以通过技术手段让破解代价高昂、收益低廉,从而有效保护软件的合法使用。


Rust 每次 cargo build 生成的可执行文件的哈希值通常会改变,即使源代码没有发生变化。这是因为以下原因:


1. 时间戳

Rust 编译器默认会在编译的可执行文件中嵌入时间戳(例如构建时间),这是动态生成的,每次构建都不同。


2. 调试信息

在开发模式下(cargo build 默认构建 debug 版本),可执行文件包含调试信息。这些信息中可能包含文件路径、编译器元数据等内容,这些数据可能在每次构建时有所变化。


3. 文件顺序或链接器行为

Rust 的链接器可能会以略有不同的顺序排列代码或数据段,导致最终生成的二进制文件略有不同。


如何验证是否一致?

如果你想验证是否是代码本身的变化导致了文件差异,你可以尝试以下方法:

1. 使用 --release 模式

cargo build --release 会启用优化并剥离大部分调试信息。这可能使生成的文件更稳定。

2. 禁用时间戳和非确定性信息

你可以通过在 Cargo.toml 中配置来尝试剥离时间戳等信息。例如:

[profile.release]
debug = false

或者使用自定义的 strip 工具移除不必要的信息:

strip <binary>

3. 对比代码逻辑部分

可以通过 objdump 或类似工具,分析生成文件的主要逻辑部分,而不是直接对比哈希值。例如:

objdump -d target/debug/<binary>

总结

Rust 默认生成的可执行文件哈希值每次都会变,因为它嵌入了动态信息(如时间戳、调试数据等)。如果你希望生成的文件是确定性的(即每次构建哈希值相同),可以尝试使用 --release 模式,并配置编译器剥离这些动态信息。

在防破解设计中,软件自我完整性校验是一个常用的技术,用于防止二进制文件被篡改。主要原理是:运行时计算软件自身文件的哈希值或签名,然后与预期的值进行对比,以检测是否被篡改。下面是完整的实现思路和常见方法:


1. 核心步骤

(1) 计算自身文件的哈希值

软件在运行时可以读取自身文件的内容,然后计算哈希值(如 SHA256)。

(2) 比较哈希值

将计算得到的哈希值与编译时预先生成的、嵌入到程序中的哈希值进行比较。如果不匹配,说明文件被篡改。

(3) 处理检测结果

如果检测到篡改,可以直接终止程序运行、报告错误、或者触发某种反制措施(如混淆、假输出等)。


2. Rust 中的实现思路

Rust 提供了强大的工具链和库,可以用来实现完整性校验。例如:

(1) 计算文件的哈希值

使用 std::fs 读取当前执行文件的内容,然后通过 sha2 等 crate 计算哈希值。

代码示例:

use std::fs::File;
use std::io::{Read, Result};
use sha2::{Sha256, Digest}; // 引入 sha2 crate

fn calculate_hash() -> Result<String> {
    // 获取当前程序路径
    let current_exe = std::env::current_exe()?;
    let mut file = File::open(current_exe)?;
    let mut hasher = Sha256::new();
    let mut buffer = Vec::new();

    // 读取文件内容并计算哈希值
    file.read_to_end(&mut buffer)?;
    hasher.update(&buffer);
    let hash = hasher.finalize();

    // 转换为十六进制字符串返回
    Ok(format!("{:x}", hash))
}

(2) 嵌入预期哈希值

预期的哈希值可以在编译时生成,并硬编码到程序中。例如:

  • 在编译完成后,用脚本计算二进制文件的哈希值。
  • 将该哈希值嵌入到程序代码或某个资源文件中。

例如:

const EXPECTED_HASH: &str = "abcdef1234567890..."; // 编译时生成的哈希值

(3) 比较哈希值

在程序启动时,运行自检逻辑:

fn main() {
    match calculate_hash() {
        Ok(hash) => {
            if hash == EXPECTED_HASH {
                println!("文件完整性验证通过");
            } else {
                eprintln!("文件已被篡改!");
                std::process::exit(1);
            }
        }
        Err(e) => {
            eprintln!("无法校验完整性: {}", e);
            std::process::exit(1);
        }
    }
}

3. 防破解常见问题与对策

(1) 防止哈希值被简单篡改

  • 签名验证: 使用公私钥签名替代简单的哈希值比较。例如,编译时用私钥签名二进制文件哈希值,程序运行时用公钥验证签名是否合法。
  • 示例:

    use rsa::{RsaPublicKey, PaddingScheme};
    use sha2::{Sha256, Digest};
    
    fn verify_signature(public_key: &RsaPublicKey, hash: &[u8], signature: &[u8]) -> bool {
        public_key.verify(
            PaddingScheme::PKCS1v15Sign { hash: None },
            hash,
            signature,
        ).is_ok()
    }

(2) 防止被反编译或动态修改

  • 混淆哈希验证代码: 通过代码混淆增加破解难度,使攻击者难以找到校验逻辑。
  • 分段校验: 将哈希校验分散到程序多个部分,避免单点破解。
  • 动态自我修改: 让程序在运行时修改自身代码,从而难以静态分析。

(3) 防止加载时内存修改

  • 内存保护: 在运行时计算内存段的哈希值,防止内存中的代码段被篡改。

    • *

4. 进一步增强措施

(1) 结合完整性校验和防调试

在自检过程中检测调试器的存在,例如通过以下方式:

  • 检测断点指令或调试寄存器。
  • 检测调试工具常见的环境变量或系统 API。

(2) 结合网络验证

通过远程服务器验证程序的完整性(如定期上报校验结果)。


5. 总结

Rust 中实现自我完整性校验的关键在于:

  1. 读取自身二进制文件内容,计算哈希值。
  2. 将运行时的哈希值与编译时的预期值比较。
  3. 结合签名、分段校验和防调试措施,提高安全性。

通过 Rust 强类型和安全特性,可以可靠实现防篡改机制,但需要注意完全防止破解是不可能的,只能尽量提高破解的门槛和成本。