Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save shinyquagsire23/824d4e6b08379f3b537fa703d2b1874a to your computer and use it in GitHub Desktop.
Save shinyquagsire23/824d4e6b08379f3b537fa703d2b1874a to your computer and use it in GitHub Desktop.
Pixel Watch - Dump boot_a/boot_b from fastboot using sha1sum and ramdump memes
# Dump partitions from the Pixel Watch's fastboot using `oem sha1sum`
# and `oem ramdump` memes.
#
# Currently the first 8 bytes aren't bruteforced, but they ~can be.
# Doesn't really matter though because the first 8 bytes is the "ANDROID!" magic
# for boot partitions.
import os
import sys
import subprocess
import hashlib
import struct
# Which partition are we dumping?
dump_partition = "boot_b"
if len(sys.argv) >= 2:
dump_partition = sys.argv[1]
# Get the partition size from fastboot.
os.system("fastboot getvar partition-size:" + dump_partition + " 2> tmp_partsize.txt")
part_len = 0
with open("tmp_partsize.txt", "r") as f:
part_len_str = f.read().split("\n")[0].split(": ")[1]
part_len = int(part_len_str, 16)
print ("Partition len:", hex(part_len))
# Get the first sector hash so we can bruteforce the first 8 bytes
hash_str = ""
os.system("fastboot oem sha1sum " + dump_partition + " 0x0 0x200 2> tmp_hash.txt")
with open("tmp_hash.txt", "r") as f:
hash_str = f.read().split("\n")[1].split("(bootloader) ")[1]
# Prep/cleanup
os.system("mkdir -p partitions")
os.system("rm -f initial_dump.bin")
os.system("rm -f tmp_sector.bin")
os.system("rm -f tmp_partsize.txt")
# Get the first sector of boot_b into RAM, as a ground truth
os.system("fastboot oem sha1sum boot_b 0x0 0x200")
# Dump everything after the start of abl to find the heap.
os.system("fastboot oem ramdump stage_mem 0x9c800000 0x1000000")
os.system("fastboot get_staged initial_dump.bin")
# We're looking for "buildvariant=user"
find_initial = 0x0
with open("initial_dump.bin", "rb") as f:
contents = f.read()
for i in range(0, len(contents) // 0x4):
offs = i*0x4
if contents[offs:offs+0x10-1] == b'buildvariant=us':
find_initial = offs - 0x40
break
if find_initial == 0:
print("Failed to find heap...")
sys.exit(-1)
# sha1sum allocates 0x100000 bytes, so our stride is one sector
# under that.
dump_stride_bytes = 0x100000 - 0x200
dump_stride_sectors = dump_stride_bytes // 0x200
dump_len = part_len
dump_left = dump_len
# Start dumping
with open("partitions/" + dump_partition + ".img", "wb") as f_out:
# Dump the first sector
os.system("fastboot oem sha1sum " + dump_partition + " 0 0x400")
os.system("fastboot oem ramdump stage_mem " + hex(0x9c800000 + find_initial) + " 0x200")
os.system("fastboot get_staged tmp_sector.bin")
# Read the first sector, and guess the first 8 bytes.
bruteforce_initial = struct.unpack("<Q", b"ANDROID!")[0]
contents_missing = struct.pack("<Q", bruteforce_initial)
with open("tmp_sector.bin", "rb") as f:
contents_firstsect = f.read()[:0x200]
not_found = False # don't bruteforce for now.
m = hashlib.sha1()
b = struct.pack("<Q", bruteforce_initial)
m.update(b)
m.update(contents_firstsect[8:])
if m.hexdigest() == hash_str:
not_found = False
# If we're bruteforcing...
if not_found:
print ("Bruteforcing first 8 bytes...")
for bruteforce in range(0, 0xFFFFFFFFFFFFFFFF):
m = hashlib.sha1()
b = struct.pack("<Q", bruteforce)
m.update(b)
m.update(contents_firstsect[8:])
if bruteforce % 0x10000 == 0:
print (hex(bruteforce))
if m.hexdigest() == hash_str:
contents_firstsect = b
break
f_out.write(contents_missing)
f_out.write(contents_firstsect[8:])
dump_left -= 0x200
# Dump the rest of the sectors
for i in range(1, dump_len // 0x200, dump_stride_sectors):
offs_bytes = i * 0x200
to_dump_bytes = dump_stride_bytes
if to_dump_bytes > dump_left:
to_dump_bytes = dump_left
# Dump the previous sector and desired sector, since the first 8 bytes get cut off in the alloc.
os.system("fastboot oem sha1sum " + dump_partition + " " + hex(offs_bytes - 0x200) + " " + hex(to_dump_bytes + 0x200))
os.system("fastboot oem ramdump stage_mem " + hex(0x9c800000 + find_initial + 0x200) + " " + hex(dump_stride_bytes))
os.system("fastboot get_staged tmp_sector.bin")
# Write sector
with open("tmp_sector.bin", "rb") as f:
contents = f.read()[:to_dump_bytes]
#print(hex(len(contents)))
f_out.write(contents)
dump_left -= dump_stride_bytes
# Cleanup
os.system("rm -f initial_dump.bin")
os.system("rm -f tmp_sector.bin")
os.system("rm -f tmp_partsize.txt")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment