mirror of
https://github.com/archlinux/aur.git
synced 2026-02-09 06:38:44 +01:00
142 lines
4.5 KiB
Python
142 lines
4.5 KiB
Python
lea64 = b'\x48\x8d'
|
|
call64 = b'\xFF'
|
|
little = lambda x: int.from_bytes(x, byteorder='little')
|
|
|
|
# iterate over matches for lea64
|
|
def leas(f, start=None, end=None, op=lea64):
|
|
match = f.find(op, start, end)
|
|
while match != -1:
|
|
yield match
|
|
match = f.find(op, match + 1, end)
|
|
|
|
# give the position and loaded virtual address for each lea match
|
|
def addr(f, start=None, end=None, op=lea64, reg=1):
|
|
arg = len(op) + reg
|
|
for i in leas(f, start, end, op):
|
|
yield [i, i + arg + 4 + little(f[i + arg: i + arg + 4])]
|
|
|
|
# VA offsets for .text and .rdata
|
|
def offsets(f):
|
|
# .text567_size_want
|
|
off = (f.index(b".text"), f.index(b".rdata"))
|
|
return [a - b for a, b in zip(
|
|
[little(f[i + 0xc : i + 0x10]) for i in off],
|
|
[little(f[i + 0x14 : i + 0x18]) for i in off],
|
|
)]
|
|
|
|
# gives storage locations of target phrases accounting for offsets
|
|
def rel32(f, target=b'Could not verify RL version'):
|
|
text, rdata = offsets(f)
|
|
return f.index(target) + rdata - text
|
|
|
|
# gives positions of references to the target phrases
|
|
def refs(f, rel, it):
|
|
return tuple(i for i, x in it if x == rel)
|
|
|
|
optionals = (
|
|
(2, "Magic"),
|
|
(1, "MajorLinkerVersion"),
|
|
(1, "MinorLinkerVersion"),
|
|
(4, "SizeOfCode"),
|
|
(4, "SizeOfInitializedData"),
|
|
(4, "SizeOfUninitializedData"),
|
|
(4, "AddressOfEntryPoint"),
|
|
(4, "BaseOfCode"),
|
|
(8, "ImageBase"),
|
|
(4, "SectionAlignment"),
|
|
(4, "FileAlignment"),
|
|
(2, "MajorOperatingSystemVersion"),
|
|
(2, "MinorOperatingSystemVersion"),
|
|
(2, "MajorImageVersion"),
|
|
(2, "MinorImageVersion"),
|
|
(2, "MajorSubsystemVersion"),
|
|
(2, "MinorSubsystemVersion"),
|
|
(4, "Win32VersionValue"),
|
|
(4, "SizeOfImage"),
|
|
(4, "SizeOfHeaders"),
|
|
(4, "CheckSum"),
|
|
(2, "Subsystem"),
|
|
(2, "DllCharacteristics"),
|
|
(8, "SizeOfStackReserve"),
|
|
(8, "SizeOfStackCommit"),
|
|
(8, "SizeOfHeapReserve"),
|
|
(8, "SizeOfHeapCommit"),
|
|
(4, "LoaderFlags"),
|
|
(4, "NumberOfRvaAndSizes"),
|
|
)
|
|
|
|
def header(name, offset=0):
|
|
for size, field in optionals:
|
|
if field == name:
|
|
return (offset, offset + size)
|
|
offset += size
|
|
raise Exception()
|
|
|
|
# gives offset and size of DLL directory
|
|
def idata(f, off=None):
|
|
off = off or offsets(f)
|
|
magic = f.index(b'\x0b\x02')
|
|
iaddr = header(optionals[-1][1], magic + 0xc)
|
|
isize = header(optionals[-1][1], magic + 0x10)
|
|
return little(f[slice(*iaddr)]) - off[1], little(f[slice(*isize)])
|
|
|
|
ox = lambda x: tuple(map(hex, x))
|
|
|
|
# gives IAT start and end
|
|
def user32(f, dlls=None, size=None, off=None):
|
|
off = off or offsets(f)
|
|
dlls, size = idata(f, off) if dlls is None else (dlls, size)
|
|
rows = [dlls + i for i in range(0, size, 20)]
|
|
rvas = [little(f[i + 0xc : i + 0x10]) for i in rows]
|
|
tabs = [little(f[i + 0x10 : i + 0x14]) for i in rows]
|
|
matching = b'USER32.dll'
|
|
substr = lambda i: f[i - off[1] : i - off[1] + len(matching)]
|
|
ptrs = [n for n, i in enumerate(rvas) if i != 0 and substr(i) == matching]
|
|
assert len(ptrs) == 1
|
|
ptr = ptrs[0]
|
|
end = min(i for i in tabs if i > tabs[ptr])
|
|
return tabs[ptr] - off[1], end - off[1]
|
|
|
|
# gives start of 8 byte IAT line as a .text RVA
|
|
def fns(f, iat=None, end=None, off=None):
|
|
off = off or offsets(f)
|
|
iat, end = user32(f, off=off) if iat is None else (dlls, size)
|
|
rvas = [little(f[i : i + 4]) for i in range(iat, end, 8)]
|
|
vas = [i - off[1] for i in rvas if i != 0]
|
|
hints = [f[i : i + 2] for i in vas]
|
|
names = [f[i + 2 : f.index(b'\x00', i + 2)] for i in vas]
|
|
return iat + names.index(b'MessageBoxA') * 8 + off[1] - off[0]
|
|
|
|
def calls(f, dbg=True):
|
|
loads, msg = refs(f, rel32(f), addr(f)), fns(f)
|
|
res = [refs(f, msg, (next(addr(f, ref, op=call64)),))[0] for ref in loads]
|
|
if dbg:
|
|
print(ox(res))
|
|
return res
|
|
|
|
def mask(f, update=b'\xB8\x06\x00\x00\x00', space=6):
|
|
prev, out = 0, b''
|
|
for pos in calls(f):
|
|
out += f[prev:pos] + update + b'\x90' * (space - len(update))
|
|
prev = pos + space
|
|
out += f[prev:]
|
|
assert len(f) == len(out)
|
|
return out
|
|
|
|
def dll(path=None, dest=None):
|
|
global Path
|
|
from pathlib import Path
|
|
path = Path(__file__).parents[0] if path is None else Path(path)
|
|
path = path if path.is_file() else path / "bakkesmod.dll"
|
|
with open(path, "rb") as fp:
|
|
f = fp.read()
|
|
f = mask(f)
|
|
dest = path.parents[0] if dest is None else dest
|
|
dest = dest if dest.is_file() else dest / "bakkesmod_promptless.dll"
|
|
with open(dest, "wb") as fp:
|
|
fp.write(f)
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
dll(*sys.argv[1:])
|
|
|