西工大网络空间安全学院计算机系统基础实验二(清楚实验框架及phase_1)

首先,将自己的实验包从Windows系统中使用scp命令传到Linux虚拟机中。而要想传到Linux虚拟机中,第一步就是要确定Linux虚拟机的IP地址,如 图1:确定Linux虚拟机的IP地址 所示。接着使用scp命令将实验包从Windows系统传送到Linux虚拟机中,如 图2:用scp命令将实验包从Windows系统传送到Linux虚拟机中 所示。接着在Linux虚拟机中,使用"tar xvf 你的实验包的名字"命令将压缩实验包解压缩,如 图3:使用"tar xvf 你的实验包的名字"命令将压缩实验包解压缩 所示。

图1:确定Linux虚拟机的IP地址) 

图2:用scp命令将实验包从Windows系统传送到Linux虚拟机中

 

图3:使用"tar xvf 你的实验包的名字"命令将压缩实验包解压缩) 

        此时若查看README文件,会发现我们得不到任何有用的信息,即使去查看bomb.c文件中的几行注释,也得不到有价值的信息。那么我们该怎么办呢?不要慌张,根据西工大无论是计院还是网安院开设的计基实验课的源文件的出处,我们去这些源文件的源头,CSAPP的官网上查看到了关于这次实验的介绍,它的大致内容如 图4:了解第二次实验的大致内容 所示。当看到很多很多的专有名词时,不要害怕,有个印象就可以,后面会慢慢细讲。此时既然我们手头有了bomb.c的源文件,不妨先去分析分析,看看能得到什么有用的东西。对bomb.c文件的讲解如 图5:bomb.c文件前28行的内容,图6:bomb.c文件中第28行到第52行的内容,图7: bomb.c文件中第53行到第77行的内容,图8:bomb.c文件中第78行及其之后的内容 所示。

        二进制炸弹是由一系列阶段组成的程序。每个阶段都希望您在stdin上键入一个特定的字符串。如果你键入了正确的字符串,那么这个阶段就会被解除引信,炸弹就会进入下一个阶段。否则,炸弹会通过打印“BOOM!!”然后终止而爆炸。当每一阶段都拆除炸弹之后,炸弹最终即会被自动拆除。

        每次你的炸弹爆炸时,它都会通知实验室的服务器,你会在最终分数中损失1/2分(最多20分的分数)。所以引爆炸弹会有后果。你一定要小心!

        有许多工具旨在帮助您了解程序是如何工作的,以及当他们不工作的时候哪里出了问题。以下是一些你可能会发现对分析炸弹有用的工具的列表,以及关于如何使用它们的提示:

        1. gdb,是一个几乎在每个平台上都可用的命令行调试器工具。你可以逐行跟踪程序,检查内存和寄存器,查看这两个源代码和汇编代码(我们不会为您提供大部分炸弹的源代码),设置断点,设置内存监视点,并编写脚本。

        2. objdump -t,这将打印出炸弹的符号表。符号表包括所有函数的名称和炸弹中的全局变量,炸弹调用的所有函数的名称及其地址。你通过查看函数名称可以学到一些东西!

        3. objdump -d,用这个来分解炸弹中的所有代码。您也可以只查看单个函数。阅读汇编代码可以告诉你炸弹是如何工作的。

        4. strings,此实用程序将显示炸弹中的可打印字符串。

图4:了解第二次实验的大致内容

 

图5:bomb.c文件前28行的内容。 第1行到第21行都是注释,讲的都是不重要的内容;第23行到第26行使用了4个include语句,声明了4份头文件stdio.h,stdlib.h,support.h,phases.h。)

图6:bomb.c文件中第28行到第52行的内容。 第28行到第32行是注释,讲的内容都不重要;第34行表示定义一个全局性的文件指针infile;第36行表示定义main函数,这里main函数有两个参数,第一个参数是int型的变量argc,第二个参数是指针数组argv,这个argv数组中的每一个元素都是一个指向字符串的指针。对于事先已由该bomb.c文件编译出的bomb文件而言,如果在命令行中使用"./bomb"命令执行bomb可执行文件,那么argc的值为1,表示argv指针数组中只有一个元素,argv[0]的值指向"bomb"这个字符串。如果在命令行中使用"./bomb 1.txt 2.txt 3.txt"命令执行bomb可执行文件,那么argc的值为4,表示argv指针数组中有4个元素,argv[0]的值指向"bomb"这个字符串,argv[1]的值指向"1.txt"这个字符串,argv[2]的值指向"2.txt"这个字符串,argv[3]的值指向"3.txt"这个字符串;回过头,第38行表示定义一个字符串指针input;第45、46行表示,如果argc的值为1,也就是在命令行窗口执行bomb文件时使用的命令是"./bomb",那么infile这个全局性的文件指针指向stdin这个文件,也就是指向标准输入文件。在这里我们在键盘上按下的字符都首先被存放在了stdin这个文件里。)

图7: bomb.c文件中第53行到第77行的内容。第53到第57行表示,如果argc的值为2,也就是说,在命令行窗口中执行bomb.c可执行文件的命令是"./bomb 1.txt",那么看第54行,如果再以只读的方式打开argv[1]这个字符串指针所指的文件的时候打不开,也就是fopen不给infile返回一个指向文件的指针,那么infile就为空指针,那么就会提示你"... Error: Couldn't open... ",接着退出bomb文件的执行;第61行表示,如果在Linux虚拟机中执行bomb可执行文件的命令是"./bomb 1.txt 2.txt"甚至"./bomb 1.txt 2.txt 3.txt",那么bomb可执行文件不知道该使用哪个文件作为答案,只好输出"Usage: %s [<input_file>]"来委婉提示你,然后便会退出;接着第67行初始化炸弹,这一点不重要;第69、70行输出两句话,也不重要;第73行,"input = read_line();",表示从infile这个文件指针所指的文件中,读取1行到input这个字符串指针所指的字符串当中,如果我们在命令行中执行bomb可执行文件的命令是"./bomb",那么infile这个文件指针指向的就是stdin这个标准输入文件,也就是我们从键盘中的输入;第74行表示将input这个字符串指针所指的字符串交由phase_1函数处理,如果通过的话,会保持沉默并且默认你通过执行第75行;第75行的作用暂时还不清楚,但是并不重要;第77行输出一段恭喜你的话,表示你已经成功破解了第一阶段的炸弹。)

图8:bomb.c文件中第78行及其之后的内容。 后面的几个阶段就是重复第一阶段的内容。)

事已至此,我们无法再从bomb.c文件中得到更多的消息。那么怎么才能破解这个炸弹呢?注意,虽然bomb.c文件已经无法告知我们更多的信息,但是bomb这个可执行文件就不一定了,但是bomb这个可执行文件用记事本打开的结果是一堆乱码,我们怎么能获取更多的消息呢?我们不妨对bomb文件进行反汇编处理,以得到控制生成bomb可执行文件的汇编代码,并期待从汇编代码中获取一些信息,如 图9:对bomb文件进行反汇编处理,以得到控制生成bomb可执行文件的汇编代码 所示。接着双击bomb.s文件以查看bomb.s文件的内容,如 图10:双击bomb.s文件以查看bomb.s文件的内容 所示。

图9:对bomb文件进行反汇编处理,以得到控制生成bomb可执行文件的汇编代码。使用"objdump -d bomb > bomb.s"命令获取控制生成bomb可执行文件的汇编代码,并将其保存在bomb.s文件当中) 

图10:双击bomb.s文件以查看bomb.s文件的内容

 然后按下"Ctrl+F"快捷键,打开搜索栏,如 图11:按下按下"Ctrl+F"快捷键以打开搜索栏 所示。这个搜索栏的颜色有点浅,不太好找,所以要仔细点。

图11:按下按下"Ctrl+F"快捷键以打开搜索栏

接着在搜索栏中输入"phase_1",如 图12:在搜索栏中输入"phase_1" 所示。接着往下翻,定位到到真正的phase_1函数部分,如 图13:往下翻定位到到真正的phase_1函数部分 所示。在这里注意一点,bomb.c文件中的函数名,诸如main,phase_1,phase_2,phase_3... ...等,是与通过objdump命令反汇编生成的文件中的函数名是严格的相同的!!!

图12:在搜索栏中输入"phase_1"

 

 (图13:往下翻定位到到真正的phase_1函数部分

        为了帮助我们更好的理解函数phase_1的汇编代码,我们选择通过使用gdb这款工具,动态的执行函数phase_1,看看这些汇编代码真实的效果是什么样子的。毕竟有句古话,叫作“是骡子是马拉出来遛遛”。首先,在命令行窗口中使用命令"gdb ./bomb"将bomb这个可执行文件作为gdb处理的对象,如 图14:在命令行窗口中使用命令"gdb ./bomb"将bomb这个可执行文件作为gdb处理的对象 所示。下一步就是打断点,在哪里打断点呢?在判断炸弹是否爆炸的时候打断点,这样子无论我们给出的答案字符串是否正确,我们都能够让程序停留在判断炸弹是否爆炸上,进而通过一些分析得出该怎么做,才能不让炸弹爆炸。那么结合这道题,我们选择在phase_1这个函数处打断点,命令是"b phase_1",如 图15:在phase_1这个函数处打断点,命令是"b phase_1" 所示。为了查看我们所打下的断点,使用"info b"命令,查看我们已经打下的断点,如 图16:为了查看我们所打下的断点,使用"info b"命令,查看我们已经打下的断点 所示。接着就应该运行bomb这个可执行文件了,使用命令"r"开始执行bomb这个可执行文件,如 图17:使用命令"r"开始执行bomb这个可执行文件 所示。

图14:在命令行窗口中使用命令"gdb ./bomb"将bomb这个可执行文件作为gdb处理的对象

图15:在phase_1这个函数处打断点,命令是"b phase_1"

图16:为了查看我们所打下的断点,使用"info b"命令,查看我们已经打下的断点

图17:使用命令"r"开始执行bomb这个可执行文件

这时,我们开始犹豫了。为什么会让我们现在就输入答案字符串呢?我们现在对于答案字符串一无所知,现在让我们输入答案,不是让我们送命嘛?但是不要害怕不要胆怯,只要能够保证断点打在了phase_1函数上,输入答案之后,程序就能够停留在判断炸弹是否爆炸这一步上。重要的事情强调三遍:

只要能够保证断点打在了phase_1函数上,输入答案之后,程序就能够停留在判断炸弹是否爆炸这一步上。

只要能够保证断点打在了phase_1函数上,输入答案之后,程序就能够停留在判断炸弹是否爆炸这一步上。

只要能够保证断点打在了phase_1函数上,输入答案之后,程序就能够停留在判断炸弹是否爆炸这一步上。

图18:因为断点打在了phase_1函数上,所以随便输入字符串"111"做试验 所示。

此时观察命令行给出了很多很多的反馈,一时间看不过来,甚至有些焦虑。但是不要害怕,耐下性子一步一步来。我们先看第一栏,如 图19:观察第一栏REGISTERS 所示。

图18:因为断点打在了phase_1函数上,所以随便输入字符串"111"做试验

图19:观察第一栏REGISTERS

第一栏REGISTERS表示寄存器的值,如 图19 中黄色框内的内容所示,其中我们一般重点关注的内容只有%eax、%ebx、%esp这三个寄存器。下面我们来分别讲解每一行的内容:寄存器%eax的值为0x5655b760,后面括号内的input_strings表示寄存器%eax的值是一个地址,并且是我们从键盘输入的字符串的地址,最后面的0x313131 /* '111' */就表示这个字符串是'111',那么0x313131又是怎么回事呢?原来31就是‘1’的ASCII码值,至于为什么是0x0031 3131,而不是0x3131 3100,留到评论区中作者再给出详细解释。大家也可以通过在当前命令行内,输入命令"x 0x5655b760"来查看内存中地址为0x5655b760的内容,如 图20:在当前命令行内,输入命令"x 0x5655b760"来查看内存中地址为0x5655b760的内容 所示;第二行,寄存器%ebx的值为0x5655af64,是一个_GLOBAL_OFFSET_TABLE_,这一点不需要我们去关注,大家千万不要卡在这里!!!一定要往下走!!!;第三行,寄存器%ecx的值为0x4;第四行,寄存器%edx的值为0x1;第五行,寄存器%%edi的值为0xf7fb3000;第六行,寄存器%esi的值为0xffffd1f4,而内存中地址为%esi的值的内容,也就是内存中地址为0xffffd1f4的值,是0xffffd399,而正如 图19 所示,0xffffd399也是一个地址,是一个字符串的地址。而这个字符串是什么呢?可以通过如 图20:使用"x 十六进制地址"命令来查看特定内存地址中的数 中的命令来查看。第七行,寄存器%ebp的值为0xffffd148,表示栈底指针指向0xffffd148这块内存地址;第八行,寄存器%esp的值为0xffffd11c,表示栈顶指针指向0xffffd11c这块内存地址,而内存中这块地址中,存放的数是函数phase_1的返回地址!!!;第九行,寄存器%eip的值为0x56556665,而寄存器%eip还有一个别名,叫作PC,程序计数器,记录着下一条指令的地址,其值为0x56556665,就表示下一条指令的地址为0x56556665。

图20:在当前命令行内,输入命令"x 0x5655b760"来查看内存中地址为0x5655b760的内容

图21:使用"x 十六进制地址"命令来查看特定内存地址中的数。其中ASCII码值0x2f对应'/',0x68对应'h',0x6f对应'o',0x6d对应'm'... ...)

接着,我们来观察第二栏,"DISASM",这一栏会显示即将要执行的最近若干条汇编指令。如 图22:观察第二栏"DISASM" 所示。因为每次通过"ni"命令执行一条汇编指令时,第二栏"DISASM"的内容一点也不会变,除了表示下一条指令的绿色的箭头所指的指令不一样以外。所以我们看一眼就可以。

图22:观察第二栏"DISASM"

然后观察第三栏"STACK",栈帧。如 图23:观察第三栏"STACK" 所示,不要被它给吓到,只需要知道第三栏表示栈帧就可以。至于第四栏,则不在我们关心的范围内,所以在这里不去分析它。

图23:观察第三栏"STACK"

这时,我们开始阅读phase_1函数的汇编代码。phase_1函数的汇编代码如 图25:phase_1函数的汇编代码 所示。为了便于分析,我们先记录下来执行第<phase_1>行汇编代码前各寄存器与栈帧的情况,如 图26:执行第<phase_1>行汇编代码之前各寄存器与栈帧的情况 所示。

 (图25:phase_1函数的汇编代码。

图26:执行第<phase_1>行汇编代码之前各寄存器与栈帧的情况

接着在该命令行中输入"ni",单步执行一条汇编指令,也就是执行掉第<phase_1>行汇编代码"endbr32",而执行后的结果如 图27:执行第<phase_1>行汇编代码之后各寄存器与栈帧的情况 所示。

图27:执行第<phase_1>行汇编代码之后各寄存器与栈帧的情况。在这里不需要去理会它的意思,它不会对我们需要关心的东西造成任何影响。也不需要可以去观察,观察有哪些寄存器发生了变化,或者栈帧发生了什么变化)

接着在该命令行中输入"ni",单步执行一条汇编指令,也就是执行掉第<phase_1+4>行汇编代码"push   ebx",而执行后的结果如 图28:实际验证——执行第<phase_1+4>行汇编代码之后各寄存器与栈帧的情况 所示。此时phase_1函数的栈帧如 图29:纸上分析——执行完第<phase_1+4>行后,phase_1函数的栈帧 所示。

图28:实际验证——执行第<phase_1+4>行汇编代码之后各寄存器与栈帧的情况。第<phase_1+4>行,表示将寄存器%ebx的值保存在当前函数的栈帧中,也就是保存在phase_1函数的栈帧中,与此同时,栈顶指针%esp的值也会默默的加4。为什么要将寄存器%ebx的值保存在当前函数的栈帧当中呢?因为GNU规定了寄存器%ebx是一个调用者保存的寄存器,对于该题而言,main函数调用了phase_1函数,phase_1函数是调用者,那么phase_1如果想使用寄存器%ebx,就必须先保存寄存器%ebx的值。)

内存地址内存地址中的数注释指向这块内存的寄存器
0xffffd1180x5655af64旧的%ebx的值%esp

图29:纸上分析——执行完第<phase_1+4>行后,phase_1函数的栈帧 这时结合图29图28中执行第<phase_1+4>行汇编代码之后栈帧的情况,我们会发现,函数phase_1函数的栈帧从0xffffd118开始,逐渐向下延申,而图28中0xffffd11c、0xffffd120、0xffffd124... ...等虽然也显示出来了,但是注意它们不是函数phase_1的栈帧,而是调用函数phase_1的函数main的栈帧!!!虽然这个函数中并没有使用到寄存器%ebp这个栈底指针,但是假如它使用了寄存器%ebp作为栈底指针,那么寄存器%ebp应一直指向0xffffd118。观察图28中执行第<phase_1+4>行汇编代码之后栈帧的情况,我们发现0xffffd11c保存着phase_1的返回地址,0xffffd120保存着传给phase_1的第一个参数,而正好符合我们在计基课上学到的%ebp+4=返回地址,%ebp+8=第一个入口参数!!!

接着第<phase_1+5>行,将寄存器%esp的值减去0x10,也就是减去16,这就代表着什么呢?这就代表着,原来寄存器%esp指向的是0xffffd118这块内存地址,而现在它指向的是0xffffd108这块内存地址,如 图30:纸上分析——执行完第<phase_1+5>行之后,phase_1函数的栈帧 所示。通过执行"ni"命令观察执行第<phase_1+5>行后各寄存器和栈帧的情况,发现正如我们我们所想,如 图31:实际验证——执行"ni"命令观察执行第<phase_1+5>​​​​​​​行后各寄存器和栈帧的情况 所示。

内存地址内存地址中的数注释指向这块内存的寄存器
0xffffd1180x5655af64旧的%ebx的值
0xffffd114
0xffffd110
0xffffd10c
0xffffd108%esp

图30:纸上分析——执行完第<phase_1+5>​​​​​​​行之后,phase_1函数的栈帧

图31:实际验证——执行"ni"命令观察执行第<phase_1+5>​​​​​​​行后各寄存器和栈帧的情况

第<phase_1+8>行,调用了一个不知道是用来做什么的函数,然而它无关紧要,所以接着看第<phase_1+13>行,让寄存器%ebx的值加上0x48f2。这时phase_1函数的栈帧不受影响,而相应寄存器的值却受到了影响,如 图32:纸上分析——执行完第<phase_1+13>行之后,phase_1函数的栈帧和相关寄存器的值 所示。而为了验证我们的想法,我们连续在命令行中使用两次"ni"命令,第一次以执行第<phase_1+8>行,第二次以执行第<phase_1+13>行,如 图33:实际验证——连续在命令行中使用两次"ni"命令,第一次以执行第<phase_1+8>行,第二次以执行第<phase_1+13>​​​​​​​行,观察各寄存器和栈帧的情况 所示。

内存地址内存地址中的数注释指向这块内存的寄存器
0xffffd1180x5655af64旧的%ebx的值
0xffffd114
0xffffd110
0xffffd10c
0xffffd108%esp
寄存器名称寄存器中的值
%esp0xffffd108
%ebx0x5655af64

图32:纸上分析——执行完第<phase_1+13>行之后,phase_1函数的栈帧和相关寄存器的值

图33:实际验证——连续在命令行中使用两次"ni"命令,第一次以执行第<phase_1+8>行,第二次以执行第<phase_1+13>​​​​​​​行,观察各寄存器和栈帧的情况

接着第<phase_1+19>行,先计算寄存器%ebx的值减去0x2e20h,接着将这个差作为地址,在内存中找这块地址,然后拿到这块地址中的值,把这块地址中的值放入到寄存器%eax当中。如 图34:纸上分析——执行完第<phase_1+19>行之后,phase_1函数的栈帧和相关寄存器的值 所示而为了验证我们的想法,我们在命令行中使用"ni"命令,执行第<phase_1+19>行,如 图35:实际验证——执行第<phase_1+19>行后观察各寄存器和栈帧的情况 所示。

