forked from platomav/BIOSUtilities
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPortwell_EFI_Extract.py
150 lines (96 loc) · 4.95 KB
/
Portwell_EFI_Extract.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
#!/usr/bin/env python3 -B
# coding=utf-8
"""
Portwell EFI Extract
Portwell EFI Update Extractor
Copyright (C) 2021-2024 Plato Mavropoulos
"""
import logging
import os
from common.comp_efi import efi_decompress, is_efi_compressed
from common.path_ops import make_dirs, safe_name
from common.pe_ops import get_pe_file
from common.patterns import PAT_MICROSOFT_MZ, PAT_PORTWELL_EFI
from common.system import printer
from common.templates import BIOSUtility
from common.text_ops import file_to_bytes
TITLE = 'Portwell EFI Update Extractor v3.0'
FILE_NAMES = {
0: 'Flash.efi',
1: 'Fparts.txt',
2: 'Update.nsh',
3: 'Temp.bin',
4: 'SaveDmiData.efi'
}
def is_portwell_efi(in_file):
""" Check if input is Portwell EFI executable """
in_buffer = file_to_bytes(in_file)
try:
pe_buffer = get_portwell_pe(in_buffer)[1]
except Exception as error: # pylint: disable=broad-except
logging.debug('Error: Could not check if input is Portwell EFI executable: %s', error)
pe_buffer = b''
is_mz = PAT_MICROSOFT_MZ.search(in_buffer[:0x2]) # EFI images start with PE Header MZ
is_uu = PAT_PORTWELL_EFI.search(pe_buffer[:0x4]) # Portwell EFI files start with <UU>
return bool(is_mz and is_uu)
def get_portwell_pe(in_buffer):
""" Get PE of Portwell EFI executable """
pe_file = get_pe_file(in_buffer, silent=True) # Analyze EFI Portable Executable (PE)
pe_data = in_buffer[pe_file.OPTIONAL_HEADER.SizeOfImage:] # Skip EFI executable (pylint: disable=E1101)
return pe_file, pe_data
def portwell_efi_extract(input_file, extract_path, padding=0):
""" Parse & Extract Portwell UEFI Unpacker """
efi_files = [] # Initialize EFI Payload file chunks
input_buffer = file_to_bytes(input_file)
make_dirs(extract_path, delete=True)
pe_file, pe_data = get_portwell_pe(input_buffer)
efi_title = get_unpacker_tag(input_buffer, pe_file)
printer(efi_title, padding)
# Split EFI Payload into <UU> file chunks
efi_list = list(PAT_PORTWELL_EFI.finditer(pe_data))
for idx, val in enumerate(efi_list):
efi_bgn = val.end()
efi_end = len(pe_data) if idx == len(efi_list) - 1 else efi_list[idx + 1].start()
efi_files.append(pe_data[efi_bgn:efi_end])
parse_efi_files(extract_path, efi_files, padding)
def get_unpacker_tag(input_buffer, pe_file):
""" Get Portwell UEFI Unpacker tag """
unpacker_tag_txt = 'UEFI Unpacker'
for pe_section in pe_file.sections:
# Unpacker Tag, Version, Strings etc are found in .data PE section
if pe_section.Name.startswith(b'.data'):
pe_data_bgn = pe_section.PointerToRawData
pe_data_end = pe_data_bgn + pe_section.SizeOfRawData
# Decode any valid UTF-16 .data PE section info to a parsable text buffer
pe_data_txt = input_buffer[pe_data_bgn:pe_data_end].decode('utf-16', 'ignore')
# Search .data for UEFI Unpacker tag
unpacker_tag_bgn = pe_data_txt.find(unpacker_tag_txt)
if unpacker_tag_bgn != -1:
unpacker_tag_len = pe_data_txt[unpacker_tag_bgn:].find('=')
if unpacker_tag_len != -1:
unpacker_tag_end = unpacker_tag_bgn + unpacker_tag_len
unpacker_tag_raw = pe_data_txt[unpacker_tag_bgn:unpacker_tag_end]
# Found full UEFI Unpacker tag, store and slightly beautify the resulting text
unpacker_tag_txt = unpacker_tag_raw.strip().replace(' ', ' ').replace('<', ' <')
break # Found PE .data section, skip the rest
return unpacker_tag_txt
def parse_efi_files(extract_path, efi_files, padding):
""" Process Portwell UEFI Unpacker payload files """
for file_index, file_data in enumerate(efi_files):
if file_data in (b'', b'NULL'):
continue # Skip empty/unused files
file_name = FILE_NAMES.get(file_index, f'Unknown_{file_index}.bin') # Assign Name to EFI file
printer(f'[{file_index}] {file_name}', padding + 4) # Print EFI file name, indicate progress
if file_name.startswith('Unknown_'):
printer(f'Note: Detected new Portwell EFI file ID {file_index}!', padding + 8, pause=True)
file_path = os.path.join(extract_path, safe_name(file_name)) # Store EFI file output path
with open(file_path, 'wb') as out_file:
out_file.write(file_data) # Store EFI file data to drive
# Attempt to detect EFI compression & decompress when applicable
if is_efi_compressed(file_data):
comp_fname = file_path + '.temp' # Store temporary compressed file name
os.replace(file_path, comp_fname) # Rename initial/compressed file
if efi_decompress(comp_fname, file_path, padding + 8) == 0:
os.remove(comp_fname) # Successful decompression, delete compressed file
if __name__ == '__main__':
BIOSUtility(title=TITLE, check=is_portwell_efi, main=portwell_efi_extract).run_utility()