CSAPP - bomblab 作弊方式2: gdb jump 命令, 以及修改 jne 为 nop 指令

CSAPP - bomblab 作弊方式2: gdb jump 命令, 以及修改 jne 为 nop 指令

厌倦了在 gdb 中一步步顺序执行 bomb 可执行程序。为什么不能自行控制程序的执行呢?跳到特定的函数去执行,又或者把原本要执行的指令改掉,gdb 里能做到吗?

这一篇依然不是正经的 bomblab 解题记录, 而是基于bomblab的实验,练习 gdb 命令的使用,以及基于 gdb 中人工干预的代码执行流程, 在二进制编辑器中魔改二进制文件,修改后 bomb 可执行程序,无论输入什么,都能顺利过关。

在这里插入图片描述

文章目录

    • CSAPP - bomblab 作弊方式2: gdb jump 命令, 以及修改 jne 为 nop 指令
      • 0. 从 jump 到作弊
      • 1. 最简例子
      • 2. 替换 jne 指令为 nop
      • 3. 用二进制编辑器,修改 jne 为 nop 指令
      • 4. 使用 gdb jump 命令,在 bomb lab 上作弊
      • 5. 修改二进制文件,把 call phase_x 为 nop
      • 6. 总结

0. 从 jump 到作弊

使用 gdb 中的 jump 命令, 可以改变可执行程序原本的执行流程,跳转到我们希望执行的代码,然后继续执行。

例如, 在 bomblab 中, 跳过所有的 explode_bomb() 函数, 或跳过所有的 phase_x() 函数。

这种“作弊“有什么好处呢?假设某个测试程序只接受注册用户使用, 未注册用户会被直接结束,在未使用合法用户名字作为输入的情况下,如果仍然想要运行程序,并且可以接受在 gdb 里运行的方式, 那么在 gdb 中可以使用 jump 命令, 跳过用户名是否合法的检查。

1. 最简例子

准备,以及问题描述

test3.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>int main()
{printf("Please input your name: ");char name[50] = {0};scanf("%s", name);if (strcmp(name, "admin") == 0){printf("Welcome, you are valid user\n");}else{printf("Error: you are not valid user\n");exit(1);}printf("Every feature is avaialble for valid user\n");printf("Bye~\n");return 0;
}

第一种运行结果:

zz@Legion-R7000P% ./a.out
Please input your name: admin
Welcome, you are valid user
Every feature is avaialble for valid user
Bye~

第二种运行结果:

zz@Legion-R7000P% ./a.out
Please input your name: hello
Error: you are not valid user

我们希望在 gdb 中执行程序, 并且在输入 “hello” 的情况下,看到如下的输出:

Welcome, you are valid user
Every feature is avaialble for valid user
Bye~

解法

C代码的关键是 strcmp, 对应到汇编指令是:

   0x000055555555527a <+145>:   call   0x5555555550d0 <strcmp@plt>0x0000555555555283 <+154>:   lea    rax,[rip+0xda0]        # 0x55555555602a

因此在 0x000055555555527a 处设置断点, 并跳转到 偏移地址为154 的汇编语句,继续执行。

完整的 gdb 命令:

gdb
(gdb) file a.check_output
(gdb) start
(gdb) disas main
(gdb) b *0x000055555555527a
(gdb) c
(gdb) jump *0x0000555555555283

完整的gdb命令运行记录

