init publish
							parent
							
								
									2faca77f11
								
							
						
					
					
						commit
						c4bde9632a
					
				| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					FROM ubuntu:22.04
 | 
				
			||||||
 | 
					ENV DEBIAN_FRONTEND noninteractive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RUN apt-get update && apt-get install --yes \
 | 
				
			||||||
 | 
					    python3-capstone \
 | 
				
			||||||
 | 
					    python3-setuptools \
 | 
				
			||||||
 | 
					    python3-sqlalchemy \
 | 
				
			||||||
 | 
					    && apt-get clean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COPY setup.py /app/
 | 
				
			||||||
 | 
					COPY subdisassem /app/subdisassem/
 | 
				
			||||||
 | 
					WORKDIR /app/
 | 
				
			||||||
 | 
					RUN python3 setup.py install
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					# subdisassem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- [capstone python bindings](https://github.com/capstone-engine/capstone/tree/master/bindings/python)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					version: "3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  subdisassem:
 | 
				
			||||||
 | 
					    image: subdisassem
 | 
				
			||||||
 | 
					    build:
 | 
				
			||||||
 | 
					      context: .
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - ./firmware:/firmware
 | 
				
			||||||
 | 
					    working_dir: /firmware
 | 
				
			||||||
 | 
					    command: subdisassem -b firmware.bin
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					from setuptools import setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setup(
 | 
				
			||||||
 | 
					    name="subdisassem",
 | 
				
			||||||
 | 
					    version="0.0.1",
 | 
				
			||||||
 | 
					    packages=["subdisassem"],
 | 
				
			||||||
 | 
					    entry_points={
 | 
				
			||||||
 | 
					        "console_scripts": [
 | 
				
			||||||
 | 
					            "subdisassem = subdisassem:subdisassem_script",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    python_requires=">3",
 | 
				
			||||||
 | 
					    install_requires=[
 | 
				
			||||||
 | 
					        "capstone",
 | 
				
			||||||
 | 
					        "SQLAlchemy",
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					from .scripts import subdisassem_script
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,133 @@
 | 
				
			||||||
 | 
					from capstone import Cs
 | 
				
			||||||
 | 
					from capstone import (
 | 
				
			||||||
 | 
					    CS_ARCH_ARM,
 | 
				
			||||||
 | 
					    CS_ARCH_ARM64,
 | 
				
			||||||
 | 
					    CS_ARCH_MIPS,
 | 
				
			||||||
 | 
					    CS_ARCH_PPC,
 | 
				
			||||||
 | 
					    CS_ARCH_SPARC,
 | 
				
			||||||
 | 
					    CS_ARCH_SYSZ,
 | 
				
			||||||
 | 
					    CS_ARCH_X86,
 | 
				
			||||||
 | 
					    CS_ARCH_XCORE,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					from capstone import (
 | 
				
			||||||
 | 
					    CS_MODE_16,
 | 
				
			||||||
 | 
					    CS_MODE_32,
 | 
				
			||||||
 | 
					    CS_MODE_64,
 | 
				
			||||||
 | 
					    CS_MODE_ARM,
 | 
				
			||||||
 | 
					    CS_MODE_BIG_ENDIAN,
 | 
				
			||||||
 | 
					    CS_MODE_LITTLE_ENDIAN,
 | 
				
			||||||
 | 
					    CS_MODE_MCLASS,
 | 
				
			||||||
 | 
					    CS_MODE_MICRO,
 | 
				
			||||||
 | 
					    CS_MODE_MIPS3,
 | 
				
			||||||
 | 
					    CS_MODE_MIPS32,
 | 
				
			||||||
 | 
					    CS_MODE_MIPS32R6,
 | 
				
			||||||
 | 
					    CS_MODE_MIPS64,
 | 
				
			||||||
 | 
					    CS_MODE_THUMB,
 | 
				
			||||||
 | 
					    CS_MODE_V8,
 | 
				
			||||||
 | 
					    CS_MODE_V9,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _CapstoneBase:
 | 
				
			||||||
 | 
					    def __init__(self, payload: bytes, offset: int = 0):
 | 
				
			||||||
 | 
					        self.disassembly = list()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for opcode in self.capstone.disasm(payload, offset):
 | 
				
			||||||
 | 
					            self.disassembly.append(opcode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self) -> str:
 | 
				
			||||||
 | 
					        return self.objdump
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __len__(self) -> int:
 | 
				
			||||||
 | 
					        return len(self.disassembly)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def objdump(self) -> str:
 | 
				
			||||||
 | 
					        opcodes = str()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for opcode in self.disassembly:
 | 
				
			||||||
 | 
					            opcodes += f"{opcode.address:#02x}:\t{opcode.mnemonic}\t{opcode.op_str}\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return opcodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def disasm(self) -> list:
 | 
				
			||||||
 | 
					        opcodes = list()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for opcode in self.disassembly:
 | 
				
			||||||
 | 
					            opcodes.append(
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    opcode.address,
 | 
				
			||||||
 | 
					                    opcode.mnemonic,
 | 
				
			||||||
 | 
					                    opcode.op_str,
 | 
				
			||||||
 | 
					                    opcode.size,
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return opcodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class X86_intel(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_X86, CS_MODE_16)
 | 
				
			||||||
 | 
					    arch = "x86-16"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class X86(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_X86, CS_MODE_32)
 | 
				
			||||||
 | 
					    arch = "x86-32"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class X86_64(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_X86, CS_MODE_64)
 | 
				
			||||||
 | 
					    arch = "x86-64"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ARM(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_ARM, CS_MODE_ARM)
 | 
				
			||||||
 | 
					    arch = "ARM"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Thumb(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
 | 
				
			||||||
 | 
					    arch = "Thumb"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ARM_64(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
 | 
				
			||||||
 | 
					    arch = "ARM 64"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MIPS_32_eb(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_MIPS, CS_MODE_MIPS32 + CS_MODE_BIG_ENDIAN)
 | 
				
			||||||
 | 
					    arch = "MIPS-32 (Big-endian)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MIPS_64_el(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_MIPS, CS_MODE_MIPS64 + CS_MODE_LITTLE_ENDIAN)
 | 
				
			||||||
 | 
					    arch = "MIPS-64-EL (Little-endian)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PPC_64(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_PPC, CS_MODE_BIG_ENDIAN)
 | 
				
			||||||
 | 
					    arch = "PPC-64"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Sparc(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_SPARC, CS_MODE_BIG_ENDIAN)
 | 
				
			||||||
 | 
					    arch = "Sparc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SparcV9(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_SPARC, CS_MODE_BIG_ENDIAN + CS_MODE_V9)
 | 
				
			||||||
 | 
					    arch = "SparcV9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SystemZ(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_SYSZ, 0)
 | 
				
			||||||
 | 
					    arch = "SystemZ"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class XCore(_CapstoneBase):
 | 
				
			||||||
 | 
					    capstone = Cs(CS_ARCH_XCORE, 0)
 | 
				
			||||||
 | 
					    arch = "XCore"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					from sqlalchemy import create_engine, Column, Integer, String, LargeBinary
 | 
				
			||||||
 | 
					from sqlalchemy.ext.declarative import declarative_base
 | 
				
			||||||
 | 
					from sqlalchemy.orm import Session
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .disassemble import _CapstoneBase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Base = declarative_base()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def db_config(path: Path) -> Session:
 | 
				
			||||||
 | 
					    engine = create_engine(f"sqlite:///{path.resolve()}", native_datetime=True)
 | 
				
			||||||
 | 
					    Base.metadata.create_all(engine)
 | 
				
			||||||
 | 
					    session = Session(engine)
 | 
				
			||||||
 | 
					    return session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Disassembly(Base):
 | 
				
			||||||
 | 
					    __tablename__ = "Disassembly"
 | 
				
			||||||
 | 
					    id = Column(Integer, primary_key=True)
 | 
				
			||||||
 | 
					    arch = Column(String, nullable=False)
 | 
				
			||||||
 | 
					    checksum = Column(String, nullable=False)
 | 
				
			||||||
 | 
					    count = Column(Integer, nullable=False)
 | 
				
			||||||
 | 
					    size = Column(Integer, nullable=False)
 | 
				
			||||||
 | 
					    offset = Column(Integer, nullable=False)
 | 
				
			||||||
 | 
					    opcodes = Column(String, nullable=False)
 | 
				
			||||||
 | 
					    path = Column(String, nullable=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __repr__(self):
 | 
				
			||||||
 | 
					        return f"<Disassembly {json.dumps(self.values, indent=1)}>"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def values(self) -> dict:
 | 
				
			||||||
 | 
					        values_dict = {
 | 
				
			||||||
 | 
					            "arch": self.arch,
 | 
				
			||||||
 | 
					            "checksum": self.checksum,
 | 
				
			||||||
 | 
					            "count": self.count,
 | 
				
			||||||
 | 
					            "size": self.size,
 | 
				
			||||||
 | 
					            "offset": self.offset,
 | 
				
			||||||
 | 
					            "path": self.path,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return values_dict
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,124 @@
 | 
				
			||||||
 | 
					from argparse import ArgumentParser
 | 
				
			||||||
 | 
					from hashlib import sha1
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					from sqlalchemy import desc
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .disassemble import (
 | 
				
			||||||
 | 
					    X86_intel,
 | 
				
			||||||
 | 
					    X86,
 | 
				
			||||||
 | 
					    X86_64,
 | 
				
			||||||
 | 
					    ARM,
 | 
				
			||||||
 | 
					    Thumb,
 | 
				
			||||||
 | 
					    ARM_64,
 | 
				
			||||||
 | 
					    MIPS_32_eb,
 | 
				
			||||||
 | 
					    MIPS_64_el,
 | 
				
			||||||
 | 
					    PPC_64,
 | 
				
			||||||
 | 
					    Sparc,
 | 
				
			||||||
 | 
					    SparcV9,
 | 
				
			||||||
 | 
					    SystemZ,
 | 
				
			||||||
 | 
					    XCore,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .schema import db_config, Disassembly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def subdisassem_script():
 | 
				
			||||||
 | 
					    parser = ArgumentParser(description="")
 | 
				
			||||||
 | 
					    parser.add_argument("-v", "--verbose", action="count", help="verbose logging")
 | 
				
			||||||
 | 
					    parser.add_argument("-b", "--bin-path", required=True)
 | 
				
			||||||
 | 
					    parser.add_argument("-l", "--log", action="store_true", help="log to file")
 | 
				
			||||||
 | 
					    parser.add_argument("-f", "--fuzz", default=64, help="offset bruteforce max")
 | 
				
			||||||
 | 
					    args = parser.parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    args.bin_path = Path(args.bin_path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if args.verbose:
 | 
				
			||||||
 | 
					        level = logging.DEBUG
 | 
				
			||||||
 | 
					        format = "%(asctime)s %(filename)s:%(lineno)d %(message)s"
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        level = logging.INFO
 | 
				
			||||||
 | 
					        format = "%(asctime)s %(message)s"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if args.log:
 | 
				
			||||||
 | 
					        filename = args.bin_path.parent.joinpath(f"{args.bin_path.name}.log")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        logging.basicConfig(
 | 
				
			||||||
 | 
					            level=level,
 | 
				
			||||||
 | 
					            format=format,
 | 
				
			||||||
 | 
					            filename=filename,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        logging.basicConfig(
 | 
				
			||||||
 | 
					            level=level,
 | 
				
			||||||
 | 
					            format=format,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logging.info(args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    db_path = args.bin_path.parent.joinpath(f"{args.bin_path.name}.sqlite").absolute()
 | 
				
			||||||
 | 
					    session = db_config(db_path)
 | 
				
			||||||
 | 
					    logging.info(f"results sqlite database created at {db_path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # reading the whole file into memory until I get an idea for pagnating
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with args.bin_path.open("rb") as file_open:
 | 
				
			||||||
 | 
					        raw_bytes = file_open.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sha1sum = sha1()
 | 
				
			||||||
 | 
					    sha1sum.update(raw_bytes)
 | 
				
			||||||
 | 
					    checksum = sha1sum.hexdigest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logging.info(f"sha1sum: {checksum}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    archs = [
 | 
				
			||||||
 | 
					        X86_intel,
 | 
				
			||||||
 | 
					        X86,
 | 
				
			||||||
 | 
					        X86_64,
 | 
				
			||||||
 | 
					        ARM,
 | 
				
			||||||
 | 
					        Thumb,
 | 
				
			||||||
 | 
					        ARM_64,
 | 
				
			||||||
 | 
					        MIPS_32_eb,
 | 
				
			||||||
 | 
					        MIPS_64_el,
 | 
				
			||||||
 | 
					        PPC_64,
 | 
				
			||||||
 | 
					        Sparc,
 | 
				
			||||||
 | 
					        SparcV9,
 | 
				
			||||||
 | 
					        SystemZ,
 | 
				
			||||||
 | 
					        XCore,
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for arch in archs:
 | 
				
			||||||
 | 
					        for offset in range(args.fuzz):
 | 
				
			||||||
 | 
					            disasembler = arch(payload=raw_bytes, offset=offset)
 | 
				
			||||||
 | 
					            row = Disassembly()
 | 
				
			||||||
 | 
					            row.arch = disasembler.arch
 | 
				
			||||||
 | 
					            row.checksum = checksum
 | 
				
			||||||
 | 
					            row.count = len(disasembler)
 | 
				
			||||||
 | 
					            row.size = len(raw_bytes) - offset
 | 
				
			||||||
 | 
					            row.offset = offset
 | 
				
			||||||
 | 
					            row.opcodes = disasembler.objdump
 | 
				
			||||||
 | 
					            row.path = str(args.bin_path.absolute())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            exists = (
 | 
				
			||||||
 | 
					                session.query(Disassembly)
 | 
				
			||||||
 | 
					                .filter(Disassembly.checksum == row.checksum)
 | 
				
			||||||
 | 
					                .filter(Disassembly.offset == row.offset)
 | 
				
			||||||
 | 
					                .filter(Disassembly.arch == row.arch)
 | 
				
			||||||
 | 
					                .first()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if not exists:
 | 
				
			||||||
 | 
					                session.add(row)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    count = session.query(Disassembly).order_by(desc("count")).first()
 | 
				
			||||||
 | 
					    tops = (
 | 
				
			||||||
 | 
					        session.query(Disassembly)
 | 
				
			||||||
 | 
					        .filter(Disassembly.count == count.count)
 | 
				
			||||||
 | 
					        .order_by(desc("size"))
 | 
				
			||||||
 | 
					        .all()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for top in tops[:3]:
 | 
				
			||||||
 | 
					        logging.info(top)
 | 
				
			||||||
		Loading…
	
		Reference in New Issue