数美点选验证协议全面剖析

文章目录

      • 目标网站
      • 分析请求
      • 动态JS无法调试
      • 代码混淆处理
      • conf请求分析
      • 分析fverify请求
        • 整体代码分析
        • getEncryptContent函数分析
        • 分析其他参数
        • 分析坐标算法
      • 结束

目标网站

aHR0cHM6Ly9zZWN1cmUuZWxvbmcuY29tL3Bhc3Nwb3J0L2xvZ2luX2NuLmh0bWw/bmV4dHVybD1odHRwczovL3d3dy5lbG9uZy5jb20v

在这里插入图片描述

这一次要分析的是这个数美的点选验证。

分析请求

接下来对这个验证的请求进行抓包分析,看看有哪几个请求是需要逆向的。
在这里插入图片描述

首先是一个register请求,这个对应的是图片的获取
在这里插入图片描述

payload参数如图,其中的mode表示的是验证模式,当前是点选验证,如果model的值是slide,那么说明是滑块验证。

在这里插入图片描述

返回的数据是一张点选的图片,还有order字段的值
在这里插入图片描述

通过preview可以看到,这个order字段就是文字内容。

所以我们接下来要做的事情就是通过调用这个请求,拿到返回的图片和文字数据,然后通过图片识别出文字的位置,找到坐标点,然后通过代码去构造坐标数据。

接着当我们对图片进行点选的时候,会产生这么一个请求
在这里插入图片描述

会发送一个verify请求,如果失败了,会返回REJECT,如果成功的话则是 PASS。
在这里插入图片描述

然后再往上会有一个conf请求,这个请求的payload参数和register提交的参数是一样的,所以这俩请求分析一个就可以了。

那么需要分析的核心的请求就只有下面三个:

https://captcha1.fengkongcloud.cn/ca/v2/fverify
https://captcha1.fengkongcloud.cn/ca/v1/register
https://captcha1.fengkongcloud.cn/ca/v1/conf

动态JS无法调试

在分析之前我们需要先解决一个问题,
在这里插入图片描述

这个conf请求调用栈的JS文件,是一个动态变化的,每次url都不一样,这样就导致我们的断点在下断以后再次断下。

这个问题的解决方案在于将加载JS时的url替换为固定的,可以通过charles或者mimproxy来解决,这两个都可以作为代理拦截http请求。
在这里插入图片描述

在Charles中捕获这个请求,然后右键->SaveResponse,把页面源码保存下来。
在这里插入图片描述

打开Charles的Map Local功能

在这里插入图片描述

添加一个Mapping,设置了这个以后,当有网络请求访问了我们指定的这个map页面,就会被替换为本地保存的html文件,这样就可以保证JS一直是固定的了。

也可以用mitmproxy写一个拦截脚本来处理这个问题,代码如下:

from mitmproxy import http
from mitmproxy.http import Requestdef request(flow):if flow.url.startswith("https://secure.elong.com/passport/login_cn.html?nexturl=https://www.elong.com/"):with open("res.html",mode="rb",encoding="utf-8") as f:content=f.read()flow.response = Response.make(200,content,{"Content-Type":"text/html"})def response(flow: http.HTTPFlow):pass

不过还是Charles方便,直接配置好就可以了。

代码混淆处理

然后我们来解决代码混淆的问题。
在这里插入图片描述

接着抓一个包,找到conf这个请求,这个请求指向的是api.js文件,也就是我们刚刚替换的那个动态的JS
在这里插入图片描述

点进去发现,所有的代码都是经过混淆的。

var _0x19e1cf = _0x136e2f[_0x2ae8e9(0x2fd)]

这种混淆实际上就是把字符串,替换成了函数调用,用来干扰分析。我们把这个JS文件保存下来,做一个整体的分析
在这里插入图片描述

一共有四个大函数,其中两个是自执行函数。这个页面的混淆代码大部分在第三个自执行函数里面,其他三个函数代码量都比较小。
在这里插入图片描述

其中大部分的混淆代码都是在执行_0x1f0d这个函数。

那么我们就可以把除了第三个大函数以外的函数全部抠下来,拿到本地,去执行一下加密的函数,看能不能得到结果
在这里插入图片描述

经过实际测试,确实是可以正常运行,并且打印出对应的字符串。

console.log(_0x1f0d(process.argv[2]))

然后我们把这个混淆函数的参数替换为命令行参数,从而使用python来调用,来达到批量去混淆的目录,python代码如下:

import subprocess
import redef Decode(hex_rg):res= subprocess.check_output(f"node main.js {hex_rg}",shell=True)res_string=res.decode("utf-8").strip()return res_stringdef run():with open("f1.js", mode="r", encoding="utf-8") as fr, open("f2.js", mode="w", encoding="utf-8") as fw:for line in fr:match_list = re.findall("(_0x425d8a\((.*?)\))", line)if not match_list:fw.write(line)continuefor func_string,hex_str in match_list:line=line.replace(func_string,f'"{Decode(hex_str)}"')fw.write(line)if __name__ == '__main__':run()

这个脚本做的事情就是打开f1.js,读取里面的内容,通过正则匹配的方式筛选出符合要求的代码,通过调用nodejs解混淆脚本得到结果,对其进行批量替换。

等遇到需要重点分析的代码,可以用这个方式可以去除部分混淆,提高一些代码可读性,帮助分析代码。这个脚本在后面的分析里面可以帮我们节省很多时间。

conf请求分析

我们先来分析第一个conf请求,里面携带了这么几个参数

appId: default
organization: xQsKB7v2qSFLFxnvmjdO
callback: sm_1705412287345
sdkver: 1.1.3
model: select
captchaUuid: 20240116213802Zwas5htESARemRJWfW
rversion: 1.0.4
lang: zh-cn
channel: DEFAULT

organization是一个ID,这个是固定的,多测几次就会发现,而callback是一个时间戳,那么对于conf这个请求,我们只需要去分析captchaUuid这个字段就可以了。
在这里插入图片描述

找到conf请求的调用堆栈,在中间的一个位置打一个断点,反正都是混淆的,在哪都没区别,只要在附近找到了需要跟踪的字段,一直往上找就行了。
在这里插入图片描述

断下以后,当前的作用域里面,并没有我们需要的参数,所以需要沿着调用栈一直往上翻
在这里插入图片描述

翻到这个调用栈的时候,终于找到了我们需要的相关参数

'captchaUuid': _0x2049e1

所以我们现在就要往上找_0x2049e1里面的值是从哪来的

在这里插入图片描述

_0x2049e1在这个位置被赋值,那我们接下来就要分析这个代码。

_0x2049e1 = userConfig[_0x4c37ed(0x3be)] || _0x332a8d[_0x243c3a[_0x4c37ed(0x483)]][_0x4c37ed(0x4f4)]();

这个代码实际上就是一个函数调用
在这里插入图片描述

这里可以对这个位置的代码进行选中,然后跳转到相应的代码页面
在这里插入图片描述

就跳到了这个位置,代码如下:

'getCaptchaUuid': function _0x2d350e() {var _0x49a2b0 = _0x247065, _0x1163e8 = '', _0x40ad39 = _0x49a2b0(0x42a), _0x3531f2 = _0x40ad39['length'];for (var _0x235133 = -0x149e + -0x231d * 0x1 + -0x37bb * -0x1; _0x235133 < -0x2214 + 0xd * 0x223 + 0xe9 * 0x7; _0x235133++) {_0x1163e8 += _0x40ad39['charAt'](Math[_0x49a2b0(0x2c6)](Math[_0x49a2b0(0x134)]() * _0x3531f2));}return _0x529f28[_0x49a2b0(0x294)](this[_0x49a2b0(0x232)](), _0x1163e8);},

这个代码的分析就没有什么技术含量了,一行一行硬看,反正也没有多少代码

captchaUuid: 20240116213802Zwas5htESARemRJWfW

参考这个captchaUuid的值,一边调试一边分析,我这里直接说结论

captchaUuid=当前时间+17个随机字符

实现代码如下:

def gen_captcha_uuid():total_string = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"part = "".join([random.choice(total_string) for i in range(18)])ctime = datetime.datetime.now().strftime("%Y%m%d%H%M%S")captcha_uuid = f"{ctime}{part}"return captcha_uuid

这样的话这个请求我们就搞定了。

分析fverify请求

整体代码分析

fverify请求携带的参数如下:

en: y+ugz9NIWys=
dy: Rfpr5oqb5y4=
xy: YabT6nmJOC0=
tb: 3jSn4gNaAVM=
mu: 9Zlg08y1MpaONqmLgK0lCn6u9wurdUqB5gECmblJXlSmnXHCWXXjiqvWSDI1PvzKgJdsNe46/hqf7zZh2MbjiPprUUXfMXi5+mLlFmVm6mrUS0NZ6OhMKZzNznVo51GI44gF1jVQF7Dh4/k62Zo93u4USF1RoqZU4LXyLyhHZMdhlk5nk5/NUMITc7kJqy7ngnBWIDPhl72rZtFxlvPKXxMXZnJU+GwA5pnf4/41T4rM6rYRV9Xr2E9XCLBhKno3eQAtSxt7wlDl6GMcg3LJ81dZGD6UVwy1DJJuG6fMv/M=
oc: h9oFKi8cHpg=
mp: WYfkIZp7GoA=
nu: C0kH/bWLjw8=
qd: adKA4Y0ncgGB4U6j47xchBZw9rO2THeeS/Z7vaOjAeKvmR57DOi6wsSJgovJUg2YQGQgooGYYrd2BhXCCkVQeY2gk1w7g2IquFeRYOlajtwqm5aZTx8FBx6ASb9VM7LNiZc0cVEnpnCrnXt0ICRagsT4sIYxGFg+EPenpHhwldu1Ufb8VNYIIrYBESmTOhpdttfd8gUBE36NGzqeXQpvRiqf4JT6eWB73TzFVEtzcjUHskwpvgqSmTYoQwG/gjy5APzSd96aTgTzKeX8I/wdupwYJnWvjmWWJ2R4JprB45o=
ww: aOGVECVeH60=
kq: mtlOTdT5LOE=
jo: lQ90183KgD4=固定:
ostype: web
sdkver: 1.1.3
protocol: 180
rversion: 1.0.4
organization: xQsKB7v2qSFLFxnvmjdO
act.os: web_pc已分析
captchaUuid: 20240117123606cSbtzkfecDkBDbC7Sr
callback: sm_1705466200576register接口的返回值
rid: 202401171236155825d945349adfd458

这个请求里面,排除掉不需要分析的部分,剩下需要分析的字段一共有12个。
在这里插入图片描述

查看请求的调用栈,在最后两个调用栈下断点
在这里插入图片描述

断下之后来分析下当前的这一行代码

this[_0x2c1c24(0x1b5)](_0x5c100a, _0x599170, _0x1ed2cb, _0x28800d, _0x5c6636, _0x4d541b);

为了方便阅读,我把这个代码进行简化

this[""sendRequest""]('https://', "captcha1.fengkongcloud.cn", "/ca/v2/fverify", _0x28800d, _0x5c6636, _0x4d541b);

这个位置实际上是在发送https请求,前面三个参数是在拼接请求的域名
在这里插入图片描述

而第四个参数是一个对象,里面包含的内容就是我们需要跟踪的字段。那么我们现在就摇追踪_0x28800d的来源,看这个数据是怎么生成的。
在这里插入图片描述

我们先把这一段代码的混淆给处理一下,用之前解混淆的那个方法。

var 0x2d6657 = {'nyzWi': "mouseMoveX",'SZdlb': _0x6f9c3c["pzOLX"],'TGQiS': function (_0x569f5d, _0x276cc4) {var _0x5d1724 = _0x2c1c24;return _0x6f9c3c["onIeC"](_0x569f5d, _0x276cc4);}};var _0x1fe1cf,_0x44bcff = this["_config"],_0x599170 = _0x44bcff['domains'],_0x3d6fed = _0x44bcff["fVerifyUrlV2"],_0x1ed2cb = _0x3d6fed === undefined ? _0x3b4628 : _0x3d6fed,_0x49bb92 = _0x44bcff["organization"],_0x20df23 = _0x44bcff["appId"],_0x4143cf = _0x44bcff["channel"],_0x435e2e = _0x44bcff['VERSION'],_0x7306d7 = _0x44bcff['lang'],_0x294474 = _0x44bcff["SDKVER"],_0x1b27c8 = _0x44bcff["_successCallback"],_0x2d5257 = _0x44bcff["mode"],_0x5b58d1 = this['_data'],_0x536387 = _0x5b58d1["errMsg"],_0x74fdb5 = _0x5b58d1["trueWidth"],_0x31e834 = _0x6f9c3c["tIGQt"](_0x74fdb5, undefined) ? -0x1338 + -0x145f + -0x7eb * -0x5 : _0x74fdb5,_0x3717b1 = this["getRegisterData"](_0x6f9c3c["ZGnpS"]), _0x298b01 = this["getMouseAction"](),_0x528bd = _0x6f9c3c["VQpVP"], _0x49f479 = this["getSafeParams"](),_0x28800d = _0x2460cd[_0x6f9c3c["Nlbsb"]]["extend"]((_0x1fe1cf = {'organization': _0x49bb92},_0x1f0d12[_0x6f9c3c["Nlbsb"]])(_0x1fe1cf, 'mp', this['getEncryptContent'](_0x20df23, _0x6f9c3c["NnjXa"])),_0x1f0d12[_0x6f9c3c["Nlbsb"]])(_0x1fe1cf, 'oc', this["getEncryptContent"](_0x4143cf, "c2659527")),_0x1f0d12['default'])(_0x1fe1cf, 'xy', this["getEncryptContent"](_0x7306d7, _0x6f9c3c["wIUSM"])),_0x1f0d12["default"])(_0x1fe1cf, 'jo', this["getEncryptContent"](_0x49f479, _0x6f9c3c["HwXkn"])),_0x1f0d12[_0x6f9c3c["Nlbsb"]])(_0x1fe1cf, _0x6f9c3c["ZGnpS"], _0x3717b1),_0x1f0d12[_0x6f9c3c["Nlbsb"]])(_0x1fe1cf, _0x6f9c3c["CJfJJ"], _0x435e2e),_0x1f0d12[_0x6f9c3c['Nlbsb']])(_0x1fe1cf, _0x6f9c3c["WWhmm"], _0x294474),_0x1f0d12[_0x6f9c3c['Nlbsb']])(_0x1fe1cf, _0x6f9c3c["VRUfH"], "180"),_0x1f0d12[_0x6f9c3c["Nlbsb"]])(_0x1fe1cf, "ostype", _0x528bd),_0x1fe1cf), _0x298b01)_0x2460cd["default"]["log"](_0x119bdb["LOG_ACTION"]["SEND_VERIFY"]),this["sendRequest"](_0x5c100a, _0x599170, _0x1ed2cb, _0x28800d, _0x5c6636, _0x4d541b);

当然也可以不处理,直接调试,可能你有解混淆的功夫,人家代码都已经抠完了。
在这里插入图片描述

这一段代码在做的事情首先是定义了一个organization的字典,然后往这个字典里面进行赋值;而_0x298b01这个也是一个字典,然后再通过extend操作对两个字典进行合并。

在这里插入图片描述

而这四个字段的值,都是通过调用加密函数getEncryptContent,然后传入两个参数,来获取到的参数,所以我们可以先对这四个字段进行分析。
在这里插入图片描述

然后再对这个函数进行化简,我们要知道传入的参数是什么。化简也没什么好方法,就是一个个的手动替换。

xy: YabT6nmJOC0=
oc: h9oFKi8cHpg=
mp: WYfkIZp7GoA=
jo: lQ90183KgD4=

既然这个四个字段传入的参数是固定的,那么返回的值肯定也是固定的,所以这几个参数我们可以直接写死了。

getEncryptContent函数分析

接着来分析这个加密函数

在这里插入图片描述

把鼠标选中内容,然后就可以跳转到对应的位置
在这里插入图片描述

就可以定位到加密函数的位置

在这里插入图片描述

然后用手动挡去混淆的方式,可以看到大致的一些信息,盲猜是一个DES和base64加密。

'mp',this['getEncryptContent']('default', '9cc268c1'),
'oc',this["getEncryptContent"]('DEFAULT', "c2659527")),
'xy',this["getEncryptContent"]('zh-cn', 'b1807581')),
'jo',this["getEncryptContent"]('10', '6d005958')),

那么这里就可以做一个大胆的尝试,用python算法实现一个DES和base64加密,把第一个参数当作是需要加密的字符串,第二个参数当作是Key,看能否输出对应的结果

实现代码如下:

if __name__ == '__main__':key = b'9cc268c1'data_string = 'default'pad_func = lambda text: text + '\0' * (DES.block_size - (len(text.encode('utf-8')) % DES.block_size))aes = DES.new(key, DES.MODE_ECB)enc_data = aes.encrypt(pad_func(data_string).encode("utf8"))res = base64.b64encode(enc_data).decode('utf-8')print(res)

然后查看运行的结果
在这里插入图片描述

mp: WYfkIZp7GoA=

和我们分析的请求中的mp结果是完全一致的。这种方式有些取巧,一部分情况可能不太好使,所以我们还有第二种方式,直接扣代码,大力出奇迹。
在这里插入图片描述

把鼠标放在这个位置,直接跳转到DES源码

在这里插入图片描述

跳转到这个位置之后 把这个函数还有Base64加密的函数全部扣下来,然后运行,缺什么扣什么,一直扣到不报错为止。
在这里插入图片描述

在这里插入图片描述

这样也可以拿到同样的结果

分析其他参数

在这里插入图片描述

前面四个通过DES加密的参数我们已经分析完了,接下来需要分析这个_0x298b01里面的参数来源。
在这里插入图片描述

通过处理过的JS代码可以找到来源,_0x298b01来自于this["getMouseAction"]()的函数结果。通过函数名字getMouseAction大概可以猜到这个对象的数据应该是记录的一些鼠标的坐标信息。
在这里插入图片描述

然后跳转到函数代码的位置,我们把这段代码使用解混淆的脚本进行处理。
在这里插入图片描述

重点关注case "spatial_select"里面的代码,这个里面就是我们所需要的参数,这个switch里面对应的应该是各个不同的验证分支。

spatial_select对应的是点选,slide对应的是滑块。

在这里插入图片描述

然后函数结束的地方还有其他的一些返回数据

_0x4639e5['qd'] = this["getEncryptContent"](_0x23de95, '3c9ed5cb'),_0x4639e5['mu'] = this['getEncryptContent'](_0x3602ea, "e7e1eb0d"),_0x4639e5['ww'] = this["getEncryptContent"](_0x6f9c3c["pxDrO"](_0x53f1f2, _0x5caf5a), '17a94a08'),_0x4639e5['nu'] = this['getEncryptContent'](_0x4c1632, "390aac0d"),_0x4639e5['dy'] = this["getEncryptContent"](_0x1a7546, "a9001672"),_0x4639e5["act.os"] = _0x46483a;_0x4639e5['tb'] = this["getEncryptContent"](_0x2460cd[_0x6f9c3c["Nlbsb"]]["__userConf"]["console"], '6f5e9847'),_0x4639e5['en'] = this["getEncryptContent"](_0x2460cd[_0x6f9c3c["Nlbsb"]]["runBotDetection"](), "9fc1337f"),_0x4639e5['kq'] = this["getEncryptContent"](-(0x7 * -0x537 + -0x1f77 + 0x1 * 0x43f9), _0x6f9c3c['SGQFW'])

这些参数都是通过getEncryptContent这个函数进行加密的,那么我们只需要搞清楚传入的参数分别是什么数据,就可以对整个请求进行模拟了。

在这里插入图片描述

第一个参数和第二个参数是一样的,是一个四个成员的数组,这个就对应了我们进行点选的坐标。

在这里插入图片描述

而另外三个参数则是固定值

在这里插入图片描述

后两个参数是整张点选图片的宽度和高度,可以直接看下_4fc323这个对象

在这里插入图片描述

里面是这个图片对象的信息,包括图片的宽度和高度,然后把剩下的参数也处理一下

_0x4639e5['qd'] = this["getEncryptContent"](_0x23de95, '3c9ed5cb'),
_0x4639e5['mu'] = this['getEncryptContent'](_0x3602ea, "e7e1eb0d"),_0x4639e5['ww'] = this["getEncryptContent"](28504615, '17a94a08'),_0x4639e5['nu'] = this['getEncryptContent'](300, "390aac0d"),_0x4639e5['dy'] = this["getEncryptContent"](150, "a9001672"),_0x4639e5["act.os"] = _0x46483a;
_0x4639e5['tb'] = this["getEncryptContent"](1, '6f5e9847'),
_0x4639e5['en'] = this["getEncryptContent"](0, "9fc1337f"),
_0x4639e5['kq'] = this["getEncryptContent"](-1, 'ebee8dcc')

这一部分的字段,除去_0x23de95是需要分析的坐标信息外,其他的都可以通过传参的方式,调用getEncryptContent函数来获取到对应的值。

分析坐标算法

接下来我们需要对剩下的两个坐标信息的参数进行分析
在这里插入图片描述

其中,_0x23de95selectData_0x3602eamouseData,这两个数据全部都来自于this['data']

在这里插入图片描述

这里我们通过调用栈,找到最上一层的堆栈

在这里插入图片描述

定位到这个位置发现,this['_data']['mouseData']_0x226da4赋值了,所以我们继续跟踪_0x226da4的值。

正常来说switch case分支里面的代码不会被依次执行,但是这个函数不太一样,外面套了一层while循环,然后在switch的变量是一个数组,里面的值是2,0,3,4,1,所以这个switch分支会按照20341的顺序去执行。

在这里插入图片描述

接下来需要对这整个函数进行分析,还是先用脚本去除部分的混淆

在这里插入图片描述

来分析去除混淆后的代码,这样比较方便,在54行这里通过push操作往selectData里面添加数据,那么我们只需要看被添加的数据是在哪生成的

在这里插入图片描述

_0x27bb65在这里被赋值,而这个就是我们要的坐标

_0x27bb65 = [_0x42ca39,_0x1d0195["IXgiz"](_0x28ed43['y'], _0x1e9886) / _0x30fbb2,_0x52aa32
];

这个坐标信息实际上可以拆解为列表中的三个元素,通过逗号分割开,第一个和第三个元素是一个坐标数据,中间的元素是一个算法。我们需要搞清楚这三个数组成员分别是什么。
在这里插入图片描述

第三个成员_0x52aa32实际上就是一个字符串格式的时间戳,前两个应该是当前的坐标通过算法计算出来的结果。

在这里插入图片描述

第一个值的调用来源于上面一行代码

_0x42ca39 = _0x1d0195["rlnuS"](_0x28ed43['x'] - _0x5a1fbf, _0xd3e4df);

这个是一个函数调用,传递了两个参数,分别查看一下这几个数据是什么

在这里插入图片描述

第一个参数是当前鼠标的X坐标,这个坐标可能和真实的坐标不一样,可能是做了等比例缩放,后续可以通过算法的方式来构造。

第二个参数是图片的宽度,然后再来看一下函数的原型:

'rlnuS': function(_0x4781a3, _0x52d7dc) {return _0x4781a3 / _0x52d7dc;

这个函数的代码很简单,就是一个除法指令。

_0x1d0195["IXgiz"](_0x28ed43['y'], _0x1e9886) / _0x30fbb2

至于第二个值,是当前的y坐标,跟第一个值的算法几乎没啥区别。到这里,我们的坐标算法就分析完成了。

结束

最后,如果想要把整个接口进行自动化,需要做这么几个事情。首先把上面的分析过程整理成代码,把每个表单提交的数据对应上,

然后需要对接打码平台,获取到点选验证的图片以后,通过打码平台的接口拿到坐标信息,通过提交坐标信息等数据通过点选验证的校验接口。这样就可以完成这个网站的自动化登陆了。我对这个过程并没有兴趣,只研究点选验证的原理以及逆向分析思路,各位有兴趣可以自行尝试。

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

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

相关文章

XSS漏洞:xss.haozi.me靶场通关

xss系列往期文章&#xff1a; 初识XSS漏洞-CSDN博客 利用XSS漏洞打cookie-CSDN博客 XSS漏洞&#xff1a;xss-labs靶场通关-CSDN博客 XSS漏洞&#xff1a;prompt.mi靶场通关-CSDN博客 目录 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C…

MySQL窗口函数(MySQL Window Functions)

1、窗口函数基本概念 官网地址&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/window-functions.html 窗口可以理解为 记录集合&#xff0c;窗口函数就是在满足某种条件的记录集合上执行的特殊函数。 即&#xff1a;每条记录都要在此窗口内执行函数。 静态窗口&#x…

c#异形窗体遮罩效果

c#异形窗体遮罩效果&#xff0c;移动&#xff0c;关闭&#xff0c;最大化&#xff0c;最小化&#xff0c;还原操作 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D…

C++核心编程——文件操作

本专栏记录C学习过程包括C基础以及数据结构和算法&#xff0c;其中第一部分计划时间一个月&#xff0c;主要跟着黑马视频教程&#xff0c;学习路线如下&#xff0c;不定时更新&#xff0c;欢迎关注。 当前章节处于&#xff1a; ---------第1阶段-C基础入门 ---------第2阶段实战…

蓝桥杯备赛 day 3 —— 高精度(C/C++,零基础,配图)

目录 &#x1f308;前言&#xff1a; &#x1f4c1; 高精度的概念 &#x1f4c1; 高精度加法和其模板 &#x1f4c1; 高精度减法和其模板 &#x1f4c1; 高精度乘法和其模板 &#x1f4c1; 高精度除法和其模板 &#x1f4c1; 总结 &#x1f308;前言&#xff1a; 这篇文…

RabbitMQ常见问题之高可用

文章目录 一、集群分类二、普通集群搭建1. 准备2. 配置3. 运行 三、镜像集群1. 介绍2. 启用方式3. 测试 四、仲裁队列1. 介绍2. 创建 五、Java连接RabbitMQ集群方式 一、集群分类 RabbitMQ的是基于Erlang语言编写,而Erlang又是一个面向并发的语言,天然支持集群模式。RabbitMQ的…

vue3实现动态侧边菜单栏的几种方式总结

基于自建json数据的动态侧边菜单栏 后端接口json数据 src/api/menuList.js const menuList [{url: ,name: 人员管理,icon: icon-renyuan,menuId: 1,children: [{url: /user,name: 用户管理,icon: icon-jurassic_user,menuId: 1001,children: []},{url: /role,name: 角色管…

vite 打包优化

✨专栏介绍 在当今数字化时代&#xff0c;Web应用程序已经成为了人们生活和工作中不可或缺的一部分。而要构建出令人印象深刻且功能强大的Web应用程序&#xff0c;就需要掌握一系列前端技术。前端技术涵盖了HTML、CSS和JavaScript等核心技术&#xff0c;以及各种框架、库和工具…

一篇综述洞悉医学大型语言模型的原理,应用和挑战

在过去的一年中&#xff0c;随着 GPT-4、LLaMA、Mistral&#xff0c;PaLM 等先进技术的突飞猛进&#xff0c;大型语言模型&#xff08;Large Language Models&#xff09;已经引领全球人工智能进入了一个全新的基础模型时代&#xff0c;这一时代不仅开启了技术创新的新篇章&…

opencv多张图片实现全景拼接

最近camera项目需要用到全景拼接&#xff0c;故此查阅大量资料&#xff0c;终于将此功能应用在实际项目上&#xff0c;下面总结一下此过程中遇到的一些问题及解决方式&#xff0c;同时也会将源码附在结尾处&#xff0c;供大家参考&#xff0c;本文采用的opencv版本为3.4.12。 首…

Base64编码原理解析

文章目录 一、Base64Base64编码的原理如下&#xff1a;以字符串"hello world"为例&#xff0c;它的ASCII码为&#xff08;下面&#x1f447;是ASCII码对照表&#xff09;&#xff1a;将这些ASCII码转换为二进制&#xff08;对照上表&#xff09;&#xff1a;将上述二…

Pyside6入门教学——编写一个UI界面并显示

1、安装Pyside6 输入下列命令安装Pyside6。 pip install Pyside6 2、设计UI 打开Qt设计工具&#xff08;在安装Pyside6包的目录下&#xff09;。 【注】我这用的是anaconda虚拟环境&#xff0c;所以我的路径是D:\App\Anaconda3\envs\snake\Lib\site-packages\PySide6。设计…

前端(html+css+javascript)作业--展现家乡的网页

期末期间&#xff0c;老师布置了前端作业&#xff0c;现在放到这里&#xff0c;给各位同志参考。 桂平市是广西壮族自治区的一个美丽的城市&#xff0c;拥有丰富的历史文化和自然景观&#xff0c;属于贵港市管辖&#xff0c;那为什么是看起来是市级而不是县级&#xff0c;其实他…

GaussDB(DWS)查询优化技术大揭秘

GaussDB(DWS)查询优化技术大揭秘 大数据时代&#xff0c;数据量呈爆发式增长&#xff0c;经常面临百亿、千亿数据查询场景&#xff0c;当数据仓库数据量较大、SQL语句执行效率低时&#xff0c;数据仓库性能会受到影响。本文将深入讲解在GaussDB(DWS)中如何进行表结构设计&#…

uni-app 经验分享,从入门到离职(年度实战总结:经验篇)——上传图片以及小程序隐私保护指引设置

文章目录 &#x1f525;年度征文&#x1f4cb;前言⏬关于专栏 &#x1f3af;关于上传图片需求&#x1f3af;前置知识点和示例代码&#x1f9e9;uni.chooseImage()&#x1f9e9;uni.chooseMedia()&#x1f4cc;uni.chooseImage() 与 uni.chooseMedia() &#x1f9e9;uni.chooseF…

5D动感影院新奇体验丰富环境特效7D互动影院

5D动感影院太火啦&#xff01;凭借真实互动感、全视景高清屏幕、立体环绕音效、D打造新颖沉浸式观影体验&#xff0c;成为年轻人/家长/小朋友的新晋打卡聚集地&#xff0c;时刻上演着精彩绝伦的视觉盛宴&#xff01; 1、5D动感影院【硬件组成】&#xff1a;动感平台、金属银幕、…

Alinx ZYNQ 7020 LED调试--in RAM

设置拨码开关为JTAG方式 烧写LED bit stream a. 点击“Program device”烧录程序到FPGA中&#xff08;重新上电程序就丢失了&#xff09; b. /01_led/led.runs/impl_1/led.bit 程序烧录到Flash中 ZYNQ与以往的直接烧录Flash不同&#xff0c;首先必须PS&#xff0c;然后烧…

PXE和kickstart无人值守安装

PXE高效批量网络装机 引言 1.系统装机的引导方式 启动 操作 系统 1.硬盘 2.光驱&#xff08;u盘&#xff09; 3.网络启动 pxe 重装系统&#xff1f; 在已有操作系统 新到货了一台服务器&#xff0c; 装操作系统 系统镜像 u盘 光盘 pe&#xff1a; 小型的 操作系统 在操…

HTTP/HTTPS代理IP在多线程爬虫项目中的实践

在多线程爬虫项目中&#xff0c;HTTP/HTTPS代理IP的实践主要包括以下几个关键步骤&#xff1a; 1. 收集代理IP资源&#xff1a; - 从免费或付费代理IP提供商、公开代理列表网站&#xff08;如西刺代理、无忧代理等&#xff09;抓取代理IP和端口信息。 - 存储这些IP到数据库或者…

统计学-R语言-5.3

文章目录 前言分位数统计量的标准误总结 前言 本篇文章即为概率与分布的最后一篇文章。 分位数 分位数函数是累积分布函数的反函数。 p-分位数是具有这样性质的一个值&#xff1a;小于或等于它的概率为p。 根据定义&#xff0c;中位数即50%分位数。 分位数通常用于置信区间的…