(gdb) start
Temporary breakpoint 1 at 0x11f1
Starting program: /home/zz/work/zcnn/csapp/bomblab/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".Temporary breakpoint 1, 0x00005555555551f1 in main ()
(gdb) disassemble main
Dump of assembler code for function main:0x00005555555551e9 <+0>:     endbr640x00005555555551ed <+4>:     push   rbp0x00005555555551ee <+5>:     mov    rbp,rsp
=> 0x00005555555551f1 <+8>:     sub    rsp,0x400x00005555555551f5 <+12>:    mov    rax,QWORD PTR fs:0x280x00005555555551fe <+21>:    mov    QWORD PTR [rbp-0x8],rax0x0000555555555202 <+25>:    xor    eax,eax0x0000555555555204 <+27>:    lea    rax,[rip+0xdfd]        # 0x5555555560080x000055555555520b <+34>:    mov    rdi,rax0x000055555555520e <+37>:    mov    eax,0x00x0000555555555213 <+42>:    call   0x5555555550c0 <printf@plt>0x0000555555555218 <+47>:    mov    QWORD PTR [rbp-0x40],0x00x0000555555555220 <+55>:    mov    QWORD PTR [rbp-0x38],0x00x0000555555555228 <+63>:    mov    QWORD PTR [rbp-0x30],0x00x0000555555555230 <+71>:    mov    QWORD PTR [rbp-0x28],0x00x0000555555555238 <+79>:    mov    QWORD PTR [rbp-0x20],0x00x0000555555555240 <+87>:    mov    QWORD PTR [rbp-0x18],0x00x0000555555555248 <+95>:    mov    WORD PTR [rbp-0x10],0x00x000055555555524e <+101>:   lea    rax,[rbp-0x40]0x0000555555555252 <+105>:   mov    rsi,rax0x0000555555555255 <+108>:   lea    rax,[rip+0xdc5]        # 0x5555555560210x000055555555525c <+115>:   mov    rdi,rax0x000055555555525f <+118>:   mov    eax,0x00x0000555555555264 <+123>:   call   0x5555555550e0 <__isoc99_scanf@plt>0x0000555555555269 <+128>:   lea    rax,[rbp-0x40]0x000055555555526d <+132>:   lea    rdx,[rip+0xdb0]        # 0x5555555560240x0000555555555274 <+139>:   mov    rsi,rdx0x0000555555555277 <+142>:   mov    rdi,rax0x000055555555527a <+145>:   call   0x5555555550d0 <strcmp@plt>0x000055555555527f <+150>:   test   eax,eax0x0000555555555281 <+152>:   jne    0x5555555552c6 <main+221>0x0000555555555283 <+154>:   lea    rax,[rip+0xda0]        # 0x55555555602a0x000055555555528a <+161>:   mov    rdi,rax0x000055555555528d <+164>:   call   0x5555555550a0 <puts@plt>0x0000555555555292 <+169>:   lea    rax,[rip+0xdcf]        # 0x5555555560680x0000555555555299 <+176>:   mov    rdi,rax0x000055555555529c <+179>:   call   0x5555555550a0 <puts@plt>0x00005555555552a1 <+184>:   lea    rax,[rip+0xdea]        # 0x5555555560920x00005555555552a8 <+191>:   mov    rdi,rax0x00005555555552ab <+194>:   call   0x5555555550a0 <puts@plt>0x00005555555552b0 <+199>:   mov    eax,0x00x00005555555552b5 <+204>:   mov    rdx,QWORD PTR [rbp-0x8]0x00005555555552b9 <+208>:   sub    rdx,QWORD PTR fs:0x280x00005555555552c2 <+217>:   je     0x5555555552e4 <main+251>0x00005555555552c4 <+219>:   jmp    0x5555555552df <main+246>0x00005555555552c6 <+221>:   lea    rax,[rip+0xd79]        # 0x5555555560460x00005555555552cd <+228>:   mov    rdi,rax0x00005555555552d0 <+231>:   call   0x5555555550a0 <puts@plt>0x00005555555552d5 <+236>:   mov    edi,0x10x00005555555552da <+241>:   call   0x5555555550f0 <exit@plt>0x00005555555552df <+246>:   call   0x5555555550b0 <__stack_chk_fail@plt>0x00005555555552e4 <+251>:   leave0x00005555555552e5 <+252>:   ret
End of assembler dump.
(gdb) b *0x000055555555527a
Breakpoint 2 at 0x55555555527a
(gdb) c
Continuing.
Please input your name: helloBreakpoint 2, 0x000055555555527a in main ()
(gdb) jump 0x0000555555555283
Function "0x0000555555555283" not defined.
(gdb) jump *0x0000555555555283
Continuing at 0x555555555283.
Welcome, you are valid user
Every feature is avaialble for valid user
Bye~
[Inferior 1 (process 121417) exited normally]
(gdb)

2. 替换 jne 指令为 nop

相比于 jump 到一个地址, 改为 nop 命令的做法更简单和通用:

  • jump 到的新地址,不好判断
  • nop 指令是 0x90, 好改

如下是关键命令和gdb运行结果:

(gdb) start
(gdb) disas main
(gdb) set {char[2]}0x0000555555555281 = {0x90, 0x90}
(gdb) c
Continuing.
Please input your name: hello
Welcome, you are valid user
Every feature is avaialble for valid user
Bye~
[Inferior 1 (process 144989) exited normally]

其中 set 修改地址之前, disas 看到的关键代码:

   0x0000555555555274 <+139>:   mov    rsi,rdx0x0000555555555277 <+142>:   mov    rdi,rax0x000055555555527a <+145>:   call   0x5555555550d0 <strcmp@plt>0x000055555555527f <+150>:   test   eax,eax0x0000555555555281 <+152>:   jne    0x5555555552c6 <main+221>0x0000555555555283 <+154>:   lea    rax,[rip+0xda0]        # 0x55555555602a0x000055555555528a <+161>:   mov    rdi,rax

其中 jne 这行的地址 0x0000555555555281 被 set 拿去修改来着。

在 set 前、 set 后,可以分别检查 jne 和 nop 的代码的数值. 执行前:

(gdb) x /2b 0x0000555555555281
0x555555555281 <main+152>:      0x75    0x43
(gdb)
(gdb) disassemble 0x0000555555555281,0x0000555555555281+2
Dump of assembler code from 0x555555555281 to 0x555555555283:0x0000555555555281 <main+152>:       jne    0x5555555552c6 <main+221>

执行后:

(gdb) x /2b 0x0000555555555281
0x555555555281 <main+152>:      0xffffffffffffff90      0xffffffffffffff90
(gdb)
(gdb) disassemble 0x0000555555555281,0x0000555555555281+2
Dump of assembler code from 0x555555555281 to 0x555555555283:0x0000555555555281 <main+152>:       nop0x0000555555555282 <main+153>:       nop
End of assembler dump.

3. 用二进制编辑器,修改 jne 为 nop 指令

首先要确定 jne 指令的位置。 在 gdb 中, 如果没有输入 start 命令, 此时 disas main 看到的结果, 和在执行 start 命令后再执行 disas main, 是有一个偏移地址的。

start 之前,执行 disas main:

   0x0000000000001274 <+139>:   mov    rsi,rdx0x0000000000001277 <+142>:   mov    rdi,rax0x000000000000127a <+145>:   call   0x10d0 <strcmp@plt>0x000000000000127f <+150>:   test   eax,eax0x0000000000001281 <+152>:   jne    0x12c6 <main+221>

start 之后, 执行 disas main:

   0x0000555555555274 <+139>:   mov    rsi,rdx0x0000555555555277 <+142>:   mov    rdi,rax0x000055555555527a <+145>:   call   0x5555555550d0 <strcmp@plt>0x000055555555527f <+150>:   test   eax,eax0x0000555555555281 <+152>:   jne    0x5555555552c6 <main+221>

其中第一列有差别。而当我们在二进制编辑器中, 修改 jne 汇编指令为 nop 时, 显然程序没有运行, 是要根据 start 之前的 disas main 得到的地址, 去修改。

因此,需要把地址为 0x1281, 0x1282 的内容, 从原来的 0x75, 0x43, 修改为 0x90, 0x90.

使用 vim 修改二进制的步骤

cp a.out aa.out
vim aa.out
:%!xxd
找到 1280 行, 修改75 43 为90 90
:%!xxd -r    # 转换回到二进制,否则结果不对
:wq          # 保存

其中 %!xxd -r 是关键。

使用https://hexed.it

成功了,很简单。

zz@Legion-R7000P% ./aa.out
Please input your name: he
Welcome, you are valid user
Every feature is avaialble for valid user
Bye~

4. 使用 gdb jump 命令,在 bomb lab 上作弊

跳过每个 phase_x() 函数的执行, 直接运行 phase_x() 之后的“恭喜通关”汇编指令.

需要收集 call phase_x 的地址, 然后设置断点。以 phase_1 为例:

(gdb) disas main
Dump of assembler code for function main:
=> 0x0000000000400da0 <+0>:     push   rbx0x0000000000400da1 <+1>:     cmp    edi,0x10x0000000000400da4 <+4>:     jne    0x400db6 <main+22>0x0000000000400da6 <+6>:     mov    rax,QWORD PTR [rip+0x20299b]        # 0x603748 <stdin@@GLIBC_2.2.5>0x0000000000400dad <+13>:    mov    QWORD PTR [rip+0x2029b4],rax        # 0x603768 <infile>0x0000000000400db4 <+20>:    jmp    0x400e19 <main+121>0x0000000000400db6 <+22>:    mov    rbx,rsi0x0000000000400db9 <+25>:    cmp    edi,0x20x0000000000400dbc <+28>:    jne    0x400df8 <main+88>0x0000000000400dbe <+30>:    mov    rdi,QWORD PTR [rsi+0x8]0x0000000000400dc2 <+34>:    mov    esi,0x4022b40x0000000000400dc7 <+39>:    call   0x400c10 <fopen@plt>0x0000000000400dcc <+44>:    mov    QWORD PTR [rip+0x202995],rax        # 0x603768 <infile>0x0000000000400dd3 <+51>:    test   rax,rax0x0000000000400dd6 <+54>:    jne    0x400e19 <main+121>0x0000000000400dd8 <+56>:    mov    rcx,QWORD PTR [rbx+0x8]0x0000000000400ddc <+60>:    mov    rdx,QWORD PTR [rbx]0x0000000000400ddf <+63>:    mov    esi,0x4022b60x0000000000400de4 <+68>:    mov    edi,0x10x0000000000400de9 <+73>:    call   0x400c00 <__printf_chk@plt>0x0000000000400dee <+78>:    mov    edi,0x80x0000000000400df3 <+83>:    call   0x400c20 <exit@plt>0x0000000000400df8 <+88>:    mov    rdx,QWORD PTR [rsi]0x0000000000400dfb <+91>:    mov    esi,0x4022d30x0000000000400e00 <+96>:    mov    edi,0x10x0000000000400e05 <+101>:   mov    eax,0x00x0000000000400e0a <+106>:   call   0x400c00 <__printf_chk@plt>0x0000000000400e0f <+111>:   mov    edi,0x80x0000000000400e14 <+116>:   call   0x400c20 <exit@plt>0x0000000000400e19 <+121>:   call   0x4013a2 <initialize_bomb>0x0000000000400e1e <+126>:   mov    edi,0x4023380x0000000000400e23 <+131>:   call   0x400b10 <puts@plt>0x0000000000400e28 <+136>:   mov    edi,0x4023780x0000000000400e2d <+141>:   call   0x400b10 <puts@plt>0x0000000000400e32 <+146>:   call   0x40149e <read_line>0x0000000000400e37 <+151>:   mov    rdi,rax0x0000000000400e3a <+154>:   call   0x400ee0 <phase_1>0x0000000000400e3f <+159>:   call   0x4015c4 <phase_defused>0x0000000000400e44 <+164>:   mov    edi,0x4023a80x0000000000400e49 <+169>:   call   0x400b10 <puts@plt>0x0000000000400e4e <+174>:   call   0x40149e <read_line>0x0000000000400e53 <+179>:   mov    rdi,rax0x0000000000400e56 <+182>:   call   0x400efc <phase_2>0x0000000000400e5b <+187>:   call   0x4015c4 <phase_defused>0x0000000000400e60 <+192>:   mov    edi,0x4022ed0x0000000000400e65 <+197>:   call   0x400b10 <puts@plt>0x0000000000400e6a <+202>:   call   0x40149e <read_line>0x0000000000400e6f <+207>:   mov    rdi,rax0x0000000000400e72 <+210>:   call   0x400f43 <phase_3>0x0000000000400e77 <+215>:   call   0x4015c4 <phase_defused>0x0000000000400e7c <+220>:   mov    edi,0x40230b0x0000000000400e81 <+225>:   call   0x400b10 <puts@plt>0x0000000000400e86 <+230>:   call   0x40149e <read_line>0x0000000000400e8b <+235>:   mov    rdi,rax0x0000000000400e8e <+238>:   call   0x40100c <phase_4>0x0000000000400e93 <+243>:   call   0x4015c4 <phase_defused>0x0000000000400e98 <+248>:   mov    edi,0x4023d80x0000000000400e9d <+253>:   call   0x400b10 <puts@plt>0x0000000000400ea2 <+258>:   call   0x40149e <read_line>0x0000000000400ea7 <+263>:   mov    rdi,rax0x0000000000400eaa <+266>:   call   0x401062 <phase_5>0x0000000000400eaf <+271>:   call   0x4015c4 <phase_defused>0x0000000000400eb4 <+276>:   mov    edi,0x40231a0x0000000000400eb9 <+281>:   call   0x400b10 <puts@plt>0x0000000000400ebe <+286>:   call   0x40149e <read_line>0x0000000000400ec3 <+291>:   mov    rdi,rax0x0000000000400ec6 <+294>:   call   0x4010f4 <phase_6>0x0000000000400ecb <+299>:   call   0x4015c4 <phase_defused>0x0000000000400ed0 <+304>:   mov    eax,0x00x0000000000400ed5 <+309>:   pop    rbx0x0000000000400ed6 <+310>:   ret
End of assembler dump.
(gdb) b *0x0000000000400e3a
Breakpoint 2 at 0x400e3a: file bomb.c, line 74.
(gdb) c
Continuing.
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
helloBreakpoint 2, 0x0000000000400e3a in main (argc=<optimized out>, argv=<optimized out>) at bomb.c:74
74          phase_1(input);                  /* Run the phase               */
(gdb) jump *0x0000000000400e3f
Continuing at 0x400e3f.
Phase 1 defused. How about the next one?

