Skip to content

Instantly share code, notes, and snippets.

@antonylu
Created February 9, 2021 07:16
Show Gist options
  • Select an option

  • Save antonylu/2e451826e1ed0f34512c30ae96cf37b9 to your computer and use it in GitHub Desktop.

Select an option

Save antonylu/2e451826e1ed0f34512c30ae96cf37b9 to your computer and use it in GitHub Desktop.
Android signed images extractor script
#!/usr/bin/env python3
# Android signed images extractor. To use the script:
#
# python3 signimg2img -u vendor-sign.img # to unpack any other image.
#
# display("USAGE: signimg2img.py -option:\n")
# print(" -u image-sign.img: Unsigns the given image. (If is system will unpack it)")
# print(" -o: Get image info (-o image_name).")
# print(" -c: Full cleanup (removes all!)")
# print("")
from subprocess import *
import struct
import sys
import glob
import time
import shutil
import os
__version__ = '1.5'
SRC_HEADERS = [
1178748482,\
1397969747
]
BFBF_SIZE = 16448
if sys.platform.startswith("darwin") or sys.platform.startswith("win") or sys.platform.startswith("linux"):
pass
else:
raise Exception("Unsupported platform")
if sys.version_info[0] != 3:
raise Exception("Python 3.x is required")
def display(s):
text = "[signimg2img-log] {}".format(s)
print(text)
def shCommand(sh_command, output):
if output is 0:
call(sh_command, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
else:
call(sh_command, shell=True)
def get_offset(image):
image = open(image, 'rb')
image.read(60)
offset = struct.unpack('<I', image.read(4))[0]
return offset
def delete_header(image, outimage, hdr_type, offset):
display("Deleting the header...")
if hdr_type == "BFBF":
with open(image, 'rb') as in_file:
with open(outimage, 'wb') as out_file:
out_file.write(in_file.read()[BFBF_SIZE:])
elif hdr_type == "SSSS":
if sys.platform.startswith("win"):
raise RuntimeError("Windows cannot unpack images with SSSS header!")
offset = get_offset(image)
display("Got {} as offset!".format(offset))
print("dd if={} of={} iflag=count_bytes,skip_bytes bs=8192 skip=64 count={}".format(image,outimage,offset), 0)
shCommand("dd if={} of={} iflag=count_bytes,skip_bytes bs=8192 skip=64 count={}".format(image,outimage,offset), 0)
#shCommand("dd if=system-sign.img of=system.img iflag=count_bytes,skip_bytes bs=8192 skip=64 count={}".format(offset), 0)
display("Header remove complete!")
else:
raise Exception("Must be SSSS or BFBF not {}".format(hdr_type))
def regen_folder(folder):
try:
os.rmdir(folder)
except OSError:
display("Warning: {} isn't empty...".format(folder))
shutil.rmtree(folder)
os.mkdir(folder)
def check_header(image):
try:
with open(image, "rb") as image:
image_header = struct.unpack('<I', image.read(4))[0]
image.seek(0x000010)
try:
img_string = (image.read(8)).decode("utf-8")
except UnicodeDecodeError:
display("Warning: Cannot parse the string inside the image..")
global header
if image_header == SRC_HEADERS[0]:
display("Header is BFBF: {}".format(image_header))
display("Found {} at 0x000010".format(img_string))
header = "BFBF"
elif image_header == SRC_HEADERS[1]:
display("Header is SSSS: {}".format(image_header))
header = "SSSS"
else:
display("This is not a signed image!!\n")
exit()
except FileNotFoundError:
raise RuntimeError("Cannot find {}. Is it in the correct path?".format(image))
exit()
def unpack_system(header):
if header == "BFBF":
delete_header("system-sign.img", "system.img", header, 0)
elif header == "SSSS":
display("Getting the offset...")
offset = get_offset("system-sign.img")
display("Got {} as offset!".format(offset))
delete_header("system-sign.img", "system.img", header, offset)
display("Converting to ext4 image...")
p = Popen("simg2img system.img system.ext4", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
if(len(p.stderr.read()) != 0):
raise RuntimeError("Something went wrong while converting to ext4 image. Is simg2img installed? Bailing out...")
display("Unpacking system image...")
if os.path.exists("system_out"):
shCommand("sudo umount system_out", 0)
regen_folder("system_out")
else:
os.mkdir("system_out")
shCommand("mount -r -t ext4 -o loop system.ext4 system_out", 0)
display("system-sign.img extracted at system_out\n")
exit()
def remove_old_files(image):
display("Removing old files if they're present...")
if os.path.isfile("signimg2img.py"):
for file in glob.glob('*.*'):
if image is "full":
if file.startswith("signimg2img") or file.startswith("LICENSE") or file.startswith("README"):
pass
else:
os.remove(file)
elif file.endswith(".unpack") or file.endswith(".ext4"):
os.remove(file)
elif file.startswith("signimg2img") or file.endswith("-sign.img") or file.startswith("LICENSE") or file.startswith("README"):
pass
else:
os.remove(file)
def help():
display("USAGE: signimg2img.py -option:\n")
print(" -u image-sign.img: Unsigns the given image. (If is system will unpack it)")
print(" -o: Get image info (-o image_name).")
print(" -c: Full cleanup (removes all!)")
print("")
exit()
def main():
print('signimg2img binary - version: {}\n'.format(__version__))
if len(sys.argv) == 1:
display("Expected more arguments.\n")
help()
elif sys.argv[1] == "-h":
help()
elif sys.argv[1] == "-u" and len(sys.argv) != 1:
display("Selected: Unpack {}".format(sys.argv[2]))
check_header(sys.argv[2])
remove_old_files(sys.argv[2])
if "system" in sys.argv[2]:
unpack_system(header)
else:
delete_header(sys.argv[2], "{}.unpack".format(sys.argv[2]), header, 0)
display("Done, image extracted as {}.unpack\n".format(sys.argv[2]))
elif sys.argv[1] == "-o":
check_header(sys.argv[2])
if header is "SSSS":
offset = get_offset(sys.argv[2])
display("Offset: {}".format(offset))
display("Size: {} bytes".format(os.path.getsize(sys.argv[2])))
if header is "BFBF" or "SSSS":
display("Image can be unpacked: yes\n")
else:
display("Image can be unpacked: no (invalid header)\n")
elif sys.argv[1] == "-c":
remove_old_files("full")
if os.path.exists("system_out"):
p = Popen("umount system_out", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
if(len(p.stderr.read()) != 0):
raise RuntimeError("Cannot unmount the system_out folder!")
shutil.rmtree("system_out")
display("Cleaned up!\n")
else:
display("Invalid option: {}\n".format(sys.argv[1]))
help()
if __name__ == "__main__":
if os.getuid() != 0:
#raise RuntimeError("Script must be ran as root")
pass
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment