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

MikroTik RouterOS 授权签名验证分析

MikroTik 软路由

百科icon-default.png?t=N7T8https://baike.baidu.com/item/mikrotik/9776775官网icon-default.png?t=N7T8https://mikrotik.com/

授权文件分析

        -----BEGIN MIKROTIK SOFTWARE KEY------------
        mr3jH5qhn9irtF53ZICFTN7Tk7wIx7ZkxdAxJ19ydASY
        ShhFteHMntBTyaS8wuNdIJJPidJxbuNPLTvCsv7zLA==
        -----END MIKROTIK SOFTWARE KEY--------------

采用自定义的Base64编码,自定义Base64的转换如下:

MIKRO_BASE64_CHARACTER_TABLE = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
def mikro_base64_encode(data:bytes, pad = False)->str:encoded = ''left = 0for i in range(0, len(data)):if left == 0:encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[i] & 0x3F])left = 2else:if left == 6:encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[i - 1] >> 2])encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[i] & 0x3F])left = 2else:index1 = data[i - 1] >> (8 - left)index2 = data[i] << (left)encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[(index1 | index2) & 0x3F])left += 2if left != 0:encoded += chr(MIKRO_BASE64_CHARACTER_TABLE[data[len(data) - 1] >> (8 - left)])if pad:for i in range(0, (4 - len(encoded) % 4) % 4):encoded += '='return encoded
def mikro_base64_decode(data:str)->bytes:ret = b""data = data.replace("=", "").encode()left = 0for i in range(0, len(data)):if left == 0:left = 6else:value1 = MIKRO_BASE64_CHARACTER_TABLE.index(data[i - 1]) >> (6 - left)value2 = MIKRO_BASE64_CHARACTER_TABLE.index(data[i]) & (2 ** (8 - left) - 1)value = value1 | (value2 << left)ret += bytes([value])left -= 2return ret

Base64解码后为固定64字节长度的数据,其中16字节授权内容、16字节随机数、32字节签名。

16字节授权内容通过如下方式加密解密

def mikro_encode(s:bytes)->bytes:s = list(struct.unpack('>' + 'I' * (len(s) // 4), s))for i in reversed(range(16)):s[(i+0) % 4] = to32bits(rotl(s[(i+3) % 4], MIKRO_SHA256_K[i*4+3] & 0x0F) ^ (s[(i+0) % 4] - s[(i+3) % 4]))s[(i+3) % 4] = to32bits(s[(i+3) % 4] + s[(i+1) % 4] + MIKRO_SHA256_K[i*4+3])s[(i+1) % 4] = to32bits(rotl(s[(i+2) % 4], MIKRO_SHA256_K[i*4+2] & 0x0F) ^ (s[(i+1) % 4] - s[(i+2) % 4]))s[(i+0) % 4] = to32bits(s[(i+0) % 4] + s[(i+2) % 4] + MIKRO_SHA256_K[i*4+2])s[(i+2) % 4] = to32bits(rotl(s[(i+1) % 4], MIKRO_SHA256_K[i*4+1] & 0x0F) ^ (s[(i+2) % 4] - s[(i+1) % 4]))s[(i+1) % 4] = to32bits(s[(i+1) % 4] + s[(i+3) % 4] + MIKRO_SHA256_K[i*4+1])s[(i+3) % 4] = to32bits(rotl(s[(i+0) % 4], MIKRO_SHA256_K[i*4+0] & 0x0F) ^ (s[(i+3) % 4] - s[(i+0) % 4]))s[(i+2) % 4] = to32bits(s[(i+2) % 4] + s[(i+0) % 4] + MIKRO_SHA256_K[i*4+0])encodedLicensePayload = b''for x in s:encodedLicensePayload += x.to_bytes(4, 'big')return encodedLicensePayloaddef mikro_decode(s:bytes)->bytes:s = list(struct.unpack('>'+'I'*(len(s) // 4), s))for i in range(16):s[(i+2) % 4] = to32bits(s[(i+2) % 4] - s[(i+0) % 4] - MIKRO_SHA256_K[i*4+0])s[(i+3) % 4] = to32bits((rotl(s[(i+0) % 4], MIKRO_SHA256_K[i*4+0] & 0x0F) ^ s[(i+3) % 4]) + s[(i+0) % 4])s[(i+1) % 4] = to32bits(s[(i+1) % 4] - s[(i+3) % 4] - MIKRO_SHA256_K[i*4+1])s[(i+2) % 4] = to32bits((rotl(s[(i+1) % 4], MIKRO_SHA256_K[i*4+1] & 0x0F) ^ s[(i+2) % 4]) + s[(i+1) % 4])s[(i+0) % 4] = to32bits(s[(i+0) % 4] - s[(i+2) % 4] - MIKRO_SHA256_K[i*4+2])s[(i+1) % 4] = to32bits((rotl(s[(i+2) % 4], MIKRO_SHA256_K[i*4+2] & 0x0F) ^ s[(i+1) % 4]) + s[(i+2) % 4])s[(i+3) % 4] = to32bits(s[(i+3) % 4] - s[(i+1) % 4] - MIKRO_SHA256_K[i*4+3])s[(i+0) % 4] = to32bits((rotl(s[(i+3) % 4], MIKRO_SHA256_K[i*4+3] & 0x0F) ^ s[(i+0) % 4]) + s[(i+3) % 4])ret = b''for x in s:ret += x.to_bytes(4, 'big')return ret

16字节解密后前6个字节为授权软件ID,第7个字节为授权软件版本号,第8个字节为授权等级

6字节转换为软件ID的方式如下:

SOFTWARE_ID_CHARACTER_TABLE = b'TN0BYX18S5HZ4IA67DGF3LPCJQRUK9MW2VE'
def mikro_softwareid_decode(software_id:str)->int:assert(isinstance(software_id, str))software_id = software_id.replace('-', '')ret = 0for i in reversed(range(len(software_id))):ret *= len(SOFTWARE_ID_CHARACTER_TABLE)ret += SOFTWARE_ID_CHARACTER_TABLE.index(ord(software_id[i]))return retdef mikro_softwareid_encode(id:int)->str:assert(isinstance(id, int))ret = ''for i in range(8):ret += chr(SOFTWARE_ID_CHARACTER_TABLE[id % 0x23])id //= 0x23if i == 3:ret += '-'return ret

16字节随机数和32字节校验码通过EC-KCDSA算法签名生成,采用toyecc库,其中sha256采用自定义的K和State表

自定义的sha256实现算法

MIKRO_SHA256_K = (0x0548D563, 0x98308EAB, 0x37AF7CCC, 0xDFBC4E3C,0xF125AAC9, 0xEC98ACB8, 0x8B540795, 0xD3E0EF0E,0x4904D6E5, 0x0DA84981, 0x9A1F8452, 0x00EB7EAA,0x96F8E3B3, 0xA6CDB655, 0xE7410F9E, 0x8EECB03D,0x9C6A7C25, 0xD77B072F, 0x6E8F650A, 0x124E3640,0x7E53785A, 0xE0150772, 0xC61EF4E0, 0xBC57E5E0,0xC0F9A285, 0xDB342856, 0x190834C7, 0xFBEB7D8E,0x251BED34, 0x0E9F2AAD, 0x256AB901, 0x0A5B7890,0x9F124F09, 0xD84A9151, 0x427AF67A, 0x8059C9AA,0x13EAB029, 0x3153CDF1, 0x262D405D, 0xA2105D87,0x9C745F15, 0xD1613847, 0x294CE135, 0x20FB0F3C,0x8424D8ED, 0x8F4201B6, 0x12CA1EA7, 0x2054B091,0x463D8288, 0xC83253C3, 0x33EA314A, 0x9696DC92,0xD041CE9A, 0xE5477160, 0xC7656BE8, 0x5179FE33,0x1F4726F1, 0x5F393AF0, 0x26E2D004, 0x6D020245,0x85FDF6D7, 0xB0237C56, 0xFF5FBD94, 0xA8B3F534
)
class MikroSHA256(SHA256):K = MIKRO_SHA256_KINITIAL_STATE = SHA256.State(0x5B653932, 0x7B145F8F, 0x71FFB291, 0x38EF925F,0x03E1AAF9, 0x4A2057CC, 0x4CAF4DD9, 0x643CC9EA)
def mikro_sha256(data:bytes)->bytes:return MikroSHA256(data).digest()

EC-KCDSA签名验证算法

from toyecc import AffineCurvePoint, getcurvebyname, FieldElement,ECPrivateKey,ECPublicKey,Tools
from toyecc.Random import secure_rand_int_between
def mikro_kcdsa_sign(data:bytes,private_key:bytes)->bytes:assert(isinstance(data, bytes))assert(isinstance(private_key, bytes))curve = getcurvebyname('Curve25519')private_key:ECPrivateKey = ECPrivateKey(Tools.bytestoint_le(private_key), curve)public_key:ECPublicKey = private_key.pubkeywhile True:nonce_secret = secure_rand_int_between(1, curve.n - 1)nonce_point = nonce_secret * curve.Gnonce = int(nonce_point.x) % curve.nnonce_hash = mikro_sha256(Tools.inttobytes_le(nonce,32))data_hash = bytearray(mikro_sha256(data))for i in range(16):data_hash[8+i] ^= nonce_hash[i] data_hash[0] &= 0xF8data_hash[31] &= 0x7Fdata_hash[31] |= 0x40data_hash = Tools.bytestoint_le(data_hash)signature = pow(private_key.scalar, -1, curve.n) * (nonce_secret - data_hash)signature %= curve.nif int((public_key.point * signature + curve.G * data_hash).x) == nonce:return bytes(nonce_hash[:16]+Tools.inttobytes_le(signature,32))def mikro_kcdsa_verify(data:bytes, signature:bytes, public_key:bytes)->bool:assert(isinstance(data, bytes))assert(isinstance(signature, bytes))assert(isinstance(public_key, bytes))curve = getcurvebyname('Curve25519')#y^2 = x^3 + ax^2 + xx = Tools.bytestoint_le(public_key)X_field = FieldElement(x, curve.p)YY = ((X_field**3) + (curve.a * X_field**2) + X_field).sqrt()public_keys = []for y in YY:public_keys += [AffineCurvePoint(x, int(y), curve)]data_hash = bytearray(mikro_sha256(data))nonce_hash = signature[:16]signature = signature[16:]for i in range(16):data_hash[8+i] ^= nonce_hash[i]data_hash[0] &= 0xF8data_hash[31] &= 0x7Fdata_hash[31] |= 0x40data_hash = Tools.bytestoint_le(data_hash)signature = Tools.bytestoint_le(signature)for public_key in public_keys:nonce = int((public_key * signature + curve.G * data_hash).x) if mikro_sha256(Tools.inttobytes_le(nonce,32))[:len(nonce_hash)] == nonce_hash:return Truereturn False
def generate_kcdsa_keypair():curve = getcurvebyname('Curve25519')private_key = ECPrivateKey.generate(curve)return Tools.inttobytes_le(private_key.scalar, 32),Tools.inttobytes_le(int(private_key.pubkey.point.x), 32)

授权文件解析

通过分析/nova/bin/keyman可以获取授权文件验证签名的公钥为

MIKRO_LICENSE_PUBLIC_KEY = bytes.fromhex('8E1067E4305FCDC0CFBF95C10F96E5DFE8C49AEF486BD1A4E2E96C27F01E3E32')

因此可以解析验证授权文件是否有效

def prase_license(lic:str,public_key:bytes):assert(isinstance(public_key, bytes))slic = lic.replace(MIKRO_LICENSE_HEADER, '').replace(MIKRO_LICENSE_FOOTER, '').replace('\n', '').replace(' ','')lic:bytes = mikro_base64_decode(slic)licVal = mikro_decode(lic[:16])software_id = int.from_bytes(licVal[:6], 'little')print(f"Software ID: {mikro_softwareid_encode(software_id)}({hex(software_id)})")print(f"RouterOS Version: {licVal[6]}")print(f"License Level: {licVal[7]}")nonce_hash = lic[16:32]print(f"Nonce Hash: {nonce_hash.hex()}")signature = lic[32:64]print(f"Signature: {signature.hex()}")print(f'License valid:{mikro_kcdsa_verify(licVal, nonce_hash+signature,public_key)}')

解析后的输出

Software ID: TI09-7WK3(0x137f8e8673d)
RouterOS Version: 6
License Level: 6
Nonce Hash: b34fe40e23f19e917107c449ddcb1d20
Signature: 61521816ad7730671b4cb226f1b0db7448923c6297c49bdb3ccbf40aecbbcf0b
License valid:True

授权文件生成

生成秘钥对,通过私钥对授权内容进行签名即可。

MIKRO_LICENSE_HEADER = '-----BEGIN MIKROTIK SOFTWARE KEY------------'
MIKRO_LICENSE_FOOTER = '-----END MIKROTIK SOFTWARE KEY--------------' 
def generate_license(software_id,private_key:bytes,version:int=7, level:int=6):assert(isinstance(private_key, bytes))if isinstance(software_id, str):software_id = mikro_softwareid_decode(software_id)lic = software_id.to_bytes(6, 'little')lic += version.to_bytes(1, 'little')lic += level.to_bytes(1, 'little')lic += b'\0'*8sig = mikro_kcdsa_sign(lic, private_key)lic = mikro_base64_encode(mikro_encode(lic)+sig,True)return MIKRO_LICENSE_HEADER + '\n' + lic[:len(lic)//2] + '\n' + lic[len(lic)//2:] + '\n' + MIKRO_LICENSE_FOOTER

替换公钥

用生成的秘钥对的公钥替换原先官方的公钥

squashfs-root/nova/bin/loader public key patched 8E1067E4305FCDC0CFBF95C10F96E5DF...

squashfs-root/nova/bin/keyman public key patched 8E1067E4305FCDC0CFBF95C10F96E5DF...

squashfs-root/nova/bin/mode public key patched 8E1067E4305FCDC0CFBF95C10F96E5DF...

initramfs/init public key patched 8E1067E4305FCDC0CFBF95C10F96E5DF...

initramfs/setup public key patched 8E1067E4305FCDC0CFBF95C10F96E5DF...

但是routeros的npk文件也是验证签名的,因此直接替换公钥,系统启动的时候验证npk的文件的签名是不通过的,所以还得对npk文件重新签名

NPK文件签名分析

npk文件的签名由132个字节组成,其中20字节sha1,48字节EC-KCDSA签名,64字节EDDSA签名

同理生成自定义秘钥对替换原来的官方公钥,用自定义的私钥对npk重新签名即可。

squashfs-root/nova/bin/installer public key patched C293CED638A2A33C681FC8DE98EE26C5...

squashfs-root/nova/bin/sys2 public key patched C293CED638A2A33C681FC8DE98EE26C5...

initramfs/init public key patched C293CED638A2A33C681FC8DE98EE26C5...

initramfs/setup public key patched C293CED638A2A33C681FC8DE98EE26C5...

 源代码

MikroTikPatchicon-default.png?t=N7T8https://github.com/elseif/MikroTikPatch

       netinstall.py,修改netinstall.exe,通过修改过的netinstall可以网络安装ISO里重新签名的npk文件

       upgrade.py,通过在routeros添加静态域名解析,实现upgrade的时候安装ISO里重新签名的npk文件。

相关文章:

MikroTik RouterOS 授权签名验证分析

MikroTik 软路由 百科https://baike.baidu.com/item/mikrotik/9776775官网https://mikrotik.com/ 授权文件分析 -----BEGIN MIKROTIK SOFTWARE KEY------------ mr3jH5qhn9irtF53ZICFTN7Tk7wIx7ZkxdAxJ19ydASY ShhFteHMntBTyaS8wuNdIJJPidJxbuNPLTvCsv7zLA …...

C#开发-集合使用和技巧(六)特殊转换方法SelectMany的介绍和用法

介绍 SelectMany 方法在C#中用于将集合中的元素转换为其他类型的集合&#xff0c;并将这些集合扁平化为一个单一的序列。它是LINQ的一部分&#xff0c;允许你在一个序列上进行投影和过滤操作&#xff0c;然后将结果合并成一个序列。 方法定义 public static IEnumerable<…...

高考后的抉择:如何在心仪专业与知名学校之间做出选择?

目录 前言1. 专业选择的深度探讨1.1 专业的优势与挑战1.1.1 课程学习1.1.2 就业前景 1.2 专业选择的个人经验与思考 2. 名校对个人发展的长短期影响2.1 名校声誉的品牌效应2.1.1 职业发展2.1.2 社会认可度 2.2 教育资源与学术氛围2.2.1 教育资源2.2.2 学术氛围 2.3 就业优势 3.…...

黄仁勋提到的机器人世界,还需要AI数据来“调教” | CVPR 2024

本周&#xff0c;CVPR 2024正在美国西雅图拉开序幕。今年CVPR论文投稿数再次创下新纪录&#xff0c;可想而知本届会议的火热。 从研究主题来看&#xff0c;具身智能这一大热点值得关注。 黄仁勋在COMPUTEX大会开幕前夕的演讲中预言&#xff1a;AI的下一个浪潮将是物理AI。 即…...

语言中 函数用地址传参的好处

在C语言中&#xff0c;使用地址传参&#xff08;传递指针&#xff09;有以下几个好处&#xff1a; 1. **减少内存开销**&#xff1a; - 传递一个指针&#xff08;通常是一个地址&#xff09;比传递一个大的结构体或数组要高效得多&#xff0c;因为指针通常是一个固定大小&a…...

Python进阶二: NumPy基础:数组和矢量计算

二、NumPy基础&#xff1a;数组和矢量计算 本文源自微博客(www.microblog.store),且以获得授权 NumPy&#xff08;Numerical Python的简称&#xff09;是Python数值计算最重要的基础包。大多数提供科学计算的包都是用NumPy的数组作为构建基础。 NumPy的部分功能如下&#xf…...

2024北京智源大会开幕,智源推出大模型全家桶及全栈开源技术基座新版图,大模型先锋集结共探AGI之路

2024年6月14日&#xff0c;第六届“北京智源大会”在中关村展示中心开幕。 北京智源大会是智源研究院主办的“AI内行顶级盛会”&#xff0c;以“全球视野、思想碰撞、前沿引领”为特色&#xff0c;汇聚海内外研究者分享研究成果、探寻前沿知识、交流实践经验。2024北京智源大会…...

李光明从程序员到架构师的逆袭之路(三)

我&#xff0c;李光明&#xff0c;正在参加一个重要的技术会议。会场上&#xff0c;我们团队正在讨论着接口设计以及接口设计模式。我深知&#xff0c;一个好的接口设计应当遵循简洁、清晰、可扩展的原则。比如&#xff0c;在设计一个用户信息查询接口时&#xff0c;我们会定义…...

基于Spring Boot+VUE毕业生信息招聘平台

系统详细设计 1管理员功能模块 管理员登录&#xff0c;管理员通过输入用户名、密码、角色等信息进行系统登录&#xff0c;如图1所示。 图1管理员登录界面图 管理员登录进入毕业生信息招聘平台可以查看首页、个人中心、企业管理、空中宣讲会管理、招聘岗位管理、毕业生管理、个…...

设计模式-创建型-04-建造者模式

1、盖房项目需求 1&#xff09;需要建房子&#xff1a;这一过程为打桩、砌墙、封顶2&#xff09;房子有各种各样的&#xff0c;比如普通房&#xff0c;高楼&#xff0c;别墅&#xff0c;各种房子的过程虽然一样&#xff0c;但是要求不要相同的3&#xff09;请编写程序&#xf…...

jenkins中配置参数化,并在python脚本接收参数实现参数化执行

在公司内进行Monkey测试脚本集成jenkins时&#xff0c;因为需要指定公司内不同的app进行测试。那么可以有两种方法解决该问题&#xff0c;要么创建多个脚本文件&#xff0c;多个jenkins jobs&#xff0c;进行构建。要么可以在配置job时通过传参数的方式&#xff0c;在python脚本…...

【SCAU数据挖掘】数据挖掘期末总复习题库应用题及解析

1. 给定圆的半径为e &#xff0c;令 MinPts3&#xff0c;考虑下面两幅图。 &#xff08;1&#xff09;哪些对象是核心对象? m,p,o,r(因为这些核心对象在半径e的范围内都至少包含MinPts3个对象) &#xff08;2&#xff09;哪些对象是直接密度可达的? 对象q是…...

ADB->获取当前正在显示的Fragment和Activity的ADB命令

获取当前显示的Activity adb shell "dumpsys window | grep mCurrentFocus"指令拆解adb shell&#xff1a;启动一个远程shell来运行设备上的命令dumpsys window&#xff1a;获取当前窗口管理器的信息|&#xff1a;将前一个命令的输出作为后一个命令的输入grep mCurr…...

C#——集合List

list list集合和Arraylist基本一样&#xff0c;只不过list是C#2.0版本新加入的范型类型。list也可以通过索引操作里面的元素&#xff0c;也有对list进行增删改查 概念 Array静态数组 * Arraylist 动态数组 * list集合 * 1. Array是容量是固定的&#xff0c;但是ArrayList和…...

小程序-生命周期(2) 应用周期/页面周期

一.应用周期 应用周期指的是小程序&#xff1a;启动->运行->销毁的整个过程。 应用周期伴随一些函数来进行控制&#xff0c;这些函数卸载app.js里面的App方法里。 分别由onLaunch&#xff0c; onShow&#xff0c;onHide依次进行。 onLaunch&#xff1a;初始化的时候运行…...

什么是模板字符串?

模板字符串&#xff08;Template Literals&#xff09;是ES6&#xff08;ECMAScript 2015&#xff09;中引入的一种新的字符串表示方法&#xff0c;允许我们嵌入表达式&#xff0c;并在运行时将它们转换为字符串。模板字符串使用反引号&#xff08;&#xff09;来定义&#xff…...

服务器数据恢复—热备盘未完全启用导致raid5阵列崩溃的数据恢复案例

服务器存储故障&#xff1a; 一台EMC某型号存储由于存储中raid5阵列出现故障导致服务器崩溃&#xff0c;由于数据涉密&#xff0c;需要工程师到现场恢复数据。 服务器数据恢复工程师到现场后对数据进行检测&#xff0c;经过检测发现服务器崩溃是由于raid中某些硬盘掉线所导致。…...

微服务项目雪崩的解决思路

雪崩的介绍 雪崩是微服务中某个服务挂了&#xff0c;无法返回请求&#xff0c;导致调用改服务的上层服务也故障&#xff0c;最终形成连锁反应&#xff0c;导致整个系统故障。 解决思路 一般有四种思路&#xff1a; 1.最简单的就是超时处理&#xff0c;即超过一段时间就返回…...

汇编语言程序设计 - 新建一个文件:d:\abc.txt,从键盘输入文件的内容(不超过100个字符)

80x86汇编习题 题目描述&#xff1a;编写一个程序&#xff0c;新建一个文件&#xff1a;d:\abc.txt&#xff0c;从键盘输入文件的内容&#xff08;不超过100个字符&#xff09; 思路&#xff1a; 1&#xff0c;定义好文件名&#xff0c;记得末尾0 2&#xff0c;定义好缓冲区…...

【Linux】进程间通信2——命名管道

1. 命名管道(FIFO) 1.1. 基本概念 简单&#xff0c;给匿名管道起个名字就变成了命名管道 那么如何给 匿名管道 起名字呢&#xff1f; 结合文件系统&#xff0c;给匿名管道这个纯纯的内存文件分配 inode&#xff0c;将文件名与之构建联系&#xff0c;关键点在于不给它分配 D…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)

第一篇&#xff1a;Liunx环境下搭建PaddlePaddle 3.0基础环境&#xff08;Liunx Centos8.5安装Python3.10pip3.10&#xff09; 一&#xff1a;前言二&#xff1a;安装编译依赖二&#xff1a;安装Python3.10三&#xff1a;安装PIP3.10四&#xff1a;安装Paddlepaddle基础框架4.1…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)

引言 在嵌入式系统中&#xff0c;用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例&#xff0c;介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单&#xff0c;执行相应操作&#xff0c;并提供平滑的滚动动画效果。 本文设计了一个…...

ThreadLocal 源码

ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物&#xff0c;因为每个访问一个线程局部变量的线程&#xff08;通过其 get 或 set 方法&#xff09;都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段&#xff0c;这些类希望将…...

stm32进入Infinite_Loop原因(因为有系统中断函数未自定义实现)

这是系统中断服务程序的默认处理汇编函数&#xff0c;如果我们没有定义实现某个中断函数&#xff0c;那么当stm32产生了该中断时&#xff0c;就会默认跑这里来了&#xff0c;所以我们打开了什么中断&#xff0c;一定要记得实现对应的系统中断函数&#xff0c;否则会进来一直循环…...

数据库优化实战指南:提升性能的黄金法则

在现代软件系统中&#xff0c;数据库性能直接影响应用的响应速度和用户体验。面对数据量激增、访问压力增大&#xff0c;数据库性能瓶颈经常成为项目痛点。如何科学有效地优化数据库&#xff0c;提升查询效率和系统稳定性&#xff0c;是每位开发与运维人员必备的技能。 本文结…...

scan_mode设计原则

scan_mode设计原则 在进行mtp controller设计时&#xff0c;基本功能设计完成后&#xff0c;需要设计scan_mode设计。 1、在进行scan_mode设计时&#xff0c;需要保证mtp处于standby模式&#xff0c;不会有擦写、编程动作。 2、只需要固定mtp datasheet说明的接口即可&#xf…...