Skip to content

Instantly share code, notes, and snippets.

@UserUnknownFactor
Last active August 27, 2026 20:51
Show Gist options
  • Select an option

  • Save UserUnknownFactor/7862d44ecb3726490ac82665c35e2cde to your computer and use it in GitHub Desktop.

Select an option

Save UserUnknownFactor/7862d44ecb3726490ac82665c35e2cde to your computer and use it in GitHub Desktop.
Chromium gpu blackist permanent commandline patcher
#!/usr/bin/env python3
"""
Universal Chrome x64 patcher.
Replaces the "ignore-gpu-blocklist" etc checks call with mov eax, 1.
Requires: pip install pefile
Usage: python patch_chrome.py chrome.exe
"""
import sys
import pefile
TARGET_STRING1 = b"ignore-gpu-blacklist"
TARGET_STRING2 = b"unsafely-disable-devtools-self-xss-warnings"
def get_string_rva(pe: pefile.PE, s: bytes) -> int | None:
"""Return RVA of first occurrence of s in any section."""
for sec in pe.sections:
data = sec.get_data()
off = data.find(s)
if off != -1:
return sec.VirtualAddress + off
return None
def patch(path: str, paramstr: bytes):
pe = pefile.PE(path, fast_load=False)
# --- 1. Find exact RVA of our parameter via pefile ----------------
str_rva = get_string_rva(pe, paramstr)
if str_rva is None:
print(f"[!] Parameter string \"{paramstr.decode()}\" not found in PE.")
pe.close()
return
print(f"[+] Parameter string \"{paramstr.decode()}\" found at RVA: 0x{str_rva:08X}")
# --- 2. Locate .text section -------------------------------------------------
text_sec = None
for sec in pe.sections:
# Chrome uses .text; sometimes it's named differently, but .text is standard
if sec.Name.startswith(b".text"):
text_sec = sec
break
if not text_sec:
pe.close()
print("[!] .text section missing.")
sys.exit(1)
text_raw = text_sec.PointerToRawData
text_rva = text_sec.VirtualAddress
text_data = text_sec.get_data()
print(f"[+] .text FileOffset: 0x{text_raw:08X} RVA: 0x{text_rva:08X} Size: 0x{len(text_data):08X}")
# --- 3. Scan for lea rdx, [rip+disp32] ------------------------------------
# Encoding: 48 8D 15 <disp32> (7 bytes total)
# RIP = address of next instruction = lea_rva + 7
# Target RVA = lea_rva + 7 + disp32
i = 0
found_at_file_off = None
while i <= len(text_data) - 7:
if text_data[i:i+3] == b"\x48\x8D\x15":
disp = int.from_bytes(text_data[i+3:i+7], byteorder="little", signed=True)
lea_rva = text_rva + i
target_rva = lea_rva + 7 + disp
if target_rva == str_rva:
# --- 4. Verify sequence: ---
# Offsets relative to lea start (i):
# +0 : 48 8D 15 ... (lea)
# +7 : 48 89 F9/C1 (mov rcx, rdi/rax)
# +10 : E8 xx xx xx xx (call ...)
# +15 : 88 46 xx | 84 C0 xx (mov byte ptr [rsi+xx], al) | (test al, al)
if (i + 17 <= len(text_data) and
(text_data[i+7:i+10] == b"\x48\x89\xF9" or text_data[i+7:i+10] == b"\x48\x89\xC1") and
text_data[i+10] == 0xE8 and
(text_data[i+15:i+17] == b"\x88\x46" or text_data[i+15:i+17] == b"\x84\xC0")):
found_at_file_off = text_raw + i + 10 # file offset of CALL instruction
print(f"[+] Pattern matched at file offset 0x{found_at_file_off:08X} "
f"(RVA 0x{text_rva + i + 10:08X})")
break
i += 1
if found_at_file_off is None:
print("[!] Pattern not found. This Chrome may use different layout or already patched.")
pe.close()
return
# --- 5. Patch: 5-byte call -> 5-byte mov eax, 1 ----------------------------
# Original: E8 33 3F 9B FF (example)
# New: B8 01 00 00 00 (mov eax, 1)
pe.close()
with open(path, "r+b") as f:
f.seek(found_at_file_off)
old_bytes = f.read(5)
f.seek(found_at_file_off)
f.write(b"\xB8\x01\x00\x00\x00")
print(f"[+] Replaced CALL (0x{old_bytes.hex().upper()}) with MOV EAX,1 (B801000000)")
if __name__ == "__main__":
#if len(sys.argv) < 2:
#print(f"Usage: {sys.argv[0]} <chromebundle.exe>")
#sys.exit(1)
PATHCED_CR = "nw.dll" # sys.argv[0]
patch(PATHCED_CR, TARGET_STRING1)
patch(PATHCED_CR, TARGET_STRING2)
print("[*] Done.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment