网络靶场实战-Qiling Fuzz实例分析

背景

在上一小节中,介绍了qiling框架的背景和基础使用,并以相关的CTF和qilinglab实例进行练习加深对qiling框架的使用,后续并简单介绍了qiling fuzz的功能。

在这一小节,我们将对qiling fuzz iot设备进行测试以及以实例的方式对其进行学习。

qiling fuzz 基础

qiling和AFL++环境的搭建在前面的小节中已经说过,这里就不再演示。我们进入到qiling的qiling/example/fuzzing目录下,qiling框架官方库提供了几个fuzz的example供我们学习和测试。我们先对tenda ac15进行测试。

图片

根据README文档中的介绍,我们首先需要提取tenda ac 15文件系统并放置于脚本同级目录中,操作步骤如下:

1. wget https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip
2. unzip US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip
3. binwalk -e US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
4. mv xxx/squashfs-root ./rootfs;cd rootfs
5. rm -rf webroot;mv webroot_ro webroot
6. mv etc_ro etc

随后我们需要运行saver_tendaac15_httpd.py

图片

使用netstat -pantl查看监听端口,当发现python3程序正在监听8080端口时,说明tenda ac仿真成功。

图片

此时我们运行./addressNet_overflow.sh生成snapshot.bin文件。

图片

运行./fuzz_tendaac15_httpd.sh进行fuzz,经过10分钟左右出现了crash。

图片

产生的crash文件内容如下

图片

这样我们就完成对实例中tenda ac15的fuzz复现。官方提供demo的saver和fuzz脚本如下,现在我们对其进行简单分析并学习。

图片

图片

通过上一小节中对qiling基础的学习,我们可以对两个脚本中的函数功能进行拆分。

saver.py

保存快照

def save_context(ql, *args, **kw):ql.save(cpu_context=False, snapshot="snapshot.bin")

替换网卡名称

def patcher(ql):br0_addr = ql.mem.search("br0".encode() + b'\x00')for addr in br0_addr:ql.mem.write(addr, b'lo\x00')

检查停止地址

def check_pc(ql):print("=" * 50)print("Hit fuzz point, stop at PC = 0x%x" % ql.arch.regs.arch_pc)print("=" * 50)ql.emu_stop()

网络设置

def nvram_listener():server_address = 'rootfs/var/cfm_socket'data = ""try:os.unlink(server_address)except OSError:if os.path.exists(server_address):raise  sock = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)sock.bind(server_address)sock.listen(1)while True:connection, client_address = sock.accept()try:while True:data += str(connection.recv(1024))if "lan.webiplansslen" in data:connection.send('192.168.170.169'.encode())else:break  data = ""finally:connection.close()

仿真流程

def my_sandbox(path, rootfs):ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)ql.add_fs_mapper("/dev/urandom","/dev/urandom")ql.hook_address(save_context, 0x10930)ql.hook_address(patcher, ql.loader.elf_entry)ql.hook_address(check_pc, 0x7a0cc)ql.run()

fuzz.py

替换网卡名称

def patcher(ql):br0_addr = ql.mem.search("br0".encode() + b'\x00')for addr in br0_addr:ql.mem.write(addr, b'lo\x00')

fuzz流程

def main(input_file, enable_trace=False):# 生成qiling实例ql = Qiling(["rootfs/bin/httpd"], "rootfs", verbose=QL_VERBOSE.DEBUG, console = True if enable_trace else False)# 恢复快照内容ql.restore(snapshot="snapshot.bin")# 变异数据地址点定位fuzz_mem=ql.mem.search(b"CCCCAAAA")target_address = fuzz_mem[0]# target_address为fuzz变异点,place_input_callback函数通过afl++对数据进行变异def place_input_callback(_ql: Qiling, input: bytes, _):_ql.mem.write(target_address, input)# fuzz函数定义def start_afl(_ql: Qiling):ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])ql.hook_address(callback=start_afl, address=0x10930+8)# qiling实例运行try:ql.run(begin = 0x10930+4, end = 0x7a0cc+4)os._exit(0)except:if enable_trace:print("\nFuzzer Went Shit")os._exit(0)

通过功能的拆分以及我们的分析,可以知道fuzz大致流程是:

1.运行saver.py生成qiling实例仿真运行,此时运行addressNet_overflow.sh触发相关执行流程,当pc寄存器运行到0x10930地址时,触发保存快照功能。

2.fuzz.sh会调用afl++并执行fuzz.py脚本对其input输入进行数据变异。

3.fuzz.py脚本中,首先会恢复快照状态,并在内容中寻找数据变异点,并接受afl++的变异数据将其写入数据变异点进行fuzz。

了解了大致fuzz流程,我们可能存在几点疑虑:

1.saver.py脚本中如何知道在哪个地址触发保存快照功能?在仿真的httpd程序触发执行addressNet_overflow.sh执行流程后,程序将变异数据存储至内存完成后,就可以触发快照保存功能。 

2.addressNet_overflow.sh脚本中为什么定义page为CCCCAAAA?CCCCAAAA为poc的溢出标识,以便后续我们进行查找定位。

3.fuzz.py脚本中为什么先要替换br0?tenda ac15路由器设备启动时会检测br0网卡状态,我们本地没有这个网卡所以替换成了lo。

4.fuzz.py脚本中如何知道ql.run的起始和结束地址?起始地址是保存快照后面的指令,需要保证执行流程的连贯性(保存寄存器状态除外),结束地址便是漏洞函数可以触发crash后的函数结束地址。

这里1、4还是不太清楚,带着疑问我们接着往下分析:

根据addressNet_overflow.sh脚本中的poc,我们使用ida进行定义,发现漏洞函数如下,产生漏洞的原因便是没有对用户发送post包data数据中的entrys、mitInterface、page参数进行过滤,并使用sprintf危险函数进行了写入。

图片

那么我们要fuzz的函数就是formAddressNat函数了,首先第1点saver.py脚本中该如何定位保存地址点,这里其实当v1=sprintf(xxx)执行完毕后,已经将用户参数数据保存到v6中时,就已经可以保存快照了(后续fuzz.py也要进行相应修改)。这里作者定位的是0x10930,那么后续fuzz的起始地址和结束地址分别就是0x10930执行流程的后面下一条指令和formAddressNet函数的结束地址。

图片

在分析的过程中,我们可以打开QL_VERBOSE.DISASM来清楚的查看汇编指令的执行流程和对应指令的寄存器信息。

分析完tenda ac同理example中的dir815实例也是同样的流程,只不过dir815的fuzz脚本并没有使用保存快照功能,而是直接使用ql.mem.search进行查找变异数据点以及使用ql.mem.write对变异数据进行写入。

图片

图片

分析完上面的流程后,我们对fuzz的流程有了大概理解。后面我们以dlink dir645路由器中的两个栈溢出实例进行fuzz测试。

qiling fuzz 实例

以经典的dir645栈溢出为例,我们使用qiling框架对两个栈溢出漏洞进行fuzz测试。

首先下载固件并使用binwalk -Me 固件名进行提取,简单查看后发现本次分析的程序hedwig.cgi和authentication.cgi均为软链接(链接到htdocs/cgibin),qiling对软连接的处理不是很友好,建议将所有软连接替换为源文件。

hedwig.cgi栈溢出

我们首先将cgibin拖入ida进行简单分析,进入main函数后发现,main程序根据传入参数与相关"*.cgi"进行比较,随后进入相关的cgi_main函数中,那我们先分析一下hedwig.cgi触发的栈溢出

图片

fuzz的第一步就是摸清楚程序的执行流程,我们先简单编写仿真程序的脚本,随后将其改为fuzz脚本。该仿真脚本定义了俩个hook函数,当程序执行执行地址处后会执行该hook函数,并打印出"Hit at xxx func"。

图片

执行仿真脚本,发现我们定义的俩个hook都被触发,说明我们简单分析后的执行流程确实没错,并且调试信息后续还打印出了一下回应信息。

图片

我们继续在ida中查找定位该信息,发现是LABEL_25中的处理,根据交叉引用,我们追踪该信息产生的原因是env中没有REQUEST_METHOD

图片

图片

那么我们在env中设置该环境变量然后传入给qiling实例就可以了,接着往下分析发现程序的溢出点位于sess_get_uid中,并通过QL_VERBOSE.DISASM信息,我们理清楚了大概的产生漏洞流程。定位了栈溢出地址后,那么我们根据漏洞产生流程设置相关的env参数数据并传入qiling实例中,随后我们使用mem.search()替换成为变异数据,程序仿真时就会取出环境变量中的值进行处理从而产生栈溢出。

注:由于程序是从env中读取变量的值,所以也就不存在前面提到的拷贝到内存中然后触发保存快照功能指定流程,这里可以直接触发保存快照的功能。

图片

最终编写saver.py脚本如下:

import ctypes, os, pickle, socket, sys, threading
sys.path.append("..")
from qiling import *
from qiling.const import QL_VERBOSEMAIN = 0x402770
HEDWIGCGI_MAIN = 0x40bfc0
SESSION_UID = 0x4083f0
SAVE_ADDRESS = 0x40c070def test_print1(ql: Qiling)->None:print("Hit at main func")def test_print2(ql: Qiling)->None:print("Hit at hedwig func")def test_print3(ql: Qiling)->None:print("Hit at session uid func")def saver(ql: Qiling):print('[!] Hit Saver 0x%X'%(ql.arch.regs.arch_pc))ql.save(cpu_context=False, snapshot='./context.bin')def my_sandbox(path, rootfs):env_vars = {"REQUEST_METHOD": "POST","REQUEST_URI": "/hedwig.cgi","CONTENT_TYPE": "application/x-www-form-urlencoded","REMOTE_ADDR": "127.0.0.1","HTTP_COOKIE": "uid=AAAABBBB"}ql = Qiling(path, rootfs,env=env_vars,verbose=QL_VERBOSE.DEBUG)ql.hook_address(test_print1, MAIN)ql.hook_address(test_print2, HEDWIGCGI_MAIN)ql.hook_address(test_print3, SESSION_UID)ql.hook_address(saver, SAVE_ADDRESS)ql.run()if __name__ == "__main__":my_sandbox(["rootfs/htdocs/web/hedwig.cgi"], "rootfs")

执行后,保存的快照为context.bin,我们可以使用strings定位栈溢出标识字符串。

图片

图片

接下来我们编写fuzz.py,前面我们触发快照的地址为getenv("REQUEST_METHOD") 执行后的一条指令,那么我们在编写fuzz.py中ql.run的起始地址时就应该为下一条指令,这里为了方便我直接让其跳过if判断直接从cgibin_parse_request处开始执行(0x40c0a4)。结束地址呢,这里直接指定hedwigcgi_main函数的结尾就可以(0x40c598),因为有溢出数据时程序执行到函数最后一定会触发crash。

图片

最终hedwig.cgi栈溢出fuzz.py的脚本如下:

import os, pickle, socket, sys, threading
sys.path.append("../../../")
from qiling import *
from qiling.const import QL_VERBOSE
from qiling.extensions.afl import ql_afl_fuzzdef main(input_file, enable_trace=False):ql = Qiling(["rootfs/htdocs/web/hedwig.cgi"], "rootfs", verbose=QL_VERBOSE.DEBUG)ql.restore(snapshot="context.bin")fuzz_mem=ql.mem.search(b"AAAABBBB")target_address = fuzz_mem[0]def place_input_callback(_ql: Qiling, input: bytes, _):_ql.mem.write(target_address, input)def start_afl(_ql: Qiling):ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])ql.hook_address(callback=start_afl, address=0x40c0a4)try:ql.run(begin = 0x40c0a4, end = 0x40c598)os._exit(0)except:if enable_trace:print("\nFuzzer Went Shit")os._exit(0)if __name__ == "__main__":if len(sys.argv) == 1:raise ValueError("No input file provided.")if len(sys.argv) > 2 and sys.argv[1] == "-t":main(sys.argv[2], enable_trace=True)else:main(sys.argv[1])

不到1分钟就fuzz到了crash,还是比较快的。

图片

authentication.cgi栈溢出

和上面的分析同理,我们首先跟一下程序的执行流程,authentication.cgi的处理函数为authenticationcgi_main函数。

图片

进入到authenticationcgi_main函数后,我们发现和上面的hedwig类似,也是同样获取env中的变量进行处理。

图片

那么我们将前面的脚本进行修改,这里直接将HEDWIGCGI_MAIN改为0x40afcc。

图片

运行后发现执行触发了俩个hook函数,说明确实执行到了authenticationcgi_main函数中。

图片

authentication.cgi触发栈溢出的执行流程为REQUEST METHOD方法为POST,并且需要设置"CONTENT_TYPE"和"CONTENT_LENGTH"环境变量。

图片

那么我们在env变量中设置如下参数并传入qiling实例。

注:CONTENT_LENGTH中的999在程序执行时还没有溢出,v73定义的1024字节。

图片

运行后发现已经执行我们想要其执行的流程了,并且需要我们输入一些信息才可执行后面的流程。

图片

图片

input中含有的内容如下,需要包含"id=xxx&password=xxx"

图片

图片

再次执行,输入"id=1&password=123"后,程序正常执行。那么我们的fuzz思路如下:

传入env环境变量,使其按照漏洞触发流程进行执行,随后在程序赋值content_length时,进行hook,将需要用到的寄存器修改成afl++变异数据的大小(其实这里应该取出所有header的字节,不过这里不是很影响),随后进行调用start_afl进行fuzz。写入栈溢出标识地址的content格式为:b"id=1&password="+input 

图片

根据上面的信息,我们编写的fuzz.py如下:

import ctypes, os, pickle, socket, sys, threading
sys.path.append("../../../")
from qiling import *
from qiling.const import QL_VERBOSE
from qiling.extensions import pipe
from qiling.extensions.afl import ql_afl_fuzzMAIN = 0x402770
AUTHENTICATION_MAIN = 0x40afcc
CONTENT_LENGTH = 0x40b48c
CONTENT_SIZE = 0x40b4b4
size = 0def test_print1(ql: Qiling)->None:print("Hit at main func")def test_print2(ql: Qiling)->None:print("Hit at authentication func")def test_print3(ql: Qiling):print("address:",hex(ql.arch.regs.s0))def test_print4(ql: Qiling):print("Hit at exit func")def test_size(ql: Qiling):global sizeql.arch.regs.s0 = sizeql.arch.regs.a2 = sizeprint("Hit at test_size func")def main(input_file, enable_trace=False):env_vars = {"REQUEST_METHOD": "POST","REQUEST_URI": "/authentication.cgi","CONTENT_TYPE": "application/x-www-form-urlencoded","REMOTE_ADDR": "127.0.0.1","CONTENT_LENGTH": "100"}ql = Qiling(["rootfs/htdocs/web/authentication.cgi"], "rootfs",env=env_vars,verbose=QL_VERBOSE.DEBUG)ql.os.stdin = pipe.SimpleInStream(0)if not enable_trace:ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno())ql.os.stderr = pipe.NullOutStream(sys.stderr.fileno())def place_input_callback(ql: Qiling, input: bytes, _: int):global sizecontent = b"id=1&password="+inputsize = len(content)ql.os.stdin.write(content)ql.hook_address(test_size,CONTENT_SIZE)def start_afl(_ql: Qiling):ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])ql.hook_address(test_print1, MAIN)ql.hook_address(test_print2, AUTHENTICATION_MAIN)ql.hook_address(test_print3, CONTENT_LENGTH)ql.hook_address(test_print4,address=0x40bc90)ql.hook_address(callback=start_afl, address=AUTHENTICATION_MAIN)try:ql.run()os._exit(0)except:if enable_trace:print("\nFuzzer Went Shit")os._exit(0)if __name__ == "__main__":if len(sys.argv) == 1:raise ValueError("No input file provided.")if len(sys.argv) > 2 and sys.argv[1] == "-t":main(sys.argv[2], enable_trace=True)else:main(sys.argv[1])

运行后发现,afl++给到的变异数据确实传入了进去。

但是fuzz了一会儿发现没crash,原来是read(fd=0x0,buf=0x7ff3c940,length=0x64),这里读取的length还是100,也就是说afl++不管变异多少数据都只读取100字节,这说明我们的hook有问题。

图片

打开QL_VERBOSE.DUMP模式,查看read时寄存器变量的值,确实hook没生效。那么我们直接在其调用read函数时hook,使其寄存器变成我们变异数据的长度就可以了。

图片

图片

重新改正脚本

import ctypes, os, pickle, socket, sys, threading
sys.path.append("../../../")
from qiling import *
from qiling.const import QL_VERBOSE
from qiling.extensions import pipe
from qiling.extensions.afl import ql_afl_fuzzMAIN = 0x402770
AUTHENTICATION_MAIN = 0x40afcc
CONTENT_LENGTH = 0x40b48c
CONTENT_SIZE = 0x40b4a8
size = 1000def test_print1(ql: Qiling)->None:print("Hit at main func")def test_print2(ql: Qiling)->None:print("Hit at authentication func")def test_print3(ql: Qiling):print("address:",hex(ql.arch.regs.s0))def test_print4(ql: Qiling):print("Hit at exit func")def test_size(ql: Qiling):global sizeql.arch.regs.s0 = sizeql.arch.regs.a1 = sizeprint("Hit at test_size func")def main(input_file, enable_trace=False):global sizeenv_vars = {"REQUEST_METHOD": "POST","REQUEST_URI": "/authentication.cgi","CONTENT_TYPE": "application/x-www-form-urlencoded","REMOTE_ADDR": "127.0.0.1","CONTENT_LENGTH": "100"}ql = Qiling(["rootfs/htdocs/web/authentication.cgi"], "rootfs",env=env_vars,verbose=QL_VERBOSE.DEBUG)ql.os.stdin = pipe.SimpleInStream(0)if not enable_trace:ql.os.stdout = pipe.NullOutStream(sys.stdout.fileno())ql.os.stderr = pipe.NullOutStream(sys.stderr.fileno())def place_input_callback(ql: Qiling, input: bytes, _: int):global sizecontent = b"id=1&password="+inputsize = len(content)ql.os.stdin.write(content)def start_afl(_ql: Qiling):ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])ql.hook_address(test_print1, MAIN)ql.hook_address(test_print2, AUTHENTICATION_MAIN)ql.hook_address(test_print3, CONTENT_LENGTH)ql.hook_address(test_print4,address=0x40bc90)ql.hook_address(callback=start_afl, address=AUTHENTICATION_MAIN)ql.hook_address(test_size,CONTENT_SIZE)try:ql.run()os._exit(0)except:if enable_trace:print("\nFuzzer Went Shit")os._exit(0)if __name__ == "__main__":if len(sys.argv) == 1:raise ValueError("No input file provided.")if len(sys.argv) > 2 and sys.argv[1] == "-t":main(sys.argv[2], enable_trace=True)else:main(sys.argv[1])

运行后,3分钟左右fuzz出crash。

图片

图片

综上,我们以实例的方式分析并fuzz了dir645路由器的俩个栈溢出漏洞,可能很多人觉得在fuzz时已经知道了漏洞点和执行流程,使用qiling进行fuzz有点多余。但是对于我们没有实体设备或者qemu不能完全仿真时,即使我们知道了漏洞点,我们也没法去简单测试或验证,那么这时qiling框架就是一个比较好的选择。这个小节中qiling 的fuzz思路像是在验证漏洞而且比较基础,而在真正fuzz漏洞时,也许我们可以hook危险函数并进行fuzz,又或者其他思路,这些就靠大家的思维拓展了。

总结

在这小节中,我们使用qiling框架分析并测试了tenda ac15、dir 815以及dir 645实例设备的栈溢出漏洞,掌握了在分析fuzz时的基础思路。熟练掌握qiling框架的使用,对于后续我们的漏洞测试方面还是有很多的帮助。

图片

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

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

相关文章

C++ 面向对象-封装

C 是一种多范式编程语言,它支持面向对象编程(OOP)范式。面向对象编程是一种程序设计思想,其中程序由对象组成,每个对象都是一个实例,具有数据和相关操作。在C中,实现面向对象编程主要通过类和对…

蓝桥杯2022年第十三届决赛真题-卡牌

贪心:循环m次,首先每次取卡片数最少的种类,判断它是否有补充机会,如果没有补充机会,根据短板效应,它的数量就是套牌数,结束。如果可以补充,就将它的数目增加1,m的数量减少…

ubuntu22.04搭建dns内网

近期,需要在无网络的ubuntu环境下搭建内部可用的dns内网,总共花费3个工作日晚上,总算成功搭建,做个记录,记录踩坑记录,同时方便以后翻阅。 安装软件包: 有网络环境下,比较简单&…

一个java项目中,如何使用sse协议,构造一个chatgpt的流式对话接口

前言 如何注册chatGPT,怎么和它交互,本文就不讲了;因为网上教程一大堆,而且你要使用的话,通常会再包一个算法服务,用来做一些数据训练和过滤处理之类的,业务服务基本不会直接与原生chatGPT交互。…

OpenCV-基于阴影勾勒的图纸清晰度增强算法

作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 实现原理 大家在工作和学习中,无论是写报告还是论文,经常有截图的需求,比如图表、图纸等&…

使用 Docker 部署 TailChat 开源即时通讯平台

1)介绍 TailChat 官网: https://tailchat.msgbyte.com/ 作者:https://www.moonrailgun.com/about/ GitHub : https://github.com/msgbyte/tailchat TailChat 是一款插件化易拓展的开源 IM 应用。可拓展架构赋予 Tailchat 无限可能…

【前端】vue的基础知识及开发指引

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Vue是什么二、学习 Vue.js 的基础知识三、熟悉 Vue.js 的生态系统四、掌握常用工具和库五、实践和项目开发六、 持续学习和跟进 前言 随着开发语言及人工智…

使用Docker搭建本地Nexus私有仓库

0-1开始Java语言编程之路 一、Ubuntu下Java语言环境搭建 二、Ubuntu下Docker环境安装 三、使用Docker搭建本地Nexus Maven私有仓库 四、Ubuntu下使用VisualStudioCode进行Java开发 你需要Nexus Java应用编译构建的一种主流方式就是通过Maven, Maven可以很方便的管理Java应用的…

全国832个贫困县名单及精准扶贫脱贫(摘帽名单)数据(2016-2020.11)

01、数据简介 自党的十八大以来,我国脱贫攻坚战取得了举世瞩目的伟大胜利。经过全党全国各族人民的共同努力,现行标准下9899万农村贫困人口全部脱贫,832个贫困县全部摘帽,12.8万个贫困村全部出列,区域性整体贫困得到解…

金融风控信用评分卡建模(Kaggle give me credit数据集)

1 数据预处理数据 数据来源于Kaggle的Give Me Some Credit,包括25万条个人财务情况的样本数据 1.1 导包读数据 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.ensemble import RandomForestRegressor import seaborn as …

Excel图表智能排序

实例需求:表格中的多个图表如下图左侧所示,对于表格进行排序时,希望第一列中的图表跟随相应数据。 方法1: Sub SortTableWithChart()Dim oSht As Worksheet, RowCnt As Long, ColCnt As LongDim arrData, i As Long, oCht As Cha…

基于STM32CubeMX的嵌入式开发基础

内部没有上拉电阻,外部就要加一个 上拉或者下拉电阻,最基本上的作用是将状态不确定的信号通过一个电阻将其稳定在高电平或低电平 上拉下拉其实起的是稳定电平的作用 问题:单片机的外围电路设计及程序编写大多是以低电平有效来驱动电路的&…

【主流电商API接口数据采集】聚合电商API接口平台:让数据成为生产力!

API接口接入测试||文档 随着数字化商业时代的到来,API接口已成为电商资源连接利器,也是全球传统互联网企业转型的基础。 2021年 Google Cloud 研究显示,全球互联网企业近3/4的企业持续投入数字化转型,2/3的企业在持续增加投入&a…

轻松学会微信小程序开发(一)

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

Java进阶-Stream流

概述 在Java8中,得益于lambda所带来的函数式编程,引入了一个全新的Stream流的概念目的:用于简化集合和数组操作的api 案例 需求:创建一个集合存储多个字符串元素,将集合中所有以“z”开头的元素存储到新的集合中&am…

Torch 模型 感受野可视化

前言:感受野是卷积神经网络 (CNN) 中一个重要的概念,它表示 CNN 每一层输出的特征图上的像素点在输入图像上映射的区域。感受野的大小和形状直接影响到网络对输入图像的感知范围和精度,进而调整网络结构、卷积核大小和步长等参数,…

javaweb-maven

前端HTML,CSS,JS,Vue,Element,Nginx最后去复习, Java开发工程师 主要学习方向是服务端 所以进入javaweb的服务端的第一个知识点 maven 什么是maven 用于管理和构建java项目的工具 maven的官方网站 Maven – Welcome to Apache Maven …

Flink面试(1)

1.Flink 的并行度的怎么设置的? Flink设置并行度的几种方式 1.代码中设置setParallelism() 全局设置: 1 env.setParallelism(3);  算子设置(部分设置): 1 sum(1).setParallelism(3) 2.客户端CLI设置&#xff0…

邀请全球创作者参与 The Sandbox 创作者训练营

作为首屈一指的元宇宙平台之一,The Sandbox 的使命是成为全球创作者的中心。随着我们对 Game Maker 的不断改进、旨在激发创作者灵感的定期 Game Jams、革命性的 "创作者挑战 "以及众多其他活动的开展,我们见证了大量个人加入我们充满活力的创…

opencv_5_图像像素的算术操作

方法1:调用库函数 void ColorInvert::mat_operator(Mat& image) { Mat dst; Mat m Mat::zeros(image.size(), image.type()); m Scalar(2, 2, 2); multiply(image, m, dst); m1 Scalar(50,50, 50); //divide(image, m, dst); //add(im…