Windows X86 远线程注入问题解惑

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

问题描述

  Windows X86 远线程注入问题解惑
¥15
c++
windows
在之前提问的知道下 成功部署了 汇编器 ; 一会即将采纳。
新问题:(请通读全文 重点在于解惑)
下面这个是 X32dbg中 看到的一个功能call 作用是选中一个物体。

0057ADEB | 8B0D 90FB3801          | mov     ecx, dword ptr [138FB90]            |
0057ADF1 | 56                     | push    esi                                 |
0057ADF2 | E8 B9C14A00            | call    A26FB0                              |
0057ADF7 | 8BC8                   | mov     ecx, eax                            |
0057ADF9 | E8 D223FCFF            | call    53D1D0                              | 选中

这个是 我用来 翻译汇编为机器码的相关代码:
该汇编器 默认 地址为绝对地址(直接翻译)。

int QQSG::AsmToHex(ks_arch arch, int mode, const char* assembly, int syntax, QString& outStr, size_t* OutSize) {int status = 0;ks_engine* ks;ks_err err;size_t count;unsigned char* encode = nullptr;size_t size = 0;// Initialize Keystone engineerr = ks_open(arch, mode, &ks);if (err != KS_ERR_OK) {printf("Failed to initialize Keystone engine: %d\n", ks_errno(ks));return -1;}// Set syntax optionif (syntax)ks_option(ks, KS_OPT_SYNTAX, syntax);// Assemble instructionif (ks_asm(ks, assembly, 0, &encode, &size, &count)) {printf("ERROR: failed on ks_asm() with count = %zu, error code = %d\n", count, ks_errno(ks));ks_free(encode); // Free encoded dataks_close(ks);    // Close Keystone enginereturn 2;}// Check if the encoding was successfulif (size > 0) {// Convert machine code to hexadecimal stringfor (size_t i = 0; i < size; i++) {char tmp[5];sprintf(tmp, "%02X", encode[i]);outStr += tmp;}outStr += "\r\n";// Print the machine code for debuggingprintf("Generated machine code: ");for (size_t i = 0; i < size; i++) {printf("%02X ", encode[i]);}printf("\n");// 调整 asmhex 的大小asmhex.resize(asmhex.size() + size);  // 调整 asmhex 的大小memcpy_s(asmhex.data() + asmhex.size() - size, size, encode, size);  // 复制数据到 asmhex*OutSize = size;}else {printf("No machine code generated for '%s'\n", assembly);}// Clean up resourcesks_free(encode);ks_close(ks);return status;
}QByteArray QQSG::assembleCode(const QByteArray& asmcode) {int all_size = 0;bool hasErrors = false;QString text = QString::fromUtf8(asmcode);printf("Original assembly code: %s\n", text.toLocal8Bit().constData());QStringList lines = text.split('\n');for (QString& asmstr : lines) {asmstr = asmstr.trimmed();printf("Processing assembly code: %s\n", asmstr.toLocal8Bit().constData());if (asmstr.isEmpty())continue;size_t hex_size = 0;// 调用 AsmToHex 并获取结果int status = this->AsmToHex(KS_ARCH_X86, KS_MODE_32, asmstr.toLocal8Bit().data(), 0, m_hexstr, &hex_size);printf("Assembly conversion result: %s\n", asmstr.toLocal8Bit().constData());if (status != 0) {printf("Assembly conversion failed: %s\n", asmstr.toLocal8Bit().constData());m_hexstr.append(" <= instruction format error!");hasErrors = true;}else {all_size += hex_size;}}// If errors occurred, return an empty QByteArrayif (hasErrors) {printf("Errors occurred, returning an empty QByteArray\n");return QByteArray();}// Add a ret instruction at the end/*   asmhex.push_back(0xC3);*/all_size++;// 转换为 QByteArray 并返回QByteArray machineCode = QByteArray::fromRawData(reinterpret_cast<const char*>(asmhex.data()), asmhex.size());// 打印最终的机器码用于调试printf("Final machine code: %s\n", machineCode.toHex().constData());asmhex.clear();return machineCode;
}
void QQSG::on_pushButtonCodeInject_clicked()
{if (ui.lineEditCodeHwnd->text().isEmpty()) {QMessageBox::warning(this, tr("Warning"), tr("请输入目标窗口句柄!"));return;}// 尝试将文本转换为DWORD类型QString asmcode = ui.textEditShellCode->toPlainText();if (asmcode.isEmpty()) {QMessageBox::warning(this, tr("Warning"), tr("请输入汇编代码!"));return;}QString CodeHwndStr = "CodeHwnd:" + ui.lineEditCodeHwnd->text();for (int i = 0; i < m_clinetList.size(); ++i){QTcpSocket* socket = m_clinetList.at(i);if (socket->state() == QTcpSocket::ConnectedState) {socket->write(CodeHwndStr.toUtf8());}}
}
void QQSG::recevieReadyRead()
{for (int i = 0; i < m_clinetList.size(); i++){QTcpSocket* socket = m_clinetList.at(i);QByteArray data = socket->readAll()if (data.contains("StartShellcodeRemoteThread")){QString asmcode = ui.textEditShellCode->toPlainText();QByteArray asmcodeBytes = asmcode.toUtf8();QByteArray machineCode = QQSG::assembleCode(asmcodeBytes);DWORD processId = 0;QString text = ui.tableWidget->item(0, UITYPE::WINWND)->text();bool ok;int m_hWnd = text.toInt(&ok);GetWindowThreadProcessId((HWND)m_hWnd, &processId);HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);if (hProcess == NULL) {qDebug() << "Failed to open process:" << GetLastError();return;}PVOID pLibRemote = VirtualAllocEx(hProcess, NULL, machineCode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);if (pLibRemote == NULL) {qDebug() << "Failed to allocate memory in remote process:" << GetLastError();CloseHandle(hProcess);return;}SIZE_T bytesWritten;BOOL result = WriteProcessMemory(hProcess, pLibRemote, machineCode.data(), machineCode.size(), &bytesWritten);if (!result || bytesWritten != machineCode.size()) {qDebug() << "Failed to write memory to remote process:" << GetLastError();VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);CloseHandle(hProcess);return;}HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLibRemote, NULL, 0, NULL);if (hThread == NULL) {qDebug() << "Failed to create remote thread:" << GetLastError();VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);CloseHandle(hProcess);return;}QMessageBox::warning(this, tr("Warning"), tr("请输入汇编代码!"));DWORD result = WaitForSingleObject(hThread, INFINITE);if (result != WAIT_OBJECT_0) {qDebug() << "Failed to wait for remote thread:" << GetLastError();VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);CloseHandle(hThread);CloseHandle(hProcess);return;}VirtualFreeEx(hProcess, pLibRemote, 0, MEM_RELEASE);CloseHandle(hThread);CloseHandle(hProcess);return; }   }
}

