Skip to content

Instantly share code, notes, and snippets.

@arisada
Created March 16, 2024 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save arisada/0423d42cfb04727ab0006392aadf42b1 to your computer and use it in GitHub Desktop.
Save arisada/0423d42cfb04727ab0006392aadf42b1 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
"""This script patches PixInsight 1.8.9-2 to nop the call to QWidget::raise()
in PixInsightWorkspace::OnMainWindowUnblocked().
This works around the very annoying behavior of PixInsight putting itself
as top window when it receives focus. This happens often when you have other
windows open on top of Pix and you have "focus follows mouse" active in your
WM.
Tested for me, your mileage may vary.
This doesn't circumvent any security from PI.
2024 Aris Adamantiadis
"""
#from pwn import ELF
import struct
from hexdump import hexdump
import subprocess
import shutil
filename = 'PixInsight/bin/PixInsight'
#filename = 'pi-1.8.9-2-old/PixInsight/bin/PixInsight'
function_name = '_ZN2pi19PixInsightWorkspace21OnMainWindowUnblockedEv'
qwidget_raise = '_ZN7QWidget5raiseEv'
def patch_function_pwn(filename):
elf = ELF(filename)
print("ELF loaded")
function = elf.functions[function_name]
if function is None:
print(f"Function '{function_name}' not found.")
return
print(f'Function: {function}')
# Find the PLT entry for the symbol
plt_entry_offset = elf.plt[qwidget_raise]
if plt_entry_offset is None:
print(f"PLT entry for '{qwidget_raise}' not found.")
return
print(f'PLT entry: 0x{plt_entry_offset:x}')
fct = elf.read(function.address, function.size)
hexdump(fct)
for i in range(len(fct)-5):
if fct[i] != 0xe8:
continue
reladdr=struct.unpack("<I", fct[i+1:i+5])[0]
dst = (function.address + i + 5 + 4 + reladdr) & 0xffffffff
print(f"Call reladdr 0x{reladdr:x}, dst 0x{dst:x}")
if dst == plt_entry_offset:
print("Match")
else:
print("Call not found")
return
print(f"Call to '{function_name}' patched successfully.")
def exec_cmd(cmd):
result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, universal_newlines=True)
return result.strip()
def patch_function_objdump():
plt_entry_offset = exec_cmd(f"objdump -d {filename} | grep {qwidget_raise}")
plt_entry_offset = int(plt_entry_offset.split(':')[0], 16)
print(f'PLT entry: 0x{plt_entry_offset:x}')
function_address = exec_cmd(f"nm {filename} | grep -w 'T {function_name}'")
function_address = int(function_address.split(' ')[0], 16)
print(f'function address: 0x{function_address:x}')
shutil.copyfile(filename, filename + ".bak")
with open(filename, "r+b") as f:
f.seek(function_address)
fct = f.read(0x250)
# hexdump(fct)
matches = []
for i in range(len(fct)-5):
if fct[i] != 0xe8:
continue
reladdr=struct.unpack("<I", fct[i+1:i+5])[0]
dst = (function_address + i + 5 + 4 + reladdr) & 0xffffffff
print(f"Call reladdr 0x{reladdr:x}, dst 0x{dst:x}")
if dst == plt_entry_offset:
print("Match")
matches.append(i)
if len(matches) != 1:
print("Won't patch, !=0 matches")
return
f.seek(function_address + matches[0])
f.write(b"\x90"*5)
print(f"Call to '{function_name}' patched successfully.")
if __name__ == "__main__":
patch_function_objdump()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment