rstruct 0.1.0 → 0.1.1
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.
- data/README.rdoc +10 -8
- data/Rakefile +1 -1
- data/TODO.rdoc +10 -0
- data/VERSION +1 -1
- data/lib/rstruct/base_types/array_type.rb +42 -0
- data/lib/rstruct/base_types/container_type.rb +20 -6
- data/lib/rstruct/base_types/packed_type.rb +2 -2
- data/lib/rstruct/base_types/type.rb +1 -1
- data/lib/rstruct/base_types.rb +1 -0
- data/lib/rstruct/field.rb +2 -3
- data/lib/rstruct/struct_builder.rb +6 -1
- data/lib/rstruct/structure.rb +6 -13
- data/lib/rstruct/types.rb +1 -1
- data/lib/rstruct.rb +1 -1
- data/samples/pe.rb +262 -0
- data/spec/array_spec.rb +24 -0
- data/spec/structure_spec.rb +55 -1
- data/spec/type_behaviors.rb +8 -48
- metadata +16 -11
data/README.rdoc
CHANGED
@@ -9,7 +9,7 @@ once, and use them in many ways as you see fit. Kind-of like... C structs in
|
|
9
9
|
system/API header files.
|
10
10
|
|
11
11
|
The structure declaration syntax emulates C structures syntax, although Rstruct
|
12
|
-
is not intended to be able to parse C structures from C header files. (
|
12
|
+
is not intended to be able to parse C structures from C header files. (however
|
13
13
|
an addon could conceivably be built to do so with minimal fuss)
|
14
14
|
|
15
15
|
Multiple approaches to parsing and building are supported and more can be added
|
@@ -21,10 +21,10 @@ as well as buffers.
|
|
21
21
|
Rstruct was written because the authors wanted something comparable to C
|
22
22
|
structures without having a strong need for extra 'magic' in parsing or
|
23
23
|
building structures. While there exist numerous options in this space, they
|
24
|
-
all seem to have suffered from a combination of performance and interface
|
25
|
-
which limit their potential (and in some cases, spotty maintenance).
|
26
|
-
tried pretty much all of these alternatives (and even contributed to a
|
27
|
-
in previous projects, the author still decided to write rstruct.
|
24
|
+
all seem to have suffered from a combination of performance and interface
|
25
|
+
issues which limit their potential (and in some cases, spotty maintenance).
|
26
|
+
Having, tried pretty much all of these alternatives (and even contributed to a
|
27
|
+
few) in previous projects, the author still decided to write rstruct.
|
28
28
|
|
29
29
|
That said, the author does not claim Rstruct to be superior to any others, it's
|
30
30
|
just another approach. There are several other excellent binary structure
|
@@ -58,7 +58,8 @@ Here is a trivial example defining and packing a raw structure
|
|
58
58
|
# => "\x00\x00\x00A\x00\x00\x00B\x00\x00\x00C" # on a big endian machine
|
59
59
|
|
60
60
|
|
61
|
-
Here is a fully functional Rstruct parser example using Apple's FAT file
|
61
|
+
Here is a fully functional Rstruct parser example using Apple's FAT file
|
62
|
+
structure.
|
62
63
|
|
63
64
|
# To compare the structs to their C counterparts, see:
|
64
65
|
# http://fxr.watson.org/fxr/source/EXTERNAL_HEADERS/mach-o/fat.h?v=xnu-1228
|
@@ -100,7 +101,7 @@ Here is a fully functional Rstruct parser example using Apple's FAT file structu
|
|
100
101
|
|
101
102
|
fat_arch = get_type(:fat_arch)
|
102
103
|
(head.nfat_arch).times do |i|
|
103
|
-
arch = fat_arch.read(f) # note, it reads to a
|
104
|
+
arch = fat_arch.read(f) # note, it reads to a new object on each arch
|
104
105
|
puts " Architecture #{i}:"
|
105
106
|
puts " " << dump(arch).join("\n ")
|
106
107
|
puts
|
@@ -128,7 +129,8 @@ Here is a fully functional Rstruct parser example using Apple's FAT file structu
|
|
128
129
|
align = 0x0000000c
|
129
130
|
|
130
131
|
|
131
|
-
Please refer to rdoc, the samples/ directory, and unit tests for more
|
132
|
+
Please refer to rdoc, the samples/ directory, and unit tests for more
|
133
|
+
information.
|
132
134
|
|
133
135
|
== Copyright
|
134
136
|
|
data/Rakefile
CHANGED
data/TODO.rdoc
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
== TODO
|
2
|
+
|
3
|
+
* DOCUMENTATION!
|
4
|
+
* add a type for unions
|
5
|
+
* really no good reason why we can't support variable length data
|
6
|
+
* add types for odd byte-alignment values like 24-bit, 12-bit, 48-bit, etc.
|
7
|
+
* add a type for handling bitfields
|
8
|
+
* add instance helpers for resolving enums forward and back
|
9
|
+
|
10
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'rstruct/registry'
|
2
|
+
require 'rstruct/field'
|
3
|
+
require 'rstruct/base_types'
|
4
|
+
|
5
|
+
module Rstruct
|
6
|
+
class ArrayContainer < Array
|
7
|
+
include ContainerMixins
|
8
|
+
end
|
9
|
+
|
10
|
+
class ArrayType < ContainerType
|
11
|
+
attr_reader :count
|
12
|
+
|
13
|
+
def initialize(name, typ, count, opts={}, &block)
|
14
|
+
opts = opts.dup
|
15
|
+
lkupreg = (opts.delete(:fields_from) || opts.delete(:register) || Rstruct.default_registry)
|
16
|
+
super(name, opts.merge(:register => false), &block)
|
17
|
+
@count = count
|
18
|
+
unless @rtype = lkupreg[typ]
|
19
|
+
raise(InvalidTypeError, "invalid array type #{typ.inspect}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def typ
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def fields
|
28
|
+
@fields ||= Array.new(self.count){|i| Field.new(i, @rtype, nil, nil) }.freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
def instance(*values)
|
32
|
+
vals = values.flatten
|
33
|
+
ary = ArrayContainer.new(self.count){|f| vals.shift }
|
34
|
+
ary.rstruct_type = self
|
35
|
+
return ary
|
36
|
+
end
|
37
|
+
|
38
|
+
def offset_of(idx)
|
39
|
+
rtype.sizeof * idx
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -30,19 +30,19 @@ module Rstruct
|
|
30
30
|
vals = (pvals.respond_to?(:values) ? pvals.values : pvals)
|
31
31
|
vals ||= self.values
|
32
32
|
|
33
|
-
opos = dst.pos
|
33
|
+
opos = dst.pos if dst.respond_to?(:pos)
|
34
34
|
typ.fields.each_with_index do |f, i|
|
35
35
|
fldval = vals[i]
|
36
36
|
if fldval.respond_to?(:write)
|
37
37
|
fldval.write(dst, fldval)
|
38
38
|
else
|
39
|
-
dst.write(f.typ.pack_value(fldval, self))
|
39
|
+
dst.write(f.typ.pack_value((fldval || 0), self))
|
40
40
|
end
|
41
41
|
end
|
42
42
|
if dst.is_a?(StringIO) and pvals.nil?
|
43
43
|
dst.pos = opos
|
44
44
|
return(dst.read)
|
45
|
-
|
45
|
+
elsif opos and dst.respond_to?(:pos)
|
46
46
|
return dst.pos - opos
|
47
47
|
end
|
48
48
|
end
|
@@ -81,22 +81,36 @@ module Rstruct
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def field_names
|
84
|
-
|
84
|
+
self.fields.map{|f| f.name }
|
85
85
|
end
|
86
86
|
|
87
87
|
def field_types
|
88
|
-
|
88
|
+
self.fields.map{|f| f.typ }
|
89
89
|
end
|
90
90
|
|
91
91
|
def read(raw, obj=nil)
|
92
92
|
raw = StringIO.new(raw) if raw.is_a?(String)
|
93
93
|
obj = self.instance()
|
94
94
|
fields.each do |f|
|
95
|
-
obj[f.name] = f.read(raw, obj)
|
95
|
+
obj[f.name] = f.typ.read(raw, obj)
|
96
96
|
end
|
97
97
|
return obj
|
98
98
|
end
|
99
99
|
|
100
|
+
def claim_value(vals, obj=nil)
|
101
|
+
if @claim_cb
|
102
|
+
@claim_cb.call(vals, obj)
|
103
|
+
else
|
104
|
+
# create our struct container
|
105
|
+
s = instance()
|
106
|
+
|
107
|
+
# iterate through the fields assigning values in the
|
108
|
+
# container and pass it along with values to each
|
109
|
+
# field's claim_value method.
|
110
|
+
self.fields.do {|f| s[f.name] = f.typ.claim_value(vals,s) }
|
111
|
+
return s
|
112
|
+
end
|
113
|
+
end
|
100
114
|
end
|
101
115
|
|
102
116
|
end
|
@@ -61,10 +61,10 @@ module Rstruct
|
|
61
61
|
|
62
62
|
class PackedType < Type
|
63
63
|
include Packable
|
64
|
-
attr_reader :
|
64
|
+
attr_reader :sizeof, :format
|
65
65
|
|
66
66
|
def initialize(name, size, format, opts={}, &block)
|
67
|
-
@
|
67
|
+
@sizeof = size
|
68
68
|
@format = format
|
69
69
|
@groupable = true
|
70
70
|
super(name, opts, &block)
|
data/lib/rstruct/base_types.rb
CHANGED
data/lib/rstruct/field.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
module Rstruct
|
2
2
|
class Field
|
3
|
-
attr_reader :name, :
|
3
|
+
attr_reader :name, :typ, :args, :block
|
4
4
|
|
5
|
-
def initialize(name, typ,
|
5
|
+
def initialize(name, typ, args, block)
|
6
6
|
@name=name
|
7
7
|
@typ=typ
|
8
|
-
@typ_name = typ_name || typ
|
9
8
|
@args=args
|
10
9
|
@block=block
|
11
10
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rstruct/registry'
|
2
2
|
require 'rstruct/field'
|
3
|
+
require 'rstruct/base_types/array_type'
|
3
4
|
|
4
5
|
module Rstruct
|
5
6
|
|
@@ -13,7 +14,11 @@ module Rstruct
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def field(name, typ, typ_name, *args, &block)
|
16
|
-
@__fields << Field.new(name,typ,
|
17
|
+
@__fields << Field.new(name,typ,args,block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def array(name, typ, count, opts={}, &block)
|
21
|
+
@__fields << ArrayType.new(name, typ, count, opts, &block)
|
17
22
|
end
|
18
23
|
|
19
24
|
def method_missing(typ_arg, *args, &block)
|
data/lib/rstruct/structure.rb
CHANGED
@@ -40,20 +40,13 @@ module Rstruct
|
|
40
40
|
return s
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
s = instance()
|
49
|
-
|
50
|
-
# iterate through the fields assigning values in the
|
51
|
-
# container and pass it along with values to each
|
52
|
-
# field's claim_value method.
|
53
|
-
self.fields.do {|f| s[f.name] = f.typ.claim_value(vals,s) }
|
54
|
-
|
55
|
-
return s
|
43
|
+
def offset_of(fld)
|
44
|
+
o = 0
|
45
|
+
self.fields.each do |f|
|
46
|
+
return o if f.name == fld
|
47
|
+
o += f.sizeof
|
56
48
|
end
|
49
|
+
raise(InvalidTypeError, "Invalid type: #{fld}")
|
57
50
|
end
|
58
51
|
end
|
59
52
|
end
|
data/lib/rstruct/types.rb
CHANGED
data/lib/rstruct.rb
CHANGED
data/samples/pe.rb
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'rstruct'
|
3
|
+
|
4
|
+
module Pe
|
5
|
+
extend Rstruct::ClassMethods
|
6
|
+
|
7
|
+
PE_REGISTRY = Rstruct::Registry.new(:portable_executable)
|
8
|
+
|
9
|
+
def self.registry
|
10
|
+
PE_REGISTRY
|
11
|
+
end
|
12
|
+
|
13
|
+
typedef :uint16le, :WORD, :register => registry()
|
14
|
+
typedef :uint32le, :LONG, :register => registry()
|
15
|
+
typedef :uint32le, :DWORD, :register => registry()
|
16
|
+
|
17
|
+
IMAGE_DOS_HEADER = struct(:IMAGE_DOS_HEADER, :register => registry()) {
|
18
|
+
WORD :magic # magic number
|
19
|
+
WORD :cblp # bytes on last page of file
|
20
|
+
WORD :cp # pages in file
|
21
|
+
WORD :crlc # relocations
|
22
|
+
WORD :cparhdr # size of header in paragraphs
|
23
|
+
WORD :minalloc # minimum extra paragraphs needed
|
24
|
+
WORD :maxalloc # maxumum extra paragraphs needed
|
25
|
+
WORD :ss # Initial (relative) SS value
|
26
|
+
WORD :sp # Initial SP value
|
27
|
+
WORD :csum # Checksum
|
28
|
+
WORD :ip # Initial IP value
|
29
|
+
WORD :cs # Initial (relative) CS value
|
30
|
+
WORD :lfarlc # File address of relocation table
|
31
|
+
WORD :ovno # overlay number
|
32
|
+
array :res1, :uint16le, 4
|
33
|
+
WORD :oemid # OEM identifier
|
34
|
+
WORD :oeminfo # OEM information; oem specific
|
35
|
+
array :res2, :uint16le, 10
|
36
|
+
LONG :lfanew # File address of new exe header (PE header)
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
IMAGE_FILE_MACHINE_TYPES = {
|
42
|
+
0 => :UNKNOWN,
|
43
|
+
0x014C => :I386,
|
44
|
+
0x8664 => :AMD64,
|
45
|
+
0x01C0 => :ARM,
|
46
|
+
0x0162 => :R3000,
|
47
|
+
0x0166 => :R4000,
|
48
|
+
0x0168 => :R10000,
|
49
|
+
0x0169 => :WCEMIPSV2,
|
50
|
+
0x0184 => :ALPHA,
|
51
|
+
0x01A2 => :SH3,
|
52
|
+
0x01A3 => :SH3DSP,
|
53
|
+
0x01A4 => :SH3E,
|
54
|
+
0x01A6 => :SH4,
|
55
|
+
0x01A8 => :SH5,
|
56
|
+
0x01C2 => :THUMB,
|
57
|
+
0x01D3 => :AM33,
|
58
|
+
0x01F0 => :POWERPC,
|
59
|
+
0x01F1 => :POWERPCFP,
|
60
|
+
0x0200 => :IA64,
|
61
|
+
0x0266 => :MIPS16,
|
62
|
+
0x0284 => :ALPHA64,
|
63
|
+
0x0366 => :MIPSFPU,
|
64
|
+
0x0466 => :MIPSFPU16,
|
65
|
+
0x0520 => :TRICORE,
|
66
|
+
0x0CEF => :CEF,
|
67
|
+
0x0EBC => :EBC,
|
68
|
+
0x9041 => :M32R,
|
69
|
+
0xC0EE => :CEE,
|
70
|
+
}
|
71
|
+
|
72
|
+
IMAGE_FILE_CHARACTERISTICS_FLAGS = {
|
73
|
+
:RELOCS_STRIPPED => 0x0001,
|
74
|
+
:EXECUTABLE_IMAGE => 0x0002,
|
75
|
+
:LINE_NUMS_STRIPPED => 0x0004,
|
76
|
+
:LOCAL_SYMS_STRIPPED => 0x0008,
|
77
|
+
:AGGRESIVE_WS_TRIM => 0x0010,
|
78
|
+
:LARGE_ADDRESS_AWARE => 0x0020,
|
79
|
+
:BYTES_REVERSED_LO => 0x0080,
|
80
|
+
:MACHINE_32BIT => 0x0100,
|
81
|
+
:DEBUG_STRIPPED => 0x0200,
|
82
|
+
:REMOVABLE_RUN_FROM_SWAP => 0x0400,
|
83
|
+
:NET_RUN_FROM_SWAP => 0x0800,
|
84
|
+
:SYSTEM => 0x1000,
|
85
|
+
:DLL => 0x2000,
|
86
|
+
:UNIPROC_SYSTEM_ONLY => 0x4000,
|
87
|
+
:BYTES_REVERSED_HI => 0x8000,
|
88
|
+
}
|
89
|
+
|
90
|
+
IMAGE_FILE_HEADER = struct(:IMAGE_FILE_HEADER, :register => registry()) {
|
91
|
+
WORD :Machine
|
92
|
+
WORD :NumberOfSections
|
93
|
+
DWORD :TimeDateStamp
|
94
|
+
DWORD :PointerToSymbolTable
|
95
|
+
DWORD :NumberOfSymbols
|
96
|
+
WORD :SizeOfOptionalHeader
|
97
|
+
WORD :Characteristics
|
98
|
+
}
|
99
|
+
|
100
|
+
IMAGE_DATA_DIRECTORY = struct(:IMAGE_DATA_DIRECTORY, :register => registry()) {
|
101
|
+
DWORD :VirtualAddress
|
102
|
+
DWORD :Size
|
103
|
+
}
|
104
|
+
|
105
|
+
IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
|
106
|
+
|
107
|
+
IMAGE_NT_OPTIONAL_HDR_MAGIC = {
|
108
|
+
0x10b => :HDR32,
|
109
|
+
0x20b => :HDR64,
|
110
|
+
0x107 => :ROM,
|
111
|
+
}
|
112
|
+
|
113
|
+
IMAGE_NT_OPTIONAL_HDR_SUBSYSTEMS = [
|
114
|
+
:UNKNOWN,
|
115
|
+
:NATIVE,
|
116
|
+
:WINDOWS_GUI,
|
117
|
+
:WINDOWS_CONSOLE,
|
118
|
+
nil,
|
119
|
+
:OS2_CONSOLE,
|
120
|
+
nil,
|
121
|
+
:POSIX_CONSOLE,
|
122
|
+
:NATIVE_WIN9X_DRIVER,
|
123
|
+
:WINDOWS_CE_GUI,
|
124
|
+
:EFI_APPLICATION,
|
125
|
+
:EFI_BOOT_SERVICE_DRIVER,
|
126
|
+
:EFI_RUNTIME_DRIVER,
|
127
|
+
:EFI_ROM,
|
128
|
+
:XBOX,
|
129
|
+
nil,
|
130
|
+
:WINDOWS_BOOT_APPLICATION,
|
131
|
+
]
|
132
|
+
|
133
|
+
IMAGE_DLL_CHARACTERISTICS_FLAGS = {
|
134
|
+
:DYNAMIC_BASE => 0x0080,
|
135
|
+
:FORCE_INTEGRITY => 0x0080,
|
136
|
+
:NX_COMPAT => 0x0100,
|
137
|
+
:NO_ISOLATION => 0x0200,
|
138
|
+
:NO_SEH => 0x0400,
|
139
|
+
:NO_BIND => 0x0800,
|
140
|
+
:WDM_DRIVER => 0x2000,
|
141
|
+
:TERM_SVR_AWARE => 0x8000,
|
142
|
+
}
|
143
|
+
|
144
|
+
IMAGE_OPTIONAL_HEADER = struct(:IMAGE_OPTIONAL_HEADER, :register => registry()) {
|
145
|
+
WORD :Magic
|
146
|
+
BYTE :MajorLinkerVersion
|
147
|
+
BYTE :MinorLinkerVersion
|
148
|
+
DWORD :SizeOfCode
|
149
|
+
DWORD :SizeOfInitializedData
|
150
|
+
DWORD :SizeOfUninitializedData
|
151
|
+
DWORD :AddressOfEntryPoint
|
152
|
+
DWORD :BaseOfCode
|
153
|
+
DWORD :BaseOfData
|
154
|
+
DWORD :ImageBase
|
155
|
+
DWORD :SectionAlignment
|
156
|
+
DWORD :FileAlignment
|
157
|
+
WORD :MajorOperatingSystemVersion
|
158
|
+
WORD :MinorOperatingSystemVersion
|
159
|
+
WORD :MajorImageVersion
|
160
|
+
WORD :MinorImageVersion
|
161
|
+
WORD :MajorSubsystemVersion
|
162
|
+
WORD :MinorSubsystemVersion
|
163
|
+
DWORD :Win32VersionValue
|
164
|
+
DWORD :SizeOfImage
|
165
|
+
DWORD :SizeOfHeaders
|
166
|
+
DWORD :CheckSum
|
167
|
+
WORD :Subsystem
|
168
|
+
WORD :DllCharacteristics
|
169
|
+
DWORD :SizeOfStackReserve
|
170
|
+
DWORD :SizeOfStackCommit
|
171
|
+
DWORD :SizeOfHeapReserve
|
172
|
+
DWORD :SizeOfHeapCommit
|
173
|
+
DWORD :LoaderFlags
|
174
|
+
DWORD :NumberOfRvaAndSizes
|
175
|
+
|
176
|
+
IMAGE_DATA_DIRECTORY :DDExportTable
|
177
|
+
IMAGE_DATA_DIRECTORY :DDImportTable
|
178
|
+
IMAGE_DATA_DIRECTORY :DDResourceTable
|
179
|
+
IMAGE_DATA_DIRECTORY :DDExceptionTable
|
180
|
+
IMAGE_DATA_DIRECTORY :DDCertificatTable
|
181
|
+
IMAGE_DATA_DIRECTORY :DDBaseRelocationTable
|
182
|
+
IMAGE_DATA_DIRECTORY :DDDebuggingInformation
|
183
|
+
IMAGE_DATA_DIRECTORY :DDArchSpecificData
|
184
|
+
IMAGE_DATA_DIRECTORY :DDGlobalPointerRegisterRVA
|
185
|
+
IMAGE_DATA_DIRECTORY :DDTlsTable
|
186
|
+
IMAGE_DATA_DIRECTORY :DDLoadConfigurationTable
|
187
|
+
IMAGE_DATA_DIRECTORY :DDBoundImportTable
|
188
|
+
IMAGE_DATA_DIRECTORY :DDImportAddressTable
|
189
|
+
IMAGE_DATA_DIRECTORY :DDDelayImportDescriptor
|
190
|
+
IMAGE_DATA_DIRECTORY :DDClrHeader
|
191
|
+
IMAGE_DATA_DIRECTORY :DDReserved
|
192
|
+
}
|
193
|
+
|
194
|
+
IMAGE_NT_HEADERS = struct(:IMAGE_NT_HEADERS, :register => registry()) {
|
195
|
+
DWORD :Signature
|
196
|
+
IMAGE_FILE_HEADER :FileHeader
|
197
|
+
IMAGE_OPTIONAL_HEADER :OptionalHeader
|
198
|
+
}
|
199
|
+
|
200
|
+
IMAGE_SIZEOF_SHORT_NAME = 8
|
201
|
+
|
202
|
+
IMAGE_SECTION_HEADER = struct(:IMAGE_SECTION_HEADER, :register => registry()) {
|
203
|
+
array :Name, :char, 8
|
204
|
+
DWORD :PhysicalAddress
|
205
|
+
DWORD :VirtualAddress
|
206
|
+
DWORD :SizeOfRawData
|
207
|
+
DWORD :PointerToRawData
|
208
|
+
DWORD :PointerToRelocations
|
209
|
+
DWORD :PointerToLinenumbers
|
210
|
+
WORD :NumberOfRelocations
|
211
|
+
WORD :NumberOfLinenumbers
|
212
|
+
DWORD :Characteristics
|
213
|
+
}
|
214
|
+
|
215
|
+
def self.parse_header(*args)
|
216
|
+
Headers.new(*args)
|
217
|
+
end
|
218
|
+
|
219
|
+
class Headers
|
220
|
+
attr_reader :dos, :stub, :coff, :sections, :file_data
|
221
|
+
|
222
|
+
def initialize(input, param = {})
|
223
|
+
input = StringIO.new(input) if input.is_a?(String)
|
224
|
+
@dos = IMAGE_DOS_HEADER.read(input)
|
225
|
+
@stub = input.read(@dos.lfanew - IMAGE_DOS_HEADER.sizeof)
|
226
|
+
@coff = IMAGE_NT_HEADERS.read(input)
|
227
|
+
@sections = Array.new(@coff.FileHeader.NumberOfSections) do
|
228
|
+
IMAGE_SECTION_HEADER.read(input)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# Returns a Time object representation of the
|
233
|
+
# PE header timestamp.
|
234
|
+
def timestamp
|
235
|
+
Time.at(coff.FileHeader.TimeDateStamp)
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns the size of the PE file based on header
|
239
|
+
# information. Specifically, the last section's
|
240
|
+
# PointerToRawData is added to its SizeOfRawData
|
241
|
+
# which should be equal to the file size.
|
242
|
+
def file_size
|
243
|
+
lseg = self.sections.last
|
244
|
+
lseg.PointerToRawData + lseg.SizeOfRawData
|
245
|
+
end
|
246
|
+
|
247
|
+
def repack(io=nil)
|
248
|
+
io ||= StringIO.new
|
249
|
+
self.dos.write(io)
|
250
|
+
io.write(self.stub)
|
251
|
+
self.coff.write(io)
|
252
|
+
self.sections.each{ |s| s.write(io) }
|
253
|
+
io.string if io.is_a?(StringIO)
|
254
|
+
end
|
255
|
+
|
256
|
+
# the checksum value from the parsed header
|
257
|
+
def checksum
|
258
|
+
coff.OptionalHeader.CheckSum
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
end
|
data/spec/array_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'type_behaviors'
|
3
|
+
|
4
|
+
describe Rstruct::ArrayType do
|
5
|
+
context "a basic character array" do
|
6
|
+
before(:each) do
|
7
|
+
@struct = Rstruct.struct(:array_test, :register => false) {
|
8
|
+
array :test_array, :char, 8
|
9
|
+
}
|
10
|
+
|
11
|
+
@pack_format = "aaaaaaaa"
|
12
|
+
|
13
|
+
@instance = @struct.instance
|
14
|
+
|
15
|
+
@rawdata = "ABCDEFGH"
|
16
|
+
@populate = lambda { @rawdata.bytes.each_with_index{|b,i| @instance.test_array[i] = b.chr} }
|
17
|
+
@verify_unpack = lambda {|ret| @rawdata.bytes.each_with_index{|b,i| ret.test_array[i].should == b.chr } }
|
18
|
+
end
|
19
|
+
|
20
|
+
it_should_behave_like "a packable type"
|
21
|
+
it_should_behave_like "a groupable type"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/spec/structure_spec.rb
CHANGED
@@ -1,6 +1,50 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require 'type_behaviors'
|
3
3
|
|
4
|
+
# Applies to structures
|
5
|
+
shared_examples_for "a structure" do
|
6
|
+
|
7
|
+
it "should return a value instance with a reference back to itself" do
|
8
|
+
s = @struct.instance()
|
9
|
+
s.rstruct_type.should == @struct
|
10
|
+
end
|
11
|
+
|
12
|
+
context "struct instance" do
|
13
|
+
it "should expose the same fields as the struct they belong to" do
|
14
|
+
@struct.field_names.each do |name|
|
15
|
+
name = name.to_s if RUBY_VERSION < '1.9'
|
16
|
+
@instance.members.should include(name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow struct field values to be set and retrieved with accessors" do
|
21
|
+
@values.each do |k,v|
|
22
|
+
@instance.__send__(k).should be_nil
|
23
|
+
@instance.__send__("#{k}=", v).should == v
|
24
|
+
@instance.__send__(k).should == v
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should allow field values to be set with arguments to instance creation" do
|
29
|
+
s=@struct.instance(@values)
|
30
|
+
s.rstruct_type.should == @struct
|
31
|
+
@values.each { |k,v| s.__send__(k).should == v }
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should yield itself to a block on instance creation" do
|
35
|
+
i=@struct.instance do |s|
|
36
|
+
@values.each do |k,v|
|
37
|
+
s.__send__(k).should be_nil
|
38
|
+
s.__send__("#{k}=", v).should == v
|
39
|
+
s.__send__(k).should == v
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
@values.each { |k,v| i.__send__(k).should == v }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
4
48
|
describe Rstruct::Structure do
|
5
49
|
context "initialization" do
|
6
50
|
it "requires a block" do
|
@@ -208,6 +252,16 @@ describe Rstruct::Structure do
|
|
208
252
|
it_should_behave_like "a structure"
|
209
253
|
it_should_behave_like "a packable type"
|
210
254
|
it_should_behave_like "a groupable type"
|
255
|
+
|
256
|
+
it "should return the correct offset for fields" do
|
257
|
+
@struct.offset_of(:magic).should == 0
|
258
|
+
@struct.offset_of(:cputype).should == 4
|
259
|
+
@struct.offset_of(:cpusubtype).should == 8
|
260
|
+
@struct.offset_of(:filetype).should == 12
|
261
|
+
@struct.offset_of(:ncmds).should == 16
|
262
|
+
@struct.offset_of(:sizeofcmds).should == 20
|
263
|
+
@struct.offset_of(:flags).should == 24
|
264
|
+
end
|
211
265
|
end
|
212
266
|
|
213
267
|
context "a simple fixed-length nested struct" do
|
@@ -278,7 +332,7 @@ describe Rstruct::Structure do
|
|
278
332
|
double_inner_values.each {|k,v| @instance.inner.double_inner[k] = v}
|
279
333
|
end
|
280
334
|
|
281
|
-
@pack_format = "
|
335
|
+
@pack_format = "NNccaa"
|
282
336
|
@rawdata = "\xde\xad\xbe\xef\xfa\xce\xfe\xeb\x01\x02\x41\x42"
|
283
337
|
|
284
338
|
@verify_unpack = lambda do |ret|
|
data/spec/type_behaviors.rb
CHANGED
@@ -7,23 +7,23 @@ shared_examples_for "a packable type" do
|
|
7
7
|
it "should write raw data and return a string when no output is specified" do
|
8
8
|
@populate.call()
|
9
9
|
ret = @instance.write()
|
10
|
-
ret.should == @rawdata
|
10
|
+
ret.bytes.to_a.should == @rawdata.bytes.to_a
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should write raw data to a string object correctly" do
|
14
14
|
s = "test"
|
15
15
|
@populate.call()
|
16
16
|
ret = @instance.write(s)
|
17
|
-
ret.should == @rawdata
|
18
|
-
s.should == "test
|
17
|
+
ret.bytes.to_a.should == @rawdata.bytes.to_a
|
18
|
+
s.bytes.to_a.should == "test#{@rawdata}".bytes.to_a
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should write raw data to a StringIO object correctly" do
|
22
22
|
sio = StringIO.new
|
23
23
|
@populate.call()
|
24
24
|
ret = @instance.write(sio)
|
25
|
-
ret.should == @rawdata
|
26
|
-
sio.string.should == @rawdata
|
25
|
+
ret.bytes.to_a.should == @rawdata.bytes.to_a
|
26
|
+
sio.string.bytes.to_a.should == @rawdata.bytes.to_a
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should write raw data to a File IO object correctly" do
|
@@ -34,7 +34,7 @@ shared_examples_for "a packable type" do
|
|
34
34
|
ret = @instance.write(tempf)
|
35
35
|
ret.should == @rawdata.size
|
36
36
|
tempf.rewind
|
37
|
-
tempf.read.should == "test
|
37
|
+
tempf.read.bytes.to_a.should == "test#{@rawdata}".bytes.to_a
|
38
38
|
ensure
|
39
39
|
tempf.close if tempf
|
40
40
|
end
|
@@ -81,7 +81,7 @@ shared_examples_for "a packable type" do
|
|
81
81
|
else
|
82
82
|
@values.each {|k,v| ret.__send__(k).should == v }
|
83
83
|
end
|
84
|
-
fio.read().should == testend
|
84
|
+
fio.read().bytes.to_a.should == testend.bytes.to_a
|
85
85
|
ensure
|
86
86
|
fio.close if fio
|
87
87
|
end
|
@@ -96,7 +96,7 @@ shared_examples_for "a packable type" do
|
|
96
96
|
@values.each {|k,v| ret.__send__(k).should == v }
|
97
97
|
end
|
98
98
|
repacked = ret.write()
|
99
|
-
repacked.should == @rawdata
|
99
|
+
repacked.bytes.to_a.should == @rawdata.bytes.to_a
|
100
100
|
end
|
101
101
|
|
102
102
|
end
|
@@ -115,44 +115,4 @@ shared_examples_for "a groupable type" do
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
# Applies to structures
|
119
|
-
shared_examples_for "a structure" do
|
120
|
-
|
121
|
-
it "should return a value instance with a reference back to itself" do
|
122
|
-
s = @struct.instance()
|
123
|
-
s.rstruct_type.should == @struct
|
124
|
-
end
|
125
|
-
|
126
|
-
context "struct instance" do
|
127
|
-
it "should expose the same fields as the struct they belong to" do
|
128
|
-
@struct.field_names.each {|name| @instance.members.should include(name.to_s) }
|
129
|
-
end
|
130
|
-
|
131
|
-
it "should allow struct field values to be set and retrieved with accessors" do
|
132
|
-
@values.each do |k,v|
|
133
|
-
@instance.__send__(k).should be_nil
|
134
|
-
@instance.__send__("#{k}=", v).should == v
|
135
|
-
@instance.__send__(k).should == v
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
it "should allow field values to be set with arguments to instance creation" do
|
140
|
-
s=@struct.instance(@values)
|
141
|
-
s.rstruct_type.should == @struct
|
142
|
-
@values.each { |k,v| s.__send__(k).should == v }
|
143
|
-
end
|
144
|
-
|
145
|
-
it "should yield itself to a block on instance creation" do
|
146
|
-
i=@struct.instance do |s|
|
147
|
-
@values.each do |k,v|
|
148
|
-
s.__send__(k).should be_nil
|
149
|
-
s.__send__("#{k}=", v).should == v
|
150
|
-
s.__send__(k).should == v
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
@values.each { |k,v| i.__send__(k).should == v }
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
118
|
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Eric Monti
|
@@ -14,10 +14,12 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-08-25 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
21
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
22
24
|
requirements:
|
23
25
|
- - ~>
|
@@ -27,11 +29,11 @@ dependencies:
|
|
27
29
|
- 3
|
28
30
|
- 0
|
29
31
|
version: 2.3.0
|
30
|
-
prerelease: false
|
31
32
|
type: :development
|
32
|
-
name: rspec
|
33
33
|
version_requirements: *id001
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
|
+
name: bundler
|
36
|
+
prerelease: false
|
35
37
|
requirement: &id002 !ruby/object:Gem::Requirement
|
36
38
|
requirements:
|
37
39
|
- - ~>
|
@@ -41,11 +43,11 @@ dependencies:
|
|
41
43
|
- 0
|
42
44
|
- 0
|
43
45
|
version: 1.0.0
|
44
|
-
prerelease: false
|
45
46
|
type: :development
|
46
|
-
name: bundler
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
|
+
name: jeweler
|
50
|
+
prerelease: false
|
49
51
|
requirement: &id003 !ruby/object:Gem::Requirement
|
50
52
|
requirements:
|
51
53
|
- - ~>
|
@@ -55,11 +57,11 @@ dependencies:
|
|
55
57
|
- 5
|
56
58
|
- 2
|
57
59
|
version: 1.5.2
|
58
|
-
prerelease: false
|
59
60
|
type: :development
|
60
|
-
name: jeweler
|
61
61
|
version_requirements: *id003
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
|
+
name: rcov
|
64
|
+
prerelease: false
|
63
65
|
requirement: &id004 !ruby/object:Gem::Requirement
|
64
66
|
requirements:
|
65
67
|
- - ">="
|
@@ -67,9 +69,7 @@ dependencies:
|
|
67
69
|
segments:
|
68
70
|
- 0
|
69
71
|
version: "0"
|
70
|
-
prerelease: false
|
71
72
|
type: :development
|
72
|
-
name: rcov
|
73
73
|
version_requirements: *id004
|
74
74
|
description: A library for working with Ruby binary structures in a way similar to c-structs
|
75
75
|
email: esmonti@gmail.com
|
@@ -87,9 +87,11 @@ files:
|
|
87
87
|
- LICENSE.txt
|
88
88
|
- README.rdoc
|
89
89
|
- Rakefile
|
90
|
+
- TODO.rdoc
|
90
91
|
- VERSION
|
91
92
|
- lib/rstruct.rb
|
92
93
|
- lib/rstruct/base_types.rb
|
94
|
+
- lib/rstruct/base_types/array_type.rb
|
93
95
|
- lib/rstruct/base_types/container_type.rb
|
94
96
|
- lib/rstruct/base_types/packed_type.rb
|
95
97
|
- lib/rstruct/base_types/type.rb
|
@@ -99,6 +101,8 @@ files:
|
|
99
101
|
- lib/rstruct/structure.rb
|
100
102
|
- lib/rstruct/types.rb
|
101
103
|
- samples/fatparse.rb
|
104
|
+
- samples/pe.rb
|
105
|
+
- spec/array_spec.rb
|
102
106
|
- spec/registry_behaviors.rb
|
103
107
|
- spec/registry_spec.rb
|
104
108
|
- spec/rstruct_spec.rb
|
@@ -136,6 +140,7 @@ signing_key:
|
|
136
140
|
specification_version: 3
|
137
141
|
summary: A library for working with Ruby binary structures in a way similar to c-structs
|
138
142
|
test_files:
|
143
|
+
- spec/array_spec.rb
|
139
144
|
- spec/registry_behaviors.rb
|
140
145
|
- spec/registry_spec.rb
|
141
146
|
- spec/rstruct_spec.rb
|