Thaks to jvvh5897 for his research.
He inspired me create a project for the automotion translating of messages in FW.
I looked into FW and found some interesting features what contain in Chinese version. And I thought why we should changing FW if we want to change only messages in FW. One more thanks to jvvh5897 for great idea. All the more so the newer version will be released and we probably wanting to use it.
So the global idea is - Unpack files from file system - Translate their contents (using Google translator for example, probably not only on English) - Pack back to file system - Correct MD5 checksum and the translated FW is ready !!!
I started my project on Python 3, because it is my favourite language, it is crossplatforming and it is very simple to understand.
At first I wrote a class for working with Windriver MemFS file system which was used in this FW.
# version 1.1 build 19-01-2015
# IMPORTANT !!! The file_name is case sensitive !!!
import struct, lzma
class WindRiverFS ():
def __init__(self, compressed = 1, attribute = 0):
self.memfs_sig = b'owowowowowowowowowowowowowowowow'
self.memfs_header_len = 0x2C
self.memfs_directory_item_len = 0x30
self.external_compression = False # Choose True if you want to use external compressor LZMA.EXE
self.memfs_compressed = compressed
self.memfs_attribute = attribute
self.format_fs()
def mount_fs_from_firmware(self, firmware_image):
self.memfs_header_ofs = firmware_image.find(self.memfs_sig)
if self.memfs_header_ofs != -1:
print('Wind River MemFS Header found at : 0x%08x' % self.memfs_header_ofs)
self.temp, self.memfs_compressed, self.memfs_num_files, self.memfs_attribute = struct.unpack_from('>32sLLL', firmware_image, self.memfs_header_ofs)
print('Compressed :', self.memfs_compressed, '\nTotal files :', self.memfs_num_files, '\nMemFS attribute : 0x%04x' % self.memfs_attribute)
self.memfs_directory_ofs = self.memfs_header_ofs + self.memfs_header_len
self.memfs_filedata_ofs = self.memfs_directory_ofs + self.memfs_num_files * self.memfs_directory_item_len
self.format_fs()
for i in range(self.memfs_num_files):
self.f_name, self.f_len, self.f_ofs = struct.unpack_from('>40sLL', firmware_image, self.memfs_directory_ofs + i * self.memfs_directory_item_len)
self.memfs_directory.append(((self.f_name[:self.f_name.find(b'\0')]).decode('utf_8'), self.f_len, firmware_image[self.memfs_header_ofs + self.f_ofs : self.memfs_header_ofs + self.f_ofs + self.f_len]))
self.memfs_end_ofs = self.memfs_header_ofs + self.f_ofs + self.f_len
print('Trace message: End addr : 0x%08x' % self.memfs_end_ofs)
self.memfs_end_ofs += (-self.memfs_end_ofs % 0x04) # Word alignment
self.memfs_body = firmware_image[self.memfs_header_ofs : self.memfs_end_ofs]
print('Wind River MemFS total length : 0x%08x' % len(self.memfs_body))
return True
else:
return False
def put_fs_into_firmware(self, firmware_image):
self.memfs_header_ofs = firmware_image.find(self.memfs_sig)
if self.memfs_header_ofs != -1:
print('Wind River MemFS Header updated at : 0x%08x' % self.memfs_header_ofs)
self.temp_firmware = bytearray(firmware_image)
self.rebuild_fs()
self.temp_memfs_body = self.memfs_body + b'\0' * 4 # 4 zero bytes as mark of the end of FS
self.temp_firmware[self.memfs_header_ofs : self.memfs_header_ofs + len(self.temp_memfs_body)] = self.temp_memfs_body
print('Wind River MemFS total length : 0x%08x' % len(self.memfs_body))
return self.temp_firmware
else:
return None
def dir(self):
return [x[:2] for x in self.memfs_directory]
def read_file(self, file_name):
self.file_body = None
for i in self.memfs_directory:
if i[0] == file_name:
self.file_body = i[2]
if self.memfs_compressed == 1:
if self.external_compression:
# External decompressor call begin
import os
self.f_name = '$tmp7546'
self.f_tmp = open(self.f_name + '.lz', 'wb')
self.f_tmp.write(self.file_body)
self.f_tmp.close()
os.system('lzma.exe d ' + self.f_name + '.lz ' + self.f_name + '.tmp')
self.f_tmp = open(self.f_name + '.tmp', 'rb')
self.file_body = self.f_tmp.read()
self.f_tmp.close()
os.remove(self.f_name + '.tmp')
os.remove(self.f_name + '.lz')
# External decompressor call end
else:
self.file_body = lzma.decompress(self.file_body)
return self.file_body
def delete_file(self, file_name):
self.status = False
for i in self.memfs_directory:
if i[0] == file_name:
self.memfs_directory.remove(i)
self.status = True
return self.status
def append_file(self, file_name, file_body):
self.file_body = file_body
if self.memfs_compressed == 1:
if self.external_compression:
# External compressor call begin
import os
self.f_name = '$tmp7546'
self.f_tmp = open(self.f_name + '.tmp', 'wb')
self.f_tmp.write(self.file_body)
self.f_tmp.close()
os.system('lzma.exe e -lc0 ' + self.f_name + '.tmp ' + self.f_name + '.lz')
self.f_tmp = open(self.f_name + '.lz', 'rb')
self.file_body = self.f_tmp.read()
self.f_tmp.close()
os.remove(self.f_name + '.tmp')
os.remove(self.f_name + '.lz')
# External compressor call end
else:
my_filters = [{"id": lzma.FILTER_LZMA1, "lc": 0}]
self.file_body = bytearray(lzma.compress(self.file_body, format=lzma.FORMAT_ALONE, filters=my_filters))
struct.pack_into('<Q', self.file_body, 5, len(file_body)) # Insert Length of uncompressed file (bug PyLZMA)
self.memfs_directory.append((file_name, len(self.file_body), self.file_body))
def rebuild_fs(self):
self.temp_memfs_header = struct.pack('>32sLLL', self.memfs_sig, self.memfs_compressed, len(self.memfs_directory), self.memfs_attribute)
self.temp_memfs_filetable_len = len(self.memfs_directory) * self.memfs_directory_item_len
self.temp_memfs_curent_file_ofs = self.memfs_header_len + self.temp_memfs_filetable_len
self.temp_memfs_filetable = b''
self.temp_memfs_filedata = b''
for i in self.memfs_directory:
self.temp_memfs_filetable += struct.pack('>40sLL', i[0].encode('utf_8'), i[1], self.temp_memfs_curent_file_ofs)
self.temp_memfs_filedata += i[2] + b'\0' * (-len(i[2]) & 0x03) # Word alignment
self.temp_memfs_curent_file_ofs += i[1] + (-i[1] % 0x04) # Word alignment
self.memfs_body = self.temp_memfs_header + self.temp_memfs_filetable + self.temp_memfs_filedata
def format_fs(self):
self.memfs_directory = []
def get_fs(self):
self.rebuild_fs()
return self.memfs_body
I found what the internal Python LZMA library has no good level of compression, so I implement alternative way in external LZMA utility. The class supports two ways of compression depends on the value of the "external_compression" variable.
And simple example to repack files. It takes a file test_fw_cn.bin as a source firmware file, extracts all MemFS files in subdir \test (must be created before start a program) and repack these files into the firmware, calculates new MD5 check sum and saves it as test_fw_update.bin at the same directory.
import memfs, hashlib
print('Running Wind River MemFS repacker v0.9 14.01.2015')
f_in = open('test_fw_cn.bin', 'rb')
input_fw = f_in.read()
input_fs = memfs.WindRiverFS()
if input_fs.mount_fs_from_firmware(input_fw):
print('Trace message: Successfuly mount input MemFS')
print('Trace message: Compressed : 0x%04x' % input_fs.memfs_compressed)
print('Trace message: Attribute : 0x%04x' % input_fs.memfs_attribute)
temp_dir = input_fs.dir()
i = 0 # exclude CASE SENSITIVE
for temp_file in temp_dir:
print('Trace message: Read file ', temp_file[0])
temp_body = input_fs.read_file(temp_file[0])
t_file = open('test\\%03d' %i + str(temp_file[0]), 'wb')
t_file.write(temp_body)
t_file.close()
i += 1
input_fs.format_fs()
i = 0 # exclude CASE SENSITIVE
for temp_file in temp_dir:
print('Trace message: Write file ', temp_file[0])
t_file = open('test\\%03d' %i + str(temp_file[0]), 'rb')
temp_body = t_file.read()
input_fs.append_file(temp_file[0], temp_body)
t_file.close()
i += 1
output_fw = bytearray(input_fs.put_fs_into_firmware(input_fw))
# MD5
md5_sig = b'\xCC\x96\x28\xEE\x8D\xFB\x21\xBB\x3D\xEF\x6C\xB5\x9F\x77\x4C\x7C'
output_fw[0x04:0x04+0x16] = md5_sig
m = hashlib.md5()
m.update(output_fw)
md5_res = m.digest()
output_fw[0x04:0x04+0x16] = md5_res
# MD5
f_out = open('test_fw_update.bin', 'wb')
f_out.write(output_fw)
f_out.close()
All we should to do is translate a messages.
TO BE CONTIINUE...
(Last edited by tarask1n on 24 Jan 2015, 14:34)