吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6962|回复: 54
上一主题 下一主题
收起左侧

[MacOS逆向] Navicat 17 之 macOS 替换公钥通杀思路

    [复制链接]
跳转到指定楼层
楼主
skrets 发表于 2025-10-6 17:45 回帖奖励
[ 本帖最后由 skrets 于 2025-10-6 17:50 编辑 ]

看了 iwolf 大佬的 Navicat 17 破解教程 觉得受益匪浅,写的很好。

正好 macOS 上几乎没人弄非商店版的 Navicat,便想试试能不能移植过来,顺便试试新的替换公钥方法。

替换公钥

相信很多人都注意到了 Navicat 安装目录下的 libcrypto,但非常可惜,经过验证它并没有调用里面的 RSA 相关函数

这里特别感谢大佬 Flamingo 提供的思路,发现其实用的是 libcf 中的 RSA 加解密的函数。

我们用 IDA 看一下 libcf 的导出表,可以看出来其实用的是 Crypto++ 这个开源库



既然是开源的那就很好办了,根据前面的分析我们知道公钥是用二进制的DER格式存储的,只需要Hook BERDecodePublicKey 函数就可以了

用 OpenSSL 生成一对自己的 nde 就和 Navicat 的公钥保持一致就行,然后在动态库中把旧的模数替换掉就行

const char *rsa_n = "00A1E96A057F38361EF91D6B81D4C5592C394565CBF4AD80F51596B8388DC047D9A41356F54F53CFE0CE83ABFE439FC2D1E63821292E512062F043CAEC57EEE33B21C5D8549C6545CD143EFAE79D1FEC6F998B11532E870CED27894652FF6CF80AB354D5C49270D8B20DAF64499BF2A8D7B62C1FEF58F69C005F10098322DA7D6B19DC39C883E171A8BD2ADEF35749856F7F3004B8421DC444494F1764AB99328C87B78FDAF844531F2601EB95E52BD92752F0528E4FE6D1FC1B61FE1F9058E56C271907B56D23B177E270CF16EF74E9E0165C82AD564D9F36B5B62A0E0DCA54043301B9633BF265135F7A1A4FDFEE0E08DB7C212DA764EEB1EBF1F188729947EFh";
// 下面两个等会要用
const char *rsa_e = "0x10001";
const char *rsa_d = "00856594D74090A46E725A5DD7D2E0616EC124BF1101DEED2F7F80F4F88B394F392E284CEDC154D1216BA42A514B8FD5E82FA0311A07B20957DA92501ED6F7D7B941430ACFF326B1129CCAA2D7AEA9BD97D8CE2E10F575891DDF0407AF9C0840783875FDC57DCD818B7920F7247A5ABBE7358D3726708A85CEFC836F02DEF55A7C5245AAC8392413BABEA5BE8AA3F4D41128B1525315AD96A9C3D6DB5F6DAA0E6B1A28405FA4E3C81BE8F43EC3A1307891D1F1E259E7EFEEBAFDEC2BB803CD55990C08CA9CAD8ED3566F155E5A55E649B5DD9F46625FDA9479B9B45ACBA9EE2B82BA5DBAABAA97F28363F9171F5717A02848114E4AEF923BA0B9EEDFEE8AA24139h";

extern void BERDecodePublicKey(RSA::PublicKey *self, void *bt, bool param, size_t size)
asm("__ZN8CryptoPP11RSAFunction18BERDecodePublicKeyERNS_22BufferedTransformationEbm");
void (*OrigBERDecodePublicKey)(RSA::PublicKey *self, void *bt, bool param, size_t size);
void MyBERDecodePublicKey(RSA::PublicKey *self, void *bt, bool param, size_t size){
    // 先调用原函数初始化好公钥
    OrigBERDecodePublicKey(self, bt, param, size);
    // 再替换为我们自己的模数
    self->SetModulus(Integer(rsa_n));
    return;
}

这里为了方便Hook,重新写了一下函数的声明,用 asm 来使最终链接到需要Hook的函数地址

原本的函数签名展开后是这样的 CryptoPP::RSAFunction::BERDecodePublicKey(CryptoPP::BufferedTransformation&, bool, unsigned long)

这里用的Inline Hook框架是 tinyhook,在 constructor 函数里加上这样一行就可以了

__attribute__((constructor(0)))
void load() {
    tiny_hook((void *)BERDecodePublicKey, (void *)MyBERDecodePublicKey, (void **)&OrigBERDecodePublicKey);
    return;
}

编译的时候需要引入 cryptopp 的头文件,然后直接链接到 libcf.dylib 就可以找到我们用到的所有函数

然后用 insert_dylib 把编译好的动态库注入到主程序就可以了,我们来启动验证一下

这是上面的 n, e, d 对应的私钥,先保存成private.pem备用

-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAoelqBX84Nh75HWuB1MVZLDlFZcv0rYD1FZa4OI3AR9mkE1b1
T1PP4M6Dq/5Dn8LR5jghKS5RIGLwQ8rsV+7jOyHF2FScZUXNFD76550f7G+ZixFT
LocM7SeJRlL/bPgKs1TVxJJw2LINr2RJm/Ko17YsH+9Y9pwAXxAJgyLafWsZ3DnI
g+FxqL0q3vNXSYVvfzAEuEIdxERJTxdkq5kyjIe3j9r4RFMfJgHrleUr2SdS8FKO
T+bR/Bth/h+QWOVsJxkHtW0jsXficM8W73Tp4BZcgq1WTZ82tbYqDg3KVAQzAblj
O/JlE196Gk/f7g4I23whLadk7rHr8fGIcplH7wIDAQABAoIBAQCFZZTXQJCkbnJa
XdfS4GFuwSS/EQHe7S9/gPT4izlPOS4oTO3BVNEha6QqUUuP1egvoDEaB7IJV9qS
UB7W99e5QUMKz/MmsRKcyqLXrqm9l9jOLhD1dYkd3wQHr5wIQHg4df3Ffc2Bi3kg
9yR6WrvnNY03JnCKhc78g28C3vVafFJFqsg5JBO6vqW+iqP01BEosVJTFa2WqcPW
219tqg5rGihAX6TjyBvo9D7DoTB4kdHx4lnn7+66/ewruAPNVZkMCMqcrY7TVm8V
XlpV5km13Z9GYl/alHm5tFrLqe4rgrpduquql/KDY/kXH1cXoChIEU5K75I7oLnu
3+6KokE5AoGBANRLyXdvDzRmzPzcg6iTQULJ1eOCh5X5x5h5xfGlkajkJyxuvnDM
F8dSAJsfKmjcNcB2tX65HgtdS55vNKL5dIz+/ApFges8reENa65Dt9ODNBPoC4b4
fsAbigAZlwGZLp7z1BcUbNKSeONjHU9CTaXQWwggFQcwgmK+r+rceiM1AoGBAMM+
U3Pz/LYdpvRiYu0kQIiR5S8HSyNRqnlUf9JJs+cl9qgzIhbejlMdeA75dopVBOd9
lJqHJNZAnFhUJl6jUFEkIaSMHSNVsfhrOmiWzHIlh3Ei55vA4f33gJQGRyVAeXLE
3zLNUplu+0R5g0JMozSS+IVpWiYy9NZkrJzPmF8TAoGARoerNje6eHFS1ws33nCV
tOezXLOH8iaazihev+p+2vp5nURplrXnjHvM4bxX7aCDZx7JK4G63pGvRsKxXRe9
Rf6Mo6j2Ab4WEnfP94Rd9TJYwehMtBmompBLp77YsVo/5+Uf6E8L3GV3LixGl4dy
noz7QVbPRaUzHDU34rI/DaUCgYEAl8foEIhouRssI2gpB7nbAVCKHplI7Fgcct4h
0FTDqrp0miXGJok1k5+hKeL9KGUXvu59i/PryzPHV1Nz0LadRbcVAFp8fG+uPzT8
3zn8DfDm7ij4bLjx9wFlz61hua/5uiMacN/1ipogdAcS54O0jLaExRI1puSOOe1h
0zX/ekkCgYEAlpACNpfGmsxJctC3l0XrU0tyWCQRRnq9GVSLbqUdL/UT1VPTAt2h
PFMBwzho0W13lj1ZE0Jz82VL4nuT6YP8dj9OwD/ZWg9FxLojFNTS9jVOTogfVJGT
KwUNq9BTtdxS0oZxYz3IHjEX4Xe5iyTSc5+t4lw+0gsXT4WWPWDvp3k=
-----END RSA PRIVATE KEY-----

断网后输入一个激活码,选择离线激活,把请求码复制出来,先用 base64 解码

$ echo "QClZInZ0LYK..." | base64 -d > request.bin

然后用用 openssl 解密试试

$ openssl pkeyutl -decrypt -inkey private.pem -in request.bin
{"K":"NAVPE4AMHQYM4SVI", "DI":"271E3B24BEXXXXXXXXXX", "P":"MAC"}

可以解密就说明我们替换公钥成功了,来试试生成对应的激活码吧

给这个JSON加上 N, O, T 字段,保存到 response.txt 里

$ echo '{"K":"NAVPE4AMHQYM4SVI", "DI":"271E3B24BEXXXXXXXXXX", "P":"MAC", "N":"skrets", "O":"52pojie.cn", "T":1761408000}' > response.txt

再使用 openssl 加密,注意这里要用 -sign 来指定用私钥加密

$ openssl pkeyutl -sign -inkey private.pem -in response.txt | base64
es7ywFxtex1cQoHl....

输出的就是我们需要的离线激活码了,拿去试试



ok,成功激活

这里没有涉及到写死的地址,通过导出的符号表进行Hook,所以理论上可以通杀后续的版本

那么,这就结束了吗?并非,手动弄这么一大通未免太麻烦了一点。

既然 Navicat 已经给我们提供了 RSA 的函数,那我们直接在动态库里调用计算好不就行了

自动计算离线激活码

那我们首先需要拿到请求码里面的JSON数据,好办,只要Hook公钥加密的函数就好了

分析一下就能知道它用的是这个函数 CryptoPP::TF_EncryptorBase::Encrypt(CryptoPP::RandomNumberGenerator&, unsigned char const*, unsigned long, unsigned char*, CryptoPP::NameValuePairs const&) const

extern void TF_EncryptorEncrypt(void *self, void *rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, void *param)
asm("__ZNK8CryptoPP16TF_EncryptorBase7EncryptERNS_21RandomNumberGeneratorEPKhmPhRKNS_14NameValuePairsE");
void (*OrigTF_EncryptorEncrypt)(void *self, void *rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, void *param);
void MyTF_EncryptorEncrypt(void *self, void *rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, void *param) {
    if (plaintext[0] == '{') {
        // 计算好离线激活码
        SaveActivationCode(plaintext, plaintextLength);
    }
    return OrigTF_EncryptorEncrypt(self, rng, plaintext, plaintextLength, ciphertext, param);
}

SaveActivationCode 的具体实现如下,基本差不多,先拼接,再进行加密

比较坑的是 Crypto++ 并没有直接用私钥加密的函数,要把数据转换成 Integer 进行RSA底层运算

并且要手动进行 PKSC #1 填充,我开始忘记了导致一直激活失败

NSString *activation_code;

static void SaveActivationCode(const byte *request, size_t requestLength) {
    // 拼接出完整注册信息JSON数据
    string reg_info = ", \"N\":\"skrets\", \"O\":\"52pojie.cn\", \"T\":1761408000}";
    size_t responseLength = requestLength + reg_info.length() - 1;
    byte *response = new byte[responseLength];
    memcpy(response, request, requestLength - 1);
    memcpy(response + requestLength - 1, reg_info.data(), reg_info.length());

    // PKCS #1 Padding
    byte *padded = new byte[256]; // 2048-bit
    padded[0] = byte{0};
    padded[1] = byte{1};
    memset(padded + 2, 0xff, 256 - 3 - responseLength);
    padded[256 - responseLength - 1] = byte{0};
    memcpy(padded + 256 - responseLength, response, responseLength);
    delete[] response;

    // 初始化RSA私钥以及随机数生成器
    AutoSeededRandomPool prng;
    RSA::PrivateKey priv_key;
    priv_key.Initialize(Integer(rsa_n), Integer(rsa_e), Integer(rsa_d));

    // 进行底层的RSA模幂运算
    Integer m(padded, 256);
    Integer c = priv_key.CalculateInverse(prng, m);
    byte *result = new byte[256];
    c.Encode(result, 256);
    delete[] padded;

    // Base64编码
    NSData *res_data = [NSData dataWithBytes:result length:256];
    activation_code = [res_data base64EncodedStringWithOptions:0];
    delete[] result;
}

算好的激活码怎么用呢,我心有一计

自动填入离线激活码

我们用 Hopper 来分析一下主程序,根据联网失败的弹窗来搜索一下这个字符串

Your license could not be activated because the activation server is temporarily unavailable. Try manual activation, or try again later.

可以看到有两个引用,我们看看来自 -[RegistrationSubscriptionWindowController activate] 的引用

    else {
            r0 = [NSBundle mainBundle];
            r0 = [r0 retain];
            r23 = r0;
            r24 = [[r0 localizedStringForKey:@"Your license could not be activated because the activation server is temporarily unavailable. Try manual activation, or try again later." value:@"" table:0x0] retain];
            r0 = [NSBundle mainBundle];
            r0 = [r0 retain];
            r20 = r0;
            r21 = [[r0 localizedStringForKey:@"If this problem persists, contact Navicat Support at https://help.navicat.com/hc/en-us/requests/new." value:@"" table:0x0] retain];
            r22 = [[NSString stringWithFormat:@"%@\n%@", r3, r4] retain];
            [r21 release];
            [r20 release];
            [r24 release];
            [r23 release];
            r0 = [NSBundle mainBundle];
            r0 = [r0 retain];
            r23 = r0;
            r24 = [[r0 localizedStringForKey:@"Activation Failed" value:@"" table:0x0] retain];
            r9 = decomp_var_118;
            r8 = &decomp_var_118;
            if (sign_extend_64(decomp_var_101) >= 0x0) {
                    r2 = r8;
            }
            else {
                    r2 = r9;
            }
            r25 = [[NSString stringWithUTF8String:r2] retain];
            [[NSString stringWithFormat:@"%@\n%@", r3, r4] retain];
            r28 = [[[[NSBundle mainBundle] retain] localizedStringForKey:@"OK" value:@"" table:0x0] retain];
            r0 = [NSBundle mainBundle];
            r0 = [r0 retain];
            r20 = r0;
            r21 = [[r0 localizedStringForKey:@"Manual Activation" value:@"" table:0x0] retain];
            r26 = [[NSAlert alertWithQuestion:r24 details:decomp_var_168 firstButtonTitle:r28 secondButtonTitle:r21] retain];
            [decomp_var_158 release];
            [r21 release];
            [r20 release];
            [r28 release];
            [decomp_var_170 release];
            [decomp_var_168 release];
    }

可以看到这里就是创建了一个带有两个按钮的弹窗,其中一个就是手动激活的

那我们往下翻翻,应该就可以找到手动激活的窗口所在位置了

    if (r20 == 0x3e9) {
            r23 = [ManualActivationWindowController alloc];
            r20 = [[NSValue valueWithPointer:decomp_var_160] retain];
            r24 = [[r19->_registrationViewController inputtedKey] retain];
            r21 = [r23 initWithDialogInfoValue:r20 key:r24];
            [r24 release];
            [r20 release];
            [r21 retainUntilWindowClosed];
            r0 = [r21 window];
            r0 = [r0 retain];
            r20 = r0;
            [r0 makeKeyAndOrderFront:r21];
            [r20 release];
            r23 = **_NSApp;
            r20 = [[r21 window] retain];
            r23 = [r23 runModalForWindow:r20];
            [r20 release];
            if (r23 == 0x1) {
                    [r19 showPerpectualInfoView];
            }
            [r21 release];
    }

到这里就很显然了,ManualActivationWindowController 就是我们要找的,去看看这个类有什么成员吧

@Class ManualActivationWindowController : NSWindowController {
    ivar _requestCodeTextField
    ivar _activationCodeTextField
    ivar _dialogInfoValue
    ivar _key
    -windowDidLoad
    -windowShouldClose:
    -initWithDialogInfoValue:key:
    -activate:
    -cancel:
    -.cxx_destruct
}

一目了然啊,这个 _activationCodeTextField 就是我们想要的,这是一个 NSTextField 对象,我们可以直接设置它的内容

那现在就是要找一个合适的时机,通过Hook拿到这个 _activationCodeTextField 并写入我们的激活码

看回上面的伪代码,通过调试我们可以知道,请求码在 retainUntilWindowClosed 的时候会被算好,也就意味着我们的激活码也算好了,再往下看看,目标就很明确了

            [r21 retainUntilWindowClosed];
            r0 = [r21 window];
            r0 = [r0 retain];
            r20 = r0;
            [r0 makeKeyAndOrderFront:r21]; // 就是你了!

函数原型是这样的 - (void) makeKeyAndOrderFront:(id) sender;

这个 sender 就是我们要的 ManualActivationWindowController,那么利用 ObjC 的运行时机制,Hook就很好写了

void (*makeKeyAndOrderFront)(id cls, SEL sel, id sender);
void my_makeKeyAndOrderFront(id cls, SEL sel, id sender) {
    if (strcmp(object_getClassName(sender), "ManualActivationWindowController") == 0) {
        // 往文本框里填入之前算好的激活码
        Ivar ivar = class_getInstanceVariable(object_getClass(sender), "_activationCodeTextField");
        NSTextField *code_field = object_getIvar(sender, ivar);
        [code_field setStringValue:activation_code];
    }
    return makeKeyAndOrderFront(cls, sel, sender);
}

这里就没有定义自己的 ObjC 方法然后再 exchange 了,直接用 setImplementation 即可

#define HOOK_INSTANCEM(cls, sel, imp) method_setImplementation(class_getInstanceMethod(objc_getClass(cls), sel_registerName(sel)),(IMP)imp)

makeKeyAndOrderFront = (void (*)(id, SEL, id))
                       HOOK_INSTANCEM("NSWindow", "makeKeyAndOrderFront:", my_makeKeyAndOrderFront);

那我们来注入试试吧



可以看到弹出的窗口已经是填入了有效激活码的,直接点Activate就可以了

都已经到这一步了,那顺便把网络屏蔽也在注入库里实现好了

屏蔽联网验证

用 Proxyman 抓包可以看到请求激活的域名是 activate.navicat.com,但非常不幸的是二进制里搜索不到这个字符串



不过注意到上面的 UA 里面有 CFNetwork,说明它用的是系统提供的网络请求框架,那么大概率就是 NSURLSession

我们可以在 lldb 中下断点验证,确实用的是 NSURLSession,并且调用了 -[NSURLRequest initWithURL:] 来初始化

那我们就直接拦截这个函数,把请求改掉就好了,非常简单

id (*initWithURL)(id cls, SEL sel, NSURL *URL);
id my_initWithURL(id cls, SEL sel, NSURL *URL) {
    if ([URL.host isEqualToString:@"activate.navicat.com"]) {
        // 改成主机环回地址使请求失败
        NSURL *newURL = [NSURL URLWithString:@"http://127.0.0.1"];
        return initWithURL(cls, sel, newURL);
    }
    return initWithURL(cls, sel, URL);
}

结语

那么到这里本文就差不多结束了

通过Hook导出表函数以及ObjC运行时实现了理论通杀,并且简化了很多步骤

现在你不需要屏蔽联网,也不用拿私钥加密解密,只需要

  1. 把动态库注入到主程序中
  2. 填一个激活码选择离线激活
  3. Enjoy!

这里放几个生成的激活码

cs
NAVN-BB6P-AN5N-TSJR
NAVJ-NVYH-36YH-FXO5
NAVI-DWA4-Q6GJ-QIXC
en
NAVJ-37MI-MXVH-BP2X
NAVE-CWN6-HDXB-O74J
NAVB-HHYW-5BCQ-3ARW

以及完整的注入库代码

#include "tinyhook.h"
#include "cryptopp/rsa.h"
#include "cryptopp/osrng.h"

#include <string>
#include <objc/runtime.h>

#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>

#define HOOK_INSTANCEM(cls, sel, imp) method_setImplementation(class_getInstanceMethod(objc_getClass(cls), sel_registerName(sel)),(IMP)imp)
#define HOOK_CLASSM(cls, sel, imp) method_setImplementation(class_getClassMethod(objc_getClass(cls), sel_registerName(sel)),(IMP)imp)

using namespace std;
using namespace CryptoPP;

const char *rsa_n = "00A1E96A057F38361EF91D6B81D4C5592C394565CBF4AD80F51596B8388DC047D9A41356F54F53CFE0CE83ABFE439FC2D1E63821292E512062F043CAEC57EEE33B21C5D8549C6545CD143EFAE79D1FEC6F998B11532E870CED27894652FF6CF80AB354D5C49270D8B20DAF64499BF2A8D7B62C1FEF58F69C005F10098322DA7D6B19DC39C883E171A8BD2ADEF35749856F7F3004B8421DC444494F1764AB99328C87B78FDAF844531F2601EB95E52BD92752F0528E4FE6D1FC1B61FE1F9058E56C271907B56D23B177E270CF16EF74E9E0165C82AD564D9F36B5B62A0E0DCA54043301B9633BF265135F7A1A4FDFEE0E08DB7C212DA764EEB1EBF1F188729947EFh";
const char *rsa_e = "0x10001";
const char *rsa_d = "00856594D74090A46E725A5DD7D2E0616EC124BF1101DEED2F7F80F4F88B394F392E284CEDC154D1216BA42A514B8FD5E82FA0311A07B20957DA92501ED6F7D7B941430ACFF326B1129CCAA2D7AEA9BD97D8CE2E10F575891DDF0407AF9C0840783875FDC57DCD818B7920F7247A5ABBE7358D3726708A85CEFC836F02DEF55A7C5245AAC8392413BABEA5BE8AA3F4D41128B1525315AD96A9C3D6DB5F6DAA0E6B1A28405FA4E3C81BE8F43EC3A1307891D1F1E259E7EFEEBAFDEC2BB803CD55990C08CA9CAD8ED3566F155E5A55E649B5DD9F46625FDA9479B9B45ACBA9EE2B82BA5DBAABAA97F28363F9171F5717A02848114E4AEF923BA0B9EEDFEE8AA24139h";

extern void BERDecodePublicKey(RSA::PublicKey *self, void *bt, bool param, size_t size) asm("__ZN8CryptoPP11RSAFunction18BERDecodePublicKeyERNS_22BufferedTransformationEbm");
void (*OrigBERDecodePublicKey)(RSA::PublicKey *self, void *bt, bool param, size_t size);
void MyBERDecodePublicKey(RSA::PublicKey *self, void *bt, bool param, size_t size){
    OrigBERDecodePublicKey(self, bt, param, size);
    // 替换为新的模数
    self->SetModulus(Integer(rsa_n));
    return;
}

NSString *activation_code;

static void SaveActivationCode(const byte *request, size_t requestLength) {
    // 拼接出完整注册信息JSON数据
    string reg_info = ", \"N\":\"skrets\", \"O\":\"52pojie.cn\", \"T\":1761408000}";
    size_t responseLength = requestLength + reg_info.length() - 1;
    byte *response = new byte[responseLength];
    memcpy(response, request, requestLength - 1);
    memcpy(response + requestLength - 1, reg_info.data(), reg_info.length());

    // PKCS #1 Padding
    byte *padded = new byte[256]; // 2048-bit
    padded[0] = byte{0};
    padded[1] = byte{1};
    memset(padded + 2, 0xff, 256 - 3 - responseLength);
    padded[256 - responseLength - 1] = byte{0};
    memcpy(padded + 256 - responseLength, response, responseLength);
    delete[] response;

    // 初始化RSA私钥以及随机数生成器
    AutoSeededRandomPool prng;
    RSA::PrivateKey priv_key;
    priv_key.Initialize(Integer(rsa_n), Integer(rsa_e), Integer(rsa_d));

    // 进行底层的RSA模幂运算
    Integer m(padded, 256);
    Integer c = priv_key.CalculateInverse(prng, m);
    byte *result = new byte[256];
    c.Encode(result, 256);
    delete[] padded;

    // Base64编码
    NSData *res_data = [NSData dataWithBytes:result length:256];
    activation_code = [res_data base64EncodedStringWithOptions:0];
    delete[] result;
}

extern void TF_EncryptorEncrypt(void *self, void *rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, void *param) asm("__ZNK8CryptoPP16TF_EncryptorBase7EncryptERNS_21RandomNumberGeneratorEPKhmPhRKNS_14NameValuePairsE");
void (*OrigTF_EncryptorEncrypt)(void *self, void *rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, void *param);
void MyTF_EncryptorEncrypt(void *self, void *rng, const byte *plaintext, size_t plaintextLength, byte *ciphertext, void *param) {
    if (plaintext[0] == '{') {
        // 计算好离线激活码
        SaveActivationCode(plaintext, plaintextLength);
    }
    return OrigTF_EncryptorEncrypt(self, rng, plaintext, plaintextLength, ciphertext, param);
}

