IDM PATCH
IDM 6.43 Build 10 Disk Patch Script
仅供学习交流使用,请支持正版。
import argparse
import hashlib
import os
import shutil
import subprocess
import sys
import time
if os.name == "nt":
for _s in (sys.stdout, sys.stderr):
try:
_s.reconfigure(encoding="utf-8", errors="replace")
except (AttributeError, ValueError):
pass
HERE = os.path.dirname(os.path.abspath(__file__))
BASE = 0x400C00 # 文件偏移 = VA - BASE
# 系统工具目录:Windows 用 %SystemRoot%,WSL 下退回 /mnt/c。
SYS32 = (os.path.join(os.environ.get("SystemRoot", r"C:\Windows"), "System32")
if os.name == "nt" else "/mnt/c/Windows/System32")
def off(va):
return va - BASE
# —— 补丁点数据省略 ——
# (label, VA, ORIG, STUB):地址、原始字节、补丁字节,请自行逆向填写。
PATCHES = []
REG_KEY = r"Software\DownloadManager"
COMMON_DIRS = [
r"C:\Program Files (x86)\Internet Download Manager",
r"C:\Program Files\Internet Download Manager",
]
def _tool(*names):
for n in names:
p = shutil.which(n)
if p:
return p
for n in names:
if os.path.isabs(n) and os.path.exists(n):
return n
return None
REG = _tool("reg", "reg.exe", os.path.join(SYS32, "reg.exe"))
PWSH = _tool("powershell", "powershell.exe",
os.path.join(SYS32, r"WindowsPowerShell\v1.0\powershell.exe"))
TASKKILL = _tool("taskkill", "taskkill.exe", os.path.join(SYS32, "taskkill.exe"))
def _run(cmd):
try:
return subprocess.run(cmd, capture_output=True, text=True, errors="replace")
except OSError:
return None
def _reg_get(hive, key, value):
if os.name == "nt":
try:
import winreg
h = {"HKCU": winreg.HKEY_CURRENT_USER,
"HKLM": winreg.HKEY_LOCAL_MACHINE}[hive]
with winreg.OpenKey(h, key) as k:
return winreg.QueryValueEx(k, value)[0]
except OSError:
return None
if not REG:
return None
r = _run([REG, "query", "%s\\%s" % (hive, key), "/v", value])
if not r or r.returncode != 0:
return None
for line in r.stdout.splitlines():
parts = line.split(None, 2)
if len(parts) == 3 and parts[0] == value:
return parts[2].strip()
return None
def _proc_path():
if not PWSH:
return None
r = _run([PWSH, "-NoProfile", "-Command",
"(Get-Process IDMan -ErrorAction SilentlyContinue | "
"Select-Object -First 1).Path"])
if r and r.returncode == 0:
return r.stdout.strip() or None
return None
def _to_native(p):
if not p or os.name == "nt" or p.startswith("/"):
return p
w = shutil.which("wslpath")
if not w:
return p
r = _run([w, "-u", p])
return (r.stdout.strip() if r and r.returncode == 0 else p)
def _exe_in(p):
"""目录 -> 里面的 idman.exe(大小写不敏感);文件 -> 原样返回。"""
if p and os.path.isdir(p):
for n in os.listdir(p):
if n.lower() == "idman.exe":
return os.path.join(p, n)
return os.path.join(p, "IDMan.exe")
return p
def find_idm(path=None):
if path:
return _exe_in(_to_native(path.strip('"')))
cands = []
exe = _reg_get("HKCU", REG_KEY, "ExePath")
if exe:
cands.append(exe)
loc = _reg_get(
"HKLM",
r"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
r"\Internet Download Manager",
"InstallLocation")
if loc:
cands.append(os.path.join(loc, "IDMan.exe"))
exe = _proc_path()
if exe:
cands.append(exe)
cands += [os.path.join(d, "IDMan.exe") for d in COMMON_DIRS]
cands.append(os.path.join(HERE, "IDMan.exe"))
for c in cands:
n = _to_native(c)
if n and os.path.exists(n):
return n
return None
def stop_idm():
if TASKKILL:
_run([TASKKILL, "/F", "/IM", "IDMan.exe"])
time.sleep(1) # 等文件句柄释放,写入失败时下面还有重试兜底
def _is_admin():
if os.name != "nt":
return True
try:
import ctypes
return bool(ctypes.windll.shell32.IsUserAnAdmin())
except Exception:
return True
def _try_elevate():
"""写 exe 被日时,用 UAC 以管理员在新窗口重跑本脚本(保留原参数)。"""
if os.name != "nt" or _is_admin():
return False
import ctypes
script = os.path.abspath(sys.argv[0])
inner = subprocess.list2cmdline([sys.executable, script] + sys.argv[1:])
try:
# cmd 规则:首字符为引号时会剥掉首尾引号,故外层只包一对。
rc = ctypes.windll.shell32.ShellExecuteW(
None, "runas", os.path.join(SYS32, "cmd.exe"),
'/k "{}"'.format(inner), None, 1)
except Exception:
return False
if rc > 32:
print("[+] 已请求管理员权限,正在新窗口重跑(UAC 弹窗请点『是』)...")
return True
print("[!] 提权被拒绝(UAC)。请右键『以管理员身份运行』后重试。")
return False
def restore_file(exe):
"""从 exe.bak 还原原版文件。"""
bak = exe + ".bak"
if not os.path.exists(bak):
print("[!] 没有备份 %s,无法还原" % bak)
return "no-bak"
for attempt in range(10):
try:
shutil.copy2(bak, exe)
break
except PermissionError:
if attempt == 9:
print("[!] 无法写入 %s(被占用或无权限)。" % exe)
print(" 请彻底退出 IDM(含托盘),或右键以管理员身份重跑。")
return "locked"
time.sleep(0.5)
with open(exe, "rb") as f:
md5 = hashlib.md5(f.read()).hexdigest()
print("[+] 已从 %s 还原原版" % bak)
print("[+] md5 %s" % md5)
return "restored"
def patch_file(exe, write=True):
bak = exe + ".bak"
data = bytearray(open(exe, "rb").read())
todo = []
for label, va, orig, new in PATCHES:
o = off(va)
cur = bytes(data[o:o + len(new)])
if cur == new:
print("[=] %-38s already patched" % label)
continue
if bytes(data[o:o + len(orig)]) != orig:
print("[!] unsupported at %#x (%s): %s" % (o, label, bytes(data[o:o + 16]).hex()))
print(" re-analyze and update PATCHES in this script.")
return "unsupported"
todo.append((o, new, label))
if not todo:
return "patched"
if not write:
for o, new, label in todo:
print("[~] %-38s would patch file %#x" % (label, o))
return "dry"
if not os.path.exists(bak):
shutil.copy2(exe, bak)
print("[+] backup ->", bak)
for o, new, label in todo:
data[o:o + len(new)] = new
print("[+] %-38s file %#x" % (label, o))
for attempt in range(10):
try:
with open(exe, "wb") as f:
f.write(data)
break
except PermissionError:
if attempt == 9:
print("[!] 无法写入 %s(被占用或无权限)。" % exe)
print(" 请彻底退出 IDM(含托盘),或右键以管理员身份重跑。")
return "locked"
time.sleep(0.5)
print("[+] patch OK. md5 %s" % hashlib.md5(bytes(data)).hexdigest())
return "ok"
def main():
ap = argparse.ArgumentParser(
description="IDM 6.43b10 disk patch")
ap.add_argument("--dir", "--install-dir", dest="dir", metavar="PATH",
help="IDM 安装目录或 IDMan.exe 路径(默认自动检测)")
ap.add_argument("--restore", action="store_true",
help="还原原版 exe(从 .bak 回滚)")
ap.add_argument("--dry-run", action="store_true", help="check only")
a = ap.parse_args()
exe = find_idm(a.dir)
if not exe or not os.path.exists(exe):
print("[!] 未找到 IDMan.exe;请用 --dir 指定安装目录或 IDMan.exe 路径")
return 2
print("[*] target:", exe)
if a.restore:
if a.dry_run:
print("[~] would restore from %s" % (exe + ".bak"))
return 0
stop_idm()
st = restore_file(exe)
if st == "locked":
return 0 if _try_elevate() else 1
return 0 if st == "restored" else 1
if not a.dry_run:
stop_idm()
state = patch_file(exe, write=not a.dry_run)
if state == "locked":
return 0 if _try_elevate() else 1
if state == "unsupported":
return 1
if a.dry_run:
print("[*] dry-run: no write")
return 0
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
print("\n[!] 已中断")
sys.exit(130)