/
armqrc.py
96 lines (85 loc) · 5.13 KB
/
armqrc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python3
import sys
import unicorn as uc
import unicorn.arm_const as uca
import elftools.elf.elffile as ef
if len(sys.argv) < 2:
print(f"usage: {sys.argv[0]} arm_elf_qt_binary...")
exit(2)
errc = 0
for binary in sys.argv[1:]:
try:
found = 0
with open(binary, "rb") as f:
elf = ef.ELFFile(f)
if elf.get_machine_arch() != "ARM":
raise Exception(f"Only ARM binaries are supported, but {binary} is {elf.get_machine_arch()}")
for sym in elf.get_section_by_name(".dynsym").iter_symbols():
if len(sym.name)-3 >= 12 and sym.name.startswith(f"_Z{len(sym.name)-3-2}qInitResources_") and sym.name[-1] == "v":
found += 1
try:
qrc_name, qrc_init = sym.name[4+len("qInitResources_"):-1], sym["st_value"]
try:
# initialize unicorn
emu = uc.Uc(uc.UC_ARCH_ARM, uc.UC_MODE_THUMB if qrc_init&1 else uc.UC_MODE_ARM)
# load the elf
for x in elf.iter_segments("PT_LOAD"):
page_size = 1024
seg_addr = x["p_vaddr"]
seg_size = x["p_memsz"]
seg_prot = x["p_flags"]
map_addr = (seg_addr//page_size)*page_size
map_size = ((seg_size+seg_addr-map_addr)//page_size + 1)*page_size
map_prot = 0
map_prot |= uc.UC_PROT_EXEC if seg_prot&0b001 else 0
map_prot |= uc.UC_PROT_WRITE if seg_prot&0b010 else 0
map_prot |= uc.UC_PROT_READ if seg_prot&0b100 else 0
emu.mem_map(map_addr, map_size, map_prot)
emu.mem_write(seg_addr, x.data()) # p_offset, p_filesz
# initialize the stack
emu.mem_map(0x40000000 - 0x10000, 0x10000)
emu.reg_write(uca.UC_ARM_REG_SP, 0x40000000)
# r0, r1, r2, r3 should be overwritten later
emu.reg_write(uca.UC_ARM_REG_R0, 0xFFFFFFFF)
emu.reg_write(uca.UC_ARM_REG_R1, 0xFFFFFFFF)
emu.reg_write(uca.UC_ARM_REG_R2, 0xFFFFFFFF)
emu.reg_write(uca.UC_ARM_REG_R3, 0xFFFFFFFF)
# execute until a branch
def block_hook(emu, address, size, user_data):
if address != qrc_init&~1:
raise Exception(f"branched")
emu.hook_add(uc.UC_HOOK_BLOCK, block_hook)
try:
emu.emu_start(qrc_init, qrc_init+128)
except Exception as err:
if str(err) != "branched":
raise Exception(f"Failed to emulate") from err
# get the args to qRegisterResourceData
qrc_version = emu.reg_read(uca.UC_ARM_REG_R0)
qrc_tree = emu.reg_read(uca.UC_ARM_REG_R1)
qrc_names = emu.reg_read(uca.UC_ARM_REG_R2)
qrc_data = emu.reg_read(uca.UC_ARM_REG_R3)
# convert the offsets
if qrc_version == 0xFFFFFFFF or qrc_tree == 0xFFFFFFFF or qrc_names == 0xFFFFFFFF or qrc_data == 0xFFFFFFFF:
raise Exception("qInitResources didn't set arguments before branching")
def virt2file(addr):
for x in elf.iter_segments("PT_LOAD"):
vaddr, offset, filesz, memsz, flags = x["p_vaddr"], x["p_offset"], x["p_filesz"], x["p_memsz"], x["p_flags"]
if addr >= vaddr and addr < addr+filesz:
return addr - vaddr + offset
raise Exception(f"Failed to find segment for address 0x{addr:X}")
qrc_tree, qrc_names, qrc_data = virt2file(qrc_tree), virt2file(qrc_names), virt2file(qrc_data)
print(f"{binary} {qrc_version} {qrc_tree:8d} {qrc_data:8d} {qrc_names:8d} # {qrc_name}")
except Exception as ex:
raise Exception(f"Failed to extract resources from qInitResources_{qrc_name}@0x{qrc_init:X}") from ex
except Exception as ex:
errc += 1
print(f"warning: {binary}: Failed to extract resources from {binary}: {ex}", file=sys.stderr)
if found == 0:
errc += 1
print(f"error: {binary}: No resources found (you may need to look manually)", file=sys.stderr)
except Exception as ex:
errc += 1
print(f"error: {binary}: {ex}", file=sys.stderr)
if errc != 0:
exit(1)