Compare commits
3 Commits
ae41d9ce41
...
d50ec846c2
Author | SHA1 | Date | |
---|---|---|---|
d50ec846c2 | |||
7774c948d2 | |||
7c2d040195 |
@ -8,7 +8,10 @@ signatures = [
|
|||||||
matcher.Zip,
|
matcher.Zip,
|
||||||
matcher.Ambarella,
|
matcher.Ambarella,
|
||||||
matcher.SquashFS,
|
matcher.SquashFS,
|
||||||
matcher.FlattenDeviceTree
|
matcher.RomFS,
|
||||||
|
matcher.CromFS,
|
||||||
|
matcher.FlattenDeviceTree,
|
||||||
|
matcher.ELF,
|
||||||
]
|
]
|
||||||
|
|
||||||
def detect(args):
|
def detect(args):
|
||||||
@ -25,6 +28,7 @@ def detect(args):
|
|||||||
print("detected", filetype.name)
|
print("detected", filetype.name)
|
||||||
for m in filetype.matches:
|
for m in filetype.matches:
|
||||||
print(">", m)
|
print(">", m)
|
||||||
|
filetype.view(m)
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
|
@ -11,3 +11,8 @@ from .flatten_device_tree import FlattenDeviceTree
|
|||||||
# file system formats
|
# file system formats
|
||||||
from .squashfs import SquashFS
|
from .squashfs import SquashFS
|
||||||
from .ubifs import UbiFS
|
from .ubifs import UbiFS
|
||||||
|
from .romfs import RomFS
|
||||||
|
from .cromfs import CromFS
|
||||||
|
|
||||||
|
# common executable formats
|
||||||
|
from .elf import ELF
|
||||||
|
111
python/matcher/cromfs.py
Normal file
111
python/matcher/cromfs.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import os
|
||||||
|
import io
|
||||||
|
|
||||||
|
from .matcher import SignatureMatcher, Match
|
||||||
|
|
||||||
|
class CromFS(SignatureMatcher):
|
||||||
|
"""
|
||||||
|
CromFS a readonly file system
|
||||||
|
little-endian
|
||||||
|
https://github.com/deadsy/nuttx/blob/master/tools/gencromfs.c
|
||||||
|
this should be more correct?
|
||||||
|
https://gitea.hedron.io/pixy2040/nuttx/src/branch/master/tools/gencromfs.c
|
||||||
|
|
||||||
|
this CromFS might be a little bit different from the CROMFS here
|
||||||
|
https://bisqwit.iki.fi/src/cromfs-format.txt
|
||||||
|
this ^ has the signature CROMFS03 and probably CROMFS02 for v2
|
||||||
|
this ^ is big-endian
|
||||||
|
"""
|
||||||
|
def __init__(self, file):
|
||||||
|
self.name = "CromFS"
|
||||||
|
self.signature = b'CROM'
|
||||||
|
super().__init__(file)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
for match in self.search():
|
||||||
|
start = match
|
||||||
|
header = io.BytesIO(self.file[start:start+20])
|
||||||
|
magic = header.read(4)
|
||||||
|
nnodes = header.read(2)
|
||||||
|
nblocks = header.read(2)
|
||||||
|
root = header.read(4)
|
||||||
|
fsize = header.read(4)
|
||||||
|
bsize = header.read(4)
|
||||||
|
|
||||||
|
as_num = lambda x: int.from_bytes(x, 'little')
|
||||||
|
nnodes = as_num(nnodes)
|
||||||
|
nblocks = as_num(nblocks)
|
||||||
|
root = as_num(root)
|
||||||
|
fsize = as_num(fsize)
|
||||||
|
bsize = as_num(bsize)
|
||||||
|
if root != 20:
|
||||||
|
continue
|
||||||
|
data = {
|
||||||
|
'nnodes': nnodes,
|
||||||
|
'nblocks': nblocks,
|
||||||
|
'root': root,
|
||||||
|
'bsize': bsize,
|
||||||
|
}
|
||||||
|
self.matches += [Match(start, fsize, data)]
|
||||||
|
|
||||||
|
return len(self.matches) != 0
|
||||||
|
|
||||||
|
def view(self, match):
|
||||||
|
as_num = lambda x: int.from_bytes(x, 'little')
|
||||||
|
|
||||||
|
root = match.data['root']
|
||||||
|
bsize = match.data['bsize']
|
||||||
|
nblocks = match.data['nblocks']
|
||||||
|
nnodes = match.data['nnodes']
|
||||||
|
|
||||||
|
region = io.BytesIO(self.file[match.offset : match.offset + match.length])
|
||||||
|
region.seek(root, os.SEEK_SET)
|
||||||
|
|
||||||
|
def read_str_at(buffer, at=None, recover=False):
|
||||||
|
if at == 0 or at is None:
|
||||||
|
return ''
|
||||||
|
s = b''
|
||||||
|
old = buffer.tell()
|
||||||
|
if at:
|
||||||
|
buffer.seek(at, os.SEEK_SET)
|
||||||
|
while True:
|
||||||
|
c = buffer.read(1)
|
||||||
|
if c == b'\x00':
|
||||||
|
break
|
||||||
|
s += c
|
||||||
|
if recover:
|
||||||
|
buffer.seek(old, os.SEEK_SET)
|
||||||
|
return s.decode()
|
||||||
|
|
||||||
|
def read_nodes(buffer, current):
|
||||||
|
mode = as_num(buffer.read(2))
|
||||||
|
buffer.read(2)
|
||||||
|
name_offset = as_num(buffer.read(4))
|
||||||
|
size = as_num(buffer.read(4))
|
||||||
|
peer = as_num(buffer.read(4))
|
||||||
|
extra = as_num(buffer.read(4))
|
||||||
|
name = read_str_at(buffer, at=name_offset, recover=True)
|
||||||
|
is_dir = lambda mode: mode & (4 << 12) != 0
|
||||||
|
is_reg = lambda mode: mode & (8 << 12) != 0
|
||||||
|
is_link = lambda mode: mode & (10 << 12) != 0
|
||||||
|
|
||||||
|
path = current + '/' + name
|
||||||
|
print(path)
|
||||||
|
|
||||||
|
if is_link(mode):
|
||||||
|
pass
|
||||||
|
if is_dir(mode):
|
||||||
|
# traverse the directory children
|
||||||
|
buffer.seek(extra, os.SEEK_SET)
|
||||||
|
read_nodes(buffer, current + '/' + name)
|
||||||
|
if is_reg(mode):
|
||||||
|
for block in range(extra):
|
||||||
|
# decrypt each block
|
||||||
|
pass
|
||||||
|
|
||||||
|
# traverse its peer
|
||||||
|
if peer != 0:
|
||||||
|
buffer.seek(peer, os.SEEK_SET)
|
||||||
|
read_nodes(buffer, current)
|
||||||
|
|
||||||
|
read_nodes(region, '')
|
17
python/matcher/elf.py
Normal file
17
python/matcher/elf.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
from .matcher import SignatureMatcher, Match
|
||||||
|
|
||||||
|
class ELF(SignatureMatcher):
|
||||||
|
def __init__(self, file):
|
||||||
|
self.name = "ELF"
|
||||||
|
self.signature = b'\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
super().__init__(file)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
for match in self.search():
|
||||||
|
# walk back for the firmware section header
|
||||||
|
start = match
|
||||||
|
self.matches += [Match(start, 0)]
|
||||||
|
|
||||||
|
return len(self.matches) != 0
|
@ -1,7 +1,23 @@
|
|||||||
|
import os
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
from pyfdt.pyfdt import FdtBlobParse
|
||||||
|
|
||||||
from .matcher import SignatureMatcher, Match
|
from .matcher import SignatureMatcher, Match
|
||||||
|
|
||||||
class FlattenDeviceTree(SignatureMatcher):
|
class FlattenDeviceTree(SignatureMatcher):
|
||||||
|
"""
|
||||||
|
https://devicetree-specification.readthedocs.io/en/stable/flattened-format.html
|
||||||
|
|
||||||
|
header
|
||||||
|
-> space
|
||||||
|
-> memory reservation block
|
||||||
|
-> space
|
||||||
|
-> structure block
|
||||||
|
-> space
|
||||||
|
-> strings block
|
||||||
|
-> space
|
||||||
|
"""
|
||||||
def __init__(self, file):
|
def __init__(self, file):
|
||||||
self.name = "Flatten Device Tree"
|
self.name = "Flatten Device Tree"
|
||||||
self.signature = b'\xd0\x0d\xfe\xed'
|
self.signature = b'\xd0\x0d\xfe\xed'
|
||||||
@ -9,6 +25,9 @@ class FlattenDeviceTree(SignatureMatcher):
|
|||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
for match in self.search():
|
for match in self.search():
|
||||||
|
"""
|
||||||
|
All the header fields ... stored in big-endian format
|
||||||
|
"""
|
||||||
start = match
|
start = match
|
||||||
header = io.BytesIO(self.file[start:start+4*10])
|
header = io.BytesIO(self.file[start:start+4*10])
|
||||||
magic = header.read(4)
|
magic = header.read(4)
|
||||||
@ -22,7 +41,32 @@ class FlattenDeviceTree(SignatureMatcher):
|
|||||||
size_dt_strings = header.read(4)
|
size_dt_strings = header.read(4)
|
||||||
size_dt_struct = header.read(4)
|
size_dt_struct = header.read(4)
|
||||||
|
|
||||||
totalsize = int.from_bytes(totalsize, 'little')
|
as_num = lambda f: int.from_bytes(f, 'big')
|
||||||
self.matches += [Match(start, totalsize)]
|
totalsize = as_num(totalsize)
|
||||||
|
data = {
|
||||||
|
'off_dt_struct': as_num(off_dt_struct),
|
||||||
|
'off_dt_strings': as_num(off_dt_strings),
|
||||||
|
'off_mem_rsvmap': as_num(off_mem_rsvmap),
|
||||||
|
'version': as_num(version),
|
||||||
|
'last_comp_version': as_num(last_comp_version),
|
||||||
|
'boot_cpuid_phys': as_num(boot_cpuid_phys),
|
||||||
|
'size_dt_strings': as_num(size_dt_strings),
|
||||||
|
'size_dt_struct': as_num(size_dt_struct),
|
||||||
|
}
|
||||||
|
self.matches += [Match(start, totalsize, data)]
|
||||||
|
|
||||||
return len(self.matches) != 0
|
return len(self.matches) != 0
|
||||||
|
|
||||||
|
def view(self, match):
|
||||||
|
as_num = lambda n: int.from_bytes(n, 'big')
|
||||||
|
data = match.data
|
||||||
|
region = io.BytesIO(self.file[match.offset : match.offset + match.length])
|
||||||
|
|
||||||
|
dtb = FdtBlobParse(region)
|
||||||
|
s = dtb.to_fdt()
|
||||||
|
|
||||||
|
for name, node in s.rootnode.walk():
|
||||||
|
print(name)
|
||||||
|
if "kernel" in name:
|
||||||
|
if len(list(filter(lambda f: f in name, ["arch", "os", "description"]))) != 0:
|
||||||
|
print("> ", node)
|
||||||
|
@ -26,3 +26,6 @@ class SignatureMatcher:
|
|||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def view(self, match):
|
||||||
|
pass
|
||||||
|
39
python/matcher/romfs.py
Normal file
39
python/matcher/romfs.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import io
|
||||||
|
|
||||||
|
from PyRomfsImage import *
|
||||||
|
|
||||||
|
from .matcher import SignatureMatcher, Match
|
||||||
|
|
||||||
|
class RomFS(SignatureMatcher):
|
||||||
|
"""
|
||||||
|
RomFS a readonly file system
|
||||||
|
big-endian
|
||||||
|
https://www.kernel.org/doc/Documentation/filesystems/romfs.txt
|
||||||
|
"""
|
||||||
|
def __init__(self, file):
|
||||||
|
self.name = "RomFS"
|
||||||
|
self.signature = b'-rom1fs-'
|
||||||
|
super().__init__(file)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
for match in self.search():
|
||||||
|
start = match
|
||||||
|
header = io.BytesIO(self.file[start:start+14])
|
||||||
|
magic = header.read(8)
|
||||||
|
fullsize = header.read(4)
|
||||||
|
checksum = header.read(4)
|
||||||
|
fullsize = int.from_bytes(fullsize, 'big')
|
||||||
|
self.matches += [Match(start, fullsize)]
|
||||||
|
|
||||||
|
return len(self.matches) != 0
|
||||||
|
|
||||||
|
def view(self, match):
|
||||||
|
|
||||||
|
start = match.offset
|
||||||
|
length = match.length
|
||||||
|
raw = io.BytesIO(self.file[start:start+length])
|
||||||
|
rom = Romfs(raw)
|
||||||
|
root = rom.getRoot()
|
||||||
|
print(root.name)
|
||||||
|
for ch in root.children:
|
||||||
|
print(ch.name)
|
@ -29,9 +29,11 @@ class Zip(SignatureMatcher):
|
|||||||
file_name_length = header.read(2)
|
file_name_length = header.read(2)
|
||||||
extra_field_length = header.read(2)
|
extra_field_length = header.read(2)
|
||||||
|
|
||||||
file_name_length = int.from_bytes(file_name_length, 'little')
|
as_num = lambda x: int.form_bytes(x, 'little')
|
||||||
extra_field_length = int.from_bytes(extra_field_length, 'little')
|
|
||||||
compressed_size = int.from_bytes(compressed_size, 'little')
|
file_name_length = as_num(file_name_length)
|
||||||
|
extra_field_length = as_num(extra_field_length)
|
||||||
|
compressed_size = as_num(compressed_size)
|
||||||
|
|
||||||
header_size = 4*4 + 2*7
|
header_size = 4*4 + 2*7
|
||||||
data = {
|
data = {
|
||||||
|
Loading…
Reference in New Issue
Block a user