This repository has been archived by the owner on Jun 4, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
sfs.lua
305 lines (257 loc) · 10.2 KB
/
sfs.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
--[[
The MIT License (MIT)
Copyright (c) 2015 [email protected]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]
do
local sfs_ports = { 9933, 9339 }
local deflate = require "deps.deflatelua"
local sfsio = require "deps.sfsio"
local json = require "deps.json"
local sfs_proto = Proto("sfs", "SFS Protocol Analysis")
local F_marker = ProtoField.uint8("sfs.marker", "sfs.marker", base.HEX)
local F_marker_binary = ProtoField.uint8("sfs.marker.binary", "sfs.marker.binary ", base.HEX, nil, 0x80)
local F_marker_encrypt = ProtoField.uint8("sfs.marker.encrypt", "sfs.marker.encrypt ", base.HEX, nil, 0x40)
local F_marker_compress = ProtoField.uint8("sfs.marker.compress", "sfs.marker.compress", base.HEX, nil, 0x20)
local F_marker_bluebox = ProtoField.uint8("sfs.marker.bluebox", "sfs.marker.bluebox ", base.HEX, nil, 0x10)
local F_marker_bigsize = ProtoField.uint8("sfs.marker.bigsize", "sfs.marker.bigsize ", base.HEX, nil, 0x08)
local F_data_length = ProtoField.uint32("sfs.data_length", "sfs.data_length", base.DEC)
local F_module = ProtoField.uint8("sfs.module", "sfs.module", base.DEC)
local F_action = ProtoField.uint8("sfs.action", "sfs.action", base.DEC)
local F_data = ProtoField.bytes("sfs.data", "sfs.data")
local F_decompressed = ProtoField.bytes("sfs.decompressed", "sfs.decompressed")
local F_string = ProtoField.string("sfs.p.string", "sfs.p.string")
local F_ustring = ProtoField.string("sfs.p.ustring", "sfs.p.ustring")
local F_int32 = ProtoField.int32("sfs.p.int32", "sfs.p.int32", base.DEC)
local F_hex32 = ProtoField.uint32("sfs.p.hex32", "sfs.p.hex32", base.HEX)
local F_int16 = ProtoField.int32("sfs.p.int16", "sfs.p.int16", base.DEC)
local F_int8 = ProtoField.int32("sfs.p.int8", "sfs.p.int8", base.DEC)
local F_uint32 = ProtoField.int32("sfs.p.uint32", "sfs.p.uint32", base.DEC)
local F_uint16 = ProtoField.int32("sfs.p.uint16", "sfs.p.uint16", base.DEC)
local F_uint8 = ProtoField.int32("sfs.p.uint8", "sfs.p.uint8", base.DEC)
local F_double = ProtoField.double("sfs.p.double", "sfs.p.double")
local F_float = ProtoField.float("sfs.p.float", "sfs.p.float")
local F_json = ProtoField.string("sfs.json", "sfs.json")
-- add the fields to the protocol
sfs_proto.fields = {
F_marker,
F_marker_binary, F_marker_encrypt, F_marker_compress, F_marker_bluebox, F_marker_bigsize,
F_data_length, F_module, F_action, F_data, F_decompressed,
F_string, F_ustring,
F_int32, F_int16, F_int8, F_uint32, F_uint16, F_uint8,
F_hex32,
F_double, F_float,
F_json,
}
local f_ip_src = Field.new("ip.src")
local f_tcp_srcport = Field.new("tcp.srcport")
local f_tcp_dstport = Field.new("tcp.dstport")
local f_tcp_stream = Field.new("tcp.stream")
local f_tcp_seq = Field.new("tcp.seq")
local ip_src
local tcp_srcport = 0
local tcp_dstport = 0
-- local idx = 0
local direction = ""
local direction_type = ""
local Actions = {}
local function hex_value(val)
if val >= string.byte('0') and val <= string.byte('9') then
return val - string.byte('0')
elseif val >= string.byte('a') and val <= string.byte('z') then
return 10 + val - string.byte('a')
elseif val >= string.byte('A') and val <= string.byte('Z') then
return 10 + val - string.byte('A')
end
return val
end
local function get_bytes_from_str(str)
local len = string.len(str)
local input_str = {}
for idx=1,len, 2 do
local val = hex_value(string.byte(str, idx)) * 16 + hex_value(string.byte(str, idx+1))
input_str[#input_str+1] = string.char(val)
print (hex_value(string.byte(str, idx)) * 16 .. " " .. hex_value(string.byte(str, idx+1)) .. " = " .. string.format('%02x', val))
end
input_str = table.concat(input_str)
return input_str
end
local function sfs_dissect_payload(tvbuffer, pinfo, treeitem, offset)
local val
val, offset = sfsio.decodeObject(tvbuffer, offset)
local jsonstr = json:encode(val)
local jsonbytes = ByteArray.new()
local jsonlen = string.len(jsonstr)
jsonbytes:set_size(jsonlen)
for i=1,jsonlen do
jsonbytes:set_index(i-1, string.byte(jsonstr,i))
end
--[[
local jsonhex, _ = string.gsub(jsonstr, ".", function (k) return string.format("%02x", k:byte(1,1)) end)
local jsonbytes = ByteArray.new(jsonhex)
]]
local jsontvb = ByteArray.tvb(jsonbytes, "DecodedJson")
local valtree = treeitem:add(F_json, jsontvb(0, jsontvb:len()))
end
local function sfs_dissector_one(tvbuffer, pinfo, treeitem, offset)
local orig_offset = offset
local packet_header = tvbuffer(offset, 1)
offset = offset + 1
local payload_length = tvbuffer(offset, 2)
offset = offset + 2
local pdu_length = 1 + 2 + payload_length:uint()
local buff_len = tvbuffer:len() - offset
if buff_len < payload_length:uint() then
info("payload too small, buff_len = " .. buff_len)
return 0
end
local subtreeitem = treeitem:add(sfs_proto, tvbuffer)
local headertree = subtreeitem:add(F_marker, packet_header)
-- header
headertree:add(F_marker_binary, packet_header)
headertree:add(F_marker_encrypt, packet_header)
headertree:add(F_marker_compress, packet_header)
headertree:add(F_marker_bluebox, packet_header)
headertree:add(F_marker_bigsize, packet_header)
-- data length
subtreeitem:add(F_data_length, payload_length)
-- decompress if needed
local compress = bit32.band(packet_header:uint(), 0x20)
-- data
local content_len = payload_length:uint()
local content_offset = offset
local dataitem = subtreeitem:add(F_data, tvbuffer(offset, content_len))
offset = offset + content_len
-- check compress
if compress ~= 0 then
local begin_idx = content_offset
local end_idx = begin_idx + content_len - 1
local input_str = {}
for buff_idx = begin_idx, end_idx do
input_str[#input_str + 1] = string.char(tvbuffer(buff_idx, 1):uint())
end
input_str = table.concat(input_str)
-- decompress
local output_table = {}
local output_fun = function(byte)
output_table[#output_table + 1] = string.format("%02x",byte)
end
deflate.inflate_zlib {
input = input_str,
output = output_fun
}
local output_str = table.concat(output_table)
-- print("output_str = " .. output_str)
local decompressed = ByteArray.new(output_str)
local tvb = ByteArray.tvb(decompressed, "decompressed")
dataitem = subtreeitem:add(F_decompressed, tvb(0, tvb:len()))
sfs_dissect_payload(tvb, pinfo, subtreeitem, 0)
else
-- dissect content
sfs_dissect_payload(tvbuffer, pinfo, subtreeitem, content_offset)
end
return offset - orig_offset
end
local function sfs_decode_payload(tvbuffer)
local buff_len = tvbuffer:len()
local buff = ByteArray.new()
buff:set_size(buff_len)
local idx
for idx = 0, buff_len-1 do
local val = tvbuffer(idx, 1):uint()
val = bit32.bxor(val, 6)
buff:set_index(idx, val)
end
local ret = ByteArray.tvb(buff, "Decoded")
return ret
end
local function sfs_dissector(tvbuffer, pinfo, treeitem)
local is_server_port = function (port)
for _, val in ipairs(sfs_ports) do
if val == port then
return true
end
end
return false
end
ip_src = ""
if f_ip_src() then
ip_src = tostring(f_ip_src())
end
tcp_srcport = 0
if f_tcp_srcport() then
tcp_srcport = f_tcp_srcport().value or 0
else
tcp_srcport = pinfo.src_port
end
tcp_dstport = 0
if f_tcp_dstport() then
tcp_dstport = f_tcp_dstport().value or 0
else
tcp_dstport = pinfo.dst_port
end
direction = ""
direction_type = ""
if tcp_srcport ~= 0 and tcp_dstport ~= 0 then
if is_server_port(tcp_srcport) then
direction = "Response <--"
direction_type = "response"
elseif is_server_port(tcp_dstport) then
direction = "Request -->"
direction_type = "request"
end
end
local direction_len = direction:len()
local protocol_name = "SFS"
-- check header
local header = tvbuffer(0, 1):uint()
if bit32.band(header, 0x07) ~= 0 then
print("ignore packet, id = " .. pinfo.number)
return 0
end
local totallen = tvbuffer:len()
local offset = 0
-- if offset < totallen then
while offset < totallen do
local payload_length = tvbuffer(offset + 1, 2):uint()
local available = totallen - offset - 1 - 2
if payload_length > available then
pinfo.desegment_len = payload_length - available
pinfo.desegment_offset = offset
return
end
local delta_offset = sfs_dissector_one(tvbuffer, pinfo, treeitem, offset)
if delta_offset <= 0 then
print("delta_offset = " .. delta_offset)
-- return
break
end
offset = offset + delta_offset
end
pinfo.cols.protocol = protocol_name
pinfo.cols.info = direction
end
-- declare the fields we need to read
function sfs_proto.dissector(tvbuffer, pinfo, treeitem)
return sfs_dissector(tvbuffer, pinfo, treeitem)
end
local tcp_dissector_table = DissectorTable.get("tcp.port")
for _, port in ipairs(sfs_ports) do
tcp_dissector_table:add(port, sfs_proto)
end
print("Version = " .. _VERSION)
end