针对二维码解析库的 Fuzzing 测试

背景

在四月份的时候出了那么一个新闻,说微信有一个点击图片就崩溃的 bug,当时各大微信群里都在传播导致手机各种闪退。

wechat.png

由于当时笔者正在忙着卷 Java Web,没有第一时间去蹭这个热点,不过当时也稍微了解了一下 crash 的原理。最近对于 Java 的漏洞挖掘正好告一段落,于是就突然想起了这个事。

这个 bug 的根源在于一个畸形的二维码解码时导致的空指针错误,详细分析和修复的记录可以参考 fix(wechat_qrcode): Init nBytes after the count value is determined #3480。

在国内二维码可以说是随处可见的东西,如果说解码的核心 SDK 有漏洞,那么很可能影响非常广泛。加上许多应用,基于各种奇怪的业务和目的还实现了静默解码的功能,要是能够发现一个可以利用的漏洞,那也是一个典型的 0-click 场景。

因此笔者打算简单 Fuzz 一下相关的二维码解码代码,放在服务器后台慢慢跑着,也不会过多占用搬砖时间,希望哪天清晨 boki 醒来能够有些意外收获。

编译

Fuzz 的第一步自然是先能成功编译目标代码,然后再尝试使用带插桩的编译器进行编译。

之前出现问题的代码仓库是 https://github.com/opencv/opencv_contrib,这是 opencv 下的一个子仓库,提供了许多额外的 OpenCV 模块,根据 README 的介绍,编译这些模块的过程如下:

$ cd <opencv_build_directory>
$ cmake -DOPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/modules <opencv_source_directory>
$ make -j5

也就是说编译这些模块还需要依赖 OpenCV 的源代码仓库,因此我们把这两个仓库都下载下来进行编译。这里图省事我就直接使用 AFL++ 进行编译了,使用 cmake 的编译命令如下:

cmake -DCMAKE_C_COMPILER=afl-clang-fast -DCMAKE_CXX_COMPILER=afl-clang-fast++ \-DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -g" \-DCMAKE_C_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -g" \-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all" \-DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all" \-DCMAKE_BUILD_TYPE=Debug,ASAN,UBSAN -DWITH_SSE2=ON -DMONOLITHIC_BUILD=ON -DBUILD_SHARED_LIBS=OFF \-DOPENCV_EXTRA_MODULES_PATH=/home/evilpan/fuzzing/opencv_contrib/modules \/home/evilpan/fuzzing/opencvmake -j128 -C modules/wechat_qrcode

关键点在于不编译动态库,而是使用静态库,因为 AFL 对于最终的测试程序只会测试静态编译代码中的覆盖率而不会考虑动态编译的库。

后来发现 cmake 可以不用指定那么多选项,只需要指定 afl-clang,config 之后在 make 时通过环境变量指定 AFL_USE_ASAN 即可,AFL 会选择好合适的编译参数。参考:
https://aflplus.plus/docs/fuzzing_in_depth/

$ cmake -DCMAKE_C_COMPILER=afl-clang-fast -DCMAKE_CXX_COMPILER=afl-clang-fast++ -DBUILD_SHARED_LIBS=OFF
$ env AFL_USE_ASAN=1 make -j32

小试牛刀

编译完后就可以编写测试代码了。不过根据前面的 pull-request,在修复漏洞时也提供了一个 testcase:

TEST(Objdetect_QRCode_bug, issue_3478) {auto detector = wechat_qrcode::WeChatQRCode();std::string image_path = findDataFile("qrcode/issue_3478.png");Mat src = imread(image_path, IMREAD_GRAYSCALE);ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;std::vector<std::string> outs = detector.detectAndDecode(src);ASSERT_EQ(1, (int) outs.size());ASSERT_EQ(16, (int) outs[0].size());ASSERT_EQ("KFCVW50         ", outs[0]);
}

可以参考这个代码来编写我们的 test-harness。我这里是直接改掉了 test_main.cpp,不使用单元测试框架而是直接自己编写,一个初始的 test-harness 代码如下:

int main(int argc, char **argv) {if (argc < 2) return 1;std::string image_path(argv[1]);Mat src = imread(image_path, IMREAD_GRAYSCALE);if (!src.empty()) {auto detector = wechat_qrcode::WeChatQRCode();std::vector<std::string> outs = detector.detectAndDecode(src);}return 0
}

修改后只需要重新编译这个模块即可:

make -C modules/wechat_qrcode

生成的测试二进制文件为 build/bin/opencv_test_wechat_qrcode:

afl-fuzz -i corpus/ -o output -t +2000 -- ./opencv_test_wechat_qrcode @@

corpus 中随便丢了几张畸形二维码文件,直接开跑!

test1.png

虽然能跑,但是慢的一批!

初步优化

上面的的 fuzz 代码实在太慢,必须要进行优化!组织架构调整!把寒气也传递给每一个 fuzzer!

由于前面的的运行是直接读取文件,因此一个直观的优化是使用 AFL++ 的 persistent mode,稍微修改一下代码,直接把 buffer 当做源图片进行读取,如下所示:

int fuzz_buf(unsigned char *buf, size_t size) {Mat src = imdecode(Mat(1, size, CV_8UC1, buf), IMREAD_GRAYSCALE);if (!src.empty()) {auto detector = wechat_qrcode::WeChatQRCode();std::vector<std::string> outs = detector.detectAndDecode(src);return outs.size();}return -1;
}__AFL_FUZZ_INIT();int main(int argc, char **argv) {// if (argc < 2) return 1;// return fuzz(argv[1]);
#ifdef __AFL_HAVE_MANUAL_CONTROL__AFL_INIT();
#endifunsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;while (__AFL_LOOP(10000)) {int len = __AFL_FUZZ_TESTCASE_LEN;fuzz_buf(buf, len);}return 0;
}

这次执行就不需要 @@ 了,可以直接运行:

afl-fuzz -i corpus/ -o output -t +2000 -- ./opencv_test_wechat_qrcode_persist

test2.png

可以看到速度已经有了显著的提升!并且跑了十几分钟后就已经出现了 Crash!

再次优化

先暂停前面的 fuzzer 来分析一下现有的 crash,执行出现的崩溃都大致如下所示:

$ ./opencv_test_wechat_qrcode output/default/crashes/id\:000001\,sig\:06\,src\:000478\,time\:871148\,execs\:98951\,op\:havoc\,rep\:4
terminate called after throwing an instance of 'cv::Exception'what():  OpenCV(4.7.0-dev) /home/jielv/fuzzing/opencv/modules/imgcodecs/src/loadsave.cpp:74: error: (-215:Assertion failed) size.width > 0 in function 'validateInputImageSize'Aborted

其实,我对你是有一些失望的。当初把你写出来,是希望你能对标古河的。我是希望你跑起来后,你能够拼一把,快速给我几个 segment fault。对于你这个层级,不是丢出几个 Abort 就可以的。

虽然也有我 -g 的原因,但这里的 Abort 实际上都出现在上面的 imdecode 中,这跟我的预期有点跑偏,明明想跑的是二维码解码,结果却在无关的地方运行了半天。虽然 image codec 也是一个攻击面,但并不是现在想要搞的,而且这个攻击面肯定也被很多其他人搞过了,出现问题的几率并不是很大。

因此,我们需要进一步优化代码。现在 AFL 变异的是 jpg/png 的图片,但我们想要变异的其实是二维码的图片内容!因此这次优化有两个思路:

  1. 提供自定义的变异策略,每次返回一个变异的二维码;
  2. 还是让 AFL 自己变异,但是变异后的内容直接转成 cv::Mat 然后进行二维码解码;

不管使用哪个思路,都需要能够将图片和 cv::Mat 进行互相转换,否则即便有 crash 也可能不能生成合法的图片导致无法利用。

Mat2Vector

一开始想着偷个懒,直接让 ChatGPT 来帮我生成代码将 Mat 转成字符数组,如下所示:

std::vector<char> mat_to_vector(const cv::Mat& mat) {// Copy data from cv::Mat to vector<char>std::vector<char> data(mat.data, mat.data + mat.total() * mat.elemSize());return data;
}

然后,将原来的预料图片用 imread 读取成 cv::Mat,并用上面的函数转成 vector 并保存到磁盘中,形成新的预料,这样在变异的时候可以直接变异并生成 cv::Mat:

int fuzz_buf(unsigned char *buf, size_t size) {// Mat src = imdecode(Mat(1, size, CV_8UC1, buf), IMREAD_GRAYSCALE);Mat src = Mat(std::vector<unsigned char>(buf, buf + size));return fuzz_mat(src);
}

理想很美好,但是这样跑并没有结果!猜测是 Mat 中除了 data 数据,应该还有一些元数据(metadata),用以表示矩阵的特性,比如行/列,直接填入数据无法表示原始的矩阵,从而在二维码解码时很早就出错退出了。

cv::Mat

既然偷懒走不通,就只能认真看一下 Mat 了。

cv::Mat 是 OpenCV 中用于表示 n 维数组的数据结构,用于表示 n 维的单通道或者多通道数组,通常是结构比较紧凑的矩阵。对于稀疏数据的高维矩阵则一般用 SparseMat 来进行表示。

对于矩阵/数组 M 而言,其数据布局根据 datastep 决定,例如对于二维数组:

M.at(i,j) = M.data + M.step[0] * i + M.step[1] * j

注意 M.step[i] >= M.step[i+1],事实上 M.step[i] >= M.step[i+1]*M.size[i+1]。这意味着对于二维矩阵而言,数据是按照行进行存储的(row-by-row),而对于三维矩阵数据则是按面进行存储(plane-by-plane)。

我们的目标是创建一个代表二维码图片的 Mat,最好是能够保存到磁盘中并从磁盘读取,方便我们使用 afl-fuzz 指定语料并进行 fuzz。于是想着有没有什么序列化/反序列化是针对 cv::Mat 的,查了一下还真有,序列化的代码如下:

cv::Mat img = cv::imread("example.jpg"); // Load an image
cv::FileStorage fs("example.yml", cv::FileStorage::WRITE); // Create a file storage object for writing
fs << "image" << img; // Write the image to the file
fs.release(); // Close the file

反序列化过程和序列化过程类似:

cv::Mat img;cv::FileStorage fs("example.yml", cv::FileStorage::READ); // Create a file storage object for reading
fs["image"] >> img; // Read the image from the file
fs.release(); // Close the file

这里是将 cv::Mat 序列化成了 YAML 数据,输出的格式如下:

%YAML:1.0
---
img: !!opencv-matrixrows: 480cols: 640dt: udata: [ 103, 103, 104, 107, 110, 112, 112, 111, 114, 113, 113, 115,...134, 135, 134, 132 ]

如果将 YAML 文件作为语料的话,又涉及了 YAML 的解析以及 FileStorage 中的无关代码,和需求有所差距。不过从序列化的数据中我们能够看出一点,即 cv::Mat 中除了 data 数据外包含的额外元数据只有 rows、cols 和 dt。其中 dt 应该表示 data type,uCV_8U 的缩写。

由于这是一个黑白的,480x640 的二维码图片,如果我们想要变异 cv::Mat,只需要保存行/列/类型不变,针对 data 区域进行 bitflip 变异即可!

不过,只进行 bitflip 变异的空间将会高达 2^(640*480),无疑是个几何数据,即便是只针对小尺寸图片如 20x20 那也是不能接受的。因此我们还是寄希望于 AFL 的覆盖率反馈能产生合适的变异!

Crash 分析

使用上述策略优化之后裁剪语料的长度,仅使用一个 29x29 的二维码图片生成的矩阵作为初始语料进行变异:

int fuzz_buf(void *buf, size_t size) {// Mat src = imdecode(Mat(1, size, CV_8UC1, buf), IMREAD_GRAYSCALE);if (size < 841) return -1;Mat src = Mat(29, 29, CV_8U, buf);return fuzz_mat(src);
}

跑起来之后发现速度又变得很慢,估计语料还是太大,先放在后台跑一会,然后去看了两集异世界厕纸番。回来之后发现居然有 crash:

test3.png

简单分析一下发现都是同一处代码的 crash,精简的 poc 执行输出如下:

$ opencv_test_wechat_qrcode poc.bin
/home/evilpan/fuzzing/opencv_contrib/modules/wechat_qrcode/src/zxing/qrcode/detector/detector.cpp:1022:50: runtime error: signed integer overflow: -2147483648 + -2147483648 cannot be represented in type 'int'

这是一个整数溢出,出现在 qrcdoe 检测过程中计算维度时:

int Detector::computeDimension(Ref<ResultPoint> topLeft, Ref<ResultPoint> topRight,Ref<ResultPoint> bottomLeft, float moduleSizeX, float moduleSizeY) {int tltrCentersDimension = ResultPoint::distance(topLeft, topRight) / moduleSizeX;int tlblCentersDimension = ResultPoint::distance(topLeft, bottomLeft) / moduleSizeY;float tmp_dimension = ((tltrCentersDimension + tlblCentersDimension) / 2.0) + 7.0; // 问题在这行int dimension = cvRound(tmp_dimension);int mod = dimension & 0x03;  // mod 4switch (mod) {  // mod 4case 0:dimension++;break;// 1? do nothingcase 2:dimension--;break;}return dimension;
}

计算锚点时 tltrCentersDimension + tlblCentersDimension 产生了整数溢出,PoC 如下:

poc.png

当然,这个 PoC 直接扫码是扫不出来的,因为我们传入的是原始数据,而且是死在了 detector 之中。

画蛇添足

虽然现在已经可以产生一些崩溃,但注意到图像并不是二值图像而是灰度图,因为我们原始的语料是 Mat.data() 保存而成的二进制数据,这个数据中每个像素还是 8 bit 的,只不过只有两种值,分别是 0 和 255,而我们变异过程中就成了灰度图。

为了解决这个问题,我们只能重新构建序列化的数据结构,确保每个字节的变异都能产生有效的二值图像。图像中每个像素只占 1 bit,由于内存是字节寻址的,每个字节可以保存 8 个像素。

写了个 MatWrapper 来封装 cols、rows、type 和 data 数据,并添加 serialize/deserialize 方法去实现序列化:

class MatWrapper {
public:int mRows;int mCols;int mType;int mSize;std::vector<uchar> mBuf;MatWrapper() {}void save(const std::string& filename, bool compact=false) {std::ofstream fs(filename);fs << serialize(compact);fs.close();}void load(const std::string& filename, bool compact=false) {std::stringstream ss;std::ifstream file(filename, std::ios::binary);ss << file.rdbuf();deserialize(ss.str(), compact);}void fromImage(const std::string& filename) {auto mat = imread(filename, IMREAD_GRAYSCALE);mRows = mat.rows;mCols = mat.cols;mType = mat.type();// assert(mat.elemSize() == 1);mSize = mat.total() * mat.elemSize();mBuf = std::vector<uchar>(mat.data, mat.data + mSize);}void toImage(const std::string& filename) {imwrite(filename, get());}Mat get() { return Mat(mRows, mCols, mType, mBuf.data()); }std::string serialize(bool compact = false) const { }bool deserialize(const std::string& buf, bool compact = false) { }// ...
};

compact 表示是否将 8 像素压缩为 1 字节,完整代码比较长这里就不贴了。写完之后先把之前的二维码图片语料批量转换为了自定义序列化的数据,然后使用 AFL 指定新的语料进行变异和 fuzz。一开始跑出来的都是我自己代码的 bug (囧),挨个修完之后终于可以很快跑出上面的整数溢出了,而这次的 PoC 也是真正的黑白二维码图片。

但是!新的测试用例跑起来也是非常之慢,可能是反序列化的时候做了太多变换,导致速度其实和使用标准的压缩图片格式去跑差不多,总而言之感觉有点白给,但还是让它先跑一会。

二维码

在把上面的 Fuzzer 放在后台运行的时,我也想清楚了这个测试用例的问题所在。当前变异的策略虽然能够生成二值的 Bitmap 图像,但并不总是合法的二维码,所以代码覆盖率始终在 detect 阶段过不去而没有执行到 decode

一个理想的策略是每次都能生成随机的、合法的二维码,并且在点阵的基础上进行变异。为此,我们需要先了解二维码的实现方式。

基本结构

一个二维码通常由于多个部分组成,如下所示:

qrcode.png

此外还有 M3 Micro QR Code,只包含一个定位点,不过比较少用,微信也扫不出来,这里先不介绍。

各个部分的简单介绍如下:

  • Quiet Zone: 二维码周围的白边,底色需要与二维码的颜色不同防止影响解析;
  • Postion Detection Patterns: 位置检测模式,也称为定位点,用于定位二维码;使用点格(module)表示外围黑边大小是 7x7,中间的黑块大小是 3x3
  • Separators for Postion Detection Patterns: 定位点周围的分隔线,线宽为一个点格;
  • Timing Patterns: 时序模式,用于定位 x/y 轴,二维码弯曲时可以通过这两条线的弯曲去进行一定程度的修复;
  • Alignment Patterns: 对齐模式,在 Version 2 及以后才有,用于进一步辅助对齐。每个对齐模式的大小是黑格 5x5,白格 3x3,中间黑点占一格。对齐模式的个数和 Version 有关;
  • Format Infomation: 格式信息,占 30 个点格,两边各占 15 个,内容相同,互为备份;
  • Version Information: 版本信息,也是有两个互为备份的区块,各占 3x6 大小;
  • Data and Error Correction Codewords: 数据区,包含编码的数据正文以及纠错信息;

二维码的最小单位是点格(module),一个二维码边长所占点格的大小称为二维码的维度(Dimension),维度和版本有关:

Dimension = Version * 4 + 17

例如当 Version 等于 2 时,边长就是 25 个点格:

width.png

Format Information 区包含了纠错级别等信息,前面说过一共 15 点格,包括:

  • 纠错等级: 2 点格,即 4 个纠错等级;
  • 数据掩码: 3 点格,即 8 种掩码类型;
  • 纠错码: 10 点格;

纠错码计算使用 BCH Code,用到的 Code Words 就保存在前面说过的数据区。

变异策略

了解二维码的基础知识之后,我们至少知道了在一个二维码中哪些能变哪些不能变。二维码的编码是相当复杂的,虽然我们可以使用现有的编码策略去生成二维码,但是这会导致我们错过可能由于编码问题导致的漏洞。

秉承着先跑起来再说的原则,三下五除二用 Python 写了一个非常丑陋的原型,首先是生成一个随机的二维码:

def random_qrcode() -> qrcode.QRCode:version = randint(1, 40)box_size = randint(1, 8)border = 1error_correction = randint(0, 3)mask_pattern = randint(0, 7)data = randstr(randint(1, 25))qr = qrcode.QRCode(version=version,error_correction=error_correction,box_size=box_size,border=border,image_factory=None,mask_pattern=mask_pattern)qr.add_data(data)qr.make()return qr

make 之后 qrcode 的 modules 已经生成了,所以我们在其基础上进行变异:

def mutate(qr: qrcode.QRCode, add_buf):if not add_buf or len(add_buf) == 1:# do not mutatereturn# flip modules in qrcode randomlywidth = len(qr.modules)rng = random.Random()flipped = set()for i in range(0, len(add_buf) - 1, 2):val = add_buf[i] + (add_buf[i+1] << 8)if val in flipped:continueflipped.add(val)rng.seed(val)x = rng.randint(0, width - 1) # coly = rng.randint(0, width - 1) # row# print("flip", (x, y))# avoid position patterns, 7x7 and 1 spaceif x < 8 and y < 8:continueif x < 8 and y >= (width - 8):continueif x >= (width - 8) and y < 8:continueqr.modules[y][x] = not qr.modules[y][x]

qr.modules 是表示点格的二维数组,我们的变异策略非常简单粗暴,就是直接基于输入的数据对点格进行随机的翻转,黑的变白的,白的变黑的。几个小优化:

  1. 使用 Random 来将 0xffff 大小的随机数 scale 到宽度 width 的范围;
  2. 对于已经翻转过的点格就不用再次翻转了;
  3. 随机翻转的范围排除掉三个角落的定位标志区;

Custom Mutators

现在我们已经可以生成随机的二维码图片了!接下来就需要将其与 Fuzzer 进行结合。在官方文档 Custom Mutators in AFL++ 中提到,我们可以编译一个 .so 并通过 AFL_CUSTOM_MUTATOR_LIBRARY 环境变量给 AFL++ 提供自定义的变异器,而且也可以使用 Python API 来提供变异器。

因此我们的 mutator 如下即可:

def fuzz(buf, add_buf, max_size):"""Called per fuzzing iteration."""qr = random_qrcode()mutate(qr, add_buf)img = qr.make_image()stream = BytesIO()img.save(stream, "png")ret = stream.getvalue()if len(ret) > max_size:return bytearray(buf)return bytearray(ret)

注: fuzz 接口的参数 buf 一般包含原始 corpus 的内容,add_buf 则是变异后返回的内容,这里是 PNG 数据。所以前面想把 add_buf 作为变异源来修改 modules 的想法其实是有问题的,当时误以为 add_buf 是随机数据但熵根本不够,因此后面直接把输入数据忽略了,全部随机生成。

将其保存为 random_qrcode.py 并使用下面的命令去启动:

env AFL_CUSTOM_MUTATOR_ONLY=1 PYTHONPATH=$PWD/mutators AFL_PYTHON_MODULE=random_qrcode afl-fuzz -i corpus/ -o output -t +9000 -- ./opencv_test_wechat_qrcode_persist2

这里我指定了 AFL_CUSTOM_MUTATOR_ONLY=1,不需要 AFL 自身的变异,因为 AFL 的变异是基于原始语料的,而我并不希望在原始的图片二进制层级进行变异,否则就又回到了最初的场景。现在我们每次生成一张随机的二维码图片,且肯定是合法的 PNG 格式,避免遭遇图片解码时的异常。初始语料我也设置为表示坐标的点而不是图片。

当然,这个 fuzzer 跑起来巨慢!这是因为我们每次生成时都需要先生成一张二维码,变异,然后编码成 PNG 图片,再再将图片输入给目标进行解析。

解决方案可以通过 C++ 直接去生成二维码,然后将二维码直接转成 cv::Mat 去作为输入。这样一方面可以节省掉 PNG 编解码的过程,另一方面也可以摆脱 Python 的依赖。QR-Code-generator 也许是一个不错的方案。不过考虑到之前画蛇添足的失败尝试,能带来多少速度的提升也不甚确定。

另外当前覆盖率反馈的变异其实是有限的(如果有的话),从文档中来看,LibFuzzer 对于自定义变异的支持可能会更完善一些。而且 LibFuzzer 可以天然支持多核,不用像 AFL 要自己手打一堆 slave。

Anyway,最终运行结果:

$ afl-whatsup -s output/
/usr/local/bin/afl-whatsup status check tool for afl-fuzz by Michal ZalewskiSummary stats
=============Fuzzers alive : 17Total run time : 32 days, 13 hoursTotal execs : 8 millions, 198 thousandsCumulative speed : 36 execs/secAverage speed : 2 execs/secPending items : 567 faves, 22476 totalPending per fuzzer : 33 faves, 1322 total (on average)Crashes saved : 0Hangs saved : 0
Cycles without finds : 0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0Time without finds : 35 seconds

meme

好吧,虽然 OpenCV “非常安全”,但是二维码解析库又不止这一个!于是又找了另外一个常用的解析库 ZXing 去进行测试,事实证明还是可以找出问题的!

  • heap-buffer-overflow in ZXing::DataMatrix::DMRegressionLine::modules #572

总结

本文可以看做是一个典型的 Fuzzing 漏洞挖掘心路历程。即先用 Generic Fuzzer 跑起来,然后通过不断深入理解代码,改良变异策略,最终变成 Structure Aware Fuzzing 的结果!虽然 AFL 一把梭能让机器为自己打工很爽,但是想得到更好的结果还是免不了动手优化。而不看代码的话可能即便找到问题也无法理解成因,轻则无法编写利用导致 award-0,重则提交错误的 patch 导致后续被其他开发者 revert 并批判一番钉在历史的耻辱柱上。因此,自动化测试和代码审计相结合往往才能达到更加有效的漏洞挖掘效果。

参考链接

  • AFL++ Document
  • How to create QRcode
  • QR code - Wikipedia

版权声明: 自由转载-非商用-非衍生-保持署名 (CC 4.0 BY-SA)
原文地址: https://evilpan.com/2023/06/10/fuzzing-qrcode/
微信订阅: 『有价值炮灰』
TO BE CONTINUED.


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

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

相关文章

服务器硬件测试 如何查看系统信息及测试使用工具

硬件长稳 一、查看硬件信息 sar&#xff08;sar命令&#xff0c;好一个大宝剑_7750783的技术博客_51CTO博客超全&#xff09; 使用 yum install sysstat 命令安装 #sar命令来对系统作一个了解&#xff0c;该命令是系统维护的重要工具&#xff0c;主要帮助我们掌握系统资源的使…

万字长文,为你送上全网最全Flutter学习资料!

话不多说直接上目录&#xff0c;干货较多内容很长&#xff0c;建议先收藏供以后慢慢查阅。 目录 文章视频组件导航模板插件框架实验性游戏开源App实用工具社区书籍福利 文章 介绍 Google IO 2018 [1.1K&#x1f44f;] - 构建美观&#xff0c;灵活的用户界面。Presentation …

百度工程师的软件质量与测试随笔

作者 | 百度移动生态质效工程师们 导读 在降本增效、以chatGPT为代表的大模型技术横空出世的背景下&#xff0c;对软件质量和软件测试的领域也带来了巨大冲击&#xff0c;也使得软件质量工作者开始变得焦虑&#xff0c;主要体现在&#xff1a;公司对软件质量从业者的不重视加剧…

首周下载量碾压ChatGPT!谷歌前员工创第二个Open AI?

来源 | 新智元 硅谷独角兽&#xff0c;又来震撼世界了&#xff01; 这支名叫Character Technologies的独角兽的核心力量&#xff0c;来自前谷歌LaMDA团队。 他们的新产品在移动端上线不到一周&#xff0c;下载量就达到了170万&#xff0c;直接碾压了ChatGPT&#xff01; 并且…

编译器大神 Chris Lattner 官宣新编程语言:Mojo,比 Python 快 35000 倍!

整理 | 王子彧 责编 | 张红月 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 说起 Chris Lattner&#xff0c;大家一定不陌生。这位编译器大神&#xff0c;曾经领导了众多大型技术项目。他不仅是 LLVM 项目的主要发起人&#xff0c;还是 Clang 编译器的创作…

走进社区客户端测试 | 得物技术

0.引言 社区 C 端 质量 体系建设思考&#xff1f; 询问 一下 ChatGPT 1.关于社区客户端 1.1 社区端上功能 1.2 客户端技术栈移动端应用可以分为三大类&#xff1a;Web 应用&#xff08;Web App&#xff09;、原生应用&#xff08;NativeApp&#xff09;、混合应用&#xff0…

Arm 确认对华禁售先进芯片设计产品;谷歌不准备推出 ChatGPT 类聊天产品;Bugzilla 宣布未来计划|极客头条...

「极客头条」—— 技术人员的新闻圈&#xff01; CSDN 的读者朋友们早上好哇&#xff0c;「极客头条」来啦&#xff0c;快来看今天都有哪些值得我们技术人关注的重要新闻吧。 整理 | 梦依丹 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 一分钟速览新闻点&#…

深度:行业拐点将至 “蔚来们”还有未来吗?

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 2023年以来&#xff0c;新能源汽车行业热点频出。 2023年2月&#xff0c;威马汽车身陷停工停产、资金紧张等舆论漩涡。据最新消息透露&#xff0c;威马此前融资的30亿已经到账&#xff0c;3月中旬会进行有序的复工复产&…

运行Trezor 钱包suite项目+firmware(包含onekey)项目

一些流程 文章目录 目录 文章目录 前言 一、trezor钱包 一、firmware的安装步骤 二、suite官方操作 步骤 1.git 1.5 以下才是核心&#xff0c;尝试运行onekey的firmware 需要在ubuntu的终端中进行nix操作 ​编辑 2.接着 三、代码分析 1.doc的分析 Repository Structure 总结 前…

FreeRTOS内核:详解Task各状态(GPT4帮写)

FreeRTOS内核&#xff1a;详解Task各状态&#xff08;GPT4帮写&#xff09; 1. 背景2. Task顶层状态区分3. 运行状态&#xff08;Running&#xff09;4. 非运行状态4.1 阻塞态&#xff08;Blocked&#xff09;&#xff1a;4.2 挂起态&#xff08;Suspended&#xff09;4.3 就绪…

FreeRTOS内核:详解Queue队列 FIFO(GPT4帮写)

FreeRTOS内核&#xff1a;详解队列管理FIFO 1. 背景2. Queue相关API2.1 xQueueCreate()&#xff1a;创建2.2 xQueueSend()&#xff1a;发送2.3 xQueueReceive()&#xff1a;接收2.4 vQueueDelete()&#xff1a;删除2.5 xQueuePeek() &#xff1a;不删除的方式从FIFO读数据&…

chatgpt-如今最流行的自动化测试框架是什么

Whats the best automation test framework? 什么是最好的自动化测试框架&#xff1f; The best automation test framework depends on your specific needs and skill level. Here are some commonly used automation test frameworks: 最好的自动化测试框架取决于您的具体…

你的自动化框架如何设计的?为什么感觉面试官总是不满意,到底问题出在哪?

前言 去面试自动化测试岗位&#xff0c;尤其是接口自动化岗位&#xff0c;面试官总会问&#xff1a;说下你的自动化框架如何设计的&#xff1f; 为什么回答后&#xff0c;面试官对你的框架设计总是感觉不满意&#xff1f; 自动化测试实现的几种方式 对于不同的公司来说&…

使用 ChatGPT 从头开始​​研究和构建 SwiftUI 应用程序

今天我试图从头开始构建一个应用程序,向 ChatGPT 征求意见。我想第一次尝试创建一个基于 IOS 16 的 100% SwiftUI 应用程序。 技术选择 我想在 SwiftUI 中创建一个应用程序,采用模块化、多语言的方法,采用 mvvm 架构并使用协调器。 所以我问我如何创建一个满足我要求的应…

大学生对chatGPT的认知和使用

新一代对话式人工智能chatGPT在全球范围狂揽1亿名用户&#xff0c;不止于科技界破圈&#xff0c;更成为街头巷尾的谈资。 chatGPT能干什么&#xff1f; https://openai.com/blog/chatgpt/ chatGPT官网 写解决方案编写代码 说笑话 个人体验&#xff1a; 实用性强&#xff0c;…

ChatGPT 成全球学生的“作弊神器”?韩国学生“喜”提 0 分……

整理 | 朱珂欣 出品 | CSDN程序人生&#xff08;ID&#xff1a;coder_life&#xff09; 仅仅横空出世 70 余天的 ChatGPT &#xff0c;已经累计用户超 1 亿&#xff0c;创下了互联网最快破亿应用记录。根据 Similarweb 的数据&#xff0c;截至今年 1 月&#xff0c;平均每天…

本科论文查重网站分享

本科论文查重网站分享 本科论文需要查重&#xff0c;很多人都知道。知网作为查重届的权威代表&#xff0c;翟天临博士却不知道知网是什么&#xff0c;于是学历造假最终被发现。本文主角不是翟“博士”&#xff0c;而是和知网一样有论文查重功能的两个免费查重网站——YY和百度…

分享一个靠谱的免费论文查重网站

给大家分享一个靠谱的免费论文查重网站PaperPP&#xff1a;http://www.paperpp.com&#xff0c;可以一站解决“论文查重、改重、降重”等问题TOC 欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑…

推荐几个免费论文查重网站

文章目录 一、paperyy二、 paperday三、 paperpass四、 paperfree五、 paperok六、 papertime 一、paperyy https://www.paperyy.com/ 二、 paperday https://www.paperday.cn/ 三、 paperpass https://www.paperpass.com/ 四、 paperfree https://www.paperfree.cn/…

股票证券行业,怎样快速获客,有效途径有哪些

证券行业最开始起源于荷兰&#xff0c;全世界第一家股票交易所便是荷兰的阿姆斯特丹交易所&#xff0c;这个交易中心确立了荷兰海上霸主的影响力。证券市场的主体作用便是处理企业融资难题。汇总起來&#xff0c;证券行业要把证券化&#xff0c;都切分优化制成标准物质&#xf…