网络靶场实战-物联网安全qiling框架初探

背景

Qiling Framework是一个基于Python的二进制分析、模拟和虚拟化框架。它可以用于动态分析和仿真运行不同操作系统、处理器和体系结构下的二进制文件。除此之外,Qiling框架还提供了易于使用的API和插件系统,方便使用者进行二进制分析和漏洞挖掘等工作。其创始人是一名IoT Hacker,创建qiling的初衷便是解决在研究IoT时遇到的种种问题,这也是为什么上一小节说qiling框架比unicorn框架更加适合IoT研究初学者。

qiling使用基础

qiling框架和AFLplusplus安装

sudo apt-get update
sudo apt-get install -y build-essential python3-dev automake cmake git flex bison libglib2.0-dev libpixman-1-dev python3-setuptools cargo libgtk-3-dev
sudo apt-get install -y lld-14 llvm-14 llvm-14-dev clang-14
sudo apt-get install -y gcc-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-plugin-dev libstdc++-$(gcc --version|head -n1|sed 's/\..*//'|sed 's/.* //')-dev
pip3 install qiling
git clone https://github.com/AFLplusplus/AFLplusplus
make -C AFLplusplus
cd AFLplusplus/unicorn_mode
./build_unicorn_support.sh

程序仿真

    首先我们需要克隆qiling仓库,仓库中一些实例脚本可供我们学习。

git clone --recurse-submodules https://github.com/qilingframework/qiling.git

    一个简单的示例:

#include <stdio.h>
#include <stdlib.h>
# gcc test.c -o test
# 注意:编译程序的主机libc需要与rootfs glibc版本(libc-2.7.so)相对应,其他架构同理
int main(){printf("hello world!");return 0;
}

    使用qiling编写一个简单的仿真脚本。

from qiling import *
from qiling.const import QL_VERBOSE
# 导入qiling模块和qiling.const模块中的QL_VERBOSE常量if __name__ == "__main__":#创建Qiling对象,实例中三个参数分别为:path(仿真程序路径)、rootfs(仿真程序文件系统目录)和verbose(输出信息参数),除此外还可以设置env和log_plain参数。ql = Qiling(["./x8664_linux_symlink/test"], "./x8664_linux_symlink",verbose=QL_VERBOSE.DEBUG)#运行Qiling对象的run()方法,开始执行仿真程序ql.run()

    这里的verbose(输出信息参数)有如下级别及其作用:

图片

图片

VFS劫持

    x86_fetch_urandom程序的作用为打开/dev/urandom文件,生成随机数。当qiling仿真x86_fetch_urandom程序时,环境需要用到仿真文件系统,我们就需要用到VFS劫持,这样就可以模拟修改文件系统。下面的代码中为仿真虚拟路径 "/dev/urandom" 会被映射到宿主系统上的现有"/dev/urandom"文件。当模拟程序将访问 /dev/random 时,将改为访问映射文件。

from qiling import Qilingif __name__ == "__main__":ql = Qiling(["x86_linux/bin/x86_fetch_urandom"], "x86_linux")ql.add_fs_mapper(r'/dev/urandom', r'/dev/urandom')ql.verbose=0ql.run()

图片

    如果我们想要控制虚拟文件'/dev/urandom'的交互结果,可以继承QlFsMappedObject类,并可自定义read、write、fstat、ioctl、readline等方法。

from qiling import Qiling
from qiling.os.mapper import QlFsMappedObjectclass FakeUrandom(QlFsMappedObject):def read(self, size: int) -> bytes:return b"\x01" #可以修改读取返回结果def fstat(self) -> int:return -1def close(self) -> int:return 0if __name__ == "__main__":ql = Qiling(["x86_linux/bin/x86_fetch_urandom"], "x86_linux")ql.add_fs_mapper(r'/dev/urandom', FakeUrandom())ql.run()

图片

函数hook

    下面示例中,我们给str1和str2俩个变量内存中分别复制"abcdef"和"ABCDEF"字符串。正常执行完毕后会打印出"str1 大于 str2"。我们可以使用qiling框架劫持strcmp实现为hook strcmp函数的效果,使其执行到不同分支的结果。

#include <stdio.h>
#include <string.h>//cd ./x8664_linux/
//gcc demo.c -o testint main ()
{char str1[15];char str2[15];int ret;strcpy(str1, "abcdef");strcpy(str2, "ABCDEF");ret = strcmp(str1, str2);if(ret < 0){printf("str1 小于 str2");}else if(ret > 0){printf("str1 大于 str2");}else{printf("str1 等于 str2");}return(0);
}

    以下代码为hook strcmp函数,并通过修改rax寄存器改变执行流程。

from qiling import *
from qiling.const import *# 自定义strcmp hook函数。当程序执行strcmp函数退出时,会调用此函数,并且在比较完毕后,将 rax 寄存器的值修改为 0,表示相等。
def hook_strcmp(ql,*args):
# qiling框架的寄存器取值为ql.arch.reg.xxxrax = ql.arch.regs.raxprint("hook_addr_rax:",hex(rax))ql.arch.regs.eax = 0 # 0:等于; -1:小于 ;1:大于# 使用 ql.os.set_api 函数为 strcmp 设置hook函数,第一个参数为要hook的函数名,第二个参数为自定义hook函数,第三个参数为hook类型,这里为退出时触发hook函数。
def hook_func(ql):ql.os.set_api('strcmp',hook_strcmp,QL_INTERCEPT.EXIT) # 也可以使用ql.hook_address()函数进行hook,使用方法为ql.hook_address(hook_strcmp,0xXXXXXXXX)if __name__ == "__main__":ql = Qiling(["./x8664_linux/test"],"./x8664_linux",verbose=QL_VERBOSE.DEBUG)hook_func(ql)#ql.debugger = "gdb:0.0.0.0:12345"ql.run()

图片

    定义hook函数时hook类型参数有以下三种:

图片

qiling使用实例

使用qiling解密CTF赛题

    当我们掌握了最基础的三个用法后,我们可以测试一个简单的例子来加深对qiling框架的理解。以上一小节中unicorn解密ctf题目为例,我们先简单写一个运行脚本。这里的ql.debugger="gdb:0.0.0.0:12345"为开启gdbserver服务,我们可以使用ida或者gdb进行调试。

图片

    简单运行后发现程序和上一小节中unicorn的运行状况类似。由于这里我设置了multithead为True,所以这里会比上一小节中unicorn的解密速度快不少。但是还是在有限时间内只输出4个字符。

图片

    当我们将verbose设置为QL_VERBOSE.DISASM便可观察模拟执行的汇编指令,根据汇编指令我们明显看到程序在call 0x400670处进行了递归调用(或使用调试器调试查看),导致解密时间非常长。所以我们需要进行代码优化,思路为使用栈空间来保存一个不同输入参数以及对应计算结果的字典来避免重复计算。

图片

   这里qiling由于是由unicorn开发而来,所以很多用法和unicorn相似。

from qiling import *
from qiling.const import *
from pwn import *def hook_start(ql):arg0 = ql.arch.regs.rdir_rsi = ql.arch.regs.rsiarg1 = u32(ql.mem.read(r_rsi,4))if (arg0,arg1) in direct:(ret_rax,ret_ref) = direct[(arg0,arg1)]ql.arch.regs.rax = ret_raxql.mem.write(r_rsi,p32(ret_ref))ql.arch.regs.rip = 0x400582else:ql.arch.stack_push(r_rsi)ql.arch.stack_push(arg1)ql.arch.stack_push(arg0)def hook_end(ql):arg0 = ql.arch.stack_pop()arg1 = ql.arch.stack_pop()r_rsi = ql.arch.stack_pop()ret_rax = ql.arch.regs.raxret_ref = u32(ql.mem.read(r_rsi,4))direct[(arg0,arg1)] = (ret_rax,ret_ref)def solve(ql):start_address = 0x400670end_address = 0x4006f1end_address2 = 0x400709ql.hook_address(hook_start,start_address)ql.hook_address(hook_end,end_address)ql.hook_address(hook_end,end_address2)if __name__ == '__main__':path = ["./x8664_linux_symlink/test"]rootfs = "./x8664_linux_symlink"direct = {}ql = Qiling(path, rootfs,verbose=QL_VERBOSE.DEFAULT)solve(ql)ql.run()

运行后便会打印出解密结果。

图片

    除了上一小节中的ctf题目掌握qiling的使用外,我们还可通过qilinglab来加深对qiling框架的使用。qilingLab是由11个小挑战组成的二进制程序,用来帮助新手快速熟悉和掌握 Qiling 框架的基本用法。官方提供了aarch64程序的解题方法,我们根据这个作为参考解密一下x86_64架构的练习程序。

    x86_64程序下载(https://www.shielder.com/attachments/qilinglab-x86_64)

    首先运行程序,给我们提示,challenges会造成程序崩溃,只有当我们解出相应challenge后才会显示信息。

图片

    我们可以通过ida逆向以及编写qiling脚本进行动态调试来完成这些challenge。

图片

    最终的解密脚本如下:

from qiling import *
from pwn import *
from qiling.const import *
from qiling.os.mapper import QlFsMappedObject
import os
import structdef hook_cpuid(ql, address, size):if ql.mem.read(address, size) == b'\x0F\xA2':regs = ql.arch.regsregs.ebx = 0x696C6951regs.ecx = 0x614C676Eregs.edx = 0x20202062regs.rip += 2def challenge11(ql):begin, end = 0, 0for info in ql.mem.map_info:#print("=====")#print(info)#print("=====")if info[2] == 5 and 'qilinglab-x86_64' in info[3]:begin, end = info[:2]#print("begin_addr",begin)#print("end_addr",end)ql.hook_code(hook_cpuid, begin=begin, end=end)class cmdline(QlFsMappedObject):def read(self, expected_len):return b'qilinglab'def close(self):return 0def challenge10(ql):ql.add_fs_mapper('/proc/self/cmdline', cmdline())def hook_tolower(ql):return 0def challenge9(ql):ql.os.set_api('tolower', hook_tolower)def find_and_patch(ql, *args, **kw):MAGIC = 0x3DFCD6EA00000539magic_addrs = ql.mem.search(p64(MAGIC))#print("magic_address:",hex(magic_addrs))for magic_addr in magic_addrs:malloc1_addr = magic_addr - 8malloc1_data = ql.mem.read(malloc1_addr, 24)string_addr, _ , check_addr = struct.unpack("QQQ",malloc1_data)if ql.mem.string(string_addr) == "Random data":ql.mem.write(check_addr, b"\x01")breakdef challenge8(ql):base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])#print("base_addr",hex(base_addr))ql.hook_address(find_and_patch, base_addr+0xFB5)def hook_sleep(ql):return 0def challenge7(ql):ql.os.set_api('sleep',hook_sleep)def hook_rax(ql):ql.arch.regs.rax = 0def challenge6(ql):base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])#print("base_addr",hex(base_addr))hook_addr = base_addr + 0xF16ql.hook_address(hook_rax, hook_addr)def hook_rand(ql):ql.arch.regs.rax = 0def challenge5(ql):ql.os.set_api('rand',hook_rand)def enter_forbidden_loop_hook(ql):ql.arch.regs.eax = 1def challenge4(ql):base = ql.mem.get_lib_base(os.path.split(ql.path)[-1])hook_addr = base + 0xE43print("qiling binary hookaddr:",hex(hook_addr))ql.hook_address(enter_forbidden_loop_hook, hook_addr)class FakeUrandom(QlFsMappedObject):def read(self, size: int) -> bytes:if size == 1:return b"\x42"else:return b"\x41" * sizedef close(self) -> int:return 0def hook_getrandom(ql, buf, buflen, flags):if buflen == 32:data = b'\x41' * buflen # b'\x41' = Aql.mem.write(buf, data)ql.os.set_syscall_return(buflen)else:ql.os.set_syscall_return(-1)def challenge3(ql):ql.add_fs_mapper(r'/dev/urandom', FakeUrandom())ql.os.set_syscall("getrandom", hook_getrandom)def my_uname_on_exit_hook(ql, *args):rdi = ql.arch.regs.rdiprint(f"utsname address: {hex(rdi)}")ql.mem.write(rdi, b'QilingOS\x00')ql.mem.write(rdi + 65 * 3, b'ChallengeStart\x00')def challenge2(ql):ql.os.set_api("uname", my_uname_on_exit_hook, QL_INTERCEPT.EXIT)def challenge1(ql):ql.mem.map(0x1000, 0x1000, info='challenge1')ql.mem.write(0x1337, p16(1337))if __name__ == '__main__':path = ["./x8664_linux/qilinglab-x86_64"]rootfs = "./x8664_linux"ql = Qiling(path, rootfs,verbose=QL_VERBOSE.OFF)challenge1(ql)challenge2(ql)challenge3(ql)challenge4(ql)challenge5(ql)challenge6(ql)challenge7(ql)challenge8(ql)challenge9(ql)challenge10(ql)challenge11(ql)#ql.debugger = "gdb:0.0.0.0:12345"ql.run()

    运行后,所有的challenge都会显示SOLVED。

图片

qiling设备仿真

    qiling提供了路由器仿真案例,该脚本路径为qiling/example路径下

#!/usr/bin/env python3
# 1. Download AC15 Firmware from https://down.tenda.com.cn/uploadfile/AC15/US_AC15V1.0BR_V15.03.05.19_multi_TD01.zip
# 2. unzip
# 3. binwalk -e US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
# 4. locate squashfs-root
# 5. rm -rf webroot && mv webroot_ro webroot
#
# notes: we are using rootfs in this example, so rootfs = squashfs-root
#
import os, socket, threading
import sys
sys.path.append("../../../")
from qiling import Qiling
# 从qiling.const中导入QL_VERBOSE,指定qiling的日志输出级别
from qiling.const import QL_VERBOSE# 定义patcher函数,用于跳过网卡信息检测。在前面小节我们仿真tenda路由器时,路由器httpd程序在初始化网络时会检查网卡名称是否为br0。这里脚本直接将代码执行前内存中的br0字符串替换成了lo,从而跳过检查。
def patcher(ql: Qiling):br0_addr = ql.mem.search("br0".encode() + b'\x00')for addr in br0_addr:ql.mem.write(addr, b'lo\x00')# 定义nvram_listener函数,使用该函数监听Unix套接字,并在收到消息时返回数据。
def nvram_listener():server_address = 'rootfs/var/cfm_socket'data = ""try:os.unlink(server_address)except OSError:if os.path.exists(server_address):raisesock = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)sock.bind(server_address)sock.listen(1)while True:connection, _ = sock.accept()try:while True:data += str(connection.recv(1024))if "lan.webiplansslen" in data:connection.send('192.168.170.169'.encode())else:breakdata = ""finally:connection.close()# 定义myvfork函数,仿真程序在执行系统调用vfork时被调用,返回值0。
def myvfork(ql: Qiling):regreturn = 0ql.log.info("vfork() = %d" % regreturn)return regreturn
# 仿真主函数,生成qiling实例和添加VFS映射。
def my_sandbox(path, rootfs):print("path:",path)print("rootfs",rootfs)ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)print("ql:",ql)ql.add_fs_mapper("/dev/urandom","/dev/urandom")ql.hook_address(patcher, ql.loader.elf_entry)ql.debugger = Falseif ql.debugger == True:ql.os.set_syscall("vfork", myvfork) # vfork函数返回0时,debugger可正常调试。ql.run()if __name__ == "__main__":# 创建后台运行的线程并执行,以便收到Unix套接字的消息时进行响应。nvram_listener_therad = threading.Thread(target=nvram_listener, daemon=True)nvram_listener_therad.start()# 运行仿真实例my_sandbox(["rootfs/bin/httpd"], "rootfs")

    当我们运行脚本后,会显示路由器的ip和端口,当我们发现本地的8080正在监听时,说明设备已经仿真成功。

图片

    仿真成功后可访问http://localhost:8080查看效果:

图片

    在后面的小节中,我们会学习对仿真路由器设备进行fuzz。其中最为重要的一步便是编写仿真脚本,后续在我们分析好固件程序中要fuzz地址范围后,只有仿真设备可以顺利触发保存快照的功能,才可保证fuzz的正确性。

qiling fuzz

    qiling框架可以使用AFLplusplus对arm架构程序进行fuzz测试,测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Program that will crash easily.
#define SIZE (10)int fun(int i)
{char *buf = malloc(SIZE);char buf2[SIZE];while ((*buf = getc(stdin)) == 'A'){buf[i++] = *buf;}strncpy(buf2, buf, i);puts(buf2);return 0;
}int main(int argc, char **argv)
{return fun(argc);
}

    qiling提供的fuzz脚本如下:

#!/usr/bin/env python3
"""
Simple example of how to use Qiling together with AFLplusplus.
This is tested with the recent Qiling framework (the one you cloned),
afl++ from https://github.com/AFLplusplus/AFLplusplusAfter building afl++, make sure you install `unicorn_mode/setup_unicorn.sh`Then, run this file using afl++ unicorn mode with
afl-fuzz -i ./afl_inputs -o ./afl_outputs -m none -U -- python3 ./fuzz_x8664_linux.py @@
"""# No more need for importing unicornafl, try ql.afl_fuzz instead!import sys, os
from binascii import hexlify
sys.path.append("../../..")
from qiling import *
from qiling.extensions import pipe
from qiling.extensions.afl import ql_afl_fuzzdef main(input_file, enable_trace=False):ql = Qiling(["./arm_fuzz"], "../../rootfs/arm_qnx", console=enable_trace)# 设置ql的标准输入为进程的标准输入ql.os.stdin = pipe.SimpleInStream(sys.stdin.fileno())# 如果没有启用控制台追踪,则将标准输出和标准错误流设置为Nullif 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):# 设置fuzz输入点ql.os.stdin.write(input)return Truedef start_afl(_ql: Qiling):# 设置fuzz实例ql_afl_fuzz(_ql, input_file=input_file, place_input_callback=place_input_callback, exits=[ql.os.exit_point])# 获取libc的基地址LIBC_BASE = int(ql.profile.get("OS32", "interp_address"), 16)# 设置hook函数,用于处理SignalKill信号ql.hook_address(callback=lambda x: os.abort(), address=LIBC_BASE + 0x38170)# main函数地址main_addr = 0x08048aa0# 设置hook函数,在main函数运行时调用start_afl函数ql.hook_address(callback=start_afl, address=main_addr)# 若启用控制台追踪,则将设置相关信息输出if enable_trace:# The following lines are only for `-t` debug outputmd = ql.arch.disassemblercount = [0]def spaced_hex(data):return b' '.join(hexlify(data)[i:i+2] for i in range(0, len(hexlify(data)), 2)).decode('utf-8')def disasm(count, ql, address, size):buf = ql.mem.read(address, size)try:for i in md.disasm(buf, address):return "{:08X}\t{:08X}: {:24s} {:10s} {:16s}".format(count[0], i.address, spaced_hex(buf), i.mnemonic,i.op_str)except:import tracebackprint(traceback.format_exc())def trace_cb(ql, address, size, count):rtn = '{:100s}'.format(disasm(count, ql, address, size))print(rtn)count[0] += 1ql.hook_code(trace_cb, count)# okay, ready to roll.# try:ql.run()# except Exception as ex:# # Probable unicorn memory error. Treat as crash.# print(ex)# os.abort()os._exit(0) # that's a looot faster than tidying up.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])

    AFLplusplus执行脚本如下:

#!/usr/bin/sh
AFL_AUTORESUME=1 AFL_PATH="$(realpath ../../../AFLplusplus)" PATH="$AFL_PATH:$PATH" afl-fuzz -i afl_inputs -o afl_outputs -U -- python3 ./fuzz_arm_qnx.py @@

    运行后fuzz.sh后,便会出现afl++ 运行界面,等待几秒后便出现crash。

图片

    crash的变异数据存放在afl_outputs目录下,我们可以使用xxd id:000000,xxxxxx命令查看变异数据。

#xxd id:000000,sig:06,src:000000,time:4112,execs:1077,op:havoc,rep:8 
00000000: 4141 4141 4141 4141 4141 4141 ff7f 4241 AAAAAAAAAAAA..BA
00000010: 4141 4145 4141 be41 4dff 0000 0041 4141 AAAEAA.AM....AAA
00000020: 41

总结

在这一小节中,我们简单学习了qiling框架,我们使用ctf例题以及qilinglab的11个闯关题目进行练习,熟练掌握了qiling框架的基础使用。后面的小节中,我们将使用qiling框架对仿真设备进行实例fuzz测试。

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

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

相关文章

【求助】西门子S7-200PLC定时中断+数据归档的使用

前言 已经经历了种种磨难来记录我的数据&#xff08;使用过填表程序、触摸屏的历史记录和数据归档&#xff09;之后&#xff0c;具体可以看看这篇文章&#xff1a;&#x1f6aa;西门子S7-200PLC的数据归档怎么用&#xff1f;&#xff0c;出现了新的问题。 问题的提出 最新的…

网工交换基础——生成树协议(01)

一、生成树的技术概述 1、技术背景 二层交换机网络的冗余性导致出现二层环路&#xff1a; 人为因素导致的二层环路问题&#xff1a; 二层环路带来的网络问题&#xff1a; 生成树协议的概念&#xff1a; STP(Spanning Tree Protocol)是生成树协议的英文缩写。该协议可应用于在网…

面向对象练习坦克大兵游戏

游戏玩家&#xff08;名称&#xff0c;生命值&#xff0c;等级&#xff09;&#xff0c;坦克&#xff0c;大兵类&#xff0c;玩家之间可以相互攻击&#xff0c;大兵拥有武器&#xff0c;用枪弹和反坦克炮弹&#xff0c;造成攻击不同&#xff0c;坦克攻击值固定&#xff0c;请设…

设计模式-六大原则

设计模式的六大原则是软件工程中的基本概念&#xff0c;使得构建可维护、可扩展和可重用的代码。 1.单一职责原则&#xff08;Single Responsibility Principle&#xff09;&#xff1a;一个类或方法应该只有一个引起变化的原因&#xff0c;确保类或模块的功能高度内聚。 案例&…

VMware-Linux切换桥接模式上网教程(超详细)

这里写目录标题 1. 虚拟机关机2. VMware 虚拟网络配置2.1 检查是否存在 VMnet02.2 修改桥接模式2.3 修改Linux虚拟机网络适配器 3. Linux 系统配置3.1 修改系统网卡配置3.1.1 配置项含义解释3.1.2 查看物理机网络信息3.3.3 修改配置 3.2 重启服务 4. 测试网络连接情况5. 注意事…

【SpringBoot整合系列】SpringBoot整合JPA

目录 前期回顾ORM解决方案 JPA简介JPA的组成技术ORM映射元数据Java持久化API查询语言&#xff08;JPQL&#xff09; JPA的优势JPA的缺点 Spring Data JPASpring Data JPA简介Spring Data 家族Spring Data JPA、JPA和其他框架之间的关系 SpringBoot整合JPAJPA的核心注解1.依赖2.…

element-ui upload 组件 手动多次出发 submit

element 上传组件 upload 上传成功以后&#xff0c;想重新 调用 submit()函数&#xff0c;发现是不可以进行多次触发的,。 直接上解决方法&#xff0c;在上传成功后的钩子函数里添加:fileList[0l.status ready fileList是文件列表&#xff0c;status是单文件的状态改成ready就…

【Fastadmin】表格导出excel,图片显示太大

目录 1.直接导出示例 2.解决办法 3. 再次导出效果 1.直接导出示例 图片过大&#xff0c;格式错乱 2.解决办法 在js页面加入代码 // 导出图片过大处理 exportOptions: {ignoreColumn: [0, operate],onBeforeSaveToFile: function (data, fileName, type, charset, encoding,…

《深入Linux设备驱动程序内核机制》学习笔记-第4章

前言 本文是《深入Linux设备驱动程序内核机制》的读书笔记&#xff0c;本文因为是读书笔记所以抄写引用了该书中的大量内容&#xff0c;写读书笔记的目的是在写作的过程中加深对书中内容的理解。 建议读者直接阅读《深入Linux设备驱动程序内核机制》&#xff0c;这本书是Linu…

mPEG-Dansyl,Methoxy PEG Dansyl由甲氧基-聚乙二醇(mPEG)和丹磺酰氯(Dansyl)两部分组成

