C/C++逆向:函数逆向分析-总体流程(整型指针)

news2024/11/25 12:32:47

函数的初始化

在逆向工程中,函数的初始化操作是函数在开始执行时,为正确运行而进行的准备工作。通常,这些操作发生在函数的序言(Prologue)阶段,具体的内容和顺序会因编译器、调用约定和目标平台(如x64x86)不同而有所差异。函数的序言(Prologue)阶段标志着函数的开始,并且包含了设置栈帧、保存上下文等关键操作。尽管在不同的体系结构和编译器优化情况下可能会有所变化,但总体上有一些通用的方式可以帮助识别它。下面分别介绍x86和x64架构下的典型序言。

序言阶段的典型操作:
①保存调用者的栈帧:通过push ebp将调用者的栈帧基址寄存器ebp压入栈中。
②建立新的栈帧:通过mov ebp, esp将当前的栈指针esp赋值给栈帧基址寄存器ebp,从而在新函数中创建一个新的栈帧。
③开辟栈空间:通过sub esp, X来减少栈指针esp的值,从而为局部变量或保存的数据开辟空间。X代表所需栈空间的大小。
④保存失性寄存器:非易失性寄存器如ebx、esi、edi等通常也在序言阶段被保存到栈上(如push ebx等操作)。
1. 标准序言的结构

在大多数情况下,函数的序言遵循一定的结构模式,特别是在有栈帧的情况下。下面分别介绍x86x64架构下的典型序言。

x86架构中的序言

x86下,使用基址指针寄存器(ebp)和栈指针寄存器(esp)来管理栈帧。典型的函数序言包括以下几个步骤:

push    ebp          ; 保存前一个栈帧的基址
mov     ebp, esp     ; 设置新的栈帧基址
sub     esp, 0xXX    ; 为局部变量分配空间

push ebp:将调用者的栈帧基址保存到栈中,以便函数返回时可以恢复。

mov ebp, esp:将当前的栈指针(esp)复制到基址指针(ebp)中,建立新的栈帧。

sub esp, XX:为函数的局部变量在栈上分配空间,XX是分配的字节数,可能会因函数复杂度而异。

esp和ebp是用于栈操作的两个重要寄存器,esp指向栈顶,栈中的每一次压栈(push)和弹栈(pop)操作都会通过esp来进行;ebp通常被用作栈帧基址寄存器,指向函数调用时栈帧的起始位置,也就是函数进入时的栈顶位置。
x64架构中的序言

类似于x86,但x64中的寄存器名称不同,例如使用rbprsp

push    rbp          ; 保存调用者的栈帧基址
mov     rbp, rsp     ; 设置当前函数的栈帧基址
sub     rsp, 0xXX    ; 为局部变量分配空间
2.保存非易失性寄存器

在某些调用约定(如stdcallfastcall)下,函数在进入时需要保存非易失性寄存器,以便在函数结束时能够恢复调用者的上下文。非易失性寄存器(Callee-saved registers)是指在函数调用过程中,如果被调用的函数修改了这些寄存器,它需要在函数结束时将其恢复到原来的状态。这样可以保证调用者在调用函数后,这些寄存器的值不会被破坏。

x86保存寄存器的典型序言
push    ebx
push    esi
push    edi
x64保存寄存器的典型序言
push    rbx
push    r12
push    r13

这些保存寄存器的指令通常出现在函数的序言阶段,他们是函数初始化的一部分,标志着对调用者上下文的保护。接着我们通过一个实际例子来进行具体分析。下面是一个 C 代码的示例:

#include <stdio.h>
​
// 传递多个整型参数
int sumIntegers(int a, int b, int c, int d, int e) {
    return a + b + c + d + e;
}
​
// 传递多个整型指针
void modifyIntegers(int *p1, int *p2, int *p3, int *p4, int *p5) {
    *p1 = 10;
    *p2 = 20;
    *p3 = 30;
    *p4 = 40;
    *p5 = 50;
}
​
int main() {
    int x = 1, y = 2, z = 3, w = 4, v = 5;
​
    int sum = sumIntegers(x, y, z, w, v);
    printf("Sum: %d\n", sum);
​
    modifyIntegers(&x, &y, &z, &w, &v);
    printf("Modified values: %d, %d, %d, %d, %d\n", x, y, z, w, v);
​
    return 0;
}

使用VS对上述代码进行编译,生成x86x64架构的exe程序。接着将这两个程序放入x96dbg中进行分析:

①x86架构

将x86架构程序载入x32dbg中

定位main函数,得到如下代码:

在图中红色方框中的代码就是main函数的序言部分:

push ebp
mov ebp,esp
sub esp,10C
push ebx
push esi
push edi

push ebp:保存调用者的栈帧基址,将调用者的ebp寄存器的值压入栈中,目的是在函数返回时恢复调用者的栈帧。

mov ebp, esp:设置当前函数的栈帧基址,将当前栈指针esp的值赋给ebp,这意味着ebp现在是当前函数的栈帧基址,用于访问局部变量和参数。

sub esp, 10C:为局部变量分配栈空间,减少栈指针esp的值,开辟0x10C(即268字节)的栈空间。这部分空间通常用于存储局部变量或其他需要在栈上分配的临时数据。

后面的三个指令则是对非易失性寄存器(ebxesiedi)进行保存。

再接下去就是初始化局部变量区域:通过rep stosd指令将0xCCCCCCCC填充到局部变量区域。这通常用于调试时帮助检测未初始化的变量或栈溢出。

lea     edi, [ebp-10C]  ; 获取局部变量空间的起始地址并存入edi
mov     ecx, 43         ; 设置计数器,表示要写入的次数为67(43h)
mov     eax, CCCCCCCC   ; 设置要写入的值为0xCCCCCCCC
rep     stosd           ; 将eax的值(0xCCCCCCCC)重复写入到edi指向的内存区域

lea edi, [ebp-10C]lea指令(Load Effective Address)将[ebp-10C](局部变量空间的起始地址)加载到edi寄存器中。此时edi指向局部变量的起始位置。

mov ecx, 43:将0x43(67)存入ecx,作为rep stosd指令的计数器,表示接下来要重复执行stosd指令67次。

mov eax, CCCCCCCC:将0xCCCCCCCC存入eax寄存器,这个值将被写入到栈空间中。0xCCCCCCCC在调试环境中通常用于填充未初始化的内存或局部变量,用于帮助识别未使用或非法使用的内存区域。

rep stosdrep是一个前缀,用于重复执行后面的stosd指令,直到ecx为0。stosdeax中的值(0xCCCCCCCC)写入edi指向的内存地址,每次写入4字节,ecx自动减1。因为ecx初始值为67,所以这个操作将0xCCCCCCCC写入[ebp-10C][ebp-4]的内存区域(268字节)。

再往后的代码中则表示函数执行过程中堆栈保护的设置,以及准备进行一次函数调用的过程。

mov eax,dword ptr ds:[<___security_cookie>]
xor eax,ebp
mov dword ptr ss:[ebp-4],eax

mov eax, dword ptr ds:[<___security_cookie>]:

作用:从数据段(ds)中的全局变量<___security_cookie>读取一个值到eax寄存器中,___security_cookie 是堆栈保护机制中常用的安全Cookie。这个值通常是一个随机生成的值,用于检测栈溢出攻击。在程序启动时,___security_cookie 会被初始化为一个随机数。它在函数开始和结束时被用来验证栈的完整性,确保没有被非法修改。

xor eax, ebp:将安全Cookieebp寄存器中的值进行异或操作(XOR),结果保存在eax中。ebp是当前函数的栈帧基址。通过异或ebp,它使得安全Cookie与当前函数的栈帧相关联,从而进一步加强了堆栈保护机制。

mov dword ptr ss:[ebp-4], eax:将eax(即异或后的安全Cookie值)存储到栈帧中[ebp-4]的位置。

后面的两行代码也则是在进行各种函数初始化时的检查,因为涉及到跳转,这边就不做过多赘述了。

再往下运行的这部分代码就开始进行参数传递,这些指令将数值压入栈中后进行函数调用,且因为在函数调用后可以看到有平栈的操作,所以基本上可以确定该函数的调用约定为cdecl

在函数的所有参数压入栈中后,此时ESPEBP寄存器的指向如下:

ESP指向栈顶,且ESP指向的地址值比EBP来的小,所以在参数压栈的过程中地址实际上是减少的。接着当我们进入函数后,程序会自动将函数的返回地址压入栈中;

此时栈中的情况为

进入函数后程序做的第一件事就是将原来的ebp压入栈中,用于保存调用者栈空间。

此时栈中的内容为:

紧接着就是mov ebp,esp将栈顶寄存器的值赋给基址寄存器,然后sub esp,C0开辟新的栈帧空间。

此时栈中的内容如下:

后续的代码就是在进行函数的相关初始化,与上述main函数的初始化相同,这边就不再赘述了。接着我们来说一下在函数内的参数引用,也就是函数如何使用栈中的参数值:

mov eax,dword ptr ss:[ebp+8]
add eax,dword ptr ss:[ebp+C]
add eax,dword ptr ss:[ebp+10]
add eax,dword ptr ss:[ebp+14]
add eax,dword ptr ss:[ebp+18]

这个时候dword ptr ss:[ebp+8]实际上就表示栈中的第一个参数,dword ptr ss:[ebp+C]就是第二个参数以此类推。

最后得到5个参数的和放在eax寄存器中。

再往后就是各种还原操作如还原非易失性寄存器、还原开辟的栈帧空间、还原ESPESP原来的位置等。

最后ret,根据栈中的返回地址回到原本的执行地址中。

由于是cdecl调用约定,所以需要进行平栈操作(add esp,14这边的14为16进制,转化为十进制为20,在32位架构中正好就是代码中5个参数占用的空间),至此函数执行完毕。

参数传递-指针

上面的例子是所有参数传递方式为整数的情况,那么如果传入的参数为指针呢?我们就接着看下面的代码。

可以看到如果传入的参数为指针的情况下,这个时候压入栈中的就是参数的地址,这边使用lea命令进行取地址,接着我们进入函数中进行查看;

可以看到此时函数中的初始化操作和还原操作都是一样的,这里我们来关注一下不同点:

mov eax,dword ptr ss:[ebp+8]
mov dword ptr ds:[eax],A
mov eax,dword ptr ss:[ebp+C]
mov dword ptr ds:[eax],14
mov eax,dword ptr ss:[ebp+10]
mov dword ptr ds:[eax],1E
mov eax,dword ptr ss:[ebp+14]
mov dword ptr ds:[eax],28
mov eax,dword ptr ss:[ebp+18]
mov dword ptr ds:[eax],32

mov eax, dword ptr ss:[ebp+8] 将第一个参数的值(即一个指针)从栈中加载到 eax 寄存器。

mov dword ptr ds:[eax], A将常量 A(十六进制数,等于 10)存储到 eax 指向的地址。这意味着将值 10 存入该指针指向的内存位置。

后面的情况也是类似的,总结一下该代码实际上就是根据传入的指针参数,将一系列特定值(10、20、30、40、50)写入这些指针所指向的内存地址。

执行完毕后也是一样根据栈中的返回地址回到原来的指令执行地址处,进行平栈操作。

至此函数执行完毕。后续的printf函数的调用这边就不做过多赘述了,如果对这个比较不熟悉的话,可以看看笔者前面的文章。

至此x86架构的函数执行的全部过程刨析完了,接着我们来看一下x64架构中函数执行的全部过程。

②x64架构

首先我们将x64架构的程序载入x64dbg中进行分析调试。

紧接着定位到main函数得到如下代码:

我们先来看一下

push rbp
push rdi
sub rsp,1B8
lea rbp,qword ptr ss:[rsp+30]  ;与x86不同,x64将rbp指向了rsp+30的地址
mov rdi,rsp
mov ecx,6E
mov eax,CCCCCCCC
rep stosd 

这段代码可以被视为一个序言阶段(prologue)。具体来说,这段代码所做的事情符合序言的特征:

①保存寄存器值:push rbppush rdi 保存了旧的基址指针和 rdi 寄存器的值。

②为局部变量分配空间:sub rsp, 1B8 在栈上分配了 440 字节的空间用于局部变量和临时数据。

③设置栈帧基址:lea rbp, [rsp+30]rbp 设置了新的基址,便于函数访问栈中的局部变量。

④初始化内存:通过 mov eax, CCCCCCCCrep stosd 初始化栈上分配的内存空间。这在某些情况下是为了确保局部变量的内存被预先设置,避免使用未初始化的数据。

再往下与x86架构程序一样是做一个堆栈保护的设置,以及函数运行的各种检查操作:

接着往下就是正式开始进行函数的参数传递

初始化参数值
mov dword ptr ss:[rbp+4], 1
mov dword ptr ss:[rbp+24], 2
mov dword ptr ss:[rbp+44], 3
mov dword ptr ss:[rbp+64], 4
mov dword ptr ss:[rbp+84], 5

这些指令将常量值 12345 存入栈中相对于 rbp 的不同偏移位置(rbp+4rbp+24 等)。这些地址似乎是用于存储函数的局部变量或传入参数的。

准备参数进行函数调用
mov eax, dword ptr ss:[rbp+84]
mov dword ptr ss:[rsp+20], eax

mov eax, [rbp+84]rbp+84 处的值(即之前存储的 5)加载到 eax 寄存器。

然后,mov [rsp+20], eaxeax 的值(5)存入栈中 rsp+20 处。这是通过栈传递的参数之一,通常用于超过寄存器限制的额外参数。

此时栈内的内容为:

接着通过寄存器传递参数

在 x64 调用约定(Windows 下的 Microsoft x64 Calling ConventionSysV ABI)中,前四个整数参数是通过寄存器 rcxrdxr8r9 传递的:

mov r9d, dword ptr ss:[rbp+64]
mov r8d, dword ptr ss:[rbp+44]
mov edx, dword ptr ss:[rbp+24]
mov ecx, dword ptr ss:[rbp+4]

r9d 被赋值为 [rbp+64] 处的值(即之前存储的 4)。

r8d 被赋值为 [rbp+44] 处的值(即之前存储的 3)。

edx 被赋值为 [rbp+24] 处的值(即之前存储的 2)。

ecx 被赋值为 [rbp+4] 处的值(即之前存储的 1)。

这些指令表明,函数 parameterpass-x64.7FF6E34C139D 将通过寄存器传递这四个参数,接着调用函数

call parameterpass-x64.7FF6E34C139D

接着我们进入函数中进行查看,因为此时我们进入了函数中,所以这个时候需要将函数的返回地址压入栈中,此时栈中的内容为:

进入函数后,直接将四个寄存器中的参数值压入栈中(可以看到x64架构的程序虽然是使用寄存器进行参数传递,但是在函数中还是需要将这些寄存器中的值压入栈中)

此时栈中的内容如下:

接着就是序言阶段,这边就不做过多赘述了。

后续就是通过ebp去取出刚刚压入栈中的几个数据进行相加运算,并把最后的和放在rax寄存器中;

具体的ebp+E8这些值可以直接右击地址,然后转到内存中进行查看即可,实际上就是刚刚压入栈中的值:

最后就是函数执行完毕后执行的还原操作即还原非易失性寄存器(rdi)中的值,以及rsp和rbp的值。

最后ret返回,至此函数运行结束。

后续做的printf操作这边就不做过多赘述,具体可以根据动态调试时窗口的变化进行判断。

参数传递-指针

接着我们来看一下关于x64架构的指针参数传递

事实上指针的传递过程与整数差不多,是将最后一个指针指向的先放入rsp+20处,接着将其他的,其他的指针参数从左往右依次放入r9-rcx中,接着调用函数。由于调用了函数,程序自动向栈中压入的函数的返回地址,原来的rsp+20处此时就是rsp+28,接着再将r9-rcx存放中的地址值分别压入rsp20-rsp+8中。

接着就是做初始化与检查:

接着就是通过如下形式,提取处栈中的地址,对地址上的值重新赋值:

mov rax,qword ptr ss:[rbp+E0] ;从栈上 rbp+E0 的位置读取一个 64 位的值(假设是一个指针),并将其存入 rax 寄存器。
mov dword ptr ds:[rax],A    ;将常量 A(十六进制,等于 10)存储到 rax 指向的内存地址中。意味着将值 10 写入该指针所指的内存位置。
mov rax,qword ptr ss:[rbp+E8]
mov dword ptr ds:[rax],14
mov rax,qword ptr ss:[rbp+F0]
mov dword ptr ds:[rax],1E
mov rax,qword ptr ss:[rbp+F8]
mov dword ptr ds:[rax],28
mov rax,qword ptr ss:[rbp+100]
mov dword ptr ds:[rax],32

这段代码从栈中读取几个指针,并向这些指针所指的内存位置分别写入值 10, 20, 30, 40, 和 50。函数执行的结果直接就体现在指针指向的地址中了。

接着将各种栈指针、寄存器恢复到调用之前的状态,同上。

最后ret返回到原来的执行地址上,函数结束。

在本文中,我们深入探讨了函数逆向工程的整体流程,通过对函数的结构、调用约定及其参数传递方式的详细分析,能够有效地识别和理解目标程序的行为,为后续的漏洞分析和安全研究奠定基础。

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

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

相关文章

【AIGC】ChatGPT提示词Prompt高效编写模式:思维链、Self-Consistency CoT与Zero-Shot CoT

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;思维链 (Chain of Thought, CoT)如何工作应用实例优势结论 &#x1f4af;一致性思维链 (Self-Consistency CoT)如何工作应用实例优势结论 &#x1f4af;零样本思维链 (Ze…

MC802单片机:触控未来,8位高性能与多IO接口的完美结合

MC802单片机&#xff1a;开启智能生活新篇章 MC802 &#xff08;2 Touch Key 4 I/O&#xff09; MC802是由厦门晶尊微电子科技有限公司&#xff08;ICman&#xff09;推出的一款高性能8位单片机&#xff0c;它集成了2个自校正容性触摸按键和4个I/O口&#xff0c;专为需要多…

地级市-制造业集聚水平数据(2008-2021年)

制造业集聚水平是衡量一个地区制造业发展程度的重要指标&#xff0c;它不仅反映了制造业在地理上的集中程度&#xff0c;还体现了该地区制造业的专业化水平。 制造业集聚水平通常通过以下几个量化指标来衡量&#xff1a; 年末单位从业人员数&#xff1a;反映了制造业的劳动力…

如何替换OCP节点(二):使用 antman脚本 | OceanBase应用实践

前言&#xff1a; OceanBase Cloud Platform&#xff08;简称OCP&#xff09;&#xff0c;是 OceanBase数据库的专属企业级数据库管理平台。 在实际生产环境中&#xff0c;OCP的安装通常是第一步&#xff0c;先搭建OCP平台&#xff0c;进而依赖OCP来创建、管理和监控我们的生…

02_安装jmeter

windows&#xff1a; 安装jdk1.8.0&#xff1a; 1、下载安装包&#xff0c;双击运行安装&#xff0c;点击“下一步”直到完成 2、配置环境变量&#xff1a; JAVA_HOME的值配置为jdk安装目录如D:\java\jdk1.8.0_201 系统变量的Path中添加"%JAVA_HOME%\bin" 3、验证安装…

海外市场充电桩需求激增:充电基础设施展望

报告显示&#xff0c;在大多数欧盟国家的路网中&#xff0c;充电桩数量存在不足、不支持快速充电且分布不均匀的问题。具体而言&#xff0c;有6个欧洲国家的平均每百公里充电桩数量不足1个&#xff0c;17个国家的平均每百公里充电桩数量少于5个&#xff0c;仅有5个国家的平均每…

【Axure原型分享】标签管理列表

今天和大家分享通过标签管理列表的原型模板&#xff0c;包括增删改查搜索筛选排序分页翻页等效果&#xff0c;这个模板是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;初始数据我们只要在中继器表格里填写即可&#xff0c;具体效果可以观看下方视频或者打开预览地址…

单片机(学习)2024.10.11

目录 按键 按键原理 按键消抖 1.延时消抖 2.抬手检测 通信 1.通信是什么 2.电平信号和差分信号 3.通信的分类 (1)时钟信号划分 同步通信 异步通信 (2)通信方式划分 串行通信 并行通信 (3)通信方向划分 单工 半双工 全双工 4.USART和UART&#xff08;串口通信&a…

selenium工具的几种截屏方法介绍(9)

在使用selenium做自动化的时候&#xff0c;可以对于某些场景截图保存当时的执行情况&#xff0c;方便后续定位问题或者作为一些证据保留现场。 获取元素后将元素截屏 我们获取元素后&#xff0c;使用函数screenshot将元素截屏&#xff0c;参数filename传入完整的png文件名路径…

最近 3 个 yyds 的开源项目!

01 电脑屏幕、麦克风记录工具 ScreenPipe 是一个开源的全天候本地屏幕与麦克风记录工具&#xff0c;为 AI 应用程序提供全方位上下文数据的支持。 该项目旨在成为 Rewind.ai 的替代方案&#xff0c;支持 Windows、Linux 和 macOS 等多平台应用&#xff0c;并且使用 Rust 语言构…

学习Ultralytics(获取yolov8自带的数据集并开始训练)

今天小编带大家学习一下YOLOv8 配置文件&#xff0c;用来定义不同数据集的参数和配置。这些文件包含了关于每个数据集的路径、类别数、类别标签等信息&#xff0c;帮助模型正确地加载和解析数据集&#xff0c;以便进行训练和推理。 具体来说&#xff0c;这些 YAML 文件的作用如…

AIGC时代的程序员生存法则:如何在AI辅助编程工具普及的背景下保持并提升核心竞争力

随着AIGC&#xff08;AI-Generated Content&#xff0c;如ChatGPT、MidJourney、Claude等&#xff09;技术的迅猛发展&#xff0c;特别是大型语言模型的不断涌现&#xff0c;程序员的工作方式正发生深刻变革。AI辅助编程工具的普及给编程行业带来了前所未有的挑战和机遇。一方面…

SwiftUI 6.0(iOS 18)将 Sections 也考虑进自定义容器子视图布局(上)

概述 在 WWDC 24 新推出的 SwiftUI 6.0 中,苹果对于容器内部子视图的布局有了更深入的支持。为了能够未雨绸缪满足实际 App 中所有可能的情况,我们还可以再接再厉,将 Sections 的支持也考虑进去。 SwiftUI 6.0 对容器子视图布局的增强支持可以认为是一个小巧的容器自定义布…

Wordpress—一个神奇的个人博客搭建框架

wordpress简介 在当今数字化的时代&#xff0c;拥有一个属于自己的个人博客&#xff0c;不仅可以记录生活点滴、分享专业知识&#xff0c;还能展示个人风采。而在众多的博客搭建框架中&#xff0c;Wordpress 以其强大的功能和灵活性脱颖而出。今天&#xff0c;就让我们一起深入…

spring boot项目日志怎么加?

使用源码LoggerFactory&#xff08;日志工厂类&#xff09; 使用方法&#xff1a;getlogger()中间传入1个类 加在过滤里所以需要传入的是过滤这个类&#xff08;reqfilter.class) 用这个对象调info方法 logger.error是打印错误信息 logger.debug打印debug 结果会增加时间名称等…

LQB焊接超声波部分原理图和焊接说明(勘误)

1、自制的板子的原理图&#xff0c;有一个错误的地方&#xff0c;导致超声波不能正常使用。 下图是实物的原理图存在错误&#xff0c;不小心&#xff0c;自我批评一下。 图中的C6电容330pF的一端接到了VCC&#xff0c;是错误的。 蓝桥杯的原理图是下图&#xff0c;接到GND 因…

【机器学习(十三)】机器学习回归案例之股票价格预测分析—Sentosa_DSML社区版

文章目录 一、背景描述二、Python代码和Sentosa_DSML社区版算法实现对比(一) 数据读入(二) 特征工程(三) 样本分区(四) 模型训练和评估(五) 模型可视化 三、总结 一、背景描述 股票价格是一种不稳定的时间序列,受多种因素的影响。影响股市的外部因素很多,主要有经济因素、政治因…

51单片机数码管循环显示0~f

原理图&#xff1a; #include <reg52.h>sbit dulaP2^6;//段选信号 sbit welaP2^7;//位选信号unsigned char num;//数码管显示的数字0~funsigned char code table[]{ 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71};//定义数码管显…

CDN服务支持多种应用场景,包括图片、大文件下载、流媒体等

中国联通国际公司产品之 CDN&#xff08;内容分发网络&#xff09; 在当今这个信息爆炸的时代&#xff0c;内容分发网络&#xff08;CDN&#xff09;已成为提升用户体验和保障数据快速传输的重要工具。中国联通国际公司凭借其全球领先的通信技术和广泛的网络覆盖&#xff0c;推…

Qualitor checkAcesso.php 任意文件上传漏洞复现(CVE-2024-44849)

0x01 漏洞概述 Qualitor 8.24及之前版本存在任意文件上传漏洞,未经身份验证远程攻击者可利用该漏洞代码执行,写入WebShell,进一步控制服务器权限。 0x02 复现环境 FOFA:app="Qualitor-Web" 0x03 漏洞复现 PoC POST /html/ad/adfilestorage/request/checkAcess…