内存地址内存地址中的数注释指向这块内存的寄存器
0xffffd1180x5655af64旧的%ebx的值
0xffffd114
0xffffd110
0xffffd10c
0xffffd108%esp
寄存器名称寄存器中的值
%esp0xffffd108
%ebx0x5655af64
%eax0x56558144

图34:纸上分析——执行完第<phase_1+19>行之后,phase_1函数的栈帧和相关寄存器的值

图35:实际验证——执行第<phase_1+19>行后观察各寄存器和栈帧的情况

接着第<phase_1+25>行,将寄存器%eax的值推入函数phase_1的栈帧中,不要忘记,push命令都带有一个默认的行为,就是将栈顶指针%esp的值减去4。所以这时函数phase_1的栈帧以及相关寄存器的值如 图36:纸上分析——执行完第<phase_1+25>行之后,phase_1函数的栈帧和相关寄存器的值 所示。为了验证我们的想法,我们在命令行中使用"ni"命令,执行第<phase_1+25>行,如 图37:实际验证——执行第<phase_1+25>行后观察各寄存器和栈帧的情况 所示。

内存地址内存地址中的数注释指向这块内存的寄存器
0xffffd1180x5655af64旧的%ebx的值
0xffffd114
0xffffd110
0xffffd10c
0xffffd108
0xffffd1040x56558144push进栈帧的%eax的值%esp
寄存器名称寄存器中的值
%esp0xffffd104
%ebx0x5655af64
%eax0x56558144

图36:纸上分析——执行完第<phase_1+25>行之后,phase_1函数的栈帧和相关寄存器的值

图37:实际验证——执行第<phase_1+25>行后观察各寄存器和栈帧的情况

接着第<phase_1+26>行,先计算寄存器%esp的值加上0x1c,然后把这个和作为地址,在内存中找这块地址,然后拿到这块地址中的值,把这块地址中的值push进函数phase_1的栈帧中,如 图38:纸上分析——执行完第<phase_1+26>​​​​​​​行之后,phase_1函数的栈帧和相关寄存器的值 所示。为了验证我们的想法,我们在命令行中使用"ni"命令,执行第<phase_1+26>行,如 图39:实际验证——执行第<phase_1+26>行后观察各寄存器和栈帧的情况 所示。

内存地址内存地址中的数注释指向这块内存的寄存器
0xffffd1180x5655af64旧的%ebx的值
0xffffd114
0xffffd110
0xffffd10c
0xffffd108
0xffffd1040x56558144push进栈帧的%eax的值
0xffffd1000x5655b760第<phase_1+26>行push进栈帧的%esp+0x1c这块地址0xffffd120的值,也就是0x5655b760%esp
寄存器名称寄存器中的值
%esp0xffffd100
%ebx0x5655af64
%eax0x56558144

图38:纸上分析——执行完第<phase_1+26>​​​​​​​行之后,phase_1函数的栈帧和相关寄存器的值

图39:实际验证——执行第<phase_1+26>行后观察各寄存器和栈帧的情况

接着第<phase_1+30>行,需要调用一个在剩下几个阶段里都非常重要的函数"strings_not_equal"。但是考虑到如果在这篇文章中展开讲"strings_not_equal"的话,会加长篇幅,所以在这里我们概括来讲,就是函数phase_1调用函数strings_not_equal之后一瞬间,会先选择寄存器%esp所指的内存中的内容(%esp所指的内存中的内容是个指针)所指的字符串,接着选择寄存器%esp+4所指的内存中的内容(%esp所指的内存中的内容是个指针)所指的字符串,然后比较这两个字符串是否相等,如果相等,则将专门用来保存函数调用的结果的寄存器%eax的值设置为0,若不相等,则设置为1。但是我们查看函数phase_1的汇编代码,如 图40:查看函数phase_1的汇编代码 所示,如果第511行执行后%eax为1,那么第512行不用考虑,第513行执行test %eax %eax时,记住test %eax %eax这条汇编指令的结果等于%eax == 0,而显然%eax == 0的值是0,进而test %eax %eax的值是0,进而会被第514行视为不相等,进而执行跳跃操作跳到第518行,进而会引爆炸弹。所以我们的思路就是,怎么样才能让第511行执行后%eax为0,也就是如何才能使得两个字符串相等。这时答案就已经呼之欲出了,我们只有输入"I turned the moon into something I call a Death Star.",才能使得炸弹最终不爆炸。

图40:查看函数phase_1的汇编代码) 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

栈(深入理解栈是什么)

这里写目录标题 栈概念栈的初始化栈的溢出函数的栈帧函数的返回 栈 概念 英文&#xff1a;stack&#xff0c;也叫做堆栈。 特点&#xff1a;先进后出。 栈的两个基本操作&#xff0c;也就是入栈和出栈。都是通过SP指针来维护。C语言中的函数的局部变量&#xff0c;传递的实参…

计算机二级Python基本操作题-序号46

Python 函数查询 1. 《卖火柴的小女孩》是丹麦童话故事作家安徒生的一篇童话故事&#xff0c;发表于1846年。主要讲了一个卖火柴的小女孩在富人阖家欢乐、举杯共庆的大年夜冻死在街头的故事。这里给出《卖火柴的小女孩》的一个网络版本文件&#xff0c;文件名为“小女孩.txt”…

IOday8作业

使用消息队列完成两个进程之间相互通信(多进程) #include<myhead.h>//定义结构体 struct buf {long mtype;char mtest[1024]; };#define SIZE (sizeof(struct buf)-sizeof(long))//进程 int main(int argc, const char *argv[]) {//创建keykey_t key1 ftok("/&quo…

系统韧性研究(7)| 韧性系统的16大指导原则

不良事件和条件可能会中断系统&#xff0c;导致系统无法提供必要的功能和服务。正如我在本系列的前几篇文章中所概述的那样&#xff0c;韧性是大多数系统的一个基本质量属性&#xff0c;因为它们提供了关键的能力和服务&#xff0c;尽管存在着不可避免的困难&#xff0c;但这些…

前沿重器[39] | 对话式推荐系统——概念和技术点

前沿重器 栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

Java设计模式-单例(Singleton)设计模式的概述及实现

目录 &#x1f436;1 设计模式概述 &#x1f436;2 何为单例模式 &#x1f436;3 实现思路 &#x1f436;4 饿汉式实现代码 &#x1f436;5 懒汉式实现代码 &#x1f436;6 对比两种模式&#xff08;特点、优缺点&#xff09; &#x1f436;7 单例模式的优点及应用场景 &…

Python中的魔力编程:掌握面向对象之道

Python中的面向对象编程 背景&#xff1a; ​ 最近在看一些代码的时候&#xff0c;对类中的一些内置方法不是很懂&#xff0c;因此出一篇文章来细说一下&#xff0c;希望大家看完后对Python中类有一个清楚的认识。 基础铺垫&#xff1a; ​ 面向对象的三个特点&#xff1a;…

【设计模式--结构型--享元模式】

设计模式--结构型--享元模式 享元模式定义结构实现案例优缺点使用场景 享元模式 定义 运用共享技术来有效的支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅减少需要创建的对象数量&#xff0c;避免大量相似的对象的开销&#xff0c;从而提高系统资源的利用率。 …

创建并测试第一个django项目并解决过程中遇到的问题

Django 是一个高级 Python Web 框架&#xff0c;它鼓励快速开发和简洁、实用的设计。它由经验丰富的开发人员构建&#xff0c;解决了 Web 开发的大部分麻烦&#xff0c;因此您可以专注于编写应用程序&#xff0c;而无需重新发明轮子。它是免费和开源的。 目录 一、django项目 …

官宣!「湾区之光群星演唱会」拉开2024新年音乐华丽序幕!

万众期待&#xff0c;群星荟萃&#xff01;青春宝安时尚湾区——湾区之光群星演唱会即将在2024年1月5日闪耀亮相深圳宝安。 华语歌坛巨星天后齐聚一堂&#xff0c;携手多位实力唱将&#xff0c;共同呈现一场无与伦比的演唱会盛宴&#xff01;在深情而又充满力量的歌声之中&…

孩子还是有一颗网安梦——Bandit通关教程:Level 0 → Level 1

&#x1f575;️‍♂️ 专栏《解密游戏-Bandit》 &#x1f310; 游戏官网&#xff1a; Bandit游戏 &#x1f3ae; 游戏简介&#xff1a; Bandit游戏专为网络安全初学者设计&#xff0c;通过一系列级别挑战玩家&#xff0c;从Level0开始&#xff0c;逐步学习基础命令行和安全概念…

LabVIEW开发矿井排水监控系统

LabVIEW开发矿井排水监控系统 针对矿井水害对煤矿安全生产构成的威胁&#xff0c;设计了一种基于嵌入式PLC和LabVIEW的矿井排水监控系统。该系统结合了PLC的可靠控制与单片机的应用灵活性&#xff0c;有效克服了传统排水方法中的不足&#xff0c;如测量不准确、效率低下等问题…

Java:字符流 文件输出 与 读入 方法

Java&#xff1a;字节流 文件输出与读入方法 并 实现文件拷贝 文章目录 字符流FileReaderFileWrite 字符流 字符流底层就是字节流。 字符流 字节流 字符集 特点&#xff1a; 输入流&#xff1a;一次读入一个字节&#xff0c;遇到中文时&#xff0c;一次读多个字节。 输出流…

图-数据结构

图的介绍 如果你有学过《离散数学》&#xff0c;那么对图的概念一定不陌生&#xff0c;在计算机科学中&#xff0c;一个图就是一些顶点的集合&#xff0c;这些顶点通过一系列边连接&#xff08;结对&#xff09;。顶点用圆圈表示&#xff0c;边就是这些圆圈之间的连线。注意&a…

Design patterns--代理模式

设计模式之代理模式 我们使用Qt开发大型应用程序时&#xff0c;经常遇见大型程序启动时需要加载一些配置信息、用户末次操作信息&#xff0c;以及算法模型等数据时比较费时&#xff0c;笔者在程序启动时设计欢迎页或加载页等窗体来提示用户程序正在加载某些数据&#xff0c;加载…

【Java数据结构 -- 顺序表】

List和ArrayList与顺序表 一. List1.1 List介绍2.1 常见接口介绍3.1 List的使用 二. ArrayList与顺序表1.线性表2.顺序表2.1 接口的实现2.2 顺序表的创建2.3 顺序表的打印2.4 顺序表的插入2.5 顺序表的按索引位置插入数据2.6 判断顺序表是否包含某个数2.7 返回顺序表某个数的索…

谁会成为第一个MoE大模型基座呢?重磅!Mixtral MoE 8x7B!!!

文章目录 谁会成为第一个MoE大模型基座呢&#xff1f;重磅&#xff01;Mixtral MoE 8x7B&#xff01;&#xff01;&#xff01;前言重磅&#xff01;Mixtral MoE 8x7B&#xff01;&#xff01;&#xff01;Mixtral是啥模型介绍模型结构长啥样&#xff1f;表现如何&#xff1f;可…

Python从入门到精通五:Python数据容器

数据容器入门 为什么学习数据容器 思考一个问题&#xff1a;如果我想要在程序中&#xff0c;记录5名学生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 学习数据容器&#xff0c;就是为了批量存储或批量使用多份数据 Python中的数据容器&#xff1a; 一种可以容纳多份…

HCIA-H12-811题目解析(10)

1、【单选题】DHCP客户端在租期到达哪个比例时第一次发送续租报文&#xff1f; 2、【单选题】在WLAN中用于标识无线网络&#xff0c; 区分不同的无线网络的是&#xff1f; 3、【单选题】我们在笔记本电脑上搜索可接入无线网络时&#xff0c;显示出来的网络名称实际是 4、【单…

哪些原因导致MES管理系统实施项目失败

在制造业中&#xff0c;实施MES管理系统是一种提高生产效率、降低成本、提升质量的重要手段。然而&#xff0c;许多MES管理系统实施项目并未取得预期的成功&#xff0c;甚至失败。本文将探讨导致MES管理系统实施项目失败的原因。 1、需求不明确 在MES实施项目中&#xff0c;需…