mirror of
https://github.com/archlinux/aur.git
synced 2026-02-09 14:34:42 +01:00
promptless dll patch
This commit is contained in:
parent
621fb4d5e9
commit
ff3ca4dd3e
3 changed files with 182 additions and 4 deletions
1
.SRCINFO
1
.SRCINFO
|
|
@ -10,6 +10,7 @@ pkgbase = bakkesmod-steam
|
|||
makedepends = mingw-w64-gcc
|
||||
makedepends = mingw-w64-headers
|
||||
makedepends = mingw-w64-winpthreads
|
||||
makedepends = python
|
||||
source = https://github.com/bakkesmodorg/BakkesModInjectorCpp/releases/latest/download/BakkesModSetup.exe
|
||||
source = https://github.com/bakkesmodorg/BakkesModInjectorCpp/releases/latest/download/BakkesModInjectorWin7.zip
|
||||
source = https://api.github.com/repos/bakkesmodorg/BakkesModInjectorCpp/zipball/master
|
||||
|
|
|
|||
10
PKGBUILD
10
PKGBUILD
|
|
@ -8,7 +8,7 @@ url="https://bakkesmod.com/"
|
|||
license=('GPL')
|
||||
groups=()
|
||||
depends=()
|
||||
makedepends=('mingw-w64-binutils' 'mingw-w64-crt' 'mingw-w64-gcc' 'mingw-w64-headers' 'mingw-w64-winpthreads')
|
||||
makedepends=('mingw-w64-binutils' 'mingw-w64-crt' 'mingw-w64-gcc' 'mingw-w64-headers' 'mingw-w64-winpthreads' 'python')
|
||||
optdepends=()
|
||||
source=(
|
||||
"https://github.com/bakkesmodorg/BakkesModInjectorCpp/releases/latest/download/BakkesModSetup.exe"
|
||||
|
|
@ -40,7 +40,7 @@ build() {
|
|||
DllInjector dllInjector;
|
||||
std::wstring ps = L"RocketLeague.exe";
|
||||
std::filesystem::path ws =
|
||||
"C:\\users\\steamuser\\Application Data\\bakkesmod\\bakkesmod\\dll\\bakkesmod.dll";
|
||||
"C:\\users\\steamuser\\Application Data\\bakkesmod\\bakkesmod\\dll\\bakkesmod_promptless.dll";
|
||||
dllInjector.InjectDLL(ps, ws);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -81,8 +81,9 @@ package() {
|
|||
else
|
||||
WINEPREFIX="$compat/pfx/" "$proton/bin/wine64" "$srcdir/BakkesModSetup.exe"
|
||||
fi
|
||||
python "$srcpath/dll_patch.py" "$bm_pfx/bakkesmod/dll"
|
||||
|
||||
cp -f "$srcdir/BakkesMod.exe" "$srcdir/inject.exe" "$bm_pfx"
|
||||
cp -f "$srcdir/BakkesMod.exe" "$srcdir/inject.exe" "$srcpath/dll_patch.py" "$bm_pfx"
|
||||
echo "$paths" > "$bm_pfx/runner.sh"
|
||||
chmod a+x "$bm_pfx/runner.sh"
|
||||
cat <<" EOF" >> "$bm_pfx/runner.sh"
|
||||
|
|
@ -94,6 +95,7 @@ package() {
|
|||
# setup=`find "$HOME/.steam/steam/userdata" -name "localconfig.vdf"`
|
||||
# setup=`grep 252950 -A 10 "$setup" | grep LaunchOptions | sed 's@^\([^"]*"\)\{3\}@@'`
|
||||
# echo "set launch options to: \"$bm_pfx/launch.sh & $setup"
|
||||
# I recommend the -NoKeyboardUI option
|
||||
# # above is still broken, with update status unclear
|
||||
# # unrelated: I recommend the -NoKeyboardUI option
|
||||
}
|
||||
|
||||
|
|
|
|||
175
dll_patch.py
Normal file
175
dll_patch.py
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
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],
|
||||
)]
|
||||
|
||||
targets = [
|
||||
b'Could not verify RL version',
|
||||
b'Dunno how the user got here',
|
||||
b'User wishes to inject anyway',
|
||||
]
|
||||
|
||||
# gives storage locations of target phrases accounting for offsets
|
||||
def rel32(f):
|
||||
text, rdata = offsets(f)
|
||||
return [f.index(i) + rdata - text for i in targets]
|
||||
|
||||
# gives positions of references to the target phrases
|
||||
def refs(f, rel=None, it=None):
|
||||
default = rel is None and it is None
|
||||
rel, it = rel or rel32(f), it or addr(f)
|
||||
res = tuple(zip(*[(rel.index(x), i) for i, x in it if x in rel]))
|
||||
if default:
|
||||
assert res[0] == (0, 1, 2, 0, 1, 2)
|
||||
return (res[1][:3], res[1][3:])
|
||||
return res
|
||||
|
||||
jcc32 = [b'\x0F' + int.to_bytes(i, 1, 'little') for i in range(0x80, 0x90)]
|
||||
jcc8 = list(bytes(range(0x70, 0x80)))
|
||||
|
||||
# gives "inject anyway" jmp RVAs
|
||||
def branches(f, ref=None):
|
||||
ref = ref or refs(f)
|
||||
def inner(a, b):
|
||||
seq = f[a : b]
|
||||
op32 = [seq.index(i) for i in jcc32 if i in seq]
|
||||
op8 = [seq.index(i) for i in jcc8 if i in seq]
|
||||
loc32 = [a + i + 6 + little(seq[i + 2 : i + 6]) for i in op32]
|
||||
loc8 = [a + i + 2 + little(seq[i + 1 : i + 2]) for i in op8]
|
||||
return loc8 + loc32
|
||||
return [max(m for m in inner(i, j) if m <= k) for i, j, k in ref]
|
||||
|
||||
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("NumberOfRvaAndSizes", magic + 0xc)
|
||||
isize = header("NumberOfRvaAndSizes", 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 shunts(f, dbg=True):
|
||||
loads, msg = refs(f), fns(f)
|
||||
calls = [refs(f, (msg,), addr(f, *ref[:2], call64))[1][0] for ref in loads]
|
||||
skips = branches(f, loads)
|
||||
paired = tuple(zip(calls, skips))
|
||||
if dbg:
|
||||
print(tuple(map(ox, paired)))
|
||||
return paired
|
||||
|
||||
jmp = lambda x: b'\xE9' + int.to_bytes(x, 4, "little")
|
||||
|
||||
def mask(f, space=6):
|
||||
prev, out = 0, b''
|
||||
for pos, end in shunts(f):
|
||||
update = jmp(end - pos - 5)
|
||||
assert len(update) == 5
|
||||
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, dest=None):
|
||||
global Path
|
||||
from pathlib import Path
|
||||
path = 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:])
|
||||
|
||||
Loading…
Add table
Reference in a new issue