问题描述
我最近一直在进行一个网络项目,以恢复死的mmo游戏供个人学习,我有一个python实现,可使用河豚(pypi / pycryptodome)解码游戏数据,并希望将此“服务器”传输到Java中项目。
最初在Java中使用河豚解密(B??ouncyCastle和Cipher-默认),我在java和python之间得到了完全不同的结果。 通过一些研究,我发现Java(以及其他大多数东西)实际上使用了兼容河豚的big endian。
这个python库似乎是唯一可以正确解码数据的库。 接下来,我决定使用python asyncio服务器作为中间中继,仅用于加密和解密。 现在,网络流如下所示:
GameClient -> Java SocketServer -> Python server (decryption) -> Java SocketServer
。
原始的Python
实现产生以下十六进制格式的字节:
32004a815f49367cc3691be26d7b668132506dc972d5a6bbad38299640c6e222c6e55096f50ff33711250675431633ca9ede
Java实现以十六进制格式生成这些结果(使用apache commons Hex.encodeHexString())
32004a815f49367cc3691be26d7b668132506dc972d5a6bbad38299640c6e222c6e5c65830d65f9b4d60eb26730685f486d7
这两个十六进制表示形式都是Python中的河豚预解密,它们只是从游戏客户端发送的原始字节。
我的问题是,为什么这些字节以相同的开头,然后似乎Java落后了?
python结果是经过测试和工作的正确结果。
我尝试将字节包装在缓冲区中的Java中,然后调用flip()
但是这也未产生正确的结果。
使用另一个stackoverflow帖子(对不起,我没有链接),我尝试将此byte []强制转换为BigInteger,这也未产生正确的结果。
任何帮助是极大的赞赏。
Python实现
#!/usr/bin/env python3
import asyncio
import binascii
import blowfish
import ipaddress
import os
import struct
import sys
AUTH_BLOWFISHKEY = b"[;'.]94-31==-%&@!^+]\000"
bf = blowfish.Cipher(AUTH_BLOWFISHKEY, byte_order="little")
class EncryptionRelay(asyncio.Protocol):
def connection_made(self, transport):
self.transport = transport
self.client = (transport.get_extra_info('peername')[0] + ":" # IP
+ str(transport.get_extra_info('peername')[1])) # port
print("Connection from: " + self.client)
def connection_lost(self, exc):
print("Connection closed: " + self.client)
def data_received(self, data):
print(data.hex()) #python output above
pt = b''.join(bf.decrypt_ecb(data[2:]))
self.transport.write(pt)
def closeSocket(self, reason):
print(reason)
self.transport.close()
def main():
loop = asyncio.get_event_loop()
coroutine = loop.create_server(EncryptionRelay, host=None, port=54556)
server = loop.run_until_complete(coroutine)
for socket in server.sockets:
print("Listening on: " + socket.getsockname()[0] + ":" +
str(socket.getsockname()[1]))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
if __name__ == "__main__":
main()
Java实现
public AuthServer(int port) {
serverGUI = new AuthServerGUI(port);
try {
serverSocket = new ServerSocket(port);
relay = new PythonEncryptionRelay(this);
new Thread(relay).start();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
try {
Socket socket = serverSocket.accept();
onConnection(socket); //sends an init packet to client -- irrelevant to question
byte[] incomingData = new byte[0];
byte[] temp = new byte[1024];
int k = -1;
while((k = socket.getInputStream().read(temp, 0, temp.length)) > -1) {
byte[] tbuff = new byte[incomingData.length + k];
System.arraycopy(incomingData, 0, tbuff, 0, incomingData.length);
System.arraycopy(temp, 0, tbuff, incomingData.length, k);
incomingData = tbuff;
receiveData(socket, incomingData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void receiveData(Socket socket, byte[] data) {
int lenLo = (int) (data[0]);
int lenHi = (int) (data[1]);
int length = lenHi * 256 + lenLo;
if(lenHi < 0) {
System.out.println("Invalid Packet Length");
}
if(data.length != length) {
System.out.println("Incomplete Packet Received");
}
serverGUI.serverDebug("DATA RECEIVED");
serverGUI.serverDebug(Hex.encodeHexString(data)); //this is the java ouput above serverGUI is simply a jframe i built no data manipulation
serverGUI.serverDebug("DATA_RECEIVED DONE");
this.relay.sendData(data); //this function sends the data from socket server to the python asyncio server
}
public void receiveDataFromPythonRelay(Socket socket, byte[] data) {
serverGUI.debugPythonRelay("DATA RECEIVED");
serverGUI.debugPythonRelay(Hex.encodeHexString(data)); //this will be the output from the python script aka data decrypted.
//The data byte[] is created in the exact same way the incomingData array is built in the AuthServer run function
serverGUI.debugPythonRelay("DATA_RECEIVED DONE");
}
另外,我从套接字导入数据byte []的方式也这样编程,因为客户端不发送endl,因此readLine将无法从流中工作。
1楼
一个字节有8位,这意味着您最多可以将0xff作为值。
但是Java使用带符号的字节,这意味着msb保留用于带符号的位。 这样您的值只剩下7位,因此您可以将最大值(0x7f)存储在变量的字节类型中。 大于0x07f的任何数字都将导致溢出。
尝试使用int数组。 由于int使用4字节(32位),因此总会有8位空间。
使用byte []从流中读取,然后将内容复制到int []中,使用int intArr[i] = byteArr[i] & 0xFF;
从byte []中获得第i个值,以避免由于字节溢出而产生负数