【JavaScript 逆向】极验三代滑块验证码逆向分析

news2024/10/7 18:27:01

声明

本文章中所有内容仅供学习交流,相关链接做了脱敏处理,若有侵权,请联系我立即删除!

案例目标

极验验证码 demo:aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby8=

滑动验证码:aHR0cHM6Ly93d3cuZ2VldGVzdC5jb20vZGVtby9zbGlkZS1mbG9hdC5odG1s

文件版本:slide.7.8.9.js

以上均做了脱敏处理,Base64 编码及解码方式:

import base64
# 编码
# result = base64.b64encode('待编码字符串'.encode('utf-8'))
# 解码
result = base64.b64decode('待解码字符串'.encode('utf-8'))
print(result)

案例分析

抓包

刚进入页面,F12 打开开发者人员工具,未点击生成验证时 Network 中的抓包情况:

现对关键部分进行分析:

  1. register-slide 注册滑动条请求,响应返回:

    • challenge: "d4e44298ed09f654b3c284a5fb6d72ad",动态变化,关键参数

    • gt: "019924a82c70bb123aae90d483087f94",固定值

  2. gettype.php 获取验证码,Query String Parameters:GET 请求时,参数以 url string 的形式进行传递提交了 gt、callback:

    • gt:register-slide 时响应返回的定值

  3. get.php 以 url string 的形式传递了一些参数:

    • gt: register-slide 时响应返回的定值

    • challenge: register-slide 时响应返回的定值

    • w:加密了,环境校验,轨迹

点击滑块后,响应返回的新数据接口:

  1. ajax.php 以 url string 的形式传递了一些参数:

    • gt: register-slide 时响应返回的定值

    • challenge: register-slide 时响应返回的定值

    • w:加密了,环境校验,轨迹,跟前文 get.php 中的 w 值不一样

  2. get.php 以 url string 的形式传递了一些参数:

    • gt: register-slide 时响应返回的定值

    • challenge: register-slide 时响应返回的定值

    • Preview 响应预览中 bg: 带缺口的背景图(乱码),3e72d088a.jpg:

    • fullbg:完整的背景原图(乱码),cd0bbb6fe.jpg:

    • slice:滑块图片,3e72d088a.png

  3. ajax.php 以 url string 的形式传递了一些参数为:

    • gt: register-slide 时响应返回的定值

    • challenge: get.php 时响应返回

    • w:加密了,环境校验,轨迹,跟前文 ajax.php 中的 w 值不一样,至此三个 w 值均不一样

    • 响应预览中,成功返回,message: "success",success: 1,validate 值:​​​​​​

    • 响应预览中,失败返回 message: "fail",success: 0:

抓包步骤梳理分析: 

  1. 进入页面,服务器响应返回一些参数(challenge、gt 等)和一些关键的 js 文件,用于生成图片及校验;

  2. 点击按钮进行验证后,会调用到第一步中的一些 js 文件,生成缺口图、完整背景图、滑块图、w 值等;

  3. 校验滑块是否对其缺口、轨迹是否异常、相关参数是否正确等,判断验证是否成功。

逆向调试

底图还原

点击按钮进行验证,会弹出滑动验证码,审查元素可以发现,底图是通过 canvas 绘制出来的: 

所以可以直接通过事件断点进行定位:

Sources → Event Listener Breakpoints → Canvas → Create canvas context,然后点击刷新验证码即会在 slide.7.8.8.js 文件处断住,点击左下角 { } 进行格式化操作,断在了第 295 行:

控制台打印输出一下该行内容:

// canvas.getContext(contextID)
var o = canvas.getContext('2d');

参数 contextID 指定想要在画布上绘制的类型, '2d' 指定了二维绘图,返回 CanvasRenderingContext2D 对象,该接口是 Canvas API 的一部分,可为 <canvas> 元素的绘图表面提供 2D 渲染上下文,它用于绘制形状,文本,图像和其他对象。

i 为 canvas 画布,宽为 312,长为 160,对应乱序背景图片的长宽:

正常验证码背景图片的长为 160,宽为 260:

该函数体中的内容经过控制流平坦化混淆处理,打乱了函数原有代码执行流程及函数调用关系,使代码逻变得混乱无序,大体架构为:

for(x){
    switch($_DAHHo){
        case xx:
        ...
        break;
        case xxx:
        ...
        break;
    }
}

更多相关可参考:【JavaScript 逆向】AST 技术反混淆

大致为从大数组中根据指定的逻辑按照下标进行取值操作:

在第 299 行打下断点,控制台打印输出:

CanvasRenderingContext2D.drawImage() 方法提供了多种在画布(Canvas)上绘制图像的方式,此处为画图操作:

drawImage(image, dx, dy)
// image:绘制到上下文的元素
// dx:image 的左上角在目标画布上 X 轴坐标
// dy:image 的左上角在目标画布上 Y 轴坐标

img 的 src 末尾为 7bfaaa72b.webp,就是乱序的完整背景图片:

下一行又绘制了一个 2d 画布:

直接在第 312 行打下断点,第 304、305 行对应了上文验证码背景图的长宽:

其中有个 for 循环需要分析一下:

for (var a = r / 2, _ = 0; _ < 52; _ += 1) {
    var c = Ut[_] % 26 * 12 + 1
      , u = 25 < Ut[_] ? a : 0
      , l = o[$_CJEZ(30)](c, u, 10, a);
    s[$_CJEZ(84)](l, _ % 26 * 10, 25 < _ ? a : 0);
}

前文提到完整乱序背景图的比例为 312 x 160,即宽为 320,长为 160,a = r / 2,r 为图片长 ,即 a = 80,为图片长度的一半,此处将图片横切分割为了上下两等份,Ut[_] % 26 * 12 + 1,_ 值为 52,再将图片上下两部分纵向切割为了 26 等份,Ut 数组为取下标的顺序,即数组还原顺序,为固定的,% 26 * 12 + 1 为特征码,不会变动,25 < Ut[_] ? a : 0 判断图片是上半部分还是下半部分,正确图片的顺序为:

0: 39   10: 50  20: 31  30: 14  40: 3   50: 16
1: 38   11: 51  21: 30  31: 15  41: 2   51: 17
2: 48   12: 33  22: 44  32: 21  42: 0
3: 49   13: 32  23: 45  33: 20  43: 1
4: 41   14: 28  24: 43  34: 8   44: 11
5: 40   15: 29  25: 42  35: 9   45: 10
6: 46   16: 27  26: 12  36: 25  46: 4
7: 47   17: 26  27: 13  37: 24  47: 5
8: 35   18: 36  28: 23  38: 6   48: 19
9: 34   19: 37  29: 22  39: 7   49: 18
l = o[$_CJEZ(30)](c, u, 10, a);

CanvasRenderingContext2D.getImageData(),返回一个 ImageData 对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为 (sx, sy)、宽为 sw、高为 sh:

ImageData ctx.getImageData(sx, sy, sw, sh);
// sx:将要被提取的图像数据矩形区域的左上角 x 坐标
// sy:将要被提取的图像数据矩形区域的左上角 y 坐标
// sw:将要被提取的图像数据矩形区域的宽度
// sh:将要被提取的图像数据矩形区域的高度
s[$_CJEZ(84)](l, _ % 26 * 10, 25 < _ ? a : 0);

CanvasRenderingContext2D.putImageData() 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法,如果提供了一个绘制过的矩形,则只绘制该矩形的像素:

void ctx.putImageData(imagedata, dx, dy);
// ImageData:包含像素值的数组对象
// dx:源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)
// dy:源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)

这里就是同时对上下两部分进行拼凑,_ % 26 * 10 表示每个小块取 10 px 像素。

随便拖动滑块,失败后 Network 中会抓包到如下内容:

响应预览中返回失败信息,ajax.php 以 url string 的形式传递的参数中包括 w 参数,通过 Initiator 跟栈进去: 

在第 4567 行 return 处打下断点,再次拖动滑块即会断住:

向上跟栈到第 1182 行,s 中包含请求 url 的全部信息:

所以要找到 s 生成的位置,接着向上跟栈,跟到函数 k 处,在第 1128 行 s 已经被作为参数传进来值了:

接着向上跟,跟到第 868 行,n 即 s 的值:

往上找,在第 844 行打下断点,o 中包含我们所要的 w 参数:

进一步往上跟栈到 $_CCBd 处,在第 6076 行,可以看到 w 参数的值由 h + u 生成:

所以接下来需要知道 h 和 u 的值是怎么生成的,c 的生成位置在第 6065 行,h 的生成位置在第 6067 行:

var u = r[$_CAHJR(706)]()
  , l = V[$_CAIAZ(339)](pt[$_CAHJR(278)](o), r[$_CAHJR(721)]())
  , h = m[$_CAIAZ(769)](l)

u 参数逆向分析

u 由 r[$_CAHJR(706)] 方法生成,先在第 6065 行打下断点,断住后,选中跟进过去,跳转到第 6206 行,在第 6215 行打下断点,点击下一个断点即会断住:

控制台打印后可以看到,返回值 e 为 u 参数的值,定义在第 6212 行,$_CBFJf(339) 为 "encrypt",同时 new U() 的原型链中包含 setPublic 方法,设置公钥,因此可以推测,这里是经过了 RSA 加密,this[$_CBFJf(721)](t) 为一串明文值,先跟进到 this[$_CBFJf(721)] 中,会跳转到第 6196 行,在下一行打下断点,Mt 即为那串明文值:

Mt 是由 rt 方法赋值的,跟进到 rt 方法中,在第 4197 行,在第 4203 行打下断点,可以知道 rt 方法的返回值是由四个 t 方法的值加起来得到的,长度为 16 位:

进一步跟进到 t 方法中,在第 4187 行,在 4192 行打下断点,返回值即为 t 方法的值,每次生成结果不一样:

(65536 * (1 + Math[$_BFBFy(14)]()) | 0)[$_BFBFy(287)](16)[$_BFBEu(489)](1);

控制台打印一下这部分内容:

根据打印内容,手动解混淆:

(65536 * (1 + Math["random"]()) | 0)["toString"](16)["substring"](1);

所以逻辑已经清楚了,直接通过代码对 this[$_CBFJf(721)](t) 的值进行复现即可:

function randomStr() {
    var data = "";
    for (var index = 0; index < 4; index++) {
        data += (65536 * (1 + Math["random"]()) | 0)["toString"](16)["substring"](1);
    }
    return data;
}
​
// console.log(randomStr());
// console.log(randomStr().length);

明文部分解决了,再来看看 RSA 加密部分,跟进到 new U() 原型链中 setPublic 设置公钥的位置:

跳转到第 2895 行,在第 2896 行打下断点,t 为 key 值,e 为公钥模数:

所以接下来只需要把 U 定义的位置找到,导出为全局变量调用即可拿到 key 值,ctrl + f 局部搜索 var U =,会发现其定义位置在第 2030 行,在后面加个 window.yyy = U; 即可导出,整个 js 文件扣下来改写后如下:

!function(){
    wv_ZX.$_AA = function() {...
    }();
    wv_ZX.$_Bo = function() {...
    }();
    wv_ZX.$_CN = function() {...
    }();
    wv_ZX.$_Dg = function() {...
    }();
    function wv_ZX() {}
    !function(){
        ...
        var U = function(){
            ...
        }();
        window.yyy = U;
    }();
}();

粘贴到 Snippets 中,打印结果无误:

key 处 debugger 后,t 值与 e 值也与网页 js 中的一致:

接下来就可以复现 u 值了:

!function(){
    wv_ZX.$_AA = function() {...
    }();
    wv_ZX.$_Bo = function() {...
    }();
    wv_ZX.$_CN = function() {...
    }();
    wv_ZX.$_Dg = function() {...
    }();
    function wv_ZX() {}
    !function(){
        ...
        var U = function(){
            ...
        }();
        window.yyy = U;
    }();
}();
function randomStr() {
    var data = "";
    for (var index = 0; index < 4; index++) {
        data += (65536 * (1 + Math["random"]()) | 0)["toString"](16)["substring"](1);
    }
    return data;
}
​
function getU(){
    return new window.yyy()['encrypt'](randomStr());
}
​
// console.log(getU());