void (*makeKeyAndOrderFront)(id cls, SEL sel, id sender);
void my_makeKeyAndOrderFront(id cls, SEL sel, id sender) {
    if (strcmp(object_getClassName(sender), "ManualActivationWindowController") == 0) {
        // 往文本框里填入之前算好的激活码
        Ivar ivar = class_getInstanceVariable(object_getClass(sender), "_activationCodeTextField");
        NSTextField *code_field = object_getIvar(sender, ivar);
        [code_field setStringValue:activation_code];
    }
    return makeKeyAndOrderFront(cls, sel, sender);
}

id (*initWithURL)(id cls, SEL sel, NSURL *URL);
id my_initWithURL(id cls, SEL sel, NSURL *URL) {
    if ([URL.host isEqualToString:@"activate.navicat.com"]) {
        // 改成主机环回地址使请求失败
        NSURL *newURL = [NSURL URLWithString:@"http://127.0.0.1"];
        return initWithURL(cls, sel, newURL);
    }
    return initWithURL(cls, sel, URL);
}

__attribute__((constructor(0)))
void load() {
    // Inline Hook
    tiny_hook((void *)BERDecodePublicKey, (void *)MyBERDecodePublicKey, (void **)&OrigBERDecodePublicKey);
    tiny_hook((void *)TF_EncryptorEncrypt, (void *)MyTF_EncryptorEncrypt, (void **)&OrigTF_EncryptorEncrypt);
    // ObjC Runtime Hook
    makeKeyAndOrderFront = (void (*)(id, SEL, id))
                           HOOK_INSTANCEM("NSWindow", "makeKeyAndOrderFront:", my_makeKeyAndOrderFront);
    initWithURL = (id (*)(id, SEL, NSURL *))
                  HOOK_INSTANCEM("NSURLRequest", "initWithURL:", my_initWithURL);
    return;
}

编译好的注入库我就放附件里了

以防有人不知道怎么用 insert_dylib

$ insert_dylib --inplace --all-yes @rpath/libNavicat.dylib 'Navicat Premium.app/Contents/MacOS/Navicat Premium'
$ codesign -f -s - 'Navicat Premium.app/Contents/MacOS/Navicat Premium'

注意别忘了重新签名,然后把 libNavicat.dylib 放在 Navicat Premium.app/Contents/Frameworks

如果遇到了损坏无法打开的弹窗,这是 quarantine 属性导致的,移除即可

$ xattr -cr 'Navicat Premium.app'


文中用到的软件

  • IDA
  • Hopper
  • Proxyman

文中用到的代码/工具

特别感谢 @iwolf 大佬的原始分析,以及 Flamingo 大佬提供的替换公钥思路

本帖仅作技术交流,请支持并使用正版软件!


libNavicat.zip

17.28 KB, 下载次数: 211, 下载积分: 吾爱币 -1 CB

Navicat的注入库

免费评分

