forked from trotsky/insyde-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
executable file
·459 lines (348 loc) · 16.5 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
#! /usr/bin/env python
# Copyright (c) 2009 d6z <d6z@tnymail.com>
# MIT License. Based on code found found at
# http://marcansoft.com/blog/2009/06/enabling-intel-vt-on-the-aspire-8930g/
#~ Permission is hereby granted, free of charge, to any person
#~ obtaining a copy of this software and associated documentation
#~ files (the "Software"), to deal in the Software without
#~ restriction, including without limitation the rights to use,
#~ copy, modify, merge, publish, distribute, sublicense, and/or sell
#~ copies of the Software, and to permit persons to whom the
#~ Software is furnished to do so, subject to the following
#~ conditions:
#~ The above copyright notice and this permission notice shall be
#~ included in all copies or substantial portions of the Software.
#~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#~ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
#~ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
#~ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
#~ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#~ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#~ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#~ OTHER DEALINGS IN THE SOFTWARE.
from __future__ import with_statement
import fsdump
from os import makedirs
from os.path import exists
from lzma import get_lzma_chunks
from struct import unpack_from
from pprint import pprint
from array import array
from dumpsetup import StringTable, Form, FormOp
from util import (md5sum, read_with_progress, find_all, find_all_backwards,
Struct, SI, from_b64, hexbytes, substitute, chexdump, FindBadPosition,
FindStopSearching
)
#~ SAVE_PATH = "data/original_bios-zchef.fd"; WHAT = "zchef"
#~ SAVE_PATH = "data/original_bios-dhlacik.fd"; WHAT = "dhlacik"
#~ SAVE_PATH = "data/original_bios-FaithX.fd"; WHAT = "FaithX"
#~ SAVE_PATH = "data/original_bios-mine.fd"; WHAT = "mine"
#~ SAVE_PATH = "data/KM2_110.fd"; WHAT = "v110"
SAVE_PATH = "data/original_bios-mine.fd"; WHAT = "mine"
KB, MB, GB = 2**10, 2**20, 2**30
# Only necessary to modify these if you are reading from /dev/mem
# If reading from BIOS dump, they are ignored.
# If someone knows how to detect the bios size automatically, that would
# be very useful
BIOS_SIZE = 2*MB
BIOS_START = 4*GB - BIOS_SIZE
class FirmwareVolumeHeader(Struct):
rsvd = SI("<16s")
guid = SI("16s")
size = SI("Q")
magic = SI("4s")
attributes = SI("I")
hdrlen = SI("H")
checksum = SI("H")
rsvd2 = SI("3s")
revision = SI("B")
cruft = SI("16s")
def showinfo(self, depth=0):
print " "*depth, "Reserved boot zone:", hexbytes(self.rsvd)
print " "*depth, "GUID:", hexbytes(self.guid)
print " "*depth, "Size: 0x%x (data 0x%x)" % (self.size, len(self.data))
print " "*depth, "Attributes: 0x%08x" % self.attributes
print " "*depth, "Revision: %d" % self.revision
class VariableHeader(Struct):
magic = SI("<2s")
status = SI("H")
attributes = SI("I")
nsize = SI("I")
dsize = SI("I")
guid = SI("16s")
# Some bioses do not have a checksum.
# Comment me out if your bios does not work
cs = SI("H")
class FirmwareVolume(object):
def __init__(self, buffer, position, where=None):
buffer = buffer[position:]
try:
fvh = FirmwareVolumeHeader(buffer)
assert fvh.magic == "_FVH", "Invalid FirmwareVolume, wrong magic"
assert fvh.hdrlen == FirmwareVolumeHeader.struct_size, (
"Invalid FirmwareVolume, wrong header length "
"0x%04x, expected 0x%04x" %
(fvh.hdrlen, FirmwareVolumeHeader.struct_size)
)
assert fvh.size <= len(buffer), (
"FirmwareVolume too big? "
"size=0x%08x buflen=0x%08x" % (fvh.size, len(buffer))
)
#blockdata = buffer[fvh.py_struct.size:fvh.hdrlen]
self.data = buffer[fvh.hdrlen:fvh.size]
self.position = position
self.where = where
self.good = True
except AssertionError, e:
#print ">>BAD FV at 0x%08x" % position, e
self.good = False
def has_vss(self):
return "$VSS" in self.data
def __repr__(self):
hasvss = " [VSS]" if self.has_vss() else ""
args = (self.position, len(self.data), self.where, hasvss)
return "<FirmVol position=0x%06x size=0x%06x where=%s%s>" % args
class Variable(object):
GLOBAL_VARIABLE = from_b64('Yd/ki8qT0hGqDQDgmAMrjA')
HEADER_MAGIC = "\xAA\x55"
ACTIVE = 0x7F
def __init__(self, complete_data):
header_size = VariableHeader.struct_size
header = complete_data[:header_size]
self.vh = vh = VariableHeader(header)
print "Blah:", hexbytes(vh.guid)
assert vh.magic == self.HEADER_MAGIC, "bad magic 0x%x" % vh.magic
total_length = vh.dsize + vh.nsize
assert len(complete_data) >= total_length, "input not long enough"
data = complete_data[header_size:]
data = data[:total_length]
nullterm = data.index("\x00\x00")+1
strend = nullterm if nullterm < vh.nsize else vh.nsize
self.name = data[:strend].decode("utf-16le")
self.value = data[vh.nsize:total_length]
# Set the checksum to 0, and the status to 0x7F
fdata = substitute(header+data, header_size-2, "\x00\x00")
fdata = substitute( fdata, 2, "\x7F\x00")
self.ccsum = self.checksum(fdata)
#assert self.ccsum == vh.cs, "Checksum Error"
def __repr__(self):
return "<Variable status=0x%02x size=0x%03x name=%s>" % (self.vh.status, self.vh.dsize, self.name)
def __len__(self):
return VariableHeader.struct_size + self.vh.nsize + self.vh.dsize
def checksum(self, data):
if len(data) % 2: data += chr(0)
shorts = array("H", [])
shorts.fromstring(data)
return -sum(shorts) & 0xFFFF
def showinfo(self, ts=''):
print ts+"Variable %s" % repr(self.name)
print ts+" Attributes: 0x%08x"%self.vh.attributes
print ts+" Status: 0x%02x" % self.vh.status
if self.vh.guid == self.GLOBAL_VARIABLE:
print ts + (" VendorGUID: EFI_GLOBAL_VARIABLE (%s)" %
' '.join('%02x'%ord(c) for c in self.vh.guid))
else:
print ts+(" VendorGUID: %s" %
' '.join('%02x'%ord(c) for c in self.vh.guid))
#print ts+" Checksum: 0x%02x"%self.vh.cs
#print ts+" calc 0x%04x"%self.ccsum
print ts+" Value (0x%x bytes):"%(len(self.value))
chexdump(self.value, ts+" ")
class VSSData(object):
def __init__(self, data):
(size,) = unpack_from("I", data[4:])
assert size < len(data), (
"Too big! size = %i len = %i" % (size, len(data))
)
vssdata = data[0x10:size]
self.vars = []
self.size = size
position = 0
while (position < len(data) and
vssdata[position:].startswith(Variable.HEADER_MAGIC)):
print "Creating variable at", position
v = Variable(vssdata[position:])
position += len(v)
self.vars.append(v)
def __repr__(self):
return "<VSSData len=%i size=0x%x>" % (len(self.vars), self.size)
def __iter__(self):
return iter(self.vars)
class BIOS(object):
def __init__(self, from_where=None):
"Create a BIOS object"
bios_data = self.load_bios(from_where)
print "Operating on BIOS %s size = 0x%x" % (SAVE_PATH, len(bios_data))
print "Loading compressed sections"
compressed_chunks = get_lzma_chunks(bios_data)
print " .. found %i compressed sections" % len(compressed_chunks)
print "Locating Firmware Volumes"
volumes = self.locate_firmware_volumes(bios_data)
for position, data in compressed_chunks:
#if False:
#with open("data/fv-compr-0x%08x" % position, "wb") as f:
# Dump the executable with the PE header in the right place
#f.write(data[data.index("MZ"):])
where = "[compr at 0x%x]" % position
volumes.extend(self.locate_firmware_volumes(data, where))
# Only good volumes
volumes = filter(lambda fv: fv.good, volumes)
vol_compr = filter(lambda fv: fv.where and "compr" in fv.where, volumes)
print (" .. found %i FirmwareVolumes (%i compressed)" %
(len(volumes), len(vol_compr)))
setup_utility = self.locate_setup_utility(vol_compr)
TYPE_PE = 0x10
setup_utility_pe = self.get_section_type(setup_utility[1], TYPE_PE)
dump_filename = "data/SetupUtility-%s.pe" % WHAT
if not exists(dump_filename):
pe = setup_utility_pe
with open(dump_filename, "wb") as fd:
fd.write(pe.data)
print "Wrote SetupUtility to %s" % dump_filename
print " Size = 0x%x MD5: %s" % (len(pe.data), md5sum(pe.data))
self.locate_packs(setup_utility_pe.data)
self.locate_vss(volumes)
def locate_vss(self, volumes):
for vss_volume in filter(FirmwareVolume.has_vss, volumes):
print "Have vss_volume:", vss_volume
try:
vssdata = VSSData(vss_volume.data)
except AssertionError, e:
#print " .. failed to load: '%s'" % e
continue
print vss_volume, vssdata
for var in vssdata:
if var.vh.status == Variable.ACTIVE:
print var
if var.name == "Setup":
var.showinfo()
def locate_packs(self, setuputility_binary):
"Searches for Forms and the English StringTable using a set of heuristics"
# 1st byte: upper bits from length: almost certainly zero
# 2-3: Short typecode, 3 == form
# 4-5: 0x0e is the formset opcode, and 0x24 is is length
# This magic string appears three bytes into the header
form_magic = "\x00\x03\x00\x0e\x24"
form_magic_offset = -3
# HiipackHeaderSize
HHS = 6
english_attribute_magic = "\x00" * 4
def create_stringtable(magic_location):
def test_stringtable(poss_header_location):
# We started at attributes, start at lnoff
poss_header_location -= 12
dat = setuputility_binary[poss_header_location:]
lnoff, plnoff, count, attributes = unpack_from("<IIII", dat)
# This check is extraordinarily unlikely to succeed in the case
# we haven't actually found the string table header.
if (magic_location - poss_header_location + HHS == lnoff and
magic_location - poss_header_location + HHS + 8 == plnoff):
string_table_loc = poss_header_location - HHS
stable = StringTable(setuputility_binary, string_table_loc)
raise FindStopSearching(stable)
raise FindBadPosition
result = find_all_backwards(setuputility_binary,
english_attribute_magic,
test_stringtable, magic_location)
if result: raise FindStopSearching(result)
raise FindBadPosition
string_magic = u"eng\x00English".encode("utf-16le")
english_stringtable = find_all(setuputility_binary, string_magic,
create_stringtable)
if not english_stringtable:
raise RuntimeError
#~ english_stringtable.showinfo()
def create_form(location):
location += form_magic_offset
try:
return Form(setuputility_binary, location, english_stringtable)
except AttributeError:
raise FindBadPosition
forms = find_all(setuputility_binary, form_magic, create_form)
found = False
# We've finally reached the bottom layer!
# Now we just search for the location of the VT switch..
for form in forms:
for opcode in form.fetch_opcodes(FormOp.EFI_IFR_ONE_OF_OP):
qid, width, pid, hid = unpack_from("<HBHH", opcode.payload)
prnt_string = english_stringtable[pid]
help_string = english_stringtable[hid]
args = (qid, width, prnt_string, help_string)
if "Vanderpool" in help_string:
found = True
print "Location = 0x%03x<%d>, name='%s' help='%s'" % args
if not found:
print "Sorry, I couldn't locate the VT flag? :("
def get_sections(self, container):
"Return a recursive list of sections"
result = []
for section in container.sections:
result.append(section)
if section.sections:
result.append(self.get_sections(section))
return result
def get_section_type(self, sections, type):
"Return the first section that has type `type`"
for section in sections:
if section.type == type:
return section
def locate_setup_utility(self, volumes):
"Locate the SetupUtility section within `volumes`"
for vol in volumes:
if vol.position != 0x10:
continue
for file in fsdump.FS(vol.data):
FILE_TYPE_CONTAINS_PE = 0x07
if file.type != FILE_TYPE_CONTAINS_PE:
continue
sections = self.get_sections(file)
TYPE_NAME = 0x15
name_section = self.get_section_type(sections[1], TYPE_NAME)
if name_section.name == "SetupUtility":
return sections
raise RuntimeError("Shouldn't get here, seems we couldn't "
"find the SetupUtility :/")
def locate_firmware_volumes(self, data, where=None):
"""Search through `data` looking for firmware volume headers
`where` optionally specifies where it came from (e.g. compressed section)
"""
FVH_OFFSET_WITHIN_HEADER = 0x28
subtract_offset = lambda x: x - FVH_OFFSET_WITHIN_HEADER
items = find_all(data, "_FVH", subtract_offset)
#print "Found the following:", items
return [FirmwareVolume(data, position, where) for position in items]
def retrieve_from_memory(self):
"Download the BIOS directly from memory. Must be root to do this."
try:
with open("/dev/mem", "rb") as memory:
memory.seek(BIOS_START)
print "Reading BIOS data.."
bios_data = read_with_progress(memory, BIOS_SIZE, 2**6*KB)
except IOError, err:
if err.errno == 13:
print "Read error reading '%s' Are you root?" % path
else:
print "Unexpected error"
raise
return bios_data
def load_bios(self, from_where=None):
"If the desired bios file doesn't exist, try to load it from memory"
if from_where is None:
from_where = SAVE_PATH
if not exists(from_where):
bios_data = self.retrieve_from_memory()
print "Saving BIOS to '%s' md5:%s" % (SAVE_PATH, md5sum(bios_data))
with open(SAVE_PATH, "wb") as f_bios:
f_bios.write(bios_data)
else:
with open(from_where, "rb") as f_bios:
bios_data = f_bios.read()
print "Opened BIOS '%s' with md5:%s" % (from_where, md5sum(bios_data))
return bios_data
def main():
if not exists("./data/"):
makedirs("./data/")
bios = BIOS()
print "Done"
if __name__ == "__main__":
main()