完整gdb命令:

start
b *0x0000000000400e3a
b *0x0000000000400e56
b *0x0000000000400e72
b *0x0000000000400e8e
b *0x0000000000400eaa
b *0x0000000000400ec6
c
1
jump *($rip+5)
2
jump *($rip+5)
3
jump *($rip+5)
4
jump *($rip+5)
5
jump *($rip+5)
6
jump *($rip+5)

其中 jump *($rip+5) 是跳过5个字节,也就是 call phase_x 。 call phase_x 对应了5个字节。

执行的部分记录:

Breakpoint 3, 0x0000000000400e56 in main (argc=<optimized out>, argv=<optimized out>) at bomb.c:82
82          phase_2(input);
(gdb) jump  *($rip + 5)
Continuing at 0x400e5b.
That's number 2.  Keep going!
helloBreakpoint 4, 0x0000000000400e72 in main (argc=<optimized out>, argv=<optimized out>) at bomb.c:89
89          phase_3(input);
(gdb) sd
Undefined command: "sd".  Try "help".
(gdb) jump  *($rip + 5)
Continuing at 0x400e77.
Halfway there!
helloBreakpoint 5, 0x0000000000400e8e in main (argc=<optimized out>, argv=<optimized out>) at bomb.c:95
95          phase_4(input);
(gdb) jump  *($rip + 5)
Continuing at 0x400e93.
So you got that one.  Try this one.
helloBreakpoint 6, 0x0000000000400eaa in main (argc=<optimized out>, argv=<optimized out>) at bomb.c:101
101         phase_5(input);
(gdb) jump  *($rip + 5)
Continuing at 0x400eaf.
Good work!  On to the next...
helloBreakpoint 7, 0x0000000000400ec6 in main (argc=<optimized out>, argv=<optimized out>) at bomb.c:108
108         phase_6(input);
(gdb) jump  *($rip + 5)
Continuing at 0x400ecb.
Congratulations! You've defused the bomb!
[Inferior 1 (process 165752) exited normally]

5. 修改二进制文件,把 call phase_x 为 nop

首先进入gdb, 在执行 start 命令之前, 从反汇编结果中获取所有的 call phase_x 指令的地址:

   0x0000000000400e3a <+154>:   call   0x400ee0 <phase_1>0x0000000000400e56 <+182>:   call   0x400efc <phase_2>0x0000000000400e72 <+210>:   call   0x400f43 <phase_3>0x0000000000400e8e <+238>:   call   0x40100c <phase_4>0x0000000000400eaa <+266>:   call   0x401062 <phase_5>0x0000000000400ec6 <+294>:   call   0x4010f4 <phase_6>

分别查看每个地址开头的5个字节的内容。这一步可选,作为后续在二进制编辑器中的对照

(gdb) x /5xb 0x0000000000400e3a
0x400e3a <main+154>:    0xe8    0xa1    0x00    0x00    0x00(gdb) x /5xb 0x0000000000400e56
0x400e56 <main+182>:    0xe8    0xa1    0x00    0x00    0x00(gdb) x /5xb 0x0000000000400e72
0x400e72 <main+210>:    0xe8    0xcc    0x00    0x00    0x00(gdb) x /5xb 0x0000000000400e8e
0x400e8e <main+238>:    0xe8    0x79    0x01    0x00    0x00(gdb) x /5xb 0x0000000000400eaa
0x400eaa <main+266>:    0xe8    0xb3    0x01    0x00    0x00(gdb) x /5xb 0x0000000000400ec6
0x400ec6 <main+294>:    0xe8    0x29    0x02    0x00    0x00

然后使用 vim 或 hexed.it, 找到上述每个地址,每个地址处连续5个字节,统统改为 0x90 (nop指令).

这时候的坑是, 并没有找到 0x400e3a 的地址, 只找到 0x00000e3a 这样的地址。大概估计一下,应该是减掉了固定的地址偏移

call phase_1 修改为 nop 前:
在这里插入图片描述
call phase_1 修改为 nop 后:
在这里插入图片描述

执行结果: 随便怎么输入,都能到达最终结果:

zz@Legion-R7000P% ./bomb2
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
hello
Phase 1 defused. How about the next one?
yes
That's number 2.  Keep going!
s
Halfway there!
s
So you got that one.  Try this one.
s
Good work!  On to the next...
ff
Congratulations! You've defused the bomb!

在这里插入图片描述

6. 总结

  1. 在 gdb 中, 你不必循规蹈矩的从头执行到尾:
    • 可以用 jump 命令跳转到希望之星的地方,例如 (gdb) jump *0x0000555555555283
    • 可以用 set 命令修改内存,把即将执行的指令改为想执行的指令, 例如修改 call phase_x 为 nop 指令,也就是把 e8 开头的5个字节,全都改为0x90
  2. 为了运行修改后的程序
    • 可以在 gdb 里修改, 也就是前面提到的做法
    • 你也可以在二进制文件中修改, 但这往往还是需要搭配 gdb 反汇编,先获得要修改的指令的地址
  3. 为了获得需要修改的指令的地址
    • 我目前的经验,是在 start 之前执行 disas xxx_fucntion, 对于自己单独写的代码的小实验,有效
    • 但是 CSAPP 官方的 bomb 可执行程序, start 前和start 后,地址一样,并且在二进制编辑的时候,地址又减低
  4. 为了完成逆向工程的一些想法,不必拘泥于原始的可执行文件
    • 通过自行构造 C 代码和可执行程序,完成了原型,很容易扩展到原始的 bomb 文件上

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

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

相关文章

linux环境下安装postgresql

PostgreSQL: Linux downloads (Red Hat family)postgresql官网 PostgreSQL: Linux downloads (Red Hat family) 环境&#xff1a; centos7 postgresql14 选择版本 执行启动命令 配置远程连接文件 vi /var/lib/pqsql/14/data/postgresql.conf 这里将listen_addresses值由lo…

【位运算】【二分查找】【C++算法】100160价值和小于等于 K 的最大数字

作者推荐 【动态规划】【字符串】扰乱字符串 本文涉及的基础知识点 二分查找算法合集 位运算 LeetCode100160. 价值和小于等于 K 的最大数字 给你一个整数 k 和一个整数 x 。 令 s 为整数 num 的下标从1 开始的二进制表示。我们说一个整数 num 的 价值 是满足 i % x 0 且…

阿里云ingress配置时间超时的参数

一、背景 在使用阿里云k8s集群的时候&#xff0c;内网API网关&#xff0c;刚开始是用的是Nginx&#xff0c;后面又搭建了ingress。 区别于nginx配置&#xff0c;ingress又该怎么设置参数呢&#xff1f;比如http超时时间等等。 本文会先梳理nginx是如何配置&#xff0c;再对比…

优雅的删除链表元

王有志&#xff0c;一个分享硬核Java技术的互金摸鱼侠加入Java人的提桶跑路群&#xff1a;共同富裕的Java人 在数据结构&#xff1a;链表中&#xff0c;我们实现了链表的删除方法&#xff0c;但代码看起来并不“优雅”&#xff0c;那么今天我们就来尝试使用多种方法&#xff0c…

windows安装conda环境,开发openai应用准备,运行第一个ai程序

文章目录 前言一、windows创建openai开发环境二、国内代理方式访问openai的方法&#xff08;简单方法&#xff09;三、测试运行第一个openai程序总结 前言 作者开发第一个openai应用的环境准备、第一个openai程序调用成功&#xff0c;做个记录&#xff0c;希望帮助新来的你。 …

31 树的存储结构一

无法直接用数组表示树的逻辑结构&#xff0c;但是可以设计结构体数组对节点间的关系进行描述&#xff1a;【如表】 这样做的问题&#xff1a; 可以利用 组织链表 parent指针&#xff1a; 注意&#xff1a;树结点在 组织链表 中的位置不代表树的任何逻辑关系 树的架构图&#xf…

从0开始学Git指令(3)

从0开始学Git指令 因为网上的git文章优劣难评&#xff0c;大部分没有实操展示&#xff0c;所以打算自己从头整理一份完整的git实战教程&#xff0c;希望对大家能够起到帮助&#xff01; 远程仓库 Git是分布式版本控制系统&#xff0c;同一个Git仓库&#xff0c;可以分布到不…

java客户端连接redis并设置序列化处理

1、导入依赖 <!--继承父依赖--> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.12.RELEASE</version><relativePath/> <!-- lookup paren…

服务器出现500、502、503错误的原因以及解决方法

服务器我们经常会遇到访问不了的情况有的时候是因为我们服务器被入侵了所以访问不了&#xff0c;有的时候是因为出现了服务器配置问题&#xff0c;或者软硬件出现问题导致的无法访问的问题&#xff0c;这时候会出现500、502、503等错误代码。基于以上问题我们第一步可以先重启服…

【Py/Java/C++三种语言详解】LeetCode每日一题240114【链表】LeetCode83、删除排序链表中的重复节点

文章目录 题目链接题目描述解题思路代码PythonJavaC时空复杂度 华为OD算法/大厂面试高频题算法练习冲刺训练 题目链接 LeetCode83、删除排序链表中的重复节点 题目描述 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返…