参与人数 57威望 +2 吾爱币 +158 热心值 +45 收起 理由
白衣剑客 + 1 + 1 谢谢@Thanks!
sszqtm + 1 谢谢@Thanks!
nnzhs + 1 + 1 谢谢@Thanks!
imyuyu + 1 我很赞同!
wanghlstar + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Zapomn + 1 + 1 谢谢@Thanks!学习了
AguaA + 1 + 1
冷色的咖啡 + 1 + 1 果真成功了,非常 nice
xwklib + 1 + 1 谢谢@Thanks!
fuzball + 1 + 1 感谢bian96删除我的回复帖
Ruomeng + 1 用心讨论,共获提升!
SPCETRE + 1 我很赞同!
sblpp + 1 谢谢@Thanks!
wzxkk123 + 3 + 1 用心讨论,共获提升!
taoyangui + 1 + 1 用心讨论,共获提升!
J4ckacc + 1 我很赞同!
jiayouzl + 1 用是可以用,但是无法保存链接数据库的密码的。
H7ang0 + 1 谢谢@Thanks!
peng214 + 1 谢谢@Thanks!
soldiergp + 1 + 1 太厉害了,学习学习
eec + 2 + 1 用心讨论,共获提升!
litcc + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Lsygood + 1 + 1 用心讨论,共获提升!
junjia215 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
红叶落尽 + 1 + 1 谢谢@Thanks!
airzen + 1 + 1 谢谢@Thanks!
eric + 1 + 1 谢谢@Thanks!
lin5789 + 1 我很赞同!
梦旅意中人 + 1 + 1 谢谢@Thanks!
moongeer + 1 用心讨论,共获提升!
yixi + 1 + 1 谢谢@Thanks!
q25l + 1 + 1 谢谢@Thanks!
wcu1117 + 1 我很赞同!
ioyr5995 + 1 + 1 用心讨论,共获提升!
dear7575 + 1 我很赞同!
江南小虫虫 + 1 + 1 谢谢@Thanks!
814182193 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
doosit + 2 + 1 谢谢@Thanks!
gpchhao + 1 + 1 谢谢@Thanks!
pedoc + 1 谢谢@Thanks!
fiscivaj + 1 热心回复!
thunder8848 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ogli324 + 1 谢谢@Thanks!
hoon + 1 谢谢@Thanks!
smilerfu + 1 + 1 谢谢@Thanks!
Akisa + 1 + 1 谢谢@Thanks!
syj224 + 1 + 1 谢谢@Thanks!
Coolman + 1 + 1 用心讨论,共获提升!
lndis + 1 + 1 谢谢@Thanks!
daxz + 1 + 1 谢谢@Thanks!
jgs + 1 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
whereismy + 1 谢谢@Thanks!
tzxinqing + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
smile1110 + 3 + 1 6666
gjianbo + 2 + 1 谢谢@Thanks!
云在天 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
Vvvvvoid 发表于 2025-10-8 19:49
mark
有空操作一下
推荐
xwklib 发表于 2025-12-12 13:58

牛!成功了!总结一下我的步骤:

  1. git clone https://github.com/tyilo/insert_dylib仓库,然后在项目根目录运行xcodebuild

    这里因为我之前没有下载Xcode,所以编译失败,下载了Xcode之后运行xcode-select -p发现输出是 /Library/Developer/CommandLineTools,就说明它指向了命令行工具,而不是Xcode,所以需要运行sudo xcode-select -s /Applications/Xcode.app/Contents/Developer。再次运行 xcode-select -p,现在输出应该变为 /Applications/Xcode.app/Contents/Developer

  2. 用编译得到的build/Release/insert_dylib运行insert_dylib --inplace --all-yes /你的目录/libNavicat.dylib /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium
  3. 运行codesign -f -s - /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium
  4. 将libNavicat.dylib放到/Applications/Navicat Premium.app/Contents/Frameworks
  5. 启动Navicat后用作者给的激活码离线激活即可。

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
inkculture + 1 + 1 谢谢@Thanks!

查看全部评分

3#
syj224 发表于 2025-10-9 08:48
我问一下 这个方式破解的话  是不是 所有功能 都可以用了
4#
msmvc 发表于 2025-10-9 09:23
这个思路真不错,非侵入PJ
5#
vitoinch 发表于 2025-10-9 09:28
思路清晰,后面版本还能一直这么玩。
6#
zhy1992 发表于 2025-10-9 10:25
楼主 这个必须使用C吗
7#
 楼主| skrets 发表于 2025-10-9 13:00 |楼主
zhy1992 发表于 2025-10-9 10:25
楼主 这个必须使用C吗

Crypto++是cpp的库,我还用到了一些objc的函数
最后的源码应该保存为.mm(objc++)文件
8#
Sen 发表于 2025-10-9 14:56
牛!成功了, M1 需要用用 clang 手动编译下, 然后再 cp,
[C] 纯文本查看 复制代码
clang main.c -o insert_dylib
9#
Y431970085 发表于 2025-10-9 18:33
Sen 发表于 2025-10-9 14:56
牛!成功了, M1 需要用用 clang 手动编译下, 然后再 cp,[mw_shl_code=c,true]clang main.c -o insert_dy ...

大佬,可以跟你学习逆向吗?
10#
武之舞 发表于 2025-10-11 09:35
弱弱的问一下,偷偷使用navicat破解版,被navicat公司检测到,向公司发了律师函怎么破?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-1-16 10:14

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表