-
Notifications
You must be signed in to change notification settings - Fork 0
/
peerchat.lua
128 lines (112 loc) · 3.99 KB
/
peerchat.lua
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
PEERCHAT_PORT = 6667
PEERCHAT_NEW_PORT = 16667
RA3_GAMEKEY = "uBZwpf"
PeerchatProtocol = Proto("peerchat", "Peerchat Protocol")
PeerchatProtocol.fields.payload = ProtoField.string("peerchat.payload", "Payload")
local peerchat_instances = {}
local tcp_stream_field = Field.new("tcp.stream")
function PeerchatProtocol.init()
peerchat_instances = {}
end
function PeerchatProtocol.dissector(buffer, pinfo, tree)
local stream_number = tcp_stream_field().value
if not peerchat_instances[stream_number] then
print("creating new peerchat instance with tcp stream number " .. stream_number)
peerchat_instances[stream_number] = {
server_cipher = PeerchatCipher:new(),
client_cipher = PeerchatCipher:new(),
decrypted_data = {},
crypted = false
}
end
local instance = peerchat_instances[stream_number]
local length = buffer:len()
if length == 0 then return end
pinfo.cols.protocol = PeerchatProtocol.name
local is_from_server = pinfo.src_port == PEERCHAT_PORT
or pinfo.src_port == PEERCHAT_NEW_PORT
local t = tree:add(PeerchatProtocol, buffer())
function add_to_wireshark(bytearray)
local info = ""
for s in bytearray:raw():gmatch("[^\r\n]+") do
t:add(PeerchatProtocol.fields.payload, ByteArray.new(s, true):tvb("Payload")())
if info:len() > 0 then info = info .. "; " end
info = info .. s
end
pinfo.columns.info = info
end
if pinfo.visited then
-- already processed, use cached data
add_to_wireshark(instance.decrypted_data[pinfo.number])
return
end
local data = buffer:bytes()
instance.decrypted_data[pinfo.number] = data
if instance.crypted then
if is_from_server then
instance.server_cipher:process(data)
else
instance.client_cipher:process(data)
end
end
add_to_wireshark(data)
if not instance.crypted and is_from_server then
-- :s 705 * k|=voRElmbgHtKGW xwDplfMB>^lexB<r
local payload = buffer():string()
local i, j, client_key, server_key = payload:find(":s 705 %* ([^ ]+) ([^ \r\n]+)[\r\n]")
if server_key and client_key then
instance.server_cipher:initialize(server_key, RA3_GAMEKEY)
instance.client_cipher:initialize(client_key, RA3_GAMEKEY)
instance.crypted = true
end
return
end
end
PeerchatCipher = {}
function PeerchatCipher:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
function PeerchatCipher:initialize(challenge, gamekey)
self.pc1 = 0
self.pc2 = 0
local chall = {}
-- lua string indices are 1-based
for i = 1, challenge:len() do
local challenge_byte = challenge:byte(i)
local gamekey_byte = gamekey:byte((i - 1) % gamekey:len() + 1)
-- however we will use 0-based indices
chall[i - 1] = bit.bxor(challenge_byte, gamekey_byte)
end
self.table = {}
for i = 0, 255 do
self.table[i] = 255 - i
end
-- scramble up the table based on challenge
local tmp = 0
for i = 0, 255 do
tmp = bit.band(tmp + chall[i % challenge:len()] + self.table[i], 0xFF)
-- now just swap
local tmp2 = self.table[tmp]
self.table[tmp] = self.table[i]
self.table[i] = tmp2
end
end
function PeerchatCipher:process(bytearray)
for i = 0, bytearray:len() - 1 do
self.pc1 = bit.band(self.pc1 + 1, 0xFF)
local tmp = self.table[self.pc1]
self.pc2 = bit.band(self.pc2 + tmp, 0xFF)
self.table[self.pc1] = self.table[self.pc2]
self.table[self.pc2] = tmp
tmp = bit.band(tmp + self.table[self.pc1], 0xFF)
local datum = bytearray:get_index(i)
datum = bit.bxor(datum, self.table[tmp])
bytearray:set_index(i, datum)
end
end
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(PEERCHAT_PORT, PeerchatProtocol)
tcp_port:add(PEERCHAT_NEW_PORT, PeerchatProtocol)