Android json功能解析

1. 简介 JAVAScript Object Notation是一种轻量级的数据交换格式具有良好的可读和便于快速编写的特性。业内主流技术为其提供了完整的解决方案&#xff08;有点类似于正则表达式 &#xff0c;获得了当今大部分语言的支持&#xff09;。  JSON采用兼容性很高的文本格式&#xf…

第 380 场周赛 解题报告 | 珂学家 | 数位DP 二分 + 字符串Hash

前言 整体评价 感觉T3更难些&#xff0c;T4太直接了&#xff0c;一般的KMP/StringHash基本就够用了。 上周T4出数位DP&#xff0c;估计是为T3打了一个铺垫。 A. 最大频率元素计数 思路: 模拟即可 class Solution {public int maxFrequencyElements(int[] nums) {Map<Int…

qt5.14.2配置opencv4.5.5

使用环境&#xff1a;windows&#xff0c;opencv4.5.5&#xff0c;qt5.14.2&#xff0c;msvc编译器 这里的opencv文件是已经编译好了&#xff0c;在qt工程中配置就可使用&#xff0c;编译器得是msvc才行&#xff0c;MinGW不管用。 资源地址&#xff1a;https://download.csdn.…

【深度学习每日小知识】Computer Vision 计算机视觉

计算机视觉是人工智能的一个领域&#xff0c;涉及算法和系统的开发&#xff0c;使计算机能够解释、理解和分析来自周围世界的视觉数据。这包括从静态图像到视频流甚至 3D 环境的一切。 使用对象检测和特征提取等方法&#xff0c;计算机视觉本质上需要从视觉输入中提取有用信息…

2023 IoTDB Summit:天谋科技高级开发工程师苏宇荣《汇其流:如何用 IoTDB 流处理框架玩转端边云融合》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

华为手机备份全过程(保姆级问题解决方案)

手机备份 前言主体信息备份一、关闭windows安全中心的内存完整性二、开启 USB 调试&#xff0c;尝试使用 ADB 连接三、开始备份 微信备份QQ备份写在最后遗留问题 前言 我的手机是荣耀 20&#xff0c;虽然不是华为&#xff0c;但系统还是鸿蒙的系统&#xff08;毕竟那阵荣耀还是…

reactNative0.71版本的使用

开发环境配置 参考reactNative 官网 版本选中0.71 打包配置 1. IOS 打开项目 -> 进入ios目录->执行命令 pod install ->项目名称.xcworkspace -> 使用xcode打开->配置证书 证书配置截图如下 &#x1f4a1;tips&#xff1a;TARGETS目录下会有多个文件&#x…

【ArcGIS Pro微课1000例】0057:未安装所需的Microsoft驱动程序

文章目录 一、错误提示二、解决办法1. Excel转表2. Excel转csv一、错误提示 ArcGIS Pro添加Excel数据时,提示未安装所需的Microsoft驱动程序,如下图所示: 二、解决办法 1. Excel转表 在选择输入表时,可能会提示未安装所需的 Microsoft 驱动程序。 这是因为要在 ArcGIS P…

为什么光刻要用黄光

光刻是集成电路&#xff08;IC或芯片&#xff09;制造中的重要工艺之一。简单来说&#xff0c;它是通过使用光掩膜和光刻胶在基板上复制电路图案的过程。 基板将涂覆硅二氧化层绝缘层和光刻胶。光刻胶在被紫外光照射后可以容易地用显影剂溶解&#xff0c;然后在腐蚀后&#xf…

控制网页的灰度显示

1.代码&#xff1a; 普通网页 <style>html {filter: grayscale(100%);}</style> 或是:webkit内核浏览器写法 <style>html {-webkit-filter: grayscale(100%)}</style> 2.说明&#xff1a; grayscale(amount) :进行灰度转换。 amount转换值的大小&…