resilience 0.0.2 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/axe.rb +18 -0
- data/bin/fcomp.rb +15 -0
- data/bin/pex.rb +16 -0
- data/bin/rarser.rb +15 -0
- data/bin/reach.rb +16 -0
- data/bin/rex.rb +7 -66
- data/bin/rinfo.rb +19 -0
- data/lib/resilience.rb +5 -0
- data/lib/resilience/attribute.rb +21 -4
- data/lib/resilience/cli/all.rb +14 -0
- data/lib/resilience/cli/bin/axe.rb +34 -0
- data/lib/resilience/cli/bin/fcomp.rb +28 -0
- data/lib/resilience/cli/bin/pex.rb +43 -0
- data/lib/resilience/cli/bin/rarser.rb +72 -0
- data/lib/resilience/cli/bin/reach.rb +34 -0
- data/lib/resilience/cli/bin/rex.rb +33 -0
- data/lib/resilience/cli/bin/rinfo.rb +19 -0
- data/lib/resilience/cli/conf.rb +14 -0
- data/lib/resilience/cli/default.rb +17 -0
- data/lib/resilience/cli/disk.rb +40 -0
- data/lib/resilience/cli/file.rb +23 -0
- data/lib/resilience/cli/image.rb +45 -0
- data/lib/resilience/cli/metadata.rb +24 -0
- data/lib/resilience/cli/output.rb +84 -0
- data/lib/resilience/collections/dirs.rb +8 -0
- data/lib/resilience/collections/files.rb +41 -0
- data/lib/resilience/collections/pages.rb +16 -0
- data/lib/resilience/conf.rb +28 -0
- data/lib/resilience/constants.rb +31 -7
- data/lib/resilience/core_ext.rb +39 -0
- data/lib/resilience/fs_dir.rb +3 -0
- data/lib/resilience/fs_dir/dir_base.rb +16 -6
- data/lib/resilience/fs_dir/dir_entry.rb +33 -0
- data/lib/resilience/fs_dir/file_entry.rb +53 -0
- data/lib/resilience/image.rb +33 -0
- data/lib/resilience/mixins/on_image.rb +25 -0
- data/lib/resilience/page.rb +104 -0
- data/lib/resilience/tables.rb +1 -0
- data/lib/resilience/tables/boot.rb +89 -0
- data/lib/resilience/tables/object.rb +6 -2
- data/lib/resilience/tables/system.rb +5 -2
- data/lib/resilience/trees.rb +5 -0
- data/lib/resilience/trees/object_tree.rb +45 -0
- metadata +55 -15
- data/bin/cle.rb +0 -24
- data/bin/clum.py +0 -28
- data/bin/fs.rb +0 -21
- data/bin/ref.rb +0 -49
- data/bin/rels.rb +0 -269
- data/bin/resilience.rb +0 -351
data/lib/resilience/constants.rb
CHANGED
@@ -2,9 +2,9 @@
|
|
2
2
|
# ReFS Constants
|
3
3
|
# Copyright (C) 2015 Red Hat Inc.
|
4
4
|
|
5
|
-
|
5
|
+
FS_SIGNATURE = [0x00, 0x00, 0x00, 0x52, 0x65, 0x46, 0x53, 0x00] # ...ReFS.
|
6
|
+
|
6
7
|
PAGE_SIZE = 0x4000
|
7
|
-
FIRST_PAGE_ADDRESS = FIRST_PAGE_ID * PAGE_SIZE
|
8
8
|
|
9
9
|
ROOT_DIR_ID = [0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0]
|
10
10
|
DIR_ENTRY = 0x20030
|
@@ -14,19 +14,43 @@ DIR_TREE = 0x301
|
|
14
14
|
DIR_LIST = 0x200
|
15
15
|
#DIR_BRANCH = 0x000 ?
|
16
16
|
|
17
|
+
PAGES = {
|
18
|
+
# page id's:
|
19
|
+
:first => 0x1e,
|
20
|
+
|
21
|
+
# virtual page numbers:
|
22
|
+
:root => 0x00,
|
23
|
+
:object_table => 0x02,
|
24
|
+
:object_tree => 0x03
|
25
|
+
}
|
26
|
+
|
17
27
|
ADDRESSES = {
|
28
|
+
# size / bounds
|
29
|
+
:bytes_per_sector => 0x20,
|
30
|
+
:sectors_per_cluster => 0x24,
|
31
|
+
|
18
32
|
# page
|
33
|
+
:page_sequence => 0x08, # shadow pages share the same virtual page number
|
19
34
|
:virtual_page_number => 0x18,
|
20
35
|
:first_attr => 0x30,
|
21
36
|
|
22
|
-
# page 0x1e
|
37
|
+
# on page 0x1e:
|
23
38
|
:system_table_page => 0xA0,
|
24
39
|
|
25
|
-
# system table
|
40
|
+
# on system table:
|
26
41
|
:system_pages => 0x58,
|
27
42
|
|
28
|
-
# generic table
|
29
|
-
|
43
|
+
# generic table:
|
44
|
+
# referenced from start of first attr
|
45
|
+
:object_id => 0x0C,
|
46
|
+
:num_objects => 0x20,
|
47
|
+
|
48
|
+
# referenced from start of table header
|
49
|
+
:table_length => 0x04,
|
30
50
|
|
31
|
-
|
51
|
+
# object tree:
|
52
|
+
:object_tree_start1 => 0x10,
|
53
|
+
:object_tree_end1 => 0x1F,
|
54
|
+
:object_tree_start2 => 0x20,
|
55
|
+
:object_tree_end2 => 0x2F
|
32
56
|
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Resilience Core Ruby Extensions
|
2
|
+
#
|
3
|
+
# Licensed under the MIT license
|
4
|
+
# Copyright (C) 2015 Red Hat, Inc.
|
5
|
+
|
6
|
+
class String
|
7
|
+
def indented(places=1)
|
8
|
+
rjust(places, '0').upcase
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Fixnum
|
13
|
+
def indented(places=1)
|
14
|
+
'0x' + to_s(16).rjust(places, '0').upcase
|
15
|
+
end
|
16
|
+
|
17
|
+
def big_endian_str
|
18
|
+
[self].big_endian_str
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class NilClass
|
23
|
+
def indented(places=1)
|
24
|
+
'0x' + ('0' * places)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Array
|
29
|
+
def big_endian_str
|
30
|
+
str = '0x'
|
31
|
+
value = false
|
32
|
+
reverse_each { |b|
|
33
|
+
next if b == 0 && !value
|
34
|
+
value = true
|
35
|
+
str += b.to_s(16)
|
36
|
+
}
|
37
|
+
str
|
38
|
+
end
|
39
|
+
end
|
data/lib/resilience/fs_dir.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
# Copyright (C) 2014-2015 Red Hat Inc.
|
4
4
|
|
5
5
|
require 'fileutils'
|
6
|
+
require 'resilience/collections/dirs'
|
7
|
+
require 'resilience/collections/files'
|
6
8
|
|
7
9
|
module Resilience
|
8
10
|
module FSDir
|
@@ -14,8 +16,8 @@ module Resilience
|
|
14
16
|
|
15
17
|
def parse_dir_obj(object_id, prefix)
|
16
18
|
object_table = image.object_table
|
17
|
-
@dirs ||=
|
18
|
-
@files ||=
|
19
|
+
@dirs ||= Dirs.new
|
20
|
+
@files ||= Files.new
|
19
21
|
|
20
22
|
page_id = object_table.pages[object_id]
|
21
23
|
page_address = page_id * PAGE_SIZE
|
@@ -90,15 +92,23 @@ module Resilience
|
|
90
92
|
if entry_type == DIR_ENTRY
|
91
93
|
dir_name = key_bytes[4..-1].pack('L*')
|
92
94
|
dir_obj = value.unpack('C*')[0...8]
|
93
|
-
dirs
|
95
|
+
dirs << DirEntry.new(:prefix => prefix,
|
96
|
+
:name => dir_name,
|
97
|
+
:metadata => dir_obj,
|
98
|
+
:record => record)
|
94
99
|
|
95
100
|
dir_obj = [0, 0, 0, 0, 0, 0, 0, 0].concat(dir_obj)
|
96
101
|
parse_dir_obj(dir_obj, "#{prefix}\\#{dir_name}")
|
97
102
|
|
98
103
|
elsif entry_type == FILE_ENTRY
|
99
|
-
|
100
|
-
|
101
|
-
|
104
|
+
filename = key_bytes[4..-1]
|
105
|
+
filename.delete(0)
|
106
|
+
filename = filename.pack('C*')
|
107
|
+
|
108
|
+
files << FileEntry.new(:prefix => prefix,
|
109
|
+
:name => filename,
|
110
|
+
:metadata => value,
|
111
|
+
:record => record)
|
102
112
|
end
|
103
113
|
end
|
104
114
|
end # class DirBase
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# ReFS Directory Dir Entry
|
3
|
+
# Copyright (C) 2014-2015 Red Hat Inc.
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Resilience
|
8
|
+
module FSDir
|
9
|
+
class DirEntry
|
10
|
+
attr_accessor :prefix
|
11
|
+
attr_accessor :name
|
12
|
+
attr_accessor :metadata
|
13
|
+
|
14
|
+
# metadata record
|
15
|
+
attr_accessor :record
|
16
|
+
|
17
|
+
def initialize(args={})
|
18
|
+
@prefix = args[:prefix]
|
19
|
+
@name = args[:name]
|
20
|
+
@metadata = args[:metadata]
|
21
|
+
@record = args[:record]
|
22
|
+
end
|
23
|
+
|
24
|
+
def fullname
|
25
|
+
"#{prefix}\\#{name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def disk_offset
|
29
|
+
image.offset + dir.record.attribute.pos
|
30
|
+
end
|
31
|
+
end # class DirEntry
|
32
|
+
end # module FSDir
|
33
|
+
end # module Resilience
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# ReFS Directory File Entry
|
3
|
+
# Copyright (C) 2014-2015 Red Hat Inc.
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Resilience
|
8
|
+
module FSDir
|
9
|
+
class FileEntry
|
10
|
+
attr_accessor :prefix
|
11
|
+
attr_accessor :name
|
12
|
+
attr_accessor :metadata
|
13
|
+
|
14
|
+
# metadata record
|
15
|
+
attr_accessor :record
|
16
|
+
|
17
|
+
# known metadata attributes
|
18
|
+
attr_accessor :metadata_attrs
|
19
|
+
|
20
|
+
def initialize(args={})
|
21
|
+
@prefix = args[:prefix]
|
22
|
+
@name = args[:name]
|
23
|
+
@metadata = args[:metadata]
|
24
|
+
@record = args[:record]
|
25
|
+
parse_attrs
|
26
|
+
end
|
27
|
+
|
28
|
+
def fullname
|
29
|
+
"#{prefix}\\#{name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def total_offset
|
33
|
+
image.offset + record.attribute.pos
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_attrs
|
37
|
+
metadata_bytes = @metadata.unpack('C*')
|
38
|
+
metadata_dwords = @metadata.unpack('L*')
|
39
|
+
attr1_length = metadata_dwords[0]
|
40
|
+
attr1_dwords = attr1_length/4
|
41
|
+
attr2_length = metadata_dwords[attr1_dwords]
|
42
|
+
attr2_dwords = attr2_length/4
|
43
|
+
attr3_length = metadata_dwords[attr1_dwords + attr2_dwords]
|
44
|
+
# there may be other attrs after this point...
|
45
|
+
|
46
|
+
attr1 = metadata_bytes[0..attr1_length]
|
47
|
+
attr2 = metadata_bytes[attr1_length..attr1_length+attr2_length]
|
48
|
+
attr3 = metadata_bytes[attr1_length+attr2_length..attr1_length+attr2_length+attr3_length]
|
49
|
+
@metadata_attrs = [attr1, attr2, attr3]
|
50
|
+
end
|
51
|
+
end # class FileEntry
|
52
|
+
end # module FSDir
|
53
|
+
end # module Resilience
|
data/lib/resilience/image.rb
CHANGED
@@ -4,22 +4,41 @@
|
|
4
4
|
|
5
5
|
module Resilience
|
6
6
|
class Image
|
7
|
+
include Conf
|
8
|
+
|
7
9
|
attr_accessor :file
|
8
10
|
attr_accessor :offset
|
9
11
|
attr_accessor :opts
|
10
12
|
|
13
|
+
attr_accessor :bytes_per_sector
|
14
|
+
attr_accessor :sectors_per_cluster
|
15
|
+
|
16
|
+
def cluster_size
|
17
|
+
@cluster_size ||= bytes_per_sector * sectors_per_cluster
|
18
|
+
end
|
19
|
+
|
11
20
|
attr_accessor :root_dir
|
12
21
|
attr_accessor :system_table
|
13
22
|
attr_accessor :object_table
|
23
|
+
attr_accessor :object_tree
|
24
|
+
|
25
|
+
# all pages including shadow pages
|
26
|
+
attr_accessor :pages
|
14
27
|
|
15
28
|
def initialize(args={})
|
16
29
|
@file = args[:file] || []
|
17
30
|
end
|
18
31
|
|
19
32
|
def parse
|
33
|
+
parse_bounds
|
34
|
+
|
35
|
+
# each of these is a seperate parsing process,
|
36
|
+
# though later ones may depend on former
|
37
|
+
@pages = Resilience::Page.extract_all if conf.pages?
|
20
38
|
@system_table = Resilience::SystemTable.parse
|
21
39
|
@object_table = Resilience::ObjectTable.parse
|
22
40
|
@root_dir = Resilience::RootDir.parse
|
41
|
+
@object_tree = Resilience::ObjectTree.parse if conf.object_tree?
|
23
42
|
end
|
24
43
|
|
25
44
|
def seek(position)
|
@@ -30,8 +49,22 @@ module Resilience
|
|
30
49
|
@file.pos - offset
|
31
50
|
end
|
32
51
|
|
52
|
+
def total_pos
|
53
|
+
@file.pos
|
54
|
+
end
|
55
|
+
|
33
56
|
def read(len)
|
34
57
|
@file.read(len)
|
35
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def parse_bounds
|
63
|
+
seek(ADDRESSES[:bytes_per_sector])
|
64
|
+
@bytes_per_sector = read(4).unpack('L').first
|
65
|
+
|
66
|
+
seek(ADDRESSES[:sectors_per_cluster])
|
67
|
+
@sectors_per_cluster = read(4).unpack('L').first
|
68
|
+
end
|
36
69
|
end
|
37
70
|
end # module Resilience
|
@@ -12,14 +12,39 @@ module Resilience
|
|
12
12
|
@image ||= Resilience::Image.new
|
13
13
|
end
|
14
14
|
|
15
|
+
def self.store_pos
|
16
|
+
@image_pos ||= []
|
17
|
+
@image_pos.unshift image.pos
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.restore_pos
|
21
|
+
image.seek @image_pos.shift
|
22
|
+
end
|
23
|
+
|
15
24
|
def image
|
16
25
|
OnImage.image
|
17
26
|
end
|
18
27
|
|
28
|
+
def store_pos
|
29
|
+
OnImage.store_pos
|
30
|
+
end
|
31
|
+
|
32
|
+
def restore_pos
|
33
|
+
OnImage.restore_pos
|
34
|
+
end
|
35
|
+
|
19
36
|
module ClassMethods
|
20
37
|
def image
|
21
38
|
OnImage.image
|
22
39
|
end
|
40
|
+
|
41
|
+
def store_pos
|
42
|
+
OnImage.store_pos
|
43
|
+
end
|
44
|
+
|
45
|
+
def restore_pos
|
46
|
+
OnImage.restore_pos
|
47
|
+
end
|
23
48
|
end
|
24
49
|
end # module OnImage
|
25
50
|
end # module Resilience
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# ReFS Page Representation
|
3
|
+
# Copyright (C) 2015 Red Hat Inc.
|
4
|
+
|
5
|
+
require 'resilience/collections/pages'
|
6
|
+
|
7
|
+
module Resilience
|
8
|
+
class Page
|
9
|
+
include OnImage
|
10
|
+
|
11
|
+
attr_accessor :id
|
12
|
+
attr_accessor :contents
|
13
|
+
|
14
|
+
attr_accessor :sequence
|
15
|
+
attr_accessor :virtual_page_number
|
16
|
+
|
17
|
+
attr_accessor :attributes
|
18
|
+
|
19
|
+
attr_accessor :object_id
|
20
|
+
attr_accessor :entries
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@attributes ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.extract_all
|
27
|
+
page_id = PAGES[:first]
|
28
|
+
pages = Pages.new
|
29
|
+
|
30
|
+
image.seek(page_id * PAGE_SIZE)
|
31
|
+
while contents = image.read(PAGE_SIZE)
|
32
|
+
# only pull out metadata pages currently
|
33
|
+
extracted_id = id_from_contents(contents)
|
34
|
+
is_metadata = extracted_id == page_id
|
35
|
+
pages[page_id] = Page.parse(page_id, contents) if is_metadata
|
36
|
+
page_id += 1
|
37
|
+
end
|
38
|
+
|
39
|
+
pages
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.id_from_contents(contents)
|
43
|
+
contents.unpack('S').first
|
44
|
+
end
|
45
|
+
|
46
|
+
def offset
|
47
|
+
id * PAGE_SIZE
|
48
|
+
end
|
49
|
+
|
50
|
+
def attr_start
|
51
|
+
offset + ADDRESSES[:first_attr]
|
52
|
+
end
|
53
|
+
|
54
|
+
def root?
|
55
|
+
virtual_page_number == PAGES[:root]
|
56
|
+
end
|
57
|
+
|
58
|
+
def object_table?
|
59
|
+
virtual_page_number == PAGES[:object_table]
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.parse(id, contents)
|
63
|
+
store_pos
|
64
|
+
|
65
|
+
page = new
|
66
|
+
page.id = id
|
67
|
+
page.contents = contents
|
68
|
+
|
69
|
+
image.seek(page.offset + ADDRESSES[:page_sequence])
|
70
|
+
page.sequence = image.read(4).unpack('L').first
|
71
|
+
|
72
|
+
image.seek(page.offset + ADDRESSES[:virtual_page_number])
|
73
|
+
page.virtual_page_number = image.read(4).unpack('L').first
|
74
|
+
|
75
|
+
unless page.root? || page.object_table?
|
76
|
+
# TODO:
|
77
|
+
#page.parse_attributes
|
78
|
+
#page.parse_metadata
|
79
|
+
end
|
80
|
+
|
81
|
+
restore_pos
|
82
|
+
|
83
|
+
page
|
84
|
+
end
|
85
|
+
|
86
|
+
def has_attributes?
|
87
|
+
!@attributes.nil? && !@attributes.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_attributes
|
91
|
+
image.seek(attr_start)
|
92
|
+
while true
|
93
|
+
attr = Attribute.read
|
94
|
+
break if attr.empty?
|
95
|
+
@attributes << attr
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_metadata
|
100
|
+
@object_id = @attributes.first.unpack("C*")[ADDRESSES[:object_id]]
|
101
|
+
@entries = @attributes.first.unpack("C*")[ADDRESSES[:num_objects]]
|
102
|
+
end
|
103
|
+
end # class Page
|
104
|
+
end # module Resilience
|