三星Shannon基带固件解压方法
最近把很早以前购买的三星平板拿出来继续使用,其SoC是三星半导体自制的Exynos 9611,由于我买的是WIFI+LTE版本,其存在基带。很久以前用搭载Exynos4210的三星S2的时候,就对三星的奇怪的香农基带感到好奇。
历经快十年的对于逆向的爱好,最近再阅读了一些大牛写的代码和他们提供的Loader,成功的对目前最新的Exynos Shannon基带固件做到了解包,解压后可直接使用ShannonRE项目之中的Loader和静态分析工具进行分析。
当然如果你只是对三星的调试模式比较好奇的话,在我的解压工具中,其将自动从main区块中搜索AT指令,可通过终端模拟器连接到Samsung DM端口发送AT指令进行调试。
Pixel上的Tensor SoC中的基带,需要从运行时的设备直接Dump才可取得本工具可解析的镜像!
# siu.py v2
import struct
import re
import argparse
from contextlib import contextmanager
from dataclasses import dataclass
from typing import BinaryIO, Type, Pattern, Optional
@dataclass
class TOCHeader:
name: str
start: int
size: int
secdata: int
queue: int
def print_info(self):
print(f"Block Name: {self.name}")
print(f"Start Offset: 0x{self.start:08x}")
print(f"Size: 0x{self.size:08x}")
print(f"Sec Data: 0x{self.secdata:08x}")
print(f"Queue: {self.queue}")
class ImageHandler:
HEADER_SIZE = 32
TOC_STRUCT_FORMAT = "<12s4xix4xiiii"
def __init__(self, file_stream: BinaryIO):
self.file = file_stream
@contextmanager
def _seek_context(self, position: Optional[int] = None):
original = self.file.tell()
try:
if position is not None:
self.file.seek(position)
yield
finally:
self.file.seek(original)
def read_toc_header(self, expected_name: str, position: Optional[int] = None) -> TOCHeader:
with self._seek_context(position):
try:
buffer = self.file.read(self.HEADER_SIZE)
if len(buffer) < self.HEADER_SIZE:
raise ValueError("Incomplete TOC header data")
fields = struct.unpack(self.TOC_STRUCT_FORMAT, buffer)
name = fields[0].rstrip(b"\x00").decode("utf-8")
if name != expected_name:
raise ValueError(f"Invalid TOC header. Expected {expected_name}, got {name}")
return TOCHeader(
name=name,
start=fields[1],
size=fields[2],
secdata=fields[3],
queue=fields[4]
)
except (struct.error, UnicodeDecodeError) as e:
raise RuntimeError(f"Failed to parse TOC header: {e}") from e
def extract_image(self, header: TOCHeader, image_type: str) -> bytes:
with self._seek_context(header.start):
return self.file.read(header.size)
@staticmethod
def find_strings(data: bytes, pattern: Pattern[bytes], filename: str) -> int:
matches = re.findall(pattern, data)
with open(filename, "wb") as f:
for match in matches:
f.write(match + b"\n")
return len(matches)
class ShannonUnpacker:
IMAGE_TYPES = ["BOOT", "MAIN", "VSS", "NV"]
def __init__(self, filename: str):
self.filename = filename
self.handler: Optional[ImageHandler] = None
self.headers = {}
def process_file(self):
with open(self.filename, "rb") as f:
self.handler = ImageHandler(f)
self._process_toc_headers()
self._extract_images()
def _process_toc_headers(self):
try:
# Initial TOC header validation
toc_header = self.handler.read_toc_header("TOC")
toc_header.print_info()
# Process subsequent headers
for img_type in self.IMAGE_TYPES:
self.headers[img_type] = self.handler.read_toc_header(img_type)
self.headers[img_type].print_info()
# Process OFFSET header
self.headers["OFFSET"] = self.handler.read_toc_header("OFFSET")
self.headers["OFFSET"].print_info()
except RuntimeError as e:
print(f"[!] Processing error: {e}")
raise
def _extract_images(self):
for img_type in self.IMAGE_TYPES:
data = self.handler.extract_image(self.headers[img_type], img_type)
with open(f"{img_type.lower()}.bin", "wb") as f:
f.write(data)
print(f"Extracted {img_type} image")
def analyze_images(self, at_search: bool, version_pattern: Optional[str]):
with open("main.bin", "rb") as f:
main_data = f.read()
if at_search:
count = ImageHandler.find_strings(main_data, rb"AT\+[^\x00]*", "AT.TXT")
print(f"Found {count} AT command strings")
if version_pattern:
pattern = re.escape(version_pattern).encode() + rb"[^\x00]*"
count = ImageHandler.find_strings(main_data, pattern, "VER.TXT")
if count > 0:
with open("VER.TXT", "rb") as f:
print(f"Possible version: {f.readline().decode().strip()}")
def main():
parser = argparse.ArgumentParser(
description="SHANNON Image Unpacker v2",
epilog="Xiang Xiang Cui Cui"
)
parser.add_argument("file", help="Input firmware file")
parser.add_argument("--at", action="store_true", help="Extract AT commands")
parser.add_argument("--ver", help="Search for version strings (provide model prefix)")
args = parser.parse_args()
try:
unpacker = ShannonUnpacker(args.file)
unpacker.process_file()
unpacker.analyze_images(args.at, args.ver)
except Exception as e:
print(f"Critical error occurred: {e}")
raise SystemExit(1) from e
if __name__ == "__main__":
main()