Class: Metasm::VirtualString

Inherits:
Object
  • Object
show all
Defined in:
lib/metasm/os/main.rb

Overview

This class implements an objects that behaves like a regular string, but whose real data is dynamically fetched or generated on demand its size is immutable implements a page cache substrings are Strings (small substring) or another VirtualString (a kind of 'window' on the original VString, when the substring length is > 4096)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(addr_start, length) ⇒ VirtualString

Returns a new instance of VirtualString.



179
180
181
182
183
184
185
# File 'lib/metasm/os/main.rb', line 179

def initialize(addr_start, length)
	@addr_start = addr_start
	@length = length
	@pagecache = []
	@pagecache_len = 4
	@pagelength ||= 4096	# must be (1 << x)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(m, *args, &b) ⇒ Object

forwards unhandled messages to a frozen realstring



139
140
141
142
143
144
145
146
# File 'lib/metasm/os/main.rb', line 139

def method_missing(m, *args, &b)
	if ''.respond_to? m
		puts "Using VirtualString.realstring for #{m} from:", caller if $DEBUG
		realstring.freeze.send(m, *args, &b)
	else
		super(m, *args, &b)
	end
end

Instance Attribute Details

#addr_startObject

the real address of our first byte



172
173
174
# File 'lib/metasm/os/main.rb', line 172

def addr_start
  @addr_start
end

#lengthObject

our length



174
175
176
# File 'lib/metasm/os/main.rb', line 174

def length
  @length
end

#pagecacheObject

array of [addr, raw data], sorted by first == last accessed



176
177
178
# File 'lib/metasm/os/main.rb', line 176

def pagecache
  @pagecache
end

#pagecache_lenObject

maximum length of self.pagecache (number of cached pages)



178
179
180
# File 'lib/metasm/os/main.rb', line 178

def pagecache_len
  @pagecache_len
end

Instance Method Details

#=~(o) ⇒ Object

'=~' does not go through method_missing



165
166
167
# File 'lib/metasm/os/main.rb', line 165

def =~(o)
	realstring =~ o
end

#[](from, len = nil) ⇒ Object

formats parameters for reading



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/metasm/os/main.rb', line 71

def [](from, len=nil)
	if not len and from.kind_of? Range
		b = from.begin
		e = from.end
		b = b + length if b < 0
		e = e + length if e < 0
		len = e - b
		len += 1 if not from.exclude_end?
		from = b
	end
	from = from + length if from < 0

	return nil if from > length or (from == length and not len)
	len = length - from if len and from + len > length
	return '' if len == 0

	read_range(from, len)
end

#[]=(from, len, val = nil) ⇒ Object

formats parameters for overwriting portion of the string

Raises:

  • (TypeError)


91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/metasm/os/main.rb', line 91

def []=(from, len, val=nil)
	raise TypeError, 'cannot modify frozen virtualstring' if frozen?

	if not val
		val = len
		len = nil
	end
	if not len and from.kind_of? Range
		b = from.begin
		e = from.end
		b = b + length if b < 0
		e = e + length if e < 0
		len = e - b
		len += 1 if not from.exclude_end?
		from = b
	elsif not len
		len = 1
		val = val.chr
	end
	from = from + length if from < 0

	raise IndexError, 'Index out of string' if from > length
	raise IndexError, 'Cannot modify virtualstring length' if val.length != len or from + len > length

	write_range(from, val)
end

#cache_get_page(addr) ⇒ Object

searches the cache for a page containing addr, updates if not found



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/metasm/os/main.rb', line 205

def cache_get_page(addr)
	addr &= ~(@pagelength-1)
	i = 0
	@pagecache.each { |c|
		if addr == c[0]
			# most recently used first
			@pagecache.unshift @pagecache.delete_at(i) if i != 0
			return c
		end
		i += 1
	}
	@pagecache.pop if @pagecache.length >= @pagecache_len
	c = [addr]
	p = get_page(addr)
	c << p.to_s.ljust(@pagelength, "\0")
	c << true if not p
	@pagecache.unshift c
	c
end

#empty?Boolean

avoid triggering realstring from method_missing if possible

Returns:

  • (Boolean)


149
150
151
# File 'lib/metasm/os/main.rb', line 149

def empty?
	length == 0
end

#index(chr, base = 0) ⇒ Object

avoid triggering realstring from method_missing if possible heavily used in to find 0-terminated strings in ExeFormats



155
156
157
158
159
160
161
162
# File 'lib/metasm/os/main.rb', line 155

def index(chr, base=0)
	return if base >= length or base <= -length
	if i = self[base, 64].index(chr) or i = self[base, @pagelength].index(chr)
		base + i
	else
		realstring.index(chr, base)
	end
end

#invalidateObject

invalidates the page cache



193
194
195
# File 'lib/metasm/os/main.rb', line 193

def invalidate
	@pagecache.clear
end

#page_invalid?(addr) ⇒ Boolean

returns wether a page is valid or not

Returns:

  • (Boolean)


188
189
190
# File 'lib/metasm/os/main.rb', line 188

def page_invalid?(addr)
	cache_get_page(@addr_start+addr)[2]
end

#read_range(from, len) ⇒ Object

reads a range from the page cache returns a new VirtualString (using dup) if the request is bigger than @pagelength bytes



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/metasm/os/main.rb', line 227

def read_range(from, len)
	from += @addr_start
	if not len
		base, page = cache_get_page(from)
		page[from - base]
	elsif len <= @pagelength
		base, page = cache_get_page(from)
		s = page[from - base, len]
		if from+len-base > @pagelength		# request crosses a page boundary
			base, page = cache_get_page(from+len)
			s << page[0, from+len-base]
		end
		s
	else
		# big request: return a new virtual page
		dup(from, len)
	end
end

#realstringObject

returns the full raw data



119
120
121
122
123
124
125
126
127
128
129
# File 'lib/metasm/os/main.rb', line 119

def realstring
	ret = ''
	addr = 0
	len = length
	while len > @pagelength
		ret << self[addr, @pagelength]
		addr += @pagelength
		len -= @pagelength
	end
	ret << self[addr, len]
end

#to_strObject

alias to realstring for bad people checking respond_to? :to_str (like String#<<) XXX alias does not work (not virtual (a la C++))



134
135
136
# File 'lib/metasm/os/main.rb', line 134

def to_str
	realstring
end

#write_range(from, content) ⇒ Object

rewrites a segment of data the length written is the length of the content (a VirtualString cannot grow/shrink)



248
249
250
251
# File 'lib/metasm/os/main.rb', line 248

def write_range(from, content)
	invalidate
	rewrite_at(from + @addr_start, content)
end