成功获取到 u 值:

l 参数逆向分析

u 值获取到了,下一步就需要获取 l 值了,l 是个大数组:

控制台打印下各部分内容,这里也是个加密,pt[$_CAHJR(278)](o) 包含很多 JSON 格式的信息:

还原一下,r[$_CAHJR(721)]() 就是随机的是十六位字符串:

V["encrypt"](pt["stringify"](o), randomStr())

stringify 是将 JSON 格式转换为字符串,所以那部分信息是由 o 生成的,o 定义在第 6000 行:

拖动几次滑块对比一下参数 o 中有哪些值会发生变化:

imgload 为图片生成时间,不一样的有 aa、passtime、rp、userresponse,滑块拖动的距离不一样,aa 值的长度会不一样,所以 aa 的值很可能是滑块的移动轨迹,aa 定义在第 6005 行:

e 值是函数传进来的参数,所以向上跟栈查看生成的位置,在第 8154 行:

l = n[$_CJJJ_(957)][$_DAAAl(1022)](n[$_DAAAl(957)][$_DAAAl(1027)](), n[$_DAAAl(90)][$_CJJJ_(1004)], n[$_CJJJ_(90)][$_CJJJ_(386)]);

打下断点后,控制台打印一下各部分内容:

n[$_CJJJ_(957)][$_DAAAl(1022)] 为一个函数,传入了三个参数,n[$_DAAAl(957)][$_DAAAl(1027)]() 的值为 aa 的一部分,为轨迹值,n[$_DAAAl(90)][$_CJJJ_(1004)] 为数组,值是固定的, n[$_CJJJ_(90)][$_CJJJ_(386)] 是一个八位字符串,值会变化,根据控制台打印的内容可解混淆为:n["$_CJT"]["c"]n["$_CJT"]["s"],这两个值是 get.php 响应返回的:

因此接下来就需要跟进到 n[$_DAAAl(957)][$_DAAAl(1027)] 中,从第 4049 行一直到第 4117 行,在第 4092 行打下断点,t 即为鼠标轨迹,为 x 轴,y 轴,时间,自执行函数括号中的 this[$_BEGJj(311)] 为传入的轨迹值:

在第 4117 行打下断点,控制台打印,为 n[$_DAAAl(957)][$_DAAAl(1027)]() 的值:

对其内容逐个分析:

r[$_BEHAq(439)]($_BEHAq(50)) + $_BEGJj(476) + i[$_BEGJj(439)]($_BEGJj(50)) + $_BEHAq(476) + o[$_BEHAq(439)]($_BEHAq(50));

控制台打印还原:

r["join"]("") + "!!" + i["join"]("") + "!!" + o["join"]("");

r 在第 4093 行定义为一个空数组,传入的鼠标轨迹:

  • r["join"](""):x轴

  • i["join"](""):y轴

  • o["join"](""):时间

同样将这一部分内容定义为全局变量导出,加到 W[$_CJEZ(251)] 的最后:

window.get_track = W[$_CJEZ(251)]["\u0024\u005f\u0047\u0046\u004a"];

将鼠标轨迹作为参数传递进去,mouseTrack:

"\u0024\u005f\u0047\u0046\u004a": function(mouseTrack) {
    var $_BEGJj = wv_ZX.$_CN
      , $_BEGIl = ['$_BEHCf'].concat($_BEGJj)
      , $_BEHAq = $_BEGIl[1];
    $_BEGIl.shift();
    var $_BEHBo = $_BEGIl[0];
    function n(t) {...
    }
    var t = function(t) {...
    }(mouseTrack)
      , r = []
      , i = []
      , o = [];
    return new ct(t)[$_BEHAq(94)](function(t) {...
    }),
    r[$_BEHAq(439)]($_BEHAq(50)) + $_BEGJj(476) + i[$_BEGJj(439)]($_BEGJj(50)) + $_BEHAq(476) + o[$_BEHAq(439)]($_BEHAq(50));
}

获取轨迹值,this[$_BEGJj(311)]

传入导出的 window.get_track() 中,成功得到想要的结果:

至此传入的三个参数都解决了,就需要跟进到 n[$_CJJJ_(957)][$_DAAAl(1022)] 函数中没在第 4135 行:

同样将这部分内容导出即可:

window.get_func = W[$_CJEZ(251)]["\u0024\u005f\u0042\u0042\u0045\u0053"];

将这一部分封装成函数,参数值先固定,验证一下:

function getTrack_(){
    return window.get_func(window.get_track([[-41,-33,0],[0,0,0],[1,0,67],[5,0,84],[10,0,88],[17,0,96],[24,0,104],[29,0,111],[33,0,117],[36,0,128],[39,0,133],[40,0,144],[42,0,148],[43,-1,155],[44,-1,164],[46,-1,171],[48,-2,177],[49,-2,186],[50,-2,194],[51,-2,207],[51,-2,254]]), [12, 58, 98, 36, 43, 95, 62, 15, 12], "705a5874");        
}

成功获取到结果,aa 参数复现完成:

对比一致: 

接下来是 userresponse,在第 6014 行:

t 为滑块滑动的距离,i[$_CAHJd(182)] 为 challenge 的值:

跟进到 H 中,在第 704 行,将其作为全局变量导出:

window.userResponse = H;

t 值,challenge 值写入,控制台打印测试:

同样方法也可以将 ep 等导出:

window.getPasstime = ne[$_CJEZ(251)]["\u0024\u005f\u0043\u0043\u0043\u0071"];

passtime 值为 n,这里值为 374:

断住后向上跟栈到 $_CGlj 中,在第 8164 行生成:

n[$_DAAAV(871)] = $_Ii() - n[$_DAAAV(961)]

为滑块滑动开始到结束的时间:

rp 定义在第 6076 行,把 gt、32 位 challenge、passtime 通过 X 方法进行了加密:

// 混淆
o[$_CAIAt(791)] = X(i[$_CAIAt(104)] + i[$_CAIAt(182)][$_CAHJd(139)](0, 32) + o[$_CAHJd(704)]);

// 解混淆
o['rp'] = X(i['gt'] + i['challenge']['slice'](0, 32) + o['passtime']);

X 方法定义在第 1876 行:

同样在函数末尾,将其导出为全局变量: 

window.xFunc = X;
window.xFunc(i['gt'] + i['challenge']['slice'](0, 32) + o['passtime']);

对比测试,结果一致:

后面的直接先写成固定值,一开始的 l 参数复现大半:

现在把 V["encrypt"] 导出即可,局部搜索 var V =,只有一个结果,定义在第 2974 行,在函数结尾导出为全局变量:

window.getV_encrypt = V["encrypt"];

gt[$_CAIAt(218)](o) 先写为固定值,打印测试一下,成功得到结果:

h 参数逆向分析

拿到 l 值,再将 m[$_CAIAt(782)] 方法导出即可进一步拿到 h 值,在第 1568 行,同样导出为全局变量,控制台打印输出结果,成功得到 h 值: 

function getH(){
    return window.getM["\u0024\u005f\u0047\u0047\u0063"](window.getV_encrypt('{"lang":"zh-cn","userresponse":"6d06000dd600","passtime":269,"imgload":1193,"aa":"P,-,,,(!!@ypy!)Zy!)t!)!)!)yyXstsssxsussss(!!(k0020028/112.19/11CC:0)","ep":{"v":"7.8.9","$_BIB":false,"me":true,"tm":{"a":1668740199343,"b":0,"c":0,"d":0,"e":0,"f":1668740199352,"g":1668740199352,"h":1668740199352,"i":1668740199352,"j":1668740199352,"k":0,"l":1668740199357,"m":1668740199759,"n":1668740199760,"o":1668740199770,"p":1668740200226,"q":1668740200226,"r":1668740200229,"s":1668740200229,"t":1668740200229,"u":1668740200230},"td":-1},"vsof":"2515396075","rp":"3ced63451bb55c70951d6bbb5b851096"}', "c18a0f7fbb499af0"));
}
​
function getW(){
    return getH() + getU();
}

将值固定后对比,结果一致:

控制台打印,成功得到 w 值:

结果校验

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

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

相关文章

Mysql相关的各种类型文件

Mysql相关的各种类型文件文件大汇总Mysql数据库自身文件参数文件日志文件错误日志慢查询日志常用参数设置慢查询日志文件慢查询表更换引擎通用日志二进制日志套接字文件pid文件表结构定义文件Innodb存储引擎的文件表空间文件redo日志文件大汇总 Mysql和Innodb启动和运行过程中…

[附源码]计算机毕业设计springboot市场摊位管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

蓝桥杯嵌入式AD采样解析

文章目录前言一、什么是AD采样二、原理图查看及cubeMX配置三、代码解析总结前言 本文将带大家学习AD采样。 一、什么是AD采样 原理部分这里就不多介绍了&#xff0c;给大家推荐一篇文章大家自己去了解一下即可。 AD采样 二、原理图查看及cubeMX配置 在板子上面有两个可调电…

野火FPGA系列教学视频---多路选择器

大体思路是&#xff1a;有两路输入信号&#xff0c;一个选通信号&#xff0c;当选通信号高电平时&#xff0c;按输入1的波形&#xff0c;当选通信号为低电平时&#xff0c;按输入2的波形。 大致波形图如下图&#xff1a; rtl代码如下&#xff1a; module mux2_1 (input wir…

企业应如何选择合适的电子采购软件?

现如今是供应链竞争占据主导地位的互联网时代&#xff0c;如果使用一款高效的电子采购软件&#xff0c;不仅能为企业带来采购时的便捷&#xff0c;还成就了企业整个采购供应链的高速流畅运转&#xff0c;促进买卖双方的良性合作。 现在的市场中电子采购软件有很多&#xff0c;…

【目标检测】Faster R-CNN 论文复现代码(含源代码)

Faster R-CNN 论文复现代码 详细的代码使用守则&#xff1a; https://github.com/biluko/Faster-RCNN-Pytorch博客地址为&#xff1a; https://blog.csdn.net/wzk4869/article/details/128133224?spm1001.2014.3001.5501一、nets文件夹下 init.py classifier.py import w…

算法刷题——二叉树部分操作(翻转二叉树,平衡二叉树,最大深度)

翻转二叉树 package 二叉树.反转二叉树;import 二叉树.TreeNode;import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue;public class Solution {public static void main(String[] args) {TreeNode rootnew TreeNode(1);T…

【Javadoc生成开发文档(Terminal或IDEA中)】

Javadoc生成开发文档一、Javadoc工具介绍二、常用标记三、使用方式四、生成文档的两种方式1.Terminal方式2.IDE方式一、Javadoc工具介绍 大家在查看官网文档的时候&#xff0c;会不会感慨人家的帮助文档写的真有逻辑&#xff0c;层次分明&#xff1f; 不要羡慕&#xff0c;你…

最便宜的唯品会

你买的真的划算吗&#xff1f;这里可以看到什么时候最划算 作为一个每个月在vip上买买买花费几万的资深vip用户&#xff0c;我想给你说的是&#xff0c;VIP上的东西是真的。除了平时的衣服鞋子&#xff0c;再到家里的微波炉、电磁炉、热水壶等小家电&#xff0c;我都是从给vip…

抓包工具简单介绍和 fiddler 安装

目录 1、 抓包工具介绍 2、原理 3、fiddler 安装 1、 抓包工具介绍 抓包工具&#xff0c;是个特殊的软件&#xff0c;相当于一个 “代理程序”&#xff0c;浏览器给服务器发的请求就会经过这个代理程序&#xff0c;进一步的就能分析出请求和响应的结果如何。 通俗的讲&…

我们真的需要把训练集的损失降到零吗?

在训练模型的时候&#xff0c;我们需要将损失函数一直训练到0吗&#xff1f;显然不用。一般来说&#xff0c;我们是用训练集来训练模型&#xff0c;但希望的是验证机的损失越小越好&#xff0c;而正常来说训练集的损失降到一定值后&#xff0c;验证集的损失就会开始上升&#x…

手摸手教你 docker+jenkins+gitlab 部署你的前端项目

学习了一周的CICD,踩了很多坑,都是泪,特此记录一下整个过程,本次项目产出效果是,git push的时候自动化直接部署到服务器上,以下是整个大致流程: 本地代码push到gitlab gitlab通过webhook通知到jenkins jenkins拉取gitlab仓库代码,并执行shell脚本 shell脚本执行docker命令,打…

python数据分析——NumPy基础

目录 一、创建数组的方法 二、array的属性 三、创建特殊的数组 四、数组的变换 4.1、数组重塑 4.2、数组合并 4.3、数组分割 4.4、数组转置和轴对换 五、数组的索引和切片 5.1、一维数组的索引 5.2、多维数组的索引 5.3、多维数组的访问 六、数组的运算 6.1、数组…

Apollo 应用与源码分析:guardian 紧急处置

目录 概念 代码 分析 概念 Guardian模块的主要作用是监控自动驾驶系统状态&#xff0c;当出现模块为失败状态的时候&#xff0c;会主动切断控制命令输出&#xff0c;并且刹车。 有点像是保险丝&#xff0c;有一个fallback机制。 guardian模块的触发条件主要有2个。 上报…

虚拟机的快照与克隆

简单回顾以下快照 快照的拍摄&#xff1a; 记录虚拟机当前的状态 拍摄快照时&#xff0c;系统一定要处于关机状态 转到&#xff1a; 回到某一个历史快照节点 克隆 复制某一个历史快的的节点 克隆的方式 链接克隆&#xff1a; 当前节点文件家只存储差异性数据 相同数据放在原…

RabbitMQ之延迟队列

延迟消息是指的消息发送出去后并不想立即就被消费&#xff0c;而是需要等&#xff08;指定的&#xff09;一段时间后才触发消费。 例如下面的业务场景&#xff1a;在支付宝上面买电影票&#xff0c;锁定了一个座位后系统默认会帮你保留15分钟时间&#xff0c;如果15分钟后还没付…

zcu106 lwip搭建以太网配置寄存器

文章目录实验一1.配置网口GEM32.导出xsa文件&#xff0c;在vitis中创建工程&#xff0c;选择freertos10_xilinx的操作系统来使用3.配置lwip211&#xff0c;选择SOCKET API的模式4.创建工程 选择FreeRTOS Iwip TCP Perf Server模板5.代码分析main.cfreertos_tcp_perf_server.cfr…

基于yolov5n的轻量级MSTAR遥感影像目标检测系统设计开发实战

做过很多目标检测类的项目了&#xff0c;最近看到一个很早之前用过的数据集MSTAR&#xff0c;之前老师给的任务是基于这个数据集来搭建图像识别模型&#xff0c;殊不知他也是可以用来做目标检测的&#xff0c;今天正好有点时间就想着基于这个数据集来做一下目标检测实践。 首先…

利用车载摄像头了解道路语义的鸟瞰图

以下内容来自从零开始机器人SLAM知识星球 每日更新内容 点击领取学习资料 → 机器人SLAM学习资料大礼包 #论文##开源代码# Understanding Bird’s-Eye View of Road Semantics using an Onboard Camera 论文地址&#xff1a;https://arxiv.org/abs/2012.03040 作者单位&#…

自助建站工具

每用一次自助建站工具&#xff0c;就有一个程序员失业。 作为企业老板的你&#xff0c;要为公司的获客&#xff0c;企业推广发愁&#xff0c;但是预算有限&#xff0c;招人也很困难&#xff0c;不仅要面试程序员&#xff0c;后续还要检验这个程序员的功力&#xff0c;实在是太…