《Python 黑帽子》学习笔记 - 网络基础2- Day 3

上一篇笔记的补充:

为了验证 socket 状态的问题,可以在主机操作系统和 VMware kali 客户机系统运行TCP 服务端和客户端程序,通过查看两个主机的网络连接来进行理解。

我在主机操作系统(Win7)用 phpstudy 搭建了 Web 站点,客户机通过浏览器或 netcat 连接服务端。分别在两台机器上用 netstat 查看 TCP 连接。注意的是,VMware 网卡要使用桥接模式,这样主机系统和客户机系统才能在同一个网段里相互访问,用 netstat 查看的连接是两个系统直连的结果。

Windows 用 netstat -bno -p TCP |find ":80"

Kali 用 netstat -nva --tcp

C:\Users\Administrator
λ netstat -bno -p TCP |find ":80"
  TCP    192.168.1.3:80         192.168.1.111:45722    ESTABLISHED     7760
root@kali:~# netstat -nva --tcp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 192.168.1.111:45716     192.168.1.3:80          TIME_WAIT  

TCP 客户端

步骤:

  1. 建立 socket 对象。socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  2. 向服务端发起连接。client.connect((target_host, target_port))
  3. 向服务端发送数据。 client.send()
  4. 接收服务端数据。 client.recv()
  5. 关闭 socket。client.close()

Python2 代码:

import socket

target_host = "www.baidu.com"
target_port = 80

# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# connect the client
client.connect((target_host, target_port))

# send some data
client.send("GET / HTTP/1.1\r\nHost:baidu.com\r\n\r\n")

# receive data
response = client.recv(4096)

print response

在 Python3 下,运行上述代码,调用 socket.send 出现 TypeError: a bytes-like object is required, not 'str' 错误。

(venv-p3) root@kali:~/Black-Hat-Python/codes# python tcp-client.py 
Traceback (most recent call last):
  File "tcp-client.py", line 10, in <module>
    client.send("GET / HTTP/1.1\r\nHOST:baidu.com\r\n\r\n")
TypeError: a bytes-like object is required, not 'str'

因为 python2 和 python3 版本差异,python3 对文本和二进制数据做了更清晰的区分。文本用 unicode 编码,为 str 类型,二进制数据则为 bytes 类型。

python有两种类型转换的函数 encode(), decode()
* encode(编码),可以将 str 类型编码为 bytes 。
* decode(译码),可以将 bytes 类型转换为 str 类型。

这里的错误是因为要求 bytes 类型却给了 str 类型,send() 函数是传输字节流。解决方法:

str.encode()bytes.decode() 函数

request_heads = "GET / HTTP/1.1\r\nHOST:192.168.1.3\r\n\r\n"
client.send(str.encode(request_heads))

print(bytes.decode(response))

或者在字符串前加上 b

b"GET / HTTP/1.1\r\nHOST:192.168.1.3\r\n\r\n"

修改的 Python3 代码:

#!/usr/bin/env python3
# -*- code: utf-8 -*-

import socket

target_host = "192.168.1.3"
target_port = 80

# create a socket object
# AF_INET means IPv4 address, socket.SOCK_STREAM means TCP protocol
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# connect the server
# param is a tuple, (host, port)
client.connect((target_host, target_port))

# send HTTP request head with GET model
# encode str type to byte type
request_heads = "GET / HTTP/1.1\r\nHOST:192.168.1.3\r\n\r\n"
client.send(str.encode(request_heads))

# receive datas
response = client.recv(4096)

# decode byte type to str type 
print(bytes.decode(response))

# close the socket
client.close()

运行结果:

(venv-p3) root@kali:~/Black-Hat-Python/codes# python tcp-client.py 
HTTP/1.1 200 OK
Date: Tue, 20 Mar 2018 16:02:11 GMT
Server: Apache/2.4.23 (Win32) OpenSSL/1.0.2j PHP/5.2.17
X-Powered-By: PHP/5.2.17
Content-Length: 12
Content-Type: text/html

Hello World

TCP 服务端

步骤:

  1. 创建 TCP 套接字。socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  2. 确定服务端的 IP 和 Port。绑定套接字。 bind((bing_ip, bind_ports))
  3. 启动监听。listen(5)
  4. 服务端进入循环接收客户端的连接。accept()
  5. 客户端连接后,保存客户端套接字和地址等信息。以客户端处理函数为回调函数,创建新的线程。
  6. 运行回调函数,处理服务端与客户端的数据交互。

Python2 代码:

import socket
import threading

bind_ip = "0.0.0.0"
bind_port = 9999

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip, bind_port))
server.listen(5)

print "[*] Listening on %s:%d" % (bind_ip, bind_port)

def handle_client(client_socket):

    # send something
    client_socket.send("Connected\r\n")

    # print out what the client sends
    request = client_socket.recv(1024)

    print "[*] Reveived: %s" % request

    # send back a packet
    client_socket.send("ACK!\r\n")

    client_socket.close()

while True:
    client, addr = server.accept()

    print "[*] Accepted connection from: %s:%d" % (addr[0], addr[1])

    # spin up our client thread to handle incoming data
    client_handler = threading.Thread(target=handle_client,args=(client,))
    client_handler.start()

Python3 代码:

#!/usr/bin/env python3
# -*- code: utf-8 -*-

import socket
import threading

bind_ip = "0.0.0.0"  # all ip
bind_port = 9999     # greater than 1024 is better

# create socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bind ip and port
server.bind((bind_ip, bind_port))  
# start listen, max socket nums 5
server.listen(5)

print("[*] Listening on %s:%d"%(bind_ip, bind_port))

def handle_client(client_socket):
    request = client_socket.recv(1024)

    print("[*] Received: %s" % request)

    client_socket.send(b"ACK!\r\n")

    client_socket.close()

while True:
    # accept a client connection.
    client,addr = server.accept()

    print("[*] Accepted connection from: %s:%d" % (addr[0], addr[1]))

    # create a thread to handle incoming connection.
    client_handler = threading.Thread(target=handle_client, args=(client,))
    # start the thread.
    client_handler.start()

运行结果,服务端:

root@kali:~# cd Black-Hat-Python/
root@kali:~/Black-Hat-Python# source venv-p3/bin/activate
(venv-p3) root@kali:~/Black-Hat-Python# cd codes/
(venv-p3) root@kali:~/Black-Hat-Python/codes# ls
tcp-client.py  tcp-server.py  test.py
(venv-p3) root@kali:~/Black-Hat-Python/codes# python tcp-server.py 
[*] Listening on 0.0.0.0:9999
[*] Accepted connection from: 127.0.0.1:50512
[*] Received: b'aaaa\n'

用 nc 连接:

root@kali:~# nc 127.0.0.1 9999
aaaa
ACK!

UDP 客户端和服务端

UDP 协议和 TCP 协议不同的是,UDP 是面向非连接的,不需要和服务端先建立连接,直接可以和服务端进行数据传输。

客户端 Python2 代码:

import socket

target_host = "127.0.0.1"
target_port = 80

# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# send some data
client.sendto("AAABBBCCC",(target_host, target_port))

# recieve some data
data, addr = client.recvfrom(4096)

print data

客户端 Python3 代码:

import socket

target_host = "127.0.0.1"
target_port = 9998

# create a socket object
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# send some data
client.sendto(b"AAABBBCCC",(target_host, target_port))

# recieve some data
data, addr = client.recvfrom(4096)

print data

服务端 Python3 代码:

#!/usr/bin/env python3
# -*- code: utf-8 -*-

import socket
import threading

bind_ip = "0.0.0.0"  # all ip
bind_port = 9998     # greater than 1024 is better

# create socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# bind ip and port
server.bind((bind_ip, bind_port))  

print("[*] Binding on %s:%d" % (bind_ip, bind_port))

while True:
    # receive datas:
    data, addr = server.recvfrom(1024)
    print('Received from %s:%s: %s.' % (addr[0],addr[1], data))
    server.sendto(b'Hello, %s!' % data, addr)

运行结果,服务端:

(venv-p3) root@kali:~/Black-Hat-Python/codes# python udp-server.py 
[*] Binding on 0.0.0.0:9998
Received from 127.0.0.1:60558: b'AAABBBCCC'.
Received from 127.0.0.1:45700: b'AAABBBCCC'.

客户端:

root@kali:~/Black-Hat-Python/codes# python udp-client.py 
Hello, AAABBBCCC!

参考资料

  • 作者把黑帽子的代码转为了 Python3 https://my.oschina.net/oby/blog/804064
  • 廖雪峰 TCP 编程

你可能感兴趣的:(学习笔记)