易语言怎么做网页网站,智能科技网站模板下载地址,网站建设中模版,dw如何用表格做网站Linux内核的生成过程
内核的生成步骤可以概括如下#xff1a;
① 先生成 vmlinux#xff0c;这是一个elf可执行文件。② 然后 objcopy 成 arch/i386/boot/compressed/vmlinux.bin#xff0c;去掉了原 elf 文件中一些无用的section等信息。③ gzip 后压缩为 arch/i386/boot…Linux内核的生成过程
内核的生成步骤可以概括如下
① 先生成 vmlinux这是一个elf可执行文件。② 然后 objcopy 成 arch/i386/boot/compressed/vmlinux.bin去掉了原 elf 文件中一些无用的section等信息。③ gzip 后压缩为 arch/i386/boot/compressed/vmlinux.bin.gz。④ 把压缩文件作为数据段链接成 arch/i386/boot/compressed/piggy.o。⑤ 链接arch/i386/boot/compressed/vmlinux head.omisc.opiggy.o 其中 head.o 和 misc.o 是用来解压缩的。⑥ objcopy 成 arch/i386/boot/vmlinux.bin去掉了原 elf 文件中一些无用的 section等信息。⑦ 用 arch/i386/boot/tools/build.c 工具拼接 bzImage bootsectsetupvmlinux.bin。
想要探寻Linux内核的启动可以先从一个普通的用户态C程序的启动来入手。
C语言程序是如何启动并运行的
首先写一个最简单的C程序 test.c 1 #include stdio.h\ 2 3 int main() 4 { 5 printf(hello world ...\\n); 6 return 0; 7 }我们都知道C程序的入口时 main 函数那么下面来深入探索一下为什么是 main 函数首先使用 gcc 生成可执行文件
gcc test.c \-o test通过 file 命令我们可以查看到生成的test是一个 ELF 格式的文件具体来说是一个x86平台上的64位可执行文件。
使用 objdump 命令对该执行文件反汇编 查看反汇编代码我们发现汇编代码中多出了很多函数比如 _strart 函数等这说明gcc在链接时链接了一些库。 我们重新使用gcc编译一下test.c文件并显示出编译过程
gcc \-v test.c \-o test我们可以看到在编译链接过程中链接了 /usr/lib/… 下的 crti.o crt1.o 等文件而刚才提到的 _strart 函数就定义在 crt1.o 文件中。
使用 readelf 命令查看 elf 文件头section table 和各section信息。 在 ELF Header 中可以看到 Entry point address 一行也就是程序的入口地址。通过这个信息可以知道test程序的真正入口地址是0x400440我们在查看一下反汇编代码 可以看到0x400440地址处正是 _strart 函数。
由此可知_start 函数才是test程序首先执行的函数该函数执行完一系列初始化等工作后经过层层调用最终调用main()函数。因此在我们的C程序中main()函数时开始执行的入口。 _strart调用了__libc_start_mainplt同样后面是层层调用的过程到达main()函数。
现在我们搞明白了为什么main()函数是C程序的入口了那么test程序具体是如何执行的呢。我们可以通过 strace 跟踪test的执行过程。
strace ./test可以看到很多函数调用。第一个调用是 execve() 函数这是一个关键的系统调用它负责载入test可执行文件并运行。其中最关键的一步就是把用户态的 eip 寄存器实际上是它在内存中对应的值设置为elf文件中的入口点址也就是 _start() 函数。
由此可见程序从哪里开始执行取决于在刚开始执行的那一刻 eip 寄存器的值。而这个 eip 是由Linux内核设置的。具体过程如下① 用户在shell里运行 ./test ② shell这里是bash调用系统调用execve()③ execve() 陷入内核执行 sys_execve()把用户态的eip设置为_start()
④ 当系统调用执行完毕test进程开始运行时就从_start()开始执行⑤ test进程执行main()
BIOS、MBR、GRUB、setup
通过上面用户态C程序的启动我们可以看出程序真正的入口是 _strart 而main()只是因为被调用了所以才被看做是C程序的入口main只是一个符号如果 _strart 中调用的不是main那么C程序的入口就不是main了。
同理内核的真正入口也并不是 strart_kernel() 真正决定程序执行入口的是载入程序。对于用户态的C程序test来说Linux内核或者说bash负责设置test的入口点并且启动执行test程序。
那么谁来启动Linux内核呢基于KISS(keep it simple and stupid)原则一个简单的思路就是用一个更简单的内核或者说一段程序来启动真正的内核也就是BIOS(basic input/output system)BIOS通常存储在ROM上。
PC机器刚启动时x86 CPU 会自动从BIOS开始启动这是由硬件决定的。刚加电时寄存器CS里面的值是0xffffIP寄存器值为0于是CPU会从0xffff0处开始取指令通过下面命令可以看到0xffff0地址位于 System ROM 中也就是我们的BIOS。 BIOS通过 POST(Power On Self Test加电自检) 来加载硬件信息进行内存、CPU、主板等检测如果硬件设备正常工作BIOS会寻找硬盘第一个扇区引导扇区MBR中存储的数据512字节并使用MBR中的数据激活引导加载程序。这就是BIOS的作用后面的工作将有其它程序负责。
在x86平台上有两种保护模式32位页式寻址的保护模式和32位段式寻址的保护模式。32位页式寻址的保护模式要求系统中已经构造好页表。从16位实地址模式直接到32位页式寻址的保护模式是很有难度的因为要在16位实地址模式下构造页表。所以不妨这样来做先从16位实地址模式跳到32位段式寻址的保护模式再从32位段式寻址的保护模式跳到32位页式寻址的保护模式。也就是说我们需要这样一个程序负责从16位实地址模式跳到32位段式寻址的保护模式然后设置eip启动内核。这个程序就是 arch/i386/boot/setup.S。最后它汇编成setup程序。 事实上平时所见的压缩内核映象 bzImage 是由三部分组成的。可以从 arch/i386/boot/tools/build.c 中看到。build.c是用户态工具build的源代码后者用来把 bootsect(MBR)setup辅助程序和vmlinux.bin(压缩过的内核) 拼接成 bzImage。现在有了负责启动内核的setup程序但是谁来启动setup呢。因为MBR只有512个字节而且有64个字节来存放主分区表它的功能是非常有限的。所以还需要在setup和MBR之间再架一座桥梁。这就是引导程序引导程序用来引导setup程序。现有的引导程序如grublilo不仅功能强大而且还提供了人机交互的功能。
这样我们就可以归纳出来一系列流程如下
① CPU加电从0xffff0处执行BIOS可以理解为“硬件”引导BIOS。② BIOS执行扫描检测设备的操作然后将MBR读到物理地址为0x7c00处然后从MBR头部开始执行可以理解为BIOS引导MBR。③ MBR上的代码跳转到引导程序开始执行引导程序的代码例如grub引以理解为BIOS引导boot loader。④ 引导程序把内核映象包括bootsectsetupvmlinux读到内存中其中setup位于0x90200处如果是zImage则vmlinux.bin位于0x1000064K处。如果是bzImage则vmlinux.bin位于0x1000001M处。然后执行setup可以理解为boot loader引导setup。⑤ setup负责引导linux内核vmlinux.bin。 BIOS —— MBR —— boot loader —— setup —— kernel —— init 下面介绍下上面的这些名词都是什么含义。
BIOS加电自检 BIOS全称 Basic Input/Output System即基本输入输出系统它是一个被永久刻录在ROM中的软件加电自检是指 Power On Self TestPOST属于BIOS的主要组成部分。 计算机在接通电源后BIOS通过POST来加载硬件信息进行内存、CPU、主板等检测如果硬件设备正常工作BIOS会寻找硬盘第一个扇区中存储的数据并使用MBR中的数据激活引导加载程序。
MBR系统引导
第一个扇区512字节称为主引导记录。主引导记录分为3部分前446byte是引导信息后64byte是磁盘分区信息最后2byte是标志位。MBR的作用是找到 boot loader 。 MBR全程 Master Boot Recode是一种磁盘分区格式也是以此种格式的磁盘中0盘片0扇区中存储的一段记录——主引导记录。磁盘中扇区的大小为512byte主引导记录MBR占据第一个扇区的前446字节剩余的空间依次存储一个64字节的磁盘分区表和一个用于标识MBR是否有效的2字节的模数。 主引导记录MBR中包含一个实现引导加载功能的程序——Boot Loader。由于BIOS只能访问很少量的数据所以MBR中的引导加载程序其实只是一段初始程序的加载程序 Initial Program LoaderIPL这段程序唯一的功能就是定位并加载 Boot Loader 的主体程序。 加载引导分为两个阶段 第一阶段BIOS引导IPL获取 Boot Loader 主题程序在磁盘中的位置此时系统启动的控制权由BIOS转移到MBR 第二阶段Boot Loader 主题程序与操作系统对应的内核定位到内核文件所在的位置并将其加载到计算机内存中此时系统启动的控制权由MBR转移到内核。
GRUB
是一种 boot loader 用于加载kernel核心信息。
kernel
内核。
init进程
内核的第一个程序分为7个启动级别。
查看启动级别配置文件
cat /etc/inittab #查看启动级别相关的配置文件inti命令可以切换系统的启动级别
inti 0/1/2/3/4/5/60表示关机不能设置为开机默认启动级别 1表示单用户 2表示多用户无网络的3级别 3多用户命令行模式字符终端 4用于开发 5图形界面默认启动方式 6reboot不能设置为开机默认启动级别
runlevel #查看系统的启动级别vmlinux、vmlinuz、zImage、bzImage
vmlinuz是可引导的、压缩的内核“vm代表Virtual Memory”。Linux 支持虚拟内存不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存因此得名vm。vmlinuz是可执行的Linux内核它位于 /boot/vmlinuz它一般是一个软链接。vmlinuz的建立有两种方式。一是编译内核时通过make zImage创建然后通过cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage /boot/vmlinuz产生。zImage适用于小内核的情况它的存在是为了向后的兼容性。二是内核编译时通过命令make bzImage创建然后通过cp /usr/src/linux-2.4/arch/i386/linux/boot/bzImage /boot/vmlinuz产生。bzImage是压缩的内核映像bz表示big zImage。zImagevmlinuz和bzImagevmlinuz都是用gzip压缩的。它们不仅是一个压缩文件而且在这两个文件的开头部分内嵌有gzip解压缩代码。所以你不能用gunzip 或 gzip -dc解包vmlinuz。内核文件中包含一个微型的gzip用于解压缩内核并引导它。两者的不同之处在于老的zImage解压缩内核到低端内存第一个640KbzImage解压缩内核到高端内存1M以上。如果内核比较小那么可以采用zImage 或bzImage之一两种方式引导的系统运行时是相同的。大的内核采用bzImage不能采用zImage。vmlinux是未压缩的内核vmlinuz是vmlinux的压缩文件。
vmlinux是一个包含 linux kernel 的静态链接的可执行文件文件类型是linux接受的可执行文件格式之一(ELF、COFF或a.out)。
vmlinuz是可引导的压缩的linux内核“vm”代表的“virtual memory”。vmlinuz是vmlinux经过gzip和 objcopy(*) 制作出来的压缩文件。vmlinuz不仅是一个压缩文件而且在文件的开头部分内嵌有gzip解压缩代码。 通过file命令可以看到自己的vmlinuz是bzImage。通过前面我们知道zImage是vmlinuz经过gzip压缩后的文件适用于小内核512KB以内加载到内存的开始640KB处。bzimagenot bzizp but big是vmlinuz经过gzip压缩后的文件适用于大内核。在zImage和bzImage都可以通过解压缩提取出vmlinux只不过提取方法不同。
setup辅助程序
setup辅助程序主要进行了这些操纵以zImage为例首先把它从0x10000拷贝到0x1000调用BIOS功能查询硬件信息然后放在内存中供将来的内核使用然后建立临时的idt和gdt负责把16位实地址模式转化为32位段式寻址的保护模式。
我们可以查看下 arch/i386/boot/setup.S 中的汇编代码 \# Note that the short jump isnt strictly needed, although there are
\# reasons why it might be a good idea. It wont hurt in any case. movw $1, %ax \# protected mode (PE) bit lmsw %ax \# This is it! jmp flush\_instr flush\_instr: xorw %bx, %bx \# Flag to indicate a boot xorl %esi, %esi \# Pointer to real-mode code movw %cs, %si subw $DELTA\_INITSEG, %si shll $4, %esi \# Convert to 32-bit pointer
\# NOTE: For high loaded big kernels we need a
# jmpi 0x100000,\_\_KERNEL\_CS
#
# but we yet havent reloaded the CS register, so the default size
# of the target offset still is 16 bit.
\# However, using an operant prefix (0x66), the CPU will properly
# take our 48 bit far pointer. (INTeL 80386 Programmers Reference
# Manual, Mixing 16-bit and 32-bit code, page 16-6) .byte 0x66, 0xea \# prefix jmpi-opcode
code32: .long 0x1000 \# will be set to 0x100000 \# for big kernels .word \_\_KERNEL\_CSx86处理器提供了特殊的手段来访问大于1M的内存那就是在指令前加前缀0x66具体可见上面程序块。由于跳转地址与内核大小相关zImage和bzImage不一样所以用一个小技巧即把该指令当作数据处理。在计算机看来指令和数据是没什么区别的只要ip寄存器指向内存中某地址计算机就把地址中的数据当作指令来看待。
关于我们上面提到的所有包括testvmlinuxarch/i386/boot/compressed/vmlinuxsetupbootsect有什么区别与联系呢。这几个可执行文件都是由gcc编译生成的只是格式不一样。其中testvmlinuxarch/i386/boot/compressed/vmlinux都是elf32_i386格式的可执行文件setupbootsect是binary格式的可执行文件它们的区别如下 text是普通的elf32_i386可执行文件它的入口是 _start运行在用户态空间变量的地址都是32位页式寻址的保护模式的地址存放在用户态空间由shell负责装载。 vmlinux是未压缩的内核它的入口是startup_32(0x100000线性地址)运行在内核态空间变量的地址是32位页式寻址的保护模式的地址存放在内核态空间由内核自解压后启动运行。 arch/i386/boot/compressed/vmlinux是压缩后的内核它的入口地址是startup_32(0x100000线性地址)运行在32位段式寻址的保护模式下变量的地址是32位段式寻址的保护模式的地址由setup启动运行。 setup是装载内核的binary格式的辅助程序它的入口地址是begtext偏移地址为0运行时需要把cs段寄存器设置为0x9020运行在16位实地址模式下。变量的地址等于相对于代码段起始地址的偏移地址。由boot loader启动运行。 bootsect是MBR上的引导程序也为binary格式。它的入口地址是_start()由于装载到0x7c00处运行时需要把cs段寄存器设置为0x7c0。运行在16位实地址模式下。变量地址等于相对于代码段起始地址的偏移地址。由BIOS启动运行。
内核解压
在文件arch/i386/boot/compressed/head.S和arch/i386/kernel/head.S中都存在一个startup_32那么这两个startup_32有什么区别呢我们从内核的链接过程来看。
从内核的生成过程来看内核的链接主要有三步第一步是把内核的源代码编译成 .o 文件然后链接arch/i386/kernel/head.S生成vmlinux。注意这里的所有变量地址都是32位页寻址方式保护模式下的虚拟地址通常大小在3G以上。第二步将 vmlinux objcopy 成 arch/i386/boot/compressed/vmlinux.bin然后压缩最后作为数据编译成piggy.o。这时候在编译器看来piggy.o中还不存在startup_32。第三步把 head.omisc.o 和 piggy.o 链接生成 arch/i386/boot/compressed/vmlinux这一步链接的是arch/i386/boot/compressed/head.S。这时 arch/i386/kernel/head.S 中的 startup_32 被压缩作为一段普通的数据而被编译器忽视。这里的地址都是32位段寻址方式保护模式下的线性地址。setup执行完毕跳转到vmlinux.bin中的startup_32()是arch/i386/boot/compressed/head.S中的startup_32()这是一段自解压程序过程和内核生成的过程正好相反。这时CPU处在32位段寻址方式的保护模式下寻址范围从1M扩大到4G。只是没有页表。内核解压完毕。位于0x100000即1M处。最后执行一条跳转指令执行0x100000处的代码即 arch/i386/kernel/head.S 中的startup_32()代码。
页面映射
通过setup辅助程序现在从16位实地址模式过渡到了32位段式寻址保护模式。并且在 arch/i386/boot/compressed/head.S 帮助下实现了内核自解压从arch/i386/kernel/head.S中的startup_32开始执行。现在线性地址0x100000(1M) 处开始便是解压后的内核而startup_32的地址也是0xa00000。但是现在还没有开启页面映射所以必须引用变量的线性地址也就是变量的虚拟地址-PAGE_OFFSET。要想解决这个问题就要建立页表并开启页面映射。
在Linux中每个进程都拥有一个页表也就是说每个页表都对应着一个进程。通常情况下Linux通过fork()系统调用复制原进程来产生一个新的进程那么问题来了第一个进程是怎么来的呢。实际上第一个进程并不是复制出来的它是以全局变量的方式制造出来的即 init_thread_union也就是我们所说的0号进程swapper。swapper进程运行后调用start_kernel()整个程序就跑起来了。
有了第一个进程还要为该进程建立页表。为保证可移植性Linux采用了三级页表但是x86处理器只使用两级页表swapper的页表叫做swapper_pg_dir在arch/i386/kernel/head.S中我们可以看到这样的代码
/\* \* This is initialized to create an identity-mapping at 0-8M (for bootup \* purposes) and another mapping of the 0-8M area at virtual address \* PAGE\_OFFSET. \*/
.org 0x1000
ENTRY(swapper\_pg\_dir) .long 0x00102007 .long 0x00103007 .fill BOOT\_USER\_PGD\_PTRS-2,4,0 /\* default: 766 entries \*/ .long 0x00102007 .long 0x00103007 /\* default: 254 entries \*/ .fill BOOT\_KERNEL\_PGD\_PTRS-2,4,0这样便建好了页目录下面开始映射页表。我们知道一个页目录最多可以映射1024个页表每个页表可以映射4M虚拟地址也就是说总共可以映射4G虚拟地址空间。
由于不同进程的用户空间相互独立所以用户态进程的地址映射并不是连续的。但是所有进程共享内核态代码和数据。内核态虚拟地址从3G开始而内核代码和数据事实上是从物理地址0x100000开始本着KISS原则加上3G就作为对应的虚拟地址即可。由此可见对内核态代码和数据来说虚拟地址物理地址PAGE_OFFSET(3G)。
建表过程可见下面代码
/\* \* Initialize page tables \*/ movl $pg0-\_\_PAGE\_OFFSET,%edi /\* initialize page tables \*/ movl $007,%eax /\* 007 doesnt mean with right to kill, but PRESENTRWUSER \*/
2: stosl add $0x1000,%eax cmp $empty\_zero\_page-\_\_PAGE\_OFFSET,%edi jne 2b上面有一条注释 /* “007” doesn’t mean with right to kill, but PRESENTRWUSER */由于每个页表项有32位但其实只需保存物理地址的高20位就够了所以剩下的低12位可以用来表示页的属性。0x007正好表示PRESENTRWUSER在内存中可读写用户页面这样在用户态和内核态都可读写从而实现平滑过渡。
下面就要开启分页功能。开启页面映射后可以直接引用内核中的所有变量了。不过离start_kernel还有点距离要启动swapper进程得首先设置内核堆栈。看下面程序
/\* \* Enable paging 开启分页功能 \*/
3: movl $swapper\_pg\_dir-\_\_PAGE\_OFFSET,%eax movl %eax,%cr3 /\* set the page table pointer.. \*/ movl %cr0,%eax orl $0x80000000,%eax movl %eax,%cr0 /\* ..and set paging (PG) bit \*/ jmp 1f /\* flush the prefetch-queue \*/
1: movl $1f,%eax jmp \*%eax /\* make sure eip is relocated \*/
1: /\* Set up the stack pointer \*/ lss stack\_start,%esp #ifdef CONFIG\_SMP orw %bx,%bx jz 1f /\* Initial CPU cleans BSS \*/ pushl $0 popfl jmp checkCPUtype
1:
#endif CONFIG\_SMP/\* \* start system 32-bit setup. We need to re-do some of the things done \* in 16-bit mode for the real operations. \*/ call setup\_idtcall SYMBOL\_NAME(start\_kernel)链接脚本
在用户态内核会解析elf可执行文件的各个section并把它们映射到虚拟地址空间这些都不需要用户关心。但是在内核启动的时候映射section等这些工作都必须要内核自己来完成。除此之外内核还要负责对BSS段的变量进行初始化一般会初始化为0这些都需要内核知道section的具体位置。这就要求存在那么一个文件来指定各个section的虚拟地址。在内核源码树中arch/i386/kernel/vmlinux.lds.S 文件就是 linker scripts 连接器脚本。
在链接器脚本中. 表示当前 location counter 地址计数器的值默认为0。
#ifdef CONFIG\_X86\_32 . \ LOAD\_OFFSET LOAD\_PHYSICAL\_ADDR; phys\_startup\_32 \ ABSOLUTE(startup\_32 - LOAD\_OFFSET);
#else . \ \_\_START\_KERNEL; phys\_startup\_64 \ ABSOLUTE(startup\_64 - LOAD\_OFFSET);
#endif. __START_KERNEL; 表示地址计数器从__KERNEL_START(0xc00100000) 开始。
下面代码描述了 .text section 包含了哪些section
/\* Text and read-only data \*/ .text : AT(ADDR(.text) - LOAD\_OFFSET) { \_text \ .; \_stext \ .; /\* bootstrapping code \*/ HEAD\_TEXT TEXT\_TEXT SCHED\_TEXT CPUIDLE\_TEXT LOCK\_TEXT KPROBES\_TEXT SOFTIRQENTRY\_TEXT
#ifdef CONFIG\_RETPOLINE \_\_indirect\_thunk\_start \ .; \*(.text.\_\_x86.\*) \_\_indirect\_thunk\_end \ .;
#endif STATIC\_CALL\_TEXT ALIGN\_ENTRY\_TEXT\_BEGIN ENTRY\_TEXT ALIGN\_ENTRY\_TEXT\_END \*(.gnu.warning) } :text \0xcccc. ALIGN(PAGE_SIZE); 表示对齐方式。
链接器脚本指定了各个section的起始位置和结束位置。它还允许程序员在脚本中对变量进行赋值。这使内核可以通过 __initcall_start 和 __initcall_end之类的变量获得段的起始地址和结束地址从而对某些段进行操作。
下面是两个比较重要的section: bss section存放在代码里未初始化的全局变量最后初始化为0。 init sections所有只在初始化时调用的函数和变量包括所有在内核启动时调用的函数以及内核模块初始化时调用的函数。其中最特别的是.initcall .init section。通过__initcall_start和__initcall_end内核可以调用里面所有的函数。这些section在使用一次后就可以释放从而节省内存。
黑客网络安全如何学习
今天只要你给我的文章点赞我私藏的网安学习资料一样免费共享给你们来看看有哪些东西。
1.学习路线图 攻击和防守要学的东西也不少具体要学的东西我都写在了上面的路线图如果你能学完它们你去就业和接私活完全没有问题。
2.视频教程
网上虽然也有很多的学习资源但基本上都残缺不全的这是我自己录的网安视频教程上面路线图的每一个知识点我都有配套的视频讲解。
内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等都是网络安全入门必知必会的学习内容。 都打包成一块的了不能一一展开总共300多集
因篇幅有限仅展示部分资料需要点击下方链接即可前往获取
CSDN大礼包《黑客网络安全入门进阶学习资源包》免费分享
3.技术文档和电子书
技术文档也是我自己整理的包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点电子书也有200多本由于内容的敏感性我就不一一展示了。 因篇幅有限仅展示部分资料需要点击下方链接即可前往获取
CSDN大礼包《黑客网络安全入门进阶学习资源包》免费分享
4.工具包、面试题和源码
“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等感兴趣的同学不容错过。
还有我视频里讲的案例源码和对应的工具包需要的话也可以拿走。 因篇幅有限仅展示部分资料需要点击下方链接即可前往获取
CSDN大礼包《黑客网络安全入门进阶学习资源包》免费分享
最后就是我这几年整理的网安方面的面试题如果你是要找网安方面的工作它们绝对能帮你大忙。
这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的如果大家有好的题目或者好的见解欢迎分享。
参考解析深信服官网、奇安信官网、Freebuf、csdn等
内容特点条理清晰含图像化表示更加易懂。
内容概要包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF… 因篇幅有限仅展示部分资料需要点击下方链接即可前往获取
CSDN大礼包《黑客网络安全入门进阶学习资源包》免费分享