2022-2023-2用了我写的奖学金系统v3,当时出了不算少的问题,也收到了不少优化建议。如今新的一轮要开始了,最近想了想,有不少新的方法,我准备再次重构一下。本文讲记录我的部分思考和设计。
这里我将详细介绍在 Windows Server 上跑起 Kubeedge 边缘节点的艰难历程,我评价为举步维艰,一步一个坑,但是最终还是成功了(指启动最基本的Edgecore,调度一个Nginx容器到Windows Server并成功从Windows宿主机通过Pod IP访问,不包含云边通信)
官方文档,讲的真的很好。
总的来说,Windows有两种运行容器的方式:
一种是基于Hyper-V的虚拟机容器,这种容器和Linux的容器不同,是基于虚拟机的,所以性能会比较差,但是可以运行不支持Windows的应用。
另一种是基于进程隔离的容器,这种容器和Linux的容器类似,是基于进程隔离的,性能会比较好,但是只能运行支持Windows的应用。
比如说我们最常用的 Docker Desktop,就是基于Hyper-V的虚拟机容器(运行在WSL2的Linux容器中),此时Windows上运行的容器,都是运行在Hyper-V虚拟机里面的,也正因为这样,我们运行的容器都是linux容器。
基于进程隔离的容器(Host Process Container),这个大家平时在Windows中不太会用到,因为这只有Windows Server支持这种容器。他的实现方式和Linux的容器类似,通过Windows 内核的命名空间功能来提供进程级别的隔离,并使用 Windows Server 上的资源管理器来限制容器的资源使用。性能会比较好,但是只能运行支持Windows的应用(linux/amd64之类的镜像就不能用咯)。
runc是一个用于创建和运行容器的命令行工具。它是Open Container Initiative(OCI)的一部分,OCI是一个由多家公司共同推动的开放标准组织,旨在定义容器格式和运行时的标准。runc实现了OCI Runtime Specification,该规范定义了容器运行时的接口和行为。它负责启动和管理容器,处理容器的生命周期,包括创建、启动、停止和销毁容器等操作。runc本身是一个轻量级的工具,它依赖于Linux内核的容器特性(如cgroups和命名空间)来实现容器的隔离和资源管理。
containerd是一个面向生产环境的容器运行时。它是一个守护进程(daemon),负责管理和运行容器。containerd提供了一组API,用于创建、启动、停止和销毁容器,以及管理容器的镜像和存储等。它的设计目标是提供一个稳定、可靠、高性能的容器运行时,适用于各种容器管理工具和平台。containerd还支持插件机制,可以通过插件扩展其功能,例如支持不同的容器镜像格式、网络插件等。
(linux)runc和containerd的关系是容器运行时和容器管理器之间的关系。runc负责实际的容器运行时功能,而containerd则是一个容器管理器,使用runc来创建和管理容器。containerd通过调用runc提供的接口来操作容器,同时提供了更高级的功能,如镜像管理、存储管理、网络管理等。containerd还可以与其他工具和平台集成,例如Kubernetes,作为其底层容器运行时。
(Windows Server)runhcs是runc的Windows版本,实现的功能和runc一致,在Windows Server中containerd就是调用的runhcs来创建和管理容器。
cni是一个用于容器网络的规范和接口,它定义了容器运行时(如containerd)和网络插件之间的通信方式。CNI的目标是提供一个统一的、可插拔的网络接口,使容器运行时能够与各种网络插件进行交互,从而实现容器的网络配置和连接。containerd如果没有cni网络插件的支持,创建出来的容器就是个玩具,没有任何网络功能。在containerd中可以通过配置cni插件来管理容器的网络,CNI插件可以负责为容器分配IP地址、配置网络路由和防火墙规则等。
sdn是Software Defined Network的缩写,即软件定义网络。它是一种网络架构,通过软件来实现网络的配置、管理和控制,而不是通过硬件设备来实现。在本次实验中,我会使用nat作为节点sdn网络cni插件,而nat的工作方式则是在Windows宿主机上创建一个虚拟网卡,然后将容器的网络流量转发到这个虚拟网卡上,再通过Windows宿主机的网络设备转发到外网。
kubelet是Kubernetes集群中的一个组件,它负责管理节点上的容器,包括创建、启动、停止和销毁容器等操作。kubelet通过调用容器运行时(如containerd)的接口来管理容器。
edgecore(edged)是Kubeedge边缘节点的核心组件,edged是一个裁剪过的kubelet。它的运行方式和k8s上kubelet最大的区别是,kubelet是守护进程,但是edged不是守护进程,它随edgecore启停。
ctr和crictl是与容器运行时相关的工具,用于管理容器和容器镜像。ctr是和containerd一起分发的,它是containerd的客户端工具。
crictl是一个用于与cri兼容的容器运行时进行交互的命令行工具。crictl需要自己下载。
假如你不懂内在区别,我们可以理解crictl为给k8s用的命令行工具(最直观的感受就是,crictl的命令格式和kubectl十分相似。同时,ctr有namespace的概念,但是crictl没有,因为通过crictl执行的操作都在k8s.io里)。
为什么ctr和crictl彼此看不见对方拉的镜像?命名空间的问题咯,直接crictl pull的镜像,是在k8s.io的命名空间里,而ctr pull的镜像,是在default命名空间里。
一台云服务器,在境外,系统是Ubuntu22,将作为k8s集群的master节点,并部署 Kubeedge1.14.1 Cloudcore。
一台 Windows11 电脑,装有VMWare Workstation Pro 16,创建立一个 Windows Server 2019 的虚拟机,将作为Kubeedge windows node。
这里不是重点,粗略带过。因为我用了境外主机,所以不存在什么网络问题,直接按照k8s和kubeedge官方文档部署即可。
crictl config runtime-endpoint unix:///run/containerd/containerd.sock
。如果你要用crictl调试k8s,那么必须这样配一下,否则crictl会报错找不到runtimeapt-get install -y kubelet=1.24.14-00 kubeadm=1.24.14-00 kubectl=1.24.14-00
,因为kubeedge当前最新版本已经支持k8s1.24.14,所以我们就用1.24.14版本的k8s,这样可以避免一些版本兼容性问题。kubeadm init --control-plane-endpoint=<公网IP>
。至于为什么要指定这个参数,这不得不扯一下国内云厂商的这个弹性公网IP了,总之就是,你拥有的公网IP不会直接出现在机器的网卡上,而是通过一种叫做弹性公网IP的技术,将你的公网IP映射到机器的网卡上,直接ifcongig
,你会看见自己的ip是一个虚拟内网的IP,这会影响到k8s的初始化。如果你要跨虚拟网络部署集群,那么这里的配置会很麻烦,甚至包括手动修改etcd的yaml,配不好的话网络工具像flannel会直接烂掉,这里先不详细介绍了--pod-network-cidr
指定当前节点的子网如10.244.0.0/16
,不然后面要手动执行kubectl patch node [node name] -p '{"spec":{"podCIDR":"10.244.0.0/16"}}'
来完成分配(10.244.0.0/16
是fannel默认的子网,当然你也可以换了它,但是在部署flannel的时候需要修改配置)kubectl taint node [node name] node-role.kubernetes.io/control-plane-
和kubectl taint node [node name] node-role.kubernetes.io/master:NoSchedule-
即可keadm init --advertise-address=<公网IP>
,这里显式指定IP还是因为弹性网卡的问题keadm gettoken
拿到密钥,边缘加入做准备我们要做的工作就是,安装containerd,安装cni,运行edgecore。命令行我们使用Powershell
版本暂时请用1.6.20,因为我在使用1.6.22的时候踩了莫名其妙的坑(containerd死都加载不了cni插件,containerd始终报错说no network config found in /etc/cni/net.d: cni plugin not initialized
),同时也请先不要用1.7.x,和1.24.x的k8s windows不匹配。至于安装过程,微软有给出一套安装脚本文档地址,containerd仓库里也有一套Github地址,以及k8s的sig-windows-node也有一套Github。经过我的多次踩坑,我推荐最后一种,因为这个脚本里的安装路径能很好配合后面cni和kubelet等程序的运行(不然大概率跑步起来)。下载Install-Containerd.ps1
后,执行.\Install-Containerd.ps1 -ContainerDVersion 1.6.20 -skipHypervisorSupportCheck
,根据提示重启后再执行一遍这个命令即可(因为启用了一些系统功能,需要重启)。此脚本会帮我们装好containerd(不包含cni)和crictl
containerd的仓库里有个install-cni的脚本,里面包含了下载cni并生成默认配置文件,但是事情抽象在是个sh脚本,里面还夹杂舍powershell脚本,总之我是跑不起来。所以这边我们自己装。https://github.com/microsoft/windows-container-networking 这是仓库地址,但是我暂时不建议直接从这里下载release,因为踩坑了x,并且他没有给默认配置,指下载这个仓库中的release可能会一脸懵逼(网上零零散散的配置事例很乱,甚至还有issue里教你自己创建一个SDN网络给cni用,但是实际上完全是多余的)。
windows-container-networking中也有一个配置样例,但是先别用!
这里我推荐直接从containerd仓库的release中下载和containerd版本相匹配的带有cni的包,从中解压出已经打包好的cni二进制文件和配置。虽然containerd文档中提到不需要下载cri-containerd-(cni-)<VERSION>-<OS-<ARCH>.tar.gz
,并且还说「The cri-containerd-… archives are deprecated, do not work on old Linux distributions, and will be removed in containerd 2.0.」,但是由于我在自行安装cni的过程中踩了坑,所以这里还是极力建议本实验选用1.6.20带有cni的包下载地址。
解压后cni文件夹中存在cni/bin
和cni/conf
,把他们放到c:\Program Files\containerd\bin
和c:\Program Files\containerd\conf
下即可(如果使用sig-windows-node的脚本,那么就是这个路径,但是如果使用其他安装containerd的脚本,配置文件里的cni路径可能不是这个,但是在代码里却写死了地址是c:\Program Files\containerd
,这里是大坑)。
有一个要修改的地方,release包中给出的配置文件,gw不在子网里,潜在的结果可能是子后面部署Pod后,主机ping不通PodIP
还有一个要改的地方是dns配置,样例中capabilities.dns=true
,即将使用运行时的DNS配置覆盖其他设置,但是我们的运行时并没有配置dns,导致的结果就是在容器里无法使用dns解析,能ping通外部ip但是无法实现域名解析,解决方法是把这行删了,并手动给出Nameservers(这可能不是常规行为),我直接写了宿主机IP,当然也可以写8.8.8.8
或者114.114.114.114
这种公共DNS。
最终配置文件如下:
1 |
|
最后记得重启一下containerd。
此时输入ipconfig
不会有任何新的虚拟网络出现,不用担心,在containerd调用cni插件创建容器的时候,会自动创建虚拟网卡并绑定到容器上。
如何测试安装成功?使用ctr
工具创建一个pod出来试试。
1 |
|
如果报错cni not initialized,那么就是cni没装好
如果有输出,可以看见ipconfig会输出一个他的IP(在我们分配的子网下),那么就是cni装好了,并成功运行,如上图所示。
现在在宿主机运行ipconfig
,会看见多了一个虚拟网卡,这就是cni创建的,名字叫vEthernet (nat)
同时,在Powershell中执行Get-HnsNetwork
,也可以看见更详细的虚拟网络信息(如果命令不存在,从Github Microsoft SDN仓库下载脚本安装一下):
此时可以看看路由表,cni已经为SDN网络创建了路由:
这一步是本次操作的核心。
先是准备环境,goland+golang1.20,拉源码。然后建议配置Goland忽略vender,使用module
然后修改egde/pkg/edged/config/config.go
的ConvertConfigEdgedFlagToKubletFlag
函数,增加两行:
1 |
|
WindowsPriorityClass
参数是windows上运行Kubelet必须的,不填会Panic,可填写类型看Windows 文档
还有一个要临时修改的地方,位于egde/cmd/edgecore/app/server.go:179
:
1 |
|
当我们不开metaserver
的时候会检查宿主机是否运行了kubelet和kube-proxy,但是这里的检查是有问题的,这里的process.Name()
很可能会出错,有些进程没有名字。暂时把return err
改成continue
(前提是我们必须保证我们确实没在宿主机装这些东西)。
编译前需要准备环境,安装要mingw-64,不然编译不了sqlite相关(一定是64位,不能是32位,否则运行的时候sqlite相关会无法启动)。
接下来编译成可执行文件,在项目根目录执行:
1 |
|
可能需要一些别的环境变量,我不太清楚,我是直接用goland的编译工具编译的,可能要开CGO,因为sqlite需要CGO。
编译成功后,执行.\egdecore.exe --minconfig
生成一份最小配置文件写入到文件config.yaml
。我这里提供一个运行过后edgecore重新生成的完整配置(大部分模块都没有开,核心是测试edged):
1 |
|
由于大部分参数还没有摸透,我是改了几个地方让它能跑,所以不排除此配置文件中部分参数是处于不生效的状态。
和默认配置相比,需要改的不仅是IP地址,还有几个比较关键的地方:
C:\
,并修改分隔符为\
,因为在windows上,/
是不被允许的,会报错。(文件中有还是/etc
的,但是该模块都没开,所以不影响,以后要用的时候还是要改的)windowsPriorityClass
参数,我给的值是NORMAL_PRIORITY_CLASS
,具体可选项可以看上文给出的微软文档npipe://./pipe/containerd-containerd
mcr.microsoft.com/k8s/core/pause:1.2.0
,理论上保持不变也不会有问题,但我这里出问题了/etc/resolv.conf
,这个文件在windows上也是不存在的<token>
改成keadm拿到的token然后就可以运行了,.\egdecore.exe --config config.yaml
,如果一切正常,会看见如下输出
此时在云端执行kubectl get nodes -o wide
可以看见:
1 |
|
由于网络插件nat早已安装成功,所以node直接变为Ready状态
这一步我有被折磨到,很难想象这个镜像有多难打
我试图去网上找现成的镜像,微软官方在Github给出了Dockerfile,但是亲测跑不起来,所以无奈只能自己造了。
Windows镜像和Linux镜像的区别在于,Windows容器必须以Windows系统作为基础镜像:
1 |
|
Dockerfile中没有写CMD了,因为这边有大坑,一旦没写好,就会看见这样的报错:
1 |
|
然后陷入CrashLoopBackOff,无法启动,从各个Issue来看,原因尽然CMD写错了,系统找不到你的启动命令,类似于linux容器没有找到/bin/sh
这里我复制了一个Bat文件进去(这不是一个优雅的做法,还在找解决方案):
1 |
|
为什么要这样,为什么不直接运行nginx.exe
?这我难说,我自己在宿主机测试nginx的时候,批处理会阻塞那,但是一旦在容器里,直接就退出了!所以无奈只能临时写个死循环,让容器不退出。
现在制作镜像,我们 需要使用buildx
交叉编译出windows/amd64版本的镜像,在一个装有docker和buildx的机子上执行:
1 |
|
记得先登录docker,这样打好的镜像就能直接推到仓库了。
ps:其实tag不应该加hostprocess
,hostprocess指的是类似linux特权容器的东西,可以到宿主机命名空间运行,具体文档看 https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/create-hostprocess-pod/。但我最开始没了解这个,想当然加了这个,其实是错误的x。
最后编写nginx.yaml:
1 |
|
CMD命令请严格按照["cmd", "/c"]
开始,由于没有在Dockerfile中没有把powershell程序的位置c:\windows\system32\WindowsPowerShell\v1.0
加入环境变量,所以不能直接运行powershell,只能用cmd来启动容器。
最后在Master执行kubectl apply -f nginx.yaml
,等待长时间的Pending状态(在拉镜像),然后在云端执行kubectl get pods -o wide
,可以看见Pod已经开始运行,并且已经成功分配了IP:
当然这个IP从云端是ping不通的,因为我们还没有部署Edgemesh,但是可以在Windows server宿主机上ping通:
在主机访问映射的主机端口9002,可以看见nginx的欢迎页面:
至此,测试成功!🎉
starting container fails with: The system cannot find the file specified.: unknown
:https://github.com/containerd/containerd/issues/6300一个小 Handbook,避免每次找文档。教你如何从一台全新的只装有远程桌面的 windows server 开始,快速搭建 k8s windows node。
文章还没写完,研究的时候出了问题。。。请不要浏览安装containerD后的内容。
当启动了一台全新的 windows server,第一件事是安装 SSH Server
https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_install_firstuse?tabs=powershell
1 |
|
好了,现在可以用 ssh 连接了。使用本地 SSH 工具登陆成功后,记得退出远程桌面,以免影响后面的脚本执行。
Microsoft 官方文档两条命令(一条是下载脚本,一条是执行)安装完基于进程隔离的 containerd 和 cni:https://learn.microsoft.com/en-us/virtualization/windowscontainers/quick-start/set-up-environment?tabs=containerd
1 |
|
安装的最后一步会重启电脑,重启后,使用 ssh 连接,执行 ctr version
,如果能看到版本号,说明安装成功。部分设备自动重启后执行不了此命令,很奇怪,但是经过测试,再执行一下.\install-containerd-runtime.ps1
就好了
下面拉个镜像试试:
后面的 2019 根据自己的主机版本决定,如果你是 windows server2019,就不要妄想能拉 2022 的镜像了
1 |
|
不出意外的话不会出意外,顺利拉取镜像。
https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/
这里的版本请对齐 master,否则可能会出现版本不匹配的问题。
1 |
|
由于这个可执行程序需要放到 PATH 下,我懒的去找 PATH,就直接放到了 C:\Program Files\containerd\bin 下,这就是安装 containerd 的脚本默认的安装目录,已经被加入了 PATH。
测试一下:
1 |
|
k8s 官方文档并没有给出 windows 的安装方法,不过看看 kubelet 的下载地址,推倒一下:
1 |
|
同样是下载到 C:\Program Files\containerd\bin 下。
windows 成功加入 master 节点,flannel 和 kubeproxy 的 windows sig 版本成功调度到 windows node 上,似乎 kubelet 有一点问题,容器陷入 CrashLoop ,目前还在研究中。。。
windows 的 containerd 需要修改沙箱 https://github.com/kubernetes-sigs/sig-windows-tools/issues/76
windows 官方 containerd 安装脚本 https://github.com/microsoft/Windows-Containers/blob/Main/helpful_tools/Install-ContainerdRuntime/install-containerd-runtime.ps1
k8s sig windows 配置一把梭脚本 https://github.com/kubernetes-sigs/sig-windows-tools
相同报错 Issue,但解决方案不适用 https://github.com/kubernetes-sigs/sig-windows-tools/issues/337
k8s Windows Node Guide https://kubernetes.io/docs/concepts/windows/intro/#kubelet-compatibility
在旧版本 k8s 上需要特殊配 coredns 仓库地址 https://groups.google.com/a/kubernetes.io/g/dev/c/DYZYNQ_A6_c/m/oD9_Q8Q9AAAJ?pli=1
安装指定版本 kube* https://blog.csdn.net/roxxo/article/details/103146865
]]>妈的!留着备用
route -n
iptables -L -n
iptables -L -n -t nat
tcpdump -i flannel.1 port 8080
iptables -t nat -A OUTPUT -d 121.8.210.236 -j DNAT –to-destination 192.168.191.236
]]>国内云服务,快速部署k8s,笔记(debian为例)
https://github.com/containerd/containerd/blob/main/docs/getting-started.md
k8s 从1.24起不再支持docker,因此这里我选择使用 containerd。选择 containerd作为运行时的组件,它调用链更短,组件更少,更稳定,占用节点资源更少
根据文档,按照需求配置内核模块,配置完成最好重启
https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/#install-and-configure-prerequisites
下载二进制文件 containerd-<VERSION>-<OS>-<ARCH>.tar.gz
,地址 解压到 /usr/local
:
1 |
|
注意: 不要下网上其他教程所说的包名中含有「cri」的二进制包,如cri-containerd-....
、cri-containerd-...
,那已经过时了,最新的版本中cri已经集成进 containerd-<VERSION>-<OS>-<ARCH>.tar.gz
然后配置systmed自启动:
1 |
|
runc用来创建和运行容器,containerd作为常驻进程用来管理容器
Download the runc.
1 |
|
用来管理容器网络
Download the cni-plugins-
1 |
|
官网教程并没有这个步骤,但是值得注意的是,不手动配置containerd镜像源,大概率会导致containerd无法拉取pause镜像(我不是很理解,使用kubeadm可以指定镜像源并拉取pause,但是此操作并没有影响containerd再拉取一个pause镜像)
使用此命令生成默认配置文件
1 |
|
然后讲配置文件中的sand_box
,从k8s.gcr.io/pause:$version
改为registry.aliyuncs.com/google_containers/pause:$version
当然也可以直接修改配置文件中的镜像源,但是我没有成功,报错404,配置如下:
1 |
|
官网教程要求从google源安装,但是国内是无法访问的,我们可以使用国内阿里云镜像源来安装
https://developer.aliyun.com/mirror/kubernetes/
1 |
|
kubelet 现在每隔几秒就会重启,因为它陷入了一个等待 kubeadm 指令的死循环。
新建一个文件夹,免得东西搞乱掉
1 |
|
kubeadm 命令允许使用命令行指定各个参数,但是参数太多了,不如直接写成一个配置文件
1 |
|
这是我的,当然可以使用 kubeadm config print init-defaults
自己写一份,具体字段可以去 github 仓库里类型定义里找
1 |
|
某些云厂商不会把外网ip绑定在网卡上,因此在初始化 etcd 的时候会出问题(ETCD默认监听外网ip)
执行ifconfig
查看网卡,如果没有绑定外网ip,可以输入ifconfig eth0:1 [外网ip] netmask 255.255.255.0
添加一张虚拟网卡
一切准备就绪,使用 kubeadm 初始化集群
1 |
|
一段时间后输出加入集群的指令后,配置完成,请记住join命令,加入节点或创建高可用集群会用得到
现在还无法使用 kubectl,因为kubectl的集群还没有配置
1 |
|
现在执行 kubectl get nodes
,可以和集群联系了!
这里使用 flannel
虽然可以直接使用 kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
,但是为了能方便的进行操作回滚,我选择先下载到本地
1 |
|
当网络插件配置生效,执行kubectl get nodes
,可以看见节点已经 Ready,在此之前一直是 NotReady 状态
目前我们只有一个单节点,如果想部署服务,集群是默认master不可调度的。我的节点名称叫master-shanghai
,使用kubectl describe node master-shanghai
可以看见,污点字段写着不可调度:
我们清除这个污点:
1 |
|
污点消失:
官方教程中,nginx-ingress 使用的镜像依旧是国内访问不到的 registery.k8s.io,但是并没有官方的镜像源。
不过民间出现了热心市民,做了镜像仓库,我们可以在github的issue中找到需要的镜像,并替换进 YAML 文件中
以下命令 1.4.0 替换成自己的合适版本,版本对应关系可在对应关系表查看
1 |
|
值得一提的是,如果是单节点,并且没有执行上一步的清楚污点操作,当查看 service,会发现 nginx-service 一直处于等待状态
查看 pods 状态,可见启动成功
1 |
|
平时项目一直会用到 Redis 做缓存,只用到了皮毛,这里具体记录一下 Redis 更多的用法,以及一些应用场景如排行榜、附近的人。
Redis 是一个开源的存储引擎,一般用作数据库、缓存、消息代理和流引擎。Redis 提供丰富的数据结构,包括但不限于:string、hash、list、set、zset(sorted set)、bitmaps、hyperLogLog、地理位置索引和流记录。Redis 支持持久化,可将数据持久存储在磁盘中,并可以通过部署 Redis 集群保证服务高可用。Redis 的所有操作都是原子性的,还支持对几个操作完成后的原子性执行(事务)。
我们可以借助 Redis 实现许多关系型数据库无法胜任的任务,下文细细道来。
String 是 Redis 最基本的数据类型,键默认最长 512MB,可以用来存储文本、二进制数组、序列化对象等数据。
基础命令:set get setnx mget incrby
set [key] [value]
:get [key]
:setnx [key] [value]
:mget
:incrby
:
说到分布式系统离不开的话题 CAP理论 && BASE理论
分布式数据存储一般是指将数据以复制的方式存储在网络中的多个节点上。CAP的名称是分布式存储的几个关键点,也因此得名。
CAP理论就是针对分布式数据存储的一种理论,其想表达的含义是:当网络或节点发生故障的情况下,系统可以保证可用性、一致性和容错性中的两种,但不能三者同时保证。简单看一下CAP指的是什么:
Every read receives the most recent write or an error
从分布式数据库的任何节点查询数据,得到的都是相同的结果,即不会存在向某个节点提交数据了以后,在其他节点未同步更新的情况。如果在某个节点发生了数据的写入,系统必须同步至其他所有节点,因此无论系统连接到了哪个节点,获取到的也应该是相同的信息。但是在全球范围内维护一套绝对一致性的系统几乎不可能,因为数据传输的每一步都有可能出现问题(人们一直是在不稳定的环境中打造稳定的系统),因此我们的目标是让数据的传输同步尽可能的快(用户无感)。
银行系统就是一个需要强一致性的系统,毕竟没有人希望在ATM存完钱后,手机银行里查不到增加的的余额吧。
Every request receives a (non-error) response, without the guarantee that it contains the most recent write.
任何请求都必须得到响应。如果请求了没有烂掉的节点,那么节点必须给出回应,即使给的不是最新的数据、即使操作失败了。
朋友圈动态需要高可用,即使数据不是一致的或是最新的(空白的朋友圈会降低用户体验甚至失去用户)。在拉取动态的时候幸运选中了出现故障的节点,那么即使此节点无法同步最新的数据,它也应该返回过时数据。
**The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes.**如果两个节点之间通讯发生了中断,这时理解为他们之间产生了分区。如果我们无法成功从某一个节点查询到信息,这就说明此系统没有分区容错性。拥有分区容错性意味着,如果无法从某一节点拿到需要的数据,我们依旧能够在其他可用节点找到副本,并检索到相应数据。容错性是通过提高数据冗余度实现的,即将数据备份多个在不同节点上。不难想到,分区容错性对于任何分布式系统都是必须的。
CAP理论描述 C、A、P 三者只能同时满足两个,通过上面的介绍不难知道对于分布式存储系统,P是必须的,这就导致一致性和可用性不能同时拥有(放弃P的情况一般出现在单工作节点的系统中,因为这类系统不提供分布式,自然没有分区)。
在某些分布式数据库中,如 MongoDB、Hbase,是强调一致性的。在 MongoDB中,每个主节点都有多个副本集,它们使用各自主节点的操作日志文件异步更新数据。每个节点间都会有心跳包确认存活,当节点离线时,需要选择新的 Leader,此时系统对用户是不可用的。
以 Cassandra 为例,这是一个无主节点架构的的分布式点对点数据库,每个节点都接受读写请求。Cassandra 里有个概念叫复制因子,系统中所有节点环形相连,复制因子决定了副本数,如果复制因子为5,则意味着将顺时针在五个节点中保存副本。因此,Cassandra 容忍数据的不同版本存在,当用户查询数据的时候可以指定一致性级别,如果是 ONE 则系统会找到最近的一个副本返回(可能不是最新的),如果是 QUORUM 系统会查找所有副本确保返回最新的数据,在此过程中,Cassandra 内部会有另外一套流程确保副本同步。
一致性和可用性并不是非黑即白的,往往是可调的。上文对 MongoDB 和 Cassandra 的例子是在默认配置情况下展现出的 CP 和 AP,我们可以通过调节配置,让系统以我们的期望运行,合适的才是最好的。
不过不管是AP还是CP,都会以特有方式达成BASE理论中的「最终一致性」
BASE 理论是对 CAP 理论的延伸,核心思想是即使无法做到强一致性(Strong Consistency,CAP 的一致性就是强一致性),但应用可以采用适合的方式达到最终一致性(Eventual Consitency)
分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。在收到请求后,即使出现错误,可以使用不同策略,返回部分数据。如购物网站在拉取首页瀑布流的时候,个性推荐出现了问题,也应该返回一些基础的商品。
在接收到写入等操作请求后,允许一些中间状态存在,容忍不同节点间数据的 不一致,而此中间状态不会影响系统整体可用性。分布式数据库的数据同步过程就是 soft state 的体现。
软状态是有期限的,随着时间的推移,最终能达到一致的状态,这个时间取决于网络、负载等诸多因素。
假设现在有两个服务:订单服务和支付服务,现在来描述一次用户下单过程:
在此示例中,Payment 服务能够响应 Order 服务,尽管中间出现了支付失败的情况(基本可用)。Order 服务能够根据 Payment 服务发来的信息的更改其本地支付状态。并且 Order 和 Payment 服务会在某一刻存在不同的状态(软状态),但是最终状态会变为支付成功(最终一致性)。
]]>新学期开始了,又报了 ACM,这次会不会中途跑路呢?坚持更新!
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
这里要考虑如果输入不是数字该如何处理
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
Problem Description
1 |
|
Input
1 |
|
Output
1 |
|
Sample Input
1 |
|
Sample Output
1 |
|
1 |
|
摘要
首页显示摘要内容(替换成自己的)
1 |
|
参考文章:
参考链接
摘要
Docker操作备忘录
当利用 docker run
来创建容器时,Docker 在后台运行的标准操作包括:
1 |
|
以 Ubuntu 为例:
1 |
|
随后容器会被终止
1 |
|
容器是否会长久运行,是和 docker run
指定的命令有关,和 -d
参数无关。
1 |
|
这样可以进入容器的交互终端
1 |
|
使用 exec 进入容器后使用 exec 退出容器不会造成容器关闭
1 |
|
1 |
|
1 |
|
1 |
|
参考文章:
参考链接
摘要
2021 HCTF WEB HCTF INTERNAL SYSTEM 复现(失而复得的 MD)
题目已上线 [BUUCTF](https://buuoj.cn/challenges#[虎符CTF 2021]Internal System)
打开环境,直接是一个登陆页面:
随便测试一下,这里看到,如果密码错误,还会返回登陆页面,并且登陆信息是通过 GET 请求上传的:
测试无果,查看源代码,发现注释:
进入/source
, 发现 nodejs 代码;
1 |
|
目前对 nodejs 知之甚少,于是先慢慢看这段代码。
url.parse()
函数返回一个 url 对象:
好了,重新回到题目,从/login
下手
要想进入/index
页面,账号密码符合的条件是:账号密码都存在,账号密码不相同且长度也不相同,并且账户名不能是 字符串admin.
感谢诸位大佬, 一个新知识点出现了。在 js 中:
1 |
|
所以观察 adminHash 的构造,可以很容易的绕过登录判断:
1 |
|
于是就可以构造 payload:
1 |
|
成功登录!!!
观察一下 /index
是什么鬼东西。这里的 URL 搜索框提交的数据直接去 /proxy
来进行处理,有 post 和 get 两种方法。
观察一下 post 方法的处理过程,不难发现无论提交了什么样的 url 最后的代理请求 url 都会变成 http://127.0.0.1:3000/search?url=https://postman-echo.com/post
,所以这里其实是没有操作空间的,因此只能寄希望于 get 请求。
当向 /proxy
发送了 get 请求后,后台首先判断你是不是 admin,只能 admin 才能提交请求。然后后端会解析提交的 url 值,对这个 url 进行三次 waf 检测,只有全部通过才能继续接下来的操作。
对于 SSRF_WAF 函数,首先将要搜索的 url 中的 hostname 提取出来,然后删掉所有的方括号(我猜是为了防止使用 MAC 地址?)。举个例子,如果说 url=http://127.0.0.2:9080/api/xxx
,那么最后的结果就是 host = 127.0.0.2
。然后如果 host 是 IP 地址 并且不是内网地址,才能返回 true
对于 FLAG_WAF 函数,会检测 url 的 pathname,对于上一段的例子 pathname 就是 /api/xxx
,如果 pathname 不是以 /flag
开头就返回 true
最后一个 OTHER_WAF 函数,存在意义不大哈哈哈
就这三个 waf 如果全部通过,才能执行后续代码。
如果检测全部通过,我们提交的 url 会转发到 /search
页面,也就是http://127.0.0.1:3000/search?url=${url}
,那么接下来再看看 /search
的代码。
ummmmm,首先检测发起请求的 IP,必须得从内网 127.0.0.1
来发起请求才能继续执行代码,所以我们直接访问这个页面是无效的:
接下来,常规解析url 参数。然后对请求方式进行判断,如果对/search
发起的是 post 请求,那么后台就会同样用 post 的方式去请求我们要的 url,同样的如果是 get 请求,那么后台就会同样用 get 的方式去请求我们要的 url 。回到 /index
页面的代码,发现我们对 /proxy
发起了 post 请求,那么/search
发起的也是 post 请求,对 /proxy
发起了 get 请求,那么/search
发起的就是 get 请求。BUT 显然 post 对我们毫无用处,因此 和 post 请求有关的代码都不用看。
随后后台会直接访问由 /proxy
转发来的 url 地址,并把得到的响应展示出来。
比方说我来搜索我自己服务器IP地址:
得到了正确的响应,和直接浏览器里访问一模一样:
当然如果搜索像 http://127.0.0.1/
, http://127.0.0.1/flag
这样的地址是肯定要被 waf 的。
but 目标很明确我们得去/flag
页面,因为那有 hint:
同样的,这里也必须是内网来访问。
思考一下,前面的 waf 好像有漏洞,如果参数 url 来个套娃,其实waf是检测不到套娃里有没有内网地址或者flag的。
但是又出现了新问题:如果要实现套娃来获得 /flag
的内容,必要条件是对 /proxy
发起的搜索 url 必须是以 内网地址开头,但显然内网 ip 会被 waf。
在实际应用中,一般我们在服务端绑定端口的时候可以选择绑定到 0.0.0.0,这样我的服务访问方就可以通过我的多个ip地址访问我的服务
而 isPublic(“0.0.0.0”) //true
所以直接可以搜索 http://0.0.0.0:3000
,这样子就成功绕过了 内网waf 检测:
于是我们就可以去访问有内网限制的地址了,直接构造 http://0.0.0.0:3000/search?url=http://127.0.0.1:3000/flag
成功获得了 /flag
中的 hint,然而并没有 flag…
hint 是 内网里还有个 Netflix 服务器。
那么下一步我们要找到服务器在内网的地址。想起来刚刚登陆成功的时候,显示了几行字没有用上:
忙猜应该是在 10.0.130.8 ~ 10.0.130.24 或者是 10.128.0.99 ~ 10.128.0.99,这得扫描一波。但是并不知道具体端口号,就很烦。
去网上看了些 Netflix 的安装教程,发现 Netflix 默认安装在 8080 端口,那么就写个脚本试试吧:
1 |
|
找到了找到了,Netflix 服务器再 10.0.130.14
这就是 Netflix 的文档页。
下一步应该是查看版本,经过了解,版本号应该是藏在/api/admin/config
那就去看看:
版本2.26.0
去看了下网上的
参考文章:
参考链接
摘要
本文主要介绍研究 EMP 的过程和成果
在法律的边缘疯狂试探
[TOC]
假如一个人 “商场里的娃娃机有没有什么作弊技巧”, 一般人会说:
但是这些只能算技巧, 有一种工具,使用纯物理方法可以直接破解娃娃机,实现不付钱,娃娃机随便玩, 这种工具甚至还能破解少部分电子门禁和自动贩卖机.
还有个神奇的用处是, 隔空点灯
如果人在电磁脉冲发生时接近受影响的电子设备,则可能因为瞬间产生的超高电压而灼伤、休克,甚至死亡。
电磁脉冲炸弹被认为是21世纪规模最大的破坏性武器,可以对电子信息系统、指挥控制系统和网络信息系统产生巨大威胁,号称信息时代的“第二原子弹”。
世界各军事强国的电磁脉冲打击武器正在走向实用化。美国海军作战部部长拉夫·黑德上将曾说,这种电磁脉冲炸弹是“革命性的作战武器”,“决不能让美军错过了这种武器”。
据美国媒体5月报道,多年来,作为“反电子设备高功率微波先进导弹工程”(Champ)的一部分,科学家一直想创造出这样的武器。而美国空军近日宣布,他们已经掌握了该项先进技术,并可以配合隐形联合空地防区外导弹(JASSM)一起展开部署。而Champ将成为一种改变战争面貌的远射武器。由于轰炸机和战斗机都可以发射JASSM导弹,JASSM导弹将成为部署Champ的理想平台。
早在2012年,波音公司就曾在一次长约一小时的飞行中对该武器进行试验,成功使整个军营的计算机陷入瘫痪状态。在波音公司进行的试验中,导弹低低地飞过犹他测试与训练场上方,向七个目标发射了电磁脉冲,结果永久摧毁了它们的电子系统。波音公司表示,这次测试已经成功到“没有办法用摄像机进行记录”。
日本防务专家凯尔·沟日前在美国《国家利益》网站上发表了《5种永远不该使用的武器》的文章,电磁脉冲武器赫然位列其中。他说,大多数战争武器,从手枪到战斗机,通常都被认为是合法的,而且(考虑到使用它们的目的是枪杀)还算是相当仁慈的战争工具。然而有一些武器(无差别和不人道的杀人武器)有可能打击到非战斗人员,甚至影响全球范围的人类文明。还有一些武器,会给人造成难以忍受的伤害,导致长时间痛苦并死亡。
虽然电磁脉冲对生物体几乎不会造成直接影响,但如果在电磁脉冲发生时接近受影响的电子设备,则可能因为瞬间产生的超高电压而灼伤、休克,甚至死亡,同时也可能因为电子设备受到电磁脉冲影响而短路,造成爆炸或火灾等伤害。
1963年,美国在太平洋海岛约翰斯顿岛上空400公里处进行核爆试验,距离其数千公里外的檀香山、夏威夷群岛却深受影响,警报器、电子通信监视指挥系统相继失灵。
电磁脉冲炸弹的产生源自美苏两国进行的氢弹引爆试验。1963年7月9日,美国在太平洋海岛约翰斯顿岛上空400公里处进行空中核爆试验,距离其数千公里外的檀香山的数百个警报器全部失灵,瓦胡岛的照明变压器全部被烧坏,檀香山与威克岛的远距离短波通信也突然中断。
此外,距离爆炸中心投影点1300公里的夏威夷群岛上,美军的电子通信监视指挥系统也相继失灵,整个岛上防盗警报器响个不停,街灯熄灭,电器被烧毁。甚至连距离爆炸中心5000公里的澳大利亚都受到了影响。无独有偶,苏联进行的相关氢弹爆炸试验也导致数千公里内的电子设备被烧毁,甚至苏军的雷达也被烧坏,通信线路全部中断。
经过数年的研究,人们终于发现,原来氢弹类的原子弹爆炸不仅会产生冲击波、光辐射、核辐射和放射性污染,还会产生电磁脉冲效应。氢弹爆炸产生的密集的α射线和γ射线会从大气中电离出大量高速运动的电子,继而在空中产生强大的电场。在爆炸中心附近几公里内的电场强度可以达到数万伏/米,不断变化的电场又会激励出磁场,瞬间产生的电磁场会以光速传播并产生强大的电磁脉冲,从而导致地面上的电器产生感应电磁场,继而将电子设备烧毁。
EMP 会干扰电器的运作,有时会造成损坏,有时干扰将造成不可逆的严重后果。
比方说电子门锁,出于安全规定,设备在断电或故障状态下,门锁必须为打开状态。简单的电子锁可以分为验证模块和锁模块,当验证通过时,会向锁发送开锁信号。使用 EMP 能直接在信号线中复现出一个开锁信号,或者还可以直接攻击电路让它发生故障,这样两种方式都能导致门锁自动打开,有极大的安全隐患。
假设左图是一个正常工作的电线,通有 5V 的电平信号,当在极短时间出现了一个极高电压的电信号,如中间的图所示,这个信号即称之为 EMP ( electromagnetic pulse)。当然通常情况下其波形不会那么好看,一般情况下,其波形会像图三那样。
经过全网搜索,包括但不限于新闻、论文和视频,搜集到了一些简易 EMP 设备的电路图,经过整理大致如下:
一个简易的 EMP干扰设备主要由一个逆变器、一个线圈、若干电容和导线组成。当高压通电时,会为电容充电,当电充满之后,会流经线圈在火花激发处击穿空气,于是瞬间放电后电压骤降产生了一个电磁脉冲,而线圈的作用说简单一点就是一个天线,将这个脉冲波辐射出去。最简单的 EMP 可以不用电容,加上电容可以准确地操控电磁脉冲频率,其实这也是一种在谐振频率上驱动特斯拉线圈的方法。电路本生不复杂,但是高电压击穿空气产生的脉冲本身无法对任何设造成明显干扰,所以问题在于如何生成一个干扰能力更强的电磁脉冲,并使之能向周围扩散。
对简易 EMP 的个人理解就是,它由两部分组成,分别是生成脉冲的模块和将脉冲辐射出去的模块。脉冲生成的方法就有很多,我们可以直接选择断开一个回路导线,使之产生一个合理的间隙,并假如一个电容,当空气被击穿时,脉冲便生成了,这也是最简易的 EMP 制造的方法。这里我们必须将回路切断并留有一定间隙来让电流击穿空气。原因是假如没有间隙,回路是闭合的话,当电压冲到某一高值,根本就不会有尖峰,还发生短路,但是如果留有间隙,电容则会在某个电压处瞬间放电,循环往复就能产生脉冲信号。另外是将回路中的脉冲辐射成电磁波的模块,简而言之就是根天线,回路中的任何一段导线其实都可以充当天线的作用,因为通电导线周围能产生磁场,但是为了更好的效果,我们会选择缠绕起来的导线,即螺线管来充当导线。
我们一般直接将螺线管串联在电路里,但为了达到更好的效果,我们会选择将螺线管缠绕在另外一根绝缘导线上,来达到定向发射的目的。
对于简易的 EMP,当电压达到一定高度时,火花隙间空气被击穿,线圈瞬间通电并断电,这一过程中一个瞬间的电流(浪涌)会流过线圈,便在线圈线圈上激发处了电磁脉冲。当两根线靠得很近的时候,两者之间会生成电容,所以他们之间也会发生传递交流电压的耦合,这样磁场和电场的干扰就会同时发生。信号频率越高,电磁干扰就越严重。由于电容的阻抗和频率成反比,高频的时候电流就越大,这样磁通量的变化率也就越快,导致更大的电动势,从而导致更强的干扰效应。当频率高到一定程度,电磁场就会像光一样,以电磁波的形式向周围扩散。
上图中含有柱状炸药的则是 EMP 武器,其原理稍有不同。首先和之前的 EMP 一样,用电池给电容充电,当电容电压足够高时,向导线圈放电。等导线圈内电流达到最大时,即刻将左侧柱状炸药引爆,激波波面以超过介质声速向右传播,波后反应区里的炸药在高温高压下被连续引爆。由于螺旋形的导线形成内部直线型的磁场,随着炸药从左向右引爆,炸药住的铁皮外壳连续破裂与线圈短路,同时高速向右移动,让线圈内的磁通量不断向右压缩,同时线圈的匝数不断减少,导致线圈里的感生电流越来越高。这些感生电流都输入到虚阴极管,通过谐振生成高频电波,最后由微波天线来释放这些电波,形成破坏力强大的电磁脉冲。
如果一个电路正好处在 EMP 产生的电磁波范围内,并且它足够脆弱,那么它就会被干扰。电生磁,磁生电,EMP 激发出的电磁波使得被干扰系统的电路中的小回路上产生了感应电流。电磁干扰可以在电路上产生各种奇怪的电流电压。比方说我们需要一个正脉冲,但是感应电流在这生成了一个负脉冲,这样一来设备就会接收到错误的信息,整个系统就会出现瘫痪等问题。
如图所示,左图为一个正常的方波,当带电磁脉冲干扰启动,波形立刻被干扰了。
不过干扰电磁波的波长必须和系统中的电线长度匹配。如果电路比它的波长小很多,那么电路只会占到干扰信号相对平缓的部分,这样反而就没有干扰了。如果电路很长,或者干扰信号频率很高的话,干扰波的高电平和低电平会全部耦合进电路,这样整个系统就会产生很强的噪声了。所以说电路越小干扰就越困难,频率越高,就越容易干扰电路。
这里我们来制作一个简易的 EMP 装置感受一下。目标是能成功弄坏室友的闹钟。
我们需要一个能产生电磁脉冲的装置,为了方便,直接使用 1000KV 高压包,免去自己配升压电路的麻烦和危险。然后选择线圈的时候要考虑到在瞬间高电压的时候要确保其不会被击穿,所以我选择了 0.75 mm 的漆包线。为了保证安全,还加装了开关。但是暂时没有配备电容,一方面因为不清楚具体应该使用什么型号的电容,一方面是实验本身对电磁脉冲的频率没什么要求,能用就行。但是值得注意的是,如果要添加电容的话,一定要使用类似于闪光电容器,而不能使用类似于法拉电容,因为前者能在瞬间释放掉所有能量而后者相当于是个电路中的稳定器,而这里我们恰恰需要能在短时间内释放所有能量的电容。
集成了电源开关、启动按钮、EMP 线圈和电池充电器。
经过测试,这个小型 EMP 已经成功摧毁了我一块手表和一个闹钟…但是它的能量不足以点亮日光灯。
要想让 EMP 达到最佳的效果,影响因素是十分多的,不仅取决于攻击方,也取决于被攻击方。
较低的感应电动势不足以对电路造成干扰,所以要想得到较好的效果,必须要生成较高的感应电动势,也就是说,EMP 生成的脉冲电压一定要足够高,时间也要尽可能短。所以一方面,我们可以使用较强劲的逆变器,将低压电转换为超高压电,一方面我们可以换上更高级的电容器,使其拥有更强的性能在更短时间放出更多能量。
线圈的参数关系到 EMP 所激发的磁场。线圈匝数越多,激发出来的磁场也越强,但这同时也增加了电感,这么一来阻抗也增大了,线路中的高速电流交换就会受到影响,导致干扰效果变弱,因此必须要做好阻抗匹配才能使装置达到一个极好的效果。当线圈的匝数和直径根据电源输出电压的频率调整到一个合适的状态的时候,其干扰效果是最佳的。
干扰电磁波的波长必须和系统中的电线长度匹配。如果电路比它的波长小很多,那么电路只会占到干扰信号相对平缓的部分,这样反而就没有干扰了。如果电路很长,或者干扰信号频率很高的话,干扰波的高电平和低电平会全部耦合进电路,这样整个系统就会产生很强的噪声了。所以说电路越小干扰就越困难,频率越高,就越容易干扰电路。
大多数时候,电器虽然受到干扰就会出错,但只要干扰一停下,电器就恢复正常运作。但比起持续的干扰波,用一个脉冲有两点好处。首先,制造高功率的电磁波本身就要消耗巨大的能量,军队可以吃得消,但大多数时候我们手头的能量是有限的,所以与其向电路系统发射小功率的持续干扰,不如花点时间收集能量然后集中在一点上爆发出去,这样的爆发可以循环下去。每次脉冲都是一次强力的蓄力爆发式攻击。第二点,由于每个电路的敏感频率不一样,所以使用稳定在一个频率上的持续干扰波是不能干扰到所有电路的,但是理想状态的尖信号就可以理论上涵盖所有频率,并且理论上波尖约尖效果越好,这也是为什么需要一个能瞬间释放能量的电容的原因。
任何电路都会以辐射或者导线传导的方式释放噪声,同时也会受到来自辐射和导体的干扰噪声。那么防止被 EMP 干扰便有通过释放噪声和防止吸收噪声两个方法。
首先一定要让电路中构成回路的两根导线尽可能地接近,这样电流流进的那根线生成的磁场就可以和电流流出那根线生成的磁场相互抵消,这样就可以减少辐射,免于干扰其他电路。
其次不要使用多余的线,多余的线既会辐射也会吸收更多的噪声,理想的接线是两点之间拉直线。
再者,脉冲信号要使用缓和的波,不然就容易释放高频的谐波,同样会对其他电路造成干扰。
还有,高频信号线的接线或者印刷线路尽量避免尖锐的拐角,佛则尖锐的拐角也极易辐射噪声。
最后,一定要为电路设计一套完善的滤波体系,既可以抗干扰,也可以减少自身的释放。
电线要尽可能短,如之前所述,电线越短,那么电线只会占到干扰波波长的一小部分,这样的噪音可以很方便被过滤,有效避免干扰。
同时,弱信号线一定要和带噪声的信号线分开来放,中间用一个接地的板子隔开来,这样就可以吸收掉噪声。
然后,重要的信号强度一定要大,让信号源的输出头阻抗尽量的小,同时也要避免用尖锐的方波。同时与之对应,弱信号线可以用滤波电容过滤掉高频噪声。
还有一个方法,就是将所有东西都要用接地的屏障罩起来,比方说给电气设备来个法拉第笼。
电磁脉冲是一种突发的、宽带电磁辐射的高强度脉冲。所在电磁频段取决于EMP源。高能电磁脉冲在我们生活中无处不在,任何一根导线,一个家用电器,都会产生这种脉冲,只是它的能量弱得不足以被我们感知。我们一般所说的 EMP 其实是具有攻击性的高能电磁脉冲,它具有极大的破坏性。一般而言电磁脉冲对生物体没有任何影响,但在电磁脉冲发生时靠近电力及电器设备等足以大量聚集电磁脉冲波物品的生物体可能因瞬间的超高电压而灼伤、休克甚至死亡。
其工作原理很简单,即电路中电流的瞬间变化产生了电磁场,这个电磁场使周围的的电路产生感应电流,干扰系统正常运行。为了放大这个电磁场,可以在电路中加入天线等装置。当然为了使干扰更具有针对性,电路中加入电控、mos 管来控制脉冲频率即波形。
对 EMP 的防御不仅仅在于军事领域的防御,在普通电路板的设计中也要注重对电磁脉冲干扰的防御,以使设备你能够在复杂磁环境中稳定运行。多种措施可以防御 EMP 攻击,从最基本的只用滤波器、电子管到使用电磁屏蔽罩,都可以有效避免电路受到干扰。
参考文章:
参考链接
摘要
HGAME week3 有道 XSS,蛮有意思的,涉及到蛮多知识盲区,现在刚好有空就再来看看
网页长这样,这道题的逻辑是: 在输对验证码后,我们 POST 出去的消息会被管理员看到,并且只有我们拥有了管理员的 token 才能进 FLAG 页面拿到 flag。所以考的就是 XSS。
首先 Ctrl+U
查看网页,注释了网页源代码的地址,然后便拥有了网站的源代码。
源码贴一份防止以后想再看找不到了:
1 |
|
1 |
|
1 |
|
使用 XSS 窃取 cookie 的话免不了跨域请求,but在主页面已经禁止了跨域。
但是当时做题没注意到,**/preview
页面没有跨域限制**
再仔细看 python 程序有两个过滤函数,分别对 POST 的内容和 search 的内容进行过滤
对于 POST 内容的过滤,提取<iframe src="xxxxxx">
, 如果没有提取到,则提取 <>
中间的内容,如果尖括号也没有,就返回原字符串。
对于 search 内容的过滤,删掉所有 <
>
"
\
,
然后再看 preview.html
页面,有一段 js 代码,对文本进行正则替换
这里的逻辑是,直接将过滤后的 search 内容作为正则表达式,并将匹配结果替换成 b标签,中间的文本不是原内容,而是 过滤后的 search 内容。这里其实是可控的,只要我们输入的 search 内容绕过了过滤
由于一个页面有跨域限制一个没有,所以第一步应该想到,POST 一个 <iframe src="preview">
然后成功在主页嵌入了/preview
页面,接下来专心在/preview
搞 XSS 就行
发现对于 search 并没有过滤 |
, 而这个符号是 或 的意思,在正则匹配里,/a|b/
的表达式匹配到 a 或 b 都算匹配成功,利用 JavaScript里的 replace 漏洞,于是我们测试一下:
成功了,JavaScript 在用iframe|XSSXSSXSS
匹配到 ”iframe‘“后直接用 <b class="search_result">iframe|XSSXSSXSS</b>
替换了 ”iframe“
接下来只要把 XSSXSSXSS
换成我们需要的东西
但是要注入 XSS 代码的话,必不可少的是尖括号,search 的时候输入尖括号是注定不行了,但是可以利用 iframe 标签两边的尖括号,这时候就利用了 replace函数中和$相关的几个特殊正则表达式:
具体使用方法在 CSDN 找到了一个例子:
所以在这题里,输入$` 会输出 < **, **输入$’ 会输出 src=”preview” > ,测试一下:
成功
接下来直接构造 XSS 内容就好了
1 |
|
测试一下:
成功,然后浏览器会直接弹出窗口
XSS 平台也接收到了消息
然后手残刷新了一下主页….然后因为是 autofoucs 的缘故,会直接弹出新页面,导致主页根本看不了,所以**千万不能刷新主页
最后破解 md5 ,python 暴力破解:
1 |
|
一段时间就破解完成,提交!
参考文章:
参考链接
摘要
开始学习 SQL注入啦
sql语句长这样:
1 |
|
其实很早很早以前完全看不懂 $_GET[] 周围的符号,以为是什么类似于 python f”aaaa{t}aaaa“ 这样子的变量引用. 其实 php 没那么高级,这里是用到了字符串拼接,而且 id 的值是字符串类型,要用引号包裹,所以 id 值的两个单引号被分别安排在了前后两个 sql 语句字符串里了。
第一题不愧是第一题,比较简单,拼接后的语句如下:
1 |
|
用户--
注释掉了后面的内容,要注意的是用--
注释的时候,注释符和注释内容之间要有空格
完成
这题相较上一题,区别在于用户名那列不能出现 flag 字样,这里我采用联合查询,注意:联合查询,第二个查询语句的查询结果会被拼接到以一个查询结果下面,所以前后两个查询语句的列数要一致
拼接后的 SQL 语句如下:
1 |
|
输出如下:
这里用 1 代替了用户名,当然也有其他方法,比如说编码:
或者可以用 replace方法,比方说用 flaag 替换 flag
甚至直接换个位置,毕竟只检查用户名那列:
当然,最完整的注入应该是从爆库爆表爆字段开始,这里演示一下完整过程:
首先爆库,SQL 语句为:
1 |
|
得到所有库名:
下一步爆表,SQL 语句为:
1 |
|
得到所有表名:
下一步爆字段,SQL 语句为:
1 |
|
得到字段:
后面就按最开始的方法做就好了
这个使用正则在整个输出中搜索 flag,和上题一样的方法, 只要注意表名变了,列数也从 2 变成了 2
1 |
|
成功
这题之前做过了,也不是很难,直接 copy monster663 的方法吧,毕竟 wp 看下来,这位好哥哥的方法还是比较简洁的
这里过滤了所有数字,但是里面必然是会出现数字的,大部分人方法是 使用 replace 方法将 数字用字母组合替换,这位好哥哥想到,先转换成十六进制再做替换,因为输出的十六进制字符串英文字母均为大写,那么做替换的时候只要将数字变成小写字母,后面对输出的处理就会方便很多。
SQL 语句:
1 |
|
这样得到结果:
再把小写英文字符换回数字
最后再转换成文本:
OVER
这次直接过滤了所有可打印字符,我最初的想法是把每个字符的16进制值加上一个数让它超过 7f 再返回,但好像没有成功,我也不知道这样可不可行,浏览了别人的 wp 似乎可以直接输出到文件,使用 into outfile 命令。(but不知道为什么前面一题用这个方法行不通呜呜呜)
SQL 语句:
1 |
|
然后直接访问文件:
得到 FLAG
开始过滤注入
不好意思我不知道这题过滤了啥…
首先 fuzz 一下,发现应该是过滤了空格,然后在准备注释掉 limit 1 时发现 -- ’
失效了。
总结一下空格的 ByPass
%09 %0A %0B %0C %0D %A0 %20 /**/ ()
再总结一下注释的方法
–+ – ‘ # %23
然后经过修改后的 sql 语句如下:
1 |
|
然后 flag 就来了
得到 Flag
fuzz 了一下,发现用上题的 payload 直接过了
网上看了看,这题应该是过滤了 空格 和 *,所以不能用/**/
,所以可以换成 %09 之类的
也能得到 FLAG
依旧是对传入参数进行了过滤, 那就再 fuzz 一下, 发现是过滤了 空格 ,* 和 %09, 因此还是可以用前两题的 payload 一把梭
当然还可以用 web 177 说明的几种空格绕过方法, 就不演示了
再用前几题的 payload 已经不行了
fuzz 一下, 应该是过滤了 %23
思来想去, 或许只能直接查询 id了:
但我们并不知道 flag 的 id 是多少,于是写了个脚本
1 |
|
运行!
flag 就来了
ummm这题给了正则匹配的内容, 观察发现用上题的 exp 还能打!
flag 就来了
相较上题,类型差不多,只不过正则匹配多了 flag,我猜想可能只是为了防止查询用户名为 flag 这样子的语句吧,但是无妨,前两题的方法还能用!
flag 又来了
这题就变了,只回显结果数量,提交方式变成了 POST ,模糊测试一下:
盲猜 flag 在 pass 这列,maybe 只能对 pass 进行字典爆破,并且我们可以在 tableName 后面直接加上条件语句,并配合正则:
网上的脚本似乎都有点问题,于是自己写了一个,值得注意的是,pass 列有 22 条数据,如果爆破的字典顺序不对的话,可能把别的信息爆出来,比方说admin
,111
,为了保险,我们已经知道了 flag 开头是 ctfshow 于是可以有意的排一下字典顺序
exp:
1 |
|
一段时间后,完成!
这不太友好,连 where 和 sleep 都过滤了!
先 fuzz 一下,表名还是 ctfshow_user
因为过滤了 where ,我也不知道该怎么办,于是上网寻求了帮助,发现大佬们用了 right join 的方法,用 on 代替 where,于是自己要去了解了一下。Mysqsl 的 right join 语法是
1 |
|
经过一番研究,这句话的逻辑是,查询出 table2 的整张表,然后依次循环查询结果的每一行,对每一行,又分别以 on 后的句子为条件在 table1 中查询。最后的行数计算方法是,打个比方,如果 table2 有五行,以 on 后句子为条件查询 table1 有六条结果,那最后输出的总行数为 5*6=30 行。
那么先设计一张表 ctfshow_user:
然后做一个简单的 right join:
这里将表分别命名成了 a 和 b,是为了方便写条件式。首先查询 b 表,能查询出五条数据,也就是图中 5 个红框,随后依次循环每条数据并以 on 后的条件进行查询。这里因为 on 后的条件是 1=1,所以应该是恒成立条件,那么表 a 中每一项数据都是匹配的,因此每一条表 b 的数据又对应了 5 条表 a 中的数据,这也是为什么上图中每个红框里又有五条数据。(这里我的个人理解就是,b表有n条数据,就循环多少次,循环体内是对 a 表的遍历,a 表有几行就进行几次循环,每一次循环内的判断条件是 on 后的语句)
基本理解意思后,回到题目,只输出行数。如果 on 后的条件不成立,那么 b 表有几行数据 count(*) 就是几
如果 on 后条件恒成立,那么就会输出 count(*) = 25,前面 1=1 就是个例子。
如果 on 后条件对 a 表某一行成立:
那么 b 表的每一行都能匹配到一个结果,count(*) = 5,和 on 条件全不成立结果是一样的,这样无法区分。但是如果条件是关于 b 表的话:
对于 b 表的前四行,其 pass 列值都不符合条件,故搜索结果为 null,但对于最后一行结果,a 表有5行,就循环五次,每一次的判断条件是b.pass='cctfshow{uebwiubvu2322}'
,所以这时候 5 次循环都是成立的,故对应了五条数据。所以 count(*) = 9, 也就是 5 * 2 - 1。很显然这样可以判断出我们的条件语句成立了。
回到原题试试,分析一波,第一次测试可知这里 ctfshow_user 表有 22 行,用 right join 方法,on 后条件对右表成立,那么最后行数应该是 22*2-1=43 行。这里可以结合一下 substr 方法。等号被过滤,这里可以容正则 reg 代替,或者用 like;然后引号被过滤,无法使用"a"
这样的字符,故可以用char(97)
代替,当然 97 可以换成十六进制。测试一下想法,我们知道第一位是 c :
十六进制也行:
like 也行:
那既然知道 flag 的前几位是 ctfshow,咱转成十六进制试试:
成功!但是这里必须用 regexp,用 like 就不太行。
那么就可以写脚本了!
exp:
1 |
|
运行!
成功!!!
和上题差不多,只不过多过滤了所有数字。
网上看见张表:
意思是 true + true = 2 这个样子,可以用函数之类的字符串来表示一些数字,那么 a 就是 97 个 true 相加!
但是有个问题,我前几题的脚本在对每一位进行爆破的时候,十六进制字符串都会带上之前匹配出来的字符,比方说我要匹配第七位,我会将前六位 ctfsho 一起带上转为十六进制放在 regexp 里,这样可以有效防止跑出其他结果(加入有两行,第一行 admin,第二行 damin,那么如果匹配第二位的时候不带上第一位,字典是"abcdefghijklmnopqrstuvwxyz"
的话,那么跑出来的结果很可能就是 aamin )
在这题这样子就行不通了,或许可能是我没想到。我去网上白嫖了代码,其他大佬 的 wp 都是按位爆破的,不带已爆破的字段,不过效果好像真的还行!
exp:
1 |
|
运行!
flag 就来了
过滤的有亿点多,但是上一题的 exp 还能打!
flag 又来了!
题目越来越有趣了起来
md5()函数有两个参数,一个是要加密的字符串,另一个是输出格式,可选。规定十六进制或二进制输出格式:
TRUE - 原始 16 字符二进制格式
FALSE - 默认。32 字符十六进制数
这题比较简单,只要找到 md5 加密后会出现 ‘or’x
就行,非零极为真,网上找来两个ffifdyop、129581926211651571912466741651878684928,可以顺利解决!
flag 不会直接显示出来
摘要
刚刚写了个练手小项目,个人还是比较满意的。我觉得这是个好东西,分享给大家玩玩!
这是个控制台程序,没有加 UI,所以界面有一点丑,但不影响
这个项目开源,仓库地址Github,有兴趣可以帮我改改 Bug? 毕竟个人感觉程序里代码重复度太高了,应该是能简化的。
闲着无聊,想写代码。转眼学校里我爱记单词又要开始了,突然想起是不是可以写个小程序 ‘帮助’ 一下记单词?其实上学期的时候写过一个 Python 但是正确率大概在 75% 的样子,并且 10 次运行能有 8 次报错,不得不自己在手机上接着做下去,也就自己能用用,但用着极不舒服,程序跑起来的时候需要双手合十祈祷别崩溃。
正好开学这几天闲来无事,想着重新写一遍这个程序,增加更多的优化,把代码逻辑写写好,并尽可能防止程序意外终止。我 做到了。
现在这个软件要是运行正常的话,正确率在 95% 以上,耗时在4-5分钟。后文会详细讲述实现方法。
但这里要申明的是:
本软件不会记录任何个人信息
软件纯开源,绝无恶意代码,但是因为涉及控制台和一些系统命令,360 会报毒,所以请在打开软件前退出 360 放心使用。
不准贩卖,仅供学习娱乐使用
软件理论上能实现 我爱记单词考试,但还未进行测试,又考虑到还是要好好学习英语,所以软件中考试的选项暂时关闭,若选择了考试,程序将直接退出。
软件加了验证机制,只有拥有 license 的用户才能体验到这个小软件,拥有 License 的用户最好不要传播,后台都有使用记录
玩玩就好,不要滥用,该程序调用的 api 要 付 钱
D://study/
下,但请不要放在 D://学习资料/
下,否则会直接闪退首先,通过邮箱链接下载软件,解压后有两个.exe
程序,请将它们至于任何英文路径下, for example:
mainFullBlood_boxed.exe
为主程序,另一个为浏览器驱动,这俩一定要放在同级目录,否则必报错
在确保 VPN 等代理工具已关闭,不必要的软件也关闭的情况下,双击运行主程序,但建议最好右键以管理员身份运行
如果开了 VPN 会出现如下界面,程序随即退出:
如果网络情况不好,会显示 ”网络状况不佳“ ,程序随即退出
网络配置正确, 会出现如下 输入密钥界面, 输入你的密钥来继续运行程序吧! (如果你还没有, 快去登记页面申请一个吧,如果你足够幸运,你会拥有密钥!)
密钥正确,直接进入下一步:
现在程序需要你的 上课啦token 但为了防止隐私泄露, 这里不采用杭电账号登录的方式来完成登录
所以程序只需要一串网址
在手机打开 易班app, 进入上课啦小程序,点击右上角三个点,选择 ‘复制链接’,再去手机浏览器百度搜索二维码制作,随便选一个进去 (这里推荐 草料二维码) ,将刚刚的连接复制进去生成二维码。当然,你也可以选择手动在控制台输入那串网址 (如果你的电脑没有摄像头)
接下来回到程序,输入a
,回车,此时会弹出摄像软件(如果电脑没有摄像头,选择 a 后会直接退出程序,所以如果电脑没摄像头,请选择 b
手动输入刚刚复制的网址),将生成的二维码对准电脑摄像头,在识别到二维码后,控制台会输出扫描到的二维码,摄像软件会自动关闭。
随后软件会获取远程字典,并打开八个浏览器窗口
不要关闭这些浏览器,它们弹出来后 会自动最小化,你只需关注控制台的信息即可!
接着选择 练习
(选择考试,程序会直接退出, 因为后台暂时把这个模块锁了),周也请正确输入,上课啦 里说现在第几周,就输入几, 输错程序也会结束(这不是 bug, 这代码是我写的,并且那八个浏览器窗口得你自己关了)。总而言之,言而总之,千万不能输错。
接下来验证信息, 确认无误后,输入那串表示日期的文字(红色字体全部要输),敲下回车程序开始运行!
这里的输出有点乱,因为是多线程的原因,don’t care!
不出错的话,跑两分钟就结束了,因为有部分题程序决定不了,所以会要你自己来选择,接下来是手动答题,不过所有英文都已经安排了中文翻译,并且能匹配到的词会用明亮的颜色标注出来,所以如果你题目有颜色标注,直接选那个选项就行!这里的 模式 一般来说选n
就行,有了远程字典的帮助,不严格模式的正确率也不低
结束后,程序会输出这次的所有答案和成绩等信息,随后的 completed
是在执行远程字典同步,这次所有的题目都会录入到本软件服务器中,以后碰到同样的题目就会 100% 正确了!
最后出现 ”感谢使用”,字样后,浏览器会自动关闭,随后控制台窗口也会自动关闭。
[033[033[033[033[033[033
,不要慌,等它输出一会,会自动结束。具体原因不清楚,应该是在为输入的消息进行添加颜色时,有字符让程序进入了死循环。当然这道题的题目是看不了了,随便敲个答案,按回车继续吧!这个 Bug 出现概率极为不大。摘要
这周比较忙,只有两天有空看看 hgame 不料肝出来 3题
[TOC]
右键查看网页源代码发现注释:
下载源码,发现三个 EJS 文件,去 Google 了一波,这是个 JavaScript 模板,然后按照官方文档,把源码在本地跑了起来,并掌握了个新技能——JavaScript本地调试。
在 app.js 中,发现了模板注入漏洞。
根据之前的的 JavaScript 调试,可以得知该程序的逻辑:如果是本地访问,该 session 对象的 data 会增加一对 crying: true
,而只有 data 中 crying 存在且值为 true 时,用户才能进入 /wish :
所以解题思路已经蛮清楚了:进入 /wish →模板注入。
抓住一点,若不是本地访问,data 中便不会有 crying,且在 JavaScript 中,万物皆是对象。首先理解继承的查找过程。调用对象属性时, 会查找属性,如果本身没有,则会去__proto__
中查找,也就是构造函数的显式原型中查找,如果构造函数中也没有该属性,因为构造函数也是对象,也有__proto__
,那么会去__proto__
的显式原型中查找,一直到 null (很好说明了原型才是继承的基础)。
又发现:
所有干脆污染原型链。构造 payload:( 特别注意,数据格式为 json )
1 |
|
随后能用该 cookie 顺利进入 wish:
好了第一步完成,下面就是简单的模板注入,我准备直接用 tplmap 来注入。
为了能直接用 tplmap ,我写了个 python 代理程序:
1 |
|
网站 session 更新很快,所以污染原型链后的 cookie 要立刻复制到代理程序运行 !
接下来就是 tplmap:(既然已经知道是 ejs ,那么就直接 getshell )
1 |
|
结束了…其实当时找 flag 找了好久,实属 linux 操作不太熟练。
这题看看完成人数就知道比较简单,所以我选择试试这道题。根据题目表述,可以猜到应该是道内存取证的题,要用到 volatility。但是 kali 2020 似乎没有配 volatility,这给我整傻了。然后我在 GitHub 上找到了 volatility 的包,试图在 win10 编译运行,但还是没搞定(依赖包问题)。最后无奈,又装了个 kali 2019。
查看镜像系统:
1 |
|
发现系统是 Win7SP1x64(之后的语句加上--profile=Win7SP1x64
)
然后列举进程:
1 |
|
有一个名为 important_work 的进程,该进程 PID 为 1092
使用提取命令对该进程进行提取(-p的参数为进程ID,-D的参数为保存文件的路径):
1 |
|
再使用 foremost 将其分离,可以得到应该zip文件
注释里写着压缩包的密码是 sha256 (登录密码)
于是用 hashdump 命令找到登录密码的 NTLM 哈希值:
1 |
|
哈希值为 84b0d9c9f830238933e7131d60ac6436
再到 Cmd5 网站上进行破解
得到登录密码 asdqwe123
那么压缩包密码为 20504cdfddaad0b590ca53c4861edd4f5f5cf9c348c38295bd2dbf0e91bca4c3
打开压缩包,得到两张图片,分别是原图和加了盲水印的图
使用 BlindWaterMark 脚本,得到:
hgame{7he_f1ame_brin9s_me_end1ess_9riet}
这道题通过阅读源码,发现是 LCG 算法.
三道题分别是:已知乘数、模数和连续两个值求增量、已知连续三个值和模数,求乘数和增量、已知连续n个值。
直接使用 Crypto 库,但要注意的是,我们求得的值不一定就是答案,所以必须多次尝试。
1 |
|
要尝试好几十次,最后得到 flag。
]]>摘要
以前做过个课题,整个项目是建立在 arduino 上的,但是程序比较乱,现在正好趁这个机会完善一下。
[TOC]
通过按钮控制两组十字滑杆上下左右运动,并通过显示器显示实时坐标。
本装置搭建所需的材料及零部件的选型中十字丝杠滑台、电机及电机驱动器、Arduino 控制板是重点。
十字丝杠滑台的选择主要考虑尺寸问题。为了以后自己搭 3D打印机做准备(已经买了赤兔主板,在研究),就买了400mm×400mm规格的滚珠丝杆滑台。
电机选型主要考虑合适的扭矩,电机驱动器则需要与电机配套,最后选用了 42步进电机及 DM542 电机驱动器。
Arduino 控制板有各种配置,选型时主要考虑针脚数量需求。由于装置预计大约需要15个针脚,超出 Arduino nano、Arduino uno 的针脚数,最终选择了拥有更多针脚的 Arduino Mega 2560 控制板(省事)。
按钮控制开关、超声波传感器、LCD液晶屏与Arduino Mega2560单片机的线路连接如图:
电机驱动器与 Arduino Mega2560 单片机的线路连接如图:
软件设计主要是逻辑问题,关于代码,这里展示电机控制部分关键代码。
1 |
|
1 |
|
参考文章:
参考链接
摘要
这里介绍关于 DHT11 温湿度传感器在 arduino 上的使用方法,附头文件
所谓单线串口(全称为单线异步串行通讯接口),不同于标准串口的两根数据线( Txd 和 Rxd ),单线串口只有一根数据线来同时兼备发送和接收功能,系统的数据交换、控制均由单总线完成。因此,单线串口采用半双工的通讯方式(能双向通信,但一方发送信息的时候另一方不能发送信息,毕竟只有一根线)。单总线通常要求外接一个上拉电阻,以确保当总线闲置时,其状态为高电平,如图以 DHT11为例。
还可以在 VDD 和 GND 直接加一个电容 ,来去耦滤波。
这里以 DHT11 来说明。(科协给的数据手册确实有点意思,上面讲的比较详细,,这里挑重点说明)
DHT11 和主机的通信,一次传送 40 位数据,高位先出,一次通讯 4ms 左右。
数据格式:8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据 + 8bit校验位
校验位等于 “8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据” 所得结果的末8位。
通讯过程:
主机发送一次开始信号后,DHT11 从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11 发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据,DHT11 采集数据后转换到低速模式。所以我们每一次读取的数据是上一次的采集结果
时序图:
起始信号:微处理器把数据总线(SDA)拉低一段时间至少 18ms(最大不得超过30ms)。通知传感器准备数据。
响应信号:传感器把数据总线(SDA)拉低 83μs,再接高 87μs 以响应主机的起始信号。
数据:传感器一次性从数据总线(SDA)串出40 位数据,高位先出
最后从机拉低 50μs ,随后总线由上拉电阻拉高释放总线进入空闲状态。
Relative humidity is the amount of water vapor in air vs. the saturation point of water vapor in air. At the saturation point, water vapor starts to condense and accumulate on surfaces forming dew.
The saturation point changes with air temperature. Cold air can hold less water vapor before it becomes saturated, and hot air can hold more water vapor before it becomes saturated.
简单点说,相对湿度就是空气中水蒸气的量与空气中水蒸气的饱和点(饱和点:水蒸气开始凝结并积聚在形成露水的表面,其值随气温变化)的比值。
相对湿度计算公式:
100%RH 下,会发生凝结;0% 下,空气完全干燥。
温度传感器组件有很多种,像热敏电阻、DS18B20 、TMP36 、LM335A 等。DHT11 其实是一款结合温湿度传感器及信号处理 IC 的感测模块,外观如下。
连接 arduino 时。建议在电源与数据输出脚连接一个 10kΩ 电阻,电源和接地脚之间接一个 0.1μF(104) 电容,原因在上文有提及。怕麻烦可以直接买最右侧的。
那么它是如何从测量温度和湿度的?
DHT11 通过测量两个电极之间的电阻来检测水蒸气。两个电极之间的电阻变化与相对湿度成正比。较高的相对湿度会降低电极之间的电阻,而较低的相对湿度会增加电极之间的电阻。
DHT11 通过 NTC温度传感器(热敏电阻)来测量温度 。
材料 | 数量 |
---|---|
Arduino Uno | 1 |
DHT11 | 1 |
10kΩ 电阻(三脚的 DHT11不需要,但四脚的 DHT11 也非强制需要) | 1 |
导线 | 3 |
这里需要引入 DHT11 第三方库。下载链接
打开压缩包有两个文件
找到 Arduino 的 libraries 地址,一般默认会在 C:\Documents\Arduino\libraries (因人而异),里面有不少已经安装好的官方库。
我们新建一个文件夹dth11
,名字随意取,不一定要我这个。
把压缩包里的两个文件复制进去,大功告成。
测试程序:
1 |
|
这里不打算拍视频了。
运行一切正常,随意拔出一根线随即输出 ”无法从DHT传感器读取!“
侵删
]]>摘要
Week 3 巨难
这题应该是最 ez 的了。
首先去[factordb.com](http://factordb.com/index.php)把 n 给分解了,运气比较好这里能成功分解,于是我们便拥有了 p 和 q,然后可以计算 p 这题就解决了。脚本如下:
1 |
|
这题也考的是 RSA,根据描述可知,有消息相同,然后打开文件发现 e=3,e太小了。
在 Google 上逛了半天,了解了不少 RSA 破解算法,锁定了一个叫低加密指数广播攻击的方法,该方法适用于加密指数e
比较低,并且使用了相同的加密指数e
给若干个接收者发送相同的信息的情况,适用于本题。
但哪几个消息是相同的?总共给了七组数据,他们之间有 128 种组合,所以必须先用脚本得出所有组合。(最开始直接把七组数据进行低加密指数广播攻击的脚本中,发现毫无结果,全是乱码,后来才意识到又的消息应该不一样)
这里要用到 gmpy 库,但只支持 python2,所以我又不得不安装了python2
那么最终脚本如下:
1 |
|
运行得到输出
提取一下有用信息:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
I am afraid the dishes in the second grade are too fragrant, you will not reply my text messages,
so I won’t give you New Year greetings this year, I hope you don’t know how to praise, good night.
hgame{!f+y0u-pl4y_rem
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Hello Liki4:
I am afraid that there are too many blessings on the 30th night, you will not see my greetings,
I am afraid that the firecrackers in the first grade are too noisy, you will not hear my blessings,
@ind3r~YOu^9ot=i7}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
然后结合一下得到 flag hgame{!f+y0u-pl4y_rem@ind3r~YOu^9ot=i7}
最开始毫无想法,后来了解了下 sqlmap,然后学习了时间盲注的知识,最后试来试去在密码处发现了 sql 注入漏洞。编写 python ,把密码一位一位爆出来。
考虑到一般密码都是正常的字符,所以 ascii 码值应该都介于 30~130 之间,那么对密码的每一位都使用二分法比较,结合 if 语句,判断为真则 sleep(2)
,以此确定密码的每一位。
程序如下:(得关VPN)
1 |
|
最后得到 password :sOme7hiNgseCretw4sHidd3n
最后用 admin 登录即可获得 flag
题目说到用 git 部署到服务器,怀疑存在 .git 文件夹。通过 git_extract 获得到了网站源码。
接下来进行代码审计,从头到尾没有没有敏感函数,但是注意到在 simulate.php 中有一串 secret_key:
同时 extract 函数有一个反序列化操作,说明 Data 可控。
Eval 类可以 echo this->message
,CardsPool 类的 toString 函数可以读文件。
所以要做的就是将 Eval 类的 $msg 指向 ClassPool 类,$file 的值赋值为 $file。随后生成序列化后的字符串,再经过 base64 编码,放回到 index.php的 cookie 中,便会获得加密后的 cookie。
构造 test.php:
1 |
|
本地运行得到编码后的序列化对象:
本地运行网站源代码,在 extract 函数 中加一句 echo 用来输出加密后的 cookie:
打开 index.php ,将 cookie 改为之前 test.php 序列化后的字符串,刷新得到加密后的 cookie。
复制 cookie,打开网站,替换cookie,刷新。
啥也没发生,但是查看网页源代码就出现了 flag
参考文章:
参考链接
摘要
菜鸡视角 hgame 游戏体验
首先,/www.zip 存在源码泄露。
1 |
|
通过阅读源码了解到,需要以 admin 账户登录才能获得 flag。 但是很显然我们只能利用 php比较漏洞 来登录 testuser 账户,但是这样显然无法得到 flag。
但是 admin 被加密的 MD5 尝试了很多办法也无法解码。这时候得利用 变量覆盖 将$_SESSION['username']
的值变为 admin
。
在lazy.php
存在一段过滤代码,会将请求中所有键名进行过滤,但是只过滤了一遍,这里造成了漏洞。
我们可以构造 payload :
1 |
|
POST 发送数据即可得到flag
这题很明显是考的 XSS,为此煞费苦心自己搭建了一个 XSS 平台,用的是莲花战队的源码
题目图下:
网页逻辑:第一个文本框中输入信息,点击 Post it 后会显示在下面的留言区;第二个输入框输入验证码,点击提交后,服务器会自动浏览你的留言。但是我们必须有管理员的 token 才能点击左上角的 flag 获得 flag。所以我们要做的:
写一段 xss代码 能够截取浏览者cookies 并发送到 xss平台
爆破一个字符串,使得这个字符串经过 MD5 加密后的前六位与验证码提示框中给出的验证码相同
等待服务器读取留言,xss 平台会收到来自 admin 的 token
使用管理员的 token 点击 flag 获取答案
这个网站的 XSS 过滤原则很有趣,script
变div
,遇到http
ptth
直接删除,遇到onerror
转换成rorreon
并将所有字符串反转,遇到rorreon
转换成onerror
并将所有字符串反转
经过精致打磨,写出了 xss 代码如下:
>vid<>";)()};))()}}''nruter{)e(hctac}'':ferh.noitacol.renepo.wodniw?)ferh.noitacol.renepo.wodniw&&renepo.wodniw(nruter{yrt{)(noitcnuf((epacse+'=renepo&'+))()}}''nruter{)e(hctac}eikooc.tnemucod nruter{yrt{)(noitcnuf((epacse+'=eikooc&'+))()}}''nruter{)e(hctac}ferh.noitacol.pot nruter{yrt{)(noitcnuf((epacse+'=noitacolpot&'+))()}}''nruter{)e(hctac}ferh.noitacol.tnemucod nruter{yrt{)(noitcnuf((epacse+'=noitacol&1=noissespeek?/'+etisbew=crs.))(egamI wen({)(noitcnuf(;'php.xedni/moc.sduolcjm.ssx//:s'+b+a=etisbew rav ;'ptt'=b rav ;'h'=a rav"=rorreon x=crs gmi<>vid/<
提交后不一会,我的 XSS 收到了我自己的 cookies。
说明 XSS成果,下一步是要破解那个验证码。无从下手,于是写了个 Python 爆破,就测试五位数字看看,跑不出那就删除 cookie 刷新页面,重发脚本,使用新的验证码,总有能成功的。
1 |
|
当时我成功了:
提交!随后 XSS 平台就收到了管理员的token :
修改 token 得到 flag !
先注册账号,这里不吐槽了,简单的用户名已经都被注册了….
很明显,差两张券,这里运用条件竞争,方便点用 Burp,低线程兑换券,一次兑换一张,高线程兑换非法数量的券,我使用数量 0 ,设置如下(左侧100线程,右侧50线程):
Attack!
不一会就有结果了:
flag GET!
很 esay 直接上脚本:
1 |
|
FLAG get!
pcapng 文件,使用 Wireshark 打开,迅速锁定一个奇怪网址:
去 cf.hgame2021.cf 看看,发现有连续不断的弹窗。
那我们禁用 js
获得线索,flag 果然不在这 ….
既然说到 SPF 那我们就查看一下 SPF记录吧。
然后 flag 就出来了?
自己好菜
]]>摘要
这里记录了关于 arduino 超声波模块的使用方法
超声波,即人耳可听见频率以上的声波,可用来探测距离,其原理和雷达类似:从发射超声波到接收反射波所需的时间经过计算可以得到距离。
可在空气中传播的超声波频率大约介于 20~299kHz 之间,其衰减程度与频率成正比(频率越高,传播距离越短),我们平常能买到的超声波模块通常采用 38kHz、40kHz 或 42kHz。
在室温 20℃ 的环境中,声波的传播速度约为 344m/s(水中比空气中快 60 倍),因此,假设超声波往返的时间为 600μs 从公式可计算出被测物体的距离为 10.3cm。
从声音的传播速度和时间,可求出距离,而物体的实际距离是传播时间的一半,所以可以求得 1cm 距离的声波传递时间为 58μs。
空气的密度会影响声音的传播速度,空气的密度越高,声音传播速度越快,而空气的密度又与温度密切相关。考虑温度变化的声音传播速度的近似公式如下:
此外,物体的形状和材质会影响超声波测距的效果和准确度,反射表面平整时,声波会照入射角反射回来,但要是表面粗糙的话,声音将被散射或吸收。不过,只要物体表面的坑洞尺寸小于声波波长的四分之一,即可视为平整表面。
BTW,假如超声波的发射和接收装置不在同一点,那么声音的传播途径不是直线,计算距离时要考虑传感器造成的夹角。
我记得高中时候有一次去区里答辩,我的项目是一个基于 arduino 的实验设备,然后有个老师就问我 “超声波传感器局限在哪,和红外测距有什么区别?” 当时我是懵了一下。
答:被测物体运动速度超过声速就无法测量了
超声波模块一般有两个超声波元器件,一个用于发射,一个用于接收。电路板上一般四个引脚:VCC(正电源)、Trig(触发)、Echo(回应)、GND(接地)。大部分实验用的超声波传感器模块参数如下:
指标 | 值 |
---|---|
工作电压 | 5V |
工作电流 | 15mA |
感测角度 | ≤15° |
被测物 | 面积不小于 50cm² 且平整 |
在超声波模块的触发脚位输入 10微秒以上的高电位,即可发射超声波,发射超声波以后,与接收到传回的超声波之前,响应脚位呈现高电位。程序通过响应脚位的高电位脉冲持续时间,换算出被测物的距离。
这玩意儿比较复杂,之间抛链接吧!维基百科-Serial communication.
这里看见个有意思的东西—— Mac OS 与 Linux 的通信端口。Windows 系统使用 COM(原意是COMmunication, 通信)代表通信端口,Mac OS( 一种基干Unix 的操作系统)和 Linux 则用TIY 代表通信端口。TIY 的原意是 ‘’ teletypewriter “(早期用来操作和大型电脑联机的终端机)。Mac OS 和Linux 系统把每个设备都看成文件,位千/dev 路径底下,因此在Mac
的终端机窗口输入ls -l /dev/tty.*
, 将能列举所有通信端口。
除了 TTY,Mac 和 Linux 上的通信端口还有包含—个同名的 CU。TTY 用于输入信息给设备,CU 代表 call up,用于从设备传出细信息,两者合作能同时收发信息,被称为全双工。在 Arduino 上,无论使用 TTY 还是 CU,都能传递信息,但一般选用 TTY。
传输协议代表通信设备双方所遵循的规范和参数,通信双方的设置要一致,才能相互沟通。
对于 USB 设备,像是键盘、鼠标等 “人机接口” 类、打印机的 “打印设备” 类、移动设备的 “存储设备” 类等,每个设备都有不同的传输协议,也需要安装对应的驱动程序。
当我们接上 Arduino Uno 开发板,在设备管理器中可以观察并更改其通信属性。
每秒位数(bit per second,简称 bps)是串口的传输速率,也称为波特率(Baud rate)。两个通信设备的波特率必须一致,一般为两部机器所能接受的最高速率,常见的选择为 9600bps 和115200bps。
开始传输数据之前,RS-232 的传送(TX)与接收(RX)脚都处于高电位状态,传送数据时,它将先送出—个代表 “要开始传送囖! ” 的起始位(start bit, 低电位),接着才送出真正的数据内容(称为数据位,data bit ),每一组数据位的长度可以是5~8 个位,通常选用 8 个位。
一组数据位后面,会跟着代表 “传送完毕” 的停止位(stop bit),停止位通常占 1 位,某些低速的设备要求使用 2 位。
除了数据传输线。还需要一条确保信息收发两端步调一致的频率同步线。但 RS -232 和 USB 串口线不需要同步线,因为它们会在数据前后加上“开始” 和 “结束“ 信息。这种传送方式统称通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,简称 UART)。
fritzing 找不到超声波模块了,用网图代替下吧,毕竟不难。
名称 | 数量 |
---|---|
Adruino | 1 |
超声波传感器模块 HC-SR04 | 1 |
1 |
|
侵删
]]>任务内容:
两个按键A、B,A 按下小灯变亮一点,B 按下小灯变暗一点。
机械式开关在切换过程中,电子信号并非立即从 0 变成 1( 或从 1 变成 0 ),而会经过短暂的,像下图一样忽高忽低变化的弹跳现象。虽然弹跳动作的时间非常短暂,但微电脑仍将读取到连续变化的开关信号,导致程序误操作。
为了避免上述状况,读取机械式开关信号时,程序(或者硬件)需要加入所谓的**消除弹跳 ( de-bouncing ) **处理机制。最简单的方式,就是在发现输入信号变化时,先暂停 10~30毫秒,然后再读取一次,以便确定输入值。
如下图所示,在 “单击” 操作中,信号改变了两次。
那么我们可以声明一个 click 的变量,记录信号改变的次数,每当此变量值为 2 ,代表按了一下按钮。具备 “ 过滤 ” 弹跳信号的开关代码如下。
1 |
|
loop() 区块不停地读取开关的值,并且对比开关的信号是否和上一次不同。假如监测到开关的信号改变了,要等待 20 毫秒之后,再确认一次开关值。如果等待20毫秒后读取到的开关信号值和上一次读取到的一致,就确认开关的状态真的改变了。
Arduino 的所有数字和模拟引脚都能读取 / 输出 0 与 1 信号。只要输入值超过电源电压的一半,就代表高电压高电位;若输入值低于 0.25V,则代表低电位。
这样的开关接法并不正确:
若没有按下开关,Arduino 的引脚既没接地接地,也为接到高电位。输入信号可能在 0 与 1 之间的模糊地带漂移,造成所谓的浮动信号,Arduino 将无法正确判断输入值。
正确接法如下:
若开关没有被按下,数字第 2 脚将通过 10kΩ 接地,因而读取到低电位值;按下开关时,5V 电源将流入第 2 脚,产生高电位。如果没有 10kΩ 电阻,按下开关时,正电源将和接地直接相连,造成短路。
像上图一样,在芯片的脚位连接一个电阻再接地,则此电阻称为下拉电阻
当然也有上拉电阻,即将电阻接到电源,像下图这样:
材料 | 数量 |
---|---|
Arduino Uno | 1 |
面包板 | 1 |
1kΩ电阻 | 1 |
10kΩ电阻 | 2 |
LED | 1 |
开关 | 2 |
导线 | 若干 |
1 |
|
摘要
第一次参加CTF,小白视角参赛体验,从零开始学CTF
[TOC]
这一题其实我看了蛮久的,好几题那没看出啥来,不过后来想到这题只有100分,那应该不是很难。果然是自己想太多!
打开网页 http://hitchhiker42.0727.site:42420/ ,看到如下页面
直接一个404,没啥信息,于是 F12 打开控制台看看有没有什么线索。header 里没看见什么奇怪的信息,只有页面源代码给出了一个 link 连接HitchhikerGuide.php
那就去看看那里有啥。
直接访问 http://hitchhiker42.0727.site:42420/HitchhikerGuide.php
好家伙又来到404页面,但是注意到这次来到的是index.php
。打开控制台,发现是302转向到了index.php
页面。
大胆猜测 maybe 访问方式不对,那把 GET 方式换成 POST方式试试。打开 Hackbar 插件,启用 POST请求,发送!有信息了!
使用Infinite Improbability Drive
方式?可能再说 User-Agent ?那 header 里加上 User-Agent 看看。
好家伙,成功了。不过又说要从 Cardinal 过来,那么很 easy,header 里再加上 Referer
参数。
没毛病,成功了。又说要本地访问,那就在 header 里加个X-Forwarded-For
参数,值为127.0.0.1
。
GET!hgame{s3Cret_0f_HitCHhiking_in_the_GAl@xy_i5_dOnT_p@nic!}
简单上头的小游戏——真的上头,完了好久好久!
不难发现,得到了 2000 分才能拿到 flag。
也许能通过操作 js 来获得 flag。
通过审计页面源代码,很快定位到了一个叫project.js
的文件,里面的变量 e 应该就是分数。
发现里面有这样一段代码:
1 |
|
atob()
方法用于解码使用 base-64 编码的字符串,那么aGdhbWV7ZG9feW91X2tub3dfY29jb3NfZ2FtZT99
应该就是 base-64 编码的字符串。解码一下
GET!hgame{do_you_know_cocos_game?}
这应该是一道关于HTTP走私的题目,目前知识学明白了,但操作上不知道哪出了问题一直没成功。
BUT!我好像多次搭上了顺风车
flag如上图,具体搞明白了再补充吧。
100道定积分计算,做完给flag。服务器通过临时session来识别用户,因此手算必然不可能。那么试试自动计算?
瞬间想到Python,打算试试 selenium 自动化。但是对于分析了页面源码后,我发现以我现有水平读取积分上下限的值和程式有难度(负号在单独的标签里)。所以打算先收算几道看看有没有啥规律。
然后在微软数学手机软件的帮助下,做了十几道题,发现规律——积分下限都是负整数,上限都是正整数,积分式子都是 Ax+B
的形式,其中 A 和 B 都为正整数
那就很 ez 了,编写Python代码:
1 |
|
Ctrl
+Shift
+F10
,稍等片刻:
得到Flag hgame{3very0ne_H4tes_Math}
我还没整明白
这题蛮easy的,都说了 Base全家福 那应该就是 Base混合编码的解码,直接用 python 解决
1 |
|
运行!
得到Flag result: hgame{We1c0me_t0_HG4M3_2021}
啊哈原来是签到题
参考文章:
]]>设有三个小灯A、B、C 事先处于熄灭状态,接着A 灯开始由暗到亮,再由亮变暗,A 灯熄灭之后B 灯开始由暗变亮,再由亮变暗,B 灯熄灭之后C 灯开始由暗变亮,再由亮变暗,如此循环。
数字信号只有高、低电位两种状态,如同第一章的LED闪烁程序,把一只LED接上Arduino的第13引脚,每隔0.5s切换高低电位,LED将不停闪烁。这是一种以一秒钟为周期的切换信号,频率就是1Hz。提高切换频率(通常指30Hz以上),将能仿真模拟电压高低变化的效果。以下图的1kHz为例,若脉冲宽度(开启时间)为周期的一半(称为50%工作周期),就相当于输出高电位的一半电压;10%工作周期,就相当于输出0.5V。
如此,不需采用电阻降低电压,电能不会在变换的过程被损耗掉。这种在数字系统上“仿真”模拟输出的方式,称为脉冲宽度调制(Pulse Width Modulation,简称PWM)。某些强调省电的变频式洗衣机和冷气机等家电,也是运用PWM原理来调节机器的运转速度。
PWM的计算方式如下。
因此,在5V电源的情况下输出3.3V,从上面的式子可知:
根据计算结果得知,5V电源的66%PWM脉冲宽度就相当于输出3.3V。
Arduino的analogWrite指令可以指挥输出PWM信号,指令格式如下:
1 |
|
其中端口号必须是3、5、6、9、10或11这六个数字端口的其中之一;模拟数值介于0255之间,代表输出介于05V之间的仿真模拟电压值。
此外,Arduino微电脑板预设采用1kHz和500Hz两组不同的PWM输出频率,控制电机时,一般采用1kHz频率:
led的工作电压
颜色 | 正向电压 |
---|---|
红色 | 1.7V ~ 2.2V |
橙色 | 2.0V |
黄色 | 2.1V |
绿色 | 2.2V |
蓝色 | 3.2V ~ 3.8V |
白色 | 3.2V ~ 3.8V |
LED的工作电压约2V,但Arduino的输出电压是5V,我们应该在Arduino的输出和LED之间连接一个限流电阻。连接方式有两种。
左边的接法是由微处理器提供负载所需电流,一般称之为源流(sourve current);
右边的接法是由电源(Vcc)提供电流,此谓之替流(sink current)
为计算方便,LED工作电压通常取2V,电流去10mA(注:高亮度LED的工作电压约3V,工作电流约30mA)。
简简单单设计完成
材料 | 数量 |
---|---|
Arduino Uno板 | 1 |
面包板 | 1 |
LED | 3 |
1kΩ电阻 | 3 |
杜邦线 | 若干 |
1 |
|
此视频可以在电脑网页中加载(推荐chrome、Firefox),手机请用Firefox或chrome打开
]]>已经两年多没碰单片机了,现在正好有空,决定重新开始认真学习一下。
这一篇Blog主要介绍Arduino和基本元器件的一些基础知识
Arduino是一个基于易于使用的硬件和软件的开源原型设计平台。 Arduino板能够读取输入 - 传感器上的灯光,按钮上的手指或Twitter消息 - 并将其转换为输出 - 激活电机,打开LED,在线发布内容。您可以通过向板上的微控制器发送一组指令来告诉您的电路板该做什么。为此,您需要使用Arduino编程语言(基于连线)和基于Processing的Arduino软件(IDE)。
Arduino Uno开发板可以使用三种方式供电:
直流电源插孔 -可以使用电源插孔为Arduino开发板供电。电源插孔通常连接到一个适配器。开发板的供电范围可以是5-20V,但制造商建议将其保持在7-12V之间。高于12V时,稳压芯片可能会过热,低于7V可能会供电不足。
VIN引脚 - 该引脚用于使用外部电源为Arduino Uno开发板供电。电压应控制在上述提到的范围内。
USB电缆 - 连接到计算机时,提供500mA/5V电压。
5v和3v3
根据制造商的数据手册,它们提供稳压的5V和3.3v,向外部组件供电。
GND
在Arduino Uno引脚分配图中,可以看到有5个GND引脚,它们都是互连的。
GND引脚用于闭合电路回路,并在整个电路中提供一个公共逻辑参考电平。务必确保所有的GND(Arduino、外设和组件)相互连接并且有共同点。
RESET - 复位Arduino开发板。
IOREF - 该引脚是输入/输出参考。它提供了微控制器工作的参考电压。
Arduino Uno有6个模拟引脚,它们作为ADC(模数转换器)使用。
这些引脚用作模拟输入,但也可用作数字输入或数字输出。
Arduino引脚A0-A5能够读取模拟电压。在Arduino上,ADC具有10位分辨率,这意味着它可以通过1,024个数字电平表示模拟电压。 ADC将电压转换成微处理器可以理解的位。
Arduino Uno的引脚0-13用作数字输入/输出引脚。其中,引脚13连接到板载的LED指示灯;引脚3、5、6、9、10、11具有PWM功能。
当数字引脚配置为输入时,电压由外部设备提供。该电压可以在0-5V之间变化,并转换成数字表示(0或1)。为了确定这一点,有2个阈值:
低于0.8v - 视为0
高于2.0v - 视为1
脉宽调制(PWM)是一种调制技术,用于将消息编码为脉冲信号。 PWM由两个关键部分组成:频率和占空比。 PWM频率决定了完成单个周期(周期)所需的时间以及信号从高到低的波动速度。占空比决定信号在总时间段内保持高电平的时间。
以1kHz为例,若脉冲宽度为周期一半(称50%工作周期),就相当于输出高电位的一半电压;10%工作周期,就相当于高电位的十分之一电压。
PWN的电压输出计算方式如下:
模拟输出电压=脉冲宽度×高电平值
这是Arduino官方提供的集成开发环境。里面提供了一些示例程序和头文件。
安装过程略。但值得注意的是,如果购买的是部分国产arduino板,官方驱动可能无法识别到设备,此时需要安装CH340驱动。BTW,Mac OS和Linux不需要驱动。
当然也可以使用vscode进行开发,只需安装如下两个插件即可
新建一个adruino程序,输入ar
+Tab
即可自动生成程序基础框架。
vscode右下角可以设置开发板信息和端口信息
右上角可以进行编译、上传等操作
注意:使用前记得将arduino ide路径添加进vscode中arduino设置的path
参考文章:
引脚分配图及定义
我个人的 Office OneNote 笔记本
在手机 app 登陆不方便的时候可以在这里查看
参考文章:
参考链接
摘要
C字符串函数,有必要好好学习,对一些字符串的处理,用正确的函数很重要。
1 |
|
结果应该是只打印 New 这个字符串。可能会觉得 str1 的长度比 str2 长,调用 strcpy 函数后会只覆盖前边的内容,后边保留……但事实上并不是这样,因为 strcpy 函数复制 str2 的时候,会将该字符串最后的 ‘\0’ 也一并复制过去。如图:
1 |
|
输出为:
1 |
|
参数 | 含义 |
---|---|
dest | 指向存放字符串的目标数组 |
src | 指向待拷贝的源字符串 |
n | 指定拷贝的最大长度 |
和 strcpy 函数一样,strncpy(dest, src, n) 函数将拷贝源字符串的 n 个字符到目标数组中。如果源字符串的长度小于 n,那么就用 ‘\0’ 填充额外的空间。如果源字符串的长度大于或等于 n,那么只有 n 个字符被拷贝到目标数组中(注意:这样的话将不会以结束符 ‘\0’ 结尾)。
为了使该函数更“安全”,建议使用 dest[sizeof(dest) - 1] = ‘\0’; 语句确保目标字符串是以 ‘\0’ 结尾.
1 |
|
该程序输出sizeof str = 21
strlen str = 7
sizeof 运算符是取得字符串的尺寸,即该字符串所处存储空间的大小。
代码开头的 char str[] = "I love mjclouds.com!"
决定了该字符数组的尺寸。而字符串的长度则是由第一个遇到的结束符(’\0’)所定义的。只要编译器读取到结束符\0
,它不管你字符数组后边是否有其它内容,都会认为字符串已经结束。
1 |
|
输出是:
1 |
|
**strcat(dest, src) **
参数 | 含义 |
---|---|
dest | 指向用于存放字符串的目标数组,它应该包含一个字符串,并且提供足够容纳连接后的总字符串长度的空间(包含结束符 ‘\0’) |
scr | 指向待连接的源字符串,该参数不应该与 dest 参数指向的位置发生重叠 |
strcat 函数用于连接两个字符串。
将源字符串拷贝并连接到目标数组存放的字符串后边,此过程将覆盖第一个参数的结束符 ‘\0’。
两个参数的位置不应该重叠。
1 |
|
输出为:
1 |
|
strncat(dest, src, n)
参数 | 含义 |
---|---|
dest | 指向用于存放字符串的目标数组,它应该包含一个字符串,并且提供足够容纳连接后的总字符串长度的空间(包含结束符 ‘\0’) |
src | 指向待连接的源字符串,该参数不应该与 dest 参数指向的位置发生重叠 |
n | 指定待连接的源字符串的最大长度 |
strncat 函数用于拷贝源字符串中的 n 个字符到目标数组的字符串后边,并在末尾添加结束符 ‘\0’。
如果源字符串的长度小于 n,那么不会像 strncpy 函数那样使用 ‘\0’ 进行填充(但结束符 ‘\0’ 还是有的)。
另外,目标数组中的原有的字符串并不算在 n 中。
1 |
|
输出为:
1 |
|
strcmp(s1, s2)
(str1, str2, n)
参数 | 含义 |
---|---|
s1 | 指向待比较的字符串 1 |
s2 | 指向待比较的字符串 2 |
n | 指定待比较的字符数 |
strcmp 函数用于比较两个字符串。
该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,直到发现两个字符不相等或抵达结束符(’\0’)为止。
返回值<0,意思是 字符串 1 的字符小于字符串 2 对应位置的字符;
返回值=0,意思是 字符串 1 的字符等于字符串 2 对应位置的字符;
返回值>0,意思是 字符串 1 的字符大于字符串 2 对应位置的字符;
strncmp 函数用于比较两个字符串的前 n 个字符。
该函数从第一个字符开始,依次比较每个字符的 ASCII 码大小,发现两个字符不相等或抵达结束符(’\0’)为止,或者前 n 个字符完全一样,也会停止比较。
返回值类型同strcmp
参考文章:
参考链接
摘要
首页显示摘要内容(替换成自己的)
参考文章:
参考链接
摘要
首页显示摘要内容(替换成自己的)
The Portable Document Format (PDF) is a file format developed by Adobe in 1993 to present documents, including text formatting and images, in a manner independent of application software, hardware, and operating systems.Based on the PostScript language, each PDF file encapsulates a complete description of a fixed-layout flat document, including the text, fonts, vector graphics, raster images and other information needed to display it. PDF was standardized as ISO 32000 in 2008, and no longer requires any royalties for its implementation.
PDF files may contain a variety of content besides flat text and graphics including logical structuring elements, interactive elements such as annotations and form-fields, layers, rich media (including video content), and three-dimensional objects using U3D or PRC, and various other data formats. The PDF specification also provides for encryption and digital signatures, file attachments, and metadata to enable workflows requiring these features.
说人话,PDF是一种版式文件,它有以下几个优点:
所以用PDF格式文件提交作业绝对是个很不错的决定。那我们如何制作一个PDF文件?
其实,几乎任何格式的文件都可以被转换为pdf,包括但不限于Word、Excel、PPT文件。如果电脑中没有安装Acrobat这类软件,我们可以按照如下方法将文件转换为PDF。这里以Word为例。
文件
→打印
Microsoft Print to PDF
,按照自己的输出需求进行设置。这里一般默认即可。配置完成后点击打印
将打印输出另存为
的对话框,选择希望将pdf保存在哪,然后点保存
未完待续。将提供Acrobat安装包和使用教程
参考文章:
参考链接
摘要
首页显示摘要内容(替换成自己的)
众所周知,当打开GitHub上几乎任何一个仓库,都会有一个readme.md文件,就像这张图展示的一样。在GitHub中可以轻松浏览这种格式的文件,一般里面写的是该仓库的介绍及操作说明,但是将该文件下载到电脑里,普通的办公软件却打不开。So what is it?
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档。Markdown 编写的文档可以导出 HTML 、Word、图像、PDF、Epub 等多种格式的文档。Markdown 编写的文档后缀为 .md
, .markdown
。当前许多网站都广泛使用 Markdown 来撰写帮助文档或是用于论坛上发表消息。例如GitHub、简书等。
Markdown 完全由标点符号组成, 这些标点经过仔细挑选以使他们看上去和表达的含义相同. 例如, 星号(*)标记的单词就像 强调. 列表就像是列表. 如果你使用过 email 的话, 就连块引用都像引用的文本段落.
Markdown 是用于创作web 文档的.Markdown 从来都不是要取代 HTML . Markdown 的目标是易于阅读, 创作和编辑文章. HTML 是一种 发布 格式; Markdown 是一种 创作 格式. 因此, Markdown 处理的都是纯文本.
Markdown标题有两种格式。
使用=
和-
标记一级标题和二级标题
显示效果如下:
标记语言格式:
1 |
|
值得注意的是,有时候我们用Typora等软件编辑的时候,输入完标题行敲击Enter
并输入====
却没有标题效果,我们只需将Enter
换为Shift+Enter
便可解决这一问题,这是由于Enter为换段而非换行所致。具体在段落章节中具体介绍。
段落
Markdown语法中,
列表
区块
代码
链接
图片
表格
其他
参考文章:
参考链接