缓冲区溢出漏洞

2023-11-05

   在计算机操作系统中,“缓冲区”是指内存空间中用来存储程序运行时临时数据的一片大小有限并且连续的内存区域。根据程序中内存的分配方式和使用目的,缓冲区一般可分为栈和堆两种类型。C语言程序中定义的数组就是一种最常见的栈缓冲区。

   缓冲区溢出漏洞,作为软件中最容易发生的一类漏洞,其形成原理就是:当程序在处理用户数据时,未能对其大小进行恰当的限制,或者在进行拷贝、填充时没对这些数据限定边界,导致实际操作的数据大小超过了内存中目标缓冲区的大小,使得内存中一些关键数据被覆盖、从而引发安全问题。如果攻击者通过特制的数据进行溢出覆盖、则有机会成功利用缓冲区溢出漏洞,从而修改内存中数据,改变程序执行流程,劫持进程,执行恶意代码,最终获得主机控制权。

缓冲区与内存分布

根据操作系统的不同,一个进程可能被分配到不同的内存区域去执行。但根据进程使用的内存区域的预定功能划分,一般可大致分成以下三个部分:

1.代码区

代码区(text segment)存储着被装人执行的二进制机器代码,处理器将从内存的该区域一条一条地取出指令和操作数,并送入算术逻辑单元进行运算。通常情况下,该区域的数据只允许读,不能进行修改,其目的就是为了防止代码在运行的时候被直接修改。

2.静态数据区

静态数据区用于存储全局变量等。进一步可以划分成初始化的数据区(data segment)和未初始化的数据区(bss segment)。前者用于存放已经初始化的全局变量和静态变量,后者用于保存未初始化的全局变量。

3.动态数据区

动态数据区用来存放程序运行时的动态变量。包括两种区域:

栈区(stack segment):用于存储函数之间的调用关系以及函数内部的变量,以保证被调用函数在返回时回到父函数中继续执行。

堆区(heap segment):程序运行时向系统动态申请的内存空间位于堆区,用完之后需要程序主动释放所请求的内存空间。在C/C++中使用malloc或者new等方式申请的空间就在堆区。

在现代操作系统中,系统都会给每个进程分配独立的虚拟地址空间,在真正调用时则将其映射到物理内存空间。

在32位的Windows环境下,由高级语言编写的程序经过编译、链接,最终生成可执行文件,即PE(portable executable)文件。在运行PE文件时,操作系统会自动加载该文件到内存,并为其映射出4GB的虚拟存储空间,然后继续运行,这就形成了所谓的进程空间。

程序中所使用的缓冲区可以是堆区和栈区,也可以是存放静态变量的数据区。由于进程中各个区域都有自己的用途,根据缓冲区溢出的利用方法和缓冲区在内存中所属区域,其可分为栈溢出和堆溢出。

栈溢出

在介绍栈溢出之前,我们先对栈在程序运行期间的重要作用进行介绍

在程序设计中,栈通常指的是一种先进后出(fisrt in last out,FILO)的数据结构,而入栈(PUSH)和出栈(POP)则是进行栈操作的两种常见方法。为了标识栈的空间大小,同时为了更方便地访问栈中数据,栈通常还包括栈顶(TOP)栈底(BASE)两个栈指针。栈顶随入栈和出栈操作而动态变化,但始终指向栈中最后人栈的数据;栈底指向先入栈的数据,栈顶和栈底之间的空间存储的就是当前栈中的数据。

相对于广义的栈而言,系统栈则是操作系统在每个进程的虚拟内存空间中为每个线程划分出来的一片存储空间,它也同样遵守先进后出的栈操作原则,但是与一般的栈不同的是:系统栈由系统自动维护,用于实现高级语言中函数的调用。对于类似C语言这样的高级语言,系统栈的PUSH和POP等堆栈平衡的细节相对于用户是透明的。此外,栈帧的生长方向是从高地址向低地址增长的。

操作系统为进程中的每个函数调用都划分了一个栈帧空间,每个栈帧都是一个独立的栈结构,而系统栈则是这些函数调用栈帧的集合。对于每个函数而言,通过栈帧可以获得以下重要信息:

(1)局部变量:为函数中局部变量开辟的内存空间。

(2)栈帧状态值:保存前栈帧的顶部和底部(实际上只保存前栈帧的底部,因为前栈帧的顶部可以通过对栈平衡计算得到),用于在函数调用结束后恢复调用者函数(caller func-tion)的栈帧。

(3)函数返回地址:保存当前函数调用前的"断点"信息,即函数调用指令的后面一条指令的地址,以便在函数返回时能够恢复到函数被调用前的代码区中继续执行指令。

(4)函数的调用参数。

系统栈在工作的过程中主要用到了三个寄存器:

①ESP: 栈指针寄存器(extended stack pointer),其存放的是当前栈帧的栈顶指针。

②EBP:基址指针寄存器(exteded base pointer),其存放的是当前栈帧的栈底指针。

③EIP:指令寄存器(extended instruction pointer),其存放的是下一条等待执行的指令地址。

如果控制了EIP寄存器的内容,就可以控制进程行为——通过设置EIP的内容,使CPU去执行我们想要执行的指令,从而劫持了进程。

 通常不同的操作系统、不同的程序语言、不同的编译器在实现函数调用时,其对栈的基本操作是一致的,但在函数调用约定上仍存在差异,这主要体现在函数参数的传递顺序和恢复堆栈平衡的方式,即参数入栈顺序是从左向右还是从右向左,函数返回时恢复堆栈的操作由子函数进行还是由母函数进行。具体地,对于Windows平台下的Visual C++而言,一般按照默认的stdcall方式对函数进行调用,即参数是按照从右向左的顺序入栈,堆栈平衡由子函数完成。

进程中的函数调用主要通过如下几个步骤实现:

(1)参数入栈:将被调用函数的参数按照从右向左的顺序依次入栈。

(2)返回地址入栈:将call指令的下一条指令的地址入栈。

(3)代码区跳转:处理器从代码区的当前位置跳到被调用函数的入口处。

(4)栈帧调整:这主要包括保存当前栈帧状态、切换栈帧和给新栈帧分配空间

类似地,函数返回步骤如下:

(1)根据需要保存函数返回值到eax寄存器中(一般使用eax寄存器存储返回值)。

(2)降低栈顶,回收当前栈帧空间。

(3)恢复母函数栈帧。

(4)按照函数返回地址跳转回到父函数,继续执行。

栈溢出利用

栈溢出的利用根据被覆盖的数据位置和所要实现的目的的不同,一般可以分为以下几种:修改邻接变量、修改函数返回地址和S.E.H结构覆盖等,下面就分别对这三种方式进行阐述。

修改邻接变量

由于函数的局部变量是依次存储在栈帧上的,因此如果这些局部变量中有数组之类的缓冲区,并且程序中存在数组越界缺陷,那么数组越界后就有可能破坏栈中相邻变量的值、甚至破坏栈帧中所保存的EBP、返回地址等重要数据。

下面是一个存在栈溢出缺陷的密码验证程序的源代码

#include<stdio.h>
#define PASSWORD "1234567"
int verify_password(char* password){
   int authenticated;
   char buffer[8];
   authenticated=strcmp(password,PASSWORD);
   strcpy(buffer,password);
   return authenticated;
}
main(){
 int valid_flag=0;
 char password[1024];
 while(1){
    printf("please input password:   ");
    scanf("%s",password);
    valid_flag=verify_password(password);
    if(valid_flag){
      printf("failure");

    }
    else{
     printf("success");
     break;
    }
 }
}                                                                   

 该程序是一个简单的口令验证程序,我们在其中手动构造了一个栈溢出漏洞。当执行到int verify_password(char *password)时,栈帧状态如图所示。

可以看到,在verify_password函数的栈帧中,局部变量int authenticated恰好位于缓冲区char buffer[7]的"下方"。authenticated为int类型,在内存中是一个DWORD,占4个字节。所以,如果能够让buffer数组越界,buffer[8]、buffer[9]、buffer[10]、buffer[11]则将写人70相邻的变量authenticated中。

   分析一下源代码不难发现,authenticated变量的值来源于strcmp 函数的返回值,之后会返回给 main 函数作为口令验证成功与否的标志变量:当 authenticated为0时,表示验证成功;反之,验证不成功。

    如果我们输入的口令超过了7个字符(注意:字符串截断符NULL将占用一个字节),则越界字符的ASCII码会修改掉authenticated的值。如果这段溢出数据(长度为8个字符的口令)恰好把authenticated 改为0,则程序流程将被改变。如此,就可以成功实现用非法的超长密码去修改 buffer的邻接变量 authenticated,从而绕过密码验证程序。

我们用gcc对这段代码进行编译

gcc -z execstack -fno-stack-protector -o stackflow-example stackflow-example.c

(其中-z execstack开启堆栈可执行机制,-fno-stack-protector关闭堆栈保护机制)

【这里缺少一个GDB调试示例】

修改函数返回地址

    函数调用,一般是通过系统栈实现的。如前面所述,可以看出函数的返回地址对控制程序执行流程具有相当重要的作用——决定函数调用返回时将要执行的下一条指令。如果函数返回地址被修改,那么在当前函数执行完毕准备返回原调用函数时,程序流程将被改变。

     改写邻接变量的方法是很有用的,但这种漏洞利用对代码环境的要求相对比较苛刻。更通用、更强大的攻击其通过缓冲区溢出改写的目标往往不是某一个变量,而是栈帧高地址的EBP和函数返回地址等值通过覆盖程序中的函数返回地址和函数指针等,攻击者可以直接将程序跳转到其预先设定或已注入到目标进程的代码上去执行。

通过覆盖修改返回地址,使其指向shellcode地址,更改程序流程,从而转至shellcode处执行。与简单的邻接变量改写不同的是,通过修改函数指针可以随意更改程序指向,并执行攻击者向进程中植入的自己定制的代码,实现“自主”控制。

   一种较为简单的方法是直接将内存中shellcode的地址赋给返回地址,然后使得程序直接就跳转到shellcode处执行。但是在实际的漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧可能发生"移位",即shellcode在内存中的地址是动态变化的,所以这种采用直接赋地址值的简单方式在以后的运行过程中会出现跳转异常。另外一种不能直接使用shellcode地址的原因是:处于栈中shellcode开始位置的高位通常为0x00 如果用该地址覆盖返回地址,则构造的溢出字符串中0x00之后的数据可能在进行字符串操作(如 strepy 函数导致的溢出)时被截断。为了避免这种情况的发生,可以在覆盖返回地址的时候用系统动态链接库中某条处于高地址且位置固定的跳转指令所在的地址进行覆盖,然后再通过这条跳转指令指向动态变化的shellcode地址。这样,便能够确保程序执行流程在目标系统中运行时可以被如期进行。

  在调试前面的密码验证程序时,可以发现在函数返回时,esp 总是指向函数返回后的下一条指令,根据这一特点,如果用指令jmp esp 的地址覆盖返回地址,则函数也可以跳转到函数返回后的下一条指令;如果从函数返回后的下一条指令开始,都已经被shellcode所覆盖,那么程序则可以跳转到shellcode上,并执行它,从而实现了程序流程的控制。

在内存中搜索jmp esp 指令是比较容易的(大家可以通过ollydbg在内存中搜索),为了稳定性和通用性,一般选择kernel32.dll或者user32.dll中的地址。 

【这里缺少一个代码示例】

S.E.H结构覆盖

    在Windows平台下,操作系统或应用程序运行时,为了保证在出现除零、非法内存访问等错误时,系统也能正常运行而不至于崩溃或宕机,Windows会对运行在其中的程序提供一次补救机会来处理错误,这就是Windows下的异常处理机制。而这种异常处理机制在特殊情况下也可能被攻击者所利用。

    Windows异常处理机制的一个重要数据结构是位于系统栈中的异常处理结构体S.E.H (struct exception handler),它包含两个DWORD指针:S.E.H链表指针和异常处理函数句柄、其结构如图所示。

    当程序中包含异常处理块(exception block)时,编译器则要生成一些特殊的代码用来实现异常处理机制。这主要指编译时产生的一些来支持处理S.E.H数据结构的表(table)以及确保异常被处理的回调(callback)函数。此外,编译器还要负责准备栈结构和其他内部信息,供操作系统使用和参考。当栈中存在多个S.E.H时,它们之间通过链表指针在栈内由栈顶向栈底串成单向链表,位于链表最顶端的S.E.H位置通过TEB(thread environment block,线程环境块)0字节偏移处的指针标识。

    当发生异常时,操作系统会中断程序,并首先从TEB的0字节偏移处取出最顶端的S.E.H结构地址,使用异常处理函数句柄所指向的代码来处理异常。如果该异常处理函数运行失败,则顺着S.E.H链表依次尝试其他的异常处理函数。如果程序预先安装的所有异常处理函数均无法处理,系统将采用默认的异常处理函数,弹出错误对话框并强制关闭程序。

    实际上,S.E.H就是在程序出错之后系统关闭程序之前,让程序转去执行一个预先设定的回调函数。因此,攻击者可以以这种方式进行漏洞利用攻击:由于S.E.H存放在栈中,利用缓冲区溢出可以覆盖S.E.H;如果精心设计溢出数据,则有可能把S.E.H中异常处理函数的入口地址更改为shellcode的起始地址或可以跳转到shellcode的跳转指令的地址,从而导致在程序发生异常时,Windows处理异常机制转而执行的不是正常的异常处理函数,而是已覆盖的shellcode。

【缺少代码示例】

堆溢出

堆与栈的区别

程序在执行时需要两种不同类型的内存来协同配合。

其中一种,就是之前介绍的栈。典型的栈变量包括函数内部的普通变量、数组等。栈变量在使用时不需要额外的申请操作,系统栈会根据函数中的变量声明自动在函数栈帧中给其预留空间。栈空间由系统维护,它的分配(如 sub esp,xxx)和回收(如 add esp,xxx)都是由系统来完成的,最终达到栈平衡。所有这些对程序员来说都是透明的。

另一种就是堆,堆主要具备以下特性:

(1)堆是一种在程序运行时动态分配的内存。所谓动态是指所需内存的大小在程序设计时不能预先确定或内存过大无法在栈中进行分配,需要在程序运行时参考用户的反馈。

(2)堆在使用时需要程序员使用专有的函数进行申请。如C语言中的malloc 等函数,C++中的new函数等都是最常见的分配堆内存的函数。堆内存申请有可能成功,也有可能失败,这与申请内存的大小、机器性能和当前运行环境有关。

(3)一般用一个堆指针来使用申请得到的内存,读、写、释放都通过这个指针来完成。

(4)使用完毕后需要将堆指针传给堆释放函数回收这片内存,否则会造成内存泄露。典型的释放函数包括free,delete等。

现代操作系统中堆的数据结构一般包括堆块和堆表两类。

堆块:出于性能的考虑,堆区的内存按不同大小组织成块,以堆块为单位进行标识,而不是传统的按字节标识。一个堆块包括两个部分:块首和块身块首是一个堆块头部的几个字节,用来标识这个堆块自身的信息,例如,本块的大小,本块是空闲还是占用等信息;块身是紧跟在块首后面的部分,也是最终分配给用户使用的数据区。

堆表堆表一般位于堆区的起始位置,用于索引堆区中所有堆块的重要信息,包括堆块的位置,堆块的大小,空闲还是占用等。堆表的数据结构决定了整个堆区的组织方式,是快速检索空闲块、保证堆分配效率的关键。堆表在设计时可能会考虑采用平衡二叉树等高级数据结构用于优化查找效率。现代操作系统的堆表往往不止一种数据结构。

堆区内的基本单位则是堆块,堆区和堆块的分配都由程序员来完成。