【试剂详情】 英文名称 mPEG-Dansyl&#xff0c;Methoxy PEG Dansyl 中文名称 聚乙二醇单甲醚丹磺酸酯&#xff0c;甲氧基-聚乙二醇-丹磺酰胺 外观性状 由分子量决定&#xff0c;液体或者固体 分子量 0.4k&#xff0c;0.6k&#xff0c;1k&#xff0c;2k&#xff0c;3.4k…

前端发送请求,显示超时取消

前端发送请求&#xff0c;显示超时取消 问题说明&#xff1a;后台接口请求60s尚未完成&#xff0c;前端控制台显示取消&#xff08;canceled&#xff09; 原因 1、前端设置60s超时则取消 2、后台接口响应时间过长&#xff0c;过长的原因统计的数据量多&#xff08;实际也才17…

LLM 安全 | 大语言模型应用安全入门

一、背景 2023年以来&#xff0c;LLM 变成了相当炙手可热的话题&#xff0c;以 ChatGPT 为代表的 LLM 的出现&#xff0c;让人们看到了无限的可能性。ChatGPT能写作&#xff0c;能翻译&#xff0c;能创作诗歌和故事&#xff0c;甚至能一定程度上做一些高度专业化的工作&#x…

BUUCTF-Misc20

[ACTF新生赛2020]NTFS数据流1 1.打开附件 是一堆文件&#xff0c;随便打开一个内容是flag不在这 2.pyton脚本 编写查找文件夹下一堆文件中那个文件藏有flag的Python脚本 import os def search_flag_files(folder_path, flag): flag_files [] for root, dirs, files …

HAL STM32 SSI/SPI方式读取MT6701磁编码器获取角度例程

HAL STM32 SSI/SPI方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《HAL STM32 I2C方式读取MT6701磁编码器获取角度例程》&#x1f4cc;当前最新MT6701数据手册&#xff1a;https://www.magntek.com.cn/upload/MT6701_Rev.1.8.pdf&#x1f4dc;SSI协议读角度&#xff…

【stomp实战】搭建一套websocket推送平台

前面几个小节我们已经学习了stomp协议&#xff0c;了解了stomp客户端的用法&#xff0c;并且搭建了一个小的后台demo&#xff0c;前端页面通过html页面与后端服务建立WebSocket连接。发送消息给后端服务。后端服务将消息内容原样送回。通过这个demo我们学习了前端stomp客户端的…

剑指 Offer 03.:数组中重复的数字

剑指 Offer 03. 数组中重复的数字 找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。…

DIN特征加权、POSO特征增强、SENET特征选择

本文转自&#xff1a;DIN、POSO、SENet 聊聊推荐模型中常用的Attention-腾讯云开发者社区-腾讯云 一、前言 聊起模型结构的时候&#xff0c;经常听做推荐的同学说&#xff1a; "这里加了个self-attention" "类似于一个SENet" "一个魔改的POSO"…

[Visual Studio 报错] error 找不到指定的 SDK“Microsoft

[Visual Studio 2022 报错] error : 找不到指定的 SDK“Microsoft.NET.Sdk.Web” 问题描述&#xff1a; 在新电脑上安装了VS2022&#xff0c;打开现有项目的解决方案后的时候报了这个错&#xff0c;所有projet文件都加载失败,如图所示&#xff1a; 报错分析及解决 打开项目配…

经验丰富也被裁了,失业快2年找不到工作?

前几天徐工说&#xff0c;他有个邻居&#xff0c;最近逮到他总是要跟他扯上几句。 原因是徐工一直是做嵌入式开发&#xff0c;而他一直做纯软件开发&#xff0c;具体不知道做后端还是前端。 他说&#xff0c;他至少有半年没上班了&#xff0c;之前在一家龙头物流公司上班。 碰上…

STM32 HAL库F103系列之DAC实验(二)

DAC输出正弦波实验 实验简要 1&#xff0c;功能描述 通过DAC1通道1(PA4)输出正弦波&#xff0c;然后通过DS100示波器查看波形 2&#xff0c;使用定时器7 TRGO事件触发转换 TEN1位置1、TSEL1[2:0]010 3&#xff0c;关闭输出缓冲 BOFF1位置1 4&#xff0c;使用DMA模式 DMAE…