> https://linux.do/t/topic/1721283 最近用到了这个工具的新版本,网上没找到开源的非无限试用的补丁工具,随即和 Claude 一起逆向了一下,直接patch验证的dll文件,一劳永逸。 纯 Python 脚本,代码可查,不报毒。 #### 使用方法 1. 退出 Navicat,确保没在后台运行 2. 将 `patch_navicat_libcc.py` 复制到 Navicat 安装目录(`libcc.dll` 所在目录) 3. 运行脚本 `python patch_navicat_libcc.py` ``` #!/usr/bin/env python3 """Navicat Premium libcc.dll 补丁工具""" import struct import shutil import sys import os import re DLL_NAME = "libcc.dll" RET_TRUE = b"\xB0\x01\xC3" RET_ZERO = b"\x31\xC0\xC3" RET_VOID = b"\xC3" VALID_PROLOGUES = (0x48, 0x49, 0x4C, 0x40, 0x41, 0x55, 0x53, 0x56, 0x57, 0x44) PATCHES = [ ("keyLogic", "验证绕过", b"CSRegistrationCenter::keyLogic(bool)", RET_TRUE), ("trialLogic", "验证绕过", b"CSRegistrationCenter::trialLogic(bool)", RET_TRUE), ("subscriptionLogic", "验证绕过", b"CSRegistrationCenter::subscriptionLogic(bool)", RET_TRUE), ("activate", "通信阻断", b"CSRegistrationCenter::activate(", RET_ZERO), ("manualActivate", "通信阻断", b"CSRegistrationCenter::manualActivate(", RET_ZERO), ("completeOfflineActivate", "通信阻断", b"CSRegistrationCenter::completeOfflineActivate(", RET_ZERO), ("fetchSubscriptionInfo", "通信阻断", b"CSRegistrationCenter::fetchSubscriptionInfo(", RET_ZERO), ("subscriptionCheck", "后台禁用", b"CSRegistrationThread::subscriptionCheck(", RET_VOID), ] def parse_pe_sections(data): e_lfanew = struct.unpack_from(" limit: if data[pos] == 0xCC: cc_end = pos while pos > limit and data[pos - 1] == 0xCC: pos -= 1 if cc_end - pos + 1 >= 2: return cc_end + 1 pos -= 1 return None def find_function_by_debug_string(data, sections, marker): str_pos = data.find(marker) if str_pos == -1: return None, "未找到特征字符串" full_str_start = str_pos while full_str_start > 0 and data[full_str_start - 1] != 0x00: full_str_start -= 1 str_rva = offset_to_rva(sections, full_str_start) if str_rva is None: return None, "字符串不在有效节区内" text = next((s for s in sections if s["name"] == ".text"), None) if text is None: return None, "未找到 .text 节区" t_start, t_end = text["rptr"], text["rptr"] + text["rsize"] ref_offset = None for m in re.finditer(b"\x8D", data[t_start:t_end]): pos = t_start + m.start() if pos < 1 or pos + 5 >= t_end: continue rex = data[pos - 1] if not (0x48 <= rex <= 0x4F): continue modrm = data[pos + 1] if (modrm & 0xC7) != 0x05: continue disp32 = struct.unpack_from(" 0: print("[完成] 补丁成功,重启 Navicat 即可生效") if __name__ == "__main__": main() ``` Loading... > https://linux.do/t/topic/1721283 最近用到了这个工具的新版本,网上没找到开源的非无限试用的补丁工具,随即和 Claude 一起逆向了一下,直接patch验证的dll文件,一劳永逸。 纯 Python 脚本,代码可查,不报毒。 #### 使用方法 1. 退出 Navicat,确保没在后台运行 2. 将 `patch_navicat_libcc.py` 复制到 Navicat 安装目录(`libcc.dll` 所在目录) 3. 运行脚本 `python patch_navicat_libcc.py` ``` #!/usr/bin/env python3 """Navicat Premium libcc.dll 补丁工具""" import struct import shutil import sys import os import re DLL_NAME = "libcc.dll" RET_TRUE = b"\xB0\x01\xC3" RET_ZERO = b"\x31\xC0\xC3" RET_VOID = b"\xC3" VALID_PROLOGUES = (0x48, 0x49, 0x4C, 0x40, 0x41, 0x55, 0x53, 0x56, 0x57, 0x44) PATCHES = [ ("keyLogic", "验证绕过", b"CSRegistrationCenter::keyLogic(bool)", RET_TRUE), ("trialLogic", "验证绕过", b"CSRegistrationCenter::trialLogic(bool)", RET_TRUE), ("subscriptionLogic", "验证绕过", b"CSRegistrationCenter::subscriptionLogic(bool)", RET_TRUE), ("activate", "通信阻断", b"CSRegistrationCenter::activate(", RET_ZERO), ("manualActivate", "通信阻断", b"CSRegistrationCenter::manualActivate(", RET_ZERO), ("completeOfflineActivate", "通信阻断", b"CSRegistrationCenter::completeOfflineActivate(", RET_ZERO), ("fetchSubscriptionInfo", "通信阻断", b"CSRegistrationCenter::fetchSubscriptionInfo(", RET_ZERO), ("subscriptionCheck", "后台禁用", b"CSRegistrationThread::subscriptionCheck(", RET_VOID), ] def parse_pe_sections(data): e_lfanew = struct.unpack_from("<I", data, 0x3C)[0] if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00": raise ValueError("无效的 PE 文件") coff = e_lfanew + 4 num_sec = struct.unpack_from("<H", data, coff + 2)[0] opt_size = struct.unpack_from("<H", data, coff + 16)[0] sec_start = coff + 20 + opt_size sections = [] for i in range(num_sec): o = sec_start + i * 40 name = data[o:o + 8].rstrip(b"\x00").decode("ascii", errors="replace") vsize, vaddr, rsize, rptr = struct.unpack_from("<4I", data, o + 8) sections.append(dict(name=name, vaddr=vaddr, vsize=vsize, rsize=rsize, rptr=rptr)) return sections def rva_to_offset(sections, rva): for s in sections: if s["vaddr"] <= rva < s["vaddr"] + max(s["vsize"], s["rsize"]): return rva - s["vaddr"] + s["rptr"] return None def offset_to_rva(sections, offset): for s in sections: if s["rptr"] <= offset < s["rptr"] + s["rsize"]: return offset - s["rptr"] + s["vaddr"] return None def _scan_for_function_entry(data, ref_offset, section_start, max_dist=8192): limit = max(section_start, ref_offset - max_dist) pos = ref_offset - 1 while pos > limit: if data[pos] == 0xCC: cc_end = pos while pos > limit and data[pos - 1] == 0xCC: pos -= 1 if cc_end - pos + 1 >= 2: return cc_end + 1 pos -= 1 return None def find_function_by_debug_string(data, sections, marker): str_pos = data.find(marker) if str_pos == -1: return None, "未找到特征字符串" full_str_start = str_pos while full_str_start > 0 and data[full_str_start - 1] != 0x00: full_str_start -= 1 str_rva = offset_to_rva(sections, full_str_start) if str_rva is None: return None, "字符串不在有效节区内" text = next((s for s in sections if s["name"] == ".text"), None) if text is None: return None, "未找到 .text 节区" t_start, t_end = text["rptr"], text["rptr"] + text["rsize"] ref_offset = None for m in re.finditer(b"\x8D", data[t_start:t_end]): pos = t_start + m.start() if pos < 1 or pos + 5 >= t_end: continue rex = data[pos - 1] if not (0x48 <= rex <= 0x4F): continue modrm = data[pos + 1] if (modrm & 0xC7) != 0x05: continue disp32 = struct.unpack_from("<i", data, pos + 2)[0] inst_file_pos = pos - 1 inst_rva = offset_to_rva(sections, inst_file_pos) if inst_rva is None: continue if inst_rva + 7 + disp32 == str_rva: ref_offset = inst_file_pos break if ref_offset is None: return None, "未在 .text 节中找到字符串引用" entry = _scan_for_function_entry(data, ref_offset, t_start) if entry is None: return None, "向前扫描未找到函数边界" return entry, None def main(): dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), DLL_NAME) if not os.path.exists(dll_path): print(f"[错误] 未找到 {dll_path}") sys.exit(1) backup_path = dll_path + ".bak" with open(dll_path, "rb") as f: data = bytearray(f.read()) sections = parse_pe_sections(data) results = [] patched_any = False already_count = 0 for name, desc, marker, patch_bytes in PATCHES: func_offset, err = find_function_by_debug_string(data, sections, marker) if func_offset is None: results.append((name, desc, "跳过", err)) continue current = bytes(data[func_offset:func_offset + len(patch_bytes)]) if current == patch_bytes: results.append((name, desc, "已补丁", "")) already_count += 1 continue if current[0] not in VALID_PROLOGUES: results.append((name, desc, "跳过", f"异常序言 0x{current[0]:02X}")) continue data[func_offset:func_offset + len(patch_bytes)] = patch_bytes patched_any = True results.append((name, desc, "成功", "")) if not patched_any: if already_count == len(PATCHES): print(f"[完成] 所有 {len(PATCHES)} 个补丁均已应用,无需重复操作") else: print("[完成] 无新补丁需要写入") _print_summary(results) return if not os.path.exists(backup_path): shutil.copy2(dll_path, backup_path) try: with open(dll_path, "wb") as f: f.write(data) except PermissionError: print("[错误] 无写入权限,请关闭 Navicat 并以管理员身份运行") sys.exit(1) _print_summary(results) def _print_summary(results): ok = sum(1 for _, _, s, _ in results if s == "成功") done = sum(1 for _, _, s, _ in results if s == "已补丁") skip = sum(1 for _, _, s, _ in results if s == "跳过") for name, desc, status, info in results: icon = {"成功": "+", "已补丁": "=", "跳过": "-"}[status] suffix = f" ({info})" if info else "" print(f" [{icon}] [{desc}] {name:30s} {status}{suffix}") print(f" 成功: {ok} 已补丁: {done} 跳过: {skip} 总计: {len(results)}") if ok > 0: print("[完成] 补丁成功,重启 Navicat 即可生效") if __name__ == "__main__": main() ``` 最后修改:2026 年 04 月 07 日 © 允许规范转载 打赏 赞赏作者 赞 如果觉得我的文章对你有用,请随意赞赏