使用 Python 和 Wireshark 理解 MySQL 客户端/服务器协议:第 2 部分
source link: https://dongzl.github.io/2022/04/26/03-Understanding-MySQL-Client-Server-Protocol-Using-Python-Wireshark-Part-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
使用 Python 和 Wireshark 理解 MySQL 客户端/服务器协议:第 2 部分
在上一篇文章中我们使用 Wireshark 工具研究了 MySQL 客户端 / 服务端协议内容。现在我们开始使用 Python 语言编码来开发一个模拟 MySQL 原生客户端的工具。最终的代码在这里:Github repo
首先我们需要创建一个 MYSQL_PACKAGE
类。MYSQL_PACKAGE
类是其他 package
类(例如:HANDSHAKE_PACKAGE
, LOGIN_PACKAGE
, OK_PACKAGE
等等)的父类。
class MYSQL_PACKAGE: |
它在初始化时接受 resp
参数。 Resp
是从服务器接收到的字节数组类型的二进制响应。这个类的一个重要而有趣的方法是 next
方法。
def next(self, length = None, type=int, byteorder='little', signed=False, freeze=False): |
next
方法从二进制响应数据中读取了一部分字节的数据。当我们调用此方法时,它会读取部分字节并将指针指向读取结束的最后位置(更改 self.start 和 self.end 属性的值)。当我们再次调用这个方法时,它会从上次停止的位置开始读取字节数据。
next
方法接收五个参数:length
、type
、byteorder
、signed
和 freeze
。如果 freeze
参数是 True
,它将会从二进制响应中读取一部分字节数据,但是并不会改变指针的位置;否则的话,它将读取指定长度的一部分字节数据,并同时修改指针的位置。如果 length
参数为指定,则方法读取字节,直到响应字节数组结束。type
参数可以是 int
、str
和 hex
数据类型。方法 next
根据类型参数的值将一部分字节转换为适当的数据类型。
参数 byteorder
确定字节到整数类型的转换,这取决于您计算机的体系结构,如果你的机器是 big-endian
,那么它会在内存中存储从大地址到小地址的字节。如果您的机器是 little-endian
,那么它会将字节存储在内存中,从小地址到大地址。这就是为什么我们必须知道我们架构的确切类型才能正确地将字节转换为整数。在我的例子中,它是 little-endian
,这就是为什么我将 byteorder
参数的默认值设置为“little
”。
参数 signed
同样用于字节到整数类型的转换,我们告诉函数将每个整数视为无符号整数或有符号整数。
这个类中第二个有意思的方法是:encrypt_password
。这个方法使用指定的算法来加密密码。
from hashlib import sha1 |
这个方法接收两个参数:salt
和 password
。参数 salt
是从服务器接收到的握手数据中的两个 salt1
和 salt2
字符串的串联。参数 password
是 MySQL
用户的密码字符串。
在官方文档中密码的加密算法是:
SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) ) |
在这里 ”20-bytes random data from server“ 是从服务器接收到的握手数据中的两个 salt1
和 salt2
字符串的串联。要想知道握手包是什么内容,请看上一篇文章。
接下来我将会逐行解释一下 encrypt_password
方法的内容。
bytes1 = sha1(password.encode(“utf-8”)).digest() |
我们将密码字符串转换为字节,然后使用 sha1
函数进行加密,并将结果赋值给 bytes1
变量。它等价与算法的这一部分:
接下来我们将 salt
字符串转换为字节,并赋值给 concat1
变量:
concat1 = salt.encode(‘utf-8’) |
方法的第三行是:
concat2 = sha1(sha1(password.encode(“utf-8”)).digest()).digest() |
在这里我们使用 sha1
函数对密码字符串进行两次加密,并将结果赋值给 concat2
字符串变量。
现在我们有了两个变量:concat1
和 concat2
。我们需要使用一个字节数组将他们连接到一起:
bytes2 = bytearray() |
接下来我们需要使用 sha1
函数对连接后字节进行加密,并将结果赋值给 bytes2
变量:
bytes2 = sha1(bytes2).digest() |
现在我们已经有了两个加密的字节变量:bytes1
和 bytes2
。现在我们必须在这些变量之间进行按位异或运算并返回获得的哈希值。
hash=bytearray(x ^ y for x, y in zip(bytes1, bytes2)) |
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK