建立 MAG160C 逆向工程交接仓库
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
from .binary import (
|
||||
int_to_bin, bin_to_int, swap_bytes, encode_bin, decode_bin)
|
||||
from .bitstream import BitStreamReader, BitStreamWriter
|
||||
from .container import (Container, FlagsContainer, ListContainer,
|
||||
LazyContainer)
|
||||
from .hex import HexString, hexdump
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,107 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def int_to_bin(number: int, width: int = 32) -> bytes:
|
||||
r"""
|
||||
Convert an integer into its binary representation in a bytes object.
|
||||
Width is the amount of bits to generate. If width is larger than the actual
|
||||
amount of bits required to represent number in binary, sign-extension is
|
||||
used. If it's smaller, the representation is trimmed to width bits.
|
||||
Each "bit" is either '\x00' or '\x01'. The MSBit is first.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> int_to_bin(19, 5)
|
||||
b'\x01\x00\x00\x01\x01'
|
||||
>>> int_to_bin(19, 8)
|
||||
b'\x00\x00\x00\x01\x00\x00\x01\x01'
|
||||
"""
|
||||
if number < 0:
|
||||
number += 1 << width
|
||||
i = width - 1
|
||||
bits = bytearray(width)
|
||||
while number and i >= 0:
|
||||
bits[i] = number & 1
|
||||
number >>= 1
|
||||
i -= 1
|
||||
return bytes(bits)
|
||||
|
||||
|
||||
_bit_values: dict[int, int] = {
|
||||
0: 0,
|
||||
1: 1,
|
||||
48: 0, # '0'
|
||||
49: 1, # '1'
|
||||
}
|
||||
|
||||
def bin_to_int(bits: bytes, signed: bool = False) -> int:
|
||||
r"""
|
||||
Logical opposite of int_to_bin. Both '0' and '\x00' are considered zero,
|
||||
and both '1' and '\x01' are considered one. Set sign to True to interpret
|
||||
the number as a 2-s complement signed integer.
|
||||
"""
|
||||
number = 0
|
||||
bias = 0
|
||||
if signed and _bit_values[bits[0]] == 1:
|
||||
bits = bits[1:]
|
||||
bias = 1 << len(bits)
|
||||
for b in bits:
|
||||
number <<= 1
|
||||
number |= _bit_values[b]
|
||||
return number - bias
|
||||
|
||||
|
||||
def swap_bytes(bits: bytes, bytesize: int = 8) -> bytes:
|
||||
r"""
|
||||
Bits is a b'' object containing a binary representation. Assuming each
|
||||
bytesize bits constitute a bytes, perform a endianness byte swap. Example:
|
||||
|
||||
>>> swap_bytes(b'00011011', 2)
|
||||
b'11100100'
|
||||
"""
|
||||
i = 0
|
||||
l = len(bits)
|
||||
output = [b""] * ((l // bytesize) + 1)
|
||||
j = len(output) - 1
|
||||
while i < l:
|
||||
output[j] = bits[i : i + bytesize]
|
||||
i += bytesize
|
||||
j -= 1
|
||||
return b"".join(output)
|
||||
|
||||
|
||||
_char_to_bin = {}
|
||||
_bin_to_char = {}
|
||||
for i in range(256):
|
||||
ch = bytes((i,))
|
||||
bin = int_to_bin(i, 8)
|
||||
_char_to_bin[i] = bin
|
||||
_bin_to_char[bin] = ch
|
||||
|
||||
|
||||
def encode_bin(data: bytes) -> bytes:
|
||||
r"""
|
||||
Create a binary representation of the given b'' object. Assume 8-bit
|
||||
ASCII. Example:
|
||||
|
||||
>>> encode_bin(b'ab')
|
||||
b'\x00\x01\x01\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x01\x00'
|
||||
"""
|
||||
return b"".join(_char_to_bin[ch] for ch in data)
|
||||
|
||||
|
||||
def decode_bin(data: bytes) -> bytes:
|
||||
"""
|
||||
Logical opposite of decode_bin.
|
||||
"""
|
||||
if len(data) & 7:
|
||||
raise ValueError("Data length must be a multiple of 8")
|
||||
i = 0
|
||||
j = 0
|
||||
l = len(data) // 8
|
||||
chars = [b""] * l
|
||||
while j < l:
|
||||
chars[j] = _bin_to_char[data[i:i+8]]
|
||||
i += 8
|
||||
j += 1
|
||||
return b"".join(chars)
|
||||
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
from typing import IO, TYPE_CHECKING
|
||||
|
||||
from .binary import encode_bin, decode_bin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Buffer # 3.12+
|
||||
from typing_extensions import Self # 3.11+
|
||||
|
||||
|
||||
class BitStream(io.RawIOBase, IO[bytes]):
|
||||
|
||||
__slots__ = ("substream",)
|
||||
|
||||
def __init__(self, substream: IO[bytes]) -> None:
|
||||
self.substream = substream
|
||||
|
||||
def __enter__(self) -> Self:
|
||||
return self
|
||||
|
||||
|
||||
class BitStreamReader(BitStream):
|
||||
|
||||
__slots__ = ("buffer", "total_size")
|
||||
|
||||
def __init__(self, substream: IO[bytes]) -> None:
|
||||
super().__init__(substream)
|
||||
self.total_size = 0
|
||||
self.buffer = b""
|
||||
|
||||
def close(self) -> None:
|
||||
if self.total_size % 8 != 0:
|
||||
raise ValueError("total size of read data must be a multiple of 8",
|
||||
self.total_size)
|
||||
|
||||
def tell(self) -> int:
|
||||
return self.substream.tell()
|
||||
|
||||
def seek(self, pos: int, whence: int = 0) -> int:
|
||||
self.buffer = b""
|
||||
self.total_size = 0
|
||||
self.substream.seek(pos, whence)
|
||||
return 0
|
||||
|
||||
def read(self, count: int = -1) -> bytes:
|
||||
if count < 0:
|
||||
raise ValueError("count cannot be negative")
|
||||
|
||||
l = len(self.buffer)
|
||||
if count == 0:
|
||||
data = b""
|
||||
elif count <= l:
|
||||
data = self.buffer[:count]
|
||||
self.buffer = self.buffer[count:]
|
||||
else:
|
||||
data = self.buffer
|
||||
count -= l
|
||||
bytes = count // 8
|
||||
if count & 7:
|
||||
bytes += 1
|
||||
buf = encode_bin(self.substream.read(bytes))
|
||||
data += buf[:count]
|
||||
self.buffer = buf[count:]
|
||||
self.total_size += len(data)
|
||||
return data
|
||||
|
||||
|
||||
class BitStreamWriter(BitStream):
|
||||
|
||||
__slots__ = ("buffer", "pos")
|
||||
|
||||
def __init__(self, substream: IO[bytes]) -> None:
|
||||
super().__init__(substream)
|
||||
self.buffer: list[bytes] = []
|
||||
self.pos = 0
|
||||
|
||||
def close(self) -> None:
|
||||
self.flush()
|
||||
|
||||
def flush(self) -> None:
|
||||
bytes = decode_bin(b"".join(self.buffer))
|
||||
self.substream.write(bytes)
|
||||
self.buffer = []
|
||||
self.pos = 0
|
||||
|
||||
def tell(self) -> int:
|
||||
return self.substream.tell() + self.pos // 8
|
||||
|
||||
def seek(self, pos: int, whence: int = 0) -> int:
|
||||
self.flush()
|
||||
return self.substream.seek(pos, whence)
|
||||
|
||||
def write(self, data: Buffer) -> int:
|
||||
if not data:
|
||||
return 0
|
||||
if type(data) is not bytes:
|
||||
raise TypeError("data must be a bytes, not %r" % (type(data),))
|
||||
self.buffer.append(data)
|
||||
return len(data)
|
||||
@@ -0,0 +1,192 @@
|
||||
"""
|
||||
Various containers.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import MutableMapping
|
||||
from functools import wraps
|
||||
from pprint import pformat
|
||||
from typing import IO, TYPE_CHECKING, Any, Literal, overload
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Iterator
|
||||
from typing import Concatenate, ParamSpec, TypeVar
|
||||
|
||||
from typing_extensions import Self # 3.11+
|
||||
|
||||
from ..core import Construct
|
||||
from .hex import HexString
|
||||
|
||||
_P = ParamSpec('_P')
|
||||
_R = TypeVar('_R')
|
||||
_T = TypeVar('_T')
|
||||
|
||||
|
||||
__all__ = [
|
||||
"recursion_lock",
|
||||
"Container", "FlagsContainer", "ListContainer", "LazyContainer",
|
||||
]
|
||||
|
||||
|
||||
def recursion_lock(
|
||||
retval: _R,
|
||||
lock_name: str = "__recursion_lock__",
|
||||
) -> Callable[[Callable[Concatenate[Any, _P], _T]], Callable[Concatenate[Any, _P], _T | _R]]:
|
||||
|
||||
def decorator(
|
||||
func: Callable[Concatenate[Any, _P], _T],
|
||||
) -> Callable[Concatenate[Any, _P], _T | _R]:
|
||||
@wraps(func)
|
||||
def wrapper(self: Any, *args: _P.args, **kw: _P.kwargs) -> _T | _R:
|
||||
if getattr(self, lock_name, False):
|
||||
return retval
|
||||
setattr(self, lock_name, True)
|
||||
try:
|
||||
return func(self, *args, **kw)
|
||||
finally:
|
||||
setattr(self, lock_name, False)
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
class Container(MutableMapping[str, Any]):
|
||||
"""
|
||||
A generic container of attributes.
|
||||
|
||||
Containers are the common way to express parsed data.
|
||||
"""
|
||||
|
||||
def __init__(self, **kw: Any) -> None:
|
||||
self.__dict__ = kw
|
||||
|
||||
# The core dictionary interface.
|
||||
|
||||
@overload
|
||||
def __getitem__(self, name: Literal[
|
||||
"ch_addralign", "ch_size",
|
||||
"length",
|
||||
"n_descsz", "n_offset", "n_namesz",
|
||||
"sh_addralign", "sh_flags", "sh_size",
|
||||
"bloom_size", "nbuckets", "nchains",
|
||||
]) -> int: ...
|
||||
@overload
|
||||
def __getitem__(self, name: Literal[
|
||||
"ch_type",
|
||||
"sh_type",
|
||||
"n_name", "n_type",
|
||||
"tag", "vendor_name",
|
||||
]) -> str: ...
|
||||
@overload
|
||||
def __getitem__(self, name: Literal[
|
||||
"buckets", "chains",
|
||||
]) -> list[int]: ...
|
||||
@overload
|
||||
def __getitem__(self, name: str) -> Any: ...
|
||||
def __getitem__(self, name: str) -> Any:
|
||||
return self.__dict__[name]
|
||||
|
||||
def __delitem__(self, name: str) -> None:
|
||||
del self.__dict__[name]
|
||||
|
||||
def __setitem__(self, name: str, value: Any) -> None:
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
return iter(self.__dict__)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.__dict__.keys())
|
||||
|
||||
# Copy interface.
|
||||
|
||||
def copy(self) -> Self:
|
||||
return self.__class__(**self.__dict__)
|
||||
|
||||
__copy__ = copy
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "%s(%s)" % (self.__class__.__name__, repr(self.__dict__))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "%s(%s)" % (self.__class__.__name__, str(self.__dict__))
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# elftools.construct.debug Probe.printout()
|
||||
stream_position: int
|
||||
following_stream_data: str | HexString
|
||||
context: Container
|
||||
stack: ListContainer
|
||||
# allow arbitray attributes
|
||||
def __setattr__(self, name: str, value: object) -> None: ...
|
||||
def __getattr__(self, name: str) -> Any: ...
|
||||
|
||||
class FlagsContainer(Container):
|
||||
"""
|
||||
A container providing pretty-printing for flags.
|
||||
|
||||
Only set flags are displayed.
|
||||
"""
|
||||
|
||||
@recursion_lock("<...>")
|
||||
def __str__(self) -> str:
|
||||
d = dict((k, self[k]) for k in self
|
||||
if self[k] and not k.startswith("_"))
|
||||
return "%s(%s)" % (self.__class__.__name__, pformat(d))
|
||||
|
||||
class ListContainer(list[Any]):
|
||||
"""
|
||||
A container for lists.
|
||||
"""
|
||||
|
||||
__slots__ = ("__recursion_lock__",)
|
||||
|
||||
@recursion_lock("[...]")
|
||||
def __str__(self) -> str:
|
||||
return pformat(self)
|
||||
|
||||
class LazyContainer:
|
||||
|
||||
__slots__ = ("subcon", "stream", "pos", "context", "_value")
|
||||
|
||||
def __init__(self, subcon: Construct, stream: IO[bytes], pos: int, context: Container) -> None:
|
||||
self.subcon = subcon
|
||||
self.stream = stream
|
||||
self.pos = pos
|
||||
self.context = context
|
||||
self._value = NotImplemented
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
return isinstance(other, LazyContainer) and self._value == other._value
|
||||
|
||||
def __ne__(self, other: object) -> bool:
|
||||
return not (self == other)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__pretty_str__()
|
||||
|
||||
def __pretty_str__(self, nesting: int = 1, indentation: str = " ") -> str:
|
||||
if self._value is NotImplemented:
|
||||
text = "<unread>"
|
||||
elif hasattr(self._value, "__pretty_str__"):
|
||||
text = self._value.__pretty_str__(nesting, indentation)
|
||||
else:
|
||||
text = str(self._value)
|
||||
return "%s: %s" % (self.__class__.__name__, text)
|
||||
|
||||
def read(self) -> Any:
|
||||
self.stream.seek(self.pos)
|
||||
return self.subcon._parse(self.stream, self.context)
|
||||
|
||||
def dispose(self) -> None:
|
||||
del self.subcon
|
||||
del self.stream
|
||||
del self.context
|
||||
del self.pos
|
||||
|
||||
def _get_value(self) -> Any:
|
||||
if self._value is NotImplemented:
|
||||
self._value = self.read()
|
||||
return self._value
|
||||
|
||||
value = property(_get_value)
|
||||
|
||||
has_value = property(lambda self: self._value is not NotImplemented)
|
||||
@@ -0,0 +1,47 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import Self # 3.11+
|
||||
|
||||
|
||||
# Map an integer in the inclusive range 0-255 to its string byte representation
|
||||
_printable = {i: chr(i) if 32 <= i < 128 else "." for i in range(256)}
|
||||
|
||||
|
||||
def hexdump(data: bytes, linesize: int) -> list[str]:
|
||||
"""
|
||||
data is a bytes object. The returned result is a string.
|
||||
"""
|
||||
prettylines = []
|
||||
if len(data) < 65536:
|
||||
fmt = "%%04X %%-%ds %%s"
|
||||
else:
|
||||
fmt = "%%08X %%-%ds %%s"
|
||||
fmt = fmt % (3 * linesize - 1,)
|
||||
for i in range(0, len(data), linesize):
|
||||
line = data[i : i + linesize]
|
||||
hextext = line.hex(" ")
|
||||
rawtext = "".join(_printable[b] for b in line)
|
||||
prettylines.append(fmt % (i, hextext, rawtext))
|
||||
return prettylines
|
||||
|
||||
|
||||
class HexString(bytes):
|
||||
"""
|
||||
Represents bytes that will be hex-dumped to a string when its string
|
||||
representation is requested.
|
||||
"""
|
||||
def __init__(self, data: bytes, linesize: int = 16) -> None:
|
||||
self.linesize = linesize
|
||||
|
||||
def __new__(cls, data: bytes, *args: object, **kwargs: object) -> Self:
|
||||
return bytes.__new__(cls, data)
|
||||
|
||||
def __str__(self) -> str:
|
||||
if not self:
|
||||
return "''"
|
||||
sep = "\n"
|
||||
return sep + sep.join(
|
||||
hexdump(self, self.linesize))
|
||||
Reference in New Issue
Block a user