python黑帽子学习笔记(一)

1 网络传输中的数据对象类型

网络传输的数据是字节对象(bytes)的主要原因是因为计算机和网络是基于二进制的。

在计算机和网络中,数据的最小单位是比特(bit),即二进制的 0 和 1。所有的数据在传输和存储时,都需要被转换为比特流(bit stream)。由于字节(byte)是计算机中常用的基本存储单元,一个字节由 8 个比特组成,因此数据通常以字节为单位进行传输和存储。

字节对象(bytes)是 Python 中专门用于表示原始二进制数据的数据类型。它是一个不可变的序列,由 8 位二进制组成,表示一个字节的数据。

在网络通信中,数据需要通过网络传输到远程主机或从远程主机接收,而网络通信使用的底层协议(如 TCP 和 UDP)是基于字节流的。因此,数据在进行网络传输时,需要被转换为字节对象,以便在网络中传输。

使用字节对象的好处是:

  1. 二进制表示: 字节对象直接以二进制形式表示数据,没有编码的附加信息,使得数据的传输和解析更高效。

  2. 兼容性: 字节对象可以表示各种类型的数据,包括文本、图像、音频、视频等,不受编码方式和字符集的限制。

  3. 数据完整性: 在网络传输过程中,数据可能会经历多个网络设备和协议层的处理,使用字节对象可以确保数据在传输过程中的完整性。

在 Python 中,当进行网络编程时,使用字节对象可以更好地处理和表示网络传输的数据,确保数据在不同平台和系统之间的正确传输和解析。同时,在 Python 3 中,字符串(str)和字节对象(bytes)是明确区分的,这使得数据的处理更加明确和可靠。

2 开发一个TCP代理

4个主要功能:把本地设备和远程设备之间的通信过程显示到屏幕上(hexdump函数);从本地设备或远程设备的入口socket接收数据(receive_from函数);控制远程设备和本地设备之间的流量方向(proxy_handler函数);最后,还需要创建一个监听socket,并把它传给我们的proxy_handler(server_loop函数)。

代码

代码块1 hexdump函数

把本地设备和远程设备之间的通信过程显示到屏幕上

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
import sys
import socket
import threading


HEX_FILTER = ''.join(
[(len(repr(chr(i))) == 3) and chr(i) or '.' for i in range(256)]
)

def hexdump(src, length=16, show=True):
if isinstance(src, bytes):
src = src.decode()

results = list()
for i in range(0, len(src), length):
word = str(src[i:i+length])
printable = word.translate(HEX_FILTER)
hexa = ' '.join([f'{ord(c):02X}' for c in word])
hexwidth = length*3
results.append(f'{i:04x} {hexa:<{hexwidth}} {printable}')
if show:
for line in results:
print(line)
else:
return results

这段代码定义了一个函数 hexdump,用于以十六进制和 ASCII 码的形式打印字节数据或字符串。

让我们来解释这个函数的作用和实现:

1
HEX_FILTER = ''.join([(len(repr(chr(i))) == 3) and chr(i) or '.' for i in range(256)])
  • HEX_FILTER 是一个字符串,用于过滤非打印字符,将它们用 ‘.’ 代替。该字符串包含了所有 ASCII 码值在 0 到 255 范围内的字符,通过 repr() 函数判断字符是否是可打印字符,不可打印字符用 ‘.’ 表示。
1
def hexdump(src, length=16, show=True):
  • hexdump 函数接收三个参数:
    • src: 要打印的数据,可以是字节对象(bytes)或字符串(str)。
    • length: 每行显示的数据长度,默认为 16 字节。
    • show: 是否立即打印输出结果,默认为 True。
1
2
if isinstance(src, bytes):
src = src.decode()
  • 这段代码用于判断 src 是否是字节对象(bytes)。如果是字节对象,则将其转换为字符串,方便后续处理。
1
2
3
4
5
6
7
results = list()
for i in range(0, len(src), length):
word = str(src[i:i+length])
printable = word.translate(HEX_FILTER)
hexa = ' '.join([f'{ord(c):02X}' for c in word])
hexwidth = length*3
results.append(f'{i:04x} {hexa:<{hexwidth}} {printable}')
  • 这部分代码是核心逻辑,将数据按每行 length 字节进行切片,并转换为十六进制字符串和 ASCII 可打印字符串。
  • word 变量是当前行的数据片段,hexa 是该行数据的十六进制表示,printable 是该行数据的 ASCII 可打印表示。
  • results 列表保存了每行的结果。
1
2
3
4
5
if show:
for line in results:
print(line)
else:
return results
  • 最后,根据 show 参数判断是否立即打印输出结果。如果 show 为 True,则逐行打印结果;如果 show 为 False,则返回结果列表。

示例用法:

1
2
data = b'Hello, world!\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f'
hexdump(data)

输出结果:

1
2
0000 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 01 02 Hello, world!...
0010 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ..............

这个例子中,我们调用 hexdump 函数打印了一个包含 ASCII 和非 ASCII 字符的字节对象的内容。输出结果按每行 16 个字节进行划分,并分别显示了十六进制和 ASCII 可打印字符的表示。

代码块2 receive_from函数

这段代码定义了一个函数 receive_from(connection),用于从给定的连接中接收数据,并将接收到的数据存储在缓冲区中,最后返回缓冲区中的数据。

让我解释一下这个函数的主要逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
def receive_from(connection):
buffer = b"" # 创建一个字节字符串用于存储接收到的数据
connection.settimeout(5) # 设置连接的超时时间为 5 秒
try:
while True:
data = connection.recv(4096) # 接收最多 4096 字节的数据
if not data:
break # 如果没有数据接收到,退出循环
buffer += data # 将接收到的数据追加到缓冲区
except Exception as e:
pass # 发生异常时不做处理
return buffer # 返回缓冲区中的数据

这个函数主要有以下步骤:

  1. 创建一个字节字符串 buffer,用于存储接收到的数据。

  2. 使用 connection.settimeout(5) 设置连接的超时时间为 5 秒,即如果在 5 秒内没有数据可接收,会引发超时异常。

  3. 使用 while True: 进入一个无限循环,不断尝试接收数据。

  4. 在循环中,使用 connection.recv(4096) 从连接中接收最多 4096 字节的数据。如果接收到的数据为空,表示连接已关闭,退出循环。

  5. 将接收到的数据追加到 buffer 中。

  6. 使用 except Exception as e: 捕获任何可能发生的异常,不做处理。

  7. 最后,函数返回存储在 buffer 中的所有接收到的数据。

这个函数的目的是从连接中连续接收数据,直到连接关闭,然后将接收到的数据返回。在网络编程中,经常需要从连接中接收数据,这个函数提供了一个方便的方法来实现这一操作。

代码块3 proxy_handler函数

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
def request_handler(buffer):
return buffer


def response_handler(buffer):
return buffer


def proxy_handler(client_socket, remote_host, remote_port, receive_first):
remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
remote_socket.connect((remote_host, remote_port))

if receive_first:
remote_buffer = receive_from(remote_socket)
hexdump(remote_buffer)

remote_buffer = response_handler(remote_buffer)
if len(remote_buffer):
print("[<==] Sending %d bytes to localhost." % len(remote_buffer))
client_socket.send(remote_buffer)

while True:
local_buffer = receive_from(client_socket)
if len(local_buffer):
line = "[==>]Received %d bytes from localhost." % len(local_buffer)
print(line)
hexdump(local_buffer)

local_buffer = request_handler(local_buffer)
remote_socket.send(local_buffer)
print("[==>] Sent to remote.")

remote_buffer = request_handler(local_buffer)
if len(remote_buffer):
print("[<==] Received %d bytes from remote." % len(remote_buffer))
hexdump(remote_buffer)

remote_buffer = response_handler(remote_buffer)
client_socket.send(remote_buffer)
print("[<==] Sent to localhost.")

if not len(local_buffer) or not len(remote_buffer):
client_socket.close()
remote_socket.close()
print("[*] No more data. Closing connections.")
break

这段代码是一个代理服务器处理程序,负责将客户端的数据转发到远程服务器,然后将远程服务器的响应返回给客户端。让我对每个部分进行解释:

  1. proxy_handler(client_socket, remote_host, remote_port, receive_first):这是代理服务器的处理函数。它接受客户端套接字、远程主机、远程端口和 receive_first 参数。

  2. remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM):创建一个与远程服务器通信的套接字。

  3. remote_socket.connect((remote_host, remote_port)):建立与远程服务器的连接。

  4. if receive_first::如果 receive_firstTrue,表示需要在发送数据之前先接收一些数据。

    • remote_buffer = receive_from(remote_socket):从远程服务器接收数据。
    • hexdump(remote_buffer):使用之前定义的 hexdump 函数来打印接收到的数据。
  5. remote_buffer = response_handler(remote_buffer):对从远程服务器接收到的数据进行处理。

  6. if len(remote_buffer)::如果处理后的远程数据长度不为零。

    • client_socket.send(remote_buffer):将处理后的数据发送回客户端。
  7. 进入一个无限循环,循环处理客户端和远程服务器的数据。

    • local_buffer = receive_from(client_socket):从客户端接收数据。

    • local_buffer = request_handler(local_buffer):对从客户端接收到的数据进行处理。

    • remote_socket.send(local_buffer):将处理后的客户端数据发送到远程服务器。

    • remote_buffer = request_handler(local_buffer):对从客户端发送到远程服务器的数据进行处理。

    • if len(remote_buffer)::如果处理后的远程响应数据长度不为零。

      • client_socket.send(remote_buffer):将处理后的响应数据发送回客户端。
  8. 如果没有数据可接收(无论是客户端还是远程服务器),关闭客户端和远程套接字,然后结束循环。

这个代理服务器处理程序的目的是将客户端和远程服务器之间的数据进行中转,同时可以根据需要对数据进行处理。这是一个简单的示例,实际应用中可能需要更多的错误处理和安全性考虑。

代码块4 server_loop函数


python黑帽子学习笔记(一)
http://example.com/2023/08/03/python黑帽子学习笔记-一/
Author
John Doe
Posted on
August 3, 2023
Licensed under