为 SQLAlchemy Model 添加 type hint 和 type check

TL;DR Use from typing import dataclass_transform Motivation 其实动机很简单,众所周知对于 SQLAlchemy 1.4 想要 typing 可以安装 sqlalchemy2-stub,对于 < 1.4 也可以有 sqlalchemy-stub。然而对于最新的 SQLAlchemy >= 2.0,因为它自己有类型注释,但是又很少,所以还没有一个很好的解决方案。本文就是为了介绍一种我摸索出的解决方案,基本上可以完美解决 SQLAlchemy Model / ORM 的 typing 问题。 当然,如果你在手写 raw sql,那肯定是没办法自动弄好类型的,不要做梦了🚫。本文只针对使用了 SQLAlchemy ORM Model 的用户。 Implementation 基本思路是,Python 在 PEP 681 (>= Python 3.11) 当中为 typing 模块提供了一个 dataclass_transform decorator,可以将第三方的 class 标注为和原生的 dataclass 提供类似的功能: Most type checkers, linters and language servers have full support for dataclasses. This proposal aims to generalize this functionality and provide a way for third-party libraries to indicate that certain decorator functions, classes, and metaclasses provide behaviors similar to dataclasses. These behaviors include: Synthesizing an __init__ method based on declared data fields. <-- good for us Optionally synthesizing __eq__, __ne__, __lt__, __le__, __gt__ and __ge__ methods. Supporting “frozen” classes, a way to enforce immutability during static type checking. Supporting “field specifiers”, which describe attributes of individual fields that a static type checker must be aware of, such as whether a default value is provided for the field. <-- mostly good for us 总体来讲这个 decorator 提供了所有我们想要的功能,唯一的问题是 field: type = mapped_column(...) 会导致 type checker 认为 field 是 Optional 的。但总归基本的类型检查是能用的,甚至能兼容外键和 relationship,只是在初始化类的时候会有提供参数没写全的风险。如果感觉这样不够好,可以通过给每个 Model 编写一个 dummy __init__ 的方式来把 optional 干掉: 1 2 3 4 5 class A(Base): id: MappedColumn[str] = mapped_column(String, nullable=False, ...) def __init__(/, id: str): ... 实践上,我们为了方便,可以直接在 SQLAlchemy 的 Base class 上面使用这个 decorator,这样就不用每次定义一个 Model class 都要写一遍了。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from sqlalchemy import Integer, String, Text, text, ForeignKey from sqlalchemy.orm import DeclarativeBase, Mapped, MappedColumn, mapped_column, relationship from typing import TYPE_CHECKING, dataclass_transform if TYPE_CHECKING: # this gives any class that derives from Base some simple type hints @dataclass_transform() class Base(DeclarativeBase): pass else: # dataclass_transform should never mess with runtime codes to avoid behavior changes class Base(DeclarativeBase): pass class User(Base): id: MappedColumn[str] = mapped_column(String, nullable=False, primary_key=True) ... class Example(Base): __tablename__ = "example" id: MappedColumn[int] = mapped_column(Integer, autoincrement=True, primary_key=True) user_id: MappedColumn[str] = mapped_column(String, ForeignKey("user.id"), nullable=False) user: Mapped[User] = relationship(User, backref="example") # should not use Relationship[] here example = Example(...) # has autocompletion & basic type check example.user.id # has autocompletion & basic type check 这里 class Base(DeclarativeBase): 写法得到的 Base class 和 Base = declarative_base() 得到的 Base 在功能上是一样的,但是给了我们使用 dataclass_transform 的空间。 来源:https://blog.jiejiss.com/

2024/3/4
articleCard.readMore

Boot an Arch Linux RISC-V using qemu-system

CAUTION We (distro packagers) decided to modify GCC spec to get rid of just way too many atomic symbol missing error. AFAIK debian does the same thing in the same way. So, you may notice that some program compiles without -latomic flag, but that's not the case if you switch to some other distros. I wrote another blog post explaining this in detail. In short: For GCC you should add -latomic if you use std::atomic<T> where sizeof(T) is lower than 4. Also apply to those __atomic_compare_exchange_2() stuff. Clang is not affected. Method This approach requires you to be on a non-RISC-V Arch Linux (x86_64 or aarch64, etc.) machine, because we use pacstrap and pacman. 1 2 3 4 5 6 7 # pacman -Syu then reboot is recommended before this sudo pacman -S arch-install-scripts git qemu-img qemu-system-riscv riscv64-linux-gnu-gcc git clone https://github.com/CoelacanthusHex/archriscv-scriptlet.git cd archriscv-scriptlet ./mkrootfs ./mkimg ./startqemu.sh The default root password is archriscv. You will be asked to change the root password the first time you boot the machine. DO NOT LEAVE IT BLANK, or you won't be able to login as root user later. If, in the last step, you find yourself stucked at [ OK ] Reached target Graphical Interface for too long, just press Ctrl-C and re-run startqemu.sh. After booting the machine, you may need to install necessary packages: 1 sudo pacman -S git vim gcc Detailed Explanation (zh) mkrootfs 首先执行 /usr/share/makepkg/util.sh 来初始化当前的 bash 环境。这个脚本由 Arch Linux 的 makepkg 提供,里面有许多工具函数,例如 msg 函数可以往屏幕上打印出好看的 log。 1 . /usr/share/makepkg/util.sh 解析参数、显示帮助的部分这里略过。 创建 rootfs 的第一步是确保将会被视作 rootfs 的 / 目录的新创建的文件夹权限正确: 1 2 mkdir -p ./rootfs sudo chown root:root ./rootfs 随后调用 pacstrap 来生成 rootfs。 1 2 3 4 5 sudo pacstrap \ -C /usr/share/devtools/pacman-extra-riscv64.conf \ -M \ ./rootfs \ base 这里的 /usr/share/devtools/pacman-extra-riscv64.conf 是类似于 /etc/pacman.conf 的一个配置文件,其中声明了包括软件源在内的一些配置: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #[testing] #Server = https://archriscv.felixc.at/repo/$repo [core] Server = https://archriscv.felixc.at/repo/$repo [extra] Server = https://archriscv.felixc.at/repo/$repo #[community-testing] #Server = https://archriscv.felixc.at/repo/$repo [community] Server = https://archriscv.felixc.at/repo/$repo 这里的软件源配置比较重要,是 pacstrap 魔法的一部分。实际上,在调用 pacstrap 时,传入的 base 参数就告诉 pacstrap 需要从上述源里面下载、安装 base 组里面所有的软件包,其中就包括了 linux、glibc 和 pacman。而上述源就是 riscv64gc 架构的源,其中的软件包均面向 riscv64gc 架构编译,因此可以在 RISC-V 64 位 CPU 上的 Arch Linux 操作系统中运行。在 pacstrap 执行完毕后,base meta package 中的所有软件包都已安装完成,这时的 rootfs 已经安装了 Arch Linux 的最小发行版。 再往下,则是配置镜像源。 1 sudo sed -E -i 's|#(Server = https://riscv\.mirror\.pkgbuild\.com/repo/\$repo)|\1|' ./rootfs/etc/pacman.d/mirrorlist https://riscv.mirror.pkgbuild.com 为 Arch Linux 维护者在 pkgbuild.com 域名上为 Arch Linux RISC-V 创建的全球镜像。如果不添加这个镜像,初始的默认源从国内访问可能较慢或干脆无法访问。 1 2 3 sudo pacman \ --sysroot ./rootfs \ --sync --clean --clean 清空 pacman 的软件包缓存。通过 --sysroot 参数指定了 rootfs。 1 sudo usermod --root $(realpath ./rootfs) --password $(openssl passwd -6 "$password") root 设置系统密码。默认为 archriscv。usermod 的 --password 接受的是密码的哈希,因此需要使用 openssl passwd -6 "$password" 算出给定的密码的哈希,再传给 usermod。 1 2 3 4 sudo bsdtar --create \ --auto-compress --options "compression-level=9" \ --xattrs --acls \ -f "$filename" -C rootfs/ . 将配置完毕的 rootfs 文件夹压缩为 .tar.gz 格式,使用 --xattrs 以确保文件的 extended attribute 得到保留。压缩成功后,可以删除刚才临时创建的 rootfs 文件夹。 mkimg TBD 来源:https://blog.jiejiss.com/

2023/3/7
articleCard.readMore

关于基于机器学习的多目标对象追踪算法的文献综述

问题描述 多目标对象追踪(Multi-Object Tracking, MOT)一直是计算机视觉(Computer Vision,CV)领域中非常重要的研究对象,其核心是通过分析输入的图像序列,构建出不同帧的物体间的对应关系。多目标对象追踪常被用于自动驾驶、人流量统计、水果分拣、嫌犯追踪等领域,在工业中存在着广泛的应用。 目前,研究者已经提出了许多的多目标对象追踪算法。按照工作流程分类,MOT 算法分为基于检测的追踪(Detection-Based Tracking, DBT)和无检测追踪(Detection-Free Tracking, DFT)两类[1]。DBT 依赖于对象检测,通常建立在一些已有的多目标对象检测及分类算法,如 YOLO[2] 等算法的基础上;而 DFT 则不需要对象检测的参与。有关 DBT 和 DFT 的特点可以参考表 1。 表 1:DBT 及 DFT 特性对比[1] DBTDFT 初始化自动;不完美人工介入;完美 画面中对象数量可变固定不变 优点无需人工介入,画面中物体数量可变不需要识别器和分类器 缺点性能受到识别器和分类器的限制需要人工介入 常见应用场景物体种类固定,画面中物体数量改变物体种类不固定,但运动范围较小 而如果按照实现方法分类,多目标对象追踪算法可以被分成传统算法和基于机器学习的算法两类,其中传统算法通常工作在单摄像头场景下,基于机器学习的 MOT 算法在多摄像头场景下也工作良好,也更能适应工业上的多种应用需要[3]。因此,本文主要聚焦于近五年来提出的基于机器学习的 MOT 算法研究进展。 图 1:多摄像头场景下同时追踪两个目标对象的示意图[3] 近期研究综述 近年来,随着机器学习理论和模型的不断发展,MOT 领域相关的研究热度也在持续上升。自 2017 年 DeepSORT[4] 被提出起,在短短的几年内涌现出一大批基于机器学习的高性能、高准确率的 MOT 算法,如 CenterTrack[5]、Tracktor++[6] 等。有些研究成果很好地解决了在某些特定领域内特定需求下的准确率问题,有些则提出了新的网络结构和模型架构,福泽所有在这一领域开展研究的研究人员。本文试图以时间顺序为主轴,按照这些研究所解决的问题的种类来分类不同的研究,并综述各研究的思路和主要成果。 虽然将机器学习引入 MOT 算法的研究很早以前就已经开展,但是真正在准确率上做出突破性提升的是 2017 年的 DeepSORT。DeepSORT 在原本的基于卡尔曼滤波(高斯滤波)预测和匈牙利匹配计算最优解的 SORT[7] 算法的基础上引入了深度学习的概念,在一个大规模的行人重识别数据集上训练,增加了对图像部分缺失和短时间遮挡的鲁棒性,同时保持了算法的高效性[4]。并且 DeepSORT 是一个在线(Online)算法,其在识别物体关联和轨迹时只需要参考过往的信息,无需参考未来的图像,因此它能够工作在实时输入的视频流上,使用场景更广。 不过,由于 DeepSORT 仍然工作在 SORT 基础上,因此 SORT 存在的缺陷仍然会在 DeepSORT 中存在。例如,SORT 采用卡尔曼滤波预测物体在下一帧中的位置。卡尔曼滤波为贝叶斯滤波在置信度用多元正态分布的特殊情况下推导得出,可得其函数表示如下: \[p(x)=\det(2\pi S)^{-\frac{1}{2}}\exp\left( -\frac{1}{2}(x-\mu)^{T}S^{-1}(x-\mu)\right)\\\] 其中,\(\mu\) 为样本均值,\(S\) 为样本方差。然而实际上,由相机录制的视频往往存在不满足正态分布的位移扰动如手持相机导致的不规律抖动,不难想到此时卡尔曼滤波模型产生的结果的准确度会降低。这也是 DeepSORT 算法的主要缺点。 在随后的一段时间内,虽然又有一些基于机器学习的 MOT 算法被提出,但它们对准确率的提升微乎其微,甚至在两年中仅仅使得最佳准确率(State-of-the-Art,SOTA)提升了 2%[1]。这时,Bergmann 团队在 ICCV2019 上发布了 Tracktor 算法的论文[6]。这篇论文不仅部分否定了过去两年中全世界研究人员普遍采用的检测器和追踪器配合的思路、简化了此前的网络模型,还对 SOTA 的提升做出了贡献。具体来讲,Bergmann 团队尝试了仅仅使用检测器和物体检测算法,通过引入回归层来调优物体检测的外接矩形(Bounding Box)位置的做法,实现了性能优秀且准确率高的 MOT 算法。这种实现方式的优势主要有两点:首先,无需额外训练追踪器,不仅节约了算力,也降低了性能和功耗要求;再者,检测器全部为在线算法,回归层也只需要参考此前的输入和输出,因此 Tracktor 算法同样是在线算法。不过,Tracktor 算法也同样存在一定的缺点。例如,Tracktor 无法解决由于物体相互遮挡导致身份交换(Identify Switch,IDSW)的问题。为了解决这一问题,在同一篇论文中作者还提出了 Tracktor++ 算法,通过引入短期(Short-Term)身份重识别(Re-Identification, ReID),基于 Siamese Network 提取出物体的表面特征来帮助匹配[6]。然而,加入 ReID 导致了计算耗时的增加和性能的下降,为此作者还额外提出了通过等速假设和增强型相关系数最大化这两种运动模型(Motion Model)来改善预测的外接矩形在下一帧中的位置以减少 ReID 匹配耗时。 这一阶段同样有一些别的改进 DeepSORT 的研究,例如有一些 MOT 爱好者在私下尝试将 DeepSORT 中的 SORT 部分替换为其它的目标检测算法如 YOLOv4 甚至 YOLOv5,同样也取得了接近 SOTA 的效果,并且摆脱了 SORT 算法的固有缺陷。在正式的期刊中同样出现了类似的研究,例如有团队提出通过遮挡组管理(Occlusion Group Management)来改进 DeepSORT 算法的匹配部分[8]。然而 2020 年提出的 CenterTrack 算法[5] 指出了他们所采用的检测器和追踪器同时训练(Joint Learning the Detector and Embedding Model,JDE)方法中存在的固有缺陷,2021 年提出的 FairMOT 算法[9] 同样也注意到了这些缺陷。二者均在传统的 JDE 基础上做出了有针对性的改进,通过将 Anchor-Based 检测替换为 Anchor-Free 检测,缓解了这些问题,在多个数据集上取得了 SOTA 的成绩。在 FairMOT 的论文中,作者提出,目前 JDE 训练的检测方式为同时提取检测框和检测框内物体的 ReID 信息,然而由于同一个物体可能出现在多个检测框中,会导致较高的网络模糊性。同时,物体的实际中心可能并不是其外接矩形的几何中心,这就可能导通过几何中心计算出的位移距离和实际位移距离存在偏差。CenterTrack 算法采用了基于其灵感来源 CenterNet 的基于物体实际中心点的检测[10],采用这种方式能够更加准确地提取到物体的特征用于 ReID 层,可以更好地避免身份互换的问题。FairMOT 算法同样采用了基于物体实际中心点的特征检测,并且还引入了类似编码器和解码器模型(Encoder-Decoder Model)的网络,通过逐层降采样、分层提取特征信息的方式产生经过融合的多层信息,恰好满足了 ReID 算法需要多层融合信息的需求。提取出的高分辨率特征信息将根据其维度分别送入检测器和 ReID 层,最终取得了很好的效果。 除了改进基于 ReID 的匹配算法外,也有一些研究聚焦于如何更好地计算不同实例的相似度以提升准确率。Pang 团队在 2021 年提出了 QDTrack 算法[11] 便是如此。Pang 团队认为,此前工作仅仅利用像素级先验知识进行追踪,这种方法大多只适合一些简单的场景,当目标较多、存在大量遮挡或拥挤现象时,单纯基于位置信息的匹配很容易产生错误的结果。因此,QDTrack 通过拟密集(Quasi-Dense)匹配,支持在一张图片中创建上百个兴趣区域,通过对比损失以学习网络参数,尽可能多地利用图片中的已有信息。下图为展示 QDTrack 创建上百个兴趣区域的示意图。 图 2:QDTrack 的拟密集匹配能够创建远多于此前算法的兴趣区域并学习[11] 同时,由于目标较多的场合下一定会频繁出现新目标进入画面和已有目标在画面边缘消失的情况,因此作者将背景单独作为一类参与训练和匹配,从而能够通过双向 Softmax 函数增强一致性。由于学习到的实例相似度特征太好,在最终的关联步骤即使是仅仅采用最简单的最近邻搜索也能得到非常好的匹配准确率。QDTrack 在采用了 Softmax 函数后的目标函数如下所示[11]: \[\mathcal{L}_e=\log \left[1+\sum_{\mathbf{k}^{+}} \sum_{\mathbf{k}^{-}} \exp \left(\mathbf{v} \cdot \mathbf{k}^{-}-\mathbf{v} \cdot \mathbf{k}^{+}\right)\right]\] 其中,\(\mathbf v, \mathbf{k}^{+}, \mathbf{k}^{-}\) 分别为训练样本、正目标样本和负目标样本的特征嵌入(Embedding)。这里的 Embedding 指的是是一种把原始输入数据分布地表示成一系列特征的线性组合的表示方法。 随后的研究表明,Quasi-Dense 特征匹配方案的泛化性很强。Hu et al. 在 2022 年将 Quasi-Dense 泛化到三维空间中的多目标对象匹配问题上,提出了 QD-3DT 算法[12]。该算法能够基于单目摄像头的二维的图像序列输入,给出估计的物体在三维空间中的外接矩形,并且在相邻帧中以高准确率和良好的性能匹配识别到的物体。论文作者在自动驾驶的常见场景下测试了该算法,取得了非常优秀的结果。需要注意的是,自动驾驶的应用场景比较特殊,涉及到人类的生命安全,故通常要求算法的鲁棒性极高。QD-3DT 在测试中很好地适应了雨天和夜晚等行驶条件,证明了拟密集匹配算法的优越性[12]。 未来展望 目前看来,2021 年前后提出的一些算法和模型(如 QDTrack)已经能够满足绝大多数场景下的使用需求,二维场景下的多目标匹配问题可以认为已经得到了较好的解决。因此,未来的发展或许主要会聚焦在两个方向上:第一个方向是使得学术界的模型能够尽快在工业届投入使用,在实践中检验模型存在的不足,并尝试逐步替换掉目前广泛使用的 DeepSORT 模型;第二个方向是尝试将上述算法泛化、迁移至 3D 领域,解决三维空间中的多对象匹配问题,从而更好地服务于自动驾驶和工业控制等领域。不过,在解决三维空间中多对象匹配问题时,可能需要模型有能力接受来自不同位置的多个摄像头的输入以增强匹配的准确度,此方向的研究暂时还比较空白。考虑到工业届对这类算法存在较大的需求,可以预计在未来一定会有一些优秀的相关成果出现。 参考文献 [1] Luo, W., Xing, J., Milan, A., Zhang, X., Liu, W., & Kim, T.-K. (2021). Multiple object tracking: A literature review. Artificial Intelligence, 293, 103448. https://doi.org/10.1016/j.artint.2020.103448 [2] Bochkovskiy, A., Wang, C.-Y., & Liao, H.-Y. M. (2020). YOLOv4: Optimal Speed and Accuracy of Object Detection. doi:10.48550/ARXIV.2004.10934 [3] Kalake, L., Wan, W., & Hou, L. (2021). Analysis based on recent deep learning approaches applied in real-time multi-object tracking: A Review. IEEE Access, 9, 32650–32671. https://doi.org/10.1109/access.2021.3060821 [4] Wojke, N., Bewley, A., & Paulus, D. (2017). Simple online and realtime tracking with a Deep Association metric. 2017 IEEE International Conference on Image Processing (ICIP). https://doi.org/10.1109/icip.2017.8296962 [5] Zhou, X., Koltun, V., & Krähenbühl, P. (2020). Tracking objects as points. Computer Vision – ECCV 2020, 474–490. https://doi.org/10.1007/978-3-030-58548-8_28 [6] Bergmann, P., Meinhardt, T., & Leal-Taixe, L. (2019). Tracking without bells and whistles. 2019 IEEE/CVF International Conference on Computer Vision (ICCV). https://doi.org/10.1109/iccv.2019.00103 [7] Bewley, A., Ge, Z., Ott, L., Ramos, F., & Upcroft, B. (2016). Simple online and realtime tracking. 2016 IEEE International Conference on Image Processing (ICIP). https://doi.org/10.1109/icip.2016.7533003 [8] Song, Y.-M., Yoon, K., Yoon, Y.-C., Yow, K. C., & Jeon, M. (2019). Online multi-object tracking with GMPHD Filter and Occlusion Group management. IEEE Access, 7, 165103–165121. https://doi.org/10.1109/access.2019.2953276 [9] Zhang, Y., Wang, C., Wang, X., Zeng, W., & Liu, W. (2021). FairMOT: On the fairness of detection and re-identification in multiple object tracking. International Journal of Computer Vision, 129(11), 3069–3087. https://doi.org/10.1007/s11263-021-01513-4 [10] Duan, K., Bai, S., Xie, L., Qi, H., Huang, Q., & Tian, Q. (2019). CenterNet: Keypoint Triplets for object detection. 2019 IEEE/CVF International Conference on Computer Vision (ICCV). https://doi.org/10.1109/iccv.2019.00667 [11] Pang, J., Qiu, L., Li, X., Chen, H., Li, Q., Darrell, T., & Yu, F. (2021). Quasi-dense similarity learning for multiple object tracking. 2021 IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR). https://doi.org/10.1109/cvpr46437.2021.00023 [12] Hu, H.-N., Yang, Y.-H., Fischer, T., Darrell, T., Yu, F., & Sun, M. (2022). Monocular quasi-dense 3D object tracking. IEEE Transactions on Pattern Analysis and Machine Intelligence, 1–1. https://doi.org/10.1109/tpami.2022.3168781 来源:https://blog.jiejiss.com/

2022/10/9
articleCard.readMore

RV64 板子更换 rootfs 指南

XieJiSS, revision 3 本文可能不会及时更新,请以RV64 板子更换 rootfs 指南 - archriscv-packages Wiki为准 0x00 警告 请确保你或你的同伴可以在物理上(通过串口)访问到板子。否则,你的板子将在 0x02 的第 6 步重新启动时失联。 0x01 准备工作 首先,以 root 身份回到根目录: 1 2 sudo su cd / 下载 rootfs tarball(确保安装了 wget): 1 2 3 4 mkdir new && cd new wget curl -O https://archriscv.felixc.at/images/archriscv-20220727.tar.zst tar -xf archriscv-20220727.tar.zst cd .. 在根目录创建 old 文件夹: 1 mkdir old 看一下 fstab: 1 cat /etc/fstab 最好拍照或截图备查。 获取当前的 Linux 内核版本: 首先,初始化用于从 vmlinuz 提取内核版本的脚本。vmlinuz 一般位于 /boot 目录下,需要 root 权限才能访问。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 kver_generic() { # For unknown architectures, we can try to grep the uncompressed or gzipped # image for the boot banner. # This should work at least for ARM when run on /boot/Image, or RISC-V on # gzipped /boot/vmlinuz-linuz. On other architectures it may be worth trying # rather than bailing, and inform the user if none was found. # Loosely grep for `linux_banner`: # https://elixir.bootlin.com/linux/v5.7.2/source/init/version.c#L46 local kver= reader=cat [[ $(file -b --mime-type "$1") == 'application/gzip' ]] && reader=zcat read _ _ kver _ < <($reader "$1" | grep -m1 -aoE 'Linux version .(\.[-[:alnum:]]+)+') printf '%s' "$kver" } 执行 kver_generic /boot/vmlinuz,记住 Linux 内核版本。可以将其赋值给一个临时的 shell 变量,也可以继续拍照记录: 1 linux_kver=$(kver_generic /boot/vmlinuz) 注意,如果你当前的系统没有开启内核压缩,那么可能需要将 vmlinuz 修改为 vmlinux。 看一下 ip addr 和 router 的地址: 1 2 3 # 这里 eth0 可能需要按实际情况修改,ip addr show 可以查看全部 device # 一般 ip addr show 的第二个 device 就是我们需要的 device(第一个是 lo 本地回环) ip addr show dev eth0 记录 inet 行的 IP,这是你本机当前的 IP 地址。之后会用到,建议拍照或截图留存。(包括 IP 后面的 /24,如果显示的不是 /24 那么之后步骤中也要对应修改成此时显示的后缀) 其实理论上这步不需要的,用 dhcp 即可。但似乎 PLCT 南京内网的端口转发本质上基于静态 IP,依赖了 dhcp 客户端重启后服务端优先发放此前释放的 IP 的特征,并不稳定。建议还是在 0x02 的第七步里配成静态 IP。 1 ip route show dev eth0 记录 default via 后面的 IP 地址,这个就是你的板子所在局域网的 router 的地址。建议拍照或截图留存。 如果目标板子不在 PLCT 南京内网,并且你并不理解上述命令的用途:建议在此停下,先和 mentor 讨论清楚网络拓扑再继续。 0x02 开始更换 rootfs 记录几个关键路径(不用真的记,反正这里有): 1 2 /new/lib/ # 后面步骤中会设置为 LD_LIBRARY_PATH /new/lib/ld-linux-riscv64-lp64d.so.1 在新的 rootfs 并非 Arch Linux 时,可能需要将 /lib 替换为 /usr/lib。原因详见:The /lib directory becomes a symlink - Arch Linux News 移动文件夹 1 2 mv etc home media mnt opt root srv var old/ # 这里保留 /boot mv new/etc new/home new/mnt new/opt new/root new/srv new/var ./ 继续移动文件夹 1 2 3 4 LD_LIBRARY_PATH=/new/lib/ /new/lib/ld-linux-riscv64-lp64d.so.1 /new/bin/mv bin sbin usr lib old/ LD_LIBRARY_PATH=/new/lib/ /new/lib/ld-linux-riscv64-lp64d.so.1 /new/bin/mv new/bin new/sbin new/usr new/lib ./ mv old/lib/firmware ./lib/ /lib/firmware 是 Ubuntu on Unmatched 的 firmware 路径,可能需要按实际情况修改。 把 kernel modules 移动回来 1 mv old/usr/lib/modules/$linux_kver ./lib/modules/ 这里的 $linux_kver 是前面「获取当前的 Linux 内核版本」步骤中设置的。 dtbs、vmlinuz 等均位于 /boot 下,此前并未覆盖 /boot 因此在这一步不用操心它们。备注:一般来讲,新 rootfs 的 tar 文件里面并不会包含 dtbs 和 vmlinuz 等文件,它们一般会出现在 .img 或 .iso 内,这超出了 rootfs 的范畴,故本文不做详细解释。 修改 fstab 1 2 echo "LABEL=cloudimg-rootfs / ext4 discard,errors=remount-ro 0 1" >> /etc/fstab echo "LABEL=timesyncd-clock /var/lib/systemd/timesync/ tmpfs size=1K,rw,nodev,nosuid,noexec,noatime 0 1" >> /etc/fstab 注意这里可能需要根据之前看的 fstab 内容来酌情修改第一行,例如原本是 vfat 的盘你肯定不会希望它被设置成按照 ext4 格式读取。 第二行的 tmpfs 是因为我们稍后要启用 systemd-timesyncd,但是不希望它太过影响硬盘寿命。如果你不在乎,可以丢掉这行。 重启机器。 重启后已经是 Arch Linux 了。开始配网络和 fstab: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 echo test > test.txt # 测试 / 是否被 mount 为 rw # mount -o remount,rw / # 如果上一行报错,则执行这一行,然后停下并联系你的 mentor rm test.txt ip addr add 之前记录下的内网IPv4地址/24 dev eth0 ip link set eth0 up ip route add default via 之前记录下的router地址 dev eth0 # 114.114.114.114 是国内常用的 dns 递归服务器 echo 'nameserver 114.114.114.114' > /etc/resolv.conf systemctl start systemd-timesyncd pacman -Syu --noconfirm pacman -Syy vim openssh dhcpcd systemctl start sshd.service systemctl enable sshd.service touch .ssh/authorized_keys # 受信任的 ssh 公钥,可以使用 vim 编辑 vim /etc/dhcpcd.conf # 配置样例如下。这里受限于南京内网拓扑,提供的是静态 IP 的配置方案 1 2 3 4 5 6 # /etc/dhcpcd.conf interface eth0 static ip_address=之前记录下的内网IPv4地址/24 static routers=之前记录下的router地址 static domain_name_servers=114.114.114.114 更新 pacman 软件源,随后再次重启。 1 2 pacman -Syu --noconfirm # reboot -f,对于 Unmatched 板子可能还需要按 reset 按钮 跑完 Syu 如果不重启,可能会遇到很多「升级升了一半」导致的问题,例如找不到 kernel module、找不到各种符号等等。 来源:https://blog.jiejiss.com/

2022/8/25
articleCard.readMore

ACTF2022 safer-tg-bot-{1,2} WP

safer-telegram-bot-1 Search for flag1 in the source code 1 const user1 = createUser(~~(1 + Math.random() * 1000000), "test", fs.readFileSync(__dirname + "/flag1.txt", "utf8")); Search for user1.flag 1 2 3 4 5 6 7 8 9 10 11 12 13 bot.on("callback_query", async (query) => { const callbackData = query.data || ""; const userId = parseInt(callbackData.split("_")[0]); if(userId !== user1.uid) { return await sendMessage(chatId, `...not authorized. ...`); } if(!isAuthorizedUid(query.from.id)) { authorizedUids.push({ // ... }); } return await sendMessage(chatId, "...Your flag is `" + toSafeCode(user1.flag) + "`", ...); } Read the Telegram Bot API document: CallbackQuery This object represents an incoming callback query from a callback button in an inline keyboard. FieldTypeDescription idStringUnique identifier for this query fromUserSender dataStringOptional. Data associated with the callback button. Be aware that the message originated the query can contain no callback buttons with this data. Conclusion: We need to make sure that in the provided callback_data, the substring before the first _ equals to user1's uid. We can tell that the callback data is set in the handler of /login, and there are three types of them: "0_login_callback:" + msg.chat.id + ":" + msg.message_id authorizedUids[0].uid + "_login_callback:" + msg.chat.id + ":" + msg.message_id "-1_login_callback:" + msg.chat.id + ":" + msg.message_id Hence, we only need to click the button exactly when the second kind of callback data appears. Under the competition environment, the time frame available for this is about 400ms. Since the first type of callback data will last for 2 seconds to 16 seconds, trying to click the button with human hands and expecting the flag to appear is probably not feasible. After a quick search in Google, we can find two major automated Telegram MTProto API Framework: Telethon and Pyrogram. Here, a solution based on pyrogram is provided: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import asyncio from pyrogram import Client, filters api_id = YOUR_API_ID api_hash = "YOUR_API_HASH" # these two values are from https://core.telegram.org/api/obtaining_api_id bot = Client("my_account", api_id, api_hash) @bot.on_edited_message() def auth(client, message): if message.chat.username == "actfgamebot01bot": print("attempting to login as user1...") if message.reply_markup and message.reply_markup.inline_keyboard: client.request_callback_answer("actfgamebot01bot", message.id, message.reply_markup.inline_keyboard[0][0].callback_data) bot.run() The above code will be triggered twice per a /login's response message, but that's OK. safer-telegram-bot-2 There are 3 expected methods to solve this challenge. Background root user's userid is set to 777000, which is the same as Telegram official account's userid. In other words, we need to let the official account send /iamroot to the bot. This is not quite possible; however, if we search for "Telegram 777000" on Google, we can find a GitHub issue: [BUG] PTB detect anonymous send channel as 777000. By observing the screenshot, we can see that when a channel is linked to a group (see also: Discussion Groups), messages sent in the channel will be automatically forwarded to the discussion group. This forward operation is actually done by user 777000, which means that bot will think this message comes from Telegram's official account. But the exploit is not so easy. If we invite the bot to a group, it will quit automatically: 1 2 3 4 5 6 7 8 bot.on("my_chat_member", async (update) => { // this works for both channels and groups... I think so if(String(update.chat.id).startsWith("-100")) { await sendMessage(update.chat.id, "This bot is not allowed to join groups"); await bot.leaveChat(update.chat.id); return; } }); Thus, the problem becomes "how to stop the bot from quitting groups". Solution 1: I'm a Telegram Mechanism Expert We may recognize that in the callback function bind to the my_chat_member event, an if statement is used to check whether update.chat.id starts with -100. Telegram's groups and channels use merely the same underlying codes, and their chatIds both start with -100. However, people familiar with Telegram will know that not all groups starts with -100. This is caused by one of the history burdens of Telegram. Specifically, Telegram has two types of chats: group and supergroup. Supergroup supports more functions in comparison with group, e.g. setting admins with different admin rights, linking to a channel to act as it's discussion group, obtaining a group username so that it becomes a public group, preserving all history messages, etc. The Telegram dev team is devoting much efforts to hide the UX difference between groups and supergroup. Newly created chats are all groups by default, which has negative chatId but not starting with -100 (Aha!), and will escalate to supergroup automatically when users try to perform actions that are not supported by groups on it. Note that during the escalation process, the group (which is becoming a supergroup) will discard its old chatId and obtain a new one, which starts with -100. Knowing this, it is not hard to come up with a viable solution: Create an ordinary group Invite the bot into this group Link the group to your channel, so that the group becomes a discussion group, which is necessarily a supergroup. At this point, an automatic escalation will happen on the group. The client will prompt the user whether the previous 100 messages is visible to the bot. If you choose false, then everything works fine; Otherwise, the bot will receive those messages (as Updates) again, which will probably trigger the my_chat_member callback again, resulting in the bot leaving the group (because the now supergroup has a chatId starting with -100). To avoid this consequence, you can send 100 garbage messages prior to linking the chat to your channel. Send /iamroot in your channel, and receive flag2. Solution 2: Prototype Pollution This path is added for those not familiar with Telegram. Diving into the handler of /addkw key reply command, we can discover that the program tries to write the reply specified by the user into the corresponding entry of user1's keywordMap: 1 2 3 4 5 6 onText(/^\/addkw (\S+) (\S+)/, async (msg, match) => { const keyword = match[1]; const reply = match[2]; user1["keywordMap?." + keyword] = () => reply; await sendMessage(msg.chat.id, "success"); }); Noticing keywordMap?. looks suspicious, let's have a quick glance at its definition: 1 2 3 4 5 6 7 8 9 10 get(target, prop) { const paths = prop.split("?."); let current = target; for (const path of paths) { current = current[path]; if(!current) return undefined; } return current; } Inside the getter function, the key is split at ?. , before accessing corresponding values layer-by-layer. By doing so, it implements something similar to the ?. optional chaining operator. However, here it does not filter the key to be accessed, hence we can construct a prototype pollution. For instance, we set the key to be __proto__, and now we can overwrite Object.prototype. Send /addkw __proto__?.test 1 to bot, and we can pollute Object.prototype.test: 1 2 const a = {}; console.log(a.test); // "1" Read the source code of node-telegram-bot-api, and we can know that the framework tries to determine Update type by a series of ifs: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // ... const pollAnswer = update.poll_answer; const chatMember = update.chat_member; const myChatMember = update.my_chat_member; // ... } else if (pollAnswer) { debug('Process Update poll_answer %j', pollAnswer); this.emit('poll_answer', pollAnswer); } else if (chatMember) { debug('Process Update chat_member %j', chatMember); this.emit('chat_member', chatMember); } else if (myChatMember) { debug('Process Update my_chat_member %j', myChatMember); this.emit('my_chat_member', myChatMember); // ... Obviously, we can pollute any attribute access operation before update.my_chat_member, e.g. chat_member, so that the handler of my_chat_member will never be invoked: 1 /addkw __proto__?.chat_member 1 Solution 3: Race Condition If the method of racing condition is to be carried out, some special techniques might be needed. The very first Update the bot will receive after it enters the group is always the Update representing the bot's join chat event, hence making it impossible for other callbacks to be triggered before my_chat_member. What's more, the auto-forwarding of channel messages to linked discussion groups in Telegram has a noticeable lag, so if the attacker invite the bot prior to sending the message in channel, the exploitation will never success. So, we need to send /iamroot in the channel first, and after sleeping for a proper duration, we'll invite the bot to join the discussion group, so that this message is forwarded to the group between the asynchronous my_chat_member handler's await sendMessage and await bot.leaveChat call. 来源:https://blog.jiejiss.com/

2022/7/23
articleCard.readMore

A RISC-V gcc pitfall revealed by a glibc update

UPDATE 2023-05-11: Finally we have builtin inline subword atomic support in GCC! [PATCH] riscv: Don't add -latomic with -pthread https://gcc.gnu.org/pipermail/gcc-patches/2023-May/617355.html [PATCH v7] RISCV: Inline subword atomic ops https://gcc.gnu.org/pipermail/gcc-patches/2023-April/616807.html So we won't be bothered by this issue anymore. The original blog post: TL;DR: see "Wrap Up" and "Solution". Problem Recently, when we're working on a series of package rebuild pipelines triggered by a glibc update (from 2.33 to 2.34), we discovered that some previously compiling packages, mostly configured to use CMake, refuse to compile now. Their error logs look like this: 1 2 3 /usr/bin/ld: libbson-1.0.so.0.0.0: undefined reference to `__atomic_exchange_1' /usr/bin/ld: libbson-1.0.so.0.0.0: undefined reference to `__atomic_compare_exchange_1' collect2: error: ld returned 1 exit status This is quite strange to us, because we used to think we have got rid of this for all by manually appending set(THREADS_PREFER_PTHREAD_FLAG ON) to these packages' CMakeLists.txt, despite replacing all -lpthread with -pthread. This was done in a per-package manner, modifying the patch to meet the need of the tech stack used by the specific package, and we are sure that they used to work before the glibc upgrade. Before proceeding, you need to know the difference between -pthread and -lpthread, and that -pthread is the most preferable way if you want to link to the pthread library, at least for glibc<=2.33. So, what's happening? Is there anything vital got changed in this glibc upgrade? Cause After reading the release note of glibc 2.34, we did notice something related: ...all functionality formerly implemented in the libraries libpthread, libdl, libutil, libanl has been integrated into libc. New applications do not need to link with -lpthread, -ldl, -lutil, -lanl anymore. For backwards compatibility, empty static archives libpthread.a, libdl.a, libutil.a, libanl.a are provided, so that the linker options keep working. Hmm, good, sounds like we don't need -pthread anymore. Actually, it appeared to us that CMake thinks the same. After running cmake for the failing packages, we can confirm that nothing looks like -pthread is appended to either CXXFLAGS or LDFLAGS. However, if forced to compile with -pthread, the previous-mentioned link error disappears. In other words, applying this patch fixes the error. 1 2 - make + CFLAGS="-pthread $CFLAGS" CXXFLAGS="-pthread $CXXFLAGS" make At this point, every clue we have gathered so far seems to indicate a bug inside cmake: Other tools work fine set(THREADS_PREFER_PTHREAD_FLAG ON) appears to be "broken" Manually appending -pthread fixes the issue, so cmake failed to recognize that -pthread is necessary Root Cause Blame CMake? So we dig into CMake's source code to see what happened. To our surprise, we didn't notice any change to the detection code it used for determining whether -pthread is necessary. CMake's detection code, at the time of writing, looks like this: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <pthread.h> static void* test_func(void* data) { return data; } int main(void) { pthread_t thread; pthread_create(&thread, NULL, test_func, NULL); pthread_detach(thread); pthread_cancel(thread); pthread_join(thread, NULL); pthread_atfork(NULL, NULL, NULL); pthread_exit(NULL); return 0; } It turned out that according to the glibc upgrade, this detection code now compiles without additional arguments: 1 2 3 $ gcc test_pthread.c $ echo $? 0 As far as I can tell, this does not look like a CMake bug. The expected behavior, which is exactly what we have seen, is that if the test code can be compiled without -pthread, then the argument should not be appended to the command like (or {C,CXX}FLAGS, correspondingly). This lead to another question: why those packages are failing now, provided that the detection code is unchanged, and it used to work properly? Dive into Source Code Let's take the package mongo-c-driver as an example. As of version 1.21.1, we can see the code listed below in its src/libbson/src/bson/bson-atomic.h (Feel familiar with this path? Remember the libbson-1.0.so.0.0.0: undefined reference error log mentioned before?): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #define DECL_ATOMIC_STDINT(Name, VCSuffix) \ DECL_ATOMIC_INTEGRAL (Name, Name##_t, VCSuffix) #define DECL_ATOMIC_INTEGRAL(NamePart, Type, VCIntrinSuffix) \ static BSON_INLINE Type bson_atomic_##NamePart##_fetch_add ( \ Type volatile *a, Type addend, enum bson_memory_order ord) \ { \ DEF_ATOMIC_OP (BSON_CONCAT (_InterlockedExchangeAdd, VCIntrinSuffix), \ __atomic_fetch_add, \ __sync_fetch_and_add, \ ord, \ a, \ addend); \ } // Omitted many lines... As we can tell from libbson's source code, it attempts to use the __atomic_* builtins of GCC, like __atomic_fetch_add and __atomic_compare_exchange. We can construct some test cases to check whether we're right: 1 2 3 4 5 // test1.c int main() { char u = 0, v = 1; __atomic_exchange_n(&u, &v, __ATOMIC_RELAXED); } 1 2 3 4 5 // test2.c int main() { short u = 0, v = 1; __atomic_exchange_n(&u, &v, __ATOMIC_RELAXED); } 1 2 3 4 5 // test4.c int main() { int u = 0, v = 1; __atomic_exchange_n(&u, &v, __ATOMIC_RELAXED); } 1 2 3 4 5 // test8.c int main() { long long u = 0, v = 1; __atomic_exchange_n(&u, &v, __ATOMIC_RELAXED); } Here, we use the gcc built-in __atomic_* because libbson used it. Replacing this with std::atomic<T> (and compile it with g++) yields identical results correspondingly. You may also use uint*_t provided by the linux/types.h header, as their sizes are promised to be the same across all architectures. Compile results: 1 2 3 4 5 6 7 8 9 10 $ gcc test1.c /usr/bin/ld: /tmp/ccNst0x1.o: in function `.L0 ': test1.c:(.text+0x34): undefined reference to `__atomic_exchange_1' collect2: error: ld returned 1 exit status $ gcc test2.c /usr/bin/ld: /tmp/ccvK2EbX.o: in function `.L0 ': test2.c:(.text+0x36): undefined reference to `__atomic_exchange_2' collect2: error: ld returned 1 exit status $ gcc test4.c $ gcc test8.c If you are kind of familiar with C++, you may have heard that codes using atomic operations might need to link libatomic (I remembered once reading this, but I can't find it now). It turned out that if we provide -latomic to gcc, the code compiles: 1 2 $ gcc test1.c -latomic $ gcc test2.c -latomic And this also works for -pthread: 1 2 $ gcc test1.c -pthread $ gcc test2.c -pthread Smart readers might have felt something unusual: First, as we can see, gcc compiles test4.c and test8.c successfully without -latomic or -pthread, but it couldn't handle the atomic operations in test1.c and test2.c without making a call to libatomic: 1 2 3 4 5 $ gcc test1.c -latomic -S $ gcc test4.c -S $ cat test1.s | grep atomic call __atomic_exchange_1@plt $ cat test4.s | grep atomic This is partially explained in a GitHub issue (riscv-collab/riscv-gcc#12). Till April 2022, i.e. when this blog is written, gcc does not support inlining subword atomics, and this is the reason why -latomic must be presented when invoking gcc. But what about -pthread? Why -pthread works either? I mean, libpthread itself does not provide those atomic symbols, right? Actually, if you use ldd to check a.out generated by gcc test1.c -pthread, you will notice that libatomic is linked, instead of libpthread as we are expecting from -pthread. Changing -pthread with -lpthread won't work, so gcc must have done something internally for -pthread despite linking libpthread. In order to figure out the difference, we have to check the gcc spec: 1 2 $ gcc -dumpspecs | grep pthread %{pthread:-lpthread} %{shared:-lc} %{!shared:%{profile:-lc_p}%{!profile:-lc}} %{pthread:--push-state --as-needed -latomic --pop-state} Oh, the answer finally reveals: gcc silently append --as-needed -latomic if -pthread is provided. This also explains why changing -lpthread to -pthread works. The reason why gcc decided to link libatomic when -pthread is provided had been lost in a squashed commit. My guess is that they tried to cover up the subword atomic pitfall using this strategy. (UPDATE: aswaterman's initial fix did not limit the scope to -pthread. The limitation was added one week later.) Generally, atomics are used with multi-threading, and you use pthread in such cases. However, one may use atomics without pthread, like when writing SIGNAL handlers. So IMO this workaround is not good enough, and should be replaced by subword atomic inline support. UPDATE: We discussed this with some gcc RISC-V devs, and it turned out that when compiling gcc, at stage 1 it may not recognize libatomic (i.e. -latomic may not work) if you are compiling it with a gcc that uses the old spec So you'll have to set --with-spec at stage 1 to workaround this. Hopefully there would be an --always-link-libatomic configuration flag in the future. Fun facts: gcc once had added --as-needed -latomic to its LIB_SPEC unconditionally, in this commit (2017-02-01): 1 2 3 + #undef LIB_SPEC + #define LIB_SPEC GNU_USER_TARGET_LIB_SPEC \ + " " LD_AS_NEEDED_OPTION " -latomic " LD_NO_AS_NEEDED_OPTION \ But later in the final port commit submitted to gcc (2017-02-07; svn r245224), they limited the change to -pthread: 1 2 - " " LD_AS_NEEDED_OPTION " -latomic " LD_NO_AS_NEEDED_OPTION \ + " %{pthread:" LD_AS_NEEDED_OPTION " -latomic " LD_NO_AS_NEEDED_OPTION "}" The reason of making such limitation remains unknown. Wrap Up glibc moved libpthread into libc, and CMake's pthread sample code compiles directly. Hence, CMake thinks there's no need to provide -pthread when invoking gcc / g++. Since we used to patch such packages with -pthread instead of -latomic, and are actually relying on -pthread's side effect (--as-needed -latomic), this led to the consequence that neither -pthread nor -latomic is provided, so subword atomics refuse to compile as libatomic is not linked. For ways of fixing this problem, please refer to the Solution chapter. Also note that the dependence on libatomic causes another problem. Some packages / frameworks, e.g. JUCE insists that its atomic wrap only accepts lock-free values. However, when a program is compiled, the compiler cannot know whether a call to libatomic will be lock-free or not (actually, the emitted sequence will be lock-free, but the compiler doesn't know). Hence, the compiler will return false for std::atomic<bool>::always_lock_free, and this also happens for the macro ATOMIC_BOOL_LOCK_FREE defined in atomic.h. As a result, if you use juce::Atomic<bool>, some static asserts will fail, and the package refuses to compile. Solution Solutions vary according to the scale of projects. As a developer / user on behalf of yourself, export CFLAGS="$CFLAGS --as-needed -latomic" and likewise for CXXFLAGS solve the problem. As an alternative, you can also patch the Makefile file, and edit the flags inside it. As a packager maintaining multiple packages with the same toolchain, you may want to write a module / universal patch. For projects with CMake, you can use LLVM's CheckAtomic.cmake, which (if you are using a newer version) has taken subword atomics into consideration. As a developer maintaining a relatively large project that has gained some users, you may need to consider the compatibility with clang and compiler-rt. Clang should be able to handle -latomic, but better perform a double-check. You can temporarily rely on the side-effect of gcc -pthread (which is not recommended), or edit your configure script manually to disable -latomic when clang and compiler-rt are detected. As a developer of a widely-used toolchain project, despite considering clang and compiler-rt, compatibility with older compilers / linkers that do not support --as-needed and even -pthread might need to be considered. CMake reverted their -pthread fix on this because a compiler named XL does not support -pthread, and interprets it as -p -t hread, returning zero (oops! false negative) as its exit code. The final approach of solving this is to add subword atomic inlining support into gcc. There's a pending patch regarding this, but this patch only adds support for __atomic_fetch_add, so the problem would not be mitigated (but this patch indicates a good start!). Complicated solutions may increase the burden on project developers / maintainers, and their willingness to port their projects to RISC-V might be reduced. Aiming to tackle the problem, we have proposed to change LIB_SPEC back to its originally patched form to mitigate this issue partially (still not lock-free), and hope that the subword atomic pitfall can be solved completely in the future. UPDATE: After discussing this with gcc RISC-V devs, we reached the consensus that maybe a gcc configuration argument --always-link-libatomic can be added. UPDATE2: This breaks the bootstrap process of GCC. GCC compiles itself twice to get rid of e.g. dirty compiling environments stuffs. Modifying the spec string too early causes glitches, like libatomic calls itself infinitely. So we have to use sed to replace the spec string inside the first-stage GCC binary carefully, filling blanks (U+0020) to make sure old and new spec strings have the same length. 来源:https://blog.jiejiss.com/

2022/4/7
articleCard.readMore

Setup an Arch Linux RISC-V Development Environment

This is a frequently updating doc, and you can find its latest version here. This documentation aims to help you set up an Arch Linux RISC-V (riscv64gc) development environment powered by usermode QEMU and systemd-nspawn. Currently, the documentation contains instructions for Arch Linux, Debian and Ubuntu. Caution: If you are using a native RISC-V board, you should skip these steps, and scroll down to the bottom of this page. The following instruction is for x86_64, etc. users. 1 Prepare the Container 1.1 Install Packages If you are using Ubuntu/Debian as the host OS, skip to "For Debian and Ubuntu". For Arch Linux Add [archlinuxcn] Repo Source We need to install some packages from [archlinuxcn] later. Append these two lines to /etc/pacman.conf: 1 2 [archlinuxcn] Server = https://repo.archlinuxcn.org/$arch There is also a list of public mirrors available. After doing so, you need to trust the archlinuxcn maintainers' PGP keys to install packages from it: 1 $ sudo pacman -Sy && sudo pacman -S archlinuxcn-keyring Install Packages 1 $ sudo pacman -S qemu-user-static qemu-user-static-binfmt where qemu-user-static-binfmt is for registering QEMU interpreter to execute RISC-V ELF files. Other necessary packages like zstd and systemd-nspawn are listed in the dependency tree of base meta package, so they will also be installed by the provided command, hence there's no need to install them explicitly. For Debian and Ubuntu 1 $ sudo apt install zstd qemu-user-static systemd-container where zstd is for decompressing the Arch Linux RISC-V rootfs compressed tarball, and systemd-container is for the systemd-nspawn command, which we'll use later to spawn a container from the rootfs. 1.2 Download rootfs 1 $ curl -O https://archriscv.felixc.at/images/archriscv-20220727.tar.zst If you have poor connectivity upon reaching archriscv.felixc.at, you can also download the rootfs from mirror sites. They are listed at https://archriscv.felixc.at/. root user's password is sifive, but probably you don't need it if you are creating a container with this rootfs to chroot into it later. 1.3 Decompress rootfs Arch Linux 1 2 $ mkdir archriscv $ sudo bsdtar -xf archriscv-20220727.tar.zst -C archriscv Debian and Ubuntu 1 2 $ mkdir archriscv $ sudo tar -I zstd -xf archriscv-20220727.tar.zst -C archriscv tar may spit out some warnings, which turn out to be harmless and we can safely ignore them. 2 Start and Setup a Container 1 $ sudo systemd-nspawn -D ./archriscv -a -U where -D provides the root directory for the container, -a for preventing processes with PID 1 from not reaping zombie children, -U for preventing processes in container from using the same UID range as those used outside the container. 2.1 Check Architecture 1 2 # uname -m riscv64 2.2 System Upgrade 1 # pacman -Syu 2.3 (Optional) Install Packages For example, if you want to install vim: 1 # pacman -S vim 2.4 (Optional) Set Default Editor 1 # echo 'export EDITOR=vim' >> ~/.bashrc && source ~/.bashrc 2.5 (Optional) Create a Regular User for Development 1 # useradd -m <username> where -m means to create a home directory for the user to be added. Use visudo if you'd like to grant sudo privilege for this user, and append this line under ## User privilege specification: 1 <username> ALL=(ALL) NOPASSWD: ALL 2.6 (Optional) Switch to a Regular User 1 2 3 4 5 # exec su username $ cd ~ $ pwd /home/<username> $ echo 'export EDITOR=vim' >> ~/.bashrc && source ~/.bashrc 3 Compile and Run a Program This guide is about compile & run a program manually. If you want to start your development from a git repo or so, jump to 3.2 Compile for instructions on how to install git and other packages needed by your toolchain. If you are trying to create or modify an Arch Linux package (i.e. dealing with a PKGBUILD), you may refer to related ArchWiki pages. 3.1 Coding Run vim hello.c and type: 1 2 3 4 5 6 #include <stdio.h> int main(int argc, char *argv[]) { printf("Hello RISC-V!\n"); return 0; } Exit with esc and :wqEnter, as this S/O answer suggested :-P 3.2 Compilation First of all, update your local packages repo data from remote package sources (like sudo apt-get update): 1 $ sudo pacman -Sy You should run this prior to any attempt to install a package, unless you are sure that your local copy of package info is up-to-date. Then, install your development toolchain: 1 $ sudo pacman -S gcc cmake foo bar bah Compile your code: 1 $ gcc -o hello hello.c 3.3 Checking for File Format 1 2 $ file hello hello: ELF 64-bit LSB pie executable, UCB RISC-V, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-riscv64-lp64d.so.1, BuildID[sha1]=4a75c57e4e99654dca0d6dc91689dffbbe7dc581, for GNU/Linux 4.15.0, not stripped 3.4 Running the Program 1 2 $ ./hello Hello RISC-V! For native RISC-V board users: Edit your /etc/pacman.conf with the following settings: 1 2 3 4 5 6 7 8 9 10 11 [core] Server = https://archriscv.felixc.at/repo/$repo [extra] Server = https://archriscv.felixc.at/repo/$repo [community] Server = https://archriscv.felixc.at/repo/$repo [unsupported] Server = https://archriscv.felixc.at/repo/$repo Finally, add the packager's public key to your keyring: 1 $ pacman-key --recv-keys "B5971F2C5C10A9A08C60030F786C63F330D7CB92" # Always grab the latest key ID from https://archlinux.org/people/developers/#felixonmars 来源:https://blog.jiejiss.com/

2022/3/22
articleCard.readMore

解构赋值踩坑:神秘失踪的数据

话说在我们 PLCT ArchRV 组,基础设施都是我们自己搭出来的,分工、打标记、追踪状态全部靠 gh: XieJiSS/plct-archrv-pkg-bot、gh: cubercsl/archrv-pkg-notification-bot 和 gh: Ast-x64/plct-archrv-status-worker 实现。近日我们在维护标记和追踪状态时,发现 plct-archrv-pkg-bot (以下简称 bot)在重启时会丢失几条记录。 最开始,怀疑是重启的时候还有数据没写入到磁盘,导致数据丢失。但是经过排查,发现并不太可能,理论上所有的数据都是在修改时当场写入磁盘的。保险起见,增加了使用同步 IO 的 storePackageMarksSync,通过 npm: async-exit-hook 在进程退出时强制保存。然而还是丢数据。 看 log 可以确认,在退出时把内存里的数据全部写入到磁盘了,那丢数据是怎么回事呢? 又开始考虑是不是 race condition,于是通过 npm: lockfile 实现了进程的保护锁,确保同时只有一个 bot 进程在运行。除了没效果以外,效果非常好。看来也不是多进程并行写入的问题。 最后经过排查,发现问题的根源在 export 和 import 上。 最小可复现样例如下: 1 2 3 4 5 6 7 8 9 10 11 12 // a.js let A = [1, 2, 3, null]; function cleanUp() { A = A.filter(num => typeof num === "number"); } function showData() { console.log(A); } module.exports = { A, cleanUp, showData }; 1 2 3 4 5 6 7 8 // b.js const { A, cleanup, showData } = require("./a"); A.push(4); cleanUp(); A.push(5); A.push(6); showData(); // 只有 1, 2, 3, 4 可以看到,在执行 cleanUp 之后的数据全部丢失了。这是因为在 a.js 里对 A 重新赋值过后,b.js 解构赋值得到的 A 和 a.js 里的 A 不再指向同一个对象。解构赋值出来的 identifier A 指向的是 module.exports 的 A 属性在解构赋值那一时刻所指向的内存,随后执行的 A = A.filter 只是在 a.js 内部修改了 A 的指向,外部解构赋值出的 A 并不会随之更新。我们可以用 cpp 来翻译一下第一种写法: 1 2 3 const auto *A = a.A; a.A = new vector<int>(); // 此时 A 和 a.A 显然已经不再指向同一块内存了 概括一下:如果把 JS 里面解构赋值拿到的东西当做 &ref 来理解(这是常有的事)就会导致这种 inconsistency 的出现。 如果想修复这个问题,需要在 a.js 里面把 A 声明为 const,随后将 Array#filter 替换为自己实现的 in-place filter,详见这个 commit。当然你也可以在 b.js 里始终使用 a.A 的方式来访问 A。 来源:https://blog.jiejiss.com/

2022/3/15
articleCard.readMore

How's the GDPR (used to) related to my blog

Felix: they say that EU readers are blocked from your blog me: wtf really? how? also me (3 yrs ago): Haha GDPR goes brrrrrrr This is funny, I used to have google analytics integrated in my blog (maybe it still presents now? I'm not sure), and in order to make ga work, I added an adblock detector (but I've just realized that I'm not using it from the very beginning!). Also, I'm not willing to prompt users every time they visit the blog, so I set a cookie named blker (for blocker, I guess). Then I realized that this might violate the GDPR. Not good, I thought, so I searched on google for ways to create a valid, legal popup that informs users about those GDPR user agreement stuff. Unfortunately, the search results showed that I need to pay an amount of money to generate the legal text I need, and the fee was not what I could afford as I was still a high school boie at that time. So the approach was simple: I need to use ga to count readers (I know they provides far more information despite the readers count, but I'm not interested), so I want readers to disable their adblocker; In order to tell them to disable adblocker, I need to prompt them; As I don't want them to be prompted every time they visit the site, I have to set a cookie to mark readers that have been prompted. At that time, I felt like setting cookies without asking for agreement is violating the GDPR, and I couldn't cope with it because I have no money to generate the legal text, so it looks like I have no chance to be compatible with the GDPR law. Hmm, maybe I can stop EU readers from accessing the site, I thought. That's simple, just make a GET to /cgi-bin/trace api provided by cloudflare, and you can get the country code determined by source IP. At the time I wrote these code, the UK was still part of the EU (legacy code, lol), so I think I should keep UK in the list, and remove it when they finally complete the transaction (of course, I totally forgot this lol) Felix told me that if cookies are not correlated to a single user, then setting it may not violate the GDPR. But now I don't need cookies anymore: since 2020, I've been using busuanzi which only counts the number of readers. And I've just realized that they have shutdown the service (502 Bad Gateway for busuanzi). UPDATE: seems like the busuanzi service is working again, so I've just encountered a short outage? 来源:https://blog.jiejiss.com/

2022/3/11
articleCard.readMore

Rust is incompatible with LLVM, at least partially

Lately we've been compiling rust 1.59.1 on riscv64gc (will be referred to as rv64 in this article). We are packaging for the PLCT's Arch Linux RISC-V project, and thanks to the Arch wisdom, tools in the toolchain we use are always the latest. Still, we met a strange compile error: 1 2 3 4 5 6 7 8 9 error: unknown directive | note: instantiated into assembly here --> <inline asm>:1:2 | 1 | .insn i 0x0F, 0, x0, x0, 0x010 | ^ error: could not compile `log` due to previous error After performing some simple queries (cs.github.com is awesome!), we managed to locate the following code (ref): 1 2 3 4 5 6 7 8 9 10 11 12 //! Shared RISC-V intrinsics use crate::arch::asm; /// Generates the `PAUSE` instruction /// /// The PAUSE instruction is a HINT that indicates the current hart's rate of instruction retirement /// should be temporarily reduced or paused. The duration of its effect must be bounded and may be zero. #[inline] pub fn pause() { unsafe { asm!(".insn i 0x0F, 0, x0, x0, 0x010", options(nomem, nostack)) } } pause, fence and .insn TL;DR: pause is fence w,0, which is .insn i 0x0F, 0, x0, x0, 0x010. pause is provided by the Zihintpause extension. Though the widely adopted riscv64gc does not contains Zihintpause, the fence w,0 won't trigger a SIGILL, because it is treated similar to a nop instruction, so we can use it safely without bothering about compatibility. Firstly, by reading the comments, we can infer that this .insn assembly acts as the pause instruction. This stunned me a bit because AFAIK, the HINT feature in RISC-V ISA has always been in the reserved state (at least until December 2021): No standard hints are presently defined. We anticipate standard hints to eventually include memory-system spatial and temporal locality hints, branch prediction hints, thread-scheduling hints, security tags, and instrumentation flags for simulation/emulation. It's not hard to realize, though, that the pause instruction is introduced by Zihintpause extension, as an alias for fence w, 0. Previously, we have to use nop to polyfill the pause function implemented for other architectures: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 From 4e559dabe28e57ee27cb45c8297e1e387beed1d3 Mon Sep 17 00:00:00 2001 From: Xeonacid <h.dwwwwww@gmail.com> Date: Thu, 6 Jan 2022 17:12:11 +0800 Subject: [PATCH] riscv support added. tested on Arch Linux RISC-V. --- include/atomic_queue/defs.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/atomic_queue/defs.h b/include/atomic_queue/defs.h index 4098914..c0e7fb0 100644 --- a/include/atomic_queue/defs.h +++ b/include/atomic_queue/defs.h @@ -47,6 +47,13 @@ namespace atomic_queue { constexpr int CACHE_LINE_SIZE = 256; // TODO: Review that this is the correct value. static inline void spin_loop_pause() noexcept {} // TODO: Find the right instruction to use here, if any. } // namespace atomic_queue +#elif defined(__riscv) +namespace atomic_queue { +constexpr int CACHE_LINE_SIZE = 64; +static inline void spin_loop_pause() noexcept { + asm volatile ("nop"); +} +} // namespace atomic_queue #else #warning "Unknown CPU architecture. Using L1 cache line size of 64 bytes and no spinloop pause instruction." namespace atomic_queue { However, in RISC-V, the nop instruction simply stands for "no operation", and does not provide any further clues to relax the CPU, hence not saving any energy (but instead wasting it). So, it's definitely better to replace the fake pause (i.e. nop) with the real pause, as provided in the Zihintpause extension. One may say that, hey, this extension is not part of the riscv64gc extension set! This argue is valid, as riscv64gc stands for riscv64imafdc_Zicsr_Zifencei (used to be riscv64imafdc, when the I baseline has not been split to I + Zicsr + Zifencei). Let's look at the pause instruction in detail: Before doing this, you need to grab a RISC-V run-time, via QEMU or via buying a board from SiFive. 1 2 3 4 5 $ cat pause.asm pause $ as pause.asm -o pause-as pause.asm: Assembler messages: pause.asm:1: Error: unrecognized opcode `pause' Uh-oh, seems like the assembler does not know this instruction! That's because 1) the pause instruction is too new; 2) I'm using riscv64gc, which does not include Zihintpause. Never mind, we can still use fence w,0 as noted in the spec: 1 2 3 4 5 $ cat pause.asm fence w,0 $ as pause.asm -o pause-as pause.asm: Assembler messages: pause.asm:1: Error: illegal operands `fence w,0' Not good. But at least the rust version should work, as they have RISC-V as tier-2 target, and managed to make the release pass the CI test, right? 1 2 3 4 5 6 7 8 9 10 11 12 $ cat pause.asm .insn i 0x0F, 0, x0, x0, 0x010 $ as pause.asm -o pause-as $ objdump -d pause-as pause-as: file format elf64-littleriscv Disassembly of section .text: 0000000000000000 <.text>: 0: 0100000f fence w,unknown Hmm, seems like the .insn i is working (compiling, at least), but what does this fence w,unknown stands for? Let's have a loot at the spec: PAUSE spec Shouldn't it be fence w,0? Actually, it is fence w,0, as denoted by the hex 0100000f: Fence spec IMO this is a subtle bug (used to be a feature) of the disassembler, and as we can see, this is already fixed in the llvm toolchain: 1 2 3 4 5 6 7 8 9 10 11 12 13 diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp index 3268740849f00..cfac3f76de42e 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp @@ -146,7 +146,7 @@ void RISCVInstPrinter::printFenceArg(const MCInst *MI, unsigned OpNo, if ((FenceArg & RISCVFenceField::W) != 0) O << 'w'; if (FenceArg == 0) - O << "unknown"; + O << "0"; } void RISCVInstPrinter::printFRMArg(const MCInst *MI, unsigned OpNo, After digging into the underlying mechanism of the pause instruction, we can easily conclude that it will not trigger a SIGILL, as the fence instruction is part of the RV32I baseline instruction set, hence available in all valid RISC-V instruction sets. 1 2 3 4 5 6 7 8 9 10 $ cat pause.c #include <stdio.h> int main() { __asm__ (".insn i 0x0F, 0, x0, x0, 0x010"); // fence w,0 } $ gcc pause.c -o pause-c $ ./pause-c $ echo $? 0 In conclusion, it's 100% safe to replace pause with .insn i 0x0F, 0, x0, x0, 0x010 to make the code compile, regardless of what RISC-V extensions you are using. Tracing the Problem Ah, I know someone must be already complaining: you've write so much analysis, but how are they related to the error occurred when compiling Rust? Actually, the direct answer to this question is simple and naive. Let's take the pause.c, and use clang 13 to compile it, and see what will happen: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ clang -v clang version 13.0.1 Target: riscv64-unknown-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /usr/bin/../lib/gcc/riscv64-unknown-linux-gnu/11.2.0 Selected GCC installation: /usr/bin/../lib/gcc/riscv64-unknown-linux-gnu/11.2.0 $ clang pause.c -o pause-clang pause.c:4:12: error: unknown directive __asm__ (".insn i 0x0F, 0, x0, x0, 0x010"); // fence w,0 ^ <inline asm>:1:2: note: instantiated into assembly here .insn i 0x0F, 0, x0, x0, 0x010 ^ 1 error generated. Obviously, clang 13.0.1 still doesn't support compiling the .insn directive. The support is to be added into clang since 14.0, as we can see from the target branch of llvm commit 28387979: [RISCV] Initial support .insn directive for the assembler. This is imported into rust in this pull request (#91528). Still, there's a tiny issue haunting: The rust PR (#91528) is merged months before the initial release of llvm 14.0.0-rc1. Sure, Rust guys are always keen on trying those nightly, bleeding-edge stuffs, but how can they grab the 14.x llvm toolchain before the upstream has ever released it? After investigating PR #91528, things become clear. The Rust team is maintaining a fork of llvm at rust-lang/llvm-project, and they cherry-picked commit 28387979 to make the .insn stuff compiles when using llvm 13. By maintaining a fork and constantly modifying / cherry-picking on demand, the Rust team is able to benefit from unreleased changes, or add support for older OS/platforms that are not supported by the upstream. However, this is definitely not a good news for downstream packagers: You can't compile Rust with the original toolchain from time to time, when there're cherry-picked commits, or when the Rust team adds new features to their fork, and failed to submit them to the upstream quickly enough. If you compile rust-lang/llvm-project first, and use the compiled llvm toolchain (let's call it rust-llvm) to compile Rust itself, then the rust package would have conflicts with the llvm package, as rustc may need to link to .so files provided by llvm (or rust-llvm, depends on which one you are using to compile rustc), and the .so files may have different ABI (Application Binary Interface), causing incompatibility. Also, Arch Linux only provides shared build, so static linking is not a preferable way to solve this. Splitting the rust package to rust-llvm + rust won't help. That's because the linked .so files need to be presented at run-time, so rust-llvm will be put into rust's depends array, hence failing to resolve the conflict. Currently, we can still hide the problem by letting the build fail for some time, and wait for newer llvm releases that contain those features required by building rustc. Sure, this solution is not elegant, and things might get worse when the difference between rust-llvm and llvm becomes so huge that it's impossible to compile rustc with the upstream llvm. But we can't take the burden to make rust incompatible with llvm -- there are way too much packages that depend on both rust and llvm now. Fortunately, consider the Rust team's claim on rust-llvm, that they will always attempt to submit their new features to upstream, maybe we don't need to worry too much. 来源:https://blog.jiejiss.com/

2022/3/11
articleCard.readMore

python-mtrpacket 在 riscv64 上编译测试不通过

python-mtrpacket 是使用 Python 实现的异步网络探测工具。其当前版本(1.0.0-3)在 Arch Linux 主线的 x86_64 架构能正常编译通过,在 riscv64 下报错: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 .E/bin/sh: line 1: mtr-packet-missing: command not found .. ====================================================================== ERROR: test_commands (__main__.TestCommands) ---------------------------------------------------------------------- Traceback (most recent call last): File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/test/test.py", line 210, in test_commands asyncio_run(self.async_commands()) File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/test/test.py", line 218, in asyncio_run return loop.run_until_complete(loop.create_task(coro)) File "/usr/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete return future.result() File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/test/test.py", line 203, in async_commands async with mtrpacket.MtrPacket() as mtr: File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/mtrpacket/__init__.py", line 138, in __aenter__ await self.open() File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/mtrpacket/__init__.py", line 314, in open if not await self.check_support('send-probe'): File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/mtrpacket/__init__.py", line 368, in check_support (_, args) = await self._command('check-support', check_args) File "/build/python-mtrpacket/src/mtr-packet-python-1.0.0/mtrpacket/__init__.py", line 275, in _command return await future mtrpacket.ProcessError: failure to communicate with subprocess "./nc_mock.sh" (is it installed and in the PATH?) ---------------------------------------------------------------------- Ran 4 tests in 1.296s FAILED (errors=1) 这里面 /bin/sh: line 1: mtr-packet-missing: command not found 是正常输出,属于第三个测试点,不用管它。研究下代码就能发现这个错误是预期行为,会被 self.assertRaises 抓到。E(错误)的是第二个测试点,本不应该出现 mtrpacket.ProcessError,但是出现了。 看一下 test/nc_mock.sh,发现很简单,就这么一点东西: 1 2 3 #!/bin/sh nc localhost 8901 那这怎么就在 riscv64 上锅了呢? 首先怀疑 qemu-user 的 argv[0] 导致找不到 nc_mock.sh,但这很容易排除。只需要在 nc_mock.sh 中加入: 1 sleep 30 然后发现测试在第二个点上卡住三十秒,就知道其实 nc_mock.sh 是被运行了的。由此可以确定:原本的报错提示具有误导性。 那么,既然运行了,怎么就报错了呢?修改一下 PKGBUILD(Arch Linux 编译包时使用的脚本),把 check() 中的 ./test.sh || bash -i,这样我们就在 test.sh(它会间接调用 nc_mock.sh)非零返回的时候得到进入到打包所用的 rootfs 环境的快捷通道。 之后通过修改 test/test.py 和 mtrpacket/__init__.py,最终定位到出问题的地方: 首先是测试入口,在 test.py 里面创建了一个 mtr 的服务端,之后去启动对应的客户端。第二个测试点使用 nc_mock.sh 作为客户端: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mtr_packet_executable = os.environ.get('MTR_PACKET') # ./nc_mock.sh if not mtr_packet_executable: mtr_packet_executable = 'mtr-packet' self._subprocess_name = mtr_packet_executable self.process = await asyncio.create_subprocess_shell( mtr_packet_executable, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE) self._result_task = asyncio.ensure_future(self._dispatch_results()) self._opened = True if not await self.check_support('send-probe'): await self.close() 这里可以看到,创建了一个 ./nc_mock.sh 的子进程。这个子进程会通过 nc 连接到 localhost 的 8901 端口,在这个端口上监听的是 test.py 预先创建好的服务端,于是就可以走正常测试流程了。 之后是测试流程,可以看到立马执行的是 check_support('send-probe'),追进去: 1 2 3 4 5 6 7 8 9 10 11 12 13 async def check_support(self, feature: str) -> bool: check_args = {'feature': feature} (_, args) = await self._command('check-support', check_args) async def _command(self, command_type: str, arguments: Dict[str, str]) -> Tuple[str, Dict[str, str]]: # ... command_str = token + ' ' + command_type for argument_name in arguments: argument_value = arguments[argument_name] command_str += ' ' + argument_name + ' ' + argument_value command_str += '\n' self.process.stdin.write(command_str.encode('ascii')) 到这里,就出问题了。由于 python-mtrpacket 的 checkdepends 依赖是 gnu-netcat,这个 netcat 在 riscv64 上的表现和 x86_64 上不一致,会提早退出。如果将 LOCALE 设置为 it 或 sk,还能看到对应语言(意大利语/斯洛伐克语)的错误提示。为什么只有这两种语言呢?我也不知道,反正 Package Contents 里只有这两个: 1 2 3 4 5 6 usr/share/locale/it/ usr/share/locale/it/LC_MESSAGES/ usr/share/locale/it/LC_MESSAGES/netcat.mo usr/share/locale/sk/ usr/share/locale/sk/LC_MESSAGES/ usr/share/locale/sk/LC_MESSAGES/netcat.mo So……反正我也不关心具体报错信息了,意大利语我也看不懂。直接把 PKGBUILD 里的 checkdepends 从 gnu-netcat 换成 openbsd-netcat 完事,反正 ./nc_mock.sh 里的简单用法还不至于触及到两个 variant 存在不兼容的黑色高级部分。 来源:https://blog.jiejiss.com/

2022/1/31
articleCard.readMore

快速获得 RISC-V 开发环境

注意:这里说的是基于 QEMU 的开发环境,不是真的 riscv64 机器,买真机请找 SiFive 下单 HiFive Unmatched 板子。 首先,你要有一台 Arch Linux 的机器。 准备工作 需要在一台 Arch Linux 机器上; 假如 ssh 连接不稳定,跑 extra-* 命令时推荐使用 nohup 或类似物; 确保 Linux 内核版本 >= 5.8,否则 glibc 会出问题,还会报怨找不到 /lib/ld-linux-riscv64-lp64d.so.1。 将全部软件包的 riscv64 patch 下载到本地: 1 git clone https://github.com/felixonmars/archriscv-packages 修改 /etc/pacman.conf,在文件末尾添加两行(SiFive 板子上不需要,archlinuxcn 没有 riscv64 的源): 1 2 [archlinuxcn] Server = https://repo.archlinuxcn.org/$arch 安装依赖: 1 2 3 sudo pacman -Sy sudo pacman -S base-devel asp pacman-contrib qemu-user-static binfmt-qemu-static devtools-riscv64 # 板子上不需要最后三个 sudo pacman -S archlinux-keyring archlinuxcn-keyring # 板子上不需要最后一个 注意,这里曾经需要手动给 devtools 打 patch,现在不需要了,肥猫已经将 devtools-riscv64 放到了 archlinuxcn 源里。如果是 SiFive 板子,可能得手动从 archlinuxcn 拉取 devtools-riscv64 的源代码(PKGBUILD 等)并手动 makepkg -si 得到 .tar.zst 再用 pacman -U foobar.tar.zst 来安装。 更新 asp 记录: 1 asp update 创建 alias: 1 2 3 cd ~ mkdir pkg # 用来放缓存之类的,不用管它 alias rv64build="extra-riscv64-build -- -d ~/pkg:/var/cache/pacman/pkg" 配置 ~/.makepkg.conf: 1 echo 'MAKEFLAGS="-j15"' >> ~/.makepkg.conf # 假如你有 16 个核 开发 (假设你的工作目录为 $HOME,以下用 ~ 代替) 更新 riscv64 patch 仓库: 1 2 3 cd ~/archriscv-packages # 就是最开始 git clone 的目录 git pull cd ~ # 回到工作目录 获取 x86_64 版本的包源码: 1 asp checkout package-name 将对应的 riscv64 patch(如果存在的话)移动到 x86_64 版本的包源码所在目录: 1 cp ./archriscv-packages/package-name/*.patch ./package-name/trunk/ 切换目录,应用 riscv64.patch: 1 2 cd ./package-name/trunk/ patch -Np0 -i ./riscv64.patch # 可能有多个 patch 但需要手动应用的只有这个 修改 PKGBUILD 中的 arch: 修改前: 1 2 3 arch=(any) # 这种不用修改 arch=(x86_64) arch=("x86_64") 分别对应到修改后: 1 2 3 arch=(any) arch=(riscv64) arch=("riscv64") 总之就是把 x86_64 替换成 riscv64,别的不用动。这一步需要在应用 riscv64.patch 之后完成,顺序颠倒可能导致 patch 打不上。 编译: 1 2 3 # 需要位于 PKGBUILD 所在的目录中 # 使用刚刚在「准备工作」里创建的 alias rv64build PKGBUILD 说明 编译的过程其实就是使用 chroot 切换到一个干净的环境中,然后开始解析 PKGBUILD 文件。riscv64.patch 也是针对 PKGBUILD 文件的。 PKGBUILD 里会有一些东西: source 数组,标明代码来源,在编译时会自动下载到本地。 手动下载可以使用 updpkgsums,它在下载源码的同时会更新 PKGBUILD 中的 checksum。 prepare() 负责准备工作,通常是打 patch 和跑一些 sed 命令。 build() 负责编译工作,通常是 configure make cmake 之类的。 check() 负责测试工作,常见内容:make test 或者 npm run test 或者 ctest 之类的。 package() 负责最后的打包工作,例如指定依赖的 .so 文件,不用管。 手动操作进入 RISC-V 环境 首先,下载镜像并解压: 1 2 3 4 5 cd ~ wget -c https://archriscv.felixc.at/images/archriscv-20210601.tar.zst sha512sum archriscv-20210601.tar.zst mkdir ~/archriscv sudo bsdtar -xvf archriscv-20210601.tar.zst -C archriscv 其中,sha512sum 结果应为 1 6f012a169fe6f1ea15aeb3283091466e7992f78d823951ee2170940fa030e7fa2394aee11bf67c29943d21579ab42d2262a3d5ca973b5de8be779f338ba1dd44 archriscv-20210601.tar.zst 随后,启动这个 archriscv 容器: 1 sudo systemd-nspawn -D ~/archriscv/ --machine archriscv -a -U 启动容器后,更新软件包: 1 pacman -Syu 检查自己是否在 riscv64 环境下: 1 2 $ uname -m riscv64 安装一些软件包: 1 pacman -S vim nodejs-lts-gallium 不是所有 x86_64 的包都能装上,我们还在做艰苦的迁移工作。 比如:electron 当前还不支持 riscv64 架构,自然也就无法安装。 可能遇到的问题 GPG unknown public key 如果遇到报错如下: 1 FAILED (unknown public key FC1B547C8D8172C8) 那么说明需要导入 GPG Key。 导入命令: 1 gpg --recv-keys FC1B547C8D8172C8 # 这里的 hash 换成你在报错里看到的 key 就可以了 你也可以 cat PKGBUILD 并一次性导入其中提到的所有 key。 Failed retrieving file 'core.db' 如果你参考了 Arch Linux Wiki - Building in a clean chroot,在 riscv64 环境下(nspawn,或者你真的有 SiFive 的板子)使用如下命令: 1 2 mkarchroot $CHROOT/root base-devel arch-nspawn $CHROOT/root pacman -Syu 那么你可能会在 pacman -Syu 时遇到这个报错: 1 error: failed retrieving file 'core.db' from archriscv.felixc.at : The requested URL returned error: 404 这是因为默认的 /etc/pacman.d/mirrorlist 配置有误,可以按如下方式修改: 1 echo "Server = https://archriscv.felixc.at/repo/$arch" > /etc/pacman.d/mirrorlist 之后重新 pacman -Syu 就可以了。 引用 该镜像的 pacman 软件源由中科院软件所 PLCT 实验室 Arch Linux 小队维护。 参考链接: 使用 QEMU 和 systemd nspawn 搭建 RISC-V 轻量级用户模式开发环境 archbuild 脚本解读 archbuild 使用参考 FS#69563 - core/glibc 2.33 prevents Archlinux runing under systemd-nspawn 来源:https://blog.jiejiss.com/

2022/1/23
articleCard.readMore

在 RISC-V 上编译 Node.js 16

最近因为实习原因经常和 RISC-V 打交道,迫于需要,尝试在 riscv64gc 环境下编译运行 njs 16 并大获成功,记录一下流程步骤。 当前 njs 16(gallium LTS)主线版本是 v16.13.1,把源代码拉下来: 1 2 wget https://nodejs.org/dist/v16.13.1/node-v16.13.1.tar.xz tar -xvf node-v16.13.1.tar.xz 把同事(luyahan)的 patch 拉下来: 1 wget https://github.com/nodejs/node/pull/41566.patch 这个 patch 是上游 v8 的 4 个 commit 的 cherry-pick,修复了编译时 node_mksnapshot 卡住、测试时 inspector 相关某个测试点 CRASH signal 11(也就是 SIGSEGV / Seg Fault)的问题。 把 patch 打上去: 1 2 cd node-v16.13.1 patch -Np1 -i ../41566.patch 之后用 riscv-gnu-toolchain 正常编译就行了,不会用 make 那套的话可以照抄 Arch Linux 官方源的 PKGBUILD。如果是 gcc>=10,注意在 make 的时候指定 CFLAGS="-fno-strict-aliasing $CFLAGS" 和 CXXFLAGS="-fno-strict-aliasing $CXXFLAGS",否则会挂一个测试点(cctest/test_node_postmortem_metadata.cc)。 这个 toolchain 可以直接下载到针对 x86_64 平台 cross-compile 的版本。编译的时候会开 qemu-user 来模拟 RISC-V 环境,由于 QEMU 的 bug(和 feature),有几个测试点会挂,这是正常现象。 嫌麻烦可以开一台 Arch Linux,然后: 1 2 3 4 5 6 wget -c http://ns2.felixcat.org/images/archriscv-20210601.tar.zst sha512sum archriscv-20210601.tar.zst # 6f012a169fe6f1ea15aeb3283091466e7992f78d823951ee2170940fa030e7fa2394aee11bf67c29943d21579ab42d2262a3d5ca973b5de8be779f338ba1dd44 archriscv-20210601.tar.zst mkdir ~/archriscv tar -xvf archriscv-20210601.tar.zst -C ~/archriscv sudo systemd-nspawn -D ~/archriscv/ --machine archriscv 这样你就得到了一个经过了 chroot 的 Arch Linux RISC-V 环境,它的 repo 源指向 PLCT archrv 小队(其实主要是 Felix Yan)维护的 repo。 这样甚至不用编译 njs,我们已经编译好了,形成 .pkg.tar.zst 放到 repo 里,直接 pacman -S nodejs-lts-gallium 就可以装上。 来源:https://blog.jiejiss.com/

2022/1/21
articleCard.readMore

Redmi AirDots 3 Pro 连接电脑后蓝牙类别不正确的解决方案

控制面板>硬件和声音>设备和打印机 在「未指定」栏找到 Redmi AirDots 3 Pro,如下图所示 image.png 双击打开属性页面,切换到「服务」选项卡,如下图所示 image.png 将列出的服务全部勾选(也可以部分勾选,关键是「音频接收器」和「可远程控制的设备」这两项): image.png 点击「确定」,等待 Windows 在约 10 秒内重设设备类型并自动重新配对。 另外,需要注意输出设备要选择为「Redmi AirDots 3 Pro Hands-Free AG Audio」而非「Redmi AirDots 3 Pro Stereo」: 系统设置网易云音乐设置 来源:https://blog.jiejiss.com/

2021/8/26
articleCard.readMore

为什么 ArrowFunction 不能 new

0x00 TL;DR 因为 Lexical this 导致没有 [[Construct]],所以不能 new。 0x01 详细版本 如果去查一下 ECMA spec,其实可以看到如下的解释: A function object is an object that supports the [[Call]] internal method. A constructor is an object that supports the [[Construct]] internal method. Every object that supports [[Construct]] must support [[Call]]; that is, every constructor must be a function object. Therefore, a constructor may also be referred to as a constructor function or constructor function object. 所以想要对某个对象使用 new,就得确保该对象具有 [[Construct]] 这个内部方法。而 ArrowFunction 没有 [[Construct]]。 再多查一点的话,可以在 14.2.17 这一节看到如下的注解: An ArrowFunction does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. 再多查一点的话,可以找到最终通过的 proposal:https://tc39wiki.calculist.org/es6/arrow-functions/ The goal of Arrow Functions is to address and resolve several common pain points of traditional Function Expression: Lexical this binding; Shorter syntactical form (() => {} vs. function () {}) Lexical this matches the dominant cohort measured by Kevin Smith (see the BTF Measurements thread that either does not use this or wants it lexically bound. Best results after modifying subject code to use method definition shorthand and only then scanning for function. From lexical this it follows that arrow functions are not constructors (no .prototype or [[Construct]]). 再多查一点的话,你可以在 2011 年的一次讨论里找到这个设计的来源:https://mail.mozilla.org/pipermail/es-discuss/2012-March/021953.html We do not want to delegate to a target function that can [[Construct]], because there is no such target function -- we're not trying to sugar .bind on a full function. That is too expensive and general (no pre-args, no full function under the hood). Lexical-only this binding means no [[Construct]] and no .prototype for arrow functions. I'm ok with this. 考虑典型的代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A { constructor() { this.attr = 0; } } const a = new A(); // or function A() { this.attr = 0; } const a = new A(); 在这两种构造实例的方法中,this 指向的都是正在被操作的变量 a。 但是 arrow function 在还是个草案的时候就只计划支持 Lexical this,因此它的函数体中的 this 不会指向正在被操作的变量 a,无法修改赋值表达式的左值,因此就算 ArrowFunction 有 [[Construct]] 内部方法也无法用于构造新对象。 来源:https://blog.jiejiss.com/

2020/10/29
articleCard.readMore

Bilibili 2020-10-24 CTF WriteUp

0x00 前言 重拾半年多没更新的博客。毕竟此前写博客很大程度上是为了排解学业上每天机械性重复学习那点少到可怜的知识带来的烦闷,近来一则生活充实,于是便无甚更新博客之动力;二则生活充实,入了许多新坑,甚少有闲暇时间;三则因找到了稳定的内容输出渠道(私下的、公开的),且常能收到反馈,感觉确实已经无欲无求。 但是,但是,但是,但是这个逼站的 CTF 这两天属实是把我恶心到了,令人不吐不快,直欲将出题人的脑袋剖开了研究下其脑回路的卷绕数 [1] 究竟是多少。话不多说,直接开喷。 0x01 弱智 UA 题 页面存档:https://archive.ph/GOm5b 提示 需要使用bilibili Security Browser浏览器访问,遂直接用 Chrome 插件 ModHeader 修改请求头中的 User-Agent 为 bilibili Security Browser,拿到了 flag2。又读了下网页源码,在一个弱智的 display: none 的元素里拿到了 flag1。 后来发现忘记把 UA 改回去了 0x02 在 0x01 里不小心做完了 略(让人无语。。。) 0x03 弱智弱密码猜解题 页面存档:https://archive.ph/OdIuz 用户名 admin,密码 bilibili,没有理由。反正就一个弱密码组合扔你脸上,几次以内试出来是你欧气足,试不出来是你非酋。离谱。 0x04 弱智 Cookie 校验题 页面存档:https://archive.ph/6oayl 页面提示为 有些秘密只有超级管理员才能看见,挖了一下源码发现请求的 api 是 /api/ctf/4,得到了 "code": 403。那显然只能是 Cookie 鉴权,看眼 Cookie 发现除了通用的 session 之外还有个 role=ee11cbb19052e40b07aac0ca060c23e,也就是 role=md5("user")。解法是将 role 改成 md5("Administrator")。可见 Bilibili 程序员的英语是有多么的差,「超级」= super 都不会!super 应该是小学英语词汇吧?「超级管理员」如果翻译成 Administrator,那「管理员」应该翻译成啥?还是说贵站的程序员认为超级管理员和管理员是一个意思?那可能不仅仅是小学英语没学好,看上去小学语文也没学好。不禁为十几年前的义务教育普及程度哀叹一声。当浮一大白。 什么你问我为啥 Administrator 的 A 要大写,但是上一题的用户名 admin 就不用大写?我怎么知道,可能这两道题不是一个人出的吧。 什么你问我为啥 md5("user") 的 u 就是小写?因为,因为出题人可能正好在那个地方键盘的 Shift 坏了。别问我,问他去 0x05 弱智暴力题 页面存档:https://archive.ph/hIgMD 页面提示为 这里没有你想要的答案,挖了一下源码发现请求了 api /api/ctf/5?uid=100336889,得到 "code": 403。于是立刻开始暴力枚举 uid,开 30 线程从 uid=0 开始跑,跑到两三万的时候把服务器跑崩了。crash ++ 之后坐等服务器恢复,在此期间看了看这个 uid 的人的 bilibili 空间,发现就是个可怜的无辜路人。后来想到从这个 uid 附近开始筛查,最终在这个 uid 加上一个不大于 100 的偏移量处拿到了 flag5(这个偏移量每个人还不一样,就很弱智) 我最开始以为权限不够就得找高权限的人的 uid,但是手动试了 bishi 等人(还有陈睿)的 uid 发现同样 403 之后感觉这波是贵站程序员揭竿而起,立了一个普通路人(uid=100336889)当皇帝,拒绝承认 bishi 等的历史地位和现任 CEO 陈睿的领导地位。看不懂了,不知道陈睿得知自己被一个 uid 在一亿多的人给 ntr 了会有什么感想。 0x06 我们仍未知道题干在哪 页面存档:https://archive.ph/UgYJc 首先看看这个弱智页面的初次加载的资源: statustypesizemethodurl 200text/html1487GEThttp://45.113.201.36/blog/single.php?id=1 200text/css18754GEThttp://45.113.201.36/blog/css/bootstrap.min.css 200text/css8313GEThttp://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css 404text/css0GEThttp://45.113.201.36/blog/css/pace.css 200text/css3134GEThttp://45.113.201.36/blog/css/custom.css 404application/javascript0GEThttp://45.113.201.36/blog/js/jquery-2.1.3.min.js 200application/javascript9223GEThttp://45.113.201.36/blog/js/bootstrap.min.js 404application/javascript0GEThttp://45.113.201.36/blog/js/pace.min.js 404application/javascript0GEThttp://45.113.201.36/blog/js/modernizr.custom.js 404application/javascript0GEThttp://45.113.201.36/blog/js/script.js 200application/javascript922GEThttp://45.113.201.36/blog/js/canvas-nest.min.js 404application/javascript0GEThttp://45.113.201.36/blog/js/pace.min.js 404application/javascript0GEThttp://45.113.201.36/blog/js/modernizr.custom.js 是的,你没看错,这个丑得一比没几个字还是抄的模板(gh: TheFifthMan/simple-blog)的网页,有一堆 404。先纵观整个 ctf 活动,出现的框架和库就已经有 Vue.js、jQuery、Boostrap 和 Element UI 四种。我们再看看后端的架构: 1 2 3 4 5 Service Unavailable The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later. --------------------- Apache/2.4.7 (Ubuntu) Server at 10.34.12.219 1 2 3 4 HTTP/1.0 500 Internal Server Error Date: Sun, 25 Oct 2020 14:44:12 GMT Server: Apache/2.4.7 (Ubuntu) X-Powered-By: PHP/5.5.9-1ubuntu4.14 (笑)所以后端是跑在 Ubuntu 上的 Apache 2.4.7 后面反代的一个 PHP 5.5.9。都是古早版本,于是想看看有没有 CVE,搜了一圈发现不太行。惯例跑了一个 gh: maurosoria/dirsearch,跑出来两个 PHP 文件:/blog/test.php 和 /blog/end.php。打开 test.php 得到了一大长串 JSFuck,放进 Console 执行,得到了如下代码: 1 2 3 var str1 = "程序员最多的地方"; var str2 = "bilibili1024havefun"; console.log() 不知道 console.log() 有啥用,反正先去 github 上搜了 bilibili1024havefun,果然搜到了一个 repo:gh: interesting-1024/end。白盒审计呗。放出来的 PHP 源码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $str = intval($_GET['id']); $reg = preg_match('/\d/is', $_GET['id']); if(!is_numeric($_GET['id']) and $reg !== 1 and $str === 1){ $content = file_get_contents($_GET['url']); //文件路径猜解 if (false){ echo "还差一点点啦~"; }else{ echo $flag; } }else{ echo "你想要的不在这儿~"; } 不难看出一个可行的攻击方法是 end.php?id[]=1。至于文件路径猜解,经过一些尝试之后发现只要是包含 flag.txt(但又不只有 flag.txt)的 url 都是能拿到一张图片的,把图片扔进 StegSolve 或者 StegOnline,或者直接 tail 一下都能拿到位于 jpg 文件末尾的 flag10(你没看错,甚至没有用到 LSB 隐写,而且拿到的是 flag10)。 世界第八大未解之谜:flag6 在哪? 我们尝试了 SQL 注入 single.php?id=1 中的 id 参数(毕竟不加这个参数就显示不出页面),但似乎后端只是判断了 isset($_GET["id"]),而没有真正用到这个 id 的值。从 -4000 到 120000 的暴力枚举结果也佐证了这个猜测。sqlmap 跑完之后,众人发现似乎有个 Referer 头的基于时间的盲注,但是此时因为同时有几十个人在跑 sqlmap,另外几十个人在跑 dirsearch,贵站这台配置比学生机高不了多少的服务器很快就卡到让人无从判断 timeout 是因为注入的 SLEEP 起效了还是因为服务器没空闲资源来处理这个请求了。于是就卡住了。 我们还尝试了 nmap 扫服务器的开放端口,扫出来了这样的结果: 1 2 3 4 5 6 7 8 25/tcp open smtp 80/tcp open http 110/tcp open pop3 443/tcp closed https 1194/tcp closed openvpn 6379/tcp closed redis 8069/tcp closed unknown 8091/tcp closed jamlink 接下来就是喜闻乐见的 redis 未授权登录,直接用 redis-cli 连到 :6379 上,搞事情: 1 2 3 > keys * 1) flag8 > get flag8 就得到了 flag8。之后当然要想办法删库跑路了,结果 set flag8 "" 虽然返回了 OK 但是 flag8 的值丝毫没变。百思不得其解,直到我发现这个 redis 是假的: 1 2 3 4 5 6 7 8 > set flag8 "" OK > wdnmd OK > ??? OK > shit cnm OK 所以这本质上就是个带有 tab 自动补全的、只支持少量特定 redis 命令的 OK 复读机。离谱。用 ncat 连到 6379 端口,甚至可以得到一个 output=input[1:] 的奇怪东西。比如你输入 ping,就会收到 ing。 于是乎,少年在打败恶龙解救公主的路途中,经商入仕不小心成了百万富翁和达官贵人,可是连半片龙鳞都还没看到。此情此景让我们不得不发问:nmd,你们究竟把恶龙藏到哪里去了? 0x0? 疑点 在首页和题目列表页使用了相同的背景图片名(83b92f73637ab8056346bb6b8a3af6d9840e8bb0.(jpg|png)),但只有一张图的 sha1 哈希值能对上。 两张图片对比发现 22 娘和 33 娘的位置发生了位移,背景图中一些像素改变,且 33 娘从一只左手一只右手变成了有两只右手?? 既然从模板阉割成 single.php 都已经删了那么多东西了,为什么不把引入了 404 掉的资源的那些代码给删了?留着又没有用。 end.php 的 url 参数包含 flag.txt 就能得到 flag10(如 url=cnmflag.txt),但是 url=flag.txt 却不会有 flag。且构造了特殊的输入 url=https://jiejiss.com/test 表明,end.php 真的通过 file_get_contents 去读了文件。猜测是如果文件不存在且路径包含 flag.txt 才会返回 flag10,那么这就意味着当前目录下真的有一个 flag.txt,只不过根据 end.php,它回显不出来。大胆猜测榜首 90 分的队伍差的就是这 10 分。。。可惜没法构造任意文件上传的洞,否则可以传一个 phar 包上去,直接 getshell。 中提到的两张图,仔细看会发现背景的颜色分成不同颜色的条带,且并不均匀。看了 LSB 发现隐隐能看到字母和数字,但实在是太模糊太抽象了,只能看到 _2c 几个字符。 0x0! 游戏时间 接下来是找不到恶龙而无所事事的挑战者们玩的一些游戏。 发现 end.php 真的会去读 url= 后面的文件路径,于是用 url=/dev/urandom 把 PHP 搞崩了,PHP 带走了 Apache 大家惊讶地发现这个服务器竟然还运行了 nginx v1.10,实际的处理途径是 nginx - Apache - PHP 发现 single.php 里面似乎有一些和邮件相关的代码,于是对着服务器上开放的 25 和 110 端口(分别对应 SMTP 和 POP3)使劲搞事情。不过无论怎么搞都收不到邮件。 大家用十几门语言写了 flag 生成器,每秒提交 5 个 flag。有人在多台服务器上使用多线程提交,根据他的计算,只需要 27 年就能知道 flag6 了。 既然 redis-fake 里面的 flag8 删不掉,那我们总可以往里塞假 flag 吧?于是来得晚的人发现 redis-fake 里面有 flag1 到 flag10,看上去跟真的一样。 私信轰炸贵站的客服,问客服「结束亦是开始」(第六题提示词)是什么意思。客服:您是在用户转正答题中遇到了困难吗?答曰:非也,我问你的这句话来自贵站「1024 大型脑筋急转弯」活动。客服:啥? 后来有大佬找到了额外的一两个 flag,在 QQ 群内恼怒发言:「这 flag 真的藏在 sb 地方!」我们于是开始思考什么是 sb 地方。想了想,以这弱智猜谜的套路,恐怕这个 sb 的地方我们根本就想不到。 很多人在后期魔怔了,看到啥都说「是线索」。后来 Gmail 换 logo 了他们都觉得这是线索,肯定是贵站和 Google 联动了。获得称号:魔怔人。 现在就等一个官方 WriteUp。毕竟不知道 6 7 9 的标答思路,没法喷。 UPDATE 0x06 正常注入题 弱智服务器 TL;DR: 一个基于 HTTP Referer 头的简单注入题,有一些非常基础的过滤 这个题本身就是普通 ctf 题,没什么好说的。用 sqlmap 都能跑出来在 HTTP Referer 这里有注入。但让选手非常非常困惑的是,在盲注过程中由于服务器本身性能太差,无从判断 timeout 的成因。(详见分割线前的 0x06 大点) Anyway,在从 MySQL 中拿到 flag6 之后还可以顺便构造一个文件路径爆破的攻击方法,通过二分实现目录猜解和文件内容读取。经过读取的 end.php 实际内容如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <?php $str = intval($_GET['id']); $reg = preg_match('/\d/is', $_GET['id']); if(!is_numeric($_GET['id']) and $reg !== 1 and $str === 1){ $content = file_get_contents($_GET['url']); //echo $content; $filename = "./imgs/bilibili_224a634752448def6c0ec064e49fe797_havefun".".jpg"; if (strpos($_GET['url'],"flag.txt") == false){ echo "还差一点点啦~"; }else{ //file_put_contents($filename,$content); echo "<img src=".$filename.">"; } }else{ echo "你想要的不在这儿~"; } ?> 0x07 弱智中的弱智的路径猜解题 首先在第三题里,背景图的路径从 banner_bg.gif 切换成了 api/images?file=banner.png。在前文的「疑点」中我已经提到过这个问题,因为通常来讲 ctf 中不会有无用功。虽然 b 站这次的 ctf 有一堆弱智一样的操作,凭空创造出 n 多个疑点让人非常无力吐槽,但是这个 api 开头的路径确实还是非常可疑,首先要考虑的就是任意文件读。可是 api/images?file=./banner.png 却返回了 404,dirsearch 的扫描也认为该入口是硬编码了白名单。 后来大家在 QQ 群里讨论了一下,有师傅一语道破天机:这个地方确实是硬编码了白名单,flag7 就藏在 ?file=../../../flag7.txt 里!于是一口老血郁结于心,闷闷不乐,直欲取贵站出题人之项上人头,蒸煮烹炸而啖之。但后来又仔细思虑,放弃了这个想法:一则实在无法进入 b 站工作区,二则也担心自身智力遭到同化,陷入只会出弱智猜谜题的可怜境地! 所以最终的解法是访问 /api/images?file=../../../flag7.txt。 0x08 弱智 redis 题 未授权访问,在前面讲过了。 0x09 弱智 aes 题 首先你要猜到 /api/images?file=../../../secret.txt 这个路径。大多数人肯定想不到这一个 images?file 的 api,他们用了两次!(其实后面又用了一次)所以很容易错过。打开文件之后发现是一串 base64: 1 SkRGWDZRZnJxelJQU21YME42MU04OWlwV1l0SlYvcEJRVEJPWWFYUXVHOGZBcnJ1bjNXS3hXRlpHd05uMjFjRw== 两次解码之后发现出现大量特殊字符,长度为 48 bytes,猜测是经过了 AES 加密。但迫于找不到 key,最后这 10 分也没拿到。复盘发现原来又要用到 /api/images?file= 这个 api,这次需要传入 file=md5sum(secret.txt).jpg,也就是 file=ae10c97f6de1129abb00b5c961394336.jpg。然后在文件中寻找字符串,可得: this is the key of secret.txt:aes_key 遂以 "\x00".repeat(16) 为初始化向量,以 "aes_key" + "\x00".repeat(9) 为 key,以 CBC Mode 解码之,得到 flag9。 本题全场无一人做出,复盘思路来自第一天就到了 90 分的大佬和官方有关弱智人士交流时得到的提示。 0x0a 弱智文件路径猜解题 用 dirsearch 跑出来一个 test.php 和一个 end.php,test.php 里的东西在 GitHub 上搜能搜到 end.php 的修改过的源码,然后随便构造下参数就过了。 总结 wcnm 来源:https://blog.jiejiss.com/

2020/10/25
articleCard.readMore

对于又一种对SS流式加密的攻击(奇虎团队)的详细分析

0x00 TL;DR 论文:这里 本来 SS/SSR 的非 AEAD 加密就已经死的差不多了,不缺这么一种攻击方式。不过攻击的思路确实很新颖。 单就这种攻击方法而言,不安全的 SS 加密方法:主要是 aes-*-cfb,aes-*-ofb, aes-*-ctr;安全的 SS 加密方法主要是 chacha20,aes-*-gcm,*-poly1305。 如何补救:将加密方法换成 aes-*-gcm,或是转战其它工具。 0x01 攻击方法详解 写在开始之前: 我写这篇博文只是想仔细剖析一种新的重放攻击的具体过程,其中内容仅作学术讨论用。 对于 SS 和 SSR 的开发、维护团队的爱恨情仇,我无意参与;大家如果心中已有定论,也不要询问我相关的内容及我本人的意见,且本篇博文中也不会涉及相关内容。 攻击方法其实真的很简单。 才怪嘞 写了一半,回来补充:尊敬的论文作者,您能不能把细节写清楚啊……给您跪了啊…… 密文修改目标 流式加密的数据是被分装在不同的数据包里的,将这些数据包拼接在一块就能得到所有的密文。 密文格式如下: 1 [IV][encrypted payload] 其中的 IV 在解密 encrypted payload 时会用到,不需要操心它具体是干什么用的。 代理内容以明文 HTTP 请求为例,根据 SS 所使用的协议,将要向服务器发送的原始数据(加密前)的格式如下: 1 [target data][payload] payload 部分储存着所有的 HTTP 请求的数据,target data 中储存着关于最终目的服务器(想要访问的网站所在的服务器)的重要信息。target data 的结构如下: 1 [type][destination address][port] type 可能为 0x01、0x02 或 0x04(参见 RFC 1928)type == 0x01 表示随后的 destination address 部分是个 4 字节的 IPv4 地址,也就是目标服务器的 IP 地址。port 是 2 字节的端口号。 需要知道的知识:由于 AES 流式加密的特性,明文的每个 block 在加密后出现在密文的相同位置,类似于“先入先出”概念。因此,target data 对应的密文一定会出现在第一个数据包(“初始化数据包”)中。 每个 block 的长度为 16 字节,一般与 IV(初始化向量)的长度相同,详见这个链接。 如果,我们能够修改密文,使得解密后的明文的 destination address 部分储存的 IPv4 地址指向我们自己控制的服务器的 IPv4 地址,不就能够使得服务器端的 SS 将解密后的 HTTP 请求发给我的服务器了吗? 备注:图 2 中对于 GFW 节点的工作方式有一定的简化,与实际情况不完全一致,仅作示意用途。 从上图中可以大概了解到这种攻击的流程。攻击者在收到境外服务器发送给境内用户的可疑数据包时,将数据包复制进内存,并在一定的延时(几十毫秒到几十秒)后,将修改过密文的数据包反向发送给 ss-server,用一定的手段使 ss-server 把这个数据包解密后的明文发送给受攻击方控制的服务器。当然,由于不知道秘钥,攻击者不可能通过加密明文的方式得到他想要的密文。但是,由于 aes-*-cfb 和 aes-*-ofb 的特性,可以通过异或密文的最开始的一个 block 来对应地修改明文的第一个 block: aes-cfb-decryption 以 aes-*-cfb 为例,上图的解密流程可以转化为: 密文、明文下标均从 1 开始,解密函数与加密函数都是 \(\mathrm{Encrypt}\) 函数。 \(K\) 是解密所需的秘钥(\(Key\))。 \(P_i = \mathrm{Encrypt}(C_{i-1}, K)\,\,\,\mathtt{xor}\,\,C_i\),其中 \(C_0=\mathrm{IV}\)。 我们不妨假设所有 ss-server 发送给 ss-local 的数据包都是 Encrypted HTTP Response(未经 TLS 加密),也即解密后的明文遵循如下格式: 1 2 3 4 5 6 HTTP 1.1 404 Not Found # HTTP 版本 状态码 状态文字 Date: Thu, 13 Feb 2020 06:51:25 GMT # HTTP Header,一行一个 Server: nginx ... Response Body 然而这些数据在网络上是加密传输的,我们如何在不知道 \(K\) 的情况下获得明文呢? 答案是:ss-server 有 \(K\),让 ss-server 帮我们解密,把明文发给我们的服务器就可以了~ 换言之,我们要想办法修改 Encrypted HTTP Response,使得修改后的密文解密所得的明文遵循如下格式: 为什么是修改 Encrypted Response 而不是修改 Encrypted Request 我会在后文的适当位置解释。 1 2 3 4 5 6 [type (1 byte)][fake address (4 bytes)][port (2 bytes)]1 404 Not Found Date: Thu, 13 Feb 2020 06:51:25 GMT # 剩余的明文不做改动,一律视为 [payload] Server: nginx ... Response Body 为什么是替换而不是在最前面添加内容我会在后文的适当位置解释。 Ta-da!ss-server 收到这个数据包,解密之后发现前七个字节符合 [type][address][port] 规则,认为这个数据包是 ss-local 发送给它的请求数据包,于是把解密后的明文发送到了 fake_address:port。就这样,我们在 fake_address:port 监听的程序收到了如下解密后的明文: 1 2 3 4 5 6 1 404 Not⛬졷.ꖵ� # 版本(残缺) 状态码 状态文字(乱码) İ차﯐Thu, 13 Feb 2020 06:51:25 GMT # HTTP Header(最开始的部分是乱码),一行一个 Server: nginx ... Response Body 为什么解密修改后的密文得到的明文会有乱码我会在后文的适当位置解释。 顺便说一句,这三个“为什么”,原论文里一个字都没提……我佛了……鲨了我吧…… 具体密文修改方法 不难发现的是,aes-*-cfb 的加密方法会导致密文与明文逐字节一一对应,修改密文的第 \(k\) 个字节就会导致明文的第 \(k\) 个字节相应地被修改。 由于我们想要修改的是解密出的明文的第一个 block,也就是 \(P_1\),因此我们只需要考虑 \(i=1\) 的情况: \(P_1 = \mathrm{Encrypt}(C_0, K)\,\,\,\mathtt{xor}\,\,C_1\),等价于: \(P_1 = \mathrm{Encrypt}(\mathrm{IV}, K)\,\,\,\mathtt{xor}\,\,C_1\)。 其中,\(\mathrm{IV}\) 可以从数据包的最开头(\(C_1\) 之前的部分)直接得到,\(K\) 是未知的且无法得到,但是 \(K\) 是个固定的值,因此 \(\mathrm{Encrypt}(\mathrm{IV},K)\) 也是个固定的值,设这个值为 \(E_0\)。因此,我们可以将式子改写为: \(P_1=E_0\,\,\mathtt{xor}\,\,C_1\)。 接下来的操作就很有意思了。根据异或运算的运算规律,我们可以得知:如果 \(x\,\,\mathtt{xor}\,\,y=z\),那么一定有 \(x\,\,\mathtt{xor}\,\,(y\,\,\mathtt{xor}\,\,m)=z\,\,\mathtt{xor}\,\,m\),记为性质①。因此,有: \(P_1\,\,\mathtt{xor}\,\,m = \mathrm{Encrypt}(\mathrm{IV}, K)\,\,\,\mathtt{xor}\,\,(C_1\,\,\mathtt{xor}\,\,m)\),其中的 \(m\) 就是我们接下来要想办法构造的东西。构造目标是让 \(P_1\,\,\mathtt{xor}\,\,m\) 的值等于我们控制的服务器的 IPv4 地址。同样是根据异或运算的性质,我们可以计算出 \(m\): \(m = P_1\,\,\mathtt{xor}\,\,\mathtt{IPv4}\)。 有鉴于 \(P_1\) 的格式如下所示: 1 [type][fake address][port][other data] 备注:前文已经计算过,[type][fake address][port] 总长度为 7 字节,一定会出现在第一个 block。 因此,我们其实只需要关心 \(P_1\) 的前 7 个字节。初始的 \(P_1\) 的前七个字节——根据 HTTP 协议——一定是 HTTP 1. 这七个字符,所以: 1 2 3 m = "HTTP 1.\x00\x00\x00\x00\x00\x00\x00\x00\x00" ^ [0x01][addr][port][9 bytes data] # 一个 block 长度为 16 bytes,每次必须根据 block 长度按位异或。 # 因此需要用 9 个 \x00 补齐。 HTTP Request 的前 7 个字符是不确定的,无法计算出 \(m\)。这就是为什么一定要操作 Encrypted HTTP Response。 计算出 \(m\) 之后,就可以计算出修改后的密文 \(C_1'\):\(C_1'=C_1\,\,\mathtt{xor}\,\,m\)。 没想明白?再讲一遍: \(C_1\) 解密后为 \(P_1\),由于 aes-*-cfb 的性质,\(C_1\,\,\mathtt{xor}\,\,m\) 解密后也为 \(P_1\,\,\mathtt{xor}\,\,m\)。(上文提到的性质①) 经过精心构造,\(P_1\,\,\mathtt{xor}\,\,m\) 的值是 [0x01][fake addr][port][other data],正好是我们想要的 \(P_1'\)。 因此 \(P_1'\) 对应的密文 \(C_1'\) 就是 \(P_1\,\,\mathtt{xor}\,\,m\) 对应的密文 \(C_1\,\,\mathtt{xor}\,\,m\)。 最后,将 \(C_1'\) 拼接回密文中: \(C=\mathrm{IV}+C_1'+C_2+C_3+\cdots\) 得到的最终的密文直接发送给 ss-server,ss-server 就会解密出如下内容了: 1 2 3 4 5 6 [0x01][fake address (4 bytes)][port (2 bytes)]1 404 Not⛬졷.ꖵ� İ차﯐Thu, 13 Feb 2020 06:51:25 GMT # [payload] Server: nginx ... Response Body 接下来,ss-server 会根据 SOCKS5 协议,把从第八个字节(1)开始的 [payload] 原封不动地发给 fake_address:port,无秘钥获取明文至此成功。 Q & A Q:为什么是修改 Encrypted HTTP Response 而不是修改 Encrypted HTTP Request? A:不是不想,是不能。Encrypted HTTP Response 的明文前七个字节固定且已知,可以依据此计算 \(m\);Encrypted HTTP Request 的明文前七个字节可能性太多,GET url...、POST url... 等,穷举成本太大。 Q:在修改密文以修改对应的明文时,为什么是替换而不是在最前面添加内容? A:不是不想,是不能。AES 加密以块为单位,在最前面添加字符会导致整体的错位,解密出来完全变成乱码。 Q:解密修改后的密文得到的明文为什么会有乱码? A:其实也不一定,如果是用 aes-*-ofb 或 aes-*-ctr 加密的,就不会有乱码。再看一遍 aes-*-cfb 解密的模式图,可以发现,\(C_{i-1}\) 参与了 \(C_i\) 的解密。我们修改了 \(C_1\),因此在解密 \(C_2\) 时,会解密出乱码;但是我们没有修改 \(C_2\) 以及更往后的其它 block,因此在解密 \(C_3\) 以及更往后的其它 block 时,不会出现乱码。(补充阅读:错误扩散) 而 aes-*-ofb 在解密 \(C_i\) 时,不需要 \(C_{i-1}\) 或 \(P_{i-1}\) 参与(请看这个链接),因此就不会出现乱码。 Q:如果加密前本身就不是明文(比如HTTPS),会受影响吗? A:攻击者仍能知道你在使用 SS,但是你的数据安全和隐私不会受到威胁。攻击者仅能实现最外层的解密。 0x02 结语 还是要感慨一句,SS、SSR 占领全世界的时代已经过去了。不论是 SS 的 aes-*-*fb 还是已经弃用的 OTA,抑或者是停止维护的 SSR,显然已经不适合作为安全的选项。即使是理论上安全的 aes-*-gcm 类的 AEAD 算法,仍旧躲不过非常时期的大范围阻断(除非配合 *-obfs)。以后的世界必将是伪装类软件的天下,无论是伪装成 HTTP/HTTPS(TLS),还是伪装成 WebSocket,都已经有了较为成熟的软件和较为活跃的社区。此次新提出的攻击方法,不过是旧时代的又一声残响,仅此而已。 来源:https://blog.jiejiss.com/

2020/2/12
articleCard.readMore

在MacOS上配置dnscrypt-proxy(免分流)

导语 中国大陆的互联网服务提供商经常劫持部分域名,转到自己指定的 IP,以强行插入自己的广告。dnscrypt-proxy 支持 DNSCrypt 协议和 DoH 方案等,可以避免受到 DNS 污染。 然而,很多人在按照官方教程安装完 dnscrypt-proxy 后,发现国内网站访问缓慢,甚至加载失败。这是由于 dnscrypt-proxy 在解析域名时,很可能会返回域名的国外主机的 IP。这就导致数据包传输距离大大变长,甚至可能需要经过海底光缆,耗时及久、速度下降明显。本教程采用的配置在很大程度上解决了这个问题,并提出了更多的可行思路。 第零步 前置要求 你的电脑上应该有正常工作的 SOCKS5 Proxy。后文中,我们均假定它工作在本机的 8886 端口。 如果还有 HTTP/HTTPS Proxy,当然更好。后文中,我们均假定它工作在本机的 8887 端口。 如果只有 HTTP/HTTPS Proxy,需要另外再准备一个 SOCKS5 Proxy。 你的电脑上的本机 53 端口没有被占用。 你的电脑上安装了 curl 或 wget。本教程采用 curl。 你的电脑上可以使用 nslookup 命令。 你应当拥有一个管理员(root)权限的账户,并知晓它的密码。 你应当可以流畅访问 GitHub。 第一步 安装 dnscrypt-proxy 1.1 获得 root 权限 打开 Terminal.app(或者任何你熟悉的 shell),在其中输入并执行: 1 sudo -s 你可能需要输入你的电脑密码。输入密码时,屏幕上不会有显示,这是为了防止你的密码被身后的陌生人偷看。 执行完成后,在命令行中输入并执行: 1 whoami 如果回显为 root,说明你成功获得了 root 权限。 1.2 下载文件 点击这个链接:gh: DNSCrypt/dnscrypt-proxy/releases,下载最新版本的 dnscrypt-proxy 的二进制发布版本。文件大小大约在 1-10 MB,文件名格式如下: 1 dnscrypt-proxy-macos-版本.tar.gz 下载完成后,解压缩至任何便于记忆的目录。后文中均假定解压到 ~/dnscrypt-proxy 目录下。 解压后的文件应当包含 dnscrypt-proxy 和 example-dnscrypt-proxy.toml 文件。 第二步 配置 dnscrypt-proxy 2.1 初始化 首先,在命令行中切换到 dnscrypt-proxy 所在目录: 1 cd ~/dnscrypt-proxy 随后,执行如下命令: 1 2 mv ./example-dnscrypt-proxy.toml ./dnscrypt-proxy.toml ./dnscrypt-proxy 因为是初次运行,dnscrypt-proxy 会自动初始化,需要较长时间。如果等待一段时间后命令行中不再显示新的文字,并且也没有出现 error 字样,表明初始化成功。 如果初始化失败,并且出现了 permission denied 字样,可以尝试执行:sudo ./dnscrypt-proxy 如果初始化失败,并且出现了 address already in use 字样,请检查本机的 53 端口是否被占用:sudo lsof -i:53,并退出此命令显示的全部进程(不论 NODE 是 TCP 还是 UDP,除非你确定完全知道你在做什么)。 如果初始化失败,并且出现了 Syntax error 字样,说明你下载了错误的文件,需要重新下载。 2.2 测试 dnscrypt-proxy 是否正常工作 新开启一个命令行窗口。 首先,在此窗口中执行: 1 2 cd ~/dnscrypt-proxy ./dnscrypt-proxy -resolve www.baidu.com 你应当看到 IP addresses: xxx.xxx.xxx.xxx 的字样。如果出现 IP addresses: -,说明此前的配置有问题,或你的电脑的网络连接已中断。 随后,在命令行中执行: 1 nslookup www.baidu.com 127.0.0.1 你应当看到十行左右的文字出现。 接着,打开系统设置,在右上角搜索框中输入 DNS,点击 DNS 服务器(DNS servers),并在左侧的白框左下角找到 + 号。点击 + 号,在新出现的输入框中输入 127.0.0.1 并回车;用鼠标将此条记录拖拽到所有其他记录的上方,以确保它是第一条记录。点击右下角的 确定(OK)按钮,待此窗口关闭后,点击其下方的设置窗口右下角的 应用(Apply)按钮。 打开你常用的浏览器,开启无痕模式(或无扩展模式),尝试访问 ip.sb。你可能需要事先关闭所有全局代理。此时,网页应当正常加载。 最后,回到最开始打开的命令行窗口,按下 Control-C 组合键以停止 ./dnscrypt-proxy。随后执行: 1 2 ./dnscrypt-proxy -service install ./dnscrypt-proxy -service start 这两条命令需要 root 权限。此后,均可以通过 sudo ./dnscrypt-proxy -service start 或 stop 来开始或停止 dnscrypt-proxy 服务。 2.3 使 dnscrypt-proxy 正确解析国内域名 也许此时你已经发现,dnscrypt-proxy 往往解析出海外 IP,如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ nslookup music.163.com 127.0.0.1 Server:127.0.0.1 Address:127.0.0.1#53 Non-authoritative answer: music.163.comcanonical name = music.ntes53.netease.com. music.ntes53.netease.comcanonical name = overseasv4.music.ntes53.netease.com. Name:overseasv4.music.ntes53.netease.com Address: 103.126.92.133 Name:overseasv4.music.ntes53.netease.com Address: 103.126.92.132 $ whois 103.126.92.0/24 % Information related to '103.126.92.0/22AS137263' route: 103.126.92.0/22 origin: AS137263 descr: HONGKONG NETEASE INTERACTIVE ENTERTAINMENT LIMITED Unit 802, 8th Floor, Chuang's Tower, 30-32 Connaught Road Central mnt-by: MAINT-NETEASEIE-HK last-modified: 2018-12-03T07:34:50Z source: APNIC 在这个例子中,网易云音乐的域名被解析到香港,显然不是最优解。 最简单的解决办法是,使用代码编辑软件编辑 ~/dnscrypt-proxy/dnscrypt-proxy.toml 文件: 在第 96 行附近,找到: 1 # proxy = 'socks5://127.0.0.1:9050' 并修改为(以 8886 端口为例): 1 proxy = 'socks5://127.0.0.1:8886' (可选的)在第 103 行附近,找到: 1 # http_proxy = 'http://127.0.0.1:8888' 并修改为(以 8887 端口为例): 1 http_proxy = 'http://127.0.0.1:8887' 最重要的一步: 访问 dnscrypt.info/map,找到地理位置在中国大陆的圆点,单击圆点以查看其名称。在创作此教程时(2020年02月04日),中国大陆上共有三个圆点(三台可用的服务器),分别是:geekdns-south,geekdns-doh-east 和 geekdns-hk。 在 ~/dnscrypt-proxy/dnscrypt-proxy.toml 文件的第 30 行附近,找到: 1 # server_names = ['scaleway-fr', 'google', 'yandex', 'cloudflare'] 并修改为(以 geekdns-south,geekdns-doh-east 和 geekdns-hk 为例): 1 server_names = ['geekdns-south', 'geekdns-doh-east', 'geekdns-hk'] 请注意,单引号为英文半角单引号。 保存文件并关闭代码编辑器。 在命令行中执行如下命令以重启 dnscrypt-proxy 服务: 1 2 ./dnscrypt-proxy -service stop ./dnscrypt-proxy -service start 再次解析域名,就可以发现,此时的解析结果已经正常,如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ nslookup music.163.com 127.0.0.1 Server:127.0.0.1 Address:127.0.0.1#53 Non-authoritative answer: music.163.comcanonical name = music.ntes53.netease.com. music.ntes53.netease.comcanonical name = bgpv6.music.ntes53.netease.com. Name:bgpv6.music.ntes53.netease.com Address: 59.111.181.35 Name:bgpv6.music.ntes53.netease.com Address: 59.111.181.60 Name:bgpv6.music.ntes53.netease.com Address: 59.111.181.38 $ whois 59.111.181.0/24 person: Zongyi Xu address: NetEase Building No.16 Ke Yun Road, address: Tianhe Avenue,Guangzhou,Guangdong,China. country: CN phone: +86020-85106115 e-mail: jiangxu@corp.netease.com nic-hdl: ZX3316-AP mnt-by: MAINT-CNNIC-AP last-modified: 2016-03-07T06:05:41Z source: APNIC 此结果与阿里巴巴公共 DNS 服务(223.5.5.5)解析结果相同。 2.4 其它思路 2.4.1 贵富网list + 黑白名单 参见:酥酥乳.tools 第 511 篇博文 在转换出 dnsmasq_贵富网list.conf 之后,使用正则替换:将 /^server=\/([\S\s]+)\/127.0.0.1#5353 替换为 $1 127.0.0.1:53,并将结果粘贴到 dnscrypt-proxy 配置文件的 301 行左右的 Example map entries 下方,配合自建的 DNSCrypt 协议或 DoH 的本机 DNS 服务器食用。 2.4.2 在路由器上完成 dnsmasq + lede 之类的,有成熟的教程了,不再赘述。 2.4.3 使用分流脚本 传统模式,需要经常维护,但是效果不错。 gh: CNMan/dnscrypt-proxy-config 来源:https://blog.jiejiss.com/

2020/2/4
articleCard.readMore

市县中心点坐标(经纬度)

参考: ECharts.js 范例 https://www.echartsjs.com/examples/en/editor.html?c=effectScatter-bmap 百度地图 城市中心点坐标 - 胡dot https://www.cnblogs.com/hhhz/p/7591852.html 拾取坐标系统 - 百度地图 https://api.map.baidu.com/lbsapi/getpoint/index.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 { "北京": [ 116.395, 39.929 ], "平谷": [ 117.1, 40.13 ], "密云": [ 116.85, 40.37 ], "顺义": [ 116.65, 40.13 ], "通县": [ 116.67, 39.92 ], "怀柔": [ 116.62, 40.32 ], "大兴": [ 116.33, 39.73 ], "房山": [ 115.98, 39.72 ], "延庆": [ 115.97, 40.47 ], "昌平": [ 116.2, 40.22 ], "上海": [ 121.487, 31.249 ], "嘉定": [ 121.24, 31.4 ], "宝山": [ 121.48, 31.41 ], "川沙": [ 121.7, 31.19 ], "南汇": [ 121.76, 31.05 ], "奉贤": [ 121.46, 30.92 ], "松江": [ 121.24, 31 ], "金山": [ 121.16, 30.89 ], "青浦": [ 121.1, 31.15 ], "崇明": [ 121.4, 31.73 ], "天津": [ 117.21, 39.143 ], "宁河": [ 117.83, 39.33 ], "静海": [ 116.92, 38.93 ], "蓟县": [ 117.4, 40.05 ], "宝坻": [ 117.3, 39.75 ], "武清": [ 117.05, 39.4 ], "重庆": [ 106.556, 29.568 ], "綦江": [ 106.56, 29.41 ], "长寿": [ 106.64, 29.01 ], "南桐": [ 107.04, 29.86 ], "合川": [ 106.28, 29.26 ], "潼南": [ 106.22, 30.03 ], "铜梁": [ 105.8, 30.16 ], "壁山": [ 106.03, 29.86 ], "荣昌": [ 106.21, 29.62 ], "大足": [ 105.59, 29.4 ], "永川": [ 105.71, 29.75 ], "万盛": [ 105.91, 29.38 ], "石家庄": [ 114.522, 38.048 ], "唐山": [ 118.183, 39.65 ], "行唐": [ 114.54, 38.42 ], "灵寿": [ 114.38, 38.31 ], "束鹿": [ 115.18, 37.94 ], "晋县": [ 115.03, 38.03 ], "藁城": [ 114.84, 38.03 ], "高邑": [ 114.58, 37.62 ], "赵县": [ 114.78, 37.76 ], "井陉": [ 114.13, 38.03 ], "获鹿": [ 114.03, 38.08 ], "新乐": [ 114.67, 38.33 ], "正定": [ 114.56, 38.13 ], "深泽": [ 115.2, 38.2 ], "无极": [ 114.96, 38.16 ], "赞皇": [ 114.35, 37.65 ], "元氏": [ 114.5, 37.74 ], "栾城": [ 114.64, 38.87 ], "平山": [ 114.24, 38.2 ], "邯郸": [ 114.482, 36.609 ], "永年": [ 114.5, 36.77 ], "曲周": [ 114.92, 36.78 ], "馆陶": [ 115.4, 36.47 ], "魏县": [ 114.94, 36.37 ], "成安": [ 114.68, 36.43 ], "大名": [ 115.14, 36.28 ], "涉县": [ 113.67, 36.57 ], "鸡泽": [ 113.85, 36.95 ], "丘县": [ 115.18, 36.84 ], "广平": [ 114.94, 36.49 ], "肥乡": [ 114.8, 36.56 ], "临漳": [ 114.62, 36.35 ], "磁县": [ 114.37, 36.37 ], "武安": [ 114.2, 36.7 ], "邢台": [ 114.52, 37.069 ], "柏乡": [ 114.68, 37.49 ], "宁普": [ 114.9, 37.62 ], "隆尧": [ 114.75, 37.35 ], "临西": [ 115.5, 36.87 ], "南官": [ 115.37, 37.37 ], "巨鹿": [ 115.03, 37.22 ], "任县": [ 114.68, 37.11 ], "沙河": [ 114.52, 36.94 ], "临城": [ 114.5, 37.43 ], "内丘": [ 114.5, 37.28 ], "新河": [ 115.22, 37.53 ], "清河": [ 115.67, 37.07 ], "威县": [ 115.08, 36.97 ], "广宗": [ 115.14, 37.06 ], "平乡": [ 115.02, 37.06 ], "南和": [ 114.71, 37 ], "保定": [ 115.494, 38.886 ], "涞水": [ 115.71, 39.39 ], "涿县": [ 115.98, 39.48 ], "定兴": [ 115.78, 39.28 ], "容城": [ 115.86, 39.06 ], "安新": [ 115.92, 38.92 ], "蠡县": [ 115.58, 38.49 ], "博野": [ 115.46, 38.46 ], "定县": [ 114.02, 38.52 ], "阜平": [ 114.18, 38.85 ], "唐县": [ 114.97, 38.75 ], "涞源": [ 114.67, 39.37 ], "易县": [ 115.49, 39.35 ], "新城": [ 115.84, 39.34 ], "雄县": [ 116.1, 38.98 ], "徐水": [ 115.65, 39.02 ], "高阳": [ 115.78, 38.68 ], "安国": [ 115.3, 38.41 ], "清苑": [ 115.47, 38.76 ], "望都": [ 115.14, 38.71 ], "曲阳": [ 114.68, 38.62 ], "完县": [ 115.12, 38.84 ], "满城": [ 115.45, 38.95 ], "张家口": [ 114.893, 40.811 ], "康保": [ 114.6, 41.87 ], "赤城": [ 115.82, 40.92 ], "怀来": [ 115.54, 40.4 ], "蔚县": [ 114.53, 39.83 ], "宣化": [ 115.03, 40.63 ], "张北": [ 114.7, 41.15 ], "沽源": [ 115.68, 41.68 ], "崇礼": [ 115.25, 40.98 ], "涿鹿": [ 115.2, 40.37 ], "阳原": [ 114.15, 40.12 ], "怀安": [ 114.38, 40.67 ], "尚义": [ 113.95, 41.05 ], "万全": [ 114.73, 40.84 ], "承德": [ 117.933, 40.992 ], "围场": [ 117.72, 41.95 ], "平泉": [ 118.68, 41.02 ], "宽城": [ 118.47, 40.62 ], "兴隆": [ 117.48, 40.42 ], "滦平": [ 117.53, 40.95 ], "隆化": [ 117.7, 41.32 ], "青龙": [ 118.93, 40.43 ], "丰宁": [ 116.63, 41.2 ], "秦皇岛": [ 119.604, 39.945 ], "迁西": [ 118.3, 40.15 ], "迁安": [ 118.69, 40.02 ], "昌黎": [ 119.15, 39.72 ], "卢龙": [ 118.85, 39.89 ], "滦南": [ 118.67, 39.49 ], "玉田": [ 117.9, 39.9 ], "唐海": [ 118.54, 39.31 ], "遵化": [ 117.97, 40.2 ], "抚宁": [ 119.22, 39.88 ], "乐亭": [ 118.9, 39.43 ], "滦县": [ 118.73, 39.74 ], "丰南": [ 118.1, 39.58 ], "丰润": [ 118.13, 39.82 ], "廊坊": [ 116.703, 39.518 ], "安次": [ 116.69, 39.52 ], "三河": [ 117.06, 39.97 ], "香河": [ 117, 39.76 ], "霸县": [ 116.38, 39.12 ], "固安": [ 116.29, 39.44 ], "大城": [ 116.63, 38.7 ], "文安": [ 116.45, 38.87 ], "永清": [ 116.48, 39.32 ], "大厂": [ 116.98, 39.98 ], "沧州": [ 116.863, 38.297 ], "黄骅": [ 117.33, 38.4 ], "盐山": [ 117.22, 38.07 ], "吴桥": [ 116.37, 37.65 ], "东光": [ 116.52, 37.89 ], "肃宁": [ 115.82, 38.43 ], "河间": [ 116.07, 38.45 ], "泊头": [ 116.56, 38.08 ], "交河": [ 116.27, 38.02 ], "青县": [ 116.8, 38.58 ], "海兴": [ 117.85, 38.17 ], "南皮": [ 116.7, 38.05 ], "任丘": [ 116.08, 38.72 ], "献县": [ 116.12, 38.2 ], "孟村": [ 117.1, 38.06 ], "衡水": [ 115.686, 37.746 ], "饶阳": [ 115.74, 38.24 ], "阜城": [ 116.14, 37.87 ], "景县": [ 116.26, 37.69 ], "枣强": [ 115.72, 37.52 ], "深县": [ 115.56, 38.02 ], "安平": [ 115.5, 38.22 ], "武强": [ 115.96, 38.03 ], "武邑": [ 115.9, 37.81 ], "故城": [ 115.96, 37.36 ], "冀县": [ 115.56, 37.59 ], "太原": [ 112.55, 37.89 ], "阳曲": [ 112.65, 38.05 ], "娄烦": [ 111.78, 38.05 ], "清徐": [ 112.33, 37.62 ], "大同": [ 113.29, 40.113 ], "阳泉": [ 113.569, 37.869 ], "长治": [ 113.12, 36.201 ], "天镇": [ 114.08, 40.42 ], "灵丘": [ 114.2, 39.47 ], "怀仁": [ 113.1, 39.82 ], "山阴": [ 112.82, 39.52 ], "平鲁": [ 112.12, 39.53 ], "右玉": [ 112.33, 40.18 ], "阳高": [ 113.72, 40.38 ], "广灵": [ 113.27, 39.75 ], "浑源": [ 113.68, 39.7 ], "应县": [ 113.18, 39.58 ], "朔县": [ 112.42, 39.32 ], "左云": [ 112.67, 40.02 ], "忻县": [ 112.7, 38.38 ], "代县": [ 112.97, 39.07 ], "五台": [ 113.32, 38.72 ], "静乐": [ 111.9, 38.37 ], "保德": [ 111.09, 38.01 ], "河曲": [ 111.17, 39.38 ], "神池": [ 112.17, 39.1 ], "原平": [ 112.7, 38.73 ], "繁峙": [ 113.28, 39.2 ], "定襄": [ 112.95, 38.5 ], "岢岚": [ 111.58, 38.7 ], "五寨": [ 111.82, 38.93 ], "偏关": [ 111.47, 39.45 ], "宁武": [ 112.28, 39 ], "榆次": [ 112.72, 37.68 ], "孟县": [ 112.77, 34.92 ], "昔阳": [ 113.68, 37.62 ], "左权": [ 113.35, 37.07 ], "太谷": [ 112.53, 37.42 ], "平遥": [ 112.18, 37.2 ], "灵石": [ 111.77, 36.83 ], "寿阳": [ 113.17, 37.88 ], "平定": [ 113.62, 37.79 ], "和顺": [ 113.55, 37.33 ], "榆社": [ 112.97, 37.08 ], "祁县": [ 112.33, 37.36 ], "介休": [ 111.88, 37.03 ], "离石": [ 111.13, 37.53 ], "兴县": [ 111.22, 38.47 ], "方由": [ 111.24, 37.86 ], "岚县": [ 111.62, 38.28 ], "交城": [ 112.14, 37.55 ], "文水": [ 112.02, 37.42 ], "汾阳": [ 111.75, 37.27 ], "孝义": [ 111.8, 37.12 ], "交口": [ 111.2, 36.97 ], "石楼": [ 110.83, 37 ], "中阳": [ 111.17, 37.37 ], "临县": [ 110.95, 37.95 ], "柳林": [ 110.85, 37.45 ], "襄垣": [ 113.02, 36.55 ], "黎城": [ 113.4, 36.56 ], "壶关": [ 113.23, 35.11 ], "高平": [ 112.88, 35.48 ], "阳城": [ 112.38, 35.84 ], "长子": [ 112.87, 36.13 ], "沁源": [ 112.32, 36.5 ], "潞城": [ 113.22, 36.33 ], "武乡": [ 112.83, 36.83 ], "平顺": [ 113.43, 36.19 ], "陵川": [ 113.27, 35.78 ], "晋城": [ 112.867, 35.499 ], "沁水": [ 112.15, 35.67 ], "屯留": [ 112.87, 36.32 ], "沁县": [ 112.68, 36.75 ], "临汾": [ 111.538, 36.099 ], "汾西": [ 111.53, 36.63 ], "安泽": [ 112.2, 36.15 ], "古县": [ 111.9, 36.29 ], "翼城": [ 111.68, 35.73 ], "曲沃": [ 111.33, 35.63 ], "吉县": [ 110.65, 36.12 ], "大宁": [ 110.72, 36.47 ], "侯马": [ 111.45, 35.03 ], "永和": [ 110.64, 36.62 ], "洪洞": [ 111.68, 36.25 ], "霍县": [ 111.72, 36.57 ], "浮山": [ 111.83, 35.97 ], "襄汾": [ 111.43, 35.86 ], "乡宁": [ 110.8, 35.97 ], "蒲县": [ 111.07, 36.42 ], "运城": [ 111.006, 35.038 ], "闻喜": [ 111.2, 35.37 ], "垣曲": [ 111.63, 35.3 ], "芮城": [ 110.68, 34.71 ], "临猗": [ 110.78, 35.15 ], "新绛": [ 111.22, 35.62 ], "河津": [ 110.7, 35.58 ], "夏县": [ 111.22, 35.12 ], "绛县": [ 111.58, 35.48 ], "平陆": [ 111.2, 34.12 ], "永济": [ 110.42, 34.88 ], "万荣": [ 110.83, 110.83 ], "稷山": [ 110.97, 35.6 ], "沈阳": [ 123.432, 41.808 ], "新民": [ 122.83, 42 ], "辽中": [ 122.7, 41.52 ], "大连": [ 121.593, 38.948 ], "金县": [ 121.7, 39.13 ], "复县": [ 121.97, 39.63 ], "新金": [ 121.95, 39.55 ], "庄河": [ 22.97, 39.7 ], "长海": [ 122.58, 39.28 ], "鞍山": [ 123.007, 41.118 ], "海城": [ 122.75, 40.85 ], "台安": [ 122.4, 41.4 ], "抚顺": [ 123.929, 41.877 ], "新宾": [ 125.02, 41.72 ], "清原": [ 124.9, 42.13 ], "本溪": [ 123.778, 41.325 ], "垣仁": [ 125.33, 41.28 ], "锦州": [ 121.147, 41.13 ], "锦县": [ 121.35, 41.17 ], "义县": [ 121.22, 41.55 ], "黑山": [ 122.12, 41.7 ], "北镇": [ 121.8, 41.6 ], "锦西": [ 120.83, 40.77 ], "兴城": [ 120.68, 40.63 ], "绥中": [ 120.32, 40.35 ], "丹东": [ 124.338, 40.129 ], "东沟": [ 124.13, 39.97 ], "岫岩": [ 123.25, 40.3 ], "凤城": [ 124.05, 40.47 ], "宽甸": [ 124.77, 40.75 ], "阜新": [ 121.66, 42.019 ], "彰武": [ 122.52, 42.42 ], "营口": [ 122.233, 40.668 ], "盖县": [ 122.37, 40.42 ], "盘山": [ 122.03, 41.02 ], "大洼": [ 122.06, 41 ], "辽阳": [ 123.172, 41.273 ], "灯塔": [ 123.34, 41.43 ], "铁岭": [ 123.854, 42.299 ], "开原": [ 124.03, 42.53 ], "昌图": [ 124.13, 42.8 ], "铁法": [ 123.5, 42.48 ], "康平": [ 123.33, 42.75 ], "法库": [ 123.37, 42.52 ], "西丰": [ 124.7, 42.77 ], "朝阳": [ 120.446, 41.571 ], "建昌": [ 119.78, 40.82 ], "北票": [ 120.75, 41.82 ], "凌源": [ 119.37, 41.27 ], "建平": [ 119.63, 41.38 ], "长春": [ 125.313, 43.898 ], "吉林": [ 126.564, 43.871 ], "农安": [ 125.15, 44.45 ], "德惠": [ 125.68, 44.52 ], "榆树": [ 126.55, 44.83 ], "九台": [ 126.83, 44.15 ], "双阳": [ 125.68, 43.53 ], "永吉": [ 126.57, 43.87 ], "舒兰": [ 126.97, 44.4 ], "蛟河": [ 127.33, 43.75 ], "桦甸": [ 126.72, 42.97 ], "磐石": [ 126.03, 42.93 ], "延吉": [ 129.52, 42.93 ], "汪清": [ 129.75, 43.32 ], "珲春": [ 130.35, 42.85 ], "图们": [ 129.83, 42.98 ], "和龙": [ 129, 42.52 ], "安图": [ 128.3, 42.58 ], "敦化": [ 128.18, 43.35 ], "通化": [ 125.942, 41.736 ], "柳河": [ 125.7, 40.88 ], "海龙": [ 125.65, 42.53 ], "辉南": [ 126.03, 42.68 ], "靖宇": [ 126.8, 42.38 ], "浑江": [ 126.4, 41.97 ], "抚松": [ 127.27, 42.33 ], "集安": [ 126.17, 41.15 ], "长白": [ 128.17, 41.43 ], "四平": [ 124.391, 43.175 ], "梨树": [ 124.33, 43.32 ], "怀德": [ 124.82, 43.5 ], "伊通": [ 125.32, 43.33 ], "辽源": [ 125.133, 42.923 ], "东丰": [ 125.5, 42.68 ], "双辽": [ 123.5, 43.52 ], "白城": [ 122.84, 45.621 ], "大安": [ 124.18, 45.5 ], "扶余": [ 124.82, 45.2 ], "乾安": [ 124.02, 45 ], "长岭": [ 123.97, 44.3 ], "通榆": [ 123.13, 44.82 ], "洮安": [ 122.75, 45.35 ], "哈尔滨": [ 126.657, 45.773 ], "齐齐哈尔": [ 123.987, 47.347 ], "鹤岗": [ 130.292, 47.338 ], "双鸭山": [ 131.171, 46.655 ], "鸡四": [ 130.97, 45.3 ], "大庆": [ 125.021, 46.596 ], "伊春": [ 128.91, 47.734 ], "嘉荫": [ 130, 48.93 ], "铁力": [ 128.08, 47.98 ], "绥化": [ 126.989, 46.646 ], "绥棱": [ 127.12, 47.22 ], "海伦": [ 126.97, 47.47 ], "庆安": [ 127.5, 46.87 ], "兰西": [ 126.3, 46.28 ], "肇东": [ 125.98, 46.07 ], "肇州": [ 125.25, 45.72 ], "肇源": [ 125.07, 45.53 ], "安达": [ 125.33, 46.42 ], "明水": [ 125.88, 47.18 ], "青岗": [ 126.13, 46.68 ], "望奎": [ 126.5, 46.83 ], "黑河": [ 127.5, 50.25 ], "爱辉": [ 127.53, 50.22 ], "德都": [ 126.17, 48.5 ], "通北": [ 126.8, 49.76 ], "北安": [ 126.5, 48.22 ], "孙吴": [ 127.5, 49.22 ], "逊克": [ 128.42, 49.57 ], "嫩江": [ 125.2, 49.17 ], "佳木斯": [ 130.284, 46.813 ], "桦川": [ 130.68, 47.02 ], "萝北": [ 130.83, 47.58 ], "绥滨": [ 131.83, 47.3 ], "富锦": [ 132.02, 47.23 ], "同江": [ 132.5, 47.67 ], "抚远": [ 134.15, 48.33 ], "饶河": [ 134, 46.78 ], "七台河": [ 131.019, 45.775 ], "宝清": [ 132.17, 46.33 ], "集贤": [ 131.13, 46.7 ], "勃利": [ 130.53, 45.75 ], "桦南": [ 130.53, 46.25 ], "依兰": [ 129.55, 46.33 ], "汤源": [ 129.92, 46.73 ], "牡丹江": [ 129.608, 44.588 ], "林口": [ 130.23, 45.3 ], "鸡东": [ 131.04, 45.27 ], "密山": [ 131.85, 45.53 ], "虎林": [ 133.97, 45.75 ], "绥芬河": [ 131.17, 44.38 ], "东宁": [ 131.12, 44.07 ], "穆棱": [ 130.5, 44.9 ], "宁安": [ 129.47, 44.35 ], "海林": [ 129.35, 44.57 ], "阿城": [ 126.95, 45.52 ], "呼兰": [ 126.58, 46 ], "巴彦": [ 127.38, 46.08 ], "宾县": [ 127.48, 45.75 ], "木兰": [ 128.03, 45.95 ], "通河": [ 128.7, 45.98 ], "方正": [ 128.8, 45.83 ], "延寿": [ 128.35, 45.47 ], "尚志": [ 127.95, 45.22 ], "五常": [ 127.17, 44.93 ], "双城": [ 126.32, 45.53 ], "富裕": [ 124.4, 47.8 ], "讷河": [ 124.85, 48.48 ], "克山": [ 125.87, 48.03 ], "克东": [ 126.22, 48.03 ], "拜泉": [ 126.07, 47.62 ], "依安": [ 125.3, 47.92 ], "林甸": [ 124.87, 47.18 ], "泰来": [ 123.45, 46.4 ], "龙江": [ 123.18, 47.35 ], "甘南": [ 123.48, 47.9 ], "杜尔伯特": [ 124.44, 46.86 ], "加格达奇": [ 124.07, 50.42 ], "呼玛": [ 126.6, 51.72 ], "塔河": [ 124.7, 52.32 ], "漠河": [ 122.37, 53.48 ], "杭州": [ 120.219, 30.259 ], "余杭": [ 120.3, 30.43 ], "富阳": [ 119.95, 30.07 ], "建德": [ 119.27, 29.49 ], "临安": [ 119.72, 30.23 ], "萧山": [ 120.25, 30.16 ], "桐庐": [ 119.64, 29.8 ], "淳安": [ 119.05, 29.61 ], "宁波": [ 121.579, 29.885 ], "镇海": [ 121.72, 29.96 ], "温州": [ 120.69, 28.002 ], "瓯海": [ 120.65, 28.01 ], "永喜": [ 120.68, 28.16 ], "洞头": [ 121.12, 27.84 ], "平阳": [ 120.55, 27.68 ], "泰顺": [ 119.7, 27.57 ], "乐清": [ 120.94, 28.14 ], "瑞安": [ 120.62, 27.8 ], "文成": [ 120.08, 27.08 ], "苍南": [ 120.36, 27.53 ], "湖州": [ 120.137, 30.877 ], "平湖": [ 121.02, 30.7 ], "桐乡": [ 120.54, 30.64 ], "安吉": [ 119.68, 30.68 ], "嘉善": [ 120.92, 30.84 ], "嘉兴": [ 120.76, 30.773 ], "海盐": [ 120.92, 30.53 ], "海宁": [ 120.69, 30.53 ], "德清": [ 120.08, 30.54 ], "长兴": [ 119.91, 30.01 ], "定海": [ 122.11, 30.03 ], "岱山": [ 122.2, 30.26 ], "嵊四": [ 122.45, 30.72 ], "普陀": [ 122.3, 29.97 ], "鄞县": [ 121.56, 29.86 ], "象山": [ 121.8, 29.48 ], "奉化": [ 121.41, 29.66 ], "慈溪": [ 121.23, 30.18 ], "宁海": [ 121.42, 29.3 ], "余姚": [ 121.16, 30.04 ], "绍兴": [ 120.592, 30.002 ], "新昌": [ 120.89, 29.49 ], "诸暨": [ 120.23, 29.71 ], "上虞": [ 120.87, 30.03 ], "嵊县": [ 120.81, 29.6 ], "椒江": [ 121.44, 28.67 ], "临海": [ 121.13, 28.8 ], "三门": [ 121.38, 29.11 ], "温岭": [ 121.36, 28.36 ], "仙居": [ 120.73, 28.85 ], "天台": [ 121.03, 29.15 ], "黄岩": [ 121.27, 28.64 ], "玉环": [ 121.23, 28.14 ], "丽水": [ 119.929, 28.456 ], "青田": [ 120.28, 28.45 ], "庆无": [ 119.06, 27.61 ], "遂昌": [ 119.25, 28.59 ], "缙云": [ 120.6, 28.66 ], "云和": [ 119.56, 28.12 ], "龙泉": [ 119.13, 28.08 ], "松阳": [ 119.48, 28.46 ], "金华": [ 119.652, 29.102 ], "浦江": [ 119.88, 29.46 ], "东阳": [ 120.23, 29.27 ], "武义": [ 119.81, 28.9 ], "江山": [ 118.61, 28.74 ], "开化": [ 118.39, 29.15 ], "衢州": [ 118.875, 28.956 ], "兰溪": [ 119.48, 29.19 ], "义乌": [ 120.06, 29.32 ], "永康": [ 120.02, 28.92 ], "常山": [ 118.5, 28.9 ], "福州": [ 119.33, 26.047 ], "闽侯": [ 119.14, 26.16 ], "厦门": [ 118.103, 24.489 ], "同安": [ 118.15, 24.74 ], "南平": [ 118.181, 26.643 ], "建瓯": [ 118.32, 27.05 ], "浦城": [ 118.55, 27.92 ], "邵武": [ 117.48, 27.34 ], "顺昌": [ 117.8, 26.8 ], "崇安": [ 118.02, 27.76 ], "光泽": [ 117.34, 27.54 ], "松溪": [ 118.77, 27.53 ], "政和": [ 118.85, 27.38 ], "宁德": [ 119.542, 26.656 ], "福安": [ 119.65, 27.09 ], "连江": [ 119.53, 26.2 ], "福鼎": [ 120.2, 27.34 ], "霞浦": [ 120, 26.89 ], "吉田": [ 118.74, 26.59 ], "罗源": [ 119.55, 26.49 ], "寿宁": [ 119.5, 27.47 ], "周宁": [ 119.36, 27.12 ], "屏南": [ 118.98, 26.92 ], "柘荣": [ 119.89, 27.25 ], "莆田": [ 119.077, 25.448 ], "仙游": [ 118.7, 25.37 ], "福清": [ 119.39, 25.73 ], "长乐": [ 119.52, 25.96 ], "永泰": [ 118.95, 25.88 ], "平潭": [ 119.78, 25.51 ], "闽清": [ 118.86, 26.21 ], "泉州": [ 118.6, 24.901 ], "晋江": [ 118.57, 24.82 ], "南安": [ 118.39, 24.96 ], "惠安": [ 118.78, 25.04 ], "安溪": [ 118.18, 25.07 ], "永春": [ 118.3, 25.34 ], "德化": [ 118.24, 25.5 ], "金门": [ 118.34, 24.43 ], "漳州": [ 117.676, 24.517 ], "龙海": [ 117.79, 24.44 ], "漳浦": [ 117.61, 24.12 ], "诏安": [ 117.16, 23.73 ], "平和": [ 117.3, 24.38 ], "云霄": [ 117.34, 23.99 ], "南靖": [ 117.35, 24.51 ], "长泰": [ 117.75, 24.62 ], "东山": [ 117.4, 23.72 ], "华安": [ 117.53, 25 ], "龙岩": [ 117.017, 25.078 ], "上杭": [ 116.41, 25.43 ], "永定": [ 116.81, 24.76 ], "长汀": [ 116.37, 25.85 ], "武平": [ 116.1, 25.11 ], "连城": [ 116.75, 25.72 ], "漳平": [ 117.4, 25.3 ], "三明": [ 117.642, 26.27 ], "龙溪": [ 118.17, 26.18 ], "宁化": [ 116.64, 26.26 ], "大田": [ 117.83, 25.69 ], "永安": [ 117.37, 25.97 ], "沙县": [ 117.77, 26.41 ], "将乐": [ 117.45, 26.73 ], "清流": [ 116.81, 26.12 ], "建宁": [ 116.82, 26.85 ], "泰宁": [ 117.15, 26.92 ], "明溪": [ 117.18, 26.36 ], "济南": [ 117.024, 36.682 ], "历城": [ 117.07, 36.69 ], "长清": [ 116.73, 36.55 ], "章丘": [ 117.53, 36.72 ], "青岛": [ 120.384, 36.105 ], "崂山": [ 120.42, 36.15 ], "胶南": [ 119.97, 35.88 ], "即墨": [ 120.45, 36.38 ], "胶县": [ 120, 36.28 ], "淄博": [ 118.059, 36.804 ], "枣庄": [ 117.279, 34.807 ], "滕县": [ 117.17, 35.09 ], "东营": [ 118.583, 37.487 ], "垦利": [ 118.54, 37.59 ], "利津": [ 118.25, 37.49 ], "德州": [ 116.328, 37.46 ], "宁津": [ 116.8, 37.64 ], "乐陵": [ 117.22, 37.74 ], "商河": [ 117.15, 37.31 ], "济阳": [ 117.2, 36.97 ], "禹城": [ 116.66, 36.95 ], "夏津": [ 116, 36.95 ], "陵县": [ 116.58, 37.34 ], "庆云": [ 117.37, 37.37 ], "临邑": [ 116.86, 37.2 ], "齐河": [ 116.76, 36.79 ], "平原": [ 116.44, 37.16 ], "武城": [ 116.08, 37.2 ], "滨州": [ 117.968, 37.405 ], "滨县": [ 117.97, 37.47 ], "广饶": [ 118.41, 37.04 ], "桓台": [ 118.12, 36.95 ], "邹平": [ 117.75, 36.89 ], "阳信": [ 117.58, 37.65 ], "沾化": [ 118.14, 37.7 ], "博兴": [ 118.12, 37.12 ], "高青": [ 117.66, 37.18 ], "惠民": [ 117.51, 17.49 ], "无棣": [ 117.58, 37.73 ], "潍坊": [ 119.142, 36.716 ], "潍县": [ 119.22, 36.77 ], "平度": [ 119.97, 36.77 ], "诸城": [ 119.42, 35.99 ], "安丘": [ 119.2, 36.42 ], "临朐": [ 118.53, 36.5 ], "寿光": [ 118.73, 36.86 ], "昌邑": [ 119.41, 36.86 ], "高密": [ 119.75, 36.38 ], "五莲": [ 119.2, 35.74 ], "昌乐": [ 118.83, 36.69 ], "高都": [ 118.47, 36.69 ], "烟台": [ 121.309, 37.536 ], "牟平": [ 121.59, 37.38 ], "文登": [ 122.05, 37.2 ], "海阳": [ 121.17, 36.76 ], "莱阳": [ 120.71, 36.97 ], "栖霞": [ 120.83, 37.28 ], "掖县": [ 119.93, 37.18 ], "长岛": [ 120.73, 37.91 ], "威海": [ 122.093, 37.528 ], "福山": [ 121.27, 37.49 ], "荣成": [ 122.41, 37.16 ], "乳山": [ 121.52, 36.89 ], "莱西": [ 120.53, 36.86 ], "招远": [ 120.38, 37.35 ], "黄县": [ 120.51, 37.64 ], "蓬莱": [ 120.75, 37.8 ], "临沂": [ 118.34, 35.072 ], "沂水": [ 118.64, 35.78 ], "日照": [ 119.507, 35.42 ], "临沭": [ 118.73, 34.89 ], "仓山": [ 118.03, 34.84 ], "平邑": [ 117.63, 35.49 ], "沂源": [ 118.17, 36.18 ], "沂南": [ 118.47, 35.54 ], "营县": [ 118.83, 35.57 ], "莒南": [ 118.83, 35.17 ], "郯城": [ 118.35, 34.61 ], "费县": [ 117.97, 35.26 ], "蒙阴": [ 117.95, 35.7 ], "泰安": [ 117.089, 36.188 ], "莱芜": [ 117.684, 36.233 ], "肥城": [ 116.76, 36.24 ], "平阴": [ 116.46, 36.29 ], "新汶": [ 117.67, 35.86 ], "新泰": [ 117.76, 35.91 ], "宁阳": [ 116.8, 35.76 ], "东平": [ 116.3, 35.91 ], "济宁": [ 116.6, 35.402 ], "兖州": [ 116.83, 35.54 ], "泗水": [ 117.27, 35.65 ], "鱼台": [ 116.65, 35 ], "嘉祥": [ 116.34, 35.41 ], "汶上": [ 116.49, 35.71 ], "曲阜": [ 116.98, 35.59 ], "邹县": [ 116.97, 35.39 ], "微山": [ 117.12, 34.8 ], "金乡": [ 116.32, 35.07 ], "荷泽": [ 115.43, 35.24 ], "郓城": [ 115.94, 35.59 ], "巨野": [ 116.08, 35.38 ], "单县": [ 116.07, 34.82 ], "曹县": [ 115.53, 34.83 ], "鄄城": [ 115.5, 35.57 ], "梁山": [ 116.1, 35.8 ], "成武": [ 115.88, 34.97 ], "定陶": [ 115.57, 35.07 ], "东明": [ 115.08, 35.31 ], "聊城": [ 115.986, 36.455 ], "高唐": [ 116.23, 36.86 ], "东阿": [ 116.23, 36.32 ], "莘县": [ 115.67, 36.24 ], "临清": [ 115.72, 36.68 ], "茌平": [ 116.27, 36.58 ], "阳谷": [ 115.78, 36.11 ], "冠县": [ 115.45, 35.47 ], "郑州": [ 113.649, 34.756 ], "荥阳": [ 113.35, 34.79 ], "开封": [ 114.351, 34.801 ], "平顶山": [ 113.3, 33.745 ], "洛阳": [ 112.447, 34.657 ], "焦作": [ 113.211, 35.234 ], "鹤壁": [ 114.297, 35.755 ], "杞县": [ 114.77, 34.56 ], "尉氏": [ 114.17, 34.41 ], "新郑": [ 113.71, 34.4 ], "登封": [ 113.02, 34.46 ], "通许": [ 114.46, 34.48 ], "中牟": [ 114, 34.73 ], "密县": [ 113.35, 34.51 ], "巩县": [ 112.96, 34.76 ], "兰考": [ 114.81, 34.69 ], "新乡": [ 113.912, 35.307 ], "汲县": [ 114.05, 35.44 ], "封丘": [ 114.04, 35.03 ], "获嘉": [ 113.63, 35.27 ], "温贺": [ 113.06, 34.94 ], "济源": [ 112.57, 35.08 ], "博爱": [ 113.05, 35.16 ], "辉县": [ 113.77, 35.46 ], "延津": [ 114.19, 35.14 ], "原阳": [ 113.96, 35.05 ], "武陟": [ 113.38, 35.1 ], "沁阳": [ 112.92, 35.08 ], "修武": [ 113.42, 35.24 ], "安阳": [ 114.351, 36.11 ], "南乐": [ 115.21, 36.08 ], "范县": [ 115.46, 35.9 ], "台前": [ 115.83, 36 ], "滑县": [ 114.52, 35.57 ], "浚县": [ 114.54, 35.67 ], "淇县": [ 114.17, 35.6 ], "内黄": [ 114.88, 35.95 ], "清丰": [ 115.1, 35.89 ], "濮阳": [ 115.026, 35.753 ], "长垣": [ 114.67, 35.19 ], "汤阴": [ 114.35, 35.92 ], "林县": [ 113.81, 36.06 ], "商丘": [ 115.641, 34.438 ], "夏邑": [ 116.13, 34.22 ], "柘城": [ 115.29, 34.08 ], "睢县": [ 115.04, 34.46 ], "虞城": [ 115.87, 34.4 ], "永城": [ 116.37, 33.94 ], "宁陵": [ 115.31, 34.44 ], "民权": [ 115.13, 34.65 ], "周口": [ 114.654, 33.623 ], "商水": [ 114.59, 33.54 ], "扶沟": [ 114.38, 34.05 ], "鹿邑": [ 115.48, 33.86 ], "淮阳": [ 114.88, 33.74 ], "沈丘": [ 115.06, 33.41 ], "西华": [ 114.5, 33.79 ], "太康": [ 114.85, 34.06 ], "郸城": [ 115.17, 33.63 ], "项城": [ 114.9, 33.44 ], "许昌": [ 113.835, 34.026 ], "鄢县": [ 114.17, 34.11 ], "郾城": [ 113.98, 33.6 ], "襄城": [ 113.46, 33.86 ], "鲁山": [ 112.88, 33.74 ], "郏县": [ 113.19, 33.98 ], "漯河": [ 114.046, 33.576 ], "长葛": [ 113.77, 34.22 ], "临颖": [ 113.94, 33.81 ], "舞阳": [ 113.58, 33.44 ], "叶县": [ 113.35, 33.62 ], "宝丰": [ 113.04, 33.86 ], "禹县": [ 113.47, 34.16 ], "驻马店": [ 114.049, 32.983 ], "确山": [ 114.02, 32.83 ], "西平": [ 114, 33.38 ], "汝南": [ 114.35, 33 ], "新蔡": [ 114.97, 32.75 ], "泌阳": [ 113.31, 32.72 ], "遂平": [ 113.98, 33.15 ], "上蔡": [ 114.26, 33.25 ], "平舆": [ 114.62, 32.97 ], "正阳": [ 114.38, 32.62 ], "信阳": [ 114.085, 32.128 ], "息县": [ 114.72, 32.35 ], "固始": [ 115.68, 32.17 ], "潢川": [ 115.04, 32.13 ], "新县": [ 114.83, 31.62 ], "罗山": [ 114.53, 32.21 ], "淮滨": [ 115.41, 32.44 ], "商城": [ 115.42, 31.81 ], "光山": [ 114.91, 32.02 ], "南阳": [ 112.542, 33.011 ], "方城": [ 112.98, 33.25 ], "唐河": [ 112.83, 32.7 ], "新野": [ 112.36, 32.51 ], "邓县": [ 112.08, 32.68 ], "淅川": [ 111.47, 33.14 ], "南召": [ 112.4, 33.49 ], "社旗": [ 112.92, 33.05 ], "桐柏": [ 113.4, 32.37 ], "镇平": [ 112.23, 33.03 ], "内乡": [ 111.83, 33.05 ], "西峡": [ 111.5, 33.31 ], "三门峡": [ 111.181, 34.783 ], "孟津": [ 112.42, 34.84 ], "临汝": [ 112.83, 34.17 ], "汝阳": [ 112.46, 34.16 ], "嵩县": [ 112.07, 34.14 ], "栾川": [ 111.6, 33.81 ], "灵宝": [ 110.85, 34.52 ], "渑池": [ 111.75, 34.76 ], "义马": [ 111.92, 34.73 ], "偃师": [ 112.77, 34.73 ], "伊川": [ 112.42, 34.43 ], "宜阳": [ 112.15, 34.51 ], "洛宁": [ 111.65, 34.39 ], "卢氏": [ 111.03, 34.06 ], "陕县": [ 111.19, 34.76 ], "新安": [ 112.14, 34.75 ], "武汉": [ 114.316, 30.581 ], "武昌": [ 114.33, 30.35 ], "汉阳": [ 114.02, 30.57 ], "黄石": [ 115.05, 30.216 ], "十堰": [ 110.801, 32.636 ], "沙市": [ 112.24, 30.32 ], "宜昌": [ 111.31, 30.732 ], "襄樊": [ 112.14, 30.02 ], "孝感": [ 113.935, 30.927 ], "黄陂": [ 114.36, 30.88 ], "汉川": [ 113.59, 30.63 ], "云梦": [ 113.73, 31.02 ], "应山": [ 113.81, 31.62 ], "大悟": [ 114.09, 31.56 ], "应城": [ 113.6, 30.94 ], "安陆": [ 113.69, 31.25 ], "鄂城": [ 114.87, 30.38 ], "黄冈": [ 114.906, 30.446 ], "新洲": [ 114.8, 31.84 ], "红安": [ 114.61, 31.29 ], "麻城": [ 115, 31.17 ], "罗川": [ 115.37, 30.79 ], "浠水": [ 115.22, 30.46 ], "蕲春": [ 115.3, 30.24 ], "黄梅": [ 115.93, 30.09 ], "广济": [ 115.56, 29.85 ], "英山": [ 115.57, 30.75 ], "咸宁": [ 114.3, 29.88 ], "阳新": [ 115.22, 29.83 ], "通山": [ 114.52, 29.6 ], "通城": [ 113.8, 29.23 ], "嘉鱼": [ 113.91, 29.97 ], "崇阳": [ 114.04, 29.54 ], "蒲圻": [ 113.85, 29.71 ], "荆门": [ 112.217, 31.042 ], "江陵": [ 112.18, 30.35 ], "钟祥": [ 112.58, 31.17 ], "京山": [ 113.11, 31.03 ], "监利": [ 112.9, 29.83 ], "石首": [ 112.41, 29.73 ], "长沙": [ 112.979, 28.213 ], "望城": [ 112.8, 28.37 ], "株洲": [ 113.131, 27.827 ], "湘潭": [ 112.935, 27.835 ], "衡阳": [ 112.583, 26.898 ], "邵阳": [ 111.461, 27.236 ], "岳阳": [ 113.146, 29.378 ], "临湘": [ 113.42, 29.48 ], "平江": [ 113.56, 29.71 ], "泪罗": [ 113.05, 28.8 ], "湘阴": [ 112.87, 28.68 ], "华容": [ 112.55, 29.52 ], "浏阳": [ 113.63, 28.16 ], "醴陵": [ 113.5, 27.67 ], "攸县": [ 113.32, 27.01 ], "茶陵": [ 113.54, 26.79 ], "酃县": [ 113.77, 26.49 ], "湘乡": [ 112.5, 27.75 ], "郴州": [ 113.037, 25.782 ], "郴县": [ 113, 25.79 ], "安仁": [ 113.27, 26.71 ], "永兴": [ 113.11, 26.13 ], "资兴": [ 113.39, 25.95 ], "桂东": [ 113.91, 25.08 ], "汝城": [ 113.68, 25.54 ], "宜章": [ 113.96, 25.41 ], "临武": [ 112.55, 25.27 ], "嘉禾": [ 112.35, 25.56 ], "桂阳": [ 112.72, 25.73 ], "来阳": [ 112.84, 26.41 ], "衡南": [ 112.61, 26.89 ], "衡山": [ 112.86, 27.25 ], "衡东": [ 112.95, 27.1 ], "常宁": [ 112.39, 26.38 ], "祁阳": [ 111.85, 26.59 ], "祁东": [ 112.14, 26.8 ], "永州": [ 111.614, 26.435 ], "零陵": [ 111.63, 26.22 ], "新田": [ 112.21, 25.91 ], "宁远": [ 111.95, 25.6 ], "蓝山": [ 112.16, 25.37 ], "双牌": [ 111.64, 25.96 ], "江永": [ 111.33, 25.41 ], "道县": [ 111.57, 25.52 ], "东安": [ 111.28, 26.41 ], "江华": [ 111.79, 24.97 ], "新宁": [ 110.84, 26.44 ], "武冈": [ 110.61, 26.73 ], "隆回": [ 111.04, 27.13 ], "绥宁": [ 110.14, 25.59 ], "洞口": [ 110.57, 27.06 ], "城步": [ 110.3, 26.37 ], "娄底": [ 111.996, 27.741 ], "涟源": [ 111.66, 27.68 ], "新邵": [ 111.46, 27.33 ], "双峰": [ 112.18, 27.44 ], "冷水江": [ 111.41, 27.68 ], "邵东": [ 111.73, 27.25 ], "新化": [ 111.29, 27.73 ], "怀化": [ 109.986, 27.557 ], "黔阳": [ 110.14, 27.33 ], "辰溪": [ 110.18, 28.02 ], "沅陵": [ 110.39, 28.46 ], "溆浦": [ 110.57, 27.92 ], "会同": [ 109.71, 26.86 ], "靖县": [ 109.68, 26.57 ], "洪江": [ 110.38, 21.2 ], "芷江": [ 109.78, 27.44 ], "麻阳": [ 109.79, 27.87 ], "通道": [ 109.77, 26.16 ], "新晃": [ 109.16, 27.37 ], "吉首": [ 109.71, 28.3 ], "永顺": [ 109.84, 29 ], "桑植": [ 110.16, 29.38 ], "大庸": [ 110.48, 29.13 ], "古丈": [ 109.91, 28.62 ], "泸溪": [ 110.73, 28.29 ], "凤凰": [ 109.43, 27.92 ], "花垣": [ 109.46, 28.59 ], "保靖": [ 109.64, 28.7 ], "龙山": [ 109.42, 29.64 ], "常德": [ 111.653, 29.012 ], "临澧": [ 111.64, 29.44 ], "澧县": [ 111.75, 29.65 ], "安乡": [ 112.16, 29.41 ], "津市": [ 111.87, 29.64 ], "汉寿": [ 111.97, 28.9 ], "桃源": [ 111.47, 28.9 ], "慈利": [ 111.09, 29.41 ], "石门": [ 111.35, 29.59 ], "益阳": [ 112.366, 28.588 ], "南县": [ 112.39, 29.37 ], "沅江": [ 112.36, 28.83 ], "宁乡": [ 112.55, 28.27 ], "安化": [ 111.2, 28.38 ], "桃江": [ 112.11, 28.51 ], "广州": [ 113.307, 23.12 ], "花县": [ 113.19, 23.4 ], "新十": [ 114.2, 24.09 ], "增城": [ 113.81, 23.13 ], "从化": [ 113.55, 23.57 ], "龙门": [ 114.25, 23.75 ], "番禺": [ 113.36, 22.95 ], "海口": [ 110.33, 20.022 ], "汕头": [ 116.728, 23.383 ], "茂名": [ 110.931, 21.668 ], "佛山": [ 113.134, 23.035 ], "江门": [ 113.078, 22.575 ], "深圳": [ 114.025, 22.546 ], "宝安": [ 113.85, 22.58 ], "珠海": [ 113.562, 22.256 ], "韶关": [ 113.594, 24.802 ], "曲江": [ 113.58, 24.68 ], "乐昌": [ 113.35, 25.14 ], "仁化": [ 113.73, 25.11 ], "南雄": [ 114.33, 25.14 ], "始兴": [ 114.08, 24.78 ], "翁源": [ 114.13, 24.36 ], "佛岗": [ 113.52, 23.86 ], "英德": [ 113.38, 24.17 ], "清远": [ 113.04, 23.698 ], "阳山": [ 112.65, 24.48 ], "连县": [ 112.4, 24.77 ], "连山": [ 112.07, 24.59 ], "连南": [ 112.28, 24.77 ], "惠州": [ 114.41, 23.113 ], "惠阳": [ 114.4, 23.09 ], "博罗": [ 114.28, 23.18 ], "河源": [ 114.713, 23.757 ], "连平": [ 114.48, 24.39 ], "和平": [ 114.89, 24.45 ], "龙川": [ 115.25, 24.09 ], "紫金": [ 115.18, 23.64 ], "惠东": [ 114.7, 22.97 ], "东莞": [ 113.763, 23.043 ], "梅州": [ 116.126, 24.304 ], "梅县": [ 116.1, 24.55 ], "平远": [ 117.9, 24.59 ], "蕉岭": [ 116.18, 24.66 ], "大埔": [ 116.7, 24.34 ], "丰顺": [ 116.18, 23.78 ], "五华": [ 115.75, 23.93 ], "兴宁": [ 115.75, 24.15 ], "潮州": [ 116.63, 23.661 ], "澄海": [ 116.8, 23.48 ], "潮安": [ 116.63, 23.68 ], "饶平": [ 117.01, 23.7 ], "南澳": [ 117.03, 23.44 ], "潮阳": [ 116.61, 23.27 ], "惠来": [ 116.29, 23.07 ], "陆丰": [ 117.64, 22.95 ], "海丰": [ 117.33, 22.98 ], "普宁": [ 116.17, 23.29 ], "揭西": [ 115.82, 23.45 ], "揭阳": [ 116.379, 23.547 ], "南海": [ 113.11, 23.05 ], "三水": [ 112.89, 23.18 ], "顺德": [ 113.24, 22.84 ], "中山": [ 113.422, 22.545 ], "斗门": [ 113.25, 22.2 ], "新会": [ 113.02, 22.52 ], "鹤山": [ 112.94, 22.76 ], "开平": [ 112.68, 22.36 ], "台山": [ 112.78, 22.27 ], "恩平": [ 112.29, 22.21 ], "高明": [ 112.76, 21.71 ], "廉江": [ 110.27, 21.63 ], "化州": [ 110.59, 21.64 ], "高州": [ 110.83, 21.95 ], "信宜": [ 110.9, 22.36 ], "阳春": [ 111.78, 22.16 ], "阳江": [ 111.977, 21.871 ], "电白": [ 110.99, 21.52 ], "吴川": [ 110.78, 21.43 ], "徐闻": [ 110.17, 20.34 ], "海康": [ 110.07, 20.91 ], "遂溪": [ 110.24, 21.39 ], "肇庆": [ 112.479, 23.078 ], "高要": [ 112.44, 23.05 ], "怀集": [ 112.18, 23.93 ], "广宁": [ 112.43, 23.14 ], "四会": [ 112.68, 23.36 ], "新兴": [ 112.2, 22.68 ], "云浮": [ 112.05, 22.937 ], "罗定": [ 111.56, 22.77 ], "郁南": [ 111.51, 23.23 ], "德庆": [ 111.75, 23.15 ], "封开": [ 111.48, 23.45 ], "琼山": [ 110.33, 19.98 ], "文昌": [ 110.72, 19.61 ], "定安": [ 110.31, 19.68 ], "琼海": [ 110.46, 19.25 ], "万宁": [ 110.39, 18.8 ], "屯昌": [ 110.1, 19.36 ], "澄迈": [ 110, 19.75 ], "儋县": [ 109.57, 19.52 ], "临高": [ 109.69, 19.91 ], "保亭": [ 109.7, 18.64 ], "白沙": [ 109.44, 19.23 ], "琼中": [ 109.83, 19.05 ], "陵水": [ 110.02, 18.48 ], "崖县": [ 109.5, 18.25 ], "乐东": [ 109.17, 18.73 ], "东方": [ 108.64, 19.09 ], "昌江": [ 109.03, 19.25 ], "成都": [ 104.067, 30.679 ], "金堂": [ 104.32, 30.88 ], "双流": [ 104.94, 30.57 ], "蒲江": [ 103.29, 30.2 ], "郫县": [ 103.86, 30.8 ], "新都": [ 104.13, 30.82 ], "来易": [ 102.15, 26.9 ], "盐边": [ 101.56, 26.9 ], "温江": [ 103.81, 30.97 ], "灌县": [ 103.61, 31.04 ], "彭县": [ 103.94, 30.99 ], "什邡": [ 104.16, 31.13 ], "广汉": [ 104.25, 30.99 ], "新津": [ 103.78, 30.42 ], "邛崃": [ 103.47, 30.42 ], "大邑": [ 103.53, 30.58 ], "崇庆": [ 103.69, 30.63 ], "绵阳": [ 104.705, 31.504 ], "江油": [ 104.7, 31.8 ], "青川": [ 105.21, 32.59 ], "平武": [ 104.52, 32.42 ], "光元": [ 105.86, 32.44 ], "旺苍": [ 106.33, 32.25 ], "剑阁": [ 105.45, 32.03 ], "梓潼": [ 105.16, 31.64 ], "三台": [ 105.06, 31.1 ], "盐亭": [ 105.35, 31.23 ], "射洪": [ 105.31, 30.9 ], "遂宁": [ 105.564, 30.557 ], "蓬溪": [ 105.74, 30.78 ], "中江": [ 104.68, 31.06 ], "德阳": [ 104.402, 31.131 ], "绵竹": [ 104.19, 31.32 ], "安县": [ 104.41, 31.64 ], "北川": [ 104.44, 31.89 ], "内江": [ 105.073, 29.599 ], "乐至": [ 105.02, 30.3 ], "安岳": [ 105.3, 30.12 ], "威远": [ 104.7, 29.57 ], "资中": [ 104.85, 29.81 ], "资阳": [ 104.635, 30.132 ], "简阳": [ 104.53, 30.38 ], "隆昌": [ 105.25, 29.64 ], "宜宾": [ 104.633, 28.769 ], "富顺": [ 104.97, 29.24 ], "南溪": [ 104.96, 28.87 ], "江安": [ 105.06, 28.71 ], "纳溪": [ 105.38, 28.77 ], "泸县": [ 105.46, 28.96 ], "合江": [ 105.78, 28.79 ], "泸州": [ 105.443, 28.895 ], "古蔺": [ 105.79, 28.03 ], "叙水": [ 105.44, 28.19 ], "长宁": [ 104.91, 28.6 ], "兴文": [ 105.06, 28.36 ], "琪县": [ 104.81, 28.38 ], "高县": [ 104.52, 28.4 ], "筠连": [ 104.53, 28.16 ], "屏由": [ 104.15, 28.68 ], "乐由": [ 103.73, 29.59 ], "夹江": [ 103.59, 29.75 ], "洪雅": [ 103.38, 29.95 ], "丹棱": [ 103.53, 30.04 ], "青神": [ 103.81, 29.86 ], "眉由": [ 103.81, 30.05 ], "彭由": [ 103.83, 30.22 ], "井研": [ 104.06, 29.67 ], "仁寿": [ 104.09, 30 ], "犍为": [ 103.93, 29.21 ], "沐川": [ 103.98, 28.96 ], "娥眉": [ 103.5, 29.62 ], "马边": [ 103.53, 28.87 ], "峨边": [ 103.25, 29.23 ], "金口": [ 103.13, 29.24 ], "涪陵": [ 107.36, 29.7 ], "垫江": [ 107.34, 30.36 ], "丰都": [ 107.7, 29.89 ], "石柱": [ 108.13, 29.98 ], "秀山": [ 108.97, 28.47 ], "西阳": [ 108.75, 28.85 ], "黔江": [ 108.81, 29.53 ], "彭水": [ 108.19, 29.29 ], "武隆": [ 108.72, 29.29 ], "南川": [ 107.13, 29.15 ], "万县": [ 108.35, 30.83 ], "开县": [ 108.39, 31.23 ], "城口": [ 108.67, 31.98 ], "巫溪": [ 109.6, 31.42 ], "巫山": [ 109.86, 31.1 ], "奉节": [ 109.52, 31.06 ], "云阳": [ 108.89, 30.99 ], "忠县": [ 108.03, 30.33 ], "梁平": [ 107.78, 30.66 ], "南允": [ 106.06, 30.8 ], "苍溪": [ 105.96, 31.75 ], "阆中": [ 105.97, 31.75 ], "仪陇": [ 106.38, 31.52 ], "南部": [ 106.03, 31.34 ], "西允": [ 105.84, 31.01 ], "营山": [ 106.57, 31.07 ], "蓬安": [ 106.44, 31.04 ], "广安": [ 106.635, 30.463 ], "岳池": [ 106.43, 30.55 ], "武胜": [ 106.3, 30.38 ], "华云": [ 106.74, 30.41 ], "达县": [ 107.49, 31.23 ], "万源": [ 108.06, 32.07 ], "宜汉": [ 107.71, 31.39 ], "开江": [ 107.87, 31.1 ], "邻水": [ 106.91, 30.36 ], "大竹": [ 107.21, 30.75 ], "渠县": [ 106.94, 30.85 ], "南江": [ 106.83, 32.36 ], "巴中": [ 106.757, 31.869 ], "平昌": [ 107.11, 31.59 ], "通江": [ 108.24, 31.95 ], "百沙": [ 108.18, 32 ], "雅安": [ 103.009, 29.999 ], "芦山": [ 102.91, 30.17 ], "名山": [ 103.06, 30.09 ], "荣经": [ 102.81, 29.79 ], "汉源": [ 102.66, 29.4 ], "石棉": [ 102.38, 29.21 ], "天全": [ 102.78, 30.09 ], "宝兴": [ 102.84, 30.36 ], "马尔康": [ 102.22, 31.92 ], "红原": [ 102.55, 31.79 ], "阿坝": [ 101.72, 31.93 ], "若尔盖": [ 102.94, 33.62 ], "黑水": [ 102.95, 32.06 ], "松潘": [ 103.61, 32.64 ], "南坪": [ 104.19, 33.23 ], "汶川": [ 103.61, 31.46 ], "理县": [ 103.16, 31.42 ], "小金": [ 102.34, 30.97 ], "金川": [ 102.03, 31.48 ], "壤塘": [ 100.97, 32.3 ], "茂汶": [ 103.89, 31.67 ], "康定": [ 101.95, 30.04 ], "炉霍": [ 100.65, 31.38 ], "甘孜": [ 99.96, 31.64 ], "新龙": [ 100.28, 30.96 ], "白玉": [ 98.83, 32.23 ], "德格": [ 98.57, 31.81 ], "石渠": [ 98.06, 33.01 ], "色达": [ 100.35, 32.3 ], "泸定": [ 102.25, 29.92 ], "丹巴": [ 101.87, 30.85 ], "九龙": [ 114.173, 22.307 ], "雅江": [ 101, 30.03 ], "道孚": [ 101.14, 30.99 ], "理塘": [ 100.28, 30.03 ], "乡城": [ 99.78, 28.93 ], "稻城": [ 100.31, 29.04 ], "巴塘": [ 99, 30 ], "得荣": [ 99.25, 28.71 ], "西昌": [ 102.29, 27.92 ], "昭觉": [ 102.83, 28.03 ], "甘洛": [ 102.74, 28.96 ], "雷波": [ 103.62, 28.21 ], "宁南": [ 102.76, 27.07 ], "会东": [ 102.55, 26.74 ], "会理": [ 102.21, 26.67 ], "德昌": [ 102.15, 27.4 ], "美姑": [ 103.14, 28.33 ], "金阳": [ 103.22, 27.73 ], "布拖": [ 102.8, 27.7 ], "普格": [ 102.52, 27.38 ], "喜德": [ 102.42, 28.33 ], "越西": [ 102.49, 28.66 ], "盐源": [ 101.51, 27.42 ], "冕宁": [ 102.15, 28.58 ], "木里": [ 101.25, 27.9 ], "贵阳": [ 106.709, 26.629 ], "六盘水": [ 104.852, 26.591 ], "水城": [ 104.82, 26.58 ], "盘县": [ 104.64, 25.81 ], "六枝": [ 105.47, 26.21 ], "遵义": [ 106.931, 27.699 ], "绥阳": [ 107.19, 27.95 ], "道真": [ 107.6, 28.89 ], "凤冈": [ 107.72, 27.97 ], "余庆": [ 107.88, 27.22 ], "赤水": [ 105.69, 28.57 ], "桐梓": [ 106.8, 28.16 ], "正安": [ 107.43, 28.56 ], "务川": [ 107.87, 28.54 ], "湄潭": [ 107.5, 27.76 ], "仁怀": [ 106.41, 27.81 ], "习水": [ 106.2, 28.33 ], "铜仁": [ 109.168, 27.674 ], "玉屏": [ 108.91, 27.24 ], "思南": [ 108.23, 27.94 ], "德江": [ 108.13, 28.27 ], "万山": [ 109.2, 27.52 ], "江口": [ 108.82, 27.68 ], "师阡": [ 108.24, 27.52 ], "印江": [ 108.41, 28.02 ], "沿河": [ 108.48, 28.57 ], "松桃": [ 109.18, 28.17 ], "毕节": [ 105.333, 27.408 ], "黔西": [ 106.04, 27.03 ], "织金": [ 105.76, 26.66 ], "赫章": [ 104.71, 27.13 ], "大方": [ 105.61, 27.16 ], "金沙": [ 106.22, 27.46 ], "钠雍": [ 105.38, 26.77 ], "威宁": [ 104.28, 26.87 ], "安顺": [ 105.928, 26.228 ], "息烽": [ 106.73, 27.1 ], "清镇": [ 106.46, 26.56 ], "普定": [ 105.75, 26.32 ], "开阳": [ 106.95, 27.06 ], "修文": [ 106.59, 26.84 ], "平坝": [ 106.26, 26.42 ], "镇宁": [ 105.75, 26.08 ], "紫云": [ 106.06, 25.75 ], "关岭": [ 105.62, 25.94 ], "兴义": [ 104.91, 25.1 ], "普安": [ 104.96, 25.79 ], "贞丰": [ 105.63, 25.39 ], "望谟": [ 106.09, 25.17 ], "册亭": [ 105.79, 25 ], "安龙": [ 105.49, 25.11 ], "兴仁": [ 105.18, 25.44 ], "晴龙": [ 105.21, 25.83 ], "凯里": [ 107.97, 26.59 ], "施秉": [ 108.11, 27.03 ], "镇远": [ 108.41, 27.06 ], "天柱": [ 109.2, 26.89 ], "剑河": [ 108.58, 26.64 ], "黎平": [ 109.14, 26.24 ], "从江": [ 108.9, 25.76 ], "麻江": [ 107.58, 26.49 ], "黄平": [ 107.89, 26.89 ], "三穗": [ 108.68, 26.98 ], "岑巩": [ 108.72, 27.21 ], "锦屏": [ 109.18, 26.7 ], "台江": [ 108.32, 26.68 ], "榕江": [ 108.5, 25.94 ], "雷山": [ 108.07, 26.38 ], "丹寨": [ 107.79, 26.21 ], "都匀": [ 107.53, 26.72 ], "贵定": [ 107.22, 26.58 ], "瓮安": [ 107.48, 27.08 ], "平塘": [ 107.55, 25.83 ], "长顺": [ 106.45, 26.03 ], "惠水": [ 106.66, 26.14 ], "荔波": [ 107.88, 25.42 ], "福泉": [ 107.51, 26.7 ], "独山": [ 107.54, 25.84 ], "罗甸": [ 106.74, 25.43 ], "龙里": [ 106.98, 26.46 ], "三都": [ 107.86, 26 ], "昆明": [ 102.714, 25.049 ], "富民": [ 102.48, 25.21 ], "晋宁": [ 102.58, 24.68 ], "呈贡": [ 102.79, 24.9 ], "安宁": [ 102.44, 24.95 ], "昭通": [ 103.725, 27.34 ], "永善": [ 103.63, 28.22 ], "大关": [ 103.91, 27.74 ], "彝良": [ 104.06, 27.61 ], "鲁甸": [ 103.54, 27.21 ], "绥江": [ 103.97, 28.58 ], "盐津": [ 104.28, 28.08 ], "威信": [ 105.05, 27.85 ], "镇雄": [ 104.86, 27.42 ], "巧家": [ 102.92, 26.9 ], "永富": [ 104.38, 28.62 ], "曲靖": [ 103.782, 25.52 ], "宣威": [ 104.09, 26.24 ], "富源": [ 104.24, 25.67 ], "师宗": [ 103.97, 24.85 ], "嵩明": [ 103.03, 25.35 ], "会泽": [ 103.27, 26.41 ], "沽益": [ 103.82, 25.62 ], "罗平": [ 104.3, 24.88 ], "陆良": [ 104.64, 25.04 ], "宜良": [ 103.12, 24.9 ], "马龙": [ 103.61, 25.41 ], "路南": [ 103.24, 24.77 ], "寻甸": [ 103.25, 25.56 ], "玉溪": [ 102.545, 24.37 ], "华宁": [ 102.93, 24.26 ], "通海": [ 102.75, 24.09 ], "澄江": [ 102.91, 24.68 ], "江川": [ 102.73, 24.27 ], "易门": [ 102.15, 24.67 ], "元江": [ 102, 23.59 ], "新平": [ 101.98, 24.06 ], "峨山": [ 102.38, 24.16 ], "思茅": [ 101, 22.79 ], "普洱": [ 100.98, 22.788 ], "镇沅": [ 100.88, 23.9 ], "景东": [ 100.82, 24.42 ], "景谷": [ 100.71, 23.5 ], "黑江": [ 101.71, 23.4 ], "澜沦": [ 99.97, 22.55 ], "西盟": [ 99.47, 22.73 ], "江城": [ 101.88, 22.58 ], "孟连": [ 99.55, 22.32 ], "临沦": [ 100.09, 23.88 ], "云县": [ 100.12, 24.44 ], "镇康": [ 99.02, 23.92 ], "永德": [ 99.25, 24.03 ], "凤庆": [ 99.92, 24.58 ], "双江": [ 99.85, 23.45 ], "沧源": [ 99.24, 23.15 ], "耿马": [ 99.41, 23.56 ], "保由": [ 99.18, 25.12 ], "施甸": [ 99.15, 24.69 ], "腾冲": [ 98.51, 25.01 ], "昌宁": [ 99.61, 24.82 ], "龙陵": [ 98.7, 24.58 ], "丽江": [ 100.234, 26.859 ], "华坪": [ 101.24, 26.63 ], "永胜": [ 100.76, 26.71 ], "宁蒗": [ 100.82, 27.29 ], "文山": [ 104.24, 23.37 ], "广南": [ 105.09, 24.05 ], "西畴": [ 104.68, 23.42 ], "麻栗坡": [ 104.71, 23.12 ], "马关": [ 104.4, 23.01 ], "丘北": [ 104.19, 24.03 ], "砚山": [ 104.35, 23.62 ], "富宁": [ 105.6, 23.62 ], "个旧": [ 102.43, 23.35 ], "弥勒": [ 103.43, 24.41 ], "蒙自": [ 103.41, 23.36 ], "元阳": [ 102.81, 23.17 ], "红河": [ 102.42, 23.35 ], "石屏": [ 102.48, 23.73 ], "泸西": [ 103.76, 24.52 ], "金平": [ 103.24, 22.77 ], "开远": [ 103.23, 23.7 ], "绿春": [ 102.42, 23.01 ], "建水": [ 102.79, 23.64 ], "河口": [ 103.98, 22.52 ], "屏边": [ 103.67, 22.68 ], "景淇": [ 100.79, 22 ], "勐海": [ 100.5, 21.95 ], "勐腊": [ 101.56, 21.48 ], "楚雄": [ 101.54, 25.01 ], "元谋": [ 101.85, 25.7 ], "武定": [ 102.36, 25.55 ], "禄丰": [ 102.08, 25.15 ], "南华": [ 101.26, 25.21 ], "大姚": [ 101.34, 25.73 ], "永仁": [ 101.7, 26.07 ], "禄劝": [ 102.45, 25.58 ], "牟定": [ 101.58, 25.32 ], "双柏": [ 101.67, 24.68 ], "姚安": [ 101.24, 25.4 ], "下关": [ 100.24, 25.45 ], "剑川": [ 99.88, 26.53 ], "洱源": [ 99.94, 26.1 ], "宾川": [ 100.55, 25.82 ], "弥渡": [ 100.52, 25.34 ], "永平": [ 99.52, 25.45 ], "鹤庆": [ 100.18, 26.55 ], "大理": [ 100.19, 25.69 ], "漾濞": [ 99.98, 25.68 ], "云龙": [ 99.39, 25.9 ], "祥云": [ 100.56, 25.48 ], "巍山": [ 100.33, 25.23 ], "南涧": [ 100.51, 25.04 ], "潞西": [ 98.6, 24.41 ], "陇川": [ 97.96, 24.33 ], "盈江": [ 97.93, 24.69 ], "畹町": [ 98.08, 24.08 ], "瑞丽": [ 97.83, 24 ], "梁河": [ 98.3, 24.78 ], "泸水": [ 98.82, 25.97 ], "碧江": [ 98.95, 26.55 ], "福贡": [ 98.92, 26.89 ], "兰坪": [ 99.29, 26.49 ], "贡山": [ 98.65, 27.73 ], "中甸": [ 99.72, 27.78 ], "德钦": [ 98.93, 28.49 ], "维西": [ 99.27, 27.15 ], "南昌": [ 115.893, 28.689 ], "新建": [ 115.8, 28.69 ], "景德镇": [ 117.186, 29.303 ], "萍乡": [ 113.859, 27.639 ], "九江": [ 115.999, 29.719 ], "彭泽": [ 116.56, 29.9 ], "湖口": [ 116.23, 29.75 ], "都昌": [ 116.19, 29.29 ], "星子": [ 116.03, 29.47 ], "永修": [ 115.82, 29.04 ], "德安": [ 115.75, 29.33 ], "瑞昌": [ 115.65, 29.68 ], "武宁": [ 115.09, 29.26 ], "修永": [ 114.55, 29.04 ], "上饶": [ 117.955, 28.457 ], "婺源": [ 117.83, 29.25 ], "德兴": [ 117.58, 28.96 ], "玉山": [ 118.25, 28.68 ], "广丰": [ 118.2, 28.45 ], "铅山": [ 117.71, 28.32 ], "横峰": [ 117.62, 28.42 ], "鹰潭": [ 117.035, 28.241 ], "贵溪": [ 117.2, 28.3 ], "余江": [ 116.82, 28.22 ], "万年": [ 117.08, 28.7 ], "乐平": [ 117.12, 28.97 ], "波阳": [ 116.68, 29 ], "于干": [ 116.69, 28.7 ], "弋阳": [ 117.43, 28.42 ], "宜春": [ 114.4, 27.811 ], "万载": [ 114.44, 28.11 ], "铜鼓": [ 114.37, 28.53 ], "宜丰": [ 114.78, 28.4 ], "上高": [ 114.91, 28.25 ], "安义": [ 115.55, 28.86 ], "奉新": [ 115.38, 28.71 ], "高安": [ 115.38, 28.42 ], "丰城": [ 115.7, 28.19 ], "清江": [ 119.02, 33.59 ], "新余": [ 114.947, 27.822 ], "分宜": [ 114.68, 27.82 ], "靖安": [ 115.37, 28.88 ], "抚州": [ 116.36, 27.954 ], "临川": [ 116.29, 27.95 ], "金溪": [ 116.77, 27.92 ], "资溪": [ 117.06, 27.7 ], "黎川": [ 116.91, 27.3 ], "南丰": [ 116.52, 27.22 ], "南城": [ 116.62, 27.56 ], "宜黄": [ 116.2, 27.55 ], "崇仁": [ 116.05, 27.75 ], "乐安": [ 115.82, 27.44 ], "东乡": [ 103.39, 35.68 ], "进贤": [ 116.26, 28.37 ], "吉安": [ 114.992, 27.113 ], "新干": [ 115.4, 27.77 ], "峡江": [ 115.15, 27.56 ], "吉水": [ 115.14, 27.22 ], "永丰": [ 115.42, 27.33 ], "泰和": [ 114.88, 26.81 ], "万安": [ 114.77, 26.47 ], "遂川": [ 114.5, 26.33 ], "宁冈": [ 113.97, 26.71 ], "永新": [ 114.23, 26.96 ], "莲花": [ 113.94, 27.14 ], "安福": [ 114.62, 27.39 ], "井冈山": [ 114.17, 26.57 ], "赣州": [ 114.935, 25.845 ], "广昌": [ 116.32, 26.84 ], "石城": [ 116.32, 26.34 ], "宁都": [ 116, 26.46 ], "兴国": [ 115.33, 26.32 ], "于都": [ 115.39, 25.96 ], "瑞金": [ 116.02, 25.89 ], "会昌": [ 115.79, 25.58 ], "安远": [ 115.41, 25.15 ], "寻乌": [ 115.64, 24.96 ], "定南": [ 115.02, 24.7 ], "龙南": [ 114.79, 24.91 ], "全南": [ 114.53, 24.76 ], "信丰": [ 114.94, 25.39 ], "赣县": [ 114.02, 25.85 ], "南康": [ 114.75, 25.66 ], "上犹": [ 114.55, 25.8 ], "崇义": [ 114.31, 25.69 ], "大余": [ 114.36, 25.39 ], "西安": [ 108.953, 34.277 ], "长安": [ 108.97, 34.18 ], "铜川": [ 108.968, 34.908 ], "耀县": [ 108.98, 34.91 ], "宝鸡": [ 107.17, 34.364 ], "凤翔": [ 107.39, 34.53 ], "千阳": [ 107.13, 34.65 ], "陇县": [ 106.86, 34.91 ], "麟游": [ 107.8, 34.69 ], "岐山": [ 107.63, 34.46 ], "浮风": [ 107.87, 34.38 ], "武功": [ 108.22, 34.28 ], "眉县": [ 107.76, 34.29 ], "太白": [ 107.3, 34.09 ], "凤县": [ 106.51, 33.93 ], "榆林": [ 109.745, 38.279 ], "神木": [ 110.51, 38.83 ], "府谷": [ 111.07, 39.05 ], "佳县": [ 110.48, 38.04 ], "米脂": [ 110.23, 37.78 ], "吴堡": [ 110.73, 37.49 ], "绥德": [ 110.24, 37.49 ], "清涧": [ 110.15, 37.11 ], "子洲": [ 110.05, 37.45 ], "横山": [ 109.32, 37.97 ], "靖边": [ 108.79, 37.61 ], "定边": [ 107.59, 37.6 ], "延安": [ 109.5, 36.603 ], "安寨": [ 109.34, 36.88 ], "子长": [ 109.65, 37.16 ], "延川": [ 110.18, 36.87 ], "延长": [ 110.02, 36.59 ], "宜川": [ 110.15, 36.04 ], "黄龙": [ 109.86, 35.6 ], "洛川": [ 109.42, 35.76 ], "宜君": [ 109.11, 35.43 ], "黄陵": [ 109.27, 35.6 ], "富县": [ 109.37, 36 ], "甘泉": [ 109.37, 36.29 ], "志丹": [ 108.78, 36.84 ], "吴旗": [ 108.22, 36.93 ], "咸阳": [ 108.707, 34.345 ], "礼泉": [ 108.43, 34.5 ], "永寿": [ 108.14, 34.71 ], "彬县": [ 108.09, 35.04 ], "长武": [ 107.8, 35.22 ], "旬邑": [ 108.33, 35.13 ], "淳化": [ 108.57, 34.81 ], "泾阳": [ 108.84, 34.53 ], "三原": [ 108.94, 34.62 ], "高陵": [ 109.1, 34.55 ], "户县": [ 108.61, 34.12 ], "周至": [ 108.22, 34.18 ], "兴平": [ 108.49, 34.32 ], "乾县": [ 108.25, 34.54 ], "渭南": [ 109.483, 34.502 ], "蒲城": [ 109.59, 34.97 ], "白水": [ 109.6, 35.18 ], "成城": [ 109.93, 35.2 ], "韩城": [ 110.45, 35.47 ], "合阳": [ 110.15, 35.24 ], "人荔": [ 109.96, 34.82 ], "潼关": [ 110.25, 34.56 ], "华阴": [ 110.09, 34.58 ], "华县": [ 109.77, 34.53 ], "蓝田": [ 109.32, 34.17 ], "临潼": [ 109.22, 34.38 ], "富平": [ 109.17, 34.76 ], "商县": [ 109.96, 33.88 ], "洛南": [ 110.15, 34.11 ], "丹凤": [ 110.35, 33.71 ], "商南": [ 110.88, 33.54 ], "山阳": [ 109.91, 33.55 ], "镇安": [ 109.16, 33.45 ], "柞水": [ 109.14, 33.69 ], "安康": [ 109.038, 32.704 ], "旬阳": [ 109.35, 32.83 ], "白河": [ 110.06, 32.83 ], "平利": [ 109.37, 32.41 ], "镇坪": [ 109.51, 31.91 ], "岚皋": [ 108.89, 32.3 ], "紫阳": [ 108.55, 32.56 ], "汉阴": [ 108.53, 32.9 ], "石泉": [ 108.26, 33.05 ], "宁陕": [ 108.33, 33.34 ], "汉中": [ 107.045, 33.081 ], "留坝": [ 106.95, 33.65 ], "城固": [ 107.32, 33.16 ], "洋县": [ 107.56, 33.23 ], "佛坪": [ 108, 33.55 ], "西乡": [ 107.77, 33 ], "镇巴": [ 107.91, 32.56 ], "南郑": [ 106.93, 33 ], "宁强": [ 106.25, 32.82 ], "勉县": [ 106.68, 33.16 ], "略阳": [ 106.16, 33.34 ], "西宁": [ 101.767, 36.64 ], "大通": [ 101.67, 36.92 ], "平安": [ 102.09, 36.47 ], "湟中": [ 101.57, 36.49 ], "乐都": [ 102.38, 36.49 ], "民和": [ 102.8, 36.3 ], "湟源": [ 101.28, 36.72 ], "互助": [ 101.95, 36.84 ], "化隆": [ 102.3, 36.11 ], "循化": [ 102.46, 35.84 ], "门源": [ 101.62, 37.37 ], "海晏": [ 100.99, 36.89 ], "刚察": [ 100.17, 37.32 ], "祁连": [ 100.22, 38.2 ], "同仁": [ 102, 35.54 ], "尖扎": [ 102, 35.92 ], "泽库": [ 101.5, 35.03 ], "河南": [ 101.62, 34.75 ], "共和": [ 100.61, 36.27 ], "贵德": [ 101.47, 36.02 ], "贵南": [ 100.75, 35.57 ], "同德": [ 100.63, 35.24 ], "兴海": [ 99.99, 35.6 ], "玛沁": [ 100.26, 34.49 ], "甘德": [ 99.89, 33.95 ], "久治": [ 101.47, 33.46 ], "班玛": [ 100.73, 32.92 ], "达日": [ 99.68, 33.74 ], "玛多": [ 98.26, 34.92 ], "玉树": [ 97.013, 33.006 ], "称多": [ 97.12, 33.35 ], "囊谦": [ 96.47, 32.23 ], "杂多": [ 95.3, 32.92 ], "治多": [ 95.6, 33.86 ], "曲麻菜": [ 95.5, 34.52 ], "格尔木": [ 94.9, 36.41 ], "乌兰": [ 98.46, 36.9 ], "都兰": [ 98.13, 36.3 ], "天峻": [ 99.03, 37.28 ], "兰州": [ 103.823, 36.064 ], "永登": [ 103.25, 36.73 ], "榆中": [ 104.09, 35.87 ], "永昌": [ 101.94, 38.23 ], "皋兰": [ 103.97, 36.32 ], "定西": [ 104.626, 35.586 ], "会宁": [ 105.08, 35.72 ], "陇西": [ 104.61, 34.98 ], "临洮": [ 103.88, 35.39 ], "靖远": [ 104.71, 36.54 ], "通渭": [ 105.27, 35.24 ], "渭源": [ 104.19, 35.17 ], "平凉": [ 106.688, 35.55 ], "灵台": [ 107.61, 35.1 ], "华亭": [ 106.65, 35.21 ], "静宁": [ 105.73, 35.51 ], "泾川": [ 107.38, 35.31 ], "崇信": [ 107.05, 35.27 ], "庄浪": [ 106.06, 35.2 ], "庆阳": [ 107.644, 35.726 ], "华池": [ 108, 36.44 ], "庄宁": [ 108.43, 35.5 ], "镇源": [ 107.22, 35.7 ], "环县": [ 107.33, 36.57 ], "合水": [ 108.02, 35.81 ], "宁县": [ 107.94, 35.17 ], "天水": [ 105.736, 34.584 ], "徽县": [ 106.11, 33.78 ], "礼县": [ 105.15, 34.22 ], "武山": [ 104.88, 34.69 ], "秦安": [ 105.69, 34.89 ], "清水": [ 106.12, 34.73 ], "两当": [ 106.28, 33.9 ], "西和": [ 105.28, 34.02 ], "甘谷": [ 105.35, 34.7 ], "漳县": [ 104.48, 34.87 ], "张家川": [ 106.23, 35 ], "武都": [ 104.94, 33.43 ], "宕昌": [ 104.38, 34.06 ], "康县": [ 105.58, 33.33 ], "成县": [ 105.7, 33.75 ], "文县": [ 104.7, 32.95 ], "临潭": [ 103.35, 34.69 ], "舟曲": [ 104.38, 33.81 ], "玛曲": [ 102.04, 33.97 ], "下河": [ 102.46, 35.21 ], "卓尼": [ 103.54, 34.61 ], "迭部": [ 103.23, 34.08 ], "碌曲": [ 102.5, 34.6 ], "临夏": [ 103.22, 35.62 ], "永靖": [ 103.34, 35.97 ], "和政": [ 103.31, 35.43 ], "康乐": [ 103.68, 35.39 ], "广河": [ 103.54, 35.46 ], "岷县": [ 104.04, 34.41 ], "积石山": [ 102.85, 35.74 ], "武威": [ 102.64, 37.933 ], "民勤": [ 103.08, 38.62 ], "古浪": [ 102.86, 37.43 ], "景泰": [ 104.05, 37.14 ], "天祝": [ 102.84, 37.24 ], "张掖": [ 100.459, 38.939 ], "民乐": [ 100.85, 38.43 ], "临泽": [ 100.17, 39.14 ], "山丹": [ 101.19, 38.79 ], "高台": [ 99.84, 39.14 ], "肃南": [ 99.57, 38.86 ], "玉门": [ 97.58, 39.81 ], "酒泉": [ 98.508, 39.741 ], "敦煌": [ 94.71, 40.13 ], "金塔": [ 98.92, 39.97 ], "安西": [ 95.77, 40.51 ], "阿克塞": [ 94.25, 38.46 ], "肃北": [ 94.89, 39.49 ], "南宁": [ 108.297, 22.806 ], "柳州": [ 109.422, 24.329 ], "桂林": [ 110.26, 25.262 ], "梧州": [ 111.305, 23.485 ], "凭祥": [ 106.75, 22.11 ], "邕宁": [ 108.49, 22.74 ], "武鸣": [ 108.27, 23.17 ], "马山": [ 108.2, 23.73 ], "上林": [ 108.59, 23.44 ], "宾阳": [ 108.8, 23.22 ], "横县": [ 109.2, 22.69 ], "扶绥": [ 107.92, 22.65 ], "崇左": [ 107.357, 22.415 ], "宁明": [ 107.08, 22.12 ], "龙州": [ 106.84, 22.36 ], "大新": [ 107.21, 22.85 ], "天等": [ 107.12, 23.08 ], "隆安": [ 107.68, 23.18 ], "河池": [ 108.069, 24.699 ], "环江": [ 108.26, 24.83 ], "罗城": [ 108.9, 24.79 ], "宜山": [ 108.64, 24.47 ], "东兰": [ 107.36, 24.53 ], "凤山": [ 107.05, 24.55 ], "天峨": [ 107.16, 25.01 ], "南丹": [ 107.54, 24.98 ], "都安": [ 108.09, 23.94 ], "巴马": [ 107.25, 24.15 ], "合山": [ 108.89, 23.82 ], "柳城": [ 109.24, 24.67 ], "融安": [ 109.37, 24.24 ], "鹿寨": [ 109.74, 24.49 ], "象州": [ 109.7, 23.98 ], "武宜": [ 109.66, 23.6 ], "柳江": [ 109.34, 24.27 ], "来宾": [ 109.231, 23.741 ], "忻城": [ 108.66, 24.07 ], "融水": [ 109.24, 25.07 ], "三江": [ 109.58, 25.8 ], "金秀": [ 110.18, 24.14 ], "临桂": [ 110.22, 25.22 ], "灵川": [ 110.33, 25.42 ], "兴安": [ 110.66, 25.6 ], "资源": [ 110.66, 26.03 ], "全州": [ 111.06, 25.96 ], "灌阳": [ 111.14, 25.49 ], "恭城": [ 110.81, 24.85 ], "平乐": [ 110.66, 24.64 ], "荔浦": [ 110.38, 24.51 ], "永福": [ 109.98, 24.99 ], "龙胜": [ 110.02, 25.78 ], "苍悟": [ 111.22, 23.51 ], "钟山": [ 111.3, 24.53 ], "富川": [ 110.26, 24.83 ], "贺县": [ 111.54, 24.44 ], "岑溪": [ 111, 22.95 ], "藤县": [ 110.9, 23.36 ], "蒙山": [ 110.54, 24.22 ], "昭平": [ 110.8, 24.18 ], "玉林": [ 110.151, 22.643 ], "桂平": [ 110.07, 23.38 ], "平南": [ 110.4, 23.55 ], "容县": [ 110.53, 22.87 ], "北流": [ 110.33, 22.71 ], "陆川": [ 110.25, 22.33 ], "博白": [ 109.98, 22.27 ], "贵县": [ 109.6, 23.11 ], "北海": [ 109.122, 21.472 ], "钦州": [ 108.638, 21.973 ], "灵山": [ 109.29, 22.44 ], "浦北": [ 109.56, 22.27 ], "合浦": [ 109.2, 21.33 ], "上思": [ 107.98, 22.16 ], "防城": [ 108.35, 21.78 ], "百色": [ 106.631, 23.901 ], "凌云": [ 106.55, 24.35 ], "乐业": [ 106.56, 24.78 ], "田阳": [ 106.9, 23.75 ], "田东": [ 107.12, 23.62 ], "平果": [ 107.59, 23.33 ], "德保": [ 106.6, 23.34 ], "靖西": [ 106.41, 23.15 ], "那坡": [ 105.85, 23.42 ], "西林": [ 105.08, 24.51 ], "田林": [ 106.24, 24.31 ], "隆林": [ 105.34, 24.8 ], "乌鲁木齐": [ 87.564, 43.84 ], "克拉玛依": [ 84.881, 45.594 ], "石河子": [ 85.94, 44.27 ], "吐鲁番": [ 89.181, 42.96 ], "托克逊": [ 88.63, 42.77 ], "鄯善": [ 90.25, 42.82 ], "哈密": [ 93.44, 42.78 ], "伊吾": [ 94.65, 43.28 ], "巴里坤": [ 93, 43.6 ], "库尔勒": [ 86.06, 41.68 ], "和静": [ 86.35, 42.31 ], "和硕": [ 86.84, 42.23 ], "博湖": [ 86.53, 41.95 ], "尉梨": [ 86.24, 41.36 ], "轮台": [ 84.25, 41.77 ], "焉耆": [ 86.55, 42.05 ], "和田": [ 79.94, 37.12 ], "民丰": [ 82.63, 37.07 ], "策勒": [ 80.78, 37.04 ], "于田": [ 81.63, 36.86 ], "洛浦": [ 80.17, 37.12 ], "皮山": [ 78.29, 37.06 ], "墨玉": [ 79.74, 37.31 ], "阿克苏": [ 80.29, 41.15 ], "温宿": [ 80.24, 41.29 ], "拜城": [ 81.84, 41.82 ], "库车": [ 82.97, 41.68 ], "新和": [ 82.63, 41.55 ], "沙雅": [ 82.9, 41.25 ], "阿瓦提": [ 80.34, 40.64 ], "柯平": [ 79.06, 40.55 ], "乌什": [ 79.25, 41.22 ], "咯什": [ 75.94, 39.52 ], "巴楚": [ 78.59, 39.78 ], "枷师": [ 76.78, 39.46 ], "乐普湖": [ 76.67, 39.23 ], "麦盖提": [ 77.62, 38.95 ], "莎车": [ 77.25, 38.45 ], "泽普": [ 77.26, 38.2 ], "叶城": [ 77.42, 37.89 ], "疏勒": [ 76.05, 39.41 ], "英吉沙": [ 76.17, 38.91 ], "疏附": [ 75.83, 39.42 ], "塔什库尔干": [ 75.22, 75.22 ], "阿图什": [ 76.12, 39.73 ], "阿合奇": [ 78.42, 41.91 ], "阿克陶": [ 75.94, 39.14 ], "乌恰": [ 75.18, 39.7 ], "昌吉": [ 87.31, 44.05 ], "阜康": [ 87.94, 44.14 ], "奇台": [ 89.52, 44.02 ], "吉木萨尔": [ 89.14, 44 ], "米泉": [ 87.68, 43.97 ], "玛纳斯": [ 86.22, 44.28 ], "呼图壁": [ 86.92, 44.18 ], "木垒": [ 90.34, 43.8 ], "博乐": [ 82.1, 44.93 ], "精河": [ 82.92, 44.67 ], "温泉": [ 81.08, 44.95 ], "伊宁": [ 81.33, 43.91 ], "尼勒克": [ 82.53, 43.82 ], "新源": [ 83.27, 43.41 ], "巩留": [ 82.23, 43.35 ], "奎屯": [ 84.89, 44.45 ], "特克斯": [ 81.81, 43.23 ], "昭苏": [ 81.08, 43.15 ], "霍城": [ 80.87, 44.07 ], "察布察尔": [ 81.12, 43.82 ], "塔城": [ 82.96, 46.74 ], "额敏": [ 83.62, 46.52 ], "乌苏": [ 84.62, 44.45 ], "托里": [ 83.59, 45.92 ], "裕民": [ 82.94, 46.21 ], "沙湾": [ 85.56, 44.29 ], "和布克赛尔": [ 85.13, 46.78 ], "阿勒泰": [ 88.14, 47.86 ], "青河": [ 90.37, 46.71 ], "富蕴": [ 89.44, 47.05 ], "福海": [ 87.51, 47.15 ], "吉木乃": [ 85.84, 47.42 ], "布尔津": [ 86.92, 47.7 ], "哈巴河": [ 86.41, 48.05 ], "呼和浩特": [ 111.66, 40.828 ], "上默特左旗": [ 111.13, 40.72 ], "托克托": [ 111.15, 40.28 ], "上默特右旗": [ 110.52, 40.55 ], "固阳": [ 110.03, 41.03 ], "乌海": [ 106.831, 39.683 ], "集宁": [ 113.08, 41.03 ], "兴和": [ 113.97, 40.88 ], "清水河": [ 111.65, 39.92 ], "武川": [ 111.42, 41.12 ], "卓资": [ 112.52, 40.93 ], "商都": [ 113.53, 41.58 ], "丰镇": [ 113.15, 40.45 ], "凉城": [ 112.48, 40.52 ], "和林格尔": [ 111.8, 40.4 ], "化德": [ 114, 41.9 ], "察哈尔右翼后旗": [ 113.15, 41.85 ], "察哈尔右翼中旗": [ 112.62, 41.28 ], "察哈尔右翼前旗": [ 113.18, 40.78 ], "四子王旗": [ 111.68, 41.37 ], "达尔罕茂明安联合旗": [ 110.42, 41.72 ], "阿巴哈纳尔旗": [ 116.08, 43.95 ], "多伦": [ 116.48, 42.18 ], "阿巴嘎旗": [ 114.97, 44.03 ], "西乌珠穆沁旗": [ 117.58, 44.6 ], "东乌珠穆沁旗": [ 116.97, 45.53 ], "苏尼特左旗": [ 113.7, 43.85 ], "苏尼特右旗": [ 112.95, 42.47 ], "太仆寺旗": [ 115.3, 41.9 ], "正镶白旗": [ 115, 42.32 ], "正蓝旗": [ 116.02, 42.25 ], "镶黄旗": [ 113.83, 42.25 ], "海拉尔": [ 119.73, 29.22 ], "陈巴尔虎旗": [ 119.45, 49.33 ], "额尔古纳右旗": [ 120.08, 50.45 ], "额尔古纳左旗": [ 121.52, 50.8 ], "喜桂图旗": [ 120.73, 49.3 ], "阿荣旗": [ 123.5, 48.13 ], "布特哈旗": [ 122.78, 47.98 ], "新巴尔虎左旗": [ 116.82, 48.67 ], "新巴尔虎右旗": [ 118.23, 48.22 ], "鄂伦春自治旗": [ 123.7, 50.58 ], "莫力达瓦达斡尔族自治旗": [ 124.5, 48.47 ], "鄂温克族自治旗": [ 119.75, 49.13 ], "通辽": [ 122.26, 43.633 ], "开鲁": [ 121.32, 43.62 ], "科尔沁左翼后旗": [ 122.35, 42.97 ], "科尔沁左翼中旗": [ 123.28, 44.13 ], "库伦旗": [ 121.75, 42.72 ], "奈曼旗": [ 120.65, 42.85 ], "扎鲁特旗": [ 120.87, 44.55 ], "赤峰": [ 118.93, 42.297 ], "宁城": [ 119.32, 41.62 ], "林西": [ 118.02, 43.62 ], "喀喇沁旗": [ 118.67, 41.95 ], "敖汉旗": [ 119.87, 42.3 ], "翁牛特旗": [ 119, 42.97 ], "巴林右旗": [ 118.65, 43.52 ], "巴林左旗": [ 119.35, 43.98 ], "阿鲁科尔沁旗": [ 120.05, 43.97 ], "克什克腾旗": [ 117.48, 43.28 ], "伊克昭盟": [ 110, 39.83 ], "东胜县": [ 110, 39.83 ], "准格尔旗": [ 111.13, 39.68 ], "乌审旗": [ 109.03, 38.38 ], "伊金霍洛旗": [ 109.77, 39.25 ], "鄂托克旗": [ 107.97, 39.12 ], "鄂托克前旗": [ 107.43, 38.18 ], "杭锦旗": [ 108.7, 39.83 ], "达拉特旗": [ 110.02, 40.42 ], "临河": [ 107.37, 40.78 ], "五原": [ 108.28, 41.12 ], "磴口": [ 106.98, 40.33 ], "杭锦后旗": [ 107.12, 40.88 ], "乌拉特中旗": [ 108.52, 41.55 ], "乌拉特前旗": [ 108.65, 40.75 ], "乌拉特后旗": [ 108.52, 40.88 ], "阿拉善左旗": [ 105.68, 38.85 ], "阿拉善右旗": [ 101.68, 39.2 ], "额济纳旗": [ 100.88, 41.9 ], "突泉": [ 121.5, 45.4 ], "科尔沁右翼前旗": [ 122.03, 46.12 ], "科尔沁右翼中旗": [ 121.47, 45.05 ], "拉萨": [ 91.111, 29.662 ], "林周": [ 91.24, 30.2 ], "当雄": [ 91.05, 30.51 ], "墨竹工卡": [ 91.77, 29.77 ], "尼木": [ 90.14, 29.44 ], "米林": [ 94.13, 29.18 ], "墨脱": [ 95.26, 29.22 ], "达孜": [ 91.39, 29.63 ], "曲水": [ 90.7, 29.39 ], "堆龙德庆": [ 90.96, 29.67 ], "林芝": [ 94.25, 29.59 ], "工布江达": [ 93.25, 29.92 ], "那曲": [ 92.1, 31.47 ], "巴青": [ 94.1, 31.96 ], "比如": [ 93.68, 31.53 ], "班戈": [ 90.05, 31.35 ], "嘉黎": [ 93.46, 30.63 ], "聂荣": [ 92.3, 31.08 ], "索县": [ 93.71, 31.92 ], "安多": [ 91.68, 32.29 ], "申扎": [ 88.7, 30.94 ], "吕都": [ 97.14, 31.18 ], "贡觉": [ 98.29, 30.86 ], "左贡": [ 97.9, 29.68 ], "察隅": [ 97.49, 28.62 ], "洛隆": [ 95.76, 30.81 ], "丁青": [ 95.63, 31.42 ], "波密": [ 95.75, 29.92 ], "江达": [ 89.19, 31.53 ], "察雅": [ 97.56, 30.69 ], "芒康": [ 98.68, 29.64 ], "八宿": [ 96.95, 30.04 ], "边坝": [ 94.69, 30.94 ], "类乌齐": [ 96.57, 31.2 ], "乃东": [ 91.76, 29.18 ], "加查": [ 92.6, 29.09 ], "曲松": [ 92.11, 29.08 ], "错那": [ 91.91, 27.98 ], "穷结": [ 91.65, 29.04 ], "贡嘎": [ 90.96, 29.25 ], "浪卡子": [ 90.33, 29.96 ], "桑日": [ 92, 29.26 ], "朗县": [ 93.11, 29.06 ], "隆子": [ 92.42, 28.46 ], "措美": [ 91.4, 28.49 ], "洛扎": [ 90.83, 28.42 ], "扎囊": [ 91.26, 29.22 ], "日喀则": [ 88.82, 29.28 ], "定结": [ 87.77, 28.38 ], "拉孜": [ 87.62, 29.1 ], "聂拉木": [ 85.94, 28.19 ], "谢通门": [ 88.25, 29.43 ], "仲巴": [ 84.15, 29.66 ], "康马": [ 89.67, 28.57 ], "亚东": [ 88.93, 27.55 ], "岗巴": [ 88.5, 28.29 ], "南木林": [ 89.02, 29.71 ], "萨迦": [ 88, 28.87 ], "定日": [ 87.11, 28.57 ], "吉隆": [ 85.29, 28.94 ], "昂仁": [ 87.22, 29.3 ], "江孜": [ 89.63, 28.94 ], "仁布": [ 89.77, 29.21 ], "白朗": [ 89.16, 29.11 ], "萨嘎": [ 85.3, 29.38 ], "噶尔": [ 80, 32.08 ], "革吉": [ 81.13, 32.45 ], "扎达": [ 79.76, 31.47 ], "措勤": [ 85.16, 31.06 ], "日上": [ 79.61, 33.44 ], "改则": [ 84.1, 32.33 ], "普兰": [ 81.18, 30.37 ], "银川": [ 106.206, 38.502 ], "永宁": [ 106.24, 38.28 ], "贺兰": [ 106.35, 38.55 ], "石嘴山": [ 106.379, 39.02 ], "平罗": [ 106.54, 38.91 ], "陶乐": [ 106.69, 38.82 ], "吴忠": [ 106.208, 37.993 ], "同心": [ 105.94, 36.97 ], "灵武": [ 106.34, 38.1 ], "中宁": [ 105.66, 37.48 ], "盐池": [ 107.41, 37.78 ], "中卫": [ 105.196, 37.521 ], "青铜峡": [ 106.07, 38.02 ], "固原": [ 106.285, 36.021 ], "西吉": [ 105.7, 35.97 ], "泾源": [ 106.33, 35.5 ], "海原": [ 105.64, 36.56 ], "隆德": [ 106.11, 35.63 ], "台湾": [ 121.5, 25.14 ], "香港": [ 114.1, 22.2 ], "澳门": [ 113.33, 22.13 ], "合肥": [ 117.282, 31.866 ], "长丰": [ 117.16, 32.47 ], "淮南": [ 117.018, 32.642 ], "凤台": [ 116.71, 32.68 ], "淮北": [ 116.791, 33.96 ], "濉溪": [ 116.76, 33.92 ], "芜湖": [ 118.384, 31.366 ], "铜陵": [ 117.819, 30.94 ], "蚌埠": [ 117.357, 32.929 ], "马鞍山": [ 118.515, 31.688 ], "安庆": [ 117.058, 30.537 ], "宿州": [ 116.988, 33.636 ], "宿县": [ 116.97, 33.63 ], "砀山": [ 116.34, 34.42 ], "萧县": [ 116.93, 34.19 ], "吴壁": [ 117.55, 33.55 ], "泗县": [ 117.89, 33.49 ], "五河": [ 117.87, 33.14 ], "固镇": [ 117.32, 33.33 ], "怀远": [ 117.19, 32.95 ], "滁州": [ 118.324, 32.317 ], "嘉山": [ 117.98, 32.78 ], "天长": [ 119, 32.68 ], "来安": [ 118.44, 32.44 ], "全椒": [ 118.27, 32.1 ], "定远": [ 117.68, 32.52 ], "凤阳": [ 117.4, 32.86 ], "巢湖": [ 117.87, 31.62 ], "巢县": [ 117.87, 31.62 ], "肥东": [ 117.47, 31.89 ], "含山": [ 118.11, 31.7 ], "和县": [ 118.37, 31.7 ], "无为": [ 117.75, 31.3 ], "卢江": [ 117.29, 31.23 ], "宣城": [ 118.752, 30.951 ], "当涂": [ 118.49, 31.55 ], "郎溪": [ 119.17, 31.14 ], "广德": [ 119.41, 30.89 ], "泾县": [ 118.41, 30.68 ], "南陵": [ 118.32, 30.91 ], "繁昌": [ 118.21, 31.07 ], "宁国": [ 118.95, 30.62 ], "青阳": [ 117.84, 30.64 ], "屯溪": [ 118.31, 29.72 ], "休宁": [ 118.19, 29.81 ], "旌得": [ 118.53, 30.28 ], "绩溪": [ 118.57, 30.07 ], "歙县": [ 118.44, 29.88 ], "祁门": [ 117.7, 29.86 ], "黟县": [ 117.92, 29.93 ], "太平": [ 118.13, 30.28 ], "石台": [ 117.48, 30.19 ], "桐城": [ 116.94, 31.04 ], "纵阳": [ 117.21, 30.69 ], "怀宁": [ 116.63, 30.41 ], "望江": [ 116.69, 30.12 ], "宿松": [ 116.13, 30.15 ], "太湖": [ 116.27, 30.42 ], "岳西": [ 116.36, 30.84 ], "潜山": [ 116.53, 30.62 ], "东至": [ 116.99, 30.08 ], "贵池": [ 117.48, 30.66 ], "六安": [ 116.505, 31.755 ], "霍丘": [ 116.27, 32.32 ], "寿县": [ 116.78, 32.57 ], "肥西": [ 117.15, 31.7 ], "舒城": [ 116.94, 31.45 ], "霍山": [ 116.32, 31.38 ], "金寨": [ 115.87, 31.67 ], "阜阳": [ 115.82, 32.901 ], "毫县": [ 116.76, 33.86 ], "涡阳": [ 116.21, 33.49 ], "蒙城": [ 116.55, 33.25 ], "利辛": [ 116.19, 33.12 ], "颖上": [ 116.26, 32.62 ], "阜南": [ 115.6, 32.63 ], "临泉": [ 115.24, 33.06 ], "界首": [ 115.34, 33.24 ], "太和": [ 115.61, 33.16 ], "南京": [ 118.778, 32.057 ], "江宁": [ 118.83, 31.95 ], "六合": [ 118.83, 32.36 ], "江浦": [ 118.62, 32.07 ], "徐州": [ 117.188, 34.271 ], "连云港": [ 119.173, 34.601 ], "南通": [ 120.873, 32.014 ], "苏州": [ 120.619, 31.317 ], "无锡": [ 120.305, 31.57 ], "常州": [ 119.981, 31.771 ], "丰县": [ 116.57, 34.79 ], "沛县": [ 116.93, 34.73 ], "赣榆": [ 119.11, 34.83 ], "东海": [ 118.75, 34.54 ], "新沂": [ 118.33, 34.38 ], "邳县": [ 117.97, 34.3 ], "睢宁": [ 117.94, 33.89 ], "铜山": [ 117.2, 34.26 ], "灌云": [ 119.23, 34.3 ], "灌南": [ 119.36, 34.09 ], "沭阳": [ 118.79, 34.12 ], "宿迁": [ 118.296, 33.952 ], "泗阳": [ 118.68, 33.73 ], "盱眙": [ 118.05, 33 ], "涟水": [ 119.26, 33.77 ], "淮阴": [ 119.02, 33.62 ], "淮安": [ 119.03, 33.606 ], "洪泽": [ 118.85, 33.28 ], "泗洪": [ 118.23, 33.46 ], "金湖": [ 119.02, 33.01 ], "盐城": [ 120.148, 33.379 ], "滨海": [ 119.84, 34.01 ], "阜宁": [ 119.79, 33.78 ], "射阳": [ 120.26, 33.77 ], "建湖": [ 119.77, 33.46 ], "响水": [ 119.56, 34.2 ], "大丰": [ 120.45, 33.19 ], "东台": [ 120.31, 32.84 ], "海安": [ 120.45, 32.57 ], "如皋": [ 120.56, 32.39 ], "如东": [ 121.18, 32.33 ], "启东": [ 121.66, 31.8 ], "海门": [ 121.15, 31.89 ], "扬州": [ 119.427, 32.408 ], "宝应": [ 119.32, 33.23 ], "兴化": [ 119.82, 32.93 ], "高邮": [ 119.45, 32.78 ], "泰兴": [ 120.02, 32.16 ], "泰县": [ 120.15, 32.51 ], "泰州": [ 119.919, 32.476 ], "靖江": [ 120.26, 32.03 ], "江都": [ 119.55, 32.43 ], "邗江": [ 119.42, 32.39 ], "仪征": [ 119.16, 32.27 ], "镇江": [ 119.455, 32.204 ], "丹徒": [ 119.44, 32.2 ], "扬中": [ 119.81, 32.24 ], "丹阳": [ 119.55, 32 ], "武进": [ 119.95, 31.78 ], "宜兴": [ 119.82, 31.36 ], "金坛": [ 119.56, 31.74 ], "溧阳": [ 119.48, 31.43 ], "句容": [ 119.16, 31.95 ], "溧水": [ 119.02, 31.65 ], "高淳": [ 118.87, 31.32 ], "江阴": [ 120.26, 31.91 ], "沙洲": [ 120.55, 31.86 ], "常熟": [ 120.74, 31.64 ], "太仓": [ 121.1, 31.45 ], "昆山": [ 120.95, 31.39 ], "吴县": [ 120.62, 31.32 ], "吴江": [ 120.63, 31.16 ], "临沧": [ 100.092, 23.887 ], "保山": [ 99.177, 25.12 ], "大理白族自治州": [ 100.223, 25.596 ], "德宏州": [ 98.589, 24.441 ], "德宏": [ 98.589, 24.441 ], "怒江傈僳族自治州": [ 98.859, 25.86 ], "文山壮族苗族自治州": [ 104.246, 23.374 ], "楚雄彝族自治州": [ 101.529, 25.066 ], "红河哈尼族彝族自治州": [ 103.384, 23.367 ], "西双版纳": [ 100.803, 22.009 ], "迪庆藏族自治州": [ 99.713, 27.831 ], "乌兰察布": [ 113.112, 41.022 ], "兴安盟": [ 122.048, 46.083 ], "呼伦贝尔": [ 119.76, 49.201 ], "牙克石": [ 120.719, 49.291 ], "巴彦淖尔": [ 107.423, 40.769 ], "鄂尔多斯": [ 109.993, 39.816 ], "锡林浩特": [ 116.027, 43.939 ], "锡林郭勒盟": [ 116.027, 43.939 ], "阿拉善盟": [ 105.695, 38.843 ], "台中": [ 119.337, 26.091 ], "台北": [ 114.13, 22.374 ], "台南": [ 121.36, 38.965 ], "嘉义": [ 114.246, 22.728 ], "高雄": [ 111.59, 21.946 ], "延边州": [ 129.485, 42.896 ], "延边": [ 129.485, 42.896 ], "松原": [ 124.832, 45.136 ], "白山": [ 126.435, 41.945 ], "乐山": [ 103.76, 29.6 ], "凉山州": [ 102.259, 27.892 ], "凉山": [ 102.259, 27.892 ], "南充": [ 106.105, 30.8 ], "广元": [ 105.819, 32.441 ], "攀枝花": [ 101.722, 26.587 ], "甘孜州": [ 101.969, 30.055 ], "眉山": [ 103.841, 30.061 ], "自贡": [ 104.776, 29.359 ], "达州": [ 107.494, 31.214 ], "阿坝州": [ 102.228, 31.905 ], "亳州": [ 115.787, 33.871 ], "池州": [ 117.494, 30.66 ], "黄山": [ 118.293, 29.734 ], "菏泽": [ 115.463, 35.262 ], "吕梁": [ 111.143, 37.527 ], "忻州": [ 112.727, 38.461 ], "晋中": [ 112.738, 37.693 ], "朔州": [ 112.479, 39.337 ], "汕尾": [ 115.372, 22.778 ], "湛江": [ 110.365, 21.257 ], "贵港": [ 109.613, 23.103 ], "贺州": [ 111.552, 24.411 ], "防城港": [ 108.351, 21.617 ], "伊犁州": [ 81.297, 43.922 ], "克孜勒苏柯尔克孜自治州": [ 76.137, 39.75 ], "博尔塔拉州": [ 82.052, 44.913 ], "和田地区": [ 79.93, 37.116 ], "哈密地区": [ 93.528, 42.858 ], "喀什地区": [ 75.992, 39.47 ], "塔城地区": [ 82.974, 46.758 ], "昌吉州": [ 87.296, 44.007 ], "阿克苏地区": [ 80.269, 41.171 ], "阿勒泰地区": [ 88.137, 47.839 ], "省直辖": [ 112.41, 31.209 ], "台州": [ 121.44, 28.668 ], "舟山": [ 122.169, 30.036 ], "三亚": [ 109.522, 18.257 ], "三沙": [ 112.35, 16.84 ], "恩施州": [ 109.491, 30.285 ], "荆州": [ 112.241, 30.332 ], "襄阳": [ 112.25, 32.229 ], "鄂州": [ 114.895, 30.384 ], "随州": [ 113.379, 31.717 ], "张家界": [ 110.481, 29.124 ], "湘西州": [ 109.745, 28.317 ], "澳门半岛": [ 113.566, 22.195 ], "澳门离岛": [ 113.557, 22.204 ], "临夏州": [ 103.215, 35.598 ], "嘉峪关": [ 98.281, 39.802 ], "甘南州": [ 102.917, 34.992 ], "白银": [ 104.171, 36.546 ], "金昌": [ 102.208, 38.516 ], "陇南": [ 104.934, 33.394 ], "山南地区": [ 91.75, 29.229 ], "日喀则地区": [ 88.891, 29.269 ], "昌都地区": [ 97.185, 31.14 ], "林芝地区": [ 94.349, 29.666 ], "那曲地区": [ 92.067, 31.48 ], "阿里地区": [ 81.107, 30.404 ], "黔东南州": [ 107.985, 26.583 ], "黔南州": [ 107.523, 26.264 ], "黔西南州": [ 104.9, 25.095 ], "盘锦": [ 122.073, 41.141 ], "葫芦岛": [ 120.86, 40.743 ], "商洛": [ 109.934, 33.873 ], "果洛州": [ 100.223, 34.48 ], "海东地区": [ 102.085, 36.517 ], "海北州": [ 100.879, 36.96 ], "海南州": [ 100.624, 36.284 ], "海西州": [ 97.342, 37.373 ], "玉树州": [ 97.013, 33.006 ], "黄南州": [ 102.007, 35.522 ], "新界": [ 114.146, 22.427 ], "大兴安岭地区": [ 124.196, 51.991 ], "鸡西": [ 130.941, 45.321 ], "朝阳区": [ 116.447, 39.922 ], "海淀": [ 116.306, 39.966 ], "西城": [ 116.369, 39.934 ], "东城": [ 116.421, 39.935 ], "丰台": [ 116.291, 39.864 ], "通州": [ 116.662, 39.916 ], "石景山": [ 116.227, 39.912 ], "仙桃": [ 113.387, 30.293 ], "天门": [ 113.172, 30.669 ], "巩义": [ 113.029, 34.753 ], "儋州": [ 109.588, 19.527 ], "潜江": [ 112.904, 30.409 ], "包头": [ 110.052, 40.582 ], "鄂尔多斯东胜区": [ 109.971, 39.827 ], "满洲里": [ 117.407, 49.605 ], "乌兰浩特": [ 122.076, 46.075 ], "二连浩特": [ 111.97, 43.647 ], "鄂尔多斯鄂托克前旗": [ 107.485, 38.186 ], "神农架林区": [ 110.685, 31.75 ], "林西县": [ 118.061, 43.623 ], "第七师": [ 84.907, 44.442 ], "第八师石河子市": [ 86.092, 44.288 ], "公主岭": [ 124.833, 43.511 ] } 来源:https://blog.jiejiss.com/

2020/1/29
articleCard.readMore

二项式定理及其推广公式的一种简单理解办法

0x00 二项式定理 写在最前:\(\mathrm{C}_n^k=\binom{n}{k}=\frac{n!}{(n-k)! \cdot k!}\) 0x00.1 杨辉三角 前情提要:一种特殊的输出杨辉三角的方法 1 2 3 4 5 6 7 8 1 (a+b)**0 = 1 / \ 1 1 (a+b)**1 = a+b / \ / \ 1 2 1 (a+b)**2 = a² + 2ab + b² / \ / \ / \ 1 3 3 1 (a+b)**3 = a³ + 3a²b + 3ab² + b³ ... ... 不难发现,杨辉三角每一行的数字正好就是 \((a+b)^n\) 展开之后的对应多项式,而每一项的系数又和组合数 \(\binom{n}{k}\) 是相等的。 一种简单的理解是,杨辉三角中的每一个数字,表示的是从顶点开始,只向左下或右下前进,最终到达该数字所在位置的所有可能路径的总数。经过基本的推理,可以看出对于第 \(n\) 行的左起第 \(k\) 个数字,想要从顶点到达该数字所在位置都需要且只需要向左 \(n-k\) 次、向右 \(k-1\) 次。假想小明现在按照此规则从顶点出发,显然,小明每向左或向右一次,都会向下移动一行;因此,小明向下移动 \(n-1\) 行后,便会到达目标数所在的行。换言之,小明在路途上将且仅将移动 \(n-1\) 次,而这 \(n-1\) 次中有且仅有 \(n-k\) 次是向左下方移动的。于是,得出结论:这本质上是个超几何分布问题,可能性总数可以表示为 \({\rm C}_{n-1}^{n-k}={\rm C}_{n-1}^{k-1}=\binom{n-1}{k-1}\)。 而与之对应的,多项式展开也是一个超几何分布问题。这是因为展开式中的 \(p\cdot a^qb^{n-q}\) 项,\(p\) 表示的是有多少个 \(a^qb^{n-q}\),而每个 \(a^qb^{n-q}\) 都是在 \((a+b)(a+b)\cdots(a+b)\) 中挑选出 \(q\) 组,令这 \(q\) 组的 \(a\) 和余下的 \(n-q\) 组中的 \(b\) 相乘所得到的,从 \(n\) 组中选出 \(q\) 组的可能性总数为 \(\binom{n}{q}\)。 这样就可以推出杨辉三角的第 \(n+1\) 行全部数字是 \((a+b)^n\) 展开式的二项式系数一一对应的。 稍作拓展: 令 \(f(x,y)=(x+y)^n\),求 \(f(x,y)\) 的展开式二项式系数之和。 实际上,它的展开式的第 \(i+1\) 项可以直接表示为 \(\binom{n}{i}\cdot x^{n-i}y^i\),其中 \(0 \le i \le n\)。换言之,我们如果想要求其二项式系数之和 \(\sum_{i=0}^{n}\binom{n}{i}\),就可以通过将 \(x=1,\,y=1\) 代入到 \(\sum_{i=0}^{n}\binom{n}{i}\cdot x^{n-i}y^i\) 中来计算。换言之,所求即为:\(f(1,1)=2^n\)。 注意到 \(2^n\) 还可以视作 \(n\) 位二进制数的总数,这仅仅是一个巧合吗? 0x01 推广公式 在那耸立的高岗黑岩之上,恶魔的秽语悄然响起:\(\sum_{i=0}^n m^i\cdot\binom{n}{i}=(m+1)^n\) 引证:\(\binom{n}{n-k}=\binom{n}{k}\),这其实很好理解。\(\binom{n}{n-k}\) 表示从 \(n\) 个东西里挑出 \(n-k\) 个,剩下 \(k\) 个;\(\binom{n}{k}\) 表示从 \(n\) 个东西里挑出 \(k\) 个,剩下 \(n-k\) 个。但是实际上,每一种挑出来的组合,必然对应且仅对应一组被剩下了的组合;所以,剩下 \(k\) 个也可以理解为挑出来了 \(k\) 个,只不过挑出来它们是为了把它们剩下。当然最直观的证明方法是用组合数定义,展开阶乘,把相同的项消掉,再重新写成阶乘、转换为组合数的表达形式。 是的,这里有一个二项式定理的推广公式,就是 \(\sum_{i=0}^n m^i\cdot\binom{n}{i}=(m+1)^n\),其中 \(m,n \in \mathbb{Z^+}\)。现在我们要谈论的是该如何去简单理解这个公式。 我们先看看 \(m=1\) 的情况,这也就是上一个标题最后所给出的情况:\(\sum_{i=0}^{n}\binom{n}{i}=2^n\)。为了简单地解释它,我们暂时还是需要用到杨辉三角:小明移动 \(n\) 次后,一定会来到第 \(n+1\) 行的某一个数所在的位置。而他想要移动 \(n\) 次,就需要做出 \(n\) 个选择,每次都必须在向左下和向右下中二选其一。所以,总的可能数为:\(2^n\),这也就是推广公式的右手侧。而左手侧所表示的正是小明走到第 \(n+1\) 行的每一个数的可能之和,所以自然也就等于 \(2^n\)。 那么,对这种思考方式稍加抽象,我们可以用二进制数的思想来考虑:有一个 \(n\) 位二进制数,它的第 \(k\) 位为 \(1\) 则表示小明第 \(k\) 次选择了向左下,为 \(0\) 则表示选择了向右下。再进一步抽象,把小明完全剥离出去,就会变成:列举出所有的 \(n\) 位二进制数,这些二进制数当中,有且仅有 \(k\) 个 \(1\) 的数的数量就是 \(\binom{n}{k}\)(这等价于从 \(n\) 位里挑出 \(k\) 位,令它们为 \(1\),其余位为 \(0\))。这种方式,在数学上更容易说明 \(m=1\) 时该式子成立。 如果 \(m > 1\) 呢?我们该怎样推广这个式子?其实也很简单,只需要对应地用 \(k\) 进制数的思想就好了。 例如,令 \(m=2\):列举出所有的 \(n\) 位三进制数,共 \(3^n\) 个。这些三进制数当中,有且仅有 \(k\) 位不是 \(0\) 的数的数量就是 \(\binom{n}{k}\)(这等价于从 \(n\) 位里挑出 \(k\) 位,令它们为 \(1\) 或 \(2\),其余位为 \(0\))。注意到,每一个非 \(0\) 位都有两种选择:\(1\) 或 \(2\)。所以一个 \(n\) 位三进制数中如果有 \(n-k\) 个 \(0\),剩下的 \(k\) 位就会有 \(2^k\) 种可能性;而“ \(n\) 位三进制数中有 \(n-k\) 个 \(0\)”本身已经有 \(\binom{n}{n-k}=\binom{n}{k}\) 种可能,二者相乘正好就是 \(\binom{n}{k}\cdot 2^k\)。于是,所有的 \(\binom{n}{k}\cdot 2^k\) 相加,应该等于\(n\) 位三进制数的总数 \(3^n\)。 再往下其实也不用推了,思路是一样的。 来源:https://blog.jiejiss.com/

2020/1/11
articleCard.readMore