6 Commits
Author SHA1 Message Date
Double Sine 432e647fe1 Add support for 12.0.27 2018-04-26 15:51:26 +08:00
Double Sine 618190dde3 Fix bug 2018-03-31 15:49:25 +08:00
Double Sine e1f5900770 we don't use fixed patch any more 2018-03-30 17:29:40 +08:00
Double Sine a15d77bfc9 Update README 2018-03-30 11:42:31 +08:00
Double Sine 93f355e604 Update for README 2018-03-29 15:24:58 +08:00
Double Sine 8f0986e00e Add support for 12.0.26 2018-03-29 14:26:16 +08:00
8 changed files with 585 additions and 112 deletions
+110 -9
View File
@@ -22,6 +22,92 @@
If you have the corresponding private key, please tell me. I would be very appreciated for your generous.
__NOTICE:__
Start from __Navicat Premium 12.0.25__, Navicat do not load this public key from resource in `navicat.exe`. Actually the public key is stored in `libcc.dll` and encrypted. And to avoid being replaced easily, the public key is split into 5 parts:
The content below is discovered from `libcc.dll` of Navicat Premium x64 12.0.25 Simplified Chinese version. SHA256 value is `607e0a84c75966b00f3d12fa833e91d159e4f51ac51b6ba66f98d0c3cbefdce0`. I do not guaranteed that __offset__ value is absolutely correct in other versions. But __char string__ and __immediate values__ is highly possible to be found.
1. At file offset `+ 0x1A12090` in `libcc.dll`, stored as __char string__:
> "D75125B70767B94145B47C1CB3C0755E
> 7CCB8825C5DCE0C58ACF944E08280140
> 9A02472FAFFD1CD77864BB821AE36766
> FEEDE6A24F12662954168BFA314BD950
> 32B9D82445355ED7BC0B880887D650F5"
2. At file offset `+ 0x59D799` in `libcc.dll`, stored as __immediate value__ in a instruction:
> 0xFE 0xEA 0xBC 0x01
In decimal: `29158142`
3. At file offset `+ 0x1A11DA0` in `libcc.dll`, stored as __char string__:
> "E1CED09B9C2186BF71A70C0FE2F1E0AE
> F3BD6B75277AAB20DFAF3D110F75912B
> FB63AC50EC4C48689D1502715243A79F
> 39FF2DE2BF15CE438FF885745ED54573
> 850E8A9F40EE2FF505EB7476F95ADB78
> 3B28CA374FAC4632892AB82FB3BF4715
> FCFE6E82D03731FC3762B6AAC3DF1C3B
> C646FE9CD3C62663A97EE72DB932A301
> 312B4A7633100C8CC357262C39A2B3A6
> 4B224F5276D5EDBDF0804DC3AC4B8351
> 62BB1969EAEBADC43D2511D6E0239287
> 81B167A48273B953378D3D2080CC0677
> 7E8A2364F0234B81064C5C739A8DA28D
> C5889072BF37685CBC94C2D31D0179AD
> 86D8E3AA8090D4F0B281BE37E0143746
> E6049CCC06899401264FA471C016A96C
> 79815B55BBC26B43052609D9D175FBCD
> E455392F10E51EC162F51CF732E6BB39
> 1F56BBFD8D957DF3D4C55B71CEFD54B1
> 9C16D458757373E698D7E693A8FC3981
> 5A8BF03BA05EA8C8778D38F9873D62B4
> 460F41ACF997C30E7C3AF025FA171B5F
> 5AD4D6B15E95C27F6B35AD61875E5505
> 449B4E"
4. At file offset `+ 0x59D77F` in `libcc.dll`, stored as __immediate value__ in a instruction:
> 0x59 0x08 0x01 0x00 (in decimal )
In decimal: `67673`
5. At file offset `+ 0x1A11D8C` in `libcc.dll`, stored as __char string__:
> "92933"
Then output encrypted public key with format `"%s%d%s%d%s"`, the order is the same as it list:
> D75125B70767B94145B47C1CB3C0755E7CCB8825C5DCE0C58ACF944E082801409A02472FAFFD1CD77864BB821AE36766FEEDE6A24F12662954168BFA314BD95032B9D82445355ED7BC0B880887D650F529158142E1CED09B9C2186BF71A70C0FE2F1E0AEF3BD6B75277AAB20DFAF3D110F75912BFB63AC50EC4C48689D1502715243A79F39FF2DE2BF15CE438FF885745ED54573850E8A9F40EE2FF505EB7476F95ADB783B28CA374FAC4632892AB82FB3BF4715FCFE6E82D03731FC3762B6AAC3DF1C3BC646FE9CD3C62663A97EE72DB932A301312B4A7633100C8CC357262C39A2B3A64B224F5276D5EDBDF0804DC3AC4B835162BB1969EAEBADC43D2511D6E023928781B167A48273B953378D3D2080CC06777E8A2364F0234B81064C5C739A8DA28DC5889072BF37685CBC94C2D31D0179AD86D8E3AA8090D4F0B281BE37E0143746E6049CCC06899401264FA471C016A96C79815B55BBC26B43052609D9D175FBCDE455392F10E51EC162F51CF732E6BB391F56BBFD8D957DF3D4C55B71CEFD54B19C16D458757373E698D7E693A8FC39815A8BF03BA05EA8C8778D38F9873D62B4460F41ACF997C30E7C3AF025FA171B5F5AD4D6B15E95C27F6B35AD61875E5505449B4E6767392933
This encrypted public key can be decrypted by my another repo: [how-does-navicat-encrypt-password](https://github.com/DoubleLabyrinth/how-does-navicat-encrypt-password), while the key used is `b'23970790'`
Example:
```cmd
E:\GitHub>git clone https://github.com/DoubleLabyrinth/how-does-navicat-encrypt-password.git
...
E:\GitHub>cd how-does-navicat-encrypt-password\python3
E:\GitHub\how-does-navicat-encrypt-password\python3>python
Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from NavicatCrypto import *
>>> cipher = Navicat11Crypto(b'23970790')
>>> print(cipher.DecryptString('D75125B70767B94145B47C1CB3C0755E7CCB8825C5DCE0C58ACF944E082801409A02472FAFFD1CD77864BB821AE36766FEEDE6A24F12662954168BFA314BD95032B9D82445355ED7BC0B880887D650F529158142E1CED09B9C2186BF71A70C0FE2F1E0AEF3BD6B75277AAB20DFAF3D110F75912BFB63AC50EC4C48689D1502715243A79F39FF2DE2BF15CE438FF885745ED54573850E8A9F40EE2FF505EB7476F95ADB783B28CA374FAC4632892AB82FB3BF4715FCFE6E82D03731FC3762B6AAC3DF1C3BC646FE9CD3C62663A97EE72DB932A301312B4A7633100C8CC357262C39A2B3A64B224F5276D5EDBDF0804DC3AC4B835162BB1969EAEBADC43D2511D6E023928781B167A48273B953378D3D2080CC06777E8A2364F0234B81064C5C739A8DA28DC5889072BF37685CBC94C2D31D0179AD86D8E3AA8090D4F0B281BE37E0143746E6049CCC06899401264FA471C016A96C79815B55BBC26B43052609D9D175FBCDE455392F10E51EC162F51CF732E6BB391F56BBFD8D957DF3D4C55B71CEFD54B19C16D458757373E698D7E693A8FC39815A8BF03BA05EA8C8778D38F9873D62B4460F41ACF997C30E7C3AF025FA171B5F5AD4D6B15E95C27F6B35AD61875E5505449B4E6767392933'))
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
awIDAQAB
-----END PUBLIC KEY-----
```
* __Request Code__
It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the cipher text of the __offline activation information__ encrypted by __Navicat Activation Public Key__.
@@ -166,20 +252,35 @@
5. Input __Activation Code__, then offline activation is done.
## 4. How to use
1. Build patcher and keygen.
1. Build patcher and keygen in Release configuration or download the latest release.
2. Replace __Navicat Activation Public Key__ in `navicat.exe`.
2. Replace __Navicat Activation Public Key__ in `navicat.exe` or `libcc.dll`.
Example:
```bash
E:\GitHub\navicat-keygen\x64\Release>navicat-patcher.exe "D:\Program Files\PremiumSoft\Navicat Premium 12\navicat.exe"
D:\Program Files\PremiumSoft\Navicat Premium 12\navicat.exe has been backed up.
Public key has been replaced.
Success!
* For Navicat Premium version < 12.0.25
```bash
E:\GitHub\navicat-keygen\x64\Release>navicat-patcher.exe "D:\Program Files\PremiumSoft\Navicat Premium 12"
D:\Program Files\PremiumSoft\Navicat Premium 12\navicat.exe has been backed up.
Public key has been replaced.
Success!
```
```
* For Navicat Premium version >= 12.0.25
```bash
E:\GitHub\navicat-keygen\x64\Release>navicat-patcher.exe "D:\Program Files\PremiumSoft\Navicat Premium 12"
D:\Program Files\PremiumSoft\Navicat Premium 12\libcc.dll has been backed up.
Public key has been replaced.
Success!
```
You may wait for a few seconds or even longer, because patcher is search for a appropriate RSA key. Finally you will get `RegPrivateKey.pem` file at current directory.
You will get `RegPrivateKey.pem` file at current directory.
If you do not want to search, use `RegPrivateKey.pem` in my latest release, then:
```bash
E:\GitHub\navicat-keygen\x64\Release>navicat-patcher.exe "D:\Program Files\PremiumSoft\Navicat Premium 12" RegPrivateKey.pem
D:\Program Files\PremiumSoft\Navicat Premium 12\libcc.dll has been backed up.
Public key has been replaced.
Success!
```
3. Then in console:
+156 -58
View File
@@ -1,4 +1,4 @@
#include <tchar.h>
#include <tchar.h>
#include <windows.h>
#include <wincrypt.h>
@@ -22,13 +22,26 @@
#else
#pragma comment(lib, "libcryptoMT.lib")
#endif
#pragma comment(lib, "WS2_32.lib") // some symbol are used in OpenSSL lib
#pragma comment(lib, "Crypt32.lib") // some symbol are used in OpenSSL lib
#pragma comment(lib, "WS2_32.lib") // some symbol are used in OpenSSL static lib
#pragma comment(lib, "Crypt32.lib") // some symbol are used in OpenSSL static lib
#define NAVICAT_12
#define NAVICAT_CHS
void GenerateSnKey(char(&SnKey)[16]) {
enum NavicatLanguage {
English,
SimplifiedChinese,
TraditionalChinese,
Japanese,
Polish,
Spanish,
French,
German,
Korean,
Russian,
Portuguese
};
void GenerateSnKey(char(&SnKey)[16], NavicatLanguage _language) {
static char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
static DES_cblock DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
@@ -37,42 +50,54 @@ void GenerateSnKey(char(&SnKey)[16]) {
temp_SnKey[3] = rand();
temp_SnKey[4] = rand();
#if defined(NAVICAT_ENG)
temp_SnKey[5] = 0xAC; // Must be 0xAC for English version.
temp_SnKey[6] = 0x88; // Must be 0x88 for English version.
#elif defined(NAVICAT_CHS)
temp_SnKey[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
temp_SnKey[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
#elif defined(NAVICAT_CHT)
temp_SnKey[5] = 0xAA; // Must be 0xAA for Traditional Chinese version.
temp_SnKey[6] = 0x99; // Must be 0x99 for Traditional Chinese version.
#elif defined(NAVICAT_JAP)
temp_SnKey[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee
#elif defined(NAVICAT_POL)
temp_SnKey[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee
#elif defined(NAVICAT_SPA)
temp_SnKey[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee
#elif defined(NAVICAT_FRE)
temp_SnKey[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79
temp_SnKey[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79
#elif defined(NAVICAT_GER)
temp_SnKey[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee
#elif defined(NAVICAT_KOR)
temp_SnKey[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee
#elif defined(NAVICAT_RUS)
temp_SnKey[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee
#elif defined(NAVICAT_POR)
temp_SnKey[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee
#else
#error "Navicat product type is not specified."
#endif
switch (_language) {
case English:
temp_SnKey[5] = 0xAC; // Must be 0xAC for English version.
temp_SnKey[6] = 0x88; // Must be 0x88 for English version.
break;
case SimplifiedChinese:
temp_SnKey[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
temp_SnKey[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
break;
case TraditionalChinese:
temp_SnKey[5] = 0xAA; // Must be 0xAA for Traditional Chinese version.
temp_SnKey[6] = 0x99; // Must be 0x99 for Traditional Chinese version.
break;
case Japanese:
temp_SnKey[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee
break;
case Polish:
temp_SnKey[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee
break;
case Spanish:
temp_SnKey[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee
break;
case French:
temp_SnKey[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79
temp_SnKey[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79
break;
case German:
temp_SnKey[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee
break;
case Korean:
temp_SnKey[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee
break;
case Russian:
temp_SnKey[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee
break;
case Portuguese:
temp_SnKey[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee
temp_SnKey[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee
break;
default:
break;
}
#if defined(NAVICAT_12)
temp_SnKey[7] = 0x65; // 0x65 - commercial, 0x66 - non-commercial
@@ -173,6 +198,59 @@ BOOL GenerateLicense(RSA* RSAPrivateKey,
#endif
}
RSA* ReadRSAPrivateKeyFromFile(LPCTSTR filename) {
#ifdef UNICODE
int req_size = WideCharToMultiByte(CP_ACP, 0, filename, -1, nullptr, 0, nullptr, nullptr);
if (req_size == 0) {
_tprintf_s(TEXT("Failed to convert wchar* to char*. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> WideCharToMultiByte]\r\n"), GetLastError());
return FALSE;
}
char* temp_filename = new char[req_size]();
WideCharToMultiByte(CP_ACP, 0, filename, -1, temp_filename, req_size, nullptr, nullptr);
BIO* b = BIO_new(BIO_s_file());
if (b == nullptr) {
_tprintf_s(TEXT("Failed to create BIO object. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_new]\r\n"), ERR_get_error());
delete[] temp_filename;
return FALSE;
}
if (1 != BIO_read_filename(b, temp_filename)) {
_tprintf_s(TEXT("Failed to set target file of BIO. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_read_filename]\r\n"), ERR_get_error());
BIO_free_all(b);
delete[] temp_filename;
return FALSE;
}
delete[] temp_filename;
#else
BIO* b = BIO_new(BIO_s_file());
if (b == nullptr) {
_tprintf_s(TEXT("Failed to create BIO object. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_new]\r\n"), ERR_get_error());
return FALSE;
}
if (1 != BIO_read_filename(b, filename)) {
_tprintf_s(TEXT("Failed to set target file of BIO. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_read_filename]\r\n"), ERR_get_error());
BIO_free_all(b);
return FALSE;
}
#endif
RSA* ret = PEM_read_bio_RSAPrivateKey(b, nullptr, nullptr, nullptr);
if (ret == nullptr) {
_tprintf_s(TEXT("Failed to read RSA private key. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> PEM_read_bio_RSAPrivateKey]\r\n"), ERR_get_error());
BIO_free_all(b);
return nullptr;
} else {
BIO_free_all(b);
return ret;
}
}
int _tmain(int argc, TCHAR* argv[]) {
if (argc != 2) {
_tprintf_s(TEXT("Usage:\r\n"));
@@ -181,28 +259,48 @@ int _tmain(int argc, TCHAR* argv[]) {
}
srand(static_cast<unsigned int>(time(nullptr)));
#ifdef UNICODE
char pem_file_path[MAX_PATH] = { };
sprintf_s(pem_file_path, "%S", argv[1]);
#else
char* pem_file_path = argv[1];
#endif
RSA* PrivateKey = nullptr;
{
BIO* PrivateKeyFile = BIO_new(BIO_s_file());
BIO_read_filename(PrivateKeyFile, pem_file_path);
PrivateKey = PEM_read_bio_RSAPrivateKey(PrivateKeyFile, nullptr, nullptr, nullptr);
BIO_free_all(PrivateKeyFile);
}
RSA* PrivateKey = ReadRSAPrivateKeyFromFile(argv[1]);
if (PrivateKey == nullptr)
return 0;
if (PrivateKey == nullptr) {
_tprintf_s(TEXT("Failed to load private key.\r\n"));
return -1;
std::cout
<< "Which is your Navicat language?" << std::endl
<< "0. English" << std::endl
<< "1. Simplified Chinese" << std::endl
<< "2. Traditional Chinese" << std::endl
<< "3. Japanese" << std::endl
<< "4. Polish" << std::endl
<< "5. Spanish" << std::endl
<< "6. French" << std::endl
<< "7. German" << std::endl
<< "8. Korean" << std::endl
<< "9. Russian" << std::endl
<< "10. Portuguese" << std::endl
<< std::endl;
int LanguageIndex = -1;
while (true) {
std::cout << "(input index)>";
std::string temp;
if (!std::getline(std::cin, temp)) {
RSA_free(PrivateKey);
return 0;
}
try {
LanguageIndex = std::stoi(temp);
if (LanguageIndex < 0 || LanguageIndex > 10)
throw std::invalid_argument("Invalid index");
break;
} catch (...) {
std::cout << "Invalid index." << std::endl;
continue;
}
}
char SnKey[16] = { };
GenerateSnKey(SnKey);
GenerateSnKey(SnKey, static_cast<NavicatLanguage>(LanguageIndex));
std::string strName;
std::string strOrganization;
+4 -4
View File
@@ -28,26 +28,26 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
+16 -7
View File
@@ -3,16 +3,25 @@
// Solution0 is for navicat premium of which the version < 12.0.25
namespace patcher::Solution0 {
BOOL Do(LPCTSTR navicat_exe_path) {
BOOL Do(LPCTSTR navicat_exe_path, LPCTSTR prepared_key_file) {
if (!BackupFile(navicat_exe_path))
return FALSE;
RSA* PrivateKey = GenerateRSAKey();
if (PrivateKey == nullptr)
return FALSE;
RSA* PrivateKey = nullptr;
if (prepared_key_file == nullptr) {
PrivateKey = GenerateRSAKey();
if (PrivateKey == nullptr)
return FALSE;
if (!WriteRSAPrivateKeyToFile(TEXT("RegPrivateKey.pem"), PrivateKey))
return FALSE;
if (!WriteRSAPrivateKeyToFile(TEXT("RegPrivateKey.pem"), PrivateKey)) {
RSA_free(PrivateKey);
return FALSE;
}
} else {
PrivateKey = ReadRSAPrivateKeyFromFile(prepared_key_file);
if (PrivateKey == nullptr)
return FALSE;
}
char* pem_pubkey = GetPEMText(PrivateKey);
if (pem_pubkey == nullptr)
@@ -39,7 +48,7 @@ namespace patcher::Solution0 {
delete[] pem_pubkey;
return FALSE;
} else {
_tprintf_s(TEXT("@[patcher::Solution0::Do]: Public key has been replaced.\r\n"));
_tprintf_s(TEXT("@[patcher::Solution0::Do]: Public key has been replaced by:\r\n%hs"), pem_pubkey);
EndUpdateResource(hUpdater, FALSE);
delete[] pem_pubkey;
+287 -26
View File
@@ -40,25 +40,267 @@ namespace patcher::Solution1 {
}
}
BOOL Do(LPCTSTR libcc_dll_path, LPCTSTR prepared_key_file) {
uint8_t expected_hash[SHA256_DIGEST_LENGTH] = {
0x60, 0x7e, 0x0a, 0x84, 0xc7, 0x59, 0x66, 0xb0,
0x0f, 0x3d, 0x12, 0xfa, 0x83, 0x3e, 0x91, 0xd1,
0x59, 0xe4, 0xf5, 0x1a, 0xc5, 0x1b, 0x6b, 0xa6,
0x6f, 0x98, 0xd0, 0xc3, 0xcb, 0xef, 0xdc, 0xe0
};
static BOOL FindPatchOffset(LPCTSTR libcc_dll_path, DWORD Offset[5]) {
Offset[0] = 0;
Offset[1] = 0;
Offset[2] = 0;
Offset[3] = 0;
Offset[4] = 0;
if (!Check_libcc_Hash(libcc_dll_path, expected_hash))
HANDLE h_libcc = CreateFile(libcc_dll_path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h_libcc == INVALID_HANDLE_VALUE) {
_tprintf_s(TEXT("Failed to open libcc.dll. CODE: 0x%08x @[FindPatchOffset -> CreateFile]\r\n"), GetLastError());
return FALSE;
}
// DWORD is enough, you know libcc.dll cannot be larger than 4GB
DWORD libcc_size = GetFileSize(h_libcc, nullptr);
HANDLE h_libcc_map = CreateFileMapping(h_libcc, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (h_libcc_map == NULL) {
_tprintf_s(TEXT("Failed to create mapping for libcc.dll. CODE: 0x%08x @[FindPatchOffset -> CreateFileMapping]\r\n"), GetLastError());
CloseHandle(h_libcc);
return FALSE;
}
const uint8_t* libcc = reinterpret_cast<const uint8_t*>(MapViewOfFile(h_libcc_map, FILE_MAP_READ, 0, 0, 0));
if (libcc == nullptr) {
_tprintf_s(TEXT("Failed to map libcc.dll. CODE: 0x%08x @[FindPatchOffset -> MapViewOfFile]\r\n"), GetLastError());
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
const IMAGE_DOS_HEADER* libcc_dos_header = reinterpret_cast<const IMAGE_DOS_HEADER*>(libcc);
// check dos signature
if (libcc_dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
_tprintf_s(TEXT("libcc.dll does not have a valid DOS header. @[FindPatchOffset]\r\n"));
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
const IMAGE_NT_HEADERS* libcc_nt_header = reinterpret_cast<const IMAGE_NT_HEADERS*>(libcc + libcc_dos_header->e_lfanew);
// check nt signature
if (libcc_nt_header->Signature != IMAGE_NT_SIGNATURE) {
_tprintf_s(TEXT("libcc.dll does not have a valid NT header. @[FindPatchOffset]\r\n"));
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
// check if a dll
if ((libcc_nt_header->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
_tprintf_s(TEXT("libcc.dll is not a DLL file. @[FindPatchOffset]\r\n"));
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
// check if 32-bits or 64-bits
#if defined(_M_X64)
if (libcc_nt_header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 || libcc_nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
_tprintf_s(TEXT("libcc.dll is not a 64-bits DLL file. @[FindPatchOffset]\r\n"));
#elif defined(_M_IX86)
if (libcc_nt_header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386 || libcc_nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
#else
#error "unknown arch"
#endif
_tprintf_s(TEXT("libcc.dll is not a 32-bits DLL file. @[FindPatchOffset]\r\n"));
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
WORD section_num = libcc_nt_header->FileHeader.NumberOfSections;
const IMAGE_SECTION_HEADER* libcc_section_headers = reinterpret_cast<const IMAGE_SECTION_HEADER*>(
libcc + libcc_dos_header->e_lfanew + sizeof(libcc_nt_header->Signature) + sizeof(libcc_nt_header->FileHeader) + libcc_nt_header->FileHeader.SizeOfOptionalHeader
);
const IMAGE_SECTION_HEADER* rdata_section = nullptr;
for (WORD i = 0; i < section_num; ++i) {
if (*reinterpret_cast<const uint64_t*>(libcc_section_headers[i].Name) == 0x61746164722e) { // b'\x00\x00atadr.'
rdata_section = libcc_section_headers + i;
break;
}
}
if (rdata_section == nullptr) {
_tprintf_s(TEXT(".rdata section is not found. @[FindPatchOffset]\r\n"));
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
const IMAGE_SECTION_HEADER* text_section = nullptr;
for (WORD i = 0; i < section_num; ++i) {
if (*reinterpret_cast<const uint64_t*>(libcc_section_headers[i].Name) == 0x747865742e) { // b'\x00\x00\x00txet.'
text_section = libcc_section_headers + i;
break;
}
}
if (text_section == nullptr) {
_tprintf_s(TEXT(".text section is not found. @[FindPatchOffset]\r\n"));
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
return FALSE;
}
// search offset[0]
{
const uint8_t keyword[] = "D75125B70767B94145B47C1CB3C0755E";
const uint8_t* start = libcc + rdata_section->PointerToRawData;
DWORD section_size = rdata_section->SizeOfRawData;
for (DWORD i = 0; i < section_size; ++i) {
if (start[i] == keyword[0]) {
bool found = true;
for (DWORD j = 1; j < sizeof(keyword) - 1; ++j)
if (start[i + j] != keyword[j]) {
found = false;
break;
}
if (found) {
Offset[0] = rdata_section->PointerToRawData + i;
break;
}
}
}
}
// search offset[2]
{
const uint8_t keyword[] = "E1CED09B9C2186BF71A70C0FE2F1E0AE";
const uint8_t* start = libcc + rdata_section->PointerToRawData;
DWORD section_size = rdata_section->SizeOfRawData;
for (DWORD i = 0; i < section_size; ++i) {
if (start[i] == keyword[0]) {
bool found = true;
for (DWORD j = 1; j < sizeof(keyword) - 1; ++j)
if (start[i + j] != keyword[j]) {
found = false;
break;
}
if (found) {
Offset[2] = rdata_section->PointerToRawData + i;
break;
}
}
}
}
// search offset[4]
{
const uint8_t keyword[] = "92933";
const uint8_t* start = libcc + rdata_section->PointerToRawData;
DWORD section_size = rdata_section->SizeOfRawData;
for (DWORD i = 0; i < section_size; ++i) {
if (start[i] == keyword[0]) {
bool found = true;
for (DWORD j = 1; j < sizeof(keyword) - 1; ++j)
if (start[i + j] != keyword[j]) {
found = false;
break;
}
if (found) {
Offset[4] = rdata_section->PointerToRawData + i;
break;
}
}
}
}
// search offset[1]
{
const uint8_t keyword[] = { 0xfe, 0xea, 0xbc, 0x01 };
const uint8_t* start = libcc + text_section->PointerToRawData;
DWORD section_size = text_section->SizeOfRawData;
for (DWORD i = 0; i < section_size; ++i) {
if (start[i] == keyword[0]) {
bool found = true;
for (DWORD j = 1; j < sizeof(keyword); ++j)
if (start[i + j] != keyword[j]) {
found = false;
break;
}
if (found) {
Offset[1] = text_section->PointerToRawData + i;
break;
}
}
}
}
// search offset[3]
{
const uint8_t keyword[] = { 0x59, 0x08, 0x01, 0x00 };
const uint8_t* start = libcc + text_section->PointerToRawData;
DWORD section_size = text_section->SizeOfRawData;
for (DWORD i = 0; i < section_size; ++i) {
if (start[i] == keyword[0]) {
bool found = true;
for (DWORD j = 1; j < sizeof(keyword); ++j)
if (start[i + j] != keyword[j]) {
found = false;
break;
}
if (found) {
Offset[3] = text_section->PointerToRawData + i;
break;
}
}
}
}
UnmapViewOfFile(libcc);
CloseHandle(h_libcc_map);
CloseHandle(h_libcc);
if (Offset[0] == 0 || Offset[1] == 0 || Offset[2] == 0 || Offset[3] == 0 || Offset[4] == 0) {
_tprintf_s(TEXT("Failed to find all patch offset. Is libcc.dll from official? Or you've patched?\r\n"));
return FALSE;
} else {
return TRUE;
}
}
BOOL Do(LPCTSTR libcc_dll_path, LPCTSTR prepared_key_file) {
// uint8_t expected_hash[SHA256_DIGEST_LENGTH] = {
// 0x60, 0x7e, 0x0a, 0x84, 0xc7, 0x59, 0x66, 0xb0,
// 0x0f, 0x3d, 0x12, 0xfa, 0x83, 0x3e, 0x91, 0xd1,
// 0x59, 0xe4, 0xf5, 0x1a, 0xc5, 0x1b, 0x6b, 0xa6,
// 0x6f, 0x98, 0xd0, 0xc3, 0xcb, 0xef, 0xdc, 0xe0
// };
//
// if (!Check_libcc_Hash(libcc_dll_path, expected_hash))
// return FALSE;
DWORD Patch_Offset[5];
if (!FindPatchOffset(libcc_dll_path, Patch_Offset))
return FALSE;
if (!BackupFile(libcc_dll_path))
return FALSE;
RSA* PrivateKey;
RSA* PrivateKey = nullptr;
if (prepared_key_file != nullptr) {
PrivateKey = ReadRSAPrivateKeyFromFile(prepared_key_file);
if (PrivateKey == nullptr)
return FALSE;
if (!CheckRSAKeyIsAppropriate(PrivateKey)) {
_tprintf_s(TEXT("The key is not appropriate to use. @[patcher::Solution1::Do -> CheckRSAKeyIsAppropriate]\r\n"));
RSA_free(PrivateKey);
return FALSE;
}
} else {
PrivateKey = GenerateAppropriateRSAKey();
if (PrivateKey == nullptr)
@@ -92,83 +334,102 @@ namespace patcher::Solution1 {
uint32_t imm1 = std::stoul(encrypted_pem_pubkey1.c_str());
uint32_t imm3 = std::stoul(encrypted_pem_pubkey3.c_str());
uint32_t patch_offset0 = 0x1A12090;
uint32_t patch_offset1 = 0x59D799;
uint32_t patch_offset2 = 0x1A11DA0;
uint32_t patch_offset3 = 0x59D77F;
uint32_t patch_offset4 = 0x1A11D8C;
HANDLE hFile = CreateFile(libcc_dll_path, GENERIC_WRITE, NULL, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE) {
_tprintf_s(TEXT("Failed to open libcc.dll. CODE: 0x%08x @[patcher::Solution1::Do -> CreateFile]\r\n"), GetLastError());
return FALSE;
}
// Start from win8, lpNumberOfBytesWritten parameter in WriteFile can be null if lpOverlapped is null.
// But win7 is not. lpNumberOfBytesWritten cannot be null if lpOverlapped is null.
// However MSDN does not mention that.
DWORD WrittenBytes;
// start patch 0
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, patch_offset0, nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("\r\nStart to do patch 0......\r\n"));
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[0], nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
if (FALSE == WriteFile(hFile, encrypted_pem_pubkey0.c_str(), encrypted_pem_pubkey0.length(), nullptr, nullptr)) {
_tprintf_s(TEXT("At offset +0x%08x, write:\r\n\"%hs\"\r\n"), Patch_Offset[0], encrypted_pem_pubkey0.c_str());
if (FALSE == WriteFile(hFile, encrypted_pem_pubkey0.c_str(), encrypted_pem_pubkey0.length(), &WrittenBytes, nullptr)) {
_tprintf_s(TEXT("Failed to write patch 0. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
_tprintf_s(TEXT("patch 0 done.....\r\n"));
// start patch 1
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, patch_offset1, nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("\r\nStart to do patch 1.....\r\n"));
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[1], nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
if (FALSE == WriteFile(hFile, &imm1, sizeof(imm1), nullptr, nullptr)) {
_tprintf_s(TEXT("At offset +0x%08x, write immediate value %d (type: uint32_t)\r\n"), Patch_Offset[1], imm1);
if (FALSE == WriteFile(hFile, &imm1, sizeof(imm1), &WrittenBytes, nullptr)) {
_tprintf_s(TEXT("Failed to write patch 1. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
_tprintf_s(TEXT("patch 1 done.....\r\n"));
// start patch 2
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, patch_offset2, nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("\r\nStart to do patch 2.....\r\n"));
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[2], nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
if (FALSE == WriteFile(hFile, encrypted_pem_pubkey2.c_str(), encrypted_pem_pubkey2.length(), nullptr, nullptr)) {
_tprintf_s(TEXT("At offset +0x%08x, write:\r\n\"%hs\"\r\n"), Patch_Offset[2], encrypted_pem_pubkey2.c_str());
if (FALSE == WriteFile(hFile, encrypted_pem_pubkey2.c_str(), encrypted_pem_pubkey2.length(), &WrittenBytes, nullptr)) {
_tprintf_s(TEXT("Failed to write patch 2. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
_tprintf_s(TEXT("patch 2 done.....\r\n"));
// start patch 3
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, patch_offset3, nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("\r\nStart to do patch 3.....\r\n"));
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[3], nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
if (FALSE == WriteFile(hFile, &imm3, sizeof(imm3), nullptr, nullptr)) {
_tprintf_s(TEXT("At offset +0x%08x, write immediate value %d (type: uint32_t)\r\n"), Patch_Offset[3], imm3);
if (FALSE == WriteFile(hFile, &imm3, sizeof(imm3), &WrittenBytes, nullptr)) {
_tprintf_s(TEXT("Failed to write patch 3. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
_tprintf_s(TEXT("patch 3 done.....\r\n"));
// start patch 4
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, patch_offset4, nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("\r\nStart to do patch 4.....\r\n"));
if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[4], nullptr, FILE_BEGIN)) {
_tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
if (FALSE == WriteFile(hFile, encrypted_pem_pubkey4.c_str(), encrypted_pem_pubkey4.length(), nullptr, nullptr)) {
_tprintf_s(TEXT("At offset +0x%08x, write:\r\n\"%hs\"\r\n"), Patch_Offset[4], encrypted_pem_pubkey4.c_str());
if (FALSE == WriteFile(hFile, encrypted_pem_pubkey4.c_str(), encrypted_pem_pubkey4.length(), &WrittenBytes, nullptr)) {
_tprintf_s(TEXT("Failed to write patch 4. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
CloseHandle(hFile);
return FALSE;
}
_tprintf_s(TEXT("patch 4 done.....\r\n\r\n"));
return TRUE;
}
+5 -1
View File
@@ -46,7 +46,11 @@ int _tmain(int argc, TCHAR* argv[]) {
BOOL status;
if (NavicatMajorVersion <= 0x000C0000 && NavicatMinorVersion < 0x00190000) { // for navicat ver < 12.0.25
status = patcher::Solution0::Do(navicat_exe_path.c_str());
} else if (NavicatMajorVersion == 0x000C0000 && NavicatMinorVersion == 0x00190000) { // for navicat ver == 12.0.25
} else if (NavicatMajorVersion == 0x000C0000 && NavicatMinorVersion == 0x00190000) { // for navicat ver = 12.0.25
status = patcher::Solution1::Do(libcc_dll_path.c_str(), argc == 3 ? argv[2] : nullptr);
} else if (NavicatMajorVersion == 0x000C0000 && NavicatMinorVersion == 0x001A0000) { // for navicat ver = 12.0.26
status = patcher::Solution1::Do(libcc_dll_path.c_str(), argc == 3 ? argv[2] : nullptr);
} else if (NavicatMajorVersion == 0x000C0000 && NavicatMinorVersion == 0x001B0000) { // for navicat ver = 12.0.27
status = patcher::Solution1::Do(libcc_dll_path.c_str(), argc == 3 ? argv[2] : nullptr);
}
+3 -3
View File
@@ -19,8 +19,8 @@
#else
#pragma comment(lib, "libcryptoMT.lib")
#endif
#pragma comment(lib, "WS2_32.lib") // some symbol are used in OpenSSL lib
#pragma comment(lib, "Crypt32.lib") // some symbol are used in OpenSSL lib
#pragma comment(lib, "WS2_32.lib") // some symbol are used in OpenSSL static lib
#pragma comment(lib, "Crypt32.lib") // some symbol are used in OpenSSL static lib
#pragma comment(lib, "version.lib") // GetFileVersionInfoSize, GetFileVersionInfo, VerQueryValue are in this lib
namespace patcher {
@@ -41,7 +41,7 @@ namespace patcher {
char* GetPEMText(RSA* PrivateKey);
namespace Solution0 {
BOOL Do(LPCTSTR navicat_exe_path);
BOOL Do(LPCTSTR navicat_exe_path, LPCTSTR prepared_key_file = nullptr);
}
namespace Solution1 {
+4 -4
View File
@@ -28,26 +28,26 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v140_xp</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>