对一个堆块而言,被分配之后,如果不被合并,那么会有两种状态:占有态和空闲态。其中,空闲态的堆块会被链入空链表中,由系统管理。而占有态的堆块会返回一个由程序员定义的句柄,由程序员管理。堆块被分为两部分:块首和块身。其中块首是一个8字节的数据,存放着堆块的信息。需要注意的是指向堆块的指针或者句柄,指向的是块身的首地址

 

可以看出,空闲堆块比占有堆块多出了两个4字节的指针,这两个指针用于链接系统中的其他空闲堆块。

(2)堆表

在Windows中,占用态的堆块被使用它的程序索引,而堆表只索引所有空闲块的堆块。其中,最重要的堆表有两种:空闲双向链表freelist(简称空表)和快速单向链表lookaside(简称快表)

①空表

空闲堆块的块首中包含一对重要的指针,这对指针用于将空闲堆块组织成双向链表。指针指向空堆块的块首第一个字节。

②快表

快表是Windows用来加速堆块分配而采用的一种堆表。之所以把它叫做"快表"是因为这类单向链表中从来不会发生堆块合并(其中的空闲块块首被设置为占有态,用来防止堆块合并)。

堆溢出的利用

基本原理与DWORD SHOOT

堆管理系统的三类操作:堆块分配,堆块释放和堆块合并,归根到底都是对空表链的修改。分配就是将堆块从空表中“卸下”;释放就是把堆块“链人”空表;合并可以看成是把若干块先从空表中“卸下”,修改块首信息,然后把更新后的块“链入”空表。所有“卸下"和“链入”堆块的工作都发生在链表中,如果能够修改链表节点的指针,在“卸下”和“链人”的过程中就有可能获得一次读写内存的机会。堆溢出利用的精髓就是用精心构造的数据去溢出覆盖下一个堆块的块首,使其改写块首中的前向指针(flink)和后向指针(blink),然后在分配,释放,合并等操作发生时同机获得一次向内存任意地址写入任意数据的机会。这种能够向内存任意位置写任意数据的机会称为"Arbitrary Dword Reset"(又称 Dword Shoot)。Arbitrary Dword Reset发生时,我们不但可以控制射击的目标(任意地址),还可以选用适当的目标数据(4字节恶意数据)。通过Arbitrary Dword Reset攻击者可以进而劫持进程,运行shellcode。

下面我们简单地分析一下空表修改中的一种:节点的拆卸,即在堆块分配和合并中是如何产生"Dword Shoot"的。

根据链表操作的常识,可以了解到,卸时发生如下操作:

node→blink→flink=node→flink;
node→flink→blink=node→blink;

当进行第一个操作时,实际上是把该节点的前向指针的内容赋给后向指针所指向位置节点的前向指针;进行第二个操作时,则是把后向指针的内容赋给前向指针所指向位置节点的后向指针。

当我们用精心构造的数据淹没该节点块身的前八个字节,即该堆块的前向指针和后向指针时,如果在 flink 里面放入的是4 字节的任意恶意数据内容,在 bink 里面放入的是目标地址,则当该节点被拆卸时,执行node→blink→flink=node→flink操作(对于node→blink→flink,系统会认为node→blink指向的是一个堆块的块身,而 flink 正是这个块身的第一个4字节单元),而node→flink即为node的前四字节,因此该拆卸操作导致目标地址的内容被修改为该4字节的恶意数据。因此,通过这种构造可以实现对任意地址的4字节(dword)数据的任意写操作。

[缺少代码示例]

Heap Spray 

Heap Spray 技术是使用栈溢出和堆结合的一个技术,这种技术可以在很大程度上解决溢出攻击在不同版本上的不兼容问题,并且可以减少对栈的破坏。缺陷在于只能在浏览器相关溢出当中使用,但是相关思想却被广泛应用于其他类型攻击中,如JIT Spray、ActivexSpray。

这种技术的关键在于,首先将shellcode放置到堆中,然后在栈溢出时,控制函数执行流程,跳转到堆中执行shellcode。

