最近把很早以前购买的三星平板拿出来继续使用,其SoC是三星半导体自制的Exynos 9611,由于我买的是WIFI+LTE版本,其存在基带。很久以前用搭载Exynos4210的三星S2的时候,就对三星的奇怪的香农基带感到好奇。
历经快十年的对于逆向的爱好,最近再阅读了一些大牛写的代码和他们提供的Loader,成功的对目前最新的Exynos Shannon基带固件做到了解包,解压后可直接使用ShannonRE项目之中的Loader和静态分析工具进行分析。
当然如果你只是对三星的调试模式比较好奇的话,在我的解压工具中,其将自动从main区块中搜索AT指令,可通过终端模拟器连接到Samsung DM端口发送AT指令进行调试。

# siu.py v2
import struct
import re
import argparse
from contextlib import closing

class TOC:
    def __init__(self, fstream, fpos=None):
        try:
            if fpos is not None:
                fstream.seek(fpos)
            self.buf = fstream.read(32)
        except Exception as e:
            print(f"[!]ERROR Reading TOC Header: {e}")
            self.buf = bytearray(32)

    def unpack(self):
        try:
            self.name = self.buf[:12].rstrip(b"\x00").decode("utf-8")
            self.start = struct.unpack("i", self.buf[12:16])[0]
            self.size = struct.unpack("i", self.buf[20:24])[0]
            self.secdata = struct.unpack("i", self.buf[24:28])[0]
            self.queue = struct.unpack("i", self.buf[28:32])[0]
        except struct.error as e:
            print(f"[!]ERROR Unpacking TOC: {e}")
            raise

    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 IMG:
    def __init__(self, fstream, header):
        fstream.seek(header.start)
        self.buf = fstream.read(header.size)

    def unpack(self):
        pass

    def write(self, dst):
        with open(f"{dst}.bin", "wb") as f:
            f.write(self.buf)


class BOOT(IMG):
    def unpack(self):
        print('Done')


def unpack_toc(file_stream, name):
    header = TOC(file_stream)
    header.unpack()
    header.print_info()
    if header.name != name:
        raise ValueError(f"Unexpected TOC name. Expected {name}, got {header.name}")
    return header


def unpack_img(file_stream, header, img_type):
    img = img_type(file_stream, header)
    img.unpack()
    return img


def find_in_img(img, pattern, filename):
    strings = re.findall(rb'%s[^\x00]*' % pattern.encode(), img.buf)
    with open(f'{filename}.TXT', 'wb') as f:
        for s in strings:
            f.write(s + b'\n')
    print(f"Found {len(strings)} strings")
    return strings


def main():
    parser = argparse.ArgumentParser(description="SHANNON Image Unpacker v1.2 by Yuu/Idea from ShannonRE by grant-h")
    parser.add_argument('file', help='Path to the binary file')
    parser.add_argument('--at', action='store_true', help='Find AT strings')
    parser.add_argument('--ver', help='Find version strings for a given model')

    args = parser.parse_args()

    with closing(open(args.file, "rb")) as file_stream:
        toc_header = unpack_toc(file_stream, "TOC")
        boot_header = unpack_toc(file_stream, "BOOT")
        main_header = unpack_toc(file_stream, "MAIN")
        vss_header = unpack_toc(file_stream, "VSS")
        nv_header = unpack_toc(file_stream, "NV")
        offset_header = unpack_toc(file_stream, "OFFSET")

        boot_img = unpack_img(file_stream, boot_header, BOOT)
        boot_img.write("boot")

        main_img = unpack_img(file_stream, main_header, IMG)
        main_img.write("main")

        vss_img = unpack_img(file_stream, vss_header, IMG)
        vss_img.write("vss")

        nv_img = unpack_img(file_stream, nv_header, IMG)
        nv_img.write("nv")

        if args.at:
            find_in_img(main_img, 'AT+', 'AT')
        if args.ver:
            ver_strings = find_in_img(main_img, args.ver, 'VER')
            if ver_strings:
                print(f"Possible Version: {ver_strings[0].decode()}")


if __name__ == "__main__":
    main()
评论