Files
oxpoeandREA (managed-code analysis) f58a011f4d GAMEMAX IceBurg LCD Linux bridge: sender + tray app
Reverse-engineered Linux driver for the LCD panel of the GAMEMAX IceBurg
Digital AIO cooler (HID 5131:2007). Includes:

- src/gamemax_report.py  pure-stdlib sender: reads hwmon/proc sensors and
  streams the 65-byte HID report the firmware understands (no deps)
- src/gamemax_gui.py     PySide6 tray app: animated monitor, fan/temperature
  channel pickers, autostart, blank mode, single instance, crash log
- packaging/             AppImage build script, udev rule, systemd user unit
- tools/                 IL dump helpers used to read the vendor MSIL
- docs/                  full reverse-engineering analysis
- DISCLAIMER.md          Linux-compatibility project, not affiliated with vendor

Co-authored-by: REA (managed-code analysis)
2026-10-13 06:26:52 +07:00

104 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Dump MSIL of methods in a .NET/mixed-mode assembly with resolved operands.
Usage:
ildump.py <assembly> list <regex>
ildump.py <assembly> dump <regex>
ildump.py <assembly> callers <regex>
"""
import re
import sys
import dnfile
from dncil.cil.body.reader import read_method_body_from_bytes
TBL = {0x01: "TypeRef", 0x02: "TypeDef", 0x04: "Field", 0x06: "MethodDef",
0x0A: "MemberRef", 0x1B: "TypeSpec", 0x2B: "MethodSpec", 0x70: "String"}
CALLISH = {"call", "callvirt", "ldftn", "newobj", "ldvirtftn", "jmp"}
def type_name(pe, row):
if row is None:
return "?"
for attr in ("TypeName", "Name"):
if hasattr(row, attr) and getattr(row, attr):
ns = getattr(row, "TypeNamespace", "") or ""
return str(f"{ns}.{getattr(row, attr)}").lstrip(".")
return "?"
def build_maps(pe):
md = pe.net.mdtables
names = {}
for i, row in enumerate(md.MethodDef.rows):
names[0x06000000 | (i + 1)] = str(row.Name)
for i, row in enumerate(md.MemberRef.rows):
parent = "?"
try:
parent = type_name(pe, row.Class.row) if row.Class else "?"
except Exception:
pass
names[0x0A000000 | (i + 1)] = f"{parent}::{str(row.Name)}"
for i, row in enumerate(md.Field.rows):
names[0x04000000 | (i + 1)] = str(row.Name)
for tbl, base in (("TypeDef", 0x02000000), ("TypeRef", 0x01000000)):
for i, row in enumerate(getattr(md, tbl).rows):
names[base | (i + 1)] = type_name(pe, row)
return names
def decode(pe, row, maps):
raw = pe.get_data(row.Rva, 12 + 65536)
body = read_method_body_from_bytes(raw)
out = []
for ins in body.instructions:
op = getattr(ins.opcode, "name", str(ins.opcode)).lower()
operand = ins.operand
val = getattr(operand, "value", operand)
text = ""
if isinstance(val, int) and val > 0x02000000:
text = maps.get(val, f"0x{val:08x}")
if val >> 24 == 0x70:
try:
text = repr(pe.net.user_strings.get(val & 0xFFFFFF))
except Exception:
text = "<bad string>"
elif val is not None:
text = str(val)
out.append((ins.offset, op, text))
return out
def main():
path, cmd, pattern = sys.argv[1], sys.argv[2], sys.argv[3]
pe = dnfile.dnPE(path)
maps = build_maps(pe)
rx = re.compile(pattern)
md = pe.net.mdtables
rows = [(0x06000000 | (i + 1), row) for i, row in enumerate(md.MethodDef.rows)]
targets = [(t, r) for t, r in rows if rx.search(str(r.Name))]
if cmd == "list":
for t, r in targets:
print(f"{t:08x} {str(r.Name)}")
return
if cmd == "callers":
for t, r in rows:
try:
for off, op, text in decode(pe, r, maps):
if op in CALLISH and rx.search(text):
print(f"{str(r.Name)} @il+{off}: {op} {text}")
except Exception:
continue
return
for t, r in targets:
print(f"=== {t:08x} {str(r.Name)}")
try:
for off, op, text in decode(pe, r, maps):
print(f" +{off:4} {op:16} {text}")
except Exception as e:
print(f" <decode failed: {e}>")
if __name__ == "__main__":
main()