在一次Exploit过程中,关键是用传入的shellcode所在的位置去覆盖EIP。在实际攻击中,用什么值覆盖EIP是可控的,但是这个值指向的地址是否有shellcode就很关键了。假设“地址A”表示shellcode的起始地址,“地址B”表示在缓冲区溢出中用于覆盖的函数返回地址或者函数指针的值。因此如果B<A,而地址B到地址A之间如果有诸如nop 这样的不改变程序状态的指令,那么在执行完B到A间的这些指令,就可以继续执行shellcode。

Heap Spray应用环境一般是浏览器,因为在这种环境下,内存布局比较困难,想要跳转到某个固定的位置几乎不可能,即使使用 jmp esp 等间接跳转,有时也不太可靠。因此,Heap Spray技术应运而生。使用这个技术依赖于浏览器对脚本语言很好的支持。这种攻击,使用脚本语言定义大量对象,这些对象内容为shellcode,而浏览器初始化这些对象的过程,实际上就是在堆中申请内存,并将内容设定为shellcode。然后再利用漏洞,将一个固定的值(常常为0x0c0c0c0c或者0x0a0a0a0a0a等,之所以采用这样的地址,是因为JavaScript申请的内存块一般从高地址开始分配,而这些地址常常指向这部分内存块)放入 EIP中,而这个值对应的地址通常在堆中。这样通过大量堆申请将shellcode放到堆中去,巧妙的布局可以保证极大概率的撞击成功率(只要这个固定地址不在shellcode部分)。因此使用JavaScript语句可以将shellcode放入内存,然后使用栈溢出进行跳转,就可执行到堆中的shellcode。

【缺少代码示例】

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

缓冲区溢出漏洞 的相关文章

随机推荐

  • XD插件PhotoSplash2的用法

    1 安装 略 2 在画布上插入5个矩形 3 全部选中 并点击插件photosplash2 4 查询框中输入 flower 自动按照选中的矩形数量 选择照片 按 Apply 5 photos 5 效果
  • 如何面试Python 后端工程师(持续更新)

    看到 如何面试Python后端工程师 这个问题下一位大牛罗列的问题 感觉挺有价值 现在记在这里 找出这些问题的答案 持续更新 一 语言 1 推荐一本看过最好的python书籍 拉开话题好扯淡 目前所知道的 看过的就是 Python 核心编程
  • drop、truncate和delete的区别

    drop truncate和delete的区别 1 DELETE语句执行删除的过程是每次从表中删除一行 并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作 TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把
  • 直接插入排序(C)

    直接插入排序 算法描述 所谓直接插入排序 就是从插入第1个数值开始 存在第0位 直至插入第n个数值 当插入第n个数值时 前面n 1个数值已经是排好序的 插入完第n个数值时排序结束 假设 数据集合为N 有n个数据 i 1 为第1个数值 第一步
  • 关于Qt 中update()和repaint()的区别

    void QWidget repaint int x int y int w int h bool erase TRUE 槽 通过立即调用paintEvent 来直接重新绘制窗口部件 如果erase为真 Qt在paintEvent 调用之前
  • RPM安装和卸载

    rpm 是redhat公司出的一个包管理工具 redhat package manager由于我们这是虚拟机 它有光驱 我们可以把光驱挂载一下mount dev cdrom mnt cd mnt lscd packages 这个目录下红色的
  • 支付宝数字化经营能加盟吗?真实情况原来是这样!(深度好文)

    去年支付宝的刷脸支付之火相信大家都知道 项目是个好项目 就是被那批做微商会销的人玩坏了 他们硬是把刷脸支付玩成了一个传销骗局 导致现在人家一说刷脸支付创业项目 就说是个骗局 连正规公司都受到了牵连 典型的一粒老鼠屎坏了锅粥 虽然刷脸支付肯定
  • Pygame 官方文档 - pygame.key

    pygame key 与键盘相关的 Pygame 模块 pygame key get focused 当窗口获得键盘的输入焦点时返回 True pygame key get pressed 获取键盘上所有按键的状态 pygame key g
  • c++复制省略

    复制省略问题 问题背景 工作背景 在工作过程中间 由于团队已经使用gcc7编译器并且支持c 17标准的使用 我们在大量代码内使用了tuple结合结构化绑定的代码来替代之前的返回结构体的模式 使用引用传递出参的模式 下面是几个模式的案例 返回
  • 虚拟服务器共用,vm共享虚拟主机(vmware共享的虚拟机)

    共享虚拟机 是网络中有多台VMware Workstation 在其中启用 共享虚拟机 功能后 假设这台主机为A 其他安装VMware Workstation 的主机 假设主机为B 1 使用共享文件夹 不稳定 容易保存失败2 电脑A扩展屏幕
  • 二叉树的前序遍历、中序遍历、后续遍历和层序遍历

    题目 L2 004 这是二叉搜索树吗 25 分 L2 006 树的遍历 25 分 L2 011 玩转二叉树 25 分 L2 035 完全二叉树的层序遍历 25 分 L3 010 是否完全二叉搜索树 30 分 代码 L2 004 这是二叉搜索
  • IntelliJ IDEA 设置自动导入maven依赖

    1 前言 maven自动导入依赖 如果打开 因为maven中一切皆资源 自己写的也会成为Maven中的资源 所以如果在pom中写入的坐标错误 那么只要自动导入 就会去指定的maven仓库中创建资源 文件夹等 对于新版2020的idea 好像
  • 计算机网络知识总结及知识网图

    目录 相关课程链接 第一章 计算机网络体系结构 知识网图 计算机网络分成的基本原则 TCP IP模型和OSI参考模型的比较 计算机网络与分布式计算机系统的主要区别 端到端通信与点到点通信的区别 第二章 物理层 知识网图 奈特斯特定理得到的结
  • vue路由拦截,路由嵌套,

    系列文章目录 提示 这里可以添加系列文章的所有文章的目录 目录需要自己手动添加 例如 第一章 Python 机器学习入门之pandas的使用 提示 写完文章后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 系列文章目录 前言 一
  • Cloudflare CDN 定时检测并更换优质IP

    可能有人觉得 Cloudflare 是减速 CDN 这结论应该算是一半对 一半错 相对于的大陆以及大陆周边较近的香港 日本 韩国等的服务器来讲 套上 Cloudflare 的 CDN 确实可能起到反效果或者没效果 但是 距离大陆偏远地区的服
  • 阿里java面试官问:写一个你认为最好的单例模式?怒写了7个

    面试题 写一个你认为最好的单例模式 面试考察点 考察目的 单例模式可以考察非常多的基础知识 因此对于这种问题 很多面试官都会问 小伙伴要注意 在面试过程中 但凡能够从多个维度考察求职者能力的题目 一定不会被抛弃 特别是比较泛的问题 比如 请
  • IOS Opengl ES 用多光源实现材质,阴影,高光效果

    在Opengl ES 1 0 中 要实现材质效果 往往需要光源的设置 如果环境光和漫反射光强了 材质效果难以体现 如果环境光和漫反射光弱了 材质就暗了 实现阴影效果 可以放置一个主平行光源 为了材质效果 需要将环境光设到比较弱 漫反射光设到
  • 学习记录-Qt布局中的Spacer使用

    在自定义控件后 使用自定义控件进行布局时 采用网格布局 当窗口大小合适 动态创建的空控件数量也合适时 那么控件之间的空白处比较小 能布满窗口 效果比较好看 但是当控件较小或者控件较少时 就都居中显示了 不想要这种效果 想要一种自动靠左对齐或
  • 线程池运行原理

    核心参数 corePollSize 核心线程池的大小 maximumPollSize 线程池能创建线程的最大个数 keepAliveTime 空闲线程存活时间 unit 时间单位 为keepAliveTime指定时间单位 workQuequ
  • 缓冲区溢出漏洞

    在计算机操作系统中 缓冲区 是指内存空间中用来存储程序运行时临时数据的一片大小有限并且连续的内存区域 根据程序中内存的分配方式和使用目的 缓冲区一般可分为栈和堆两种类型 C语言程序中定义的数组就是一种最常见的栈缓冲区 缓冲区溢出漏洞 作为软