Refactor code

This commit is contained in:
Double Sine 2018-12-19 18:00:46 +08:00
parent 7e0705dd9d
commit 88ccb398a0
No known key found for this signature in database
GPG Key ID: 44460E4F43EA8633
28 changed files with 2731 additions and 912 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.DS_Store *.DS_Store
bin/*

233
HOW_DOES_IT_WORK.md Normal file
View File

@ -0,0 +1,233 @@
# Navicat Keygen - How does it work?
[中文版 How does it work?](HOW_DOES_IT_WORK.zh-CN.md)
## 1. Keyword Explanation.
* __Navicat Activation Public Key__
It is an __RSA-2048__ public key that Navicat used to encrypt or decrypt offline activation information.
It is stored in
```
Navicat Premium.app/Contents/Resources/rpk
```
You can see it by any kind of text editor. The content is:
```
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
awIDAQAB
-----END PUBLIC KEY-----
```
If you have the corresponding private key, please tell me. I would be very appreciated for your generous.
__NOTICE:__
Start from __Navicat Premuim 12.0.24 for Mac__, the public key is no longer stored in
```
Navicat Premium.app/Contents/Resources/rpk
```
Instead, the public key is stored in Navicat executable file
```
Navicat Premium.app/Contents/MacOS/Navicat Premium
```
in plaintext. You can see it by searching string `"-----BEGIN PUBLIC KEY----- "`.
__NOTICE:__
Start from __Navicat Premium 12.1.14 for Mac__, the public key is still stored in the executable file in plaintext.
However, it does not load the key from the plaintext. Instead, it loads the key from a piece of ciphertext which is 0x188 bytes long. The ciphertext is
```c
const uint8_t ciphertext[0x188] = {
0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd,
0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb,
0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb,
0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3,
0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea,
0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2,
0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4,
0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc,
0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef,
0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7,
0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8,
0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc,
0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e,
0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9,
0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc,
0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88,
0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2,
0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd,
0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2,
0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce,
0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd,
0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5,
0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce,
0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb,
0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8
};
```
The ciphertext is encrypted by XOR encryption where XOR key is
```
\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba
```
* __Request Code__
It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the ciphertext of __Offline Activation Request Information__ encrypted by __Navicat Activation Public Key__.
* __Offline Activation Request Information__
It is just a JSON-style UTF-8 string which contains 3 items. Respectively they are `"K"`, `"DI"` and `"P"`, which represent __snKey__, __DeviceIdentifier__, __Platform__.
Example:
```
{ "K": "xxxxxxxxxxxxxxxx", "P": "Mac 10.13", "DI": "xxxxxxxxxxxxxxxxxxxx" }
```
* __Activation Code__
It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the ciphertext of __Offline Activation Response Information__ encrypted by __Navicat Activation Private Key__ which, so far, we don't know.
* __Offline Activation Response Information__
Just like __Offline Activation Request Information__, it is also a JSON-style UTF-8 string. But it contains 5 items. Respectively they are `"K"`, `"N"`, `"O"`, `"T"`, '`DI`'.
`"K"` and `"DI"` has the same meaning mentioned in __Offline Activation Request Information__ and must be same with the corresponding items in __Offline Activation Request Information__.
`"N"`, `"O"`, `"T"` represent __Name__, __Organization__, __Time__ respectively. __Name__ and __Organization__ are string and the type of __Time__ can be string or integer (Thanks for discoveries from @Wizr, issue #10).
Differ from Navicat Windows version, `"T"` is mandatory and must have -1 ~ +4 days difference from current time.
Example:
```
{
"DI" : "xxxxxxxxxxxxxxxxxxxx",
"T" : "1515770827.925012",
"K" : "xxxxxxxxxxxxxxxx",
"N" : "DoubleLabyrinth",
"O" : "Shadow"
}
```
* __snKey__
It is a 4-block-long string, while every block is 4-chars-long.
__snKey__ is generated by 10-bytes-long data. In order to explain it easily, I use __uint8_t data[10]__ to represent the 10-bytes-long data.
1. __data[0]__ and __data[1]__ must be `0x68` and `0x2A` respectively.
These two bytes are Naivcat signature number.
2. __data[2]__, __data[3]__ and __data[4]__ can be any byte. Just set them whatever you want.
3. __data[5]__ and __data[6]__ are related with your Navicat product language.
| Language | data[5] | data[6] | Discoverer |
|------------|-----------|-----------|-----------------|
| English | 0xAC | 0x88 | |
| 简体中文 | 0xCE | 0x32 | |
| 繁體中文 | 0xAA | 0x99 | |
| 日本語 | 0xAD | 0x82 | @dragonflylee |
| Polski | 0xBB | 0x55 | @dragonflylee |
| Español | 0xAE | 0x10 | @dragonflylee |
| Français | 0xFA | 0x20 | @Deltafox79 |
| Deutsch | 0xB1 | 0x60 | @dragonflylee |
| 한국어 | 0xB5 | 0x60 | @dragonflylee |
| Русский | 0xEE | 0x16 | @dragonflylee |
| Português | 0xCD | 0x49 | @dragonflylee |
4. __data[7]__ is Navicat product ID. (Thanks @dragonflylee and @Deltafox79)
|Product Name |Enterprise|Standard|Educational|Essentials|
|---------------------|:--------:|:------:|:---------:|:--------:|
|Navicat Report Viewer|0x0B | | | |
|Navicat Data Modeler | |0x47 |0x4A | |
|Navicat Premium |0x65 | |0x66 |0x67 |
|Navicat MySQL |0x68 |0x69 |0x6A |0x6B |
|Navicat PostgreSQL |0x6C |0x6D |0x6E |0x6F |
|Navicat Oracle |0x70 |0x71 |0x72 |0x73 |
|Navicat SQL Server |0x74 |0x75 |0x76 |0x77 |
|Navicat SQLite |0x78 |0x79 |0x7A |0x7B |
|Navicat MariaDB |0x7C |0x7D |0x7E |0x7F |
|Navicat MongoDB |0x80 |0x81 |0x82 | |
5. High 4 bits of __data[8]__ represents __major version number__.
Low 4 bits is unknown, but we can use it to delay activation deadline. Possible values are `0000` or `0001`.
__Example:__
For __Navicat 12 x64__: High 4 bits must be `1100`, which is the binary of number `12`.
For __Navicat 11 x64__: High 4 bits must be `1011`, which is the binary of number `11`.
6. __data[9]__ is unknown, but you can set it by `0xFD`, `0xFC` or `0xFB` if you want to use __not-for-resale license__.
According to symbol information in __Navicat 12 for Mac x64__ version:
* `0xFB` is __Not-For-Resale-30-days__ license.
* `0xFC` is __Not-For-Resale-90-days__ license.
* `0xFD` is __Not-For-Resale-365-days__ license.
* `0xFE` is __Not-For-Resale__ license.
* `0xFF` is __Site__ license.
After that. Navicat use __DES__ with __ECB mode__ to encrypt the last 8 bytes which are from __data[2]__ to __data[9]__.
The DES key is:
```cpp
const uint8_t DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
```
Then use Base32 to encode `uint8_t data[10]` whose encode table is
```cpp
// Thanks for discoveries from @Wizr, issue #10
char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
```
After encoding, you will get a 16-char-long string starting with `"NAV"`.
Finally, divide the 16-char-long string to four 4-chars-long blocks and join them with `"-"` then you will get __snKey__.
## 2. Activation Process
1. Check whether __snKey__ that user inputs is valid.
2. After user clicks `Activate`, Navicat will start online activation first. If fails, user can choose offline activation.
3. Navicat will use the __snKey__ that user inputs and some information collected from user's machine to generate __Offline Activation Request Information__. Then Navicat will encrypt it by __Navicat Activation Public Key__ and return a Base64-encoded string as __Request Code__.
4. In legal way, the __Request Code__ should be sent to Navicat official activation server by a Internet-accessible computer. And Navicat official activation server will return a legal __Activation Code__.
But now, we use keygen to play the official activation server's role.
1. According to the __Request Code__, get `"DI"` value and `"K"` value.
2. Fill __Offline Activation Response Information__ with `"K"` value, name, organization name, `"DI"` value and `"T"` value.
3. Encrypt __Offline Activation Response Information__ by __Navicat Activation Private Key__ and you will get 256-byte-long data.
4. Encode the 256-byte-long data by Base64. The result is __Activation Code__.
5. After user input __Activation Code__, offline activation is done successfully.

230
HOW_DOES_IT_WORK.zh-CN.md Normal file
View File

@ -0,0 +1,230 @@
# Navicat keygen - 注册机是怎么工作的?
## 1. 关键词解释.
* __Navicat激活公钥__
这是一个2048位的RSA公钥Navicat使用这个公钥来完成相关激活信息的加密和解密。
这个公钥储存在
```
Navicat Premium.app/Contents/Resources/rpk
```
中,你可以用任何一种文本编辑器打开并查看它。这个公钥的具体内容为:
```
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
awIDAQAB
-----END PUBLIC KEY-----
```
如果您有相应的私钥并乐意公开的话欢迎联系我,我将非常感谢您的慷慨。
__注意__
__Navicat Premium for Mac 12.0.24__ 开始,公钥不再存储在
```
Navicat Premium.app/Contents/Resources/rpk
```
中。事实上公钥放在了Navicat的二进制执行文件
```
Navicat Premium.app/Contents/MacOS/Navicat Premium
```
中,你可以通过搜索`"-----BEGIN PUBLIC KEY-----"`来找到它。
__注意__
__Navicat Premium for Mac 12.1.14__ 开始,公钥仍然以明文的方式储存在二进制可执行文件中。
但是Navicat并不从这个明文中加载公钥。事实上密钥是从一段0x188字节长的密文中加载的。密文为
```c
const uint8_t ciphertext[0x188] = {
0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd,
0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb,
0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb,
0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3,
0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea,
0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2,
0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4,
0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc,
0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef,
0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7,
0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8,
0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc,
0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e,
0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9,
0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc,
0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88,
0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2,
0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd,
0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2,
0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce,
0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd,
0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5,
0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce,
0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb,
0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8
};
```
这个密文是采用XOR加密得来XOR密钥为
```
\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba
```
* __请求码__
这是一个Base64编码的字符串代表的是长度为256字节的数据。这256字节的数据是 __离线激活信息____Navicat激活公钥__ 加密的密文。
* __离线激活请求信息__
这是一个JSON风格的UTF-8字符串。它包含了3个Key`"K"`、`"DI"`和`"P"`,分别代表 __序列号__、__设备识别码__与你的电脑硬件信息相关__平台__ (其实就是操作系统类型)。
例如:
```
{ "K": "xxxxxxxxxxxxxxxx", "P": "Mac 10.13", "DI": "xxxxxxxxxxxxxxxxxxxx" }
```
* __激活码__
这是一个Base64编码的字符串代表的是长度为256字节的数据。这256字节的数据是 __离线激活回复信息____Navicat激活私钥__ 加密的密文。目前我们不知道官方的 __Navicat激活私钥__,所以我们得替换掉软件里的公钥。
* __离线激活回复信息__
__离线激活请求信息__ 一样它也是一个JSON风格的UTF-8字符串。但是它包含5个Key分别为`"K"`、`"N"`、`"O"`、`"T"` 和 `"DI"`.
`"K"``"DI"` 的意义与 __离线激活请求信息__ 中的相同且Value必须与 __离线激活请求信息__ 中的相同。
`"N"`、`"O"`、`"T"` 分别代表 __注册名__、__组织__、__授权时间__。
__注册名____组织__ 的值类型为UTF-8编码的字符串。__授权时间__ 的值类型可以为字符串或整数(感谢@Wizr在issue #10中的报告)。
和Windows版本Navicat不同的是`"T"` 项不可以省略,并且和当前时间的差距必须在-1 ~ +4天之内。
例如:
```
{
"DI" : "xxxxxxxxxxxxxxxxxxxx",
"T" : "1515770827.925012",
"K" : "xxxxxxxxxxxxxxxx",
"N" : "DoubleLabyrinth",
"O" : "Shadow"
}
```
* __序列号__
这是一个被分为了4个部分的字符串其中每个部分都是4个字符长。
__序列号__ 是通过10个字节的数据来生成的。为了表达方便我用 __uint8_t data[10]__ 来表示这10个字节。
1. __data[0]____data[1]__ 必须分别为 `0x68``0x2A`
这两个字节为Navicat的标志数。
2. __data[2]__、__data[3]__ 和 __data[4]__ 可以是任意字节,你想设成什么都行。
3. __data[5]____data[6]__ 是Navicat的语言标志值如下
| 语言类型 | data[5] | data[6] | 发现者 |
|------------|:---------:|:---------:|-----------------|
| English | 0xAC | 0x88 | |
| 简体中文 | 0xCE | 0x32 | |
| 繁體中文 | 0xAA | 0x99 | |
| 日本語 | 0xAD | 0x82 | @dragonflylee |
| Polski | 0xBB | 0x55 | @dragonflylee |
| Español | 0xAE | 0x10 | @dragonflylee |
| Français | 0xFA | 0x20 | @Deltafox79 |
| Deutsch | 0xB1 | 0x60 | @dragonflylee |
| 한국어 | 0xB5 | 0x60 | @dragonflylee |
| Русский | 0xEE | 0x16 | @dragonflylee |
| Português | 0xCD | 0x49 | @dragonflylee |
4. __data[7]__ 是Navicat产品ID。感谢 @dragonflylee@Deltafox79提供的数据
|产品名 |Enterprise|Standard|Educational|Essentials|
|---------------------|:--------:|:------:|:---------:|:--------:|
|Navicat Report Viewer|0x0B | | | |
|Navicat Data Modeler | |0x47 |0x4A | |
|Navicat Premium |0x65 | |0x66 |0x67 |
|Navicat MySQL |0x68 |0x69 |0x6A |0x6B |
|Navicat PostgreSQL |0x6C |0x6D |0x6E |0x6F |
|Navicat Oracle |0x70 |0x71 |0x72 |0x73 |
|Navicat SQL Server |0x74 |0x75 |0x76 |0x77 |
|Navicat SQLite |0x78 |0x79 |0x7A |0x7B |
|Navicat MariaDB |0x7C |0x7D |0x7E |0x7F |
|Navicat MongoDB |0x80 |0x81 |0x82 | |
5. __data[8]__ 的高4位代表 __版本号__。低4位未知但可以用来延长激活期限可取的值有`0000`和`0001`。
例如:
对于 __Navicat 12__: 高4位必须是`1100`,为`12`的二进制形式。
对于 __Navicat 11__: 高4位必须是`1011`,为`11`的二进制形式。
6. __data[9]__ 目前暂未知,但如果你想要 __not-for-resale license__ 的话可以设成`0xFD`、`0xFC`或`0xFB`。
根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知:
* `0xFB`__Not-For-Resale-30-days__ license.
* `0xFC`__Not-For-Resale-90-days__ license.
* `0xFD`__Not-For-Resale-365-days__ license.
* `0xFE`__Not-For-Resale__ license.
* `0xFF`__Site__ license.
之后Navicat使用 __ECB__ 模式的 __DES__ 算法来加密 __data[10]__ 的后8字节也就是 __data[2]____data[9]__ 的部分。
相应的DES密钥为
```cpp
const uint8_t DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
```
之后使用Base32编码 __data[10]__,其中编码表改为:
```cpp
// Thanks for discoveries from @Wizr, issue #10
char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
```
编码之后你应该会得到一个16字节长的字符串并且以"NAV"打头。
将16字节的字符串分成4个4字节的小块然后用`"-"`连接就可以得到 __序列号__
## 2. 激活过程
1. 检查用户输入的 __序列号__ 是否合法。
2. 在用户点击了`激活`按钮之后Navicat会先尝试在线激活。如果失败用户可以选择离线激活。
3. Navicat会使用用户输入的 __序列号__ 以及从用户电脑收集来的信息生成 __离线激活请求信息__,然后用 __Navicat激活公钥__ 加密并将密文用Base64编码最后得到 __请求码__
4. 正常流程下__请求码__ 应该通过可联网的电脑发送给Navicat的官方激活服务器。之后Navicat的官方激活服务器会返回一个合法的 __激活码__
但现在我们使用注册机来扮演官方激活服务器的角色只是Navicat软件里的激活公钥得换成自己的公钥
1. 根据 __请求码__, 获得`"DI"`值和`"K"`值。
2. 用`"K"`值、用户名、组织名和`"DI"`值填写 __离线激活回复信息__
3. 用自己的2048位RSA私钥加密 __离线激活回复信息__你将会得到256字节的密文。
4. 用Base64编码这256字节的密文就可以得到 __激活码__
5. 在Navicat软件中填入 __激活码__ 即可完成离线激活。

57
Makefile Normal file
View File

@ -0,0 +1,57 @@
CC = g++
OPENSSL_INCLUDE_PATH = /usr/local/opt/openssl/include
OPENSSL_LIB_PATH = /usr/local/opt/openssl/lib
OUTPUT_DIR = ./bin/
PATCHER_DIR = ./navicat-patcher/
KEYGEN_DIR = ./navicat-keygen/
keygen_output = $(OUTPUB_DIR)navicat-keygen
PATCHER_HEADER = \
$(PATCHER_DIR)FileMapper.hpp \
$(PATCHER_DIR)Helper.hpp \
$(PATCHER_DIR)RSACipher.hpp \
$(PATCHER_DIR)Solutions.hpp
PATCHER_SOURCE = \
$(PATCHER_DIR)Helper.cpp \
$(PATCHER_DIR)main.cpp \
$(PATCHER_DIR)Solution0.cpp \
$(PATCHER_DIR)Solution1.cpp
PATCHER_BINARY = $(OUTPUT_DIR)navicat-patcher
KEYGEN_HEADER = \
$(KEYGEN_DIR)Helper.hpp \
$(KEYGEN_DIR)RSACipher.hpp \
$(KEYGEN_DIR)DESCipher.hpp \
$(KEYGEN_DIR)NavicatKeygen.hpp
KEYGEN_SOURCE = \
$(KEYGEN_DIR)Helper.cpp \
$(KEYGEN_DIR)main.cpp
KEYGEN_BINARY = $(OUTPUT_DIR)navicat-keygen
patcher: $(PATCHER_HEADER) $(PATCHER_SOURCE)
@if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi
$(CC) -std=c++11 -O2 -I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) -lcrypto $(PATCHER_SOURCE) -o $(PATCHER_BINARY)
keygen: $(KEYGEM_HEADER) $(KEYGEN_SOURCE)
@if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi
$(CC) -std=c++11 -O2 -I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) -lcrypto $(KEYGEN_SOURCE) -o $(KEYGEN_BINARY)
all: patcher keygen
@echo 'Done.'
.PHONY: all
clean:
ifeq ($(wildcard $(PATCHER_BINARY)), $(PATCHER_BINARY))
rm $(PATCHER_BINARY)
endif
ifeq ($(wildcard $(KEYGEN_BINARY)), $(KEYGEN_BINARY))
rm $(KEYGEN_BINARY)
endif

388
README.md
View File

@ -1,291 +1,189 @@
# Navicat Keygen # Navicat Keygen
[中文版README](README.zh-CN.md) [中文版README](README.zh-CN.md)
This repository will tell you how Navicat offline activation works. This repository will tell you how Navicat offline activation works.
## 1. Keyword Explanation. [How does it work?](HOW_DOES_IT_WORK.md)
* __Navicat Activation Public Key__ ## 1. How to build
It is a __RSA-2048__ public key that Navicat used to encrypt or decrypt offline activation information. * Before you build keygen, you should make sure you have `OpenSSL` lib and `rapidjson` lib.
It is stored in __Navicat Premium.app/Contents/Resources/rpk__. You can see it by any kind of text editor. The concrete content is: If you have `brew`, you can install them by
> -----BEGIN PUBLIC KEY-----
> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
> qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
> a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
> R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
> WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
> YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
> awIDAQAB
> -----END PUBLIC KEY-----
If you have the corresponding private key, please tell me. I would be very appreciated for your generous.
__NOTICE:__
Start from __Navicat Premuim 12.0.24 for Mac__, the public key is no longer stored in __Navicat Premium.app/Contents/Resources/rpk__. Instead, the public key is stored in Navicat executable file __Navicat Premium.app/Contents/MacOS/Navicat Premium__. You can see it by searching string `"-----BEGIN 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__.
* __Offline Activation Request Information__
It is just a JSON-style ASCII string which contains 3 items. Respectively they are `"K"`, `"DI"` and `"P"`, which represent __snKey__, __DeviceIdentifier__ (related with your machine), __Platform__ (Appropriately speaking, it should be OS Type).
Like:
> {
>     "K": "xxxxxxxxxxxxxxxx",
>     "P": "Mac 10.13",
>     "DI": "xxxxxxxxxxxxxxxxxxxx"
> }
* __Activation 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 response information__ encrypted by __Navicat Activation Private Key__ (so far, we don't know official activation private key).
* __Offline Activation Response Information__
Just like __Offline Activation Request Information__, it is also a JSON-style ASCII string. But it contains 5 items. Respectively they are `"K"`, `"N"`, `"O"`, `"T"`, '`DI`'.
`"K"` and `"DI"` has the same meaning mentioned in __Offline Activation Request Information__ and must be same with the corresponding items in __Offline Activation Request Information__.
`"N"`, `"O"`, `"T"` represent __Name__, __Organization__, __Time__ respectively. __Name__ and __Organization__ are string and the type of __Time__ can be string or integer (Thanks for discoveries from @Wizr, issue #10).
Differ from Navicat Windows version, `"T"` is mandatory and must be -1 ~ +4 days difference from current time. Here is an example of __Offline Activation Response Information__:
> {
>     "DI" : "xxxxxxxxxxxxxxxxxxxx",
>     "T" : "1515770827.925012",
>     "K" : "xxxxxxxxxxxxxxxx",
>     "N" : "DoubleLabyrinth",
>     "O" : "Shadow"
> }
* __snKey__
It is a 4-block-long string, while every block is 4-chars-long.
__snKey__ is generated by 10-bytes-long data. In order to explain it easily, I use __data[10]__ to represent the 10-bytes-long data.
1. __data[0]__ and __data[1]__ must be `0x68` and `0x2A` respectively.
_`May change when Navicat product changes. Uncertain yet.`_
2. __data[2]__, __data[3]__ and __data[4]__ can be any byte. Just set them whatever you want.
3. __data[5]__ and __data[6]__ are related with your Navicat product language. It depends.
~~_`May change when Navicat product changes. Uncertain yet.`_~~
_`Must change when Navicat product changes. Confirmed yet.`_
| Language | data[5] | data[6] | Discoverer |
|------------|-----------|-----------|-----------------|
| English | 0xAC | 0x88 | |
| 简体中文 | 0xCE | 0x32 | |
| 繁體中文 | 0xAA | 0x99 | |
| 日本語 | 0xAD | 0x82 | @dragonflylee |
| Polski | 0xBB | 0x55 | @dragonflylee |
| Español | 0xAE | 0x10 | @dragonflylee |
| Français | 0xFA | 0x20 | @Deltafox79 |
| Deutsch | 0xB1 | 0x60 | @dragonflylee |
| 한국어 | 0xB5 | 0x60 | @dragonflylee |
| Русский | 0xEE | 0x16 | @dragonflylee |
| Português | 0xCD | 0x49 | @dragonflylee |
According to __Navicat 12 for Mac x64__ version, what IDA 7.0 indicates is that this two bytes are product signature.
4. __data[7]__ represents whether it is __commercial license__ or __non-commercial license__.
For __Navicat 12 x64__: `0x65` is __commercial license__, `0x66` is __non-commercial license__.
For __Navicat 11 x64__: `0x15` is __commercial license__, `0x16` is __non-commercial license__.
_`May change when Navicat product changes. Uncertain yet.`_
_`Must change when version change.`_
According to __Navicat 12 for Mac x64__ version, what IDA 7.0 indicates is that commercial license is __Enterprise License__ and non-commercial license is __Educational License__.
5. High 4 bits of __data[8]__ represents __version number__. Low 4 bits is unknown, but we can use it to delay activation deadline. Possible value is `0000` or `0001`.
For __Navicat 12 x64__: High 4 bits must be `1100`, which is the binary of number `12`.
For __Navicat 11 x64__: High 4 bits must be `1011`, which is the binary of number `11`.
6. __data[9]__ is unknown, but you can set it `0xFD` or `0xFC` or `0xFB` if you want to use __not-for-resale license__. This must not be `0x00`. But other value is OK.
According to __Navicat 12 for Mac x64__ version, what IDA 7.0 indicates is that:
* `0xFB` is __Not-For-Resale-30-days__ license.
* `0xFC` is __Not-For-Resale-90-days__ license.
* `0xFD` is __Not-For-Resale-365-days__ license.
* `0xFE` is __Not-For-Resale__ license.
* `0xFF` is __Site__ license.
-----------------
After that. Navicat use __DES__ with __ECB mode__ to encrypt the last 8 bytes which are from __data[2]__ to __data[9]__.
The DES key is:
```cpp
unsigned char DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
```
Then encode the 10-bytes-long data:
1. Regard __data[10]__ as a 80-bits-long data.
If __data[10]__ starts with `0x68` and `0x2A`, so the 80-bits-long data is `01011000 00101010......`
2. Divide the 80-bits-long data as 16 5-bits-long blocks.
If __data[10]__ starts with `0x68` and `0x2A`, so the 80-bits-long data is `01011`, `00000`, `10101`, `0....`, ...
3. So the value every block is less than 32. Map them by a encode-table:
```cpp
// Thanks for discoveries from @Wizr, issue #10
char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
```
Then you will get a 16-char-long string.
If __data[10]__ starts with `0x68` and `0x2A`, so after encoded, it should starts with `"N"`, `"A"`, `"V"`.
4. Divide the 16-char-long string to four 4-chars-long blocks, Then you get __snKey__.
## 3. Activation Process
1. Check whether __sn_Key__ that user inputs is legal.
2. After user clicks `Activate`, Navicat will start online activation first. If fails, user can choose offline activation.
3. Navicat will use the __snKey__ that user inputs and some information collected from user's machine to generate __Offline Activation Request Information__, then encrypt it by __Navicat Activation Public Key__ and return Base64-encoded string as __Request Code__.
4. In legal way, the __Request Code__ should be sent to Navicat official activation server by a Internet-accessible computer. And Navicat official activation server will return a legal __Activation Code__.
But now, we use keygen to play the official activation server's role.
1. According to the __Request Code__, Get `"DI"` value and `"K"` value.
2. Fill __Offline Activation Response Information__ with `"K"` value, name, organization name and `"DI"` value.
3. Encrypt __Offline Activation Response Information__ by __Navicat Activation Private Key__ and you will get 256-byte-long data.
4. Encode 256-byte-long data by Base64. The result is __Activation Code__.
5. Input __Activation Code__, then offline activation is done.
## 4. How to build
* Before you build keygen, you should make sure you have installed OpenSSL.
If you have `brew`, you can install it by `brew install openssl`.
```bash ```bash
$ brew install openssl
$ brew install rapidjson
```
* Clone `mac` branch and build keygen and patcher:
```bash
$ git clone -b mac https://github.com/DoubleLabyrinth/navicat-keygen.git
$ cd navicat-keygen $ cd navicat-keygen
$ make release $ make all
``` ```
* Build patcher if your Navicat version is or is after __12.0.24__. You will see two executable files in `bin/` folder:
```bash ```bash
$ cd navicat-patcher $ ls bin/
$ make release navicat-keygen navicat-patcher
``` ```
__NOTICE:__ ## 2. How to Use
For Navicat whose version is or is after __12.0.24__, if you want to use your own RSA key, please replace the content in `/navicat-patcher/main.c` 1. Build keygen and patcher.
```cpp 2. Backup your `Navicat Premium.app/Contents/MacOS/Navicat Premium` and all of your saved database connection configurations (with password).
const char pubkey[9][72] = {
"-----BEGIN PUBLIC KEY-----", 3. Remove all connections, if have, that Navicat saved in `Keychain.app`.
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxqkTcfbKw8ysVygePlcB",
"oUAhCF6oniyP13iDtu85ZsHwqw8PnMyTp6n6FnMN9YinleIAy6NFveBu/vshTN8S", You can find them by search with keyword `navicat`.
"oXbYyy5AqdZ8CQpfvuriO9UNfgV1l7SFdPPpruFAmOw+uzA3GawMsg3QNK/htqJe",
"b4xKHFS04xC2AueE2RTmk6tJcL8TEBfRG7DEYOHPjebKl1NQ3ZIu15U97cCPYKO2", 4. Use `navicat-patcher` to replace __Navicat Activation Public Key__.
"pWHzsb+Fr4Wj0DChLoxlXxaBcJ2ozogaq0tW2t4Aopvt9kRSuSK9HcgxICJM5ct4",
"naU91WFGWlw0+0JpiMIl5OnMbpak/5xQre9DL8zM8LjRy14I88txvXvhPEsWaYCO", ```
"1QIDAQAB", Usage:
"-----END PUBLIC KEY-----" navicat-patcher <navicat executable file> [RSA-2048 PrivateKey(PEM file)]
};
``` ```
with your own RSA public key before you build patcher. * `<navicat executable file>`: The path to Navicat executable file.
## 5. How to Use __This parameter must be specified.__
1. Build keygen.
2. Generate RSA-2048 private key and public key. __(Navicat Premium version < 12.0.24 ONLY)__ * `[RSA-2048 PrivateKey(PEM file)]`: The path to an RSA-2048 private key file.
__This parameter is optional.__ If not specified, `navicat-patcher` will generate a new RSA-2048 private key file `RegPrivateKey.pem` at current directory.
__Example:__
```bash
$ openssl genrsa -out 2048key.pem 2048
$ openssl rsa -in 2048key.pem -pubout -out rpk
``` ```
You will get two file: `2048key.pem` and `rpk`.
Now you do not need to generate RSA key. I've already prepared these two file:
* `rpk` file is in `navicat-patcher` folder.
* `2048key.pem` is in `navicat-keygen` folder.
3. For Navicat Premium version < 12.0.24:
* Replace `Navicat Premium.app/Contents/Resources/rpk` file by `rpk` file.
For Navicat Premium version >= 12.0.24:
* Backup your `Navicat Premium.app/Contents/MacOS/Navicat Premium` and __all of your saved database connection configurations (with password)__.
* Delete all of passwords in `Keychain.app` that is saved by Navicat.
* Run patcher:
```bash
$ cd navicat-patcher
$ ./navicat-patcher <your navicat executable file path>
```
Example:
```bash
$ cd navicat-patcher
$ ./navicat-patcher /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium $ ./navicat-patcher /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium
``` ```
* __Generate a self-signed code-sign certificate and always trust it. Then use `codesign` to re-sign `Navicat Premium.app`.__ __FOR Navicat Premium version < 12.0.24 ONLY:__
`navicat-patcher` will not modify target file. But you should use openssl to convert `RegPrivateKey.pem` to `rpk` file and replace
```
/Applications/Navicat Premium.app/Contents/Resources/rpk
```
by it.
If you don't know how to use openssl, here's an example:
```bash ```bash
$ openssl rsa -in RegPrivateKey.pem -pubout -out rpk
```
5. __Generate a self-signed code-sign certificate and always trust it.__
__Then use `codesign` to re-sign `Navicat Premium.app`.__
```
$ codesign -f -s "Your self-signed code-sign certificate name" <path to Navicat Premium.app> $ codesign -f -s "Your self-signed code-sign certificate name" <path to Navicat Premium.app>
``` ```
Note: "Your self - signed code - sign certificate name" is the name of Your certificate, not a path. __NOTICE:__
Example: "Your self - signed code - sign certificate name" is the name of your certificate, not path.
__Example:__
```bash ```bash
$ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium $ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/
``` ```
4. Then goto `navicat-keygen` folder and in Terminal: 6. Then use `navicat-keygen` to generate __snKey__ and __Activation Code__.
```
Usage:
navicat-keygen <RSA-2048 PrivateKey(PEM file)>
```
* `<RSA-2048 PrivateKey(PEM file)>`: The path to a RSA-2048 private key file.
__This parameter must be specified.__
__Example:__
```bash ```bash
$ ./navicat-keygen 2048key.pem ./navicat-keygen ./RegPrivateKey.pem
``` ```
You will get a __snKey__ and be asked to input your name and organization. You will be asked to select Navicat language and input major version number. After that an randomly generated __snKey__ will be given.
Just input and then you will be asked to input the request code. Now DO NOT CLOSE KEYGEN.
5. Open Navicat Premium, find and click `Registration`. Then input `Registration Key` by __snKey__ that keygen gave. Then click `Activate`. ```
Which is your Navicat Premium language?
0. English
1. Simplified Chinese
2. Traditional Chinese
3. Japanese
4. Polish
5. Spanish
6. French
7. German
8. Korean
9. Russian
10. Portuguese
6. Generally online activation will failed and Navicat will ask you do `Manual Activation`, just choose it. (Input index)> 1
(Input major version number, range: 0 ~ 15, default: 12)> 12
7. Copy your request code and paste it in keygen. Leave empty line to tell keygen that your input ends (in other words, type `Enter` at least twice). Serial number:
NAVK-MWQR-LNXV-886V
8. Then you will get activation code which looks like a Base64 string. Just copy it and paste it in Navicat `Manual Activation` window, then click `Activate`. If nothing is wrong, activation should be done successfully. Your name:
```
You can use this __snKey__ to activate your Navicat preliminarily.
Then you will be asked to input `Your name` and `Your organization`. Just set them whatever you want, but not too long.
```bash
Your name: DoubleLabyrinth
Your organization: DoubleLabyrinth
Input request code (in Base64), input empty line to end:
```
After that, you will be asked to input the request code. Now __DO NOT CLOSE KEYGEN__.
7. __Disconnect your network__ and open Navicat Premium.
Find and click `Registration`. Fill license key by __Serial number__ that the keygen gave and click `Activate`.
8. Generally online activation will fail and Navicat will ask you do `Manual Activation`, just choose it.
9. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends.
```bash
Your name: DoubleLabyrinth
Your organization: DoubleLabyrinth
Input request code (in Base64), input empty line to end:
q/cv0bkTrG1YDkS+fajFdi85bwNVBD/lc5jBYJPOSS5bfl4DdtnfXo+RRxdMjJtEcYQnvLPi2LF0
OB464brX9dqU29/O+A3qstSyhBq5//iezxfu2Maqca4y0rVtZgQSpEnZ0lBNlqKXv7CuTUYCS1pm
tEPgwJysQTMUZf7tu5MR0cQ+hY/AlyQ9iKrQAMhHklqZslaisi8VsnoIqH56vfTyyUwUQXrFNc41
qG5zZNsXu/NI79JOo7qTvcFHQT/k5cTadbKTxY+9c5eh+nF3JR7zEa2BDDfdQRLNvy4DTSyxdYXd
sAk/YPU+JdWI+8ELaa0SuAuNzr5fEkD6NDSG2A==
Request Info:
{"K":"NAVADHCNP2OIDV46", "DI":"Y2eJk9vrvfGudPG7Mbdn", "P":"MAC"}
Response Info:
{"K":"NAVADHCNP2OIDV46","DI":"Y2eJk9vrvfGudPG7Mbdn","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1537630251}
License:
oyoMYr9cfVGXeT7F1dqBwHsB/vvWj6SUL6aR+Kzb0lm5IyEj1CgovuSq+qMzFfx+
oHMFaGKFg6viOY2hfJcrO2Vdq0hXZS/B/Ie3jBS2Ov37v8e3ufVajaH+wLkmEpLd
xppCVLkDQjIHYR2IPz5s/L/RuWqDpEY4TPmGFF6q+xQMnqQA3vXPyG+JYMARXLru
Y1gCDLN30v3DpyOeqKmFjUqiHK5h8s0NYiH2OpMyaCpi12JsF23miP89ldQp3+SJ
8moo0cNGy7sFp2gX9ol2zVoo7qxfYlLl03f7CALJ6im0sx4yBsmlzFDdvpQUbXk8
YZ5rT4LML2Fx6Wgnnklb5g==
```
10. Finally, you will get __Activation Code__ which looks like a Base64 string. Just copy it and paste it in Navicat `Manual Activation` window, then click `Activate`. If nothing wrong, activation should be done successfully.
9. Finally, restore your database connection configurations if you have.

View File

@ -1,285 +1,184 @@
# Navicat Keygen # Navicat Keygen
这份repo将会告诉你Navicat是怎么完成离线激活的。 这份repo将会告诉你Navicat是怎么完成离线激活的。
## 1. 关键词解释 [注册机是怎么工作的?](HOW_DOES_IT_WORK.zh-CN.md)
* __Navicat激活公钥__ ## 1. 如何编译
这是一个2048位的RSA公钥Navicat使用这个公钥来完成相关激活信息的加密和解密 * 在编译之前,你应该确保你安装了`OpenSSL`和`rapidjson`
这个公钥储存在 __Navicat Premium.app/Contents/Resources/rpk__ 中,你可以用任何一种文本编辑器打开并查看它。这个公钥的具体内容为: 如果你有`brew`的话,你可以通过
> -----BEGIN PUBLIC KEY----- ```
> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I $ brew install openssl
> qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv $ brew install rapidjson
> a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
> R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
> WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
> YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
> awIDAQAB
> -----END PUBLIC KEY-----
如果您有相应的私钥并乐意公开的话欢迎联系我,我将非常感谢您的慷慨。
__注意__
__Navicat Premium for Mac 12.0.24__ 开始,公钥不再存储在 __Navicat Premium.app/Contents/Resources/rpk__ 中。事实上公钥放在了Navicat的二进制执行文件 __Navicat Premium.app/Contents/MacOS/Navicat Premium__ 中,你可以通过搜索`"-----BEGIN PUBLIC KEY-----"`来找到它。
* __请求码__
这是一个Base64编码的字符串代表的是长度为256字节的数据。这256字节的数据是 __离线激活请求信息____Navicat激活公钥__ 加密的密文。
* __离线激活请求信息__
这是一个JSON风格的字符串。它包含了3个Key`"K"`、`"DI"`和`"P"`,分别代表 __序列号__、__设备识别码__与你的电脑硬件信息相关__平台__ (其实就是操作系统类型)。
例如:
> {
> &nbsp;&nbsp;&nbsp;&nbsp;"K": "xxxxxxxxxxxxxxxx",
> &nbsp;&nbsp;&nbsp;&nbsp;"P": "Mac 10.13",
> &nbsp;&nbsp;&nbsp;&nbsp;"DI": "xxxxxxxxxxxxxxxxxxxx"
> }
* __激活码__
这是一个Base64编码的字符串代表的是长度为256字节的数据。这256字节的数据是 __离线激活回复信息____Navicat激活私钥__ 加密的密文,目前我们不知道官方的 __Navicat激活私钥__
* __离线激活回复信息__
__离线激活请求信息__ 一样它也是一个JSON风格的字符串。但是它包含5个Key分别为`"K"`、`"N"`、`"O"`、`"T"`和`"DI"`.
`"K"``"DI"` 的意义与 __离线激活请求信息__ 中的相同且Value必须与 __离线激活请求信息__ 中的相同。
`"N"`、`"O"`、`"T"` 分别代表 __注册名__、__组织__、__授权时间__。__注册名__ 和 __组织__ 的值类型为字符串__授权时间__ 的值类型可以为字符串或整数(感谢@Wizr在issue #10的报告)。
与Windows版本不同的是`"T"`是必须的,且代表的时间必须位于当前时间-1 ~ +4天之内。下面是一个 __离线激活回复信息__ 的示例:
> {
> &nbsp;&nbsp;&nbsp;&nbsp;"DI" : "xxxxxxxxxxxxxxxxxxxx",
> &nbsp;&nbsp;&nbsp;&nbsp;"T" : "1515770827.925012",
> &nbsp;&nbsp;&nbsp;&nbsp;"K" : "xxxxxxxxxxxxxxxx",
> &nbsp;&nbsp;&nbsp;&nbsp;"N" : "DoubleLabyrinth",
> &nbsp;&nbsp;&nbsp;&nbsp;"O" : "Shadow"
> }
* __序列号__
这是一个被分为了4个部分的字符串其中每个部分都是4个字符长。
__序列号__ 是通过10个字节的数据来生成的。为了表达方便我用 __data[10]__ 来表示这10个字节。
1. __data[0]____data[1]__ 必须分别为 `0x68``0x2A`
_`Navicat产品类型变化时这两个值可能会变。目前暂未确认。`_
2. __data[2]__、__data[3]__ 和 __data[4]__ 可以是任意字节,你想设成什么都行。
3. __data[5]____data[6]__ 与你Navicat的语言有关值如下
| 语言类型 | data[5] | data[6] | 发现者 |
|------------|-----------|-----------|-----------------|
| English | 0xAC | 0x88 | |
| 简体中文 | 0xCE | 0x32 | |
| 繁體中文 | 0xAA | 0x99 | |
| 日本語 | 0xAD | 0x82 | @dragonflylee |
| Polski | 0xBB | 0x55 | @dragonflylee |
| Español | 0xAE | 0x10 | @dragonflylee |
| Français | 0xFA | 0x20 | @Deltafox79 |
| Deutsch | 0xB1 | 0x60 | @dragonflylee |
| 한국어 | 0xB5 | 0x60 | @dragonflylee |
| Русский | 0xEE | 0x16 | @dragonflylee |
| Português | 0xCD | 0x49 | @dragonflylee |
根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知这两个字节为 __Product Signature__
4. __data[7]__ 指示这是 __commercial license__ 还是 __non-commercial license__
对于 __Navicat 12__: `0x65`__commercial license__`0x66`是 __non-commercial license__
对于 __Navicat 11__: `0x15`__commercial license__`0x16`是 __non-commercial license__
_`Navicat产品类型变化时这两个值可能会变。目前暂未确认。`_
根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知__commercial license__ 是 __Enterprise License__ __non-commercial license____Educational License__
5. __data[8]__ 的高4位代表 __版本号__。低四位未知,但可以用来延长激活期限,可取的值有`0000`和`0001`。
对于 __Navicat 12__: 高4位必须是`1100`,为`12`的二进制形式。
对于 __Navicat 11__: 高4位必须是`1011`,为`11`的二进制形式。
6. __data[9]__ 目前暂未知,但如果你想要 __not-for-resale license__ 的话可以设成`0xFD`、`0xFC`或`0xFB`。这个值一定不能是`0x00`,其他值随便。
根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知:
* `0xFB`__Not-For-Resale-30-days__ license.
* `0xFC`__Not-For-Resale-90-days__ license.
* `0xFD`__Not-For-Resale-365-days__ license.
* `0xFE`__Not-For-Resale__ license.
* `0xFF`__Site__ license.
-----------------
之后Navicat使用 __ECB__ 模式的 __DES__ 算法来加密 __data[10]__ 的后8字节也就是 __data[2]____data[9]__ 的部分。
相应的DES密钥为
```cpp
unsigned char DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
``` ```
之后编码 __data[10]__ 来完成它们的安装。
1. 将 __data[10]__ 视作为一个80位长的数据。 * Clone `mac` 分支并编译keygen和patcher
如果 __data[10]__ 以`0x68`和`0x2A`开始的话80位长的数据应该为`01011000 00101010......`
2. 将80位长的数据分为16个5位长的块。
如果 __data[10]__ 以`0x68`和`0x2A`开始的话16个5位长的块应为`01011`、 `00000`、`10101`、`0....`
3. 这样每一块的值就会小于32。将它们通过下表编码
```cpp
// Thanks for discoveries from @Wizr, issue #10
char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
```
你就会得到一个16字节的字符串。
如果 __data[10]__ 以`0x68`和`0x2A`开始的话,编码之后应该以`"N"`、`"A"`、`"V"`打头。
4. 将16字节的字符串分成4个4字节的小块然后用`"-"`连接就可以得到 __序列号__
## 3. 激活过程
1. 检查用户输入的 __序列号__ 是否合法。
2. 在用户点击了`激活`按钮之后Navicat会先尝试在线激活。如果失败用户可以选择离线激活。
3. Navicat会使用用户输入的 __序列号__ 以及从用户电脑收集来的信息生成 __离线激活请求信息__,然后用 __Navicat激活公钥__ 加密并将密文用Base64编码最后得到 __请求码__
4. 正常流程下__请求码__ 应该通过可访问Internet的电脑发送给Navicat的官方激活服务器。之后Navicat的官方激活服务器会返回一个合法的 __激活码__
但现在我们使用注册机来扮演官方激活服务器的角色只是Navicat软件里的激活公钥得换成自己的公钥
1. 根据 __请求码__, 获得`"DI"`值和`"K"`值。
2. 用`"K"`值、用户名、组织名和`"DI"`值填写 __离线激活回复信息__
3. 用自己的2048位RSA私钥加密 __离线激活回复信息__你将会得到256字节的密文。
4. 用Base64编码这256字节的密文就可以得到 __激活码__
5. 在Navicat软件中填入 __激活码__ 即可完成离线激活。
## 4. 如何编译
* 在编译之前你应该确保你安装了OpenSSL。如果你有`brew`的话,你可以通过`brew install openssl`来完成OpenSSL的安装。
```bash ```bash
$ git clone -b mac https://github.com/DoubleLabyrinth/navicat-keygen.git
$ cd navicat-keygen $ cd navicat-keygen
$ make release $ make all
``` ```
* 如果你的Navicat版本号等于或大于12.0.24你需要编译patcher。 编译完成后你会在 `bin/` 文件夹下看到两个可执行文件:
```bash ```bash
$ cd navicat-patcher $ ls bin/
$ make release navicat-keygen navicat-patcher
``` ```
注意: ## 2. 如何使用这个Keygen
对于Navicat版本号等于或大于12.0.24的如果你想要使用自己的RSA密钥请在编译patcher之前替换掉`navicat-patcher/main.c`里下面的内容 1. 编译好keygen和patcher。
```cpp 2. 备份好 `Navicat Premium.app/Contents/MacOS/Navicat Premium` 以及Navicat中所有已保存的数据库连接包括密码
const char pubkey[9][72] = {
"-----BEGIN PUBLIC KEY-----", 3. 移除所有Navicat在 `Keychain.app` (即钥匙链)中保存的连接,如果有的话。
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxqkTcfbKw8ysVygePlcB",
"oUAhCF6oniyP13iDtu85ZsHwqw8PnMyTp6n6FnMN9YinleIAy6NFveBu/vshTN8S", 你可以通过搜索关键词 `navicat` 来找到它们。
"oXbYyy5AqdZ8CQpfvuriO9UNfgV1l7SFdPPpruFAmOw+uzA3GawMsg3QNK/htqJe",
"b4xKHFS04xC2AueE2RTmk6tJcL8TEBfRG7DEYOHPjebKl1NQ3ZIu15U97cCPYKO2", 4. 使用`navicat-patcher`替换掉公钥:
"pWHzsb+Fr4Wj0DChLoxlXxaBcJ2ozogaq0tW2t4Aopvt9kRSuSK9HcgxICJM5ct4",
"naU91WFGWlw0+0JpiMIl5OnMbpak/5xQre9DL8zM8LjRy14I88txvXvhPEsWaYCO", ```
"1QIDAQAB", Usage:
"-----END PUBLIC KEY-----" navicat-patcher <navicat executable file> [RSA-2048 PrivateKey(PEM file)]
};
``` ```
为你自己的RSA公钥 * `<navicat executable file>`: Navicat可执行文件的路径。
## 4. 如何使用这个Keygen __这个参数必须指定。__
1. 编译好keygen * `[RSA-2048 PrivateKey(PEM file)]`: RSA-2048私钥文件的路径。
2. 生成2048位的RSA密钥对。__仅限Navicat Premium版本号小于12.0.24__ __这个参数是可选的。__ 如果没有指定,`navicat-patcher`将会在当前目录下生成一个新的RSA-2048私钥文件`RegPrivateKey.pem`。
__例如__
```bash
$ openssl genrsa -out 2048key.pem 2048
$ openssl rsa -in 2048key.pem -pubout -out rpk
``` ```
你会得到两个文件:`2048key.pem`和`rpk`。
__现在你们可以不用生成RSA密钥了我已经准备好了这两个文件__
* `rpk`文件在`navicat-patcher`文件夹中。
* `2048key.pem`在`navicat-keygen`文件夹中。
3. 对于Navicat Premium版本 < 12.0.24的
* 用生成或提供的`rpk`文件替换掉`Navicat Premium.app/Contents/Resources/rpk`。
对于Navicat Premium版本 >= 12.0.24的:
* 备份好`Navicat Premium.app/Contents/MacOS/Navicat Premium`文件__以及Navicat中所有的数据库连接配置信息包括密码__。
* 删掉`Keychain.app`中所有由Navicat保存的密码。
* 运行patcher
```bash
$ cd navicat-patcher
$ ./navicat-patcher <your navicat executable file path>
```
例如:
```bash
$ cd navicat-patcher
$ ./navicat-patcher /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium $ ./navicat-patcher /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium
``` ```
* __生成一个自签名的代码签名证书并在`Keychain.app`中总是信任它。然后使用`codesign`命令对`Navicat Premium.app`进行重签名。这一步非常重要。__ __仅对 Navicat Premium 版本 < 12.0.24 的说明__
如果你的Navicat版本小于12.0.24,那么`navicat-patcher`将不会修改目标文件。但你必须使用openssl将`RegPrivateKey.pem`转化为`rpk`文件,并用转化得到的文件替换
```
/Applications/Navicat Premium.app/Contents/Resources/rpk
```
如果你不知道怎么转化,这是一份样例:
```bash
$ openssl rsa -in RegPrivateKey.pem -pubout -out rpk
```
5. __生成一份自签名的代码证书并总是信任该证书。这一步非常重要。__
__然后用`codesign`对`Navicat Premium.app`重签名。__
```bash ```bash
$ codesign -f -s "Your self-signed code-sign certificate name" <path to Navicat Premium.app> $ codesign -f -s "Your self-signed code-sign certificate name" <path to Navicat Premium.app>
``` ```
注意:"Your self-signed code-sign certificate name"是你证书的名字,不是路径。 __注意__
例如: "Your self-signed code-sign certificate name"是你证书的名字,不是路径。
__例如__
```bash ```bash
$ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/Contents/MacOS/Navicat\ Premium $ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/
``` ```
4. 接下来,打开`Terminal.app`,并定位到`navicat-keygen`文件夹: 6. 接下来使用`navicat-keygen`来生成 __序列号____激活码__
```
Usage:
navicat-keygen <RSA-2048 PrivateKey(PEM file)>
```
* `<RSA-2048 PrivateKey(PEM file)>`: RSA-2048私钥文件的路径。
__这个参数必须指定。__
__例如__
```bash ```bash
$ ./navicat-keygen 2048key.pem ./navicat-keygen ./RegPrivateKey.pem
``` ```
接下来你会被要求输入Navicat的语言版本然后得到一个 __序列号__同时keygen会要求你输入用户名和组织名。 你会被要求选择Navicat的语言以及输入主版本号。之后会随机生成一个 __序列号__
直接填写,之后你会被要求填写你得到的 __请求码__。注意此时 __不要关闭Terminal__.
5. 打开Navicat Premium。找到`注册`按钮并点击在弹出的窗口中填入keygen给你的 __序列号__。然后点击`激活`按钮。 ```
Which is your Navicat Premium language?
0. English
1. Simplified Chinese
2. Traditional Chinese
3. Japanese
4. Polish
5. Spanish
6. French
7. German
8. Korean
9. Russian
10. Portuguese
6. 一般来说在线激活肯定会失败这时候Navicat会询问你是否`手动激活`,直接选吧。 (Input index)> 1
(Input major version number, range: 0 ~ 15, default: 12)> 12
7. 在`手动激活`窗口你会得到一个请求码复制它并把它粘贴到keygen里。最后别忘了连按至少两下回车结束输入。 Serial number:
NAVK-MWQR-LNXV-886V
8. 如果不出意外你会得到一个看似用Base64编码的 __激活码__。直接复制它并把它粘贴到Navicat的`手动激活`窗口,最后点`激活`按钮。如果没什么意外的话应该能成功激活。 Your name:
```
9. 最后,如果你备份了数据库连接配置信息,那么恢复它把。 你可以使用这个 __序列号__ 暂时激活Navicat。
接下来你会被要求输入`用户名`和`组织名`;请随便填写,但不要太长。
```bash
Your name: DoubleLabyrinth
Your organization: DoubleLabyrinth
Input request code (in Base64), input empty line to end:
```
之后你会被要求填入请求码。注意 __不要关闭注册机__.
7. __断开网络__ 并打开Navicat。找到`注册`窗口,填入注册机给你的序列号。然后点击`激活`按钮。
8. 一般来说在线激活肯定会失败这时候Navicat会询问你是否`手动激活`,直接选吧。
9. 在`手动激活`窗口你会得到一个请求码复制它并把它粘贴到keygen里。最后别忘了连按至少两下回车结束输入。
```bash
Your name: DoubleLabyrinth
Your organization: DoubleLabyrinth
Input request code (in Base64), input empty line to end:
q/cv0bkTrG1YDkS+fajFdi85bwNVBD/lc5jBYJPOSS5bfl4DdtnfXo+RRxdMjJtEcYQnvLPi2LF0
OB464brX9dqU29/O+A3qstSyhBq5//iezxfu2Maqca4y0rVtZgQSpEnZ0lBNlqKXv7CuTUYCS1pm
tEPgwJysQTMUZf7tu5MR0cQ+hY/AlyQ9iKrQAMhHklqZslaisi8VsnoIqH56vfTyyUwUQXrFNc41
qG5zZNsXu/NI79JOo7qTvcFHQT/k5cTadbKTxY+9c5eh+nF3JR7zEa2BDDfdQRLNvy4DTSyxdYXd
sAk/YPU+JdWI+8ELaa0SuAuNzr5fEkD6NDSG2A==
Request Info:
{"K":"NAVADHCNP2OIDV46", "DI":"Y2eJk9vrvfGudPG7Mbdn", "P":"MAC"}
Response Info:
{"K":"NAVADHCNP2OIDV46","DI":"Y2eJk9vrvfGudPG7Mbdn","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1537630251}
License:
oyoMYr9cfVGXeT7F1dqBwHsB/vvWj6SUL6aR+Kzb0lm5IyEj1CgovuSq+qMzFfx+
oHMFaGKFg6viOY2hfJcrO2Vdq0hXZS/B/Ie3jBS2Ov37v8e3ufVajaH+wLkmEpLd
xppCVLkDQjIHYR2IPz5s/L/RuWqDpEY4TPmGFF6q+xQMnqQA3vXPyG+JYMARXLru
Y1gCDLN30v3DpyOeqKmFjUqiHK5h8s0NYiH2OpMyaCpi12JsF23miP89ldQp3+SJ
8moo0cNGy7sFp2gX9ol2zVoo7qxfYlLl03f7CALJ6im0sx4yBsmlzFDdvpQUbXk8
YZ5rT4LML2Fx6Wgnnklb5g==
```
10. 如果不出意外你会得到一个看似用Base64编码的激活码。直接复制它并把它粘贴到Navicat的`手动激活`窗口,最后点`激活`按钮。如果没什么意外的话应该能成功激活。

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEujCCAqICCQD77KLUs6JamDANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRh
Y3RpdmF0ZS5uYXZpY2F0LmNvbTAeFw0xODEyMTcwODEzMDNaFw0yODEyMTQwODEz
MDNaMB8xHTAbBgNVBAMMFGFjdGl2YXRlLm5hdmljYXQuY29tMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEAvGZxEqPRuMtzCS3G+PzLsmxmBTE4VNiSEeDp
MZcZOQo7egwCkZOrh3u32tJwFp0p++Gv3QiTQ709vT3h0d54uLm4oZ3Ov/X7xFQ7
Jy2qz59sil4hZTnjD9UTb6zGP2ww7LYLAkngX8+heECiJtPGckBMwfM91azR478w
BZS0Kwv2b+jlN9htzXfn00aRXhGc0x5LMTibW7ufwYjPpugmdq5JsK2pS8l75cbJ
qxZgC38l+WKrvyfTcoqzkw0fcaZIabmVsRWy2qu6O4UQ7LeII59Cl397HmjyUvis
GWdhpdQvMYpOczWnARm2+PBxMkK3nOEAvAa5LxHEf1mhMdD5t50i0Rw8rGwsfIAq
btAUDPKYhLlC7Hr+3Bijrzrq8Q9L+znlKgeCC/9eJqv9KGTLBjuB0y5MFD48hCob
Q18sZdaVXIDZD7xil/GtFayho7CKdNL/m2O/1Ek03OJgqCSXPP243nKnSKWhOfxT
vIsOaPBThdYGLp+jnbHPIRSb5TcXiTYh7dtqg9QEEzclPMXPTZEwA+TVKbQekzX3
Mv+CsJ9xzHxKX0/tY7IH3++MeEeOY3s2sNQjayS+3YdsCEYBtYhFcd+SoUyk2BXu
TpnLIkIQA9j6W+mpAVGvEF4ZGB55Mvic20gByVf4RzrT8Ws9gguWJmffNyD7qPCI
d00r6hECAwEAATANBgkqhkiG9w0BAQsFAAOCAgEApX8giNz2VERIxpcnVphtOfqA
37HJcDvmDxlnpSSUvVrCFTM+YD/uAuecE1GmQIxuwRbSb63s/vRetboHZ7A+PSkr
BwR6xkIlJwOVwaiZXs10HmP5Nz8aele+V83fCAWY/mtxn6EF0Pi7sxTCLg5PSdR+
P7QRr6/vZPoyFd8TcU5c3VFMqa/Z3Sdhmx6XWYs2COOFTfOo4k8sXYIwGVyWOt4/
XHqpouhpeF+/oxlaOo/fnJY2IcGsao3eyWevZMCrPsf/MQqeHJhsTG6yLfU7pldS
s25LbXNzF8mjfQQLWRCKCGgUqRUGjJBsIyg8X9C9e0MKL+/W7KDlcJA8VyJIjFmK
k1aEyKfLzNTkAZtzGutYL4x0lAJBxfYWbm9WVtrU2QMkqkkjru1buPRyw0vu3QgO
hizSWkU1JqERTADsRN/u8VTddmdLy0Bs44q/btnBO7EU86+lg9O1mPGpD4KmVB2e
2XWzfQ++059sA5vT2LtaQuvFG4nulnQtNeboVyzNam+zff7vrHXrszFk/0YFTw0n
bS4YHVlpYDIH7KY47L42sj/48LjmDgQ0AExFkkzPCFjaPNxIm5vsg9nm6H3iZOjx
9zoMl9EHIwfJqNxw0fsdATBJbW/MsWJ+DHiiyQVPisWC11BmUqKWh/05bLHBhKEB
7wKbi9kxjRflM5+An+g=
-----END CERTIFICATE-----

View File

@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC8ZnESo9G4y3MJ
Lcb4/MuybGYFMThU2JIR4Okxlxk5Cjt6DAKRk6uHe7fa0nAWnSn74a/dCJNDvT29
PeHR3ni4ubihnc6/9fvEVDsnLarPn2yKXiFlOeMP1RNvrMY/bDDstgsCSeBfz6F4
QKIm08ZyQEzB8z3VrNHjvzAFlLQrC/Zv6OU32G3Nd+fTRpFeEZzTHksxOJtbu5/B
iM+m6CZ2rkmwralLyXvlxsmrFmALfyX5Yqu/J9NyirOTDR9xpkhpuZWxFbLaq7o7
hRDst4gjn0KXf3seaPJS+KwZZ2Gl1C8xik5zNacBGbb48HEyQrec4QC8BrkvEcR/
WaEx0Pm3nSLRHDysbCx8gCpu0BQM8piEuULsev7cGKOvOurxD0v7OeUqB4IL/14m
q/0oZMsGO4HTLkwUPjyEKhtDXyxl1pVcgNkPvGKX8a0VrKGjsIp00v+bY7/USTTc
4mCoJJc8/bjecqdIpaE5/FO8iw5o8FOF1gYun6Odsc8hFJvlNxeJNiHt22qD1AQT
NyU8xc9NkTAD5NUptB6TNfcy/4Kwn3HMfEpfT+1jsgff74x4R45jezaw1CNrJL7d
h2wIRgG1iEVx35KhTKTYFe5OmcsiQhAD2Ppb6akBUa8QXhkYHnky+JzbSAHJV/hH
OtPxaz2CC5YmZ983IPuo8Ih3TSvqEQIDAQABAoICAQCZ67v/tZV3O8itPBgufiJR
kFw9a7wRHashLjZ2zHLP9jsneD50/0eJdht8jKcE4rxVTEqo9AOUuTyCqtce5nqM
uTdN2yb6EBb0jLiTRq0h2acM2ODB2exXmAa8G1UQpezGx+fwo7jLrk/Gdk/EFwsv
drb/UiI3u2zze9TZCme0L57USVtUJ991mbvuEd5cE1mj5kEaYpwS8xQPQx4bx9j8
52HPFeKVx3QRAzrqK2qmmefFesbEct4+23Dg+DyzVl4c+oGA3zBzlQ+e5xUvwLZa
HBiEu6Mz8hvgi8fXLW5K1DMGb2+ukqvxqzeg5JgmrvliEzXNaFTNTt/SB8E5ePk6
kQ6K4+2rvBFN6Bho/3j649ZbYQiEQ7LL2nm6kLT2QkFcNGuj+PPv97XbR/sUKnG/
0FAKisgvNEaVGz6GV1kxqZ0SZkohkSknGODbh03KxKEykUhx05DkDJ9z4vhguOaF
tOu+77FmxRpWCyUEzMvXXHuNyryW1k6WGZU7N142gB1HxnMB1OsP9nqTELmHSuh8
0z8zgMH/CNVPD6JGLQWTuDjI99oFwuI4eKFYcKqrvquO5KBPOfu65l0siE8jnGgI
f9JwkDAMqB2Ho51m241H9XPH1704Y3a2Ew7NnWbZvCKgtmbGw8doIbTur+gKCI0+
LqaFyLu35g6Z1jVfijeW7QKCAQEA7bEjK6MjrgYsucx1nnzQ+44SNSGnsTnFLK/P
WXYA5yxIUT9eabJf/VwtN0NGoTMmSN8UHFtvsyvH01TQpToUy6jU82O0bEtpDYvE
qKvp1w224+c5TCOvzq4kyBRtAito5BFwwLWBCMZ9k4+GADw4MDNgr1Hyt94a97JD
uPLbhNOJcapxNV+9nAEN61d3jhvR6YMVJ0npnnqwub/rNv7VJhyBz35p8i1nTkve
6YHe8uMvN/TXgryaoP1igrvO9T5xrVlv7nQnzNBjcRJqr99sYMdZ2AOTEQnqiXnU
kUME15CtDgdEarlkpwNAuQfq/VxOpMcEkZw4zhvAzXAAwR89swKCAQEAyulbpy+7
hTl14RBV9Y2kM25XCWRlN9GNLvfN8vfC9rix98pa+SWg9wawUZPnckZjM3vgOUpm
fZVZocBCK0cT5Fm3wUPX3dmNXhlPj6t+HukXto61N9sQoliz0RYzOGFOlT2+1bYF
pEKQW5MnRGe7PIwYBUbdsm9nbO7XflEz+F5h3pZmnMWXQVZdMTyQrMKnsifh5y+T
ls2Bboy2PLpquEQH2FWbPHaaLzMLy6wD5IAR+c/rFWIk0jl8BlvMOs3sPYPxXgIO
TdAWOCOSlymHbB3tot1lUAWfxjNO7vx0EB5mAG1kl5dU2IVzCU1o2PQUCFph9hQF
FxfFWF3h8Iy/KwKCAQBN2olkj6juJUOdD+Uupj9lReGc2Ystt3hn7KYD7WzaS1zc
j98Cy9Q5DmoZAcYuoqxVjgvuRdzOWPa4t70ngWCyXHhzyCKengyozTD69mQ/dlSD
TYcs8ztwfQW7K0WRQlq26Sd/V3QORcV4AbiodxEIaKuwgSz5IJzAqPkZqSJR3V/V
hThTfSqD4KyIezvDpkUZMUBKgCEvMYyVKtGGT+3M7+Vs60EUe96sfJlk7o7yC4u5
zk6Qmeoj9RrBK3bcSTm+x96rwgwPP+pybM/ZD1tWT7WT1YfkOtVBdlAzzDnXyaGF
SMgWFOds9NnMQVWs6wEvY6iRcRQCHoEGSTtWBj+VAoIBAH/9wMVtk3BOtW94+W/B
n5CdiiMvg7U45kB5KRyYcg9ko0W1C7tao/UN8AwVKonnok1oVyBsMMgIfBhOiK55
0C9a9FDhNXoH72cqugfa2dwvlbievep0sUJmh05dHYPzxTjYUNbHUTmyjxJkeQFq
GS3xBHnXoKIcKXYKFj3khM6m4j8gSaub63PYQUJ8fJms01DKXXGRjhwylvEfqxgV
UpnWt8ga/6ec/FbHcSpQ38ezjSMxpoy/cpB0mCn+n418NQb8gjSVQWaN2mYg8ieN
l5F4M1xnIK/gTE8eMC0Ja0B7nEWquyfv2iIV02FLxdYL59L5CA/LwwY1BVNfLNSq
Qy8CggEBAMn9Dco7uBUgE4M9EQzic9oWlrsxno+XklhRtMkWhQfdbFJbZIRs7rXO
SSx6en7IBp7L/Xqb1TS+u32sdnjUqnqCXJUlkgn5IFwEoLCFlHFvgE41hnmhlRr/
xgXsPYozKDq65ZdZE4wuWXTMqbCZTxI1bF2MZ0Jvi5GePLQaVhpbQFsNFskjYYcg
2VGaFWlDQtc2luTuvHiHsMwXx9kC4sWE6k01Nr60c8oRbVEM+UeJ1bspuk2TtLU3
cmGBdOGiqYxvbKU8+ROeimbCMwtf5lA0zYrXTW8TnZBibcv+FhFrqtJz6w54h1NR
6VJ+97E5OdzZqH2XTdf9Fi7c/WjxYCg=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,97 @@
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import sys, ssl, base64, json, time, gzip
from OpenSSL import crypto
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
openssl_lib = crypto._lib
def RSAPublicEncrypt(key, msg: bytes):
enc = bytes((key.key_size + 7) // 8)
length = openssl_lib.RSA_public_encrypt(len(msg), msg, enc, key._rsa_cdata, openssl_lib.RSA_PKCS1_PADDING)
if (length == -1):
raise ValueError('Public encrypt failure.')
return enc[:length]
def RSAPublicDecrypt(key, enc: bytes):
msg = bytes((key.key_size + 7) // 8)
length = openssl_lib.RSA_public_decrypt(len(enc), enc, msg, key._rsa_cdata, openssl_lib.RSA_PKCS1_PADDING)
if (length == -1):
raise ValueError('Public decrypt failure.')
return msg[:length]
def RSAPrivateEncrypt(key, msg: bytes):
enc = bytes((key.key_size + 7) // 8)
length = openssl_lib.RSA_private_encrypt(len(msg), msg, enc, key._rsa_cdata, openssl_lib.RSA_PKCS1_PADDING)
if (length == -1):
raise ValueError('Private encrypt failure.')
return enc[:length]
def RSAPrivateDecrypt(key, enc: bytes):
msg = bytes((key.key_size + 7) // 8)
length = openssl_lib.RSA_private_decrypt(len(enc), enc, msg, key._rsa_cdata, openssl_lib.RSA_PKCS1_PADDING)
if (length == -1):
raise ValueError('Private decrypt failure.')
return msg[:length]
def LoadKey(path: str):
with open(path, 'rb') as f:
key = serialization.load_pem_private_key(f.read(), None, default_backend())
return key
class NavicatActivateServerHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
self.wfile.write('This is an activation server for Navicat.'.encode())
def do_POST(self):
print('-' * 32)
print(self.headers)
body = self.rfile.read(int(self.headers['Content-Length'], 0))
print('Receive a POST request from %s:%d -->' % self.client_address)
print(body)
if (body.startswith(b'input:')):
enc = body[6:]
try:
snKey = RSAPrivateDecrypt(prikey, enc).decode()
print('snKey = %s' % snKey)
license = {}
license['K'] = snKey
license['DI'] = 'fjavsmSJmOssbuo4Ns9H'
license['N'] = 'DoubleLabyrinth'
license['O'] = 'DoubleLabyrinth'
license['T'] = 1545208026
license['M'] = 'MMMMMMMM'
license['FA'] = 'FAFAFAFA'
license = json.dumps(license).encode()
print(license)
data = gzip.compress(RSAPrivateEncrypt(prikey, license))
self.send_response(200)
self.send_header('Vary', 'Accept-Encoding')
self.send_header('Content-Encoding', 'gzip')
self.send_header('Content-Type', 'text/html; charset=UTF-8')
self.send_header('Content-Length', len(data))
self.end_headers()
self.wfile.write(data)
print('sent...')
print()
except:
self.send_response(404)
self.end_headers()
pass
IP = '0.0.0.0'
Port = 443
prikey = LoadKey(sys.argv[1])
httpd = HTTPServer((IP, Port), NavicatActivateServerHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
keyfile = 'cert-key.pem',
certfile = 'cert-crt.pem',
server_side = True)
httpd.serve_forever()

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAxqkTcfbKw8ysVygePlcBoUAhCF6oniyP13iDtu85ZsHwqw8P
nMyTp6n6FnMN9YinleIAy6NFveBu/vshTN8SoXbYyy5AqdZ8CQpfvuriO9UNfgV1
l7SFdPPpruFAmOw+uzA3GawMsg3QNK/htqJeb4xKHFS04xC2AueE2RTmk6tJcL8T
EBfRG7DEYOHPjebKl1NQ3ZIu15U97cCPYKO2pWHzsb+Fr4Wj0DChLoxlXxaBcJ2o
zogaq0tW2t4Aopvt9kRSuSK9HcgxICJM5ct4naU91WFGWlw0+0JpiMIl5OnMbpak
/5xQre9DL8zM8LjRy14I88txvXvhPEsWaYCO1QIDAQABAoIBAHsTYKKoPPKVKOhO
SH5itPXLnytqSZjFDtB1L1T0XGGXIZ04lXmVHJ0xJ2klGq7VXM302H1Qx/JcyydZ
OkY/pXE3ChTGsPUHloybSrojFsnuso8ynqnAAzZzroDTuIuFQVADDP/woWTmPemG
VZYqfcsp5PgsWmyae8jM2ncLBdbRnSCtQ0FqMZPxHnazxoxh43fIisdZiHVretDR
cHR0MKig8ggetpRCPv8prHvYjllmnp+vFPNbI8DD6LUho0gtsKnGG+4k5NCUArrH
gfNK7ov/wsBe991XgC+tG/Xa8GgQJjSXr6qxwI6jPJ9d8EDHi31pqOWggJz0iPUL
U/5uNoECgYEA9U+ykujylIn7WPDcoNuXXk+xb5J+7p0En1dnFPDWEqM21rHfYHpo
X8ikfW63PSYp/472BLhum3TnjbgBQy8Uu80uGXWKCTCmnguMZVAw0yJn4bwCKmUM
hI8eaB231O3pz0AeoKfjj3Y25c6EF+P0wixO95qNj3gZs8fRE1o2BoUCgYEAz1EE
AHHqNus/2iGVS/OD0cGcZBzBgz790o1YHvaAZWTB8J4Q55Z8NnbxHKPk+1FRik2c
3iqNhDtfee/9j9QkNookjP6JqliKLj6AHAzqmKAviMN/akQHO9vYrKVrgGXts9X4
k5Rn3Wyb1R9xSg9timalZ6HXsG0fqMKlQimroBECgYEAvfCPnCCyc0DDVQJqUkK0
2VlrUbBkh+0b/yR1tEkl+BhYBbSD+PfOiRuaAWUQjFBYhH/1DYKgYc4K3Dh1Acga
ja842o2f66231PesJWYJ5/Dj7mzcYGcNYjZOnN8lz8DHIKnNDSfxkss9hoJ41Oa0
RQR5wE3y/ftIK2O0Bv/6hOECgYEAhzh2fbvPYobKnQIumN16a0P3N60x4uMhN0YN
5hQt5vRzMvhGWmdjGTs3RND5t5pwrt60pIvoxgjb23LXVgqunAw6juQu8rbRWhxV
/vXFxksk9fVdk/hPJ5Hbn7A9j//rrNKxUcEIhfojIj1h7UZm0lDlLdT4FBD5B1/9
3kvDr6ECgYEAu/OS+6ApNTH/LhSeeqw9jMtxJwu560WOzSsDk8LSlSDCZHwREwVU
M+uBWShYTDlPn6Bpe15dNHghvxFnZi12pS57l5AgD4J8AW4FEveDuIjj7BOexNFa
nB099pHr6MUM5s8zmw+9Z4Rc7gk8bwTyFAwMFf8ZYSyl4WIHQfTbhx0=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,41 @@
#pragma once
#include <openssl/crypto.h>
#include <openssl/des.h>
#include <memory.h>
class DESCipher {
private:
DES_cblock _Key;
DES_key_schedule _Schedule;
public:
DESCipher() noexcept : _Key{}, _Schedule{} {}
void SetKey(const void* pKey) noexcept {
memcpy(&_Key, pKey, sizeof(_Key));
DES_set_odd_parity(&_Key);
DES_set_key(&_Key, &_Schedule);
}
void Clear() noexcept {
OPENSSL_cleanse(&_Key, sizeof(_Key));
OPENSSL_cleanse(&_Schedule, sizeof(_Schedule));
}
void EncryptBlock(void* pBuffer) noexcept {
DES_cblock block;
DES_ecb_encrypt(reinterpret_cast<const_DES_cblock*>(pBuffer), &block, &_Schedule, DES_ENCRYPT);
memcpy(pBuffer, &block, sizeof(block));
}
void DecryptBlock(void* pBuffer) noexcept {
DES_cblock block;
DES_ecb_encrypt(reinterpret_cast<const_DES_cblock*>(pBuffer), &block, &_Schedule, DES_DECRYPT);
memcpy(pBuffer, &block, sizeof(block));
}
~DESCipher() noexcept {
Clear();
}
};

82
navicat-keygen/Helper.cpp Normal file
View File

@ -0,0 +1,82 @@
#include "Helper.hpp"
namespace Helper {
//
// https://stackoverflow.com/questions/5288076/base64-encoding-and-decoding-with-openssl
// Thanks Omnifarious
// OpenSSL's base64 API is too dreadful to use
//
static const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char b64_reverse_table[128] = {
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64
};
std::string base64_encode(const std::vector<uint8_t>& bindata) {
using bytes = std::vector<uint8_t>;
if (bindata.size() > (std::numeric_limits<bytes::size_type>::max() / 4u) * 3u) {
throw ::std::length_error("Converting too large a string to base64.");
}
const std::size_t binlen = bindata.size();
// Use = signs so the end is properly padded.
std::string retval((((binlen + 2) / 3) * 4), '=');
std::size_t outpos = 0;
int bits_collected = 0;
unsigned int accumulator = 0;
const bytes::const_iterator binend = bindata.end();
for (auto i = bindata.begin(); i != binend; ++i) {
accumulator = (accumulator << 8) | (*i & 0xffu);
bits_collected += 8;
while (bits_collected >= 6) {
bits_collected -= 6;
retval[outpos++] = b64_table[(accumulator >> bits_collected) & 0x3fu];
}
}
if (bits_collected > 0) { // Any trailing bits that are missing.
assert(bits_collected < 6);
accumulator <<= 6 - bits_collected;
retval[outpos++] = b64_table[accumulator & 0x3fu];
}
assert(outpos >= (retval.size() - 2));
assert(outpos <= retval.size());
return retval;
}
std::vector<uint8_t> base64_decode(const std::string& ascdata) {
std::vector<uint8_t> retval;
const std::string::const_iterator last = ascdata.end();
int bits_collected = 0;
unsigned int accumulator = 0;
for (std::string::const_iterator i = ascdata.begin(); i != last; ++i) {
const int c = *i;
if (::std::isspace(c) || c == '=') {
// Skip whitespace and padding. Be liberal in what you accept.
continue;
}
if ((c > 127) || (c < 0) || (b64_reverse_table[c] > 63)) {
throw ::std::invalid_argument("This contains characters not legal in a base64 encoded string.");
}
accumulator = (accumulator << 6) | b64_reverse_table[c];
bits_collected += 6;
if (bits_collected >= 8) {
bits_collected -= 8;
retval.emplace_back(static_cast<uint8_t>((accumulator >> bits_collected) & 0xffu));
}
}
return retval;
}
}

45
navicat-keygen/Helper.hpp Normal file
View File

@ -0,0 +1,45 @@
#include <iostream>
#include <string>
#include <vector>
namespace Helper {
template<int min_num, int max_num>
bool ReadNumber(int& num, const char* msg, const char* err_msg) {
int temp;
std::string input;
while (true) {
std::cout << msg;
if (!std::getline(std::cin, input))
return false;
try {
temp = std::stoi(input, nullptr, 0);
if (min_num <= temp && temp <= max_num) {
num = temp;
return true;
} else {
throw std::invalid_argument("Invalid number");
}
} catch (...) {
std::cout << err_msg << std::endl;
}
}
}
template<typename _Type>
struct ResourceGuard {
_Type* ptr;
explicit ResourceGuard(_Type* p) noexcept : ptr(p) {}
~ResourceGuard() {
if (ptr) {
delete ptr;
ptr = nullptr;
}
}
};
std::string base64_encode(const std::vector<uint8_t>& bindata);
std::vector<uint8_t> base64_decode(const std::string& ascdata);
}

View File

@ -1,9 +0,0 @@
release : main.cpp
g++ -std=c++11 main.cpp -lcrypto -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -o navicat-keygen
debug : main.cpp
g++ -std=c++11 -D_DEBUG main.cpp -lcrypto -I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib -o navicat-keygen
clean:
rm navicat-keygen

View File

@ -0,0 +1,195 @@
#pragma once
#include <string>
#include <random>
#include "DESCipher.hpp"
class NavicatKeygen {
public:
enum class Language {
English,
SimplifiedChinese,
TraditionalChinese,
Japanese,
Polish,
Spanish,
French,
German,
Korean,
Russian,
Portuguese
};
enum class Product {
DataModeler,
Premium,
MySQL,
PostgreSQL,
Oracle,
SQLServer,
SQLite,
MariaDB,
MongoDB,
ReportViewer
};
private:
std::random_device rand_dev;
std::default_random_engine rand_eng;
std::uniform_int_distribution<int> rand;
uint8_t data[10];
public:
NavicatKeygen() : rand_eng(rand_dev()), rand(0, UINT8_MAX), data{} {
data[0] = 0x68;
data[1] = 0x2A;
}
void SetLanguageSignature(Language _language) {
switch (_language) {
case Language::English:
data[5] = 0xAC; // Must be 0xAC for English version.
data[6] = 0x88; // Must be 0x88 for English version.
break;
case Language::SimplifiedChinese:
data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
data[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
break;
case Language::TraditionalChinese:
data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version.
data[6] = 0x99; // Must be 0x99 for Traditional Chinese version.
break;
case Language::Japanese:
data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee
data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee
break;
case Language::Polish:
data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee
data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee
break;
case Language::Spanish:
data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee
data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee
break;
case Language::French:
data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79
data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79
break;
case Language::German:
data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee
data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee
break;
case Language::Korean:
data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee
data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee
break;
case Language::Russian:
data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee
data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee
break;
case Language::Portuguese:
data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee
data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee
break;
default:
break;
}
}
void SetLanguageSignature(uint8_t value0, uint8_t value1) {
data[5] = value0;
data[6] = value1;
}
void SetProductSignature(Product _product) {
switch (_product) {
case Product::DataModeler:
data[7] = 0x47;
break;
case Product::Premium:
data[7] = 0x65;
break;
case Product::MySQL:
data[7] = 0x68;
break;
case Product::PostgreSQL:
data[7] = 0x6C;
break;
case Product::Oracle:
data[7] = 0x70;
break;
case Product::SQLServer:
data[7] = 0x74;
break;
case Product::SQLite:
data[7] = 0x78;
break;
case Product::MariaDB:
data[7] = 0x7C;
break;
case Product::MongoDB:
data[7] = 0x80;
break;
case Product::ReportViewer:
data[7] = 0xb;
default:
break;
}
}
void SetProductSignature(uint8_t value) {
data[7] = value;
}
void SetVersion(uint8_t version) {
data[8] = version << 4;
}
void Generate() {
static const_DES_cblock DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
DESCipher cipher;
data[2] = static_cast<uint8_t>(rand(rand_eng));
data[3] = static_cast<uint8_t>(rand(rand_eng));
data[4] = static_cast<uint8_t>(rand(rand_eng));
data[9] = 0x32;
cipher.SetKey(&DESKey);
cipher.EncryptBlock(data + 2);
}
std::string GetKey() const {
static const char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567";
std::string Key;
Key.resize(16);
Key[0] = EncodeTable[data[0] >> 3];
Key[1] = EncodeTable[(data[0] & 0x07) << 2 | data[1] >> 6];
Key[2] = EncodeTable[data[1] >> 1 & 0x1F];
Key[3] = EncodeTable[(data[1] & 0x1) << 4 | data[2] >> 4];
Key[4] = EncodeTable[(data[2] & 0xF) << 1 | data[3] >> 7];
Key[5] = EncodeTable[data[3] >> 2 & 0x1F];
Key[6] = EncodeTable[(data[3] << 3 & 0x1F) | data[4] >> 5];
Key[7] = EncodeTable[data[4] & 0x1F];
Key[8] = EncodeTable[data[5] >> 3];
Key[9] = EncodeTable[(data[5] & 0x07) << 2 | data[6] >> 6];
Key[10] = EncodeTable[data[6] >> 1 & 0x1F];
Key[11] = EncodeTable[(data[6] & 0x1) << 4 | data[7] >> 4];
Key[12] = EncodeTable[(data[7] & 0xF) << 1 | data[8] >> 7];
Key[13] = EncodeTable[data[8] >> 2 & 0x1F];
Key[14] = EncodeTable[(data[8] << 3 & 0x1F) | data[9] >> 5];
Key[15] = EncodeTable[data[9] & 0x1F];
return Key;
}
std::string GetFormatedKey() const {
std::string Key = GetKey();
Key.insert(Key.begin() + 4, '-');
Key.insert(Key.begin() + 5 + 4, '-');
Key.insert(Key.begin() + 10 + 4, '-');
return Key;
}
};

View File

@ -0,0 +1,280 @@
#pragma once
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <string>
class OpenSSLException {
private:
const unsigned long ErrorCode;
public:
explicit OpenSSLException(unsigned long code) : ErrorCode(code) {}
unsigned long GetErrorCode() const noexcept { return ErrorCode; }
const char* GetErrorString() const noexcept { return ERR_error_string(ErrorCode, nullptr); }
};
template<typename _Type, void(_Type_free)(_Type*)>
class OpenSSLObject {
protected:
_Type* _pObj;
public:
// take over the object passed in
explicit OpenSSLObject(_Type* pObj) noexcept : _pObj(pObj) {}
OpenSSLObject(const OpenSSLObject<_Type, _Type_free>& other) = delete;
OpenSSLObject(OpenSSLObject<_Type, _Type_free>&& other) noexcept : _pObj(other._pObj) {
other._pObj = nullptr;
}
OpenSSLObject<_Type, _Type_free>&
operator=(const OpenSSLObject<_Type, _Type_free>& other) = delete;
OpenSSLObject<_Type, _Type_free>&
operator=(_Type* other) noexcept {
if (_pObj != other) {
_Type_free(_pObj);
}
_pObj = other;
return *this;
}
OpenSSLObject<_Type, _Type_free>&
operator=(OpenSSLObject<_Type, _Type_free>&& other) noexcept {
if (&other != this) {
if (_pObj)
_Type_free(_pObj);
_pObj = other._pObj;
other._pObj = nullptr;
}
return *this;
}
_Type* GetPointer() const noexcept {
return _pObj;
}
~OpenSSLObject() {
if (_pObj) {
_Type_free(_pObj);
_pObj = nullptr;
}
}
};
class RSACipher {
private:
OpenSSLObject<RSA, RSA_free> _RsaObj;
explicit RSACipher(RSA* lpRsa) : _RsaObj(lpRsa) {}
public:
RSACipher(const RSACipher&) = delete;
RSACipher(RSACipher&&) = delete;
RSACipher& operator=(const RSACipher&) = delete;
RSACipher& operator=(RSACipher&&) = delete;
enum class KeyType {
PrivateKey,
PublicKey
};
enum class KeyFormat {
NotSpecified,
PEM,
PKCS1
};
static RSACipher* Create() {
RSA* pObj = RSA_new();
return pObj ? new RSACipher(pObj) : nullptr;
}
bool GenerateKey(int bits, unsigned int e = RSA_F4) {
bool bSuccess = false;
OpenSSLObject<BIGNUM, BN_free> bn_e(BN_new());
if (!bn_e.GetPointer())
return false;
if (!BN_set_word(bn_e.GetPointer(), e))
return false;
if (!RSA_generate_key_ex(_RsaObj.GetPointer(), bits, bn_e.GetPointer(), nullptr))
return false;
return true;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
bool ExportKeyToFile(const std::string& filename) {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
bool bSuccess = false;
OpenSSLObject<BIO, BIO_free_all> bio_file(BIO_new_file(filename.c_str(), "w"));
if (bio_file.GetPointer() == nullptr)
return false;
if (_Type == KeyType::PrivateKey) {
bSuccess = PEM_write_bio_RSAPrivateKey(bio_file.GetPointer(), _RsaObj.GetPointer(), nullptr, nullptr, 0, nullptr, nullptr) != 0;
} else {
if (_Format == KeyFormat::PEM)
bSuccess = PEM_write_bio_RSA_PUBKEY(bio_file.GetPointer(), _RsaObj.GetPointer()) != 0;
if (_Format == KeyFormat::PKCS1)
bSuccess = PEM_write_bio_RSAPublicKey(bio_file.GetPointer(), _RsaObj.GetPointer()) != 0;
}
return bSuccess;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
std::string ExportKeyString() {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
std::string KeyString;
OpenSSLObject<BIO, BIO_free_all> bio_mem(BIO_new(BIO_s_mem()));
long len = 0;
const char* lpdata = nullptr;
if (bio_mem.GetPointer() == nullptr)
return KeyString;
if (_Type == KeyType::PrivateKey) {
if (!PEM_write_bio_RSAPrivateKey(bio_mem.GetPointer(), _RsaObj.GetPointer(), nullptr, nullptr, 0, nullptr, nullptr))
return KeyString;
} else {
if (_Format == KeyFormat::PEM) {
if (!PEM_write_bio_RSA_PUBKEY(bio_mem.GetPointer(), _RsaObj.GetPointer()))
return KeyString;
}
if (_Format == KeyFormat::PKCS1) {
if (!PEM_write_bio_RSAPublicKey(bio_mem.GetPointer(), _RsaObj.GetPointer()))
return KeyString;
}
}
len = BIO_get_mem_data(bio_mem.GetPointer(), &lpdata);
KeyString.assign(lpdata, static_cast<size_t>(len));
return KeyString;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
bool ImportKeyFromFile(const std::string& filename) {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
bool bSuccess = false;
OpenSSLObject<BIO, BIO_free_all> bio_file(BIO_new_file(filename.c_str(), "r"));
OpenSSLObject<RSA, RSA_free> newRsaObj(nullptr);
if (bio_file.GetPointer() == nullptr)
return false;
if (_Type == KeyType::PrivateKey) {
newRsaObj = PEM_read_bio_RSAPrivateKey(bio_file.GetPointer(), nullptr, nullptr, nullptr);
} else {
if (_Format == KeyFormat::PEM)
newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_file.GetPointer(), nullptr, nullptr, nullptr);
if (_Format == KeyFormat::PKCS1)
newRsaObj = PEM_read_bio_RSAPublicKey(bio_file.GetPointer(), nullptr, nullptr, nullptr);
}
if (newRsaObj.GetPointer()) {
_RsaObj = static_cast<OpenSSLObject<RSA, RSA_free>&&>(newRsaObj);
bSuccess = true;
}
return bSuccess;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
bool ImportKeyString(const std::string& KeyString) {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
bool bSuccess = false;
OpenSSLObject<BIO, BIO_free_all> bio_mem(BIO_new(BIO_s_mem()));
OpenSSLObject<RSA, RSA_free> newRsaObj(nullptr);
if (bio_mem.GetPointer() == nullptr)
return false;
BIO_puts(bio_mem.GetPointer(), KeyString.c_str());
if (_Type == KeyType::PrivateKey) {
newRsaObj = PEM_read_bio_RSAPrivateKey(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
} else {
if (_Format == KeyFormat::PEM)
newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
if (_Format == KeyFormat::PKCS1)
newRsaObj = PEM_read_bio_RSAPublicKey(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
}
if (newRsaObj.GetPointer()) {
_RsaObj = static_cast<OpenSSLObject<RSA, RSA_free>&&>(newRsaObj);
bSuccess = true;
}
return bSuccess;
}
template<KeyType _Type = KeyType::PublicKey>
int Encrypt(const void* from, int len, void* to, int padding) {
int write_bytes = 0;
if (_Type == KeyType::PrivateKey) {
write_bytes = RSA_private_encrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
} else {
write_bytes = RSA_public_encrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
}
if (write_bytes == -1)
write_bytes = 0;
return write_bytes;
}
template<KeyType _Type = KeyType::PrivateKey>
int Decrypt(const void* from, int len, void* to, int padding) {
int write_bytes = 0;
if (_Type == KeyType::PrivateKey) {
write_bytes = RSA_private_decrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
} else {
write_bytes = RSA_public_decrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
}
if (write_bytes == -1)
write_bytes = 0;
return write_bytes;
}
};

View File

@ -1,153 +1,63 @@
#include <stdio.h>
#include <memory.h>
#include <iostream> #include <iostream>
#include <string> #include "Helper.hpp"
#include <chrono> #include "RSACipher.hpp"
#include "DESCipher.hpp"
#include "NavicatKeygen.hpp"
#include <openssl/err.h> #include <rapidjson/document.h>
#include <openssl/bio.h> #include <rapidjson/writer.h>
#include <openssl/rsa.h> #include <rapidjson/stringbuffer.h>
#include <openssl/pem.h>
#include <openssl/des.h>
#include <openssl/evp.h>
enum NavicatLanguage { void help() {
English, std::cout << "Usage:" << std::endl
SimplifiedChinese, << " ./navicat-keygen <RSA-2048 PrivateKey(PEM file)>" << std::endl
TraditionalChinese,
Japanese,
Polish,
Spanish,
French,
German,
Korean,
Russian,
Portuguese
};
void GenerateSnKey(char (&SnKey)[16], NavicatLanguage _language) {
static char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567"; // Thanks for discoveries from @Wizr.
// This is not a standard Base32 alphabet table.
// The differences are:
// | Standard | Non-standard |
// |------------|----------------|
// | 'I' | '8' |
// | 'O' | '9' |
static DES_cblock DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
unsigned char temp_snKey[10] = { 0x68, 0x2a }; // must start with 0x68, 0x2a
temp_snKey[2] = rand();
temp_snKey[3] = rand();
temp_snKey[4] = rand();
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;
}
temp_snKey[7] = 0x65; // 0x65 - commercial, 0x66 - non-commercial
temp_snKey[8] = 0xC0; // High 4-bits = version number. Low 4-bits doesn't know, but can be used to delay activation time.
temp_snKey[9] = 0x32; // 0xFB is Not-For-Resale-30-days license.
// 0xFC is Not-For-Resale-90-days license.
// 0xFD is Not-For-Resale-365-days license.
// 0xFE is Not-For-Resale license.
// 0xFF is Site license.
// Must not be 0x00. 0x01-0xFA is ok.
DES_key_schedule schedule;
DES_set_odd_parity(&DESKey);
DES_set_key(&DESKey, &schedule);
DES_cblock enc_temp_snKey;
DES_ecb_encrypt(reinterpret_cast<const_DES_cblock*>(temp_snKey + 2), &enc_temp_snKey, &schedule, DES_ENCRYPT);
memmove(temp_snKey + 2, enc_temp_snKey, sizeof(enc_temp_snKey));
SnKey[0] = EncodeTable[temp_snKey[0] >> 3];
SnKey[1] = EncodeTable[(temp_snKey[0] & 0x07) << 2 | temp_snKey[1] >> 6];
SnKey[2] = EncodeTable[temp_snKey[1] >> 1 & 0x1F];
SnKey[3] = EncodeTable[(temp_snKey[1] & 0x1) << 4 | temp_snKey[2] >> 4];
SnKey[4] = EncodeTable[(temp_snKey[2] & 0xF) << 1 | temp_snKey[3] >> 7];
SnKey[5] = EncodeTable[temp_snKey[3] >> 2 & 0x1F];
SnKey[6] = EncodeTable[temp_snKey[3] << 3 & 0x1F | temp_snKey[4] >> 5];
SnKey[7] = EncodeTable[temp_snKey[4] & 0x1F];
SnKey[8] = EncodeTable[temp_snKey[5] >> 3];
SnKey[9] = EncodeTable[(temp_snKey[5] & 0x07) << 2 | temp_snKey[6] >> 6];
SnKey[10] = EncodeTable[temp_snKey[6] >> 1 & 0x1F];
SnKey[11] = EncodeTable[(temp_snKey[6] & 0x1) << 4 | temp_snKey[7] >> 4];
SnKey[12] = EncodeTable[(temp_snKey[7] & 0xF) << 1 | temp_snKey[8] >> 7];
SnKey[13] = EncodeTable[temp_snKey[8] >> 2 & 0x1F];
SnKey[14] = EncodeTable[temp_snKey[8] << 3 & 0x1F | temp_snKey[9] >> 5];
SnKey[15] = EncodeTable[temp_snKey[9] & 0x1F];
char formated_SnKeyString[20] = { };
snprintf(formated_SnKeyString, sizeof(formated_SnKeyString), "%.4s-%.4s-%.4s-%.4s", SnKey, SnKey + 4, SnKey + 8, SnKey + 12);
std::cout << std::endl;
std::cout
<< "SnKey:" << std::endl
<< formated_SnKeyString << std::endl
<< std::endl; << std::endl;
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[], char* envp[]) {
if (argc != 2) { if (argc != 2) {
std::cout help();
<< "Usage:" << std::endl
<< " ./navicat-keygen <RSA-2048 PrivateKey(PEM file)>" << std::endl
<< std::endl;
return 0; return 0;
} }
srand(time(0)); Helper::ResourceGuard<RSACipher> cipher(RSACipher::Create());
NavicatKeygen keygen;
std::string username;
std::string organization;
std::string RequestCode_b64;
std::vector<uint8_t> RequestCode;
char RequestInfo[256] = {};
char ResponseInfo[256] = {};
std::vector<uint8_t> ResponseCode;
std::string ResponseCode_b64;
rapidjson::Document json;
rapidjson::Value N_Key;
rapidjson::Value N_Value;
rapidjson::Value O_Key;
rapidjson::Value O_Value;
rapidjson::Value T_Key;
rapidjson::Value T_Value;
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
if (cipher.ptr == nullptr) {
std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
std::cout << "ERROR: Failed to create RSACipher." << std::endl;
return 0;
}
if (!cipher.ptr->ImportKeyFromFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::PEM>(argv[1])) {
std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
std::cout << "ERROR: Failed to load RSA-2048 key." << std::endl;
return 0;
}
keygen.SetProductSignature(NavicatKeygen::Product::Premium);
std::cout std::cout
<< "Which is your Navicat language?" << std::endl << "Which is your Navicat Premium language?" << std::endl
<< "0. English" << std::endl << "0. English" << std::endl
<< "1. Simplified Chinese" << std::endl << "1. Simplified Chinese" << std::endl
<< "2. Traditional Chinese" << std::endl << "2. Traditional Chinese" << std::endl
@ -161,122 +71,147 @@ int main(int argc, char* argv[]) {
<< "10. Portuguese" << std::endl << "10. Portuguese" << std::endl
<< std::endl; << std::endl;
int LanguageIndex = -1; {
while(true) { int num;
std::cout << "(input index)>"; if (!Helper::ReadNumber<0, 10>(num, "(Input index)> ", "Invalid index."))
return 0;
keygen.SetLanguageSignature(static_cast<NavicatKeygen::Language>(num));
std::string temp; if (!Helper::ReadNumber<0, 15>(num, "(Input major version number, range: 0 ~ 15, default: 12)> ", "Invalid number."))
std::getline(std::cin, temp); return 0;
try { keygen.SetVersion(static_cast<uint8_t>(num));
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, static_cast<NavicatLanguage>(LanguageIndex)); // Generate snKey
//
keygen.Generate();
std::cout << std::endl;
std::cout << "Serial number:" << std::endl;
std::cout << keygen.GetFormatedKey() << std::endl;
std::cout << std::endl;
double current_time //
= std::chrono::duration_cast<std::chrono::duration<double>>( // Get user name
std::chrono::system_clock::now().time_since_epoch()).count(); //
BIO* BIO_file = BIO_new_file(argv[1], "r");
if (BIO_file == nullptr) {
std::cout << "Failed to read file." << std::endl;
return -1;
}
RSA* PrivateKey = PEM_read_bio_RSAPrivateKey(BIO_file, nullptr, nullptr, nullptr);
if (PrivateKey == nullptr) {
std::cout << "Failed to load private key." << std::endl;
return -2;
}
BIO_free_all(BIO_file);
std::string Name;
std::string Organization;
std::cin.clear();
std::cout << "Your name: "; std::cout << "Your name: ";
std::getline(std::cin, Name); if (!std::getline(std::cin, username))
std::cout << "Yout organization: "; return 0;
std::getline(std::cin, Organization);
std::string buffer; //
std::cout << "Input Request Code (in Base64), empty line to return:" << std::endl; // Get organization name
while(true) { //
std::cout << "Your organization: ";
if (!std::getline(std::cin, organization))
return 0;
std::cout << std::endl;
//
// Get request code in base64
//
std::cout << "Input request code (in Base64), input empty line to end:" << std::endl;
while (true) {
std::string temp; std::string temp;
std::getline(std::cin, temp); if (!std::getline(std::cin, temp))
buffer += temp; return 0;
if (temp.empty()) if (temp.empty())
break; break;
RequestCode_b64 += temp;
} }
unsigned char enc_data[1024] = { }; //
char data[1024] = { }; // Get request code in raw bytes
//
std::string DeviceIdentifier(""); try {
EVP_DecodeBlock(enc_data, reinterpret_cast<const unsigned char*>(buffer.c_str()), buffer.length()); RequestCode = Helper::base64_decode(RequestCode_b64);
if (RSA_private_decrypt(256, enc_data, reinterpret_cast<unsigned char*>(data), PrivateKey, RSA_PKCS1_PADDING) == -1) { } catch(...) {
std::cout << "Failed to decrypt data." << std::endl; std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
return -3; std::cout << "ERROR: Helper::base64_decode fails." << std::endl;
} return 0;
#ifdef _DEBUG
std::cout << "-----------Begin Request Code Data---------------" << std::endl;
std::cout << data << std::endl;
std::cout <<"-----------End Request Code Data---------------" << std::endl;
#endif
// Get DeviceIdentifier from data.
for (int i = 0, length = strlen(data) - 4; i < length; ++i) {
if(data[i] == '"' &&
data[i + 1] == 'D' &&
data[i + 2] == 'I' &&
data[i + 3] == '"') {
char temp[256] = { };
int x = i + 4, j = 0;
while (data[x] != '"' && x < length)
x++;
x++;
while (data[x] != '"' && j < 256) {
temp[j++] = data[x];
x++;
}
DeviceIdentifier += temp;
break;
}
} }
memset(data, 0, sizeof(data)); //
memset(enc_data, 0, sizeof(enc_data)); // Decrypt to get request info
//
if (!cipher.ptr->Decrypt(RequestCode.data(), static_cast<int>(RequestCode.size()), RequestInfo, RSA_PKCS1_PADDING)) {
std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
std::cout << "ERROR: Decrypt<RSACipher::KeyType::PrivateKey> fails." << std::endl;
return 0;
}
snprintf(data, sizeof(data), "{\n \"DI\" : \"%s\", \n \"T\" : \"%lf\", \n \"K\" : \"%.16s\", \n \"N\" : \"%s\", \n \"O\" : \"%s\"\n}", //
DeviceIdentifier.c_str(), // print out request info
current_time, //
SnKey, std::cout << "Request Info:" << std::endl;
Name.c_str(), std::cout << RequestInfo << std::endl;
Organization.c_str() std::cout << std::endl;
);
#ifdef _DEBUG //
std::cout << "-----------Begin Activation Code Data---------------" << std::endl; // Generate response info
std::cout << data << std::endl; //
std::cout << "-----------End Activation Code Data---------------" << std::endl; json.Parse(RequestInfo);
#endif json.RemoveMember("P"); // remove Platform info
RSA_private_encrypt(strlen(data), reinterpret_cast<unsigned char*>(data), enc_data, PrivateKey, RSA_PKCS1_PADDING); N_Key.SetString("N", 1);
N_Value.SetString(username.c_str(), static_cast<rapidjson::SizeType>(username.length()));
O_Key.SetString("O", 1);
O_Value.SetString(organization.c_str(), static_cast<rapidjson::SizeType>(organization.length()));
T_Key.SetString("T", 1);
T_Value.SetUint(static_cast<unsigned>(std::time(nullptr)));
char result[1024] = { }; json.AddMember(N_Key, N_Value, json.GetAllocator());
EVP_EncodeBlock(reinterpret_cast<unsigned char*>(result), enc_data, 256); json.AddMember(O_Key, O_Value, json.GetAllocator());
std::cout << "Activation Code:" << std::endl; json.AddMember(T_Key, T_Value, json.GetAllocator());
std::cout << result << std::endl;
json.Accept(writer);
if (buffer.GetSize() > 240) {
std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
std::cout << "ERROR: Response info is too long." << std::endl;
return 0;
}
//
// print out response info
//
memcpy(ResponseInfo, buffer.GetString(), buffer.GetSize());
std::cout << "Response Info:" << std::endl;
std::cout << ResponseInfo << std::endl;
std::cout << std::endl;
//
// encrypt response info
//
ResponseCode.resize(256);
if (!cipher.ptr->Encrypt<RSACipher::KeyType::PrivateKey>(ResponseInfo,
static_cast<int>(strlen(ResponseInfo)),
ResponseCode.data(),
RSA_PKCS1_PADDING)) {
std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
std::cout << "ERROR: Encrypt<RSACipher::KeyType::PrivateKey> fails." << std::endl;
return 0;
}
//
// Encode encrypted response info in base64 format
//
try {
ResponseCode_b64 = Helper::base64_encode(ResponseCode);
} catch(...) {
std::cout << "@Function: " << __FUNCTION__ << " LINE: " << __LINE__ << std::endl;
std::cout << "ERROR: Helper::base64_encode fails." << std::endl;
return 0;
}
//
// print out activation code
//
std::cout << "License:" << std::endl;
std::cout << ResponseCode_b64 << std::endl;
RSA_free(PrivateKey);
return 0; return 0;
} }

View File

@ -0,0 +1,78 @@
#pragma once
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
class FileMapper {
private:
int _fd;
size_t _MapSize;
void* _pView;
public:
class AlreadyInUseException {};
FileMapper() noexcept :
_fd(-1),
_MapSize(static_cast<size_t>(-1)),
_pView(reinterpret_cast<void*>(-1)) { }
void Unmap() noexcept {
if (_pView != reinterpret_cast<void*>(-1)) {
munmap(_pView, _MapSize);
_MapSize = static_cast<size_t>(-1);
_pView = reinterpret_cast<void*>(-1);
}
}
void Close() noexcept {
if (_fd != -1) {
close(_fd);
_fd = -1;
}
}
bool OpenFile(const char* FilePath) {
if (_fd != -1)
throw AlreadyInUseException();
_fd = open(FilePath, O_RDWR, S_IRUSR | S_IWUSR);
return _fd != -1;
}
bool GetFileSize(off_t& refSize) noexcept {
struct stat fd_stat = {};
if (fstat(_fd, &fd_stat) != 0) {
return false;
} else {
refSize = fd_stat.st_size;
return true;
}
}
bool Map(size_t Size) {
if (_pView != reinterpret_cast<void*>(-1))
throw AlreadyInUseException();
_pView = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
if (_pView != reinterpret_cast<void*>(-1)) {
_MapSize = Size;
return true;
} else {
return false;
}
}
template<typename _Type>
_Type* GetView() const noexcept {
return reinterpret_cast<_Type*>(_pView);
}
~FileMapper() {
Unmap();
Close();
}
};

View File

@ -0,0 +1,97 @@
#include "Helper.hpp"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <signal.h>
#include <setjmp.h>
namespace Helper {
static jmp_buf env;
static void SIGSEGV_Handler(int sig) {
siglongjmp(env, 1);
}
//
// read byte(s) at address `p` as _Type to `out`
// succeed if return true, otherwise return false
//
template<typename _Type>
static inline bool ProbeForRead(const void* p, void* out) {
int r = sigsetjmp(env, 1);
if (r == 0) {
*reinterpret_cast<_Type*>(out) = *reinterpret_cast<const _Type*>(p);
return true;
} else {
return false;
}
}
//
// Print memory data in [from, to) at least
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
// NOTICE:
// `base` must >= `from`
//
void PrintMemory(const void* from, const void* to, const void* base) {
const uint8_t* start = reinterpret_cast<const uint8_t*>(from);
const uint8_t* end = reinterpret_cast<const uint8_t*>(to);
const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(base);
if (start >= end)
return;
while (reinterpret_cast<uintptr_t>(start) % 16)
start--;
while (reinterpret_cast<uintptr_t>(start) % 16)
end++;
void (*prev_handler)(int) = signal(SIGSEGV, SIGSEGV_Handler);
while (start < end) {
uint16_t value[16] = {};
if (base_ptr)
printf("+0x%p ", reinterpret_cast<const void*>(start - base_ptr));
else
printf("0x%p ", start);
for (int i = 0; i < 16; ++i) {
if (ProbeForRead<uint8_t>(start + i, value + i)) {
printf("%02x ", value[i]);
} else {
value[i] = 0xffff;
printf("?? ");
}
}
printf(" ");
for (int i = 0; i < 16; ++i) {
if (value[i] < 0x20) {
printf(".");
} else if (value[i] > 0x7e) {
printf(".");
} else {
printf("%c", value[i]);
}
}
printf("\n");
start += 0x10;
}
signal(SIGSEGV, prev_handler);
}
void ErrorReport(const char* at, size_t line, const char* msg) {
printf("ERROR: @%s LINE: %zu\n", at, line);
printf("%s\n", msg);
}
void ErrorReport(const char* at, size_t line, const char* msg, int err_code) {
printf("ERROR: @%s LINE: %zu\n", at, line);
printf("%s CODE: 0x%08x\n", msg, err_code);
printf("REASON: %s\n", strerror(err_code));
}
}

View File

@ -0,0 +1,35 @@
#pragma once
#include <stddef.h>
#include <stdio.h>
namespace Helper {
//
// Print memory data in [from, to) at least
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
// NOTICE:
// `base` must >= `from`
//
void PrintMemory(const void* from, const void* to, const void* base);
void ErrorReport(const char* at, size_t line, const char* msg);
void ErrorReport(const char* at, size_t line, const char* msg, int err_code);
template<typename _Type>
struct ResourceGuard {
_Type* ptr;
explicit ResourceGuard(_Type* p) noexcept : ptr(p) {}
~ResourceGuard() {
if (ptr) {
delete ptr;
ptr = nullptr;
}
}
};
}
#define REPORT_ERROR(msg) Helper::ErrorReport(__FUNCTION__, __LINE__, msg)
#define REPORT_ERROR_WITH_CODE(msg) Helper::ErrorReport(__FUNCTION__, __LINE__, msg, errno)
#define PRINT_MESSAGE(msg) puts(msg)

View File

@ -1,6 +0,0 @@
release : main.c
gcc main.c -o navicat-patcher
clean:
rm navicat-patcher

View File

@ -0,0 +1,280 @@
#pragma once
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <string>
class OpenSSLException {
private:
const unsigned long ErrorCode;
public:
explicit OpenSSLException(unsigned long code) : ErrorCode(code) {}
unsigned long GetErrorCode() const noexcept { return ErrorCode; }
const char* GetErrorString() const noexcept { return ERR_error_string(ErrorCode, nullptr); }
};
template<typename _Type, void(_Type_free)(_Type*)>
class OpenSSLObject {
protected:
_Type* _pObj;
public:
// take over the object passed in
explicit OpenSSLObject(_Type* pObj) noexcept : _pObj(pObj) {}
OpenSSLObject(const OpenSSLObject<_Type, _Type_free>& other) = delete;
OpenSSLObject(OpenSSLObject<_Type, _Type_free>&& other) noexcept : _pObj(other._pObj) {
other._pObj = nullptr;
}
OpenSSLObject<_Type, _Type_free>&
operator=(const OpenSSLObject<_Type, _Type_free>& other) = delete;
OpenSSLObject<_Type, _Type_free>&
operator=(_Type* other) noexcept {
if (_pObj != other) {
_Type_free(_pObj);
}
_pObj = other;
return *this;
}
OpenSSLObject<_Type, _Type_free>&
operator=(OpenSSLObject<_Type, _Type_free>&& other) noexcept {
if (&other != this) {
if (_pObj)
_Type_free(_pObj);
_pObj = other._pObj;
other._pObj = nullptr;
}
return *this;
}
_Type* GetPointer() const noexcept {
return _pObj;
}
~OpenSSLObject() {
if (_pObj) {
_Type_free(_pObj);
_pObj = nullptr;
}
}
};
class RSACipher {
private:
OpenSSLObject<RSA, RSA_free> _RsaObj;
explicit RSACipher(RSA* lpRsa) : _RsaObj(lpRsa) {}
public:
RSACipher(const RSACipher&) = delete;
RSACipher(RSACipher&&) = delete;
RSACipher& operator=(const RSACipher&) = delete;
RSACipher& operator=(RSACipher&&) = delete;
enum class KeyType {
PrivateKey,
PublicKey
};
enum class KeyFormat {
NotSpecified,
PEM,
PKCS1
};
static RSACipher* Create() {
RSA* pObj = RSA_new();
return pObj ? new RSACipher(pObj) : nullptr;
}
bool GenerateKey(int bits, unsigned int e = RSA_F4) {
bool bSuccess = false;
OpenSSLObject<BIGNUM, BN_free> bn_e(BN_new());
if (!bn_e.GetPointer())
return false;
if (!BN_set_word(bn_e.GetPointer(), e))
return false;
if (!RSA_generate_key_ex(_RsaObj.GetPointer(), bits, bn_e.GetPointer(), nullptr))
return false;
return true;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
bool ExportKeyToFile(const std::string& filename) {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
bool bSuccess = false;
OpenSSLObject<BIO, BIO_free_all> bio_file(BIO_new_file(filename.c_str(), "w"));
if (bio_file.GetPointer() == nullptr)
return false;
if (_Type == KeyType::PrivateKey) {
bSuccess = PEM_write_bio_RSAPrivateKey(bio_file.GetPointer(), _RsaObj.GetPointer(), nullptr, nullptr, 0, nullptr, nullptr) != 0;
} else {
if (_Format == KeyFormat::PEM)
bSuccess = PEM_write_bio_RSA_PUBKEY(bio_file.GetPointer(), _RsaObj.GetPointer()) != 0;
if (_Format == KeyFormat::PKCS1)
bSuccess = PEM_write_bio_RSAPublicKey(bio_file.GetPointer(), _RsaObj.GetPointer()) != 0;
}
return bSuccess;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
std::string ExportKeyString() {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
std::string KeyString;
OpenSSLObject<BIO, BIO_free_all> bio_mem(BIO_new(BIO_s_mem()));
long len = 0;
const char* lpdata = nullptr;
if (bio_mem.GetPointer() == nullptr)
return KeyString;
if (_Type == KeyType::PrivateKey) {
if (!PEM_write_bio_RSAPrivateKey(bio_mem.GetPointer(), _RsaObj.GetPointer(), nullptr, nullptr, 0, nullptr, nullptr))
return KeyString;
} else {
if (_Format == KeyFormat::PEM) {
if (!PEM_write_bio_RSA_PUBKEY(bio_mem.GetPointer(), _RsaObj.GetPointer()))
return KeyString;
}
if (_Format == KeyFormat::PKCS1) {
if (!PEM_write_bio_RSAPublicKey(bio_mem.GetPointer(), _RsaObj.GetPointer()))
return KeyString;
}
}
len = BIO_get_mem_data(bio_mem.GetPointer(), &lpdata);
KeyString.assign(lpdata, static_cast<size_t>(len));
return KeyString;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
bool ImportKeyFromFile(const std::string& filename) {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
bool bSuccess = false;
OpenSSLObject<BIO, BIO_free_all> bio_file(BIO_new_file(filename.c_str(), "r"));
OpenSSLObject<RSA, RSA_free> newRsaObj(nullptr);
if (bio_file.GetPointer() == nullptr)
return false;
if (_Type == KeyType::PrivateKey) {
newRsaObj = PEM_read_bio_RSAPrivateKey(bio_file.GetPointer(), nullptr, nullptr, nullptr);
} else {
if (_Format == KeyFormat::PEM)
newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_file.GetPointer(), nullptr, nullptr, nullptr);
if (_Format == KeyFormat::PKCS1)
newRsaObj = PEM_read_bio_RSAPublicKey(bio_file.GetPointer(), nullptr, nullptr, nullptr);
}
if (newRsaObj.GetPointer()) {
_RsaObj = static_cast<OpenSSLObject<RSA, RSA_free>&&>(newRsaObj);
bSuccess = true;
}
return bSuccess;
}
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
bool ImportKeyString(const std::string& KeyString) {
static_assert(
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
"Not supported format."
);
bool bSuccess = false;
OpenSSLObject<BIO, BIO_free_all> bio_mem(BIO_new(BIO_s_mem()));
OpenSSLObject<RSA, RSA_free> newRsaObj(nullptr);
if (bio_mem.GetPointer() == nullptr)
return false;
BIO_puts(bio_mem.GetPointer(), KeyString.c_str());
if (_Type == KeyType::PrivateKey) {
newRsaObj = PEM_read_bio_RSAPrivateKey(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
} else {
if (_Format == KeyFormat::PEM)
newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
if (_Format == KeyFormat::PKCS1)
newRsaObj = PEM_read_bio_RSAPublicKey(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
}
if (newRsaObj.GetPointer()) {
_RsaObj = static_cast<OpenSSLObject<RSA, RSA_free>&&>(newRsaObj);
bSuccess = true;
}
return bSuccess;
}
template<KeyType _Type = KeyType::PublicKey>
int Encrypt(const void* from, int len, void* to, int padding) {
int write_bytes = 0;
if (_Type == KeyType::PrivateKey) {
write_bytes = RSA_private_encrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
} else {
write_bytes = RSA_public_encrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
}
if (write_bytes == -1)
write_bytes = 0;
return write_bytes;
}
template<KeyType _Type = KeyType::PrivateKey>
int Decrypt(const void* from, int len, void* to, int padding) {
int write_bytes = 0;
if (_Type == KeyType::PrivateKey) {
write_bytes = RSA_private_decrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
} else {
write_bytes = RSA_public_decrypt(len,
reinterpret_cast<const unsigned char*>(from),
reinterpret_cast<unsigned char*>(to),
_RsaObj.GetPointer(),
padding);
}
if (write_bytes == -1)
write_bytes = 0;
return write_bytes;
}
};

View File

@ -0,0 +1,84 @@
#include "Solutions.hpp"
#include "Helper.hpp"
namespace Patcher {
const char Solution0::Keyword[452] =
"-----BEGIN PUBLIC KEY-----\x00"
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00"
"qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00"
"a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00"
"R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00"
"WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00"
"YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00"
"awIDAQAB\x00"
"-----END PUBLIC KEY-----\x00";
bool Solution0::FindPatchOffset() noexcept {
bool bFound = false;
uint8_t* pFileView = pTargetFile->GetView<uint8_t>();
off_t FileSize;
if (pFileView == nullptr)
return false;
if (!pTargetFile->GetFileSize(FileSize))
return false;
if (FileSize < KeywordLength)
return false;
FileSize -= KeywordLength;
for (off_t i = 0; i < FileSize; ++i) {
if (pFileView[i] == Keyword[0] && memcmp(pFileView + i, Keyword, KeywordLength) == 0) {
PatchOffset = i;
bFound = true;
break;
}
}
if (bFound)
printf("MESSAGE: [Solution0] Keyword has been found: offset = +0x%08lx.\n",
static_cast<unsigned long>(PatchOffset));
return bFound;
}
bool Solution0::MakePatch(RSACipher* cipher) const {
uint8_t* lpTargetFileView = pTargetFile->GetView<uint8_t>();
std::string RSAPublicKeyPEM =
cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
if (RSAPublicKeyPEM.empty()) {
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
return false;
}
for (size_t i = 0; i < RSAPublicKeyPEM.length(); ++i) {
if (RSAPublicKeyPEM[i] == '\n')
RSAPublicKeyPEM[i] = '\x00';
}
if (RSAPublicKeyPEM.length() != KeywordLength) {
REPORT_ERROR("ERROR: Public key length does not match.");
return false;
}
PRINT_MESSAGE("//");
PRINT_MESSAGE("// Begin Solution0");
PRINT_MESSAGE("//");
printf("@+0x%08llX\nPrevious:\n", PatchOffset);
Helper::PrintMemory(lpTargetFileView + PatchOffset,
lpTargetFileView + PatchOffset + KeywordLength,
lpTargetFileView);
memcpy(lpTargetFileView + PatchOffset, RSAPublicKeyPEM.c_str(), KeywordLength);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpTargetFileView + PatchOffset,
lpTargetFileView + PatchOffset + KeywordLength,
lpTargetFileView);
PRINT_MESSAGE("");
return true;
}
}

View File

@ -0,0 +1,116 @@
#include "Solutions.hpp"
#include "Helper.hpp"
namespace Patcher {
const uint8_t Solution1::Keyword[KeywordLength] = {
0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd,
0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb,
0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb,
0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3,
0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea,
0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2,
0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4,
0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc,
0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef,
0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7,
0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8,
0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc,
0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e,
0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9,
0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc,
0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88,
0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2,
0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd,
0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2,
0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce,
0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd,
0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5,
0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce,
0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb,
0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8
};
bool Solution1::FindPatchOffset() noexcept {
bool bFound = false;
uint8_t* pFileView = pTargetFile->GetView<uint8_t>();
off_t FileSize;
if (pFileView == nullptr)
return false;
if (!pTargetFile->GetFileSize(FileSize))
return false;
if (FileSize < KeywordLength)
return false;
FileSize -= KeywordLength;
for (off_t i = 0; i < FileSize; ++i) {
if (pFileView[i] == Keyword[0] && memcmp(pFileView + i, Keyword, KeywordLength) == 0) {
PatchOffset = i;
bFound = true;
break;
}
}
if (bFound)
printf("MESSAGE: [Solution1] Keyword has been found: offset = +0x%08lx.\n",
static_cast<unsigned long>(PatchOffset));
return bFound;
}
bool Solution1::MakePatch(RSACipher* cipher) const {
uint8_t* lpTargetFileView = pTargetFile->GetView<uint8_t>();
std::string RSAPublicKeyPEM =
cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
if (RSAPublicKeyPEM.empty()) {
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
return false;
}
RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
{
std::string::size_type pos = 0;
while ((pos = RSAPublicKeyPEM.find("\n", pos)) != std::string::npos) {
RSAPublicKeyPEM.erase(pos, 1);
}
}
if (RSAPublicKeyPEM.length() != KeywordLength) {
REPORT_ERROR("ERROR: Public key length does not match.");
return false;
}
{
uint8_t key = 8;
for (size_t i = 0; i < RSAPublicKeyPEM.length(); ++i) {
if (key == 0)
key = 8;
RSAPublicKeyPEM[i] ^= 0xbb - key;
--key;
}
}
PRINT_MESSAGE("//");
PRINT_MESSAGE("// Begin Solution1");
PRINT_MESSAGE("//");
printf("@+0x%08llX\nPrevious:\n", PatchOffset);
Helper::PrintMemory(lpTargetFileView + PatchOffset,
lpTargetFileView + PatchOffset + KeywordLength,
lpTargetFileView);
memcpy(lpTargetFileView + PatchOffset, RSAPublicKeyPEM.c_str(), KeywordLength);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpTargetFileView + PatchOffset,
lpTargetFileView + PatchOffset + KeywordLength,
lpTargetFileView);
PRINT_MESSAGE("");
return true;
}
}

View File

@ -0,0 +1,78 @@
#pragma once
#include "FileMapper.hpp"
#include "RSACipher.hpp"
namespace Patcher {
class Solution {
public:
virtual void SetFile(FileMapper* pFile) = 0;
virtual bool CheckKey(RSACipher* cipher) const = 0;
virtual bool FindPatchOffset() = 0;
virtual bool MakePatch(RSACipher* cipher) const = 0;
virtual ~Solution() = default;
};
// Solution0 will replace the RSA public key stored in main application.
class Solution0 : public Solution {
private:
static constexpr size_t KeywordLength = 451;
static const char Keyword[KeywordLength + 1];
FileMapper* pTargetFile;
off_t PatchOffset;
public:
Solution0() noexcept :
pTargetFile(nullptr),
PatchOffset(-1) {}
virtual void SetFile(FileMapper* pMainApp) noexcept override {
pTargetFile = pMainApp;
}
// Solution0 does not have any requirements for an RSA-2048 key
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
return true;
}
// Return true if found, other return false
virtual bool FindPatchOffset() noexcept override;
// Make a patch based on an RSA private key given
// Return true if success, otherwise return false
virtual bool MakePatch(RSACipher* cipher) const override;
};
// Solution1 will replace the RSA public key stored in main application.
class Solution1 : public Solution {
private:
static constexpr size_t KeywordLength = 0x188;
static const uint8_t Keyword[KeywordLength];
FileMapper* pTargetFile;
off_t PatchOffset;
public:
Solution1() :
pTargetFile(nullptr),
PatchOffset(-1) {}
virtual void SetFile(FileMapper* pLibccFile) noexcept override {
pTargetFile = pLibccFile;
}
// Solution1 has no requirements for an RSA-2048 key
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
return true;
}
// Return true if found, otherwise return false
virtual bool FindPatchOffset() noexcept override;
// Make a patch based on an RSA private key given
// Return true if success, otherwise return false
virtual bool MakePatch(RSACipher* cipher) const override;
};
}

View File

@ -1,127 +0,0 @@
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <memory.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
const char pubkey[9][72] = {
"-----BEGIN PUBLIC KEY-----",
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxqkTcfbKw8ysVygePlcB",
"oUAhCF6oniyP13iDtu85ZsHwqw8PnMyTp6n6FnMN9YinleIAy6NFveBu/vshTN8S",
"oXbYyy5AqdZ8CQpfvuriO9UNfgV1l7SFdPPpruFAmOw+uzA3GawMsg3QNK/htqJe",
"b4xKHFS04xC2AueE2RTmk6tJcL8TEBfRG7DEYOHPjebKl1NQ3ZIu15U97cCPYKO2",
"pWHzsb+Fr4Wj0DChLoxlXxaBcJ2ozogaq0tW2t4Aopvt9kRSuSK9HcgxICJM5ct4",
"naU91WFGWlw0+0JpiMIl5OnMbpak/5xQre9DL8zM8LjRy14I88txvXvhPEsWaYCO",
"1QIDAQAB",
"-----END PUBLIC KEY-----"
};
void help() {
printf("Usage:\n");
printf(" ./navicat-patcher <navicat executable file>\n");
printf("\n");
}
size_t search_pubkey_location(uint8_t* pFileContent, size_t FileSize) {
static const char search_str[] = "-----BEGIN PUBLIC KEY-----";
size_t i = 0;
if (FileSize < sizeof(search_str) - 1) return (size_t)-1;
FileSize -= sizeof(search_str);
for (; i < FileSize; ++i) {
if (pFileContent[i] == '-' && memcmp(pFileContent + i, search_str, sizeof(search_str) - 1) == 0)
return i;
}
return (size_t)-1;
}
void do_patch(uint8_t* pFileContent, size_t offset) {
strcpy(pFileContent + offset, pubkey[0]);
offset += strlen(pubkey[0]) + 1;
strcpy(pFileContent + offset, pubkey[1]);
offset += strlen(pubkey[1]) + 1;
strcpy(pFileContent + offset, pubkey[2]);
offset += strlen(pubkey[2]) + 1;
strcpy(pFileContent + offset, pubkey[3]);
offset += strlen(pubkey[3]) + 1;
strcpy(pFileContent + offset, pubkey[4]);
offset += strlen(pubkey[4]) + 1;
strcpy(pFileContent + offset, pubkey[5]);
offset += strlen(pubkey[5]) + 1;
strcpy(pFileContent + offset, pubkey[6]);
offset += strlen(pubkey[6]) + 1;
strcpy(pFileContent + offset, pubkey[7]);
offset += strlen(pubkey[7]) + 1;
strcpy(pFileContent + offset, pubkey[8]);
offset += strlen(pubkey[8]) + 1;
}
int main(int argc, char* argv[], char* envp[]) {
int status = 0;
int fd = -1;
struct stat fd_stat = {};
uint8_t* file_content = 0;
if (argc != 2) {
help();
return 0;
}
fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1) {
printf("Failed to open file. CODE: 0x%08x\n", errno);
status = errno;
goto main_fin;
} else {
printf("Open file successfully.\n");
}
if (fstat(fd, &fd_stat) != 0) {
printf("Failed to get file size. CODE: 0x%08x\n", errno);
status = errno;
goto main_fin;
} else {
printf("Get file size successfully: %zu\n", fd_stat.st_size);
}
file_content = mmap(NULL, fd_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (file_content == (void*)-1) {
printf("Failed to map file. CODE: 0x%08x\n", errno);
status = errno;
goto main_fin;
} else {
printf("Map file successfully.\n");
}
size_t offset = search_pubkey_location(file_content, fd_stat.st_size);
if (offset == (size_t)-1) {
printf("Failed to find pubkey location.\n");
goto main_fin;
}
printf("offset = 0x%016llx\n", offset);
do_patch(file_content, offset);
printf("Success!\n");
main_fin:
if (file_content != NULL) munmap(file_content, fd_stat.st_size);
if (fd != -1) close(fd);
return status;
}

156
navicat-patcher/main.cpp Normal file
View File

@ -0,0 +1,156 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "Helper.hpp"
#include "FileMapper.hpp"
#include "RSACipher.hpp"
#include "Solutions.hpp"
#define SAFE_DELETE(x) { delete x; x = nullptr; }
void help() {
puts("Usage:");
puts(" ./navicat-patcher <navicat executable file> [RSA-2048 PrivateKey(PEM file)]");
puts("");
}
bool LoadKey(RSACipher* cipher, const char* filename,
Patcher::Solution* pSolution0,
Patcher::Solution* pSolution1) {
if (filename) {
if (!cipher->ImportKeyFromFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::PEM>(filename)) {
REPORT_ERROR("ERROR: cipher->ImportKeyFromFile failed.");
return false;
}
if ((pSolution0 && !pSolution0->CheckKey(cipher)) ||
(pSolution1 && !pSolution1->CheckKey(cipher))) {
REPORT_ERROR("ERROR: The RSA private key you provided cannot be used.");
return false;
}
} else {
PRINT_MESSAGE("");
PRINT_MESSAGE("MESSAGE: Generating new RSA private key, it may take a long time.");
do {
cipher->GenerateKey(2048);
} while ((pSolution0 && !pSolution0->CheckKey(cipher)) ||
(pSolution1 && !pSolution1->CheckKey(cipher))); // re-generate RSA key if one of CheckKey return false
if (!cipher->ExportKeyToFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::NotSpecified>("RegPrivateKey.pem")) {
REPORT_ERROR("ERROR: Failed to save RSA private key.");
return false;
}
PRINT_MESSAGE("MESSAGE: New RSA private key has been saved to RegPrivateKey.pem.");
}
std::string PublicKeyString = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
if (PublicKeyString.empty()) {
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
return false;
}
PRINT_MESSAGE("");
PRINT_MESSAGE("Your RSA public key:");
PRINT_MESSAGE(PublicKeyString.c_str());
return true;
}
int main(int argc, char* argv[], char* envp[]) {
int status = 0;
FileMapper MainApp;
off_t MainAppSize;
Helper::ResourceGuard<RSACipher> cipher(nullptr);
Helper::ResourceGuard<Patcher::Solution> pSolution0(nullptr);
Helper::ResourceGuard<Patcher::Solution> pSolution1(nullptr);
if (argc != 2 && argc != 3) {
help();
return status;
}
PRINT_MESSAGE("NOTICE:");
printf("This patcher will modify the file: %s\n", argv[1]);
PRINT_MESSAGE("Please make a backup by your own if you care. Otherwise just ignore this notice.");
PRINT_MESSAGE("Press Enter to continue OR Ctrl+C to abort...");
getchar();
cipher.ptr = RSACipher::Create();
if (cipher.ptr == nullptr) {
REPORT_ERROR("ERROR: RSACipher::Create failed.");
return status;
}
pSolution0.ptr = new Patcher::Solution0();
pSolution1.ptr = new Patcher::Solution1();
//
// Map file
//
if (!MainApp.OpenFile(argv[1])) {
status = errno;
REPORT_ERROR_WITH_CODE("Failed to open file.");
return status;
} else {
PRINT_MESSAGE("MESSAGE: Open file successfully.");
}
if (!MainApp.GetFileSize(MainAppSize)) {
status = errno;
REPORT_ERROR_WITH_CODE("Failed to get file size.");
return status;
} else {
printf("MESSAGE: Get file size successfully: %lld\n", MainAppSize);
}
if (!MainApp.Map(static_cast<size_t>(MainAppSize))) {
status = errno;
REPORT_ERROR_WITH_CODE("Failed to map file.");
return status;
} else {
PRINT_MESSAGE("MESSAGE: Map file successfully.");
}
pSolution0.ptr->SetFile(&MainApp);
pSolution1.ptr->SetFile(&MainApp);
//
// Find patch offsets
//
if (!pSolution0.ptr->FindPatchOffset()) {
PRINT_MESSAGE("MESSAGE: Solution0: Cannot find public key. Solution0 will be omitted.");
pSolution0.ptr->SetFile(nullptr);
SAFE_DELETE(pSolution0.ptr);
}
if (!pSolution1.ptr->FindPatchOffset()) {
PRINT_MESSAGE("MESSAGE: Solution1: Cannot find public key. Solution1 will be omitted.");
pSolution1.ptr->SetFile(nullptr);
SAFE_DELETE(pSolution1.ptr);
}
//
// Load or generate RSA-2048 key
//
if (!LoadKey(cipher.ptr, argc == 3 ? argv[2] : nullptr, pSolution0.ptr, pSolution1.ptr))
return status;
//
// Making patch
//
if (pSolution0.ptr && !pSolution0.ptr->MakePatch(cipher.ptr))
return status;
if (pSolution1.ptr && !pSolution1.ptr->MakePatch(cipher.ptr))
return status;
//
// Report result
//
if (pSolution0.ptr)
PRINT_MESSAGE("Solution0 has been done successfully.");
if (pSolution1.ptr)
PRINT_MESSAGE("Solution1 has been done successfully.");
return status;
}

View File

@ -1,9 +0,0 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxqkTcfbKw8ysVygePlcB
oUAhCF6oniyP13iDtu85ZsHwqw8PnMyTp6n6FnMN9YinleIAy6NFveBu/vshTN8S
oXbYyy5AqdZ8CQpfvuriO9UNfgV1l7SFdPPpruFAmOw+uzA3GawMsg3QNK/htqJe
b4xKHFS04xC2AueE2RTmk6tJcL8TEBfRG7DEYOHPjebKl1NQ3ZIu15U97cCPYKO2
pWHzsb+Fr4Wj0DChLoxlXxaBcJ2ozogaq0tW2t4Aopvt9kRSuSK9HcgxICJM5ct4
naU91WFGWlw0+0JpiMIl5OnMbpak/5xQre9DL8zM8LjRy14I88txvXvhPEsWaYCO
1QIDAQAB
-----END PUBLIC KEY-----