syspy 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bytes.rb +98 -0
- data/lib/log.rb +45 -0
- data/lib/tds_package.rb +39 -0
- data/lib/tds_package_stream.rb +121 -0
- data/lib/tds_packages/tds_language.rb +32 -0
- data/lib/tds_packages/tds_paramfmt.rb +15 -0
- data/lib/tds_packages/tds_paramfmt2.rb +50 -0
- data/lib/tds_packages/tds_params.rb +70 -0
- data/lib/tds_tokens.rb +104 -0
- data/lib/tds_types.rb +94 -0
- data/syspy.rb +63 -0
- metadata +14 -3
data/lib/bytes.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require "bindata"
|
2
|
+
|
3
|
+
module Syspy
|
4
|
+
class Bytes
|
5
|
+
def self.uint(io)
|
6
|
+
BinData::Uint8.read(io).to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.uintle(io,length)
|
10
|
+
case length
|
11
|
+
when 1:
|
12
|
+
BinData::Uint8.read(io).to_i
|
13
|
+
when 2:
|
14
|
+
BinData::Uint16le.read(io).to_i
|
15
|
+
when 4:
|
16
|
+
BinData::Uint32le.read(io).to_i
|
17
|
+
else
|
18
|
+
raise ArgumentError.new("Invalid length")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.uint16le(io)
|
23
|
+
BinData::Uint16le.read(io).to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.uint32le(io)
|
27
|
+
BinData::Uint32le.read(io) .to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.uintbe(io,length)
|
31
|
+
case length
|
32
|
+
when 1:
|
33
|
+
BinData::Uint8.read(io).to_i
|
34
|
+
when 2:
|
35
|
+
BinData::Uint16be.read(io).to_i
|
36
|
+
when 4:
|
37
|
+
BinData::Uint32be.read(io).to_i
|
38
|
+
else
|
39
|
+
raise ArgumentError.new("Invalid length")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.uint16be(io)
|
44
|
+
BinData::Uint16be.read(io).to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.uint32be(io)
|
48
|
+
BinData::Uint32be.read(io).to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.int(io)
|
52
|
+
BinData::Int8.read(io).to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.intle(io,length)
|
56
|
+
case length
|
57
|
+
when 1:
|
58
|
+
BinData::Int8.read(io).to_i
|
59
|
+
when 2:
|
60
|
+
BinData::Int16le.read(io).to_i
|
61
|
+
when 4:
|
62
|
+
BinData::Int32le.read(io).to_i
|
63
|
+
else
|
64
|
+
raise ArgumentError.new("Invalid length")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.int16le(io)
|
69
|
+
BinData::Int16le.read(io).to_i
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.int32le(io)
|
73
|
+
BinData::Int32le.read(io).to_i
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.intbe(io,length)
|
77
|
+
case length
|
78
|
+
when 1:
|
79
|
+
BinData::Int8.read(io).to_i
|
80
|
+
when 2:
|
81
|
+
BinData::Int16be.read(io).to_i
|
82
|
+
when 4:
|
83
|
+
BinData::Int32be.read(io).to_i
|
84
|
+
else
|
85
|
+
raise ArgumentError.new("Invalid length")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.int16be(io)
|
90
|
+
BinData::Int16be.read(io).to_i
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.int32be(io)
|
94
|
+
BinData::Int32be.read(io).to_i
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
data/lib/log.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Syspy
|
4
|
+
class Log
|
5
|
+
|
6
|
+
LOG_FILE = "syspy.log"
|
7
|
+
LOGGERS = []
|
8
|
+
|
9
|
+
STDOUT_LOG = Logger.new(STDOUT)
|
10
|
+
STDOUT_LOG.level = Logger::INFO
|
11
|
+
|
12
|
+
#FILE_LOG = Logger.new(LOG_FILE)
|
13
|
+
#FILE_LOG.level = Logger::INFO
|
14
|
+
|
15
|
+
LOGGERS << STDOUT_LOG
|
16
|
+
#LOGGERS << FILE_LOG
|
17
|
+
|
18
|
+
def self.fatal(message)
|
19
|
+
self.log(Logger::FATAL,message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.error(message)
|
23
|
+
self.log(Logger::ERROR,message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.warn(message)
|
27
|
+
self.log(Logger::WARN,message)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.info(message)
|
31
|
+
self.log(Logger::INFO,message)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.debug(message)
|
35
|
+
self.log(Logger::DEBUG,message)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.log(severity,message)
|
39
|
+
LOGGERS.each(){|logger|
|
40
|
+
logger.log(severity,message)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/tds_package.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require "lib/bytes"
|
2
|
+
|
3
|
+
module Syspy
|
4
|
+
|
5
|
+
class ParseError < Exception
|
6
|
+
end
|
7
|
+
|
8
|
+
class TdsPackage
|
9
|
+
|
10
|
+
def read_uint(io)
|
11
|
+
Bytes.uint(io)
|
12
|
+
end
|
13
|
+
|
14
|
+
def read_uint32(io)
|
15
|
+
Bytes.uint32le(io)
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_uint16(io)
|
19
|
+
Bytes.uint16le(io)
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_int(io)
|
23
|
+
Bytes.int(io)
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_int32(io)
|
27
|
+
Bytes.int32le(io)
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_int16(io)
|
31
|
+
Bytes.int16le(io)
|
32
|
+
end
|
33
|
+
|
34
|
+
def read_text(io,length)
|
35
|
+
io.read(length)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
require "lib/tds_types"
|
5
|
+
require "lib/tds_tokens"
|
6
|
+
|
7
|
+
module Syspy
|
8
|
+
class TdsPackageStream
|
9
|
+
|
10
|
+
def initialize(interface,dst,dst_port)
|
11
|
+
@interface = interface
|
12
|
+
@dst = dst
|
13
|
+
@dst_port = dst_port
|
14
|
+
@in,@out = IO.pipe()
|
15
|
+
end
|
16
|
+
|
17
|
+
def each_package()
|
18
|
+
Thread.abort_on_exception = true
|
19
|
+
@tcpdump_thread = Thread.new(){
|
20
|
+
IO.popen("tcpdump -q -y EN10MB -U -B #{1024 * 1024} -w - -i #{@interface} tcp and dst #{@dst} and dst port #{@dst_port} 2>/dev/null"){|io|
|
21
|
+
content = ""
|
22
|
+
loop(){
|
23
|
+
tcp_length = read_ip_header(io)
|
24
|
+
Log.debug "Got IP package: #{tcp_length}"
|
25
|
+
content_length = read_tcp_header(io,tcp_length)
|
26
|
+
Log.debug "Got TCP package: #{content_length}"
|
27
|
+
|
28
|
+
if(content_length > 0)
|
29
|
+
content = io.read(content_length)
|
30
|
+
@out.write(content)
|
31
|
+
@out.flush
|
32
|
+
end
|
33
|
+
|
34
|
+
Log.debug "Network package done"
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
content = ""
|
40
|
+
@in.each_byte(){|byte|
|
41
|
+
if(byte == 0x0F)
|
42
|
+
last_packet_indicator = Bytes.uint(@in)
|
43
|
+
length = Bytes.uint16be(@in)
|
44
|
+
|
45
|
+
# skip next 4 bytes of the header
|
46
|
+
if(Bytes.uint32be(@in) == 0x00)
|
47
|
+
content << @in.read(length - 8)
|
48
|
+
|
49
|
+
if(last_packet_indicator == 0x1)
|
50
|
+
begin
|
51
|
+
io = StringIO.new(content)
|
52
|
+
package = handle_package(io)
|
53
|
+
yield package if package
|
54
|
+
ensure
|
55
|
+
content = ""
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def handle_package(io)
|
64
|
+
token = io.readchar()
|
65
|
+
token_class = TdsTokens.token_class(token)
|
66
|
+
if(token_class)
|
67
|
+
length = TdsTokens.fixed_length(token)
|
68
|
+
unless(length)
|
69
|
+
length_field_size = TdsTokens.length_field_size(token)
|
70
|
+
if(length_field_size)
|
71
|
+
length = Bytes.uintle(io,length_field_size)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
Log.debug("Got #{TdsTokens.token_name(token)} of #{length} bytes")
|
75
|
+
package = token_class.new(io,length)
|
76
|
+
return package
|
77
|
+
end
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def read_ip_header(io)
|
82
|
+
count = 0
|
83
|
+
loop(){
|
84
|
+
network_type = Bytes.uint16be(io)
|
85
|
+
break if network_type == 0x800 and count >= 14
|
86
|
+
count += 1
|
87
|
+
}
|
88
|
+
|
89
|
+
# read IP version and header length
|
90
|
+
ver_len = io.readchar()
|
91
|
+
version = ver_len >> 4
|
92
|
+
|
93
|
+
raise "Invalid IP version: #{version}" unless version == 4 || version == 6
|
94
|
+
|
95
|
+
header_length = (ver_len & 0x0F) * 4
|
96
|
+
|
97
|
+
# skip TOS
|
98
|
+
io.readchar()
|
99
|
+
|
100
|
+
# get package length
|
101
|
+
content_length = Bytes.uint16be(io)
|
102
|
+
|
103
|
+
# consume remaining IP header
|
104
|
+
io.read(header_length - 4)
|
105
|
+
content_length - header_length
|
106
|
+
end
|
107
|
+
|
108
|
+
def read_tcp_header(io, ip_package_size)
|
109
|
+
# skip first 12
|
110
|
+
io.read(12)
|
111
|
+
|
112
|
+
# get header length
|
113
|
+
tcp_offset = (io.readchar() >> 4) * 4
|
114
|
+
|
115
|
+
# consume remaining tcp header
|
116
|
+
io.read(tcp_offset - 13)
|
117
|
+
|
118
|
+
ip_package_size - tcp_offset
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "lib/tds_package"
|
2
|
+
|
3
|
+
module Syspy
|
4
|
+
class TdsLanguage < TdsPackage
|
5
|
+
|
6
|
+
attr_reader :status, :language_text, :tds_paramfmt, :tds_params
|
7
|
+
|
8
|
+
STATUS_PARAMETERIZED = 0x01
|
9
|
+
|
10
|
+
def initialize(io,length)
|
11
|
+
@status = read_uint(io)
|
12
|
+
@language_text = read_text(io,length - 1)
|
13
|
+
if(@status == 1 && !io.eof?)
|
14
|
+
token = read_uint(io)
|
15
|
+
if(token == TdsTokens::TDS_PARAMFMT || token == TdsTokens::TDS_PARAMFMT2)
|
16
|
+
paramfmt_length = Bytes.uintle(io,TdsTokens.length_field_size(token))
|
17
|
+
if(paramfmt_length < 1000000)
|
18
|
+
@tds_paramfmt = TdsTokens.token_class(token).new(io,paramfmt_length)
|
19
|
+
if(@tds_paramfmt.params_count > 0)
|
20
|
+
token = read_uint(io)
|
21
|
+
if(token == TdsTokens::TDS_PARAMS)
|
22
|
+
@tds_params = TdsParams.new(io,@tds_paramfmt)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
Log.warn("Illegal paramfmt length: #{paramfmt_length}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Syspy
|
2
|
+
class TdsParamfmt < TdsPackage
|
3
|
+
|
4
|
+
attr_reader :params_count, :parameters
|
5
|
+
|
6
|
+
def initialize(io,length)
|
7
|
+
@params_count = read_uint16(io)
|
8
|
+
puts "TdsParamfmt2: #{@params_count}"
|
9
|
+
@parameters = []
|
10
|
+
1.upto(@params_count){
|
11
|
+
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Syspy
|
2
|
+
class TdsParamfmt2 < TdsPackage
|
3
|
+
|
4
|
+
attr_reader :params_count, :parameters
|
5
|
+
|
6
|
+
def initialize(io,length)
|
7
|
+
@params_count = read_uint16(io)
|
8
|
+
@parameters = []
|
9
|
+
|
10
|
+
1.upto(@params_count){
|
11
|
+
parameter = {}
|
12
|
+
name_length = read_uint(io)
|
13
|
+
parameter[:name_length] = name_length
|
14
|
+
if(name_length > 0)
|
15
|
+
parameter[:param_name] = read_text(io,name_length)
|
16
|
+
end
|
17
|
+
parameter[:status] = read_uint32(io)
|
18
|
+
parameter[:user_type] = read_int32(io)
|
19
|
+
data_type = read_uint(io)
|
20
|
+
parameter[:data_type] = data_type
|
21
|
+
|
22
|
+
data_length = TdsTypes.fixed_length(data_type)
|
23
|
+
|
24
|
+
if(data_length == -5)
|
25
|
+
data_length = Bytes.int32le(io)
|
26
|
+
elsif(data_length == -4)
|
27
|
+
data_length = Bytes.int32le(io)
|
28
|
+
elsif(data_length == -2)
|
29
|
+
data_length = Bytes.int32le(io)
|
30
|
+
elsif(data_length == -1)
|
31
|
+
data_length = Bytes.uint(io)
|
32
|
+
end
|
33
|
+
|
34
|
+
parameter[:length] = data_length
|
35
|
+
|
36
|
+
if(TdsTypes.numeric?(data_type))
|
37
|
+
parameter[:precision] = read_uint(io)
|
38
|
+
parameter[:scale] = read_uint(io)
|
39
|
+
end
|
40
|
+
|
41
|
+
locale_length = read_uint(io)
|
42
|
+
parameter[:locale_length] = locale_length
|
43
|
+
if(locale_length > 0)
|
44
|
+
parameter[:locale_info] = read_text(io,locale_length)
|
45
|
+
end
|
46
|
+
@parameters << parameter
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Syspy
|
2
|
+
class TdsParams < TdsPackage
|
3
|
+
|
4
|
+
attr_reader :params_count, :parameters
|
5
|
+
|
6
|
+
def initialize(io,paramfmt)
|
7
|
+
@parameters = []
|
8
|
+
paramfmt.parameters.each(){|format|
|
9
|
+
@parameters << read_data(io,format)
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_data(io,format)
|
14
|
+
data_type = format[:data_type]
|
15
|
+
case data_type
|
16
|
+
when TdsTypes::SYBINTN
|
17
|
+
len = read_int(io)
|
18
|
+
return Bytes.intle(io,len)
|
19
|
+
when TdsTypes::SYBINT1
|
20
|
+
return Bytes.int(io)
|
21
|
+
when TdsTypes::SYBINT2
|
22
|
+
return Bytes::int16le(io)
|
23
|
+
when TdsTypes::SYBINT4
|
24
|
+
return Bytes::int32le(io)
|
25
|
+
when TdsTypes::SYBCHAR, TdsTypes::SYBVARCHAR
|
26
|
+
len = read_uint(io)
|
27
|
+
if (len > 0)
|
28
|
+
val = read_text(io,len)
|
29
|
+
# In TDS 4/5 zero length varchars are stored as a
|
30
|
+
# single space to distinguish them from nulls.
|
31
|
+
if(len == 1 && data_type == TdsTypes::SYBVARCHAR)
|
32
|
+
return " " == val ? "" : val
|
33
|
+
end
|
34
|
+
return val
|
35
|
+
end
|
36
|
+
when TdsTypes::SYBDATETIME4, TdsTypes::SYBDATETIMN, TdsTypes::SYBDATETIME
|
37
|
+
read_datetime(io,data_type)
|
38
|
+
else
|
39
|
+
raise "Unhandled data type: #{TdsTypes.name(data_type)} (0x#{data_type.to_s(16)})"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
TIME_DIFF = Time.at(0) - Time.local(1900,1,1)
|
44
|
+
|
45
|
+
def read_datetime(io,data_type)
|
46
|
+
if (data_type == TdsTypes::SYBDATETIMN)
|
47
|
+
len = read_uint(io)
|
48
|
+
elsif (data_type == TdsTypes::SYBDATETIME4)
|
49
|
+
len = 4;
|
50
|
+
else
|
51
|
+
len = 8;
|
52
|
+
end
|
53
|
+
|
54
|
+
gmt_offset = Time.new().gmt_offset
|
55
|
+
|
56
|
+
case len
|
57
|
+
when 0
|
58
|
+
return nil
|
59
|
+
when 8
|
60
|
+
days = read_int32(io)
|
61
|
+
time = read_int32(io)
|
62
|
+
return Time.at(days * 24 * 3600 - TIME_DIFF + time / 300 - gmt_offset)
|
63
|
+
when 4
|
64
|
+
days = read_int16le(io)
|
65
|
+
time = read_int16le(io)
|
66
|
+
return Time.at(days * 24 * 3600 + time * 60 - gmt_offset)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/tds_tokens.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
module Syspy
|
2
|
+
class TdsTokens
|
3
|
+
TOKENS = [
|
4
|
+
["TDS_ALTFMT", 0xA8, 2],
|
5
|
+
["TDS_ALTNAME", 0xA7, 2],
|
6
|
+
["TDS_ALTROW", 0xD3, nil],
|
7
|
+
["TDS_CAPABILITY", 0xE2, 2],
|
8
|
+
["TDS_COLINFO", 0xA5, 2],
|
9
|
+
["TDS_COLINFO2", 0x20, 4],
|
10
|
+
["TDS_CONTROL", 0xAE, 2],
|
11
|
+
["TDS_CURCLOSE", 0x80, 2],
|
12
|
+
["TDS_CURDECLARE", 0x86, 2],
|
13
|
+
["TDS_CURDECLARE2", 0x23, 4],
|
14
|
+
["TDS_CURDECLARE3", 0x10, 4],
|
15
|
+
["TDS_CURDELETE", 0x81, 2],
|
16
|
+
["TDS_CURFETCH", 0x82, 2],
|
17
|
+
["TDS_CURINFO", 0x83, 2],
|
18
|
+
["TDS_CURINFO2", 0x87, 2],
|
19
|
+
["TDS_CURINFO3", 0x88, 2],
|
20
|
+
["TDS_CUROPEN", 0x84, 2],
|
21
|
+
["TDS_CURUPDATE", 0x85, 2],
|
22
|
+
["TDS_DBRPC", 0xE6, 2],
|
23
|
+
["TDS_DBRPC2", 0xE8, 2],
|
24
|
+
["TDS_DONE", 0xFD, [8]],
|
25
|
+
["TDS_DONEINPROC", 0xFF, [8]],
|
26
|
+
["TDS_DONEPROC", 0xFE, [8]],
|
27
|
+
["TDS_DYNAMIC", 0xE7, 2],
|
28
|
+
["TDS_DYNAMIC2", 0x62, 4],
|
29
|
+
["TDS_EED", 0xE5, 2],
|
30
|
+
["TDS_ENVCHANGE", 0xE3, 2],
|
31
|
+
["TDS_ERROR", 0xAA, 2],
|
32
|
+
["TDS_EVENTNOTICE", 0xA2, 2],
|
33
|
+
["TDS_INFO", 0xAB, 2],
|
34
|
+
["TDS_KEY", 0xCA, nil],
|
35
|
+
["TDS_LANGUAGE", 0x21, 4],
|
36
|
+
["TDS_LOGINACK", 0xAD, 2],
|
37
|
+
["TDS_LOGOUT", 0x71, [1]],
|
38
|
+
["TDS_MSG", 0x65, 1, 1],
|
39
|
+
["TDS_OFFSET", 0x78, [4]],
|
40
|
+
["TDS_OPTIONCMD", 0xA6, 2],
|
41
|
+
["TDS_ORDERBY", 0xA9, 2],
|
42
|
+
["TDS_ORDERBY2", 0x22, 4],
|
43
|
+
["TDS_PARAMFMT", 0xEC, 2],
|
44
|
+
["TDS_PARAMFMT2", 0x20, 4],
|
45
|
+
["TDS_PARAMS", 0xD7, nil],
|
46
|
+
["TDS_RETURNSTATUS", 0x79, [4]],
|
47
|
+
["TDS_RETURNVALUE", 0xAC, 2],
|
48
|
+
["TDS_ROW", 0xD1, nil],
|
49
|
+
["TDS_ROWFMT", 0xEE, 2],
|
50
|
+
["TDS_ROWFMT2", 0x61, 4],
|
51
|
+
["TDS_TABNAME", 0xA4, 2]
|
52
|
+
]
|
53
|
+
|
54
|
+
NAMES = {}
|
55
|
+
FIXED_LENGTHS = {}
|
56
|
+
LENGTH_FIELD_SIZES = {}
|
57
|
+
STREAM_CLASSES = {}
|
58
|
+
|
59
|
+
TOKENS.each(){|values|
|
60
|
+
name = values[0]
|
61
|
+
token = values[1]
|
62
|
+
length = values[2]
|
63
|
+
|
64
|
+
self.const_set(name,token)
|
65
|
+
|
66
|
+
NAMES[token] = name
|
67
|
+
if(length)
|
68
|
+
if(length.instance_of?(Array))
|
69
|
+
FIXED_LENGTHS[token] = length.first
|
70
|
+
else
|
71
|
+
LENGTH_FIELD_SIZES[token] = length
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class_file_name = name.downcase
|
76
|
+
if(File.exist?("lib/tds_packages/#{class_file_name}.rb"))
|
77
|
+
require "lib/tds_packages/#{class_file_name}"
|
78
|
+
class_name = class_file_name.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
79
|
+
cl = eval(class_name)
|
80
|
+
STREAM_CLASSES[token] = cl
|
81
|
+
end
|
82
|
+
}
|
83
|
+
|
84
|
+
def self.token?(token)
|
85
|
+
NAMES.include?(token)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.token_name(token)
|
89
|
+
NAMES[token]
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.token_class(token)
|
93
|
+
STREAM_CLASSES[token]
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.fixed_length(token)
|
97
|
+
FIXED_LENGTHS[token]
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.length_field_size(token)
|
101
|
+
LENGTH_FIELD_SIZES[token]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/tds_types.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Syspy
|
2
|
+
class TdsTypes
|
3
|
+
TYPES = [
|
4
|
+
[0x2f, "SYBCHAR" , "char" , -1, -1, 1, false, false, String ],
|
5
|
+
[0x27, "SYBVARCHAR" , "varchar" , -1, -1, 1, false, false, String ],
|
6
|
+
[0x26, "SYBINTN" , "int" , -1, 10, 11, true , false, Integer ],
|
7
|
+
[0x30, "SYBINT1" , "tinyint" , 1, 3, 4, false, false, Integer ],
|
8
|
+
[0x34, "SYBINT2" , "smallint" , 2, 5, 6, true , false, Integer ],
|
9
|
+
[0x38, "SYBINT4" , "int" , 4, 10, 11, true , false, Integer ],
|
10
|
+
[0x7f, "SYBINT8" , "bigint" , 8, 19, 20, true , false, Integer ],
|
11
|
+
[0x3e, "SYBFLT8" , "float" , 8, 15, 24, true , false, Float ],
|
12
|
+
[0x3d, "SYBDATETIME" , "datetime" , 8, 23, 23, false, false, Time ],
|
13
|
+
[0x32, "SYBBIT" , "bit" , 1, 1, 1, false, false, Integer ],
|
14
|
+
[0x23, "SYBTEXT" , "text" , -4, -1, -1, false, true , String ],
|
15
|
+
[0x63, "SYBNTEXT" , "ntext" , -4, -1, -1, false, true , String ],
|
16
|
+
[0xae, "SYBUNITEXT" , "unitext" , -4, -1, -1, false, true , String ],
|
17
|
+
[0x22, "SYBIMAGE" , "image" , -4, -1, -1, false, false, String ],
|
18
|
+
[0x7a, "SYBMONEY4" , "smallmoney" , 4, 10, 12, true , false, Integer ],
|
19
|
+
[0x3c, "SYBMONEY" , "money" , 8, 19, 21, true , false, Integer ],
|
20
|
+
[0x31, "SYBDATETIME4" , "smalldatetime" , 4, 16, 19, false, false, Time ],
|
21
|
+
[0x3b, "SYBREAL" , "real" , 4, 7, 14, true , false, Integer ],
|
22
|
+
[0x2d, "SYBBINARY" , "binary" , -1, -1, 2, false, false, String ],
|
23
|
+
[0x1f, "SYBVOID" , "void" , -1, 1, 1, false, false, nil ],
|
24
|
+
[0x25, "SYBVARBINARY" , "varbinary" , -1, -1, -1, false, false, String ],
|
25
|
+
[0xa7, "SYBNVARCHAR" , "nvarchar" , -1, -1, -1, false, false, String ],
|
26
|
+
[0x68, "SYBBITN" , "bit" , -1, 1, 1, false, false, Integer ],
|
27
|
+
[0x6c, "SYBNUMERIC" , "numeric" , -1, -1, -1, true , false, Integer ],
|
28
|
+
[0x61, "SYBDECIMAL" , "decimal" , -1, -1, -1, true , false, Integer ],
|
29
|
+
[0x6d, "SYBFLTN" , "float" , -1, 15, 24, true , false, Float ],
|
30
|
+
[0x6e, "SYBMONEYN" , "money" , -1, 19, 21, true , false, Integer ],
|
31
|
+
[0x6f, "SYBDATETIMN" , "datetime" , -1, 23, 23, false, false, Time ],
|
32
|
+
[0x31, "SYBDATE" , "date" , 4, 10, 10, false, false, Time ],
|
33
|
+
[0x33, "SYBTIME" , "time" , 4, 8, 8, false, false, Time ],
|
34
|
+
[0x7b, "SYBDATEN" , "date" , -1, 10, 10, false, false, Time ],
|
35
|
+
[0x93, "SYBTIMEN" , "time" , -1, 8, 8, false, false, Time ],
|
36
|
+
[0xaf, "XSYBCHAR" , "char" , -2, -1, -1, false, true , String ],
|
37
|
+
[0xa7, "XSYBVARCHAR" , "varchar" , -2, -1, -1, false, true , String ],
|
38
|
+
[0xe7, "XSYBNVARCHAR" , "nvarchar" , -2, -1, -1, false, true , String ],
|
39
|
+
[0xef, "XSYBNCHAR" , "nchar" , -2, -1, -1, false, true , String ],
|
40
|
+
[0xa5, "XSYBVARBINARY" , "varbinary" , -2, -1, -1, false, false, String ],
|
41
|
+
[0xad, "XSYBBINARY" , "binary" , -2, -1, -1, false, false, String ],
|
42
|
+
[0xe1, "SYBLONGBINARY" , "varbinary" , -5, -1, 2, false, false, String ],
|
43
|
+
[0x40, "SYBSINT1" , "tinyint" , 1, 2, 3, false, false, Integer ],
|
44
|
+
[0x41, "SYBUINT2" , "unsigned smallint", 2, 5, 6, false, false, Integer ],
|
45
|
+
[0x42, "SYBUINT4" , "unsigned int" , 4, 10, 11, false, false, Integer ],
|
46
|
+
[0x43, "SYBUINT8" , "unsigned bigint" , 8, 20, 20, false, false, Integer ],
|
47
|
+
[0x44, "SYBUINTN" , "unsigned int" , -1, 10, 11, true , false, Integer ],
|
48
|
+
[0x24, "SYBUNIQUE" , "uniqueidentifier" , -1, 36, 36, false, false, String ],
|
49
|
+
[0x62, "SYBVARIANT" , "sql_variant" , -5, 0, 8000, false, false, String ],
|
50
|
+
[0xbf, "SYBSINT8" , "bigint" , 8, 19, 20, true , false, Integer ],
|
51
|
+
[0xf1, "XML" , "xml" , -4, -1, -1, false, true , String ]
|
52
|
+
]
|
53
|
+
|
54
|
+
NAMES = {}
|
55
|
+
RUBY_TYPES = {}
|
56
|
+
FIXED_LENGTHS = {}
|
57
|
+
SIGNED = {}
|
58
|
+
|
59
|
+
TYPES.each(){|type|
|
60
|
+
code = type[0]
|
61
|
+
name = type[1]
|
62
|
+
length = type[3]
|
63
|
+
signed = type[6]
|
64
|
+
ruby_type = type[8]
|
65
|
+
|
66
|
+
const_set(name,code)
|
67
|
+
|
68
|
+
NAMES[code] = name
|
69
|
+
FIXED_LENGTHS[code] = length
|
70
|
+
SIGNED[code] = signed
|
71
|
+
RUBY_TYPES[code] = ruby_type
|
72
|
+
}
|
73
|
+
|
74
|
+
def self.name(code)
|
75
|
+
NAMES[code]
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.numeric?(code)
|
79
|
+
code == SYBNUMERIC || code == SYBDECIMAL
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.fixed_length(code)
|
83
|
+
FIXED_LENGTHS[code]
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.signed?(code)
|
87
|
+
SIGNED[code]
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.ruby_type(code)
|
91
|
+
RUBY_TYPES[code]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/syspy.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "lib/log"
|
3
|
+
require "lib/tds_package_stream"
|
4
|
+
require "time"
|
5
|
+
|
6
|
+
|
7
|
+
module Syspy
|
8
|
+
if(__FILE__ == $0)
|
9
|
+
if(ARGV.size != 3)
|
10
|
+
puts "Usage: sudo ruby syspy.rb <interface> <destination_ip> <destination_port>"
|
11
|
+
exit(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
if(`id -u`.strip != "0")
|
15
|
+
puts "Has to be run as root"
|
16
|
+
exit(2)
|
17
|
+
end
|
18
|
+
|
19
|
+
INTERFACE = ARGV[0]
|
20
|
+
DST = ARGV[1]
|
21
|
+
DST_PORT = ARGV[2]
|
22
|
+
|
23
|
+
# INTERFACE = "eth0"
|
24
|
+
# DST = "192.168.1.6"
|
25
|
+
# DST_PORT = "2048"
|
26
|
+
|
27
|
+
stream = TdsPackageStream.new(INTERFACE,DST,DST_PORT)
|
28
|
+
stream.each_package(){|package|
|
29
|
+
if(package.instance_of?(TdsLanguage))
|
30
|
+
statement = package.language_text
|
31
|
+
replaced_statement = "#{statement}"
|
32
|
+
|
33
|
+
index = 0
|
34
|
+
if(package.tds_params)
|
35
|
+
params = package.tds_params.parameters
|
36
|
+
statement.scan(/@sql\d+[^ ]+/){|match|
|
37
|
+
param = params[index]
|
38
|
+
if(param.instance_of?(String))
|
39
|
+
replaced_statement.gsub!(match,"\"#{param}\"")
|
40
|
+
elsif(param.kind_of?(Numeric))
|
41
|
+
replaced_statement.gsub!(match,param.to_s)
|
42
|
+
elsif(param.instance_of?(Time))
|
43
|
+
replaced_statement.gsub!(match,"\"#{param.strftime("%Y%m%d %H:%M:%S")}\"")
|
44
|
+
end
|
45
|
+
index += 1
|
46
|
+
}
|
47
|
+
end
|
48
|
+
Log.info("Replaced parameters: #{index}\n\n#{replaced_statement.gsub("\n","\r\n")}\n------------------------------------------------------")
|
49
|
+
end
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syspy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matthias Balmer
|
@@ -40,6 +40,17 @@ extensions: []
|
|
40
40
|
extra_rdoc_files: []
|
41
41
|
|
42
42
|
files:
|
43
|
+
- syspy.rb
|
44
|
+
- lib/bytes.rb
|
45
|
+
- lib/tds_types.rb
|
46
|
+
- lib/tds_tokens.rb
|
47
|
+
- lib/tds_package_stream.rb
|
48
|
+
- lib/tds_packages/tds_paramfmt2.rb
|
49
|
+
- lib/tds_packages/tds_params.rb
|
50
|
+
- lib/tds_packages/tds_paramfmt.rb
|
51
|
+
- lib/tds_packages/tds_language.rb
|
52
|
+
- lib/log.rb
|
53
|
+
- lib/tds_package.rb
|
43
54
|
- bin/syspy
|
44
55
|
homepage:
|
45
56
|
licenses: []
|