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

基于eBPF的安卓逆向辅助工具——stackplz

前言

stackplz是一款基于eBPF技术实现的追踪工具,目的是辅助安卓native逆向,仅支持64位进程,主要功能如下:

  • hardware breakpoint 基于pref_event实现的硬件断点功能,在断点处可读取寄存器信息,不会被用户态探测到
    • 前提是需要内核启用CONFIG_HAVE_HW_BREAKPOINT,5.10+内核默认开启
  • uprobe hook 即在用户态对动态库下断点,在断点处可读取寄存器等上下文信息
  • trace syscall 即在内核态对syscall进行追踪,输出syscall信息,无视任何反调试
  • 自动追踪fork产生的进程,可按pid/uid/tid/package等组合进行过滤
  • 在命中hook的时候可发送信号,例如发送SIGSTOP可挂起进程(硬件断点暂未实现发送信号
    • 几乎无视反调试,并且此时可以使用lldb等调试器附加到进程,获取上下文内容
    • 如果进程已经处于相互ptrace的状态,则调试器不可附加,但此时还是可以通过dd等手段获取上下文内容
  • 使用上述功能时,均可回溯堆栈

使用要求:内核 5.10+(开发板+ReDroid也是可以的,当前支持rock5b),该限制主要来自于需要使用bpf_probe_read_user接口

stackplz最初是受到定制bcc/ebpf在android平台上实现基于dwarf的用户态栈回溯启发,加上自己对eBPF技术略有一点研究,然后决定开发这样一款工具

当时核心目的是寻求一种门槛低、不依赖额外环境、能实际落地的方案开发eBPF程序

受限于当时高版本内核安卓设备的普及程度,只实现了寄存器获取与堆栈回溯,限制是内核要求4.14+

经过一年左右的发展,GKI 2.0设备多了起来,各类eBPF相关的内容不断涌现 ,加上之前自己立了flag

于是最近几个月把之前给stackplz立的flag给陆续落地,加上了许多功能,以期能真正将eBPF技术投入实际的安卓逆向分析中

另外KernelSU近期也发布了新的计划:

  • su 使用日志;通过在内核中跟踪 fork, execve 等系统调用,给出调用 su 的进程调用的日志,如时间,命令行参数等。
  • 集成支持硬件断点并提供用户空间接口,为开发和调试提供便利。
  • 在 kernel 中集成 Lua 解释器并为用户空间提供接口;这样可以让内核运行 lua 脚本,等同于无限制的 eBPF,成为 tracing 好帮手。

什么,无限制的eBPF?那真的是太好了! 所以本文再不出,stackplz就要凉啦(>_<)

再次说明要求与限制:内核 5.10+,仅支持64位进程

请前往Releases或者Actions下载使用

1

2

3

adb push stackplz /data/local/tmp

chmod +x /data/local/tmp/stackplz

cd /data/local/tmp && ./stackplz --prepare

注意:每次更新版本请手动执行一次./stackplz --prepare

功能演示

本文演示过程中没有任何APP受到伤害!

写原理太枯燥你们可能就划走了,下次再写,还是先看看实际效果吧~

1. 硬件断点

硬件断点支持四种类型,即r,w,x,rw,断点长度为4字节

命令中必须提供pid,断点绝对地址或者断点偏移+动态库名,下面是两个示例效果:

1

./stackplz -p 37919 --brk 0xf3a4:x --brk-lib libnative-lib.so --stack

1

./stackplz -p 37919 --brk 0x763e6103a4:x --regs --stack --stack-size 4096

说明:

  • --regs 输出寄存器信息
  • --stack 默认获取用户态8192字节的栈上数据用于回溯堆栈
  • --stack-size 可以用于指定要获取的栈上数据大小,必须是8的倍数

如果要给内核空间的地址下硬件断点,可以使用pid+绝对地址的方式,pid给定一个存在的进程即可(不过目前没有回溯内核堆栈的功能,所以给内核空间地址下硬件断点聊胜于无

2. uprobe hook

其原理是替换目标位置动态库的指令为brk指令,执行到目标位置的时候会陷入内核,执行eBPF虚拟机中的代码

美中不足的是,这个brk指令是可以被扫描出来的,但优势也是只有这一个brk指令,没有inlinehook那么多特征

如果确实担心被扫描,那么可以采用硬件断点先检查下^_^

基于该能力,stackplz可以在断点位置读取上下文的内容,在逆向时以最小的侵入力度获取想要的内容,尽可能避开检测

下面是具体效果:

1

./stackplz -n com.sfx.ebpf --point strstr[str,str] --getoff --stack

1

./stackplz -n com.sfx.ebpf -l libnative-lib.so -w _Z5func4i[int] --regs

1

2

3

./stackplz -n com.sfx.ebpf -w open64[str,int] -f w:/data

./stackplz -n com.sfx.ebpf -w open64[str.f0,int] -f w:/data

./stackplz -n com.sfx.ebpf -w open64[str.f0.f1.f2,int] -f w:/data -f b:/sys -f w:/proc

说明:

  • --getoff 计算LR与PC的偏移情况,方便快速定位调用位置
  • -l/--lib 指定动态库路径,默认为libc.so
    • 大多数情况下使用库名即可,如果遇到同名不同路径,或者找不到的情况,可手动指定为/proc/{pid}/maps中的完整路径
  • -w/--point 意为观测点,配合动态库指定符号或者偏移,可设置多个,上限6个
    • 语法是{符号/基址偏移}{+偏移}{[参数类型{寄存器/读取地址},参数类型{.f{规则索引}}]}
    • 一个观测点下最多读取10个参数
    • 一个参数下最多应用6个过滤规则
    • 下面是一些使用组合示例,目前支持的类型:int,uint,int64,uint64,str,ptr,buf
    • -w _Z5func1v
    • -w 0x9542c[str,str]
    • -w strstr+0x4[str,str]
    • -w strstr[str,str] -w open[str,int]
    • -w 0xA94E8[int:x1,ptr:sp+0x30-0x2c]
      • 默认按寄存器顺序读取,也可以参考本示例,在第一个位置读取x1为int,在第二个位置读取sp+0x30-0x2c为指针
      • 没错,可以进行简单的计算,这样能够灵活读取栈上的内容
    • -w write[int,buf:x2,int]
    • -w write[int,buf:32,int]
    • -w write[int,buf:0x10,int]
      • buf较为特殊,规则如下:
      • 如果:后面是寄存器表明以该寄存器大小为长度读取数据
      • 如果:后面是十进制或十六进制字符串,则表明以这个数的大小为长度读取数据
    • -w write[buf:x2:x1,int,int]
      • 这里的规则表示,读取第一个参数时,读的是x1寄存器,长度为x2,即:后的第一个内容表示长度,末尾的才是要读取的寄存器
  • -f/--filter 字符串过滤规则,有三种,分别是w/white b/black r/replace,示例如下
    • 规则位于参数的末尾,多个规则以.作为分隔符
    • 替换规则涉及两个字符串,以:::作为分隔符
    • 示例如下,依次为白名单规则、黑名单规则、替换规则:
      • -f w:/data
      • -f b:/sys
      • -f r:/system/bin/su:::/system/bin/zz
    • -w openat[int,str.f0.f1] 意为对符号openat的第二个参数,应用规则0规则1
    • -s openat:f0 这是syscall,先在这里说了,意为对openat系统调用的第一个字符串参数应用规则0,注意分隔符为:

3. trace syscall

原理是将eBPF程序挂载到raw_tracepoint/sys_enterraw_tracepoint/sys_exit,然后根据预设配置解析参数

追踪syscall的时候自然也是能输出堆栈的,不过限于篇幅,下面的例子中就不演示了,加--stack即可

下面是具体效果:

1

./stackplz -n com.sfx.ebpf -s openat,socket,connect

1

./stackplz -n com.sfx.ebpf -s %file,%net --no-syscall openat,recvfrom

1

./stackplz -n ???.???.??? -s openat:f0.f1.f2 -f w:/system -f w:/dev -f b:/system/lib64 -o tmp.log

说明:

  • -s/--syscall 一般情况,追踪多个syscall要用,作为分隔符
    • 还可以使用下面的内容进行分组追踪,避免手打大量syscall名
      • all %attr %file %exec %clone %process %net %signal %kill %stat
    • 对syscall中的参数进行过滤,示例如下,使用:作为起始分隔符,使用.作为多个规则的分隔符
      • -s openat:f0.f1.f2
    • syscall的参数过滤仅支持syscall的首个字符串参数

4. 进阶使用

下面将演示如何过root检查,虽然对于eBPF来说做这个事情比较鸡肋,但是还是值得演示一下

核心原理就是通过bpf_probe_write_user方法修改内存数据,这里过root检查就是简单替换下相关的字符串

再次说明:本文演示过程中没有任何APP受到伤害!

这里请一位不愿透露姓名的APP做演示,过程如下:

  1. 首先执行第一个命令,该命令意为追踪execveopenat两个系统调用
  2. 打开APP,很快啊,APP就崩溃了;这个时候已经采集到syscall的调用情况了,从日志中检查调用的参数,发现有su相关的访问
  3. 还是执行第一个命令,然后另开一个shell,执行第二个命令,将有关su的访问替换为其他字符串
  4. 打开APP,很快啊,APP还是崩溃了,不过可以发现APP执行得更久一点...
  5. 配合使用--stack--mstack--getoff,进行更准确的分析
  6. 以此往复,最终发现APP还会执行mountwhich su等命令,而且是走的libc.so,于是再开一个shell执行第三个命令

1

2

3

./stackplz -n ???.???.???,iso -s execve,openat -o tmp_s.log

./stackplz -n ???.???.???,iso -s execve:f3,openat:f0.f1.f2 -f r:/sbin/su:::/xx -f r:/system/bin/su:::/xx -f r:/system/xbin/su:::/xx -f r:/system/bin/getprop:::/xx

./stackplz -n ???.???.???,iso -w popen[str.f0.f1] -f r:mount:::mounx -f "r:which su:::which zz" --stack

使用--mstack,该选项意为手工解析堆栈,因为某些情况下默认的方案可能无法给出详细的堆栈(全是unknown

在后面两个命令同时使用的情况下,最终能走到APP的启动页面:

不过使用eBPF过这个检查实在是麻烦...所以stackplz的定位是辅助分析,这里仅仅演示一下eBPF修改内存的能力

对了,APP怎么老是退出,想个办法让它不要退出好不好哇(

1

./stackplz -n ???.???.???,iso -s exit --kill SIGSTOP --stack

这样每次调用exit的时候,都会发送SIGSTOP信号,可以把整个进程挂起

每次挂起后如果要恢复进程运行,那么执行kill -SIGCONT {pid}这样的命令即可

不过有一个需要注意的问题,那就是发信号的时候,syscall还是会执行,也就是说如果在exit这个系统调用处发信号,进程有可能还是没办法挂起

那么可以配合使用--stack--mstack--getoff来判断从用户态的什么地方调用,从上面的结果来看,是libc.so的__exit

那么使用下面的命令进行追踪,这样就可以真正在执行到__exit的时候挂起,上下文也会保持在__exit的状态

1

./stackplz -n ???.???.???,iso -w __exit --kill SIGSTOP --stack

这是lldb附加上去的效果:

这里的iso指追踪isolated进程,-n/--name选项可以给定进程分组,目前的分组是root system shell app iso

5. 其他补充

如果单条命令hook无法一次性实现想要的追踪内容,那么多开stackplz即可,以及同一个位置多次下hook都是支持的

具有黑白名单的选项:

白名单黑名单
-u/--uid--no-uid
-p/--pid--no-pid
-t/--tid--no-tid
--tname--no-tname
-s/--syscall--no-syscall

在某些情况下,stackplz无法解析到偏移、堆栈,可以设置debug和输出日志到文件,然后根据日志中的各类事件信息进行分析

  • --debug -o tmp.log

仅将日志输出到文件,不输出到终端,这对于进行大量追踪的时候,有助于减少数据丢失

  • -q/--quiet

默认情况下,buf类型的数据打印为ASCII + hex,使用下面的选项将打印为hexdump

  • --dumphex

追踪一些特别频繁的调用时,可能会出现部分数据丢失的情况,这个时候请适当增加eBPF的数据缓冲区大小

  • -b/--buffer 默认8M,一般不建议超过32M

更多命令,请执行./stackplz --help查看

6. Q & A

Q: 不能修改寄存器吗
A: 是的,eBPF中无法修改寄存器,只能修改给定地址处的内存数据

Q: 运行不了
A: 首先请使用5.10+内核的设备,如果确实不行请反馈至issue

Q: 技术架构等
A: 语言:Golang + C
A: 三方库:ebpf + ebpfmanager + libbpf
A: 预编译:BTFHubForAndroid + unwinddaemon
A: 其他:ndk clang + Makefile

相关文章:

基于eBPF的安卓逆向辅助工具——stackplz

前言 stackplz是一款基于eBPF技术实现的追踪工具&#xff0c;目的是辅助安卓native逆向&#xff0c;仅支持64位进程&#xff0c;主要功能如下&#xff1a; hardware breakpoint 基于pref_event实现的硬件断点功能&#xff0c;在断点处可读取寄存器信息&#xff0c;不会被用户…...

十大排序——4.堆排序

前面我们讲了堆&#xff0c;现在我们来看一下队排序。 堆排序的步骤&#xff1a; 首先将一个无序数组建立成一个大顶堆然后&#xff0c;将堆顶的元素和堆低的元素进行交换&#xff08;即将最大的元素交换的到堆底&#xff09;&#xff0c;缩小并下潜调整堆重复上一步&#xf…...

独辟蹊径”之动态切换进程代理IP

前言 项目中遇到这样一个需求&#xff0c;需要动态切换指定进程Sockets5代理IP&#xff0c;目前了解到可通过编写驱动拦截或者劫持LSP实现&#xff0c;LSP劫持不太稳定&#xff0c;驱动无疑是相对较好的解决方案&#xff0c;奈何水平不足便有了这"蹊径"。 初步尝试…...

redis漏洞修复:(CNVD-2019-21763)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、漏洞内容二、镜像准备1.确认镜像版本2.下载镜像 三、配置文件准备1.获取配置文件2.修改配置文件 四、启动redis容器五、修改iptables文件总结 前言 漏扫发…...

手刻 Deep Learning -第壹章-PyTorch入门教学-基础概念与再探线性回归

一、前言 本章会需要 微分、线性回归与矩阵的基本观念 这次我们要来做 PyTorch 的简单教学&#xff0c;我们先从简单的计算与自动导数&#xff08; auto grad / 微分 &#xff09;开始&#xff0c;使用优化器与误差计算&#xff0c;然后使用 PyTorch 做线性回归&#xff0c;还有…...

深入学习 Redis - 如何使用 Redis 作缓存?缓存更新策略?使用需要注意哪些问题(工作/重点)

目录 一、Redis 作为缓存 1.1、缓存的基本概念 1.1.1、理解 1.1.2、缓存存什么样的数据&#xff1f;二八定律 1.2、如何使用 redis 作为缓存 1.3、缓存更新策略&#xff08;redis 内存淘汰机制 / 重点&#xff09; 1.3.1、定期生成 1.3.2、实时生成 内存淘汰策略&#…...

好用的软件测试框架有哪些?测试框架的作用是什么?

软件测试框架是现代软件开发过程中至关重要的工具&#xff0c;它可以帮助开发团队更加高效地进行测试和验证工作&#xff0c;从而大大提高软件质量和用户体验。 一、好用的软件测试框架 1. Selenium&#xff1a;作为一种开源的自动化测试框架&#xff0c;Selenium具有功能强大…...

PAT 1035 插入与归并

PAT 1035 插入与归并 题目描述思路讲解代码展示 题目描述 思路讲解 分析&#xff1a;先将i指向中间序列中满足从左到右是从小到大顺序的最后一个下标&#xff0c;再将j指向从i1开始&#xff0c;第一个不满足a[j] b[j]的下标&#xff0c;如果j顺利到达了下标n&#xff0c;说明…...

K-means 聚类算法学习笔记

K-means 聚类算法 是一种无监督学习算法&#xff0c;用来将 n n n 个样本点分成 k k k 类&#xff0c;使得整个数据集的误差平方和 S S E SSE SSE 最小。在本例中&#xff0c;样本点是指平面直角坐标系上的点&#xff0c;聚类中心也是平面直角坐标系上的点&#xff0c;而每个…...

API文档搜索引擎

导航小助手 一、认识搜索引擎 二、项目目标 三、模块划分 四、创建项目 五、关于分词 六、实现索引模块 6.1 实现 Parser类 6.2 实现 Index类 6.2.1 创建 Index类 6.2.2 创建DocInfo类 6.2.3 创建 Weight类 6.2.4 实现 getDocInfo 和 getInverted方法 6.2.5 实现 …...

文案内容千篇一律,软文推广如何加深用户印象

随着互联网技术的发展&#xff0c;企业营销的方式逐渐转向软文推广&#xff0c;但是现在软文推广的内容同质化越来越严重&#xff0c;企业应该如何让自己的软文推广保持差异性&#xff0c;在用户心中留下独特的印象呢&#xff1f;下面就让媒介盒子告诉你。 一、 找出产品独特卖…...

十二、流程控制-循环

流程控制-循环 1.while循环语句★2.do...while语句★3.for循环语句 —————————————————————————————————————————————————— 1.while循环语句★ while语句也称条件判断语句&#xff0c;它的循环方式是利用一个条件来控制是否…...

五、回溯(trackback)

文章目录 一、算法定义二、经典例题&#xff08;一&#xff09;排列1.[46.全排列](https://leetcode.cn/problems/permutations/description/)&#xff08;1&#xff09;思路&#xff08;2&#xff09;代码&#xff08;3&#xff09;复杂度分析 2.[LCR 083. 全排列](https://le…...

什么是分布式锁?他解决了什么样的问题?

相信对于朋友们来说&#xff0c;锁这个东西已经非常熟悉了&#xff0c;在说分布式锁之前&#xff0c;我们来聊聊单体应用时候的本地锁&#xff0c;这个锁很多小伙伴都会用 ✔本地锁 我们在开发单体应用的时候&#xff0c;为了保证多个线程并发访问公共资源的时候&#xff0c;…...

Ubuntu 12.04增加右键命令:在终端中打开增加打开文件

Ubuntu 12.04增加右键命令&#xff1a;在终端中打开 软件中心&#xff1a;搜索nautilus-open-terminal安装 用快捷键CtrlT打开命令行输入&#xff1a; sudo apt-get install nautilus-open-terminal 重新加载文件管理器 nautilus -q 或注销再登录即要使用...

Centos 7 访问局域网windows共享文件夹

Refer: centos7 访问windows系统的共享文件夹_centos访问windows共享_三希的博客-CSDN博客 一、在CentOS中配置CIFS网络存储服务 CIFS&#xff08;Common Internet File System&#xff09;是一种在网络上共享文件的协议&#xff0c;也称为SMB&#xff08;Server Message Blo…...

GDB的TUI模式(文本界面)

2023年9月22日&#xff0c;周五晚上 今晚在看GDB的官方文档时&#xff0c;发现GDB居然有文本界面模式 TUI (Debugging with GDB) (sourceware.org) GDB开启TUI的条件 GDB的文本界面的开启条件是&#xff1a;操作系统有适当版本的curses库 The TUI mode is supported only on…...

深入了解Python和OpenCV:图像的卡通风格化

前言 当今数字时代&#xff0c;图像处理和美化已经变得非常普遍。从社交媒体到个人博客&#xff0c;人们都渴望分享独特且引人注目的图片。本文将介绍如何使用Python编程语言和OpenCV库创建令人印象深刻的卡通风格图像。卡通风格的图像具有艺术性和创意&#xff0c;它们可以用…...

【算法挨揍日记】day06——1004. 最大连续1的个数 III、1658. 将 x 减到 0 的最小操作数

1004. 最大连续1的个数 III 1004. 最大连续1的个数 III 题目描述&#xff1a; 给定一个二进制数组 nums 和一个整数 k&#xff0c;如果可以翻转最多 k 个 0 &#xff0c;则返回 数组中连续 1 的最大个数 。 解题思路&#xff1a; 首先题目要我们求出的最多翻转k个0后&#x…...

华为云HECS安装docker

1、运行安装指令 yum install docker都选择y&#xff0c;直到安装成功 2、查看是否安装成功 运行版本查看指令&#xff0c;显示docker版本&#xff0c;证明安装成功 docker --version 或者 docker -v 3、启用并运行docker 3.1启用docker 指令 systemctl enable docker …...

力扣669 补9.16

最近大三上四天有早八&#xff0c;真的是受不了了啊&#xff0c;欧嗨呦&#xff0c;早上困如狗&#xff0c;然后&#xff0c;下午困如狗&#xff0c;然后晚上困如狗&#xff0c;尤其我最近在晚上7点到10点这个时间段看力扣&#xff0c;看得我昏昏欲睡&#xff0c;不自觉就睡了1…...

2023-9-22 没有上司的舞会

题目链接&#xff1a;没有上司的舞会 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 6010;int n; int happy[N]; int h[N], e[N], ne[N], idx; bool has_father[N];// 两个状态&#xff0c;选该节点或不选该…...

【HDFS】cachingStrategy的设置

org.apache.hadoop.hdfs.client.impl.BlockReaderFactory#getRemoteBlockReader: private BlockReader getRemoteBlockReader(Peer peer) throws IOException {int networkDistance = clientContext.getNetworkDistance(datanode);return BlockReaderRemote...

性能测试 —— 性能测试常见的测试指标 !

一、什么是性能测试 先看下百度百科对它的定义&#xff0c;性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。 我们可以认为性能测试是&#xff1a;通过在测试环境下对系统或构件的性能进行探测&#xff0c;用以验证在生产环…...

【学习草稿】背包问题

一、01背包问题 图解详细解析 &#xff08;转载&#xff09; https://blog.csdn.net/qq_37767455/article/details/99086678 &#xff1a;Vi表示第 i 个物品的价值&#xff0c;Wi表示第 i 个物品的体积&#xff0c;定义V(i,j)&#xff1a;当前背包容量 j&#xff0c;前 i 个物…...

doxygen c++ 语法

c基本语法模板 以 /*! 开头, */ 结尾 /*!\关键字1\关键字2 */1 文件头部信息 /*! \file ClassA.h* \brief 文件说明 定义了类fatherA* \details This class is used to demonstrate a number of section commands.* \author John Doe* \author Jan Doe* \v…...

ChatGLM微调基于P-Tuning/LoRA/Full parameter(上)

1. 准备环境 首先必须有7个G的显存以上,torch >= 1.10 需要根据你的cuda版本 1.1 模型下载 $ git lfs install $ git clone https://huggingface.co/THUDM/chatglm-6b1.2 docker环境搭建 环境搭建 $ sudo docker pull slpcat/chatglm-6b:latest $ sudo docker run -it …...

BLE Mesh蓝牙mesh传输大数据包传输文件照片等大数据量通讯

1、BLE Mesh数据传输现状 BLE Mesh网络技术是低功耗蓝牙的一个进阶版&#xff0c;Mesh扩大了蓝牙在应用中的规模和范围&#xff0c;因为它同时支持超过三万个网络节点&#xff0c;可以跨越大型建筑物&#xff0c;不仅可以使得医疗健康应用更加方便快捷&#xff0c;还能监测像学…...

9.18 QT作业

mainwindow.h QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr);~MainWindow();signals:void jump(); //自定义跳转信号函数private slots:vo…...

【100天精通Python】Day67:Python可视化_Matplotlib 绘动画,2D、3D 动画 示例+代码

1 绘制2D动画&#xff08;animation&#xff09; Matplotlib是一个Python绘图库&#xff0c;它提供了丰富的绘图功能&#xff0c;包括绘制动画。要绘制动画&#xff0c;Matplotlib提供了FuncAnimation类&#xff0c;允许您创建基于函数的动画。下面是一个详细的Matplotlib动画示…...

wordpress二级域名设置/传统营销与网络营销的整合方法

spring要与freemarker整合的话&#xff0c;需要两个包&#xff0c;一个是freemarker的jar包&#xff0c;另一个是spring-context-support的jar包。所以我们需要在taotao-item-web工程中确保对这两个jar包的依赖&#xff0c;如下所示。<dependency><groupId>org.spr…...

免费咨询健康/seo技术博客

一、数据概况 今天分享一份西北地区的POI数据&#xff0c;包含陕西省、甘肃省、青海省、宁夏回族自治区、新疆维吾尔自治区5个省级行政单位。 数据时间为2022年11月&#xff0c;坐标系是GCJ-02。 获取方式在文末&#xff0c;有兴趣的小伙伴自取。 陕西省POI数据可视化西安市…...

免费做国际贸易的网站/免费网站推广优化

原理如下&#xff1a; 代码如下&#xff1a; import pandas as pd import numpy as np import matplotlib.pyplot as pltdef build_data():准备数据:return:准备好的数据data_list []with open(./test.txt) as f:#将所有的元素读成一个列表lines f.readlines()#循环对每一行元…...

电子商务网站建设作业代码/南京seo报价

由于 Linux 是一个多用户系统&#xff0c;同一时刻&#xff0c;系统中运行有属于不同用户的多个进程。那么&#xff0c;当处于某个终端上的用户按下了 CtrlC 键时&#xff08;产生 SIGINT 信号&#xff09;&#xff0c;系统如何知道将该信号发送到哪个进程&#xff0c;从而不影…...

包子店vi设计/哈尔滨seo优化

点击上方蓝色字关注我们~常 错 昨天发了一篇“如何用手机投稿”的文章(点击进入)&#xff0c;今天来讲一讲排版问题。大家不要小看排版&#xff0c;编辑只要点进你的稿子看一眼&#xff0c;瞬间能判断出你是个老手还是新手。如果是纯粹的新手&#xff0c;看一眼可能就点出去…...

免费视频素材网站哪个最好/站长之家工具高清

WINDOWS 7 PRO X86 2015年9月增量补丁包&#xff0c;从Windowsupdate.log中提取的微软官方下载地址&#xff0c;大部分是2015年9月8日发布的&#xff0c;可以通过下载软件批量下载&#xff1a;http://download.windowsupdate.com/d/msdownload/update/software/secu/2015/08/wi…...