每月 Shaarli
网站备案专用
lpv4.cn,qqdns.top,ukweb.qqdns.top
本文将指导如何在 Debian 12 和 Ubuntu 24.04 下安装 Docker 以及 Docker Compose。
注册机
//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!");
}
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/
基于豆包大模型,提供智能 AI IDE 和 AI 编程助手,带给你全新的编码体验。AI IDE 提供开箱即用的开发环境,AI 编程助手提供代码生成、代码解释、单测生成和问题修复等功能,支持上百种编程语言和主流开发环境。
一个个人开发者的学习记录
数字宝库致力于打造高质量资源百宝箱,为广大用户提供国内外优秀的软件、工具、插件、模板、素材,在这里您能找到涉及各行各业,种类繁多、流行前沿的数字宝藏
配置 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
以下是如何在 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
简介2023 年 5 月, hub.docker.com "不知" 何种原因国内均无法正常访问了。当时只是官网不能访问,但不影响 pull 镜像。2024年6月7日,GFW正式DNS污染+SN...
如何设计安全的软件注册与防破解机制
在软件开发中,设计一个安全的注册与验证机制是防止非法使用和破解的重要步骤。本教程详细介绍了从机器码生成到防破解设计的全流程。
1. 机器码的生成
机器码通常是根据用户设备的硬件信息生成的,用于唯一标识设备。例如:
- CPU 序列号
- 硬盘序列号
- 网络 MAC 地址
生成的机器码可以采用哈希算法将这些硬件信息加密,确保其唯一性。
2. 机器码和授权码绑定
授权码是对机器码的加密签名,用于验证合法性。生成授权码的方法通常包括:
- HMAC(对称加密)
- 使用私钥和机器码生成签名。
- RSA(非对称加密)
- 用私钥对机器码进行签名,软件通过公钥验证。
3. 软件注册逻辑
软件注册的核心逻辑分为两部分:
-
注册机生成授权文件。
- 注册机使用私钥对机器码签名。
- 授权文件存储机器码和签名。
-
软件验证授权文件。
- 软件读取授权文件,提取机器码和签名。
- 使用公钥验证签名是否匹配机器码。
4. 公钥和私钥机制
为了提高安全性,建议使用非对称加密机制:
- 私钥:仅存储在注册机中,用于生成签名。
- 公钥:嵌入软件中,用于验证签名。
这样,即使破解者获得了公钥,也无法伪造合法的授权文件。
5. 防破解设计
为了防止破解者绕过注册逻辑,可以采取以下措施:
5.1 深度绑定功能与授权验证
将验证逻辑深度嵌入软件的核心功能,分散验证点。
5.2 代码混淆和加壳保护
- 使用代码混淆工具(如 ProGuard)。
- 对可执行文件进行加壳(如 Themida)。
5.3 校验自我完整性
验证软件自身文件的哈希值,防止被篡改。
5.4 动态授权验证
动态生成校验数据,与授权文件结合验证。
5.5 硬件绑定
将授权文件绑定到设备硬件信息。
5.6 环境检测
检测是否运行在调试器或虚拟机中,发现异常环境时终止运行。
6. 组合方案实例
一个完整的设计可以包括:
- 注册机使用非对称加密生成授权文件。
- 授权文件绑定机器码和硬件信息。
- 软件通过公钥验证签名,同时检测文件完整性。
- 代码加壳保护,并动态校验授权。
通过多种技术组合,可以显著增加破解难度。
总结
本教程详细讲解了如何设计一个安全的软件注册系统,从机器码生成到防破解设计。虽然无法完全杜绝破解,但可以通过技术手段让破解代价高昂、收益低廉,从而有效保护软件的合法使用。
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 中实现自我完整性校验的关键在于:
- 读取自身二进制文件内容,计算哈希值。
- 将运行时的哈希值与编译时的预期值比较。
- 结合签名、分段校验和防调试措施,提高安全性。
通过 Rust 强类型和安全特性,可以可靠实现防篡改机制,但需要注意完全防止破解是不可能的,只能尽量提高破解的门槛和成本。