Added initial code for fwutils.
This commit is contained in:
parent
d6876b0b95
commit
d97c763e93
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Module",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "fwutils",
|
||||
"justMyCode": true,
|
||||
"args": ["x", "../spa50x-30x-7-6-2f.bin"]
|
||||
}
|
||||
]
|
||||
}
|
36
README.md
36
README.md
|
@ -1,3 +1,35 @@
|
|||
# fwutils-spa504g
|
||||
# SPA504G firmware utilities
|
||||
|
||||
Firmware utilities for the Cisco SPA504G SIP phone.
|
||||
Firmware utilities for the Cisco SPA504G SIP phone.
|
||||
|
||||
## Installation
|
||||
|
||||
This utility requires Python 3.
|
||||
|
||||
```bash
|
||||
$ python3 -m venv venv
|
||||
$ . ./venv/bin/activate
|
||||
$ pip install -e .
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The utility will extract files to a new `extracted` directory, in the same location as the specified file.
|
||||
|
||||
```
|
||||
usage: fwutils [-h] {x} ...
|
||||
|
||||
Firmware utility for the SPA504G SIP phone.
|
||||
|
||||
positional arguments:
|
||||
{x} Extract a file.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```
|
||||
$ fwutils x ./spa50x-30x-7-6-2f.bin
|
||||
```
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
kaitaistruct>=0.9
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
with open("requirements.txt") as f:
|
||||
requirements = f.readlines()
|
||||
|
||||
setup(
|
||||
name="fwutils",
|
||||
version="0.1",
|
||||
description="SPA504G firmware utilities.",
|
||||
author="Butlersaurus",
|
||||
packages=find_packages("src"),
|
||||
package_dir={"": "src"},
|
||||
include_package_data=True,
|
||||
entry_points={"console_scripts": ["fwutils = fwutils.__main__:main"]},
|
||||
install_requires=requirements
|
||||
)
|
|
@ -0,0 +1,54 @@
|
|||
import argparse
|
||||
from io import BytesIO
|
||||
import mmap
|
||||
import pathlib
|
||||
import zlib
|
||||
from fwutils.definitions.spa504g import Spa504g
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Firmware utility for the SPA504G SIP phone.")
|
||||
subparsers = parser.add_subparsers(help="Extract a file.")
|
||||
|
||||
extract_parser = subparsers.add_parser("x")
|
||||
extract_parser.add_argument("file", type=pathlib.Path, nargs="?", help="The file to extract.")
|
||||
|
||||
args = parser.parse_args()
|
||||
if not args.file:
|
||||
parser.error("You must specify a file.")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = parse_args()
|
||||
file: pathlib.Path = args.file
|
||||
|
||||
file_handle = open(file, "rb")
|
||||
with mmap.mmap(file_handle.fileno(), 0, access=mmap.ACCESS_READ) as buf:
|
||||
firmware = Spa504g.from_io(BytesIO(buf))
|
||||
|
||||
output_dir = file.parent.absolute() / "extracted"
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
for module in firmware.modules:
|
||||
output_file = file.stem
|
||||
output_file += "_"
|
||||
output_file += hex(module.offset)
|
||||
output_file += "_"
|
||||
output_file += hex(module.offset + module.length)
|
||||
output_file += file.suffix
|
||||
output_file = pathlib.Path(output_file)
|
||||
|
||||
with open(output_dir / output_file, "wb") as f:
|
||||
f.write(module.body)
|
||||
|
||||
if module.compressed == 0: # 0 == True
|
||||
decompressed_data = zlib.decompress(module.body, 15)
|
||||
output_file = output_file.stem + "_decompressed" + output_file.suffix
|
||||
with open(output_dir / output_file, "wb") as f:
|
||||
f.write(decompressed_data)
|
||||
|
||||
f.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,71 @@
|
|||
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
|
||||
|
||||
from pkg_resources import parse_version
|
||||
import kaitaistruct
|
||||
from kaitaistruct import KaitaiStruct, KaitaiStream, BytesIO
|
||||
|
||||
|
||||
if parse_version(kaitaistruct.__version__) < parse_version('0.9'):
|
||||
raise Exception("Incompatible Kaitai Struct Python API: 0.9 or later is required, but you have %s" % (kaitaistruct.__version__))
|
||||
|
||||
class Spa504g(KaitaiStruct):
|
||||
def __init__(self, _io, _parent=None, _root=None):
|
||||
self._io = _io
|
||||
self._parent = _parent
|
||||
self._root = _root if _root else self
|
||||
self._read()
|
||||
|
||||
def _read(self):
|
||||
self.header = Spa504g.Header(self._io, self, self._root)
|
||||
self.modules = [None] * (self.header.module_count)
|
||||
for i in range(self.header.module_count):
|
||||
self.modules[i] = Spa504g.Module(self._io, self, self._root)
|
||||
|
||||
|
||||
class Header(KaitaiStruct):
|
||||
def __init__(self, _io, _parent=None, _root=None):
|
||||
self._io = _io
|
||||
self._parent = _parent
|
||||
self._root = _root if _root else self
|
||||
self._read()
|
||||
|
||||
def _read(self):
|
||||
self.magic = self._io.read_bytes(16)
|
||||
if not self.magic == b"\x53\x6B\x4F\x73\x4D\x6F\x35\x20\x66\x49\x72\x4D\x77\x41\x72\x45":
|
||||
raise kaitaistruct.ValidationNotEqualError(b"\x53\x6B\x4F\x73\x4D\x6F\x35\x20\x66\x49\x72\x4D\x77\x41\x72\x45", self.magic, self._io, u"/types/header/seq/0")
|
||||
self.signature = self._io.read_bytes(32)
|
||||
self.digest = self._io.read_bytes(16)
|
||||
self.random_sequence = self._io.read_bytes(16)
|
||||
self.header_length = self._io.read_u4be()
|
||||
self.module_header_length = self._io.read_u4be()
|
||||
self.file_length = self._io.read_u4be()
|
||||
self.version = (self._io.read_bytes(32)).decode(u"utf8")
|
||||
self.module_count = self._io.read_u4be()
|
||||
self.padding = self._io.read_bytes((self.header_length - 128))
|
||||
|
||||
|
||||
class Module(KaitaiStruct):
|
||||
def __init__(self, _io, _parent=None, _root=None):
|
||||
self._io = _io
|
||||
self._parent = _parent
|
||||
self._root = _root if _root else self
|
||||
self._read()
|
||||
|
||||
def _read(self):
|
||||
self.sector_id = self._io.read_u2be()
|
||||
self.compressed = self._io.read_u2be()
|
||||
self.length = self._io.read_u4be()
|
||||
self.offset = self._io.read_u4be()
|
||||
self.digest = self._io.read_bytes(16)
|
||||
self.padding = self._io.read_bytes((self._parent.header.module_header_length - 28))
|
||||
|
||||
@property
|
||||
def body(self):
|
||||
if hasattr(self, '_m_body'):
|
||||
return self._m_body if hasattr(self, '_m_body') else None
|
||||
|
||||
_pos = self._io.pos()
|
||||
self._io.seek(self.offset)
|
||||
self._m_body = self._io.read_bytes(self.length)
|
||||
self._io.seek(_pos)
|
||||
return self._m_body if hasattr(self, '_m_body') else None
|
Loading…
Reference in New Issue