问题来了 在学习过程中,假如我想调用这个call 实现远线程,我写了 汇编语言:

//esi的值 是选中对象的相关参数 我们假设他是 0x12345678,

mov esi,0x12345678
mov ecx,0x138FB90
mov     ecx,[ecx]
push    esi
call   0xA26FB0
mov     ecx, eax
call    0x53D1D0

注入之后的结果如下:

Original assembly code: mov esi,0x12345678
mov ecx,0x138FB90
mov     ecx,[ecx]
push    esi
call   0xA26FB0
mov     ecx, eax
call    0x53D1D0
Processing assembly code: mov ecx,0x138FB90
Generated machine code: B9 90 FB 38 01
Assembly conversion result: mov ecx,0x138FB90
Processing assembly code: mov ecx,[ecx]
Generated machine code: 8B 09
Assembly conversion result: mov ecx,[ecx]
Processing assembly code: push esi
Generated machine code: 56
Assembly conversion result: push esi
Processing assembly code: call 0xA26FB0
Generated machine code: E8 AB 6F A2 00
Assembly conversion result: call 0xA26FB0
Processing assembly code: mov ecx,eax
Generated machine code: 89 C1
Assembly conversion result: mov ecx,eax
Processing assembly code: call 0x53D1D0
Generated machine code: E8 CB D1 53 00
Assembly conversion result: call 0x53D1D0
Processing assembly code: ret
Generated machine code: C3
Assembly conversion result: ret
Processing assembly code:
Final machine code: b990fb38018b0956e8ab6fa20089c1e8cbd15300c3
Received machineCode code: B9 90 FB 38 01 8B 09 56 E8 AB 6F A2 00 89 C1 E8 CB D1 53 00 
Received machineCode: B9 90 FB 38 01 8B 09 56 E8 AB 6F A2 00 89 C1 E8 CB D1 53 00 // 原代码中的机器码(由于语言规范及传入数据,开头的机器码不同我可以理解,call后为什么不相同呢)
8B 0D 90 FB 38 01 56 E8 B9 C1 4A 00 8B C8 E8 D2 23 FC FF
此时:线程注入没有反应;
问题:
1.为什么 call 0xA26FB0 与 call 0x53D1D0翻译后的机器码 与 源代码中(文章开头)中的机器码不同呢
2.解释这种现象的原因,是因为call 后的 是偏移量?
3.为什么 注入后没有反应;
4. 综合上述问题 ,我需要如何修改 才能实现远线程注入
5.为什么我学习别人的文章,代码里也没有相关的处理 却可以调用成功;
6.为什么内联汇编却可以呢 ,比如下面:当然 内联汇编 需要修改一下,不能直接传值DWORD 功能相关地址 =0x138FB90DWORD 选中目标 = 0x12345678;
DWORD call_1 =0xA26FB0;
DWORD call_2 =0xA26FB0;
_asm
{
mov esi,选中目标
mov ecx,功能相关地址
mov     ecx,[ecx]
push    esi
call   call_1 
mov     ecx, eax
call    call_2
}

解决方案

  如下是上述问题的解决方案,仅供参考:

  在你的问题中,你涉及到了远程线程注入和汇编代码的转换,特别是关于如何将汇编语言代码注入到远程进程中并使其执行。你遇到的主要问题是关于机器码转换的准确性以及在远程线程注入后代码没有执行的原因。让我们逐一解答这些问题。

问题分析与解答

  1. 为什么 call 0xA26FB0call 0x53D1D0 翻译后的机器码与源代码中的机器码不同?

    汇编中的 call 指令需要使用相对于当前指令的偏移量,而不是绝对地址。因此,翻译成机器码时,call 指令后的地址是一个相对地址。

    • 在你的代码中,call 0xA26FB0call 0x53D1D0 在实际生成机器码时,需要计算这些地址相对于 call 指令本身的偏移量。
    • 原始机器码中的 call 指令实际上包含的是偏移量,不是绝对地址。比如,E8 AB 6F A2 00 表示 call 指令的偏移量是 0x00A26FAB(相对于 call 指令的地址)。

    在你的机器码生成中,call 后的地址可能是基于某个假设的位置计算的偏移量,而不是实际的绝对地址。确保你的汇编器或代码生成工具计算了正确的相对地址偏移量。

  2. 解释这种现象的原因,是因为 call 后的地址是偏移量?

    是的,call 指令使用的是相对偏移量,而不是绝对地址。call 指令后面跟的是相对于下一条指令的偏移量(即 call 指令的地址加上偏移量等于目标地址)。

    如果你将绝对地址直接放到 call 指令中,生成的机器码可能不会正确地跳转到目标地址,因为它们需要使用相对地址计算。

  3. 为什么注入后没有反应?

    可能有以下原因:

    • 偏移量计算错误:如前所述,call 指令需要相对地址。请确保你计算了正确的偏移量,并将其应用于机器码生成中。
    • 权限问题:检查注入的代码是否具有足够的权限执行,确保目标进程允许代码注入和执行。
    • 内存保护:确保分配的内存具有正确的保护属性(如 PAGE_EXECUTE_READWRITE)。
  4. 如何修改才能实现远程线程注入?

    你需要确保以下几点:

    • 计算相对地址:在生成机器码时,计算 call 指令的相对偏移量。你可以在汇编阶段计算 call 指令的偏移量,然后再将其转化为机器码。
    • 确保内存权限:分配的内存区域需要有正确的权限设置,确保可以执行写入的机器码。
    • 验证注入:确保远程线程的创建和代码注入过程无误。可以通过调试工具检查注入的机器码是否正确执行。
  5. 为什么我学习别人的文章,代码里也没有相关的处理却可以调用成功?

    可能是因为这些文章使用了不同的技术栈或工具,这些工具自动处理了相对地址的计算。例如,有些框架或库可能在注入代码前已经计算并调整了相对地址。你可以参考这些工具或库的文档,了解它们如何处理地址计算。

  6. 为什么内联汇编却可以?

    内联汇编通常会由编译器处理,并且编译器会自动管理相对地址和跳转目标的计算。编译器在生成目标代码时会将 call 指令的相对地址计算得很准确,因此你不需要手动处理这些偏移量。

总结

要解决你面临的问题,你需要:

  • 确保在汇编到机器码的过程中计算正确的相对地址。
  • 检查内存分配和权限设置是否正确。
  • 使用调试工具验证注入后的机器码是否按预期执行。

在生成机器码时,特别是涉及 call 指令时,确保理解相对地址和绝对地址的区别,以及如何正确计算和插入相对偏移量。

  希望如上措施及解决方案能够帮到有需要的你。

  PS:如若遇到采纳如下方案还是未解决的同学,希望不要抱怨&&急躁,毕竟影响因素众多,我写出来也是希望能够尽最大努力帮助到同类似问题的小伙伴,即把你未解决或者产生新Bug黏贴在评论区,我们大家一起来努力,一起帮你看看,可以不咯。

  若有对当前Bug有与如下提供的方法不一致,有个不情之请,希望你能把你的新思路或新方法分享到评论区,一起学习,目的就是帮助更多所需要的同学,正所谓「赠人玫瑰,手留余香」。

☀️写在最后

  如上问题有的来自我自身项目开发,有的收集网站,有的来自读者…如有侵权,立马删除。再者,针对此专栏中部分问题及其问题的解答思路或步骤等,存在少部分搜集于全网社区及人工智能问答等渠道,若最后实在是没能帮助到你,还望见谅!并非所有的解答都能解决每个人的问题,在此希望屏幕前的你能够给予宝贵的理解,而不是立刻指责或者抱怨!如果你有更优解,那建议你出教程写方案,一同学习!共同进步。

  ok,以上就是我这期的Bug修复内容啦,如果还想查找更多解决方案,你可以看看我专门收集Bug及提供解决方案的专栏《CSDN问答解惑-专业版》,都是实战中碰到的Bug,希望对你有所帮助。到此,咱们下期拜拜。

码字不易,如果这篇文章对你有所帮助,帮忙给 bug菌 来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。

同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!

📣关于我

我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。


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

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

相关文章

一.python入门

gyp的读研日记&#xff0c;哈哈哈哈&#xff0c;&#x1f642;&#xff0c;从复习python开始&#xff0c; 目录 1.python入门 1.1 Python说明书 1.2 Python具备的功能 1.3 学习前提 1.4 何为Python 1.5 编程语言 2.Python环境搭建 2.1 开发环境概述 2.2 Python的安装与…

STM32F407ZGT6-HAL-FreeRTOS-信号量示例

本程序创建了一个二进制信号量&#xff0c;然后创建2个任务&#xff1a;一个用于释放信号量&#xff0c;另一个用于获取信号 量&#xff1b; 发送任务、接收任务的代码和执行流程如下&#xff1a; A&#xff1a;发送任务优先级高&#xff0c;先执行。连续3次释放二进制信号量&…

一文搞懂offset、client、scroll系列及案例

目录 一、offset 1-1、offset系列属性 1-2、offset与style区别 1-3、案例 1-3-1、计算鼠标在盒子内的坐标 1-3-2、拖动模态框 二、client 2-1、client系列属性 三、scroll 3-1、scroll系列属性 3-2、案例 3-2-1、滚动页面一定距离后固定侧边栏 一、offset offset是…

【WRF工具】服务器上使用conda安装NCL

【WRF工具】服务器上使用conda安装NCL NCL概述使用conda下载NCL安装conda安装NCL另&#xff1a;当使用NCL时&#xff0c;则需要激活ncl_stable环境 参考 NCL概述 NCAR Command Language&#xff08;NCL&#xff09; 是由美国大气研究中心&#xff08;NCAR&#xff09;推出的一…

Spring Boot 中实现任务后台处理的几种常见方式

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 在现代应用程序中&#xff0c;后台处理对于处理发送电子邮件、处理文件、生成报告等任务至关重要。 Spring Boot 提供了多种机制来高效地实现后台任务。本文探讨了在 Spring Boot 中处理后台处理的各…

大数据新视界 --大数据大厂之 Vue.js 与大数据可视化:打造惊艳的数据界面

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

java节假日工具类,判断一个日期是否是法定节假日

java节假日工具类&#xff0c;判断一个日期是否是法定节假日 1.HolidayUtil工具类2.工具类生成的日期json文件3.结果展示 无需链接数据库&#xff0c;无需手写节假日集合列表 1.HolidayUtil工具类 import com.alibaba.fastjson.JSONObject; import com.fasterxml.jackson.data…

0-10V 电压转光纤

型号&#xff1a;MS-F155-VM(CE /ISO9001&#xff09; 功能概述 MS-F155-VM是将0-10V电压转为光纤信号的模块&#xff0c;分发送和接收两个设备发送模块将电流或者电压信号转变为光信号&#xff0c;通过光纤传输&#xff0c;接收端将光信号还原为电流或者电压信号。可以延长通信…

2024源代码加密软件分享TOP10丨保护源代码安全很重要!

在如今的数字时代&#xff0c;源代码是企业的核心资产之一。无论是开发软件、应用程序&#xff0c;还是自动化系统&#xff0c;源代码都是技术的根基&#xff0c;决定了公司的核心竞争力。然而&#xff0c;源代码泄露或被盗可能会给企业带来巨大的安全风险和经济损失。因此&…

03-Docker下载加速

03-Docker下载加速 docker下载加速 方式1&#xff1a;使用 网易数帆、阿里云等容器镜像仓库进行下载。 网易数帆官网&#xff1a;https://sf.163.com/ 例如&#xff0c;下载网易数帆镜像中的mysql。&#xff08;网易数帆的地址为 hub.c.163.com&#xff0c;网易数帆对dockerh…

光控资本:沪指涨0.72%,煤炭、银行板块拉升,车路云概念活跃

23日早盘&#xff0c;沪指盘中强势上扬&#xff0c;深证成指亦走高&#xff0c;场内超3100股飘红。 到午间收盘&#xff0c;沪指涨0.72%报2756.39点&#xff0c;深证成指涨0.58%&#xff0c;创业板指微涨0.09%&#xff0c;上证50指数涨0.73%&#xff1b;两市估计成交3657亿元。…

TLV解码 - 华为OD统一考试(E卷)

2024华为OD机试&#xff08;E卷D卷C卷&#xff09;最新题库【超值优惠】Java/Python/C合集 题目描述 TLV编码是按 [Tag Length Value] 格式进行编码的&#xff0c;一段码流中的信元用Tag标识&#xff0c;Tag在码流中唯一不重复&#xff0c;Length表示信元Value的长度&#xff…

不敲一行代码!助你快速搭建属于自己的官网博客!-VitePress保姆级教程

文章目录 前言项目搭建首页修改项目配置 前言 我们在阅读官方文档时&#xff0c;经常看到一些项目的文档非常简约精美&#xff0c;并且布局高度相似&#xff0c;其实这些官网是基于vitepress搭建&#xff0c;例如&#xff1a; Vite官方文档 Vue-Use SnowAdmin 这些官网…

从零到一:如何用Ollama和OpenUI构建强大的AI模型库

搭建开源大模型平台的步骤与模型介绍 在这篇文章中&#xff0c;我将分享如何在Windows上使用Ollama和OpenUI搭建开源大模型平台的步骤&#xff0c;并介绍我所部署的几个模型及其擅长的领域。 目录 搭建开源大模型平台的步骤与模型介绍一、搭建平台步骤1. 安装Ollama2. 安装Ope…

VScode配置连接远程服务器configure ssh Hosts

VScode配置连接远程服务器&#xff0c;具体步骤 一、点击VScode左下脚这两个∟的按钮 二、点击完上面的按钮后&#xff0c;出现如下的下拉选项&#xff0c;选择“Connect to Host” 三、选择“Connect to Host”后&#xff0c;下拉选项会更新&#xff0c;选择“Configure SSH …

影刀RPA实战:java结合影刀同步采购订单数据

1.实战目标 本次实战我们用java语言结合影刀&#xff0c;实现从自用ERP系统同步订单到旺店通中&#xff0c;在工作中&#xff0c;有时候我们的运营数据不是直接在旺店通ERP中操作&#xff0c;比如我们有自己的ERP&#xff0c;完成一些特定的内部工作后&#xff0c;再把数据同步…

18937 阿克曼(Ackmann)函数

### 思路 1. **递归定义**&#xff1a;根据阿克曼函数的定义&#xff0c;使用递归来计算函数值。 2. **递归终止条件**&#xff1a; - 当 m 0 时&#xff0c;返回 n 1&#xfffd;&#xfffd; - 当 m > 0 且 n 0 时&#xff0c;返回 ackermann(m - 1, 1)。 - 当…

QT窗口无法激活弹出问题排查记录

问题背景 问题环境 操作系统: 银河麒麟V10SP1qt版本 : 5.12.12 碰见了一个问题应用最小化,然后激活程序窗口无法弹出 这里描述一下代码的逻辑,使用QLocalServer实现一个单例进程,具体的功能就是在已存在一个程序A进程时,再启动这个程序A,新的程序A进程会被杀死,然后激活已存…

Python 从入门到实战25(模块)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们讨论了类继承的相关知识。今天我们将学习一下模块的…

照片写真记录摄影作品记录网站源码

完美适应iPad&#xff0c;平板&#xff0c;手机竖屏不支持lazy&#xff0c;横屏可以&#xff0c;但建议使用平板查看效果&#xff0c; 有服务器直接上传解压使用&#xff0c;环境nginxphp&#xff0c; 没有服务器也没关系&#xff0c;可以直接使用html