mirror of
https://github.com/DoubleLabyrinth/navicat-keygen.git
synced 2019-12-02 05:10:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
599ac96c30 | ||
|
|
e61b0f9e8e | ||
|
|
a69805e470 | ||
|
|
f1394f6aeb | ||
|
|
6272f56d01 | ||
|
|
ea583e9797 | ||
|
|
316314f9bb | ||
|
|
c05f755957 | ||
|
|
e37e72e2fa | ||
|
|
e4c55e0fa0 | ||
|
|
b32aa80331 | ||
|
|
0d0c2a2ba7 | ||
|
|
e6964a1da3 | ||
|
|
ddf0ff7443 | ||
|
|
8e74008d01 | ||
|
|
dd6d0713e6 | ||
|
|
e4e433ec94 | ||
|
|
bcce475faa | ||
|
|
88ccb398a0 | ||
|
|
7e0705dd9d | ||
|
|
193ae6bdfa | ||
|
|
23508443c4 | ||
|
|
cae2eb95ff | ||
|
|
9f1f661752 | ||
|
|
ee22a9ed24 | ||
|
|
50fbcc52c3 | ||
|
|
96cf594fe0 | ||
|
|
d24a420ce5 | ||
|
|
41b319332a | ||
|
|
0ca18231b5 | ||
|
|
1ec9c0cea9 | ||
|
|
83e43f5178 | ||
|
|
fc82c87597 | ||
|
|
5e9a98c71d |
+2
-289
@@ -1,289 +1,2 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
MailDataReciever/Renci.SshNet.dll
|
||||
*.DS_Store
|
||||
bin/*
|
||||
@@ -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.
|
||||
|
||||
@@ -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软件中填入 __激活码__ 即可完成离线激活。
|
||||
@@ -0,0 +1,89 @@
|
||||
CC = g++
|
||||
OPENSSL_INCLUDE_PATH = /usr/local/opt/openssl/include
|
||||
OPENSSL_LIB_PATH = /usr/local/opt/openssl/lib
|
||||
CAPSTONE_INCLUDE_PATH = #/usr/local/Cellar/capstone/4.0.1/include
|
||||
CAPSTONE_LIB_PATH = #/usr/local/Cellar/capstone/4.0.1/lib
|
||||
KEYSTONE_INCLUDE_PATH = #/usr/local/Cellar/keystone/0.9.1/include
|
||||
KEYSTONE_LIB_PATH = #/usr/local/Cellar/keystone/0.9.1/lib
|
||||
RAPIDJSON_INCLUDE_PATH = #/usr/local/Cellar/rapidjson/1.1.0/include
|
||||
LIBPLIST_INCLUDE_PATH = #/usr/local/Cellar/libplist/2.0.0_1/include
|
||||
LIBPLIST_LIB_PATH = #/usr/local/Cellar/libplist/2.0.0_1/lib
|
||||
|
||||
OUTPUT_DIR = ./bin/
|
||||
COMMON_DIR = ./common/
|
||||
PATCHER_DIR = ./navicat-patcher/
|
||||
KEYGEN_DIR = ./navicat-keygen/
|
||||
|
||||
COMMON_HEADER = \
|
||||
$(COMMON_DIR)Exception.hpp \
|
||||
$(COMMON_DIR)ExceptionOpenssl.hpp \
|
||||
$(COMMON_DIR)ExceptionSystem.hpp \
|
||||
$(COMMON_DIR)ResourceOwned.hpp \
|
||||
$(COMMON_DIR)ResourceTraitsOpenssl.hpp \
|
||||
$(COMMON_DIR)RSACipher.hpp \
|
||||
|
||||
PATCHER_HEADER = \
|
||||
$(PATCHER_DIR)ExceptionCapstone.hpp \
|
||||
$(PATCHER_DIR)ExceptionKeystone.hpp \
|
||||
$(PATCHER_DIR)ResourceTraitsCapstone.hpp \
|
||||
$(PATCHER_DIR)ResourceTraitsKeystone.hpp \
|
||||
$(PATCHER_DIR)ResourceTraitsUnix.hpp \
|
||||
$(PATCHER_DIR)CapstoneDisassembler.hpp \
|
||||
$(PATCHER_DIR)KeystoneAssembler.hpp \
|
||||
$(PATCHER_DIR)X64ImageInterpreter.hpp \
|
||||
$(PATCHER_DIR)PatchSolutions.hpp
|
||||
|
||||
PATCHER_SOURCE = \
|
||||
$(PATCHER_DIR)CapstoneDisassembler.cpp \
|
||||
$(PATCHER_DIR)KeystoneAssembler.cpp \
|
||||
$(PATCHER_DIR)X64ImageInterpreter.cpp \
|
||||
$(PATCHER_DIR)HelperIsResolvedTo.cpp \
|
||||
$(PATCHER_DIR)HelperPrintMemory.cpp \
|
||||
$(PATCHER_DIR)PatchSolution0.cpp \
|
||||
$(PATCHER_DIR)PatchSolution1.cpp \
|
||||
$(PATCHER_DIR)PatchSolution2.cpp \
|
||||
$(PATCHER_DIR)main.cpp
|
||||
|
||||
PATCHER_BINARY = $(OUTPUT_DIR)navicat-patcher
|
||||
|
||||
KEYGEN_HEADER = \
|
||||
$(KEYGEN_DIR)DESCipher.hpp \
|
||||
$(KEYGEN_DIR)NavicatKeygen.hpp
|
||||
|
||||
KEYGEN_SOURCE = \
|
||||
$(KEYGEN_DIR)Base64.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++17 -O2 \
|
||||
-I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) \
|
||||
$(if $(CAPSTONE_INCLUDE_PATH),-I$(CAPSTONE_INCLUDE_PATH),) $(if $(CAPSTONE_LIB_PATH),-L$(CAPSTONE_LIB_PATH),) \
|
||||
$(if $(KEYSTONE_INCLUDE_PATH),-I$(KEYSTONE_INCLUDE_PATH),) $(if $(KEYSTONE_LIB_PATH),-L$(KEYSTONE_LIB_PATH),) \
|
||||
$(if $(LIBPLIST_INCLUDE_PATH),-I$(LIBPLIST_INCLUDE_PATH),) $(if $(LIBPLIST_LIB_PATH),-L$(LIBPLIST_LIB_PATH),) \
|
||||
-lcrypto -lcapstone -lkeystone -lplist++ $(PATCHER_SOURCE) -o $(PATCHER_BINARY)
|
||||
@echo
|
||||
|
||||
keygen: $(KEYGEM_HEADER) $(KEYGEN_SOURCE)
|
||||
@if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi
|
||||
$(CC) -std=c++17 -O2 \
|
||||
-I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) \
|
||||
$(if $(RAPIDJSON_INCLUDE_PATH),-I$(RAPIDJSON_INCLUDE_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
|
||||
|
||||
@@ -1,156 +1,319 @@
|
||||
# Navicat Keygen
|
||||
|
||||
This repository will tell you how Navicat offline activation works.
|
||||
[中文版README](README.zh-CN.md)
|
||||
|
||||
## 1. Keyword Explanation.
|
||||
This repository will tell you how Navicat offline activation works.
|
||||
|
||||
* __Navicat Activation Public Key__
|
||||
[How does it work?](HOW_DOES_IT_WORK.md)
|
||||
|
||||
It is a __RSA-2048__ public key that Navicat used to encrypt or decrypt offline activation information.
|
||||
__NOTICE: This keygen only supports Navicat Premium.__
|
||||
|
||||
It is stored in __navicat.exe__ as a kind of resource called __RCData__. The resource name is `"ActivationPubKey"`. You can see it by a kind of software [___Resource Hacker___](http://www.angusj.com/resourcehacker/). The concrete content is:
|
||||
## 1. How to build
|
||||
|
||||
> -----BEGIN PUBLIC KEY-----
|
||||
> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I
|
||||
> qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv
|
||||
> a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF
|
||||
> R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2
|
||||
> WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt
|
||||
> YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ
|
||||
> awIDAQAB
|
||||
> -----END PUBLIC KEY-----
|
||||
* Before you build keygen, you should make sure you have following libs:
|
||||
|
||||
```
|
||||
openssl
|
||||
capstone
|
||||
keystone
|
||||
rapidjson
|
||||
libplist
|
||||
```
|
||||
|
||||
You can install them by
|
||||
|
||||
```shell
|
||||
$ brew install openssl
|
||||
$ brew install capstone
|
||||
$ brew install keystone
|
||||
$ brew install rapidjson
|
||||
$ brew install libplist
|
||||
```
|
||||
|
||||
* Clone `mac` branch and build keygen and patcher:
|
||||
|
||||
```shell
|
||||
$ git clone -b mac --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git
|
||||
$ cd navicat-keygen
|
||||
$ make all
|
||||
```
|
||||
|
||||
You will see two executable files in `bin/` directory:
|
||||
|
||||
```shell
|
||||
$ ls bin/
|
||||
navicat-keygen navicat-patcher
|
||||
```
|
||||
|
||||
## 2. How to Use
|
||||
|
||||
1. Build keygen and patcher.
|
||||
|
||||
2. Backup all of your saved database connection configurations (with password).
|
||||
|
||||
3. Remove all connections, if have, that Navicat saved in `Keychain Access.app`.
|
||||
|
||||
You can find them by search with keyword `navicat` in `Keychain Access.app`.
|
||||
|
||||
4. Use `navicat-patcher` to replace __Navicat Activation Public Key__.
|
||||
|
||||
```
|
||||
Usage:
|
||||
navicat-patcher <Navicat installation path> [RSA-2048 Private Key File]
|
||||
|
||||
<Navicat installation path> Path to `Navicat Premium.app`.
|
||||
Example:
|
||||
/Applications/Navicat\ Premium.app/
|
||||
This parameter must be specified.
|
||||
|
||||
[RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.
|
||||
This parameter is optional.
|
||||
```
|
||||
|
||||
* `<Navicat installation path>`: The path to `Navicat Premium.app`.
|
||||
|
||||
__This parameter must be specified.__
|
||||
|
||||
* `[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:__
|
||||
|
||||
```console
|
||||
$ ./navicat-patcher /Applications/Navicat\ Premium.app/
|
||||
```
|
||||
|
||||
It has been tested on __Navicat Premium 12.1.24 For Mac Simplified Chinese__ version.
|
||||
|
||||
The following is an example of output:
|
||||
|
||||
```console
|
||||
$ ./navicat-patcher /Applications/Navicat\ Premium.app/
|
||||
***************************************************
|
||||
* Navicat Patcher by @DoubleLabyrinth *
|
||||
* Version: 4.0 *
|
||||
***************************************************
|
||||
|
||||
Press Enter to continue or Ctrl + C to abort.
|
||||
|
||||
[*] Your Navicat version: 12.1.24
|
||||
|
||||
[+] PatchSolution0 ...... Ready to apply.
|
||||
Keyword offset = +0x024a7db8
|
||||
[-] PatchSolution1 ...... Omitted.
|
||||
[+] PatchSolution2 ...... Ready to apply.
|
||||
Function offset = +0x00ec9868
|
||||
Keyword offset = +0x0263fd60
|
||||
std::string::append(const char*) RVA = 0x000000010214b726
|
||||
|
||||
[*] Generating new RSA private key, it may take a long time...
|
||||
[+] New RSA private key has been saved to RegPrivateKey.pem.
|
||||
|
||||
[*] Your RSA public key:
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA030QIDXRx372bGrne9kp
|
||||
uuAqpxaxJX0x6LVaOf8+Uan2SQnFH8frnuMDRg5PjdBnFWEJGqZmRD1fNkLOhhCE
|
||||
iFZWxrDgJcuEBrv5VlduQ4hlYIulcf6qilBZUaaX9Kb3R7+H8ClMb00HwLc/Iht5
|
||||
bd9krhU3CT3g2ZG00GxVhEF4a/zZMDjeuQvTUeeubIeriT/2YC+w/tKfGbqWvjC6
|
||||
wkbjXGbVICSiKzhzztS4BHbtQMl8v6doMhFVd/PEDNFQrbkEr3kbk/oD8AccL8iz
|
||||
aV17UHt4VW2fR8tMyTvcuhTaUtWmt/tL6Z1RzCqH+KvTv8GpH8qFcty89YXja7dL
|
||||
kQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
**************************************************************
|
||||
* PatchSolution0 *
|
||||
**************************************************************
|
||||
@+0x024a7db8
|
||||
Previous:
|
||||
-0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG
|
||||
+0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY---
|
||||
+0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh
|
||||
+0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA
|
||||
...
|
||||
...
|
||||
...
|
||||
After:
|
||||
-0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG
|
||||
+0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY---
|
||||
+0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh
|
||||
+0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
**************************************************************
|
||||
* PatchSolution2 *
|
||||
**************************************************************
|
||||
@+0x0263fd60
|
||||
Previous:
|
||||
+0x0000000000000000 42 49 6a 57 79 6f 65 52 52 30 4e 42 67 6b 71 6e BIjWyoeRR0NBgkqn
|
||||
+0x0000000000000010 44 5a 57 78 43 67 4b 43 45 41 77 31 64 71 46 33 DZWxCgKCEAw1dqF3
|
||||
+0x0000000000000020 44 54 76 4f 42 39 31 5a 48 77 65 63 4a 59 46 72 DTvOB91ZHwecJYFr
|
||||
+0x0000000000000030 64 4d 31 4b 45 68 31 79 56 65 52 6f 47 71 53 64 dM1KEh1yVeRoGqSd
|
||||
+0x0000000000000040 4c 4c 47 5a 47 55 6c 6e 67 69 67 33 4f 44 35 6d LLGZGUlngig3OD5m
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
@+0x00ec9868
|
||||
Previous:
|
||||
-0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV
|
||||
+0x0000000000000008 53 48 83 ec 38 49 89 fe c6 45 e5 01 31 c0 88 45 SH..8I...E..1..E
|
||||
+0x0000000000000018 e6 88 45 e7 48 8d 35 c5 70 61 01 48 8d 5d b0 48 ..E.H.5.pa.H.].H
|
||||
+0x0000000000000028 89 df e8 49 d9 ff ff 48 8d 35 ef ed 60 01 48 89 ...I...H.5..`.H.
|
||||
+0x0000000000000038 df e8 80 1e 28 01 e8 3f f4 00 00 88 45 e5 e8 b7 ....(..?....E...
|
||||
+0x0000000000000048 f4 00 00 88 45 e6 e8 2f f5 00 00 88 45 e7 f6 45 ....E../....E..E
|
||||
After:
|
||||
-0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV
|
||||
+0x0000000000000008 53 48 83 ec 48 48 89 fb 48 31 c0 48 89 04 24 48 SH..HH..H1.H..$H
|
||||
+0x0000000000000018 89 44 24 08 48 89 44 24 10 48 8d 3c 24 48 8d 35 .D$.H.D$.H.<$H.5
|
||||
+0x0000000000000028 cc 64 77 01 e8 8d 1e 28 01 48 8b 04 24 48 89 03 .dw....(.H..$H..
|
||||
+0x0000000000000038 48 8b 44 24 08 48 89 43 08 48 8b 44 24 10 48 89 H.D$.H.C.H.D$.H.
|
||||
+0x0000000000000048 43 10 48 89 d8 48 83 c4 48 5b 41 5e 41 5f 5d c3 C.H..H..H[A^A_].
|
||||
|
||||
[+] PatchSolution0 has been applied.
|
||||
[+] PatchSolution2 has been applied.
|
||||
|
||||
**************************************************************
|
||||
* Patch has been done successfully. Have fun and enjoy~~ *
|
||||
* DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! *
|
||||
**************************************************************
|
||||
```
|
||||
|
||||
* __FOR Navicat Premium version < 12.0.24 ONLY:__
|
||||
|
||||
`navicat-patcher` will abort and won't apply any patch.
|
||||
|
||||
You should use openssl to generate `RegPrivateKey.pem` and `rpk` file.
|
||||
|
||||
```console
|
||||
$ openssl genrsa -out RegPrivateKey.pem 2048
|
||||
$ openssl rsa -in RegPrivateKey.pem -pubout -out rpk
|
||||
```
|
||||
|
||||
Then replace
|
||||
|
||||
```
|
||||
/Applications/Navicat Premium.app/Contents/Resources/rpk
|
||||
```
|
||||
|
||||
by `rpk` you just generated.
|
||||
|
||||
5. __Generate a self-signed code-sign certificate and always trust it.__
|
||||
|
||||
__Then use `codesign` to re-sign `Navicat Premium.app`.__
|
||||
|
||||
```console
|
||||
$ codesign -f -s "Your self-signed code-sign certificate name" <path to Navicat Premium.app>
|
||||
```
|
||||
|
||||
__NOTICE:__
|
||||
|
||||
"Your self-signed code-sign certificate name" is the name of your certificate in `Keychain Access.app`, not path.
|
||||
|
||||
__Example:__
|
||||
|
||||
```console
|
||||
$ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/
|
||||
```
|
||||
|
||||
6. Then use `navicat-keygen` to generate __snKey__ and __Activation Code__.
|
||||
|
||||
```
|
||||
Usage:
|
||||
navicat-keygen <RSA-2048 Private Key File>
|
||||
|
||||
<RSA-2048 Private Key File> Path to a PEM-format RSA-2048 private key file.
|
||||
This parameter must be specified.
|
||||
```
|
||||
|
||||
* `<RSA-2048 Private Key File>`: Path to a PEM-format RSA-2048 private key file.
|
||||
|
||||
__This parameter must be specified.__
|
||||
|
||||
__Example:__
|
||||
|
||||
```console
|
||||
$ ./navicat-keygen ./RegPrivateKey.pem
|
||||
```
|
||||
|
||||
You will be asked to select Navicat language and give major version number. After that an randomly generated __snKey__ will be given.
|
||||
|
||||
```console
|
||||
$ ./navicat-keygen ./RegPrivateKey.pem
|
||||
***************************************************
|
||||
* Navicat Keygen by @DoubleLabyrinth *
|
||||
* Version: 4.0 *
|
||||
***************************************************
|
||||
|
||||
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
|
||||
|
||||
If you have the corresponding private key, please tell me. I would be very appreciated for your generous.
|
||||
(Input index)> 1
|
||||
|
||||
* __Request Code__
|
||||
(Input major version number, range: 0 ~ 15, default: 12)> 12
|
||||
|
||||
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__.
|
||||
Serial number:
|
||||
NAVG-Z5H9-NK2L-MAZJ
|
||||
|
||||
* __Offline Activation Request Information__
|
||||
Your name:
|
||||
```
|
||||
|
||||
It is just a JSON-style ASCII string which contains 3 items. Respectively they are `"K"`, `"DI"` and `"P"`, which represent __snKey__, __checksum__ (related with your machine and OS), __Platform__ (Appropriately speaking, it should be OS Type).
|
||||
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.
|
||||
|
||||
Like:
|
||||
> {"K": "xxxxxxxxxxxxxxxx", "DI": "yyyyyyyyyyyyy", "P": "WIN8"}
|
||||
```console
|
||||
Your name: DoubleLabyrinth
|
||||
Your organization: DoubleLabyrinth
|
||||
Input request code (in Base64), input empty line to end:
|
||||
```
|
||||
|
||||
After that, you will be asked to input request code. Now __DO NOT CLOSE KEYGEN__.
|
||||
|
||||
* __Activation Code__
|
||||
7. __Disconnect your network__ and open Navicat Premium.
|
||||
|
||||
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).
|
||||
Find and click `Registration`.
|
||||
|
||||
Fill license key by __Serial number__ that the keygen gave and click `Activate`.
|
||||
|
||||
* __Offline Activation Response Information__
|
||||
8. Generally online activation will fail and Navicat will ask you do `Manual Activation`, just choose it.
|
||||
|
||||
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`'.
|
||||
9. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends.
|
||||
|
||||
`"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__.
|
||||
```console
|
||||
Your name: DoubleLabyrinth
|
||||
Your organization: DoubleLabyrinth
|
||||
|
||||
`"N"`, `"O"`, `"T"` represent __Name__, __Organization__, __Time__ respectively. __Name__ and __Organization__ are string and the type of __Time__ is unknown.
|
||||
Input request code (in Base64), input empty line to end:
|
||||
Nuk6pouXNhuGnqb2rBbxpDOiCFxhdJF4/gteYA/UZFUwqmhhphn3PAErvlxCtbUCf9Lzw02gfIFog3gmTB1C5JzPdeE5uuD6SAvhlQ7ZVOmdA66dvt6mDDpuf78cGio1Rpkd0D/6dLzgHnFJJPOfPtlIT5ZOLDiWkiSJm8d83+ckMBoMtcvpXCiwDIGb1KfVZwsgLojyrrO5OzakIzd2xQ8r3mEmbVbMl/zD0S5fO4agxEOp2WvpmM1cqom9Egll7kgcQG8A0z1Abqo1PrVBjjOsb/v8wy5V/469G6/uDT4AkZQSz8m/TX9ZQlZE3QBlzrJ+sTEkpMVhw3h3u6l4JQ==
|
||||
|
||||
`"T"` can be omitted.
|
||||
Request Info:
|
||||
{"K":"NAVGZ5H9NK2LMAZJ", "DI":"NGVlZjdjOTViMzYyMWI0", "P":"MAC"}
|
||||
|
||||
* __snKey__
|
||||
Response Info:
|
||||
{"K":"NAVGZ5H9NK2LMAZJ","DI":"NGVlZjdjOTViMzYyMWI0","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1564415636}
|
||||
|
||||
It is a 4-block-long string, while every block is 4-chars-long.
|
||||
License:
|
||||
Vd4QUzEw6DPNpJLYVKV6ZNDny0gsZWCXbKyrf2nF27iTM35YUBouXEcAB/Vy355V2z++7iXe/coKmV4kNZbywlBchI5ts7gOHnhXWzBYQ3yKsBYKob/7sxaiw7CXCmhM4mPLMzrp5okewCWjBjb51keZ4SA3F6j8HGIVYiZW3CAZtkjxs9uUoXvVIJr+Gt83TgU+sqiC4oSplokopAql2zWPieA9KuhPoCKiGLMvuQwv0wWWPc2HorY0AHAetsyZ8MN4utZ2ylQ9z/ZojwX1KViyh3xxnjWF7xXJljIdBA4tCi4QDqDLvTuICfUV7VeKzOUY+ZKCO0xGxkTe1HVwog==
|
||||
```
|
||||
|
||||
__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.
|
||||
10. Finally, you will get __Activation Code__ which looks like a Base64 string.
|
||||
|
||||
1. __data[0]__ and __data[1]__ must be `0x68` and `0x2A` respectively.
|
||||
Just copy it and paste it in Navicat `Manual Activation` window, then click `Activate`.
|
||||
|
||||
If nothing wrong, activation should be done successfully.
|
||||
|
||||
_`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.
|
||||
|
||||
_`May change when Navicat product changes. Uncertain yet. But it's very possible right.`_
|
||||
|
||||
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.`_
|
||||
|
||||
For __Navicat 12 x64 Simplified Chinese version__: They must be `0xCE` and `0x32` respectively.
|
||||
For __Navicat 12 x64 Traditional Chinese version__: They must be `0xAA` and `0x99` respectively.
|
||||
For __Navicat 11 x64 Simplified Chinese version__: They must be `0xCE` and `0x32` respectively.
|
||||
|
||||
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`.
|
||||
|
||||
_`Must change when version change. Confirmed by Navicat 12 for Mac x64 with IDA Pro 7.0`_
|
||||
|
||||
6. __data[9]__ is unknown, but you can set it `0xFD` or `0xFC` or `0xFB` if you want to use __not-for-resale license__.
|
||||
|
||||
_`May change when Navicat product changes. Uncertain yet.`_
|
||||
|
||||
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: __(Use Base32 encode if you just want a conclusion.)__
|
||||
|
||||
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
|
||||
char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
# Navicat Keygen
|
||||
|
||||
这份repo将会告诉你Navicat是怎么完成离线激活的。
|
||||
|
||||
[注册机是怎么工作的?](HOW_DOES_IT_WORK.zh-CN.md)
|
||||
|
||||
## 1. 如何编译
|
||||
|
||||
* 在编译之前,你应该确保你有如下几个库:
|
||||
|
||||
```
|
||||
openssl
|
||||
capstone
|
||||
keystone
|
||||
rapidjson
|
||||
libplist
|
||||
```
|
||||
|
||||
如果你有`brew`的话,你可以通过
|
||||
|
||||
```
|
||||
$ brew install openssl
|
||||
$ brew install capstone
|
||||
$ brew install keystone
|
||||
$ brew install rapidjson
|
||||
$ brew install libplist
|
||||
```
|
||||
|
||||
来完成它们的安装。
|
||||
|
||||
* Clone `mac` 分支,并编译keygen和patcher
|
||||
|
||||
```bash
|
||||
$ git clone -b mac --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git
|
||||
$ cd navicat-keygen
|
||||
$ make all
|
||||
```
|
||||
|
||||
编译完成后你会在 `bin/` 文件夹下看到两个可执行文件:
|
||||
|
||||
```bash
|
||||
$ ls bin/
|
||||
navicat-keygen navicat-patcher
|
||||
```
|
||||
|
||||
## 2. 如何使用这个Keygen
|
||||
|
||||
1. 编译好keygen和patcher。
|
||||
|
||||
2. 备份好Navicat中所有已保存的数据库连接(包括密码)。
|
||||
|
||||
3. 移除所有Navicat在 `Keychain Access.app` (即钥匙链)中保存的连接,如果有的话。
|
||||
|
||||
你可以通过在 `Keychain Access.app` 中搜索关键词 `navicat` 来找到它们。
|
||||
|
||||
4. 使用`navicat-patcher`替换掉公钥:
|
||||
|
||||
```
|
||||
Usage:
|
||||
navicat-patcher <Navicat installation path> [RSA-2048 Private Key File]
|
||||
|
||||
<Navicat installation path> Path to `Navicat Premium.app`.
|
||||
Example:
|
||||
/Applications/Navicat\ Premium.app/
|
||||
This parameter must be specified.
|
||||
|
||||
[RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.
|
||||
This parameter is optional.
|
||||
```
|
||||
|
||||
* `<Navicat installation path>`: `Navicat Premium.app` 的路径。
|
||||
|
||||
__这个参数必须指定。__
|
||||
|
||||
* `[RSA-2048 PrivateKey(PEM file)]`: PEM格式的RSA-2048私钥文件路径。
|
||||
|
||||
__这个参数是可选的。__
|
||||
|
||||
如果没有指定,`navicat-patcher`将会在当前目录下生成一个新的RSA-2048私钥文件`RegPrivateKey.pem`。
|
||||
|
||||
__例如:__
|
||||
|
||||
```console
|
||||
$ ./navicat-patcher /Applications/Navicat\ Premium.app/
|
||||
```
|
||||
|
||||
__Navicat Premium For Mac 12.1.24 简体中文版__ 已通过测试。下面将是一份样例输出:
|
||||
|
||||
```console
|
||||
$ ./navicat-patcher /Applications/Navicat\ Premium.app/
|
||||
***************************************************
|
||||
* Navicat Patcher by @DoubleLabyrinth *
|
||||
* Version: 4.0 *
|
||||
***************************************************
|
||||
|
||||
Press Enter to continue or Ctrl + C to abort.
|
||||
|
||||
[*] Your Navicat version: 12.1.24
|
||||
|
||||
[+] PatchSolution0 ...... Ready to apply.
|
||||
Keyword offset = +0x024a7db8
|
||||
[-] PatchSolution1 ...... Omitted.
|
||||
[+] PatchSolution2 ...... Ready to apply.
|
||||
Function offset = +0x00ec9868
|
||||
Keyword offset = +0x0263fd60
|
||||
std::string::append(const char*) RVA = 0x000000010214b726
|
||||
|
||||
[*] Generating new RSA private key, it may take a long time...
|
||||
[+] New RSA private key has been saved to RegPrivateKey.pem.
|
||||
|
||||
[*] Your RSA public key:
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA030QIDXRx372bGrne9kp
|
||||
uuAqpxaxJX0x6LVaOf8+Uan2SQnFH8frnuMDRg5PjdBnFWEJGqZmRD1fNkLOhhCE
|
||||
iFZWxrDgJcuEBrv5VlduQ4hlYIulcf6qilBZUaaX9Kb3R7+H8ClMb00HwLc/Iht5
|
||||
bd9krhU3CT3g2ZG00GxVhEF4a/zZMDjeuQvTUeeubIeriT/2YC+w/tKfGbqWvjC6
|
||||
wkbjXGbVICSiKzhzztS4BHbtQMl8v6doMhFVd/PEDNFQrbkEr3kbk/oD8AccL8iz
|
||||
aV17UHt4VW2fR8tMyTvcuhTaUtWmt/tL6Z1RzCqH+KvTv8GpH8qFcty89YXja7dL
|
||||
kQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
||||
**************************************************************
|
||||
* PatchSolution0 *
|
||||
**************************************************************
|
||||
@+0x024a7db8
|
||||
Previous:
|
||||
-0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG
|
||||
+0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY---
|
||||
+0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh
|
||||
+0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA
|
||||
...
|
||||
...
|
||||
...
|
||||
After:
|
||||
-0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG
|
||||
+0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY---
|
||||
+0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh
|
||||
+0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
**************************************************************
|
||||
* PatchSolution2 *
|
||||
**************************************************************
|
||||
@+0x0263fd60
|
||||
Previous:
|
||||
+0x0000000000000000 42 49 6a 57 79 6f 65 52 52 30 4e 42 67 6b 71 6e BIjWyoeRR0NBgkqn
|
||||
+0x0000000000000010 44 5a 57 78 43 67 4b 43 45 41 77 31 64 71 46 33 DZWxCgKCEAw1dqF3
|
||||
+0x0000000000000020 44 54 76 4f 42 39 31 5a 48 77 65 63 4a 59 46 72 DTvOB91ZHwecJYFr
|
||||
+0x0000000000000030 64 4d 31 4b 45 68 31 79 56 65 52 6f 47 71 53 64 dM1KEh1yVeRoGqSd
|
||||
+0x0000000000000040 4c 4c 47 5a 47 55 6c 6e 67 69 67 33 4f 44 35 6d LLGZGUlngig3OD5m
|
||||
...
|
||||
...
|
||||
...
|
||||
|
||||
@+0x00ec9868
|
||||
Previous:
|
||||
-0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV
|
||||
+0x0000000000000008 53 48 83 ec 38 49 89 fe c6 45 e5 01 31 c0 88 45 SH..8I...E..1..E
|
||||
+0x0000000000000018 e6 88 45 e7 48 8d 35 c5 70 61 01 48 8d 5d b0 48 ..E.H.5.pa.H.].H
|
||||
+0x0000000000000028 89 df e8 49 d9 ff ff 48 8d 35 ef ed 60 01 48 89 ...I...H.5..`.H.
|
||||
+0x0000000000000038 df e8 80 1e 28 01 e8 3f f4 00 00 88 45 e5 e8 b7 ....(..?....E...
|
||||
+0x0000000000000048 f4 00 00 88 45 e6 e8 2f f5 00 00 88 45 e7 f6 45 ....E../....E..E
|
||||
After:
|
||||
-0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV
|
||||
+0x0000000000000008 53 48 83 ec 48 48 89 fb 48 31 c0 48 89 04 24 48 SH..HH..H1.H..$H
|
||||
+0x0000000000000018 89 44 24 08 48 89 44 24 10 48 8d 3c 24 48 8d 35 .D$.H.D$.H.<$H.5
|
||||
+0x0000000000000028 cc 64 77 01 e8 8d 1e 28 01 48 8b 04 24 48 89 03 .dw....(.H..$H..
|
||||
+0x0000000000000038 48 8b 44 24 08 48 89 43 08 48 8b 44 24 10 48 89 H.D$.H.C.H.D$.H.
|
||||
+0x0000000000000048 43 10 48 89 d8 48 83 c4 48 5b 41 5e 41 5f 5d c3 C.H..H..H[A^A_].
|
||||
|
||||
[+] PatchSolution0 has been applied.
|
||||
[+] PatchSolution2 has been applied.
|
||||
|
||||
**************************************************************
|
||||
* Patch has been done successfully. Have fun and enjoy~~ *
|
||||
* DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! *
|
||||
**************************************************************
|
||||
```
|
||||
|
||||
* __仅对 Navicat Premium 版本 < 12.0.24 的说明:__
|
||||
|
||||
如果你的Navicat版本小于12.0.24,那么`navicat-patcher`将会终止并且不会修改目标文件。
|
||||
|
||||
你必须使用openssl生成`RegPrivateKey.pem`和`rpk`文件:
|
||||
|
||||
```console
|
||||
$ openssl genrsa -out RegPrivateKey.pem 2048
|
||||
$ openssl rsa -in RegPrivateKey.pem -pubout -out rpk
|
||||
```
|
||||
|
||||
接着用刚生成的`rpk`文件替换
|
||||
|
||||
```
|
||||
/Applications/Navicat Premium.app/Contents/Resources/rpk
|
||||
```
|
||||
|
||||
5. __生成一份自签名的代码证书,并总是信任该证书。这一步非常重要。__
|
||||
|
||||
__然后用`codesign`对`Navicat Premium.app`重签名。__
|
||||
|
||||
```console
|
||||
$ codesign -f -s "Your self-signed code-sign certificate name" <path to Navicat Premium.app>
|
||||
```
|
||||
|
||||
__注意:__
|
||||
|
||||
"Your self-signed code-sign certificate name"是你证书的名字,不是路径。
|
||||
|
||||
__例如:__
|
||||
|
||||
```console
|
||||
$ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/
|
||||
```
|
||||
|
||||
6. 接下来使用`navicat-keygen`来生成 __序列号__ 和 __激活码__。
|
||||
|
||||
```
|
||||
Usage:
|
||||
navicat-keygen <RSA-2048 Private Key File>
|
||||
|
||||
<RSA-2048 Private Key File> Path to a PEM-format RSA-2048 private key file.
|
||||
This parameter must be specified.
|
||||
```
|
||||
|
||||
* `<RSA-2048 Private Key File>`: PEM格式的RSA-2048私钥文件路径。
|
||||
|
||||
__这个参数必须指定。__
|
||||
|
||||
__例如:__
|
||||
|
||||
```console
|
||||
$ ./navicat-keygen ./RegPrivateKey.pem
|
||||
```
|
||||
|
||||
你会被要求选择Navicat的语言以及输入主版本号。之后会随机生成一个 __序列号__。
|
||||
|
||||
```console
|
||||
$ ./navicat-keygen ./RegPrivateKey.pem
|
||||
***************************************************
|
||||
* Navicat Keygen by @DoubleLabyrinth *
|
||||
* Version: 4.0 *
|
||||
***************************************************
|
||||
|
||||
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
|
||||
|
||||
(Input index)> 1
|
||||
|
||||
(Input major version number, range: 0 ~ 15, default: 12)> 12
|
||||
|
||||
Serial number:
|
||||
NAVG-Z5H9-NK2L-MAZJ
|
||||
|
||||
Your name:
|
||||
```
|
||||
|
||||
你可以使用这个 __序列号__ 暂时激活Navicat。
|
||||
|
||||
接下来你会被要求输入`用户名`和`组织名`;请随便填写,但不要太长。
|
||||
|
||||
```console
|
||||
Your name: DoubleLabyrinth
|
||||
Your organization: DoubleLabyrinth
|
||||
Input request code (in Base64), input empty line to end:
|
||||
```
|
||||
|
||||
之后你会被要求填入请求码。注意 __不要关闭注册机__。
|
||||
|
||||
7. __断开网络__ 并打开Navicat。
|
||||
|
||||
找到`注册`窗口,填入注册机给你的序列号。然后点击`激活`按钮。
|
||||
|
||||
8. 一般来说在线激活肯定会失败,这时候Navicat会询问你是否`手动激活`,直接选吧。
|
||||
|
||||
9. 在`手动激活`窗口你会得到一个请求码,复制它并把它粘贴到keygen里。最后别忘了连按至少两下回车结束输入。
|
||||
|
||||
```console
|
||||
Your name: DoubleLabyrinth
|
||||
Your organization: DoubleLabyrinth
|
||||
|
||||
Input request code (in Base64), input empty line to end:
|
||||
Nuk6pouXNhuGnqb2rBbxpDOiCFxhdJF4/gteYA/UZFUwqmhhphn3PAErvlxCtbUCf9Lzw02gfIFog3gmTB1C5JzPdeE5uuD6SAvhlQ7ZVOmdA66dvt6mDDpuf78cGio1Rpkd0D/6dLzgHnFJJPOfPtlIT5ZOLDiWkiSJm8d83+ckMBoMtcvpXCiwDIGb1KfVZwsgLojyrrO5OzakIzd2xQ8r3mEmbVbMl/zD0S5fO4agxEOp2WvpmM1cqom9Egll7kgcQG8A0z1Abqo1PrVBjjOsb/v8wy5V/469G6/uDT4AkZQSz8m/TX9ZQlZE3QBlzrJ+sTEkpMVhw3h3u6l4JQ==
|
||||
|
||||
Request Info:
|
||||
{"K":"NAVGZ5H9NK2LMAZJ", "DI":"NGVlZjdjOTViMzYyMWI0", "P":"MAC"}
|
||||
|
||||
Response Info:
|
||||
{"K":"NAVGZ5H9NK2LMAZJ","DI":"NGVlZjdjOTViMzYyMWI0","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1564415636}
|
||||
|
||||
License:
|
||||
Vd4QUzEw6DPNpJLYVKV6ZNDny0gsZWCXbKyrf2nF27iTM35YUBouXEcAB/Vy355V2z++7iXe/coKmV4kNZbywlBchI5ts7gOHnhXWzBYQ3yKsBYKob/7sxaiw7CXCmhM4mPLMzrp5okewCWjBjb51keZ4SA3F6j8HGIVYiZW3CAZtkjxs9uUoXvVIJr+Gt83TgU+sqiC4oSplokopAql2zWPieA9KuhPoCKiGLMvuQwv0wWWPc2HorY0AHAetsyZ8MN4utZ2ylQ9z/ZojwX1KViyh3xxnjWF7xXJljIdBA4tCi4QDqDLvTuICfUV7VeKzOUY+ZKCO0xGxkTe1HVwog==
|
||||
```
|
||||
|
||||
10. 如果不出意外,你会得到一个看似用Base64编码的激活码。
|
||||
|
||||
直接复制它,并把它粘贴到Navicat的`手动激活`窗口,最后点`激活`按钮。
|
||||
|
||||
如果没什么意外的话应该能成功激活。
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
#include <stddef.h> // NOLINT
|
||||
#include <stdint.h> // NOLINT
|
||||
|
||||
namespace nkg {
|
||||
|
||||
class Exception {
|
||||
private:
|
||||
|
||||
const char* pvt_File;
|
||||
const char* pvt_Message;
|
||||
size_t pvt_Line;
|
||||
|
||||
public:
|
||||
|
||||
Exception(const char* File, size_t Line, const char* Message) noexcept :
|
||||
pvt_File(File),
|
||||
pvt_Message(Message),
|
||||
pvt_Line(Line) {}
|
||||
|
||||
[[nodiscard]]
|
||||
const char* File() const noexcept {
|
||||
return pvt_File;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
size_t Line() const noexcept {
|
||||
return pvt_Line;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const char* Message() const noexcept {
|
||||
return pvt_Message;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual bool HasErrorCode() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual intptr_t ErrorCode() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
virtual const char* ErrorString() const noexcept {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <openssl/err.h>
|
||||
#include "Exception.hpp"
|
||||
|
||||
namespace nkg {
|
||||
|
||||
class OpensslError final : public Exception {
|
||||
private:
|
||||
|
||||
unsigned long pvt_ErrorCode;
|
||||
|
||||
public:
|
||||
|
||||
OpensslError(const char* File, unsigned Line, unsigned long ErrorCode, const char* Message) noexcept :
|
||||
Exception(File, Line, Message),
|
||||
pvt_ErrorCode(ErrorCode) {}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual intptr_t ErrorCode() const noexcept override {
|
||||
return pvt_ErrorCode;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
ERR_load_crypto_strings();
|
||||
return ERR_reason_error_string(pvt_ErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "Exception.hpp"
|
||||
#include <string.h> // NOLINT
|
||||
|
||||
namespace nkg {
|
||||
|
||||
class SystemError final : public Exception {
|
||||
private:
|
||||
|
||||
int pvt_ErrorCode;
|
||||
|
||||
public:
|
||||
|
||||
SystemError(const char* File, unsigned Line, int ErrorCode, const char* Message) noexcept :
|
||||
Exception(File, Line, Message),
|
||||
pvt_ErrorCode(ErrorCode) {}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual intptr_t ErrorCode() const noexcept override {
|
||||
return pvt_ErrorCode;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return strerror(pvt_ErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
#pragma once
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ExceptionOpenssl.hpp"
|
||||
#include "ResourceOwned.hpp"
|
||||
#include "ResourceTraitsOpenssl.hpp"
|
||||
|
||||
enum class RSAKeyType {
|
||||
PrivateKey,
|
||||
PublicKey
|
||||
};
|
||||
|
||||
enum class RSAKeyFormat {
|
||||
PEM,
|
||||
PKCS1
|
||||
};
|
||||
|
||||
class RSACipher {
|
||||
private:
|
||||
ResourceOwned<OpensslRSATraits> pvt_RsaObj;
|
||||
|
||||
template<RSAKeyType __Type, RSAKeyFormat __Format>
|
||||
static void pvt_WriteRSAToBIO(RSA* lpRSA, BIO* lpBIO) {
|
||||
if constexpr (__Type == RSAKeyType::PrivateKey) {
|
||||
if (PEM_write_bio_RSAPrivateKey(lpBIO, lpRSA, nullptr, nullptr, 0, nullptr, nullptr) == 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPrivateKey failed.");
|
||||
}
|
||||
} else {
|
||||
if constexpr (__Format == RSAKeyFormat::PEM) {
|
||||
if (PEM_write_bio_RSA_PUBKEY(lpBIO, lpRSA) == 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSA_PUBKEY failed.");
|
||||
}
|
||||
} else if constexpr (__Format == RSAKeyFormat::PKCS1) {
|
||||
if (PEM_write_bio_RSAPublicKey(lpBIO, lpRSA) == 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPublicKey failed.");
|
||||
}
|
||||
} else {
|
||||
static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<RSAKeyType _Type, RSAKeyFormat _Format>
|
||||
[[nodiscard]]
|
||||
static RSA* pvt_ReadRSAFromBIO(BIO* lpBIO) {
|
||||
RSA* lpRSA;
|
||||
|
||||
if constexpr (_Type == RSAKeyType::PrivateKey) {
|
||||
lpRSA = PEM_read_bio_RSAPrivateKey(lpBIO, nullptr, nullptr, nullptr);
|
||||
if (lpRSA == nullptr) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPrivateKey failed.");
|
||||
}
|
||||
} else {
|
||||
if constexpr (_Format == RSAKeyFormat::PEM) {
|
||||
lpRSA = PEM_read_bio_RSA_PUBKEY(lpBIO, nullptr, nullptr, nullptr);
|
||||
if (lpRSA == nullptr) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, " -> PEM_read_bio_RSA_PUBKEY failed.");
|
||||
}
|
||||
} else if constexpr (_Format == RSAKeyFormat::PKCS1) {
|
||||
lpRSA = PEM_read_bio_RSAPublicKey(lpBIO, nullptr, nullptr, nullptr);
|
||||
if (lpRSA == nullptr) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPublicKey failed.");
|
||||
}
|
||||
} else {
|
||||
static_assert(_Format == RSAKeyFormat::PEM || _Format == RSAKeyFormat::PKCS1);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
return lpRSA;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
RSACipher() : pvt_RsaObj(OpensslRSATraits{}, RSA_new()) {
|
||||
if (pvt_RsaObj.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_new failed.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
size_t Bits() const {
|
||||
#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x
|
||||
if (pvt_RsaObj->n == nullptr) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "RSA modulus has not been set.");
|
||||
} else {
|
||||
return BN_num_bits(pvt_RsaObj->n);
|
||||
}
|
||||
#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x
|
||||
return RSA_bits(pvt_RsaObj);
|
||||
#else
|
||||
#error "Unexpected openssl version!"
|
||||
#endif
|
||||
}
|
||||
|
||||
void GenerateKey(int bits, unsigned int e = RSA_F4) {
|
||||
ResourceOwned bn_e(OpensslBNTraits{}, BN_new());
|
||||
|
||||
if (bn_e.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "BN_new failed.");
|
||||
}
|
||||
|
||||
if (!BN_set_word(bn_e, e)) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BN_set_word failed.");
|
||||
}
|
||||
|
||||
if (!RSA_generate_key_ex(pvt_RsaObj, bits, bn_e, nullptr)) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_generate_key_ex failed.");
|
||||
}
|
||||
}
|
||||
|
||||
template<RSAKeyType __Type, RSAKeyFormat __Format>
|
||||
void ExportKeyToFile(const std::string& FileName) const {
|
||||
ResourceOwned BioFile(OpensslBIOTraits{}, BIO_new_file(FileName.c_str(), "w"));
|
||||
|
||||
if (BioFile.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new_file failed.");
|
||||
}
|
||||
|
||||
pvt_WriteRSAToBIO<__Type, __Format>(pvt_RsaObj, BioFile);
|
||||
}
|
||||
|
||||
template<RSAKeyType __Type, RSAKeyFormat __Format>
|
||||
[[nodiscard]]
|
||||
std::string ExportKeyString() const {
|
||||
ResourceOwned BioMemory(OpensslBIOTraits{}, BIO_new(BIO_s_mem()));
|
||||
long StringLength;
|
||||
const char* StringChars = nullptr;
|
||||
|
||||
if (BioMemory.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed.");
|
||||
}
|
||||
|
||||
pvt_WriteRSAToBIO<__Type, __Format>(pvt_RsaObj, BioMemory);
|
||||
|
||||
StringLength = BIO_get_mem_data(BioMemory, &StringChars);
|
||||
|
||||
return std::string(StringChars, StringLength);
|
||||
}
|
||||
|
||||
template<RSAKeyType __Type, RSAKeyFormat __Format>
|
||||
void ImportKeyFromFile(const std::string& FileName) {
|
||||
ResourceOwned BioFile(OpensslBIOTraits{}, BIO_new_file(FileName.c_str(), "r"));
|
||||
|
||||
if (BioFile.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new_file failed.");
|
||||
}
|
||||
|
||||
pvt_RsaObj.TakeOver(pvt_ReadRSAFromBIO<__Type, __Format>(BioFile));
|
||||
}
|
||||
|
||||
template<RSAKeyType __Type, RSAKeyFormat __Format>
|
||||
void ImportKeyString(const std::string& KeyString) {
|
||||
ResourceOwned BioMemory(OpensslBIOTraits{}, BIO_new(BIO_s_mem()));
|
||||
RSA* NewRsaObj;
|
||||
|
||||
if (BioMemory.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed.");
|
||||
}
|
||||
|
||||
if (BIO_puts(BioMemory, KeyString.c_str()) <= 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_puts failed.");
|
||||
}
|
||||
|
||||
pvt_RsaObj.TakeOver(pvt_ReadRSAFromBIO<__Type, __Format>(BioMemory));
|
||||
}
|
||||
|
||||
template<RSAKeyType __Type = RSAKeyType::PublicKey>
|
||||
size_t Encrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const {
|
||||
int BytesWritten;
|
||||
|
||||
if (cbFrom > INT_MAX) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Length overflowed.");
|
||||
}
|
||||
|
||||
if constexpr (__Type == RSAKeyType::PrivateKey) {
|
||||
BytesWritten = RSA_private_encrypt(
|
||||
static_cast<int>(cbFrom),
|
||||
reinterpret_cast<const unsigned char*>(lpFrom),
|
||||
reinterpret_cast<unsigned char*>(lpTo),
|
||||
pvt_RsaObj,
|
||||
Padding
|
||||
);
|
||||
|
||||
if (BytesWritten == -1) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_encrypt failed.");
|
||||
}
|
||||
} else {
|
||||
BytesWritten = RSA_public_encrypt(
|
||||
static_cast<int>(cbFrom),
|
||||
reinterpret_cast<const unsigned char*>(lpFrom),
|
||||
reinterpret_cast<unsigned char*>(lpTo),
|
||||
pvt_RsaObj,
|
||||
Padding
|
||||
);
|
||||
|
||||
if (BytesWritten == -1) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_encrypt failed.");
|
||||
}
|
||||
}
|
||||
|
||||
return BytesWritten;
|
||||
}
|
||||
|
||||
template<RSAKeyType __Type = RSAKeyType::PrivateKey>
|
||||
size_t Decrypt(const void* lpFrom, int cbFrom, void* lpTo, int Padding) const {
|
||||
int BytesWritten;
|
||||
|
||||
if (cbFrom > INT_MAX) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Length overflowed.");
|
||||
}
|
||||
|
||||
if constexpr (__Type == RSAKeyType::PrivateKey) {
|
||||
BytesWritten = RSA_private_decrypt(
|
||||
cbFrom,
|
||||
reinterpret_cast<const unsigned char*>(lpFrom),
|
||||
reinterpret_cast<unsigned char*>(lpTo),
|
||||
pvt_RsaObj,
|
||||
Padding
|
||||
);
|
||||
|
||||
if (BytesWritten == -1) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_decrypt failed.");
|
||||
}
|
||||
} else {
|
||||
BytesWritten = RSA_public_decrypt(
|
||||
cbFrom,
|
||||
reinterpret_cast<const unsigned char*>(lpFrom),
|
||||
reinterpret_cast<unsigned char*>(lpTo),
|
||||
pvt_RsaObj,
|
||||
Padding
|
||||
);
|
||||
|
||||
if (BytesWritten == -1) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception
|
||||
throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_decrypt failed.");
|
||||
}
|
||||
}
|
||||
|
||||
return BytesWritten;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,304 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
template<typename __ResourceTraits, typename __LambdaReleasor = void>
|
||||
class ResourceOwned {
|
||||
private:
|
||||
|
||||
using __HandleType = typename __ResourceTraits::HandleType;
|
||||
static_assert(std::is_pod_v<__HandleType>);
|
||||
|
||||
__HandleType pvt_Handle;
|
||||
__LambdaReleasor pvt_Releasor;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Construct from custom releasor.
|
||||
// ``pvt_Handle` will be set to an invalid value.
|
||||
//
|
||||
ResourceOwned(__ResourceTraits, __LambdaReleasor&& Releasor) noexcept :
|
||||
pvt_Handle(__ResourceTraits::InvalidValue),
|
||||
pvt_Releasor(std::forward<__LambdaReleasor>(Releasor)) {}
|
||||
|
||||
//
|
||||
// Construct from handle given and custom releasor.
|
||||
//
|
||||
ResourceOwned(__ResourceTraits, const __HandleType& Handle, __LambdaReleasor&& Releasor) noexcept :
|
||||
pvt_Handle(Handle),
|
||||
pvt_Releasor(std::forward<__LambdaReleasor>(Releasor)) {}
|
||||
|
||||
//
|
||||
// ResourceOwned doesn't allow to copy.
|
||||
// Because it takes `pvt_Handle` exclusively.
|
||||
//
|
||||
ResourceOwned(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete;
|
||||
|
||||
//
|
||||
// ResourceOwned allows to move.
|
||||
//
|
||||
ResourceOwned(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept :
|
||||
pvt_Handle(Other.pvt_Handle),
|
||||
pvt_Releasor(std::move(Other.pvt_Releasor))
|
||||
{
|
||||
Other.pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
|
||||
//
|
||||
// ResourceOwned doesn't allow to copy.
|
||||
// Because it takes `pvt_Handle` exclusively.
|
||||
//
|
||||
ResourceOwned<__ResourceTraits, __LambdaReleasor>&
|
||||
operator=(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete;
|
||||
|
||||
//
|
||||
// ResourceOwned allows to move.
|
||||
//
|
||||
ResourceOwned<__ResourceTraits, __LambdaReleasor>&
|
||||
operator=(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept {
|
||||
this->pvt_Handle = Other.pvt_Handle;
|
||||
this->pvt_Releasor = std::move(Other.pvt_Releasor);
|
||||
Other.pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion.
|
||||
return this->pvt_Handle;
|
||||
}
|
||||
|
||||
template<typename __AsType, bool __IsPointer = std::is_pointer_v<__HandleType>, typename = std::enable_if_t<__IsPointer>>
|
||||
[[nodiscard]]
|
||||
__AsType As() const noexcept {
|
||||
return reinterpret_cast<__AsType>(this->pvt_Handle);
|
||||
}
|
||||
|
||||
template<bool __IsPointer = std::is_pointer_v<__HandleType>, typename = typename std::enable_if_t<__IsPointer>>
|
||||
[[nodiscard]]
|
||||
__HandleType operator->() const noexcept {
|
||||
return this->pvt_Handle;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool IsValid() const noexcept {
|
||||
return __ResourceTraits::IsValid(this->pvt_Handle);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const __HandleType& Get() const noexcept {
|
||||
return this->pvt_Handle;
|
||||
}
|
||||
|
||||
template<typename __ReturnType = __HandleType*>
|
||||
[[nodiscard]]
|
||||
__ReturnType GetAddress() noexcept {
|
||||
return reinterpret_cast<__ReturnType>(&this->pvt_Handle);
|
||||
}
|
||||
|
||||
void TakeOver(const __HandleType& Handle) {
|
||||
if (__ResourceTraits::IsValid(this->pvt_Handle) == true) {
|
||||
this->pvt_Releasor(this->pvt_Handle);
|
||||
}
|
||||
this->pvt_Handle = Handle;
|
||||
}
|
||||
|
||||
void Discard() noexcept {
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
__HandleType Transfer() noexcept {
|
||||
__HandleType t = this->pvt_Handle;
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
return t;
|
||||
}
|
||||
|
||||
void Release() {
|
||||
if (__ResourceTraits::IsValid(this->pvt_Handle)) {
|
||||
this->pvt_Releasor(this->pvt_Handle);
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
}
|
||||
|
||||
~ResourceOwned() {
|
||||
if (__ResourceTraits::IsValid(this->pvt_Handle)) {
|
||||
this->pvt_Releasor(this->pvt_Handle);
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename __ResourceTraits>
|
||||
class ResourceOwned<__ResourceTraits, void> {
|
||||
private:
|
||||
|
||||
using __HandleType = typename __ResourceTraits::HandleType;
|
||||
static_assert(std::is_pod_v<__HandleType>);
|
||||
|
||||
__HandleType pvt_Handle;
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Construct from custom releasor.
|
||||
// ``pvt_Handle` will be set to an invalid value.
|
||||
//
|
||||
explicit
|
||||
ResourceOwned(__ResourceTraits) noexcept :
|
||||
pvt_Handle(__ResourceTraits::InvalidValue) {}
|
||||
|
||||
//
|
||||
// Construct from handle given and custom releasor.
|
||||
//
|
||||
ResourceOwned(__ResourceTraits, const __HandleType& Handle) noexcept :
|
||||
pvt_Handle(Handle) {}
|
||||
|
||||
//
|
||||
// ResourceOwned doesn't allow to copy.
|
||||
// Because it takes `pvt_Handle` exclusively.
|
||||
//
|
||||
ResourceOwned(const ResourceOwned<__ResourceTraits, void>& Other) = delete;
|
||||
|
||||
//
|
||||
// ResourceOwned allows to move.
|
||||
//
|
||||
ResourceOwned(ResourceOwned<__ResourceTraits, void>&& Other) noexcept :
|
||||
pvt_Handle(Other.pvt_Handle)
|
||||
{
|
||||
Other.pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
|
||||
//
|
||||
// ResourceOwned doesn't allow to copy.
|
||||
// Because it takes `pvt_Handle` exclusively.
|
||||
//
|
||||
ResourceOwned<__ResourceTraits, void>&
|
||||
operator=(const ResourceOwned<__ResourceTraits, void>& Other) = delete;
|
||||
|
||||
//
|
||||
// ResourceOwned allows to move.
|
||||
//
|
||||
ResourceOwned<__ResourceTraits, void>&
|
||||
operator=(ResourceOwned<__ResourceTraits, void>&& Other) noexcept {
|
||||
this->pvt_Handle = Other.pvt_Handle;
|
||||
Other.pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion.
|
||||
return this->pvt_Handle;
|
||||
}
|
||||
|
||||
template<typename __AsType, bool __IsPointer = std::is_pointer_v<__HandleType>, typename = typename std::enable_if_t<__IsPointer>>
|
||||
[[nodiscard]]
|
||||
__AsType As() const noexcept {
|
||||
return reinterpret_cast<__AsType>(this->pvt_Handle);
|
||||
}
|
||||
|
||||
template<bool __IsPointer = std::is_pointer_v<__HandleType>, typename = typename std::enable_if_t<__IsPointer>>
|
||||
[[nodiscard]]
|
||||
__HandleType operator->() const noexcept {
|
||||
return this->pvt_Handle;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool IsValid() const noexcept {
|
||||
return __ResourceTraits::IsValid(this->pvt_Handle);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const __HandleType& Get() const noexcept {
|
||||
return this->pvt_Handle;
|
||||
}
|
||||
|
||||
template<typename __ReturnType = __HandleType*>
|
||||
[[nodiscard]]
|
||||
__ReturnType GetAddress() noexcept {
|
||||
return reinterpret_cast<__ReturnType>(&this->pvt_Handle);
|
||||
}
|
||||
|
||||
void TakeOver(const __HandleType& Handle) {
|
||||
if (__ResourceTraits::IsValid(this->pvt_Handle) == true) {
|
||||
__ResourceTraits::Releasor(this->pvt_Handle);
|
||||
}
|
||||
this->pvt_Handle = Handle;
|
||||
}
|
||||
|
||||
void Discard() noexcept {
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
__HandleType Transfer() noexcept {
|
||||
__HandleType t = this->pvt_Handle;
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
return t;
|
||||
}
|
||||
|
||||
void Release() {
|
||||
if (__ResourceTraits::IsValid(this->pvt_Handle)) {
|
||||
__ResourceTraits::Releasor(this->pvt_Handle);
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
}
|
||||
|
||||
~ResourceOwned() {
|
||||
if (__ResourceTraits::IsValid(this->pvt_Handle)) {
|
||||
__ResourceTraits::Releasor(this->pvt_Handle);
|
||||
this->pvt_Handle = __ResourceTraits::InvalidValue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename __ResourceTraits>
|
||||
ResourceOwned(__ResourceTraits) ->
|
||||
ResourceOwned<__ResourceTraits, void>;
|
||||
|
||||
template<typename __ResourceTraits, typename __ArgType>
|
||||
ResourceOwned(__ResourceTraits, __ArgType&&) ->
|
||||
ResourceOwned<
|
||||
__ResourceTraits,
|
||||
std::conditional_t<
|
||||
std::is_same_v<std::remove_cv_t<std::remove_reference_t<__ArgType>>, typename __ResourceTraits::HandleType> == false,
|
||||
std::remove_reference_t<__ArgType>,
|
||||
void
|
||||
>
|
||||
>;
|
||||
|
||||
template<typename __ResourceTraits, typename __LambdaReleasor>
|
||||
ResourceOwned(__ResourceTraits, const typename __ResourceTraits::HandleType&, __LambdaReleasor&&) ->
|
||||
ResourceOwned<__ResourceTraits, std::remove_reference_t<__LambdaReleasor>>;
|
||||
|
||||
template<typename __ClassType>
|
||||
struct CppObjectTraits {
|
||||
using HandleType = __ClassType*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) {
|
||||
delete Handle;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename __ClassType>
|
||||
struct CppDynamicArrayTraits {
|
||||
using HandleType = __ClassType*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) {
|
||||
delete[] Handle;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
struct OpensslBIOTraits {
|
||||
using HandleType = BIO*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) noexcept {
|
||||
BIO_free(Handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct OpensslBIOChainTraits {
|
||||
using HandleType = BIO*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) noexcept {
|
||||
BIO_free_all(Handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct OpensslBNTraits {
|
||||
using HandleType = BIGNUM*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) noexcept {
|
||||
BN_free(Handle);
|
||||
}
|
||||
};
|
||||
|
||||
struct OpensslRSATraits {
|
||||
using HandleType = RSA*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) noexcept {
|
||||
RSA_free(Handle);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "navicat-keygen", "navicat-keygen\navicat-keygen.vcxproj", "{CB49F054-83AB-4C16-B73B-33D7DF696E16}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "navicat-patcher", "navicat-patcher\navicat-patcher.vcxproj", "{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Debug|x64.Build.0 = Debug|x64
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Debug|x86.Build.0 = Debug|Win32
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Release|x64.ActiveCfg = Release|x64
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Release|x64.Build.0 = Release|x64
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Release|x86.ActiveCfg = Release|Win32
|
||||
{CB49F054-83AB-4C16-B73B-33D7DF696E16}.Release|x86.Build.0 = Release|Win32
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Debug|x64.Build.0 = Debug|x64
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Debug|x86.Build.0 = Debug|Win32
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Release|x64.ActiveCfg = Release|x64
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Release|x64.Build.0 = Release|x64
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Release|x86.ActiveCfg = Release|Win32
|
||||
{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <openssl/evp.h>
|
||||
#include "../common/Exception.hpp"
|
||||
#include "../common/ResourceOwned.hpp"
|
||||
#include "../common/ResourceTraitsOpenssl.hpp"
|
||||
|
||||
std::string base64_encode(const std::vector<uint8_t>& bindata) {
|
||||
ResourceOwned b64(OpensslBIOTraits{}, BIO_new(BIO_f_base64()));
|
||||
if (b64.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed.");
|
||||
}
|
||||
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
ResourceOwned mem(OpensslBIOTraits{}, BIO_new(BIO_s_mem()));
|
||||
if (mem.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed.");
|
||||
}
|
||||
|
||||
BIO_push(b64, mem);
|
||||
|
||||
if (BIO_write(b64, bindata.data(), bindata.size()) != bindata.size()) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_write failed.");
|
||||
}
|
||||
|
||||
if (BIO_flush(b64) != 1) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_flush failed.");
|
||||
}
|
||||
|
||||
const char* data = nullptr;
|
||||
auto len = BIO_get_mem_data(mem, &data);
|
||||
|
||||
BIO_pop(b64);
|
||||
|
||||
return std::string(data, len);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> base64_decode(const std::string& ascdata) {
|
||||
ResourceOwned b64(OpensslBIOTraits{}, BIO_new(BIO_f_base64()));
|
||||
if (b64.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed.");
|
||||
}
|
||||
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
ResourceOwned mem(OpensslBIOTraits{}, BIO_new_mem_buf(ascdata.c_str(), -1));
|
||||
if (mem.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed.");
|
||||
}
|
||||
|
||||
BIO_push(b64, mem);
|
||||
|
||||
std::vector<uint8_t> bindata(ascdata.length() / 4 * 3 + 1);
|
||||
bindata.resize(BIO_read(b64, bindata.data(), bindata.size()));
|
||||
|
||||
BIO_pop(b64);
|
||||
|
||||
return bindata;
|
||||
}
|
||||
@@ -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* lpBuffer) noexcept {
|
||||
DES_cblock block;
|
||||
DES_ecb_encrypt(reinterpret_cast<const_DES_cblock*>(lpBuffer), &block, &_Schedule, DES_ENCRYPT);
|
||||
memcpy(lpBuffer, &block, sizeof(block));
|
||||
}
|
||||
|
||||
void DecryptBlock(void* lpBuffer) noexcept {
|
||||
DES_cblock block;
|
||||
DES_ecb_encrypt(reinterpret_cast<const_DES_cblock*>(lpBuffer), &block, &_Schedule, DES_DECRYPT);
|
||||
memcpy(lpBuffer, &block, sizeof(block));
|
||||
}
|
||||
|
||||
~DESCipher() noexcept {
|
||||
Clear();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 uint8_t 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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
|
||||
#ifndef UNICODE
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/des.h>
|
||||
|
||||
#pragma comment(lib, "Crypt32.lib")
|
||||
#pragma comment(lib, "libcrypto.lib")
|
||||
|
||||
#define NAVICAT_12
|
||||
|
||||
void GenerateSnKey(char(&SnKey)[16]) {
|
||||
static char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
static DES_cblock DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
|
||||
|
||||
BYTE temp_snKey[10] = { 0x68, 0x2a }; // must start with 0x68, 0x2a
|
||||
temp_snKey[2] = rand();
|
||||
temp_snKey[3] = rand();
|
||||
temp_snKey[4] = rand();
|
||||
temp_snKey[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
|
||||
// Must be 0xAA for Traditional Chinese version.
|
||||
|
||||
temp_snKey[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
|
||||
// Must be 0x99 for Traditional Chinese version.
|
||||
|
||||
#if defined(NAVICAT_12)
|
||||
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.
|
||||
#elif defined(NAVICAT_11)
|
||||
temp_snKey[7] = 0x15; // 0x15 - commercial, 0x16 - non-commercial
|
||||
temp_snKey[8] = 0xB0; // High 4-bits = version number. Low 4-bits doesn't know, but can be used to delay activation time.
|
||||
#endif
|
||||
temp_snKey[9] = 0x70; // 0xfd, 0xfc, 0xfb if you want to use not-for-resale license.
|
||||
|
||||
DES_key_schedule schedule;
|
||||
DES_set_key_unchecked(&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_s(temp_snKey + 2, sizeof(enc_temp_snKey), 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];
|
||||
|
||||
_tprintf_s(TEXT("\r\n"));
|
||||
_tprintf_s(TEXT("SnKey:\r\n"));
|
||||
_tprintf_s(TEXT("%.4hs-%.4hs-%.4hs-%.4hs\r\n"), SnKey, SnKey + 4, SnKey + 8, SnKey + 12);
|
||||
_tprintf_s(TEXT("\r\n"));
|
||||
}
|
||||
|
||||
BOOL GenerateLicense(RSA* RSAPrivateKey,
|
||||
const char* SnKey,
|
||||
const char* Name,
|
||||
const char* Organization,
|
||||
const char* DI) {
|
||||
|
||||
char LicenseJson[2048 / 8] = { };
|
||||
#if defined(NAVICAT_12)
|
||||
sprintf_s(LicenseJson, "{\"K\":\"%.16s\", \"N\":\"%s\", \"O\":\"%s\", \"DI\":\"%s\"}", SnKey, Name, Organization, DI);
|
||||
#elif defined(NAVICAT_11)
|
||||
sprintf_s(LicenseJson, "{\"K\":\"%.16s\", \"N\":\"%s\", \"O\":\"%s\"}", SnKey, Name, Organization);
|
||||
#endif
|
||||
unsigned char License[2048 / 8] = { };
|
||||
RSA_private_encrypt(strlen(LicenseJson),
|
||||
reinterpret_cast<uint8_t*>(LicenseJson),
|
||||
License,
|
||||
RSAPrivateKey,
|
||||
RSA_PKCS1_PADDING);
|
||||
|
||||
#if defined(NAVICAT_12)
|
||||
DWORD LicenseStringLength = 1024;
|
||||
TCHAR LicenseString[1024] = { };
|
||||
if (!CryptBinaryToString(License, sizeof(License), CRYPT_STRING_BASE64, LicenseString, &LicenseStringLength)) {
|
||||
_tprintf_s(TEXT("Cannot get Base64 string. CODE: 0x%08x\r\n"), GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_tprintf_s(TEXT("License:\r\n%s"), LicenseString);
|
||||
return TRUE;
|
||||
#elif defined(NAVICAT_11)
|
||||
HANDLE hLicenseFile = CreateFile(TEXT("license_file"), GENERIC_ALL, NULL, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hLicenseFile == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!WriteFile(hLicenseFile, License, sizeof(License), nullptr, nullptr)) {
|
||||
CloseHandle(hLicenseFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CloseHandle(hLicenseFile);
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
int _tmain(int argc, TCHAR* argv[]) {
|
||||
if (argc != 2) {
|
||||
_tprintf_s(TEXT("Usage:\r\n"));
|
||||
_tprintf_s(TEXT(" navicat-keygen.exe <RSA-2048 PrivateKey(PEM file)>\r\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
srand(time(nullptr));
|
||||
|
||||
#ifdef UNICODE
|
||||
char pem_file_path[256] = { };
|
||||
sprintf_s(pem_file_path, "%S", argv[1]);
|
||||
#else
|
||||
char* pem_file_path = argv[1];
|
||||
#endif
|
||||
|
||||
RSA* PrivateKey = nullptr;
|
||||
BIO* PrivateKeyFile = BIO_new(BIO_s_file());
|
||||
BIO_read_filename(PrivateKeyFile, pem_file_path);
|
||||
PrivateKey = PEM_read_bio_RSAPrivateKey(PrivateKeyFile, nullptr, nullptr, nullptr);
|
||||
BIO_free_all(PrivateKeyFile);
|
||||
|
||||
if (PrivateKey == nullptr) {
|
||||
_tprintf_s(TEXT("Failed to load private key.\r\n"));
|
||||
return -2;
|
||||
}
|
||||
|
||||
TCHAR tName[64] = { };
|
||||
TCHAR tOrganization[64] = { };
|
||||
{
|
||||
DWORD ReadCount;
|
||||
|
||||
_tprintf_s(TEXT("Your name: "));
|
||||
ReadConsole(GetStdHandle(STD_INPUT_HANDLE), tName, 64, &ReadCount, nullptr);
|
||||
tName[ReadCount - 2] = 0;
|
||||
tName[ReadCount - 1] = 0;
|
||||
_tprintf_s(TEXT("Your organization: "));
|
||||
ReadConsole(GetStdHandle(STD_INPUT_HANDLE), tOrganization, 64, &ReadCount, nullptr);
|
||||
tOrganization[ReadCount - 2] = 0;
|
||||
tOrganization[ReadCount - 1] = 0;
|
||||
}
|
||||
|
||||
#ifdef UNICODE
|
||||
char Name[64] = { };
|
||||
char Organization[64] = { };
|
||||
|
||||
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
tName, _tcslen(tName),
|
||||
Name, 64,
|
||||
NULL,
|
||||
NULL) == 0) {
|
||||
_tprintf_s(TEXT("Failed to convert name to UTF-8. CODE: 0x%08x\r\n"), GetLastError());
|
||||
RSA_free(PrivateKey);
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
tOrganization, _tcslen(tOrganization),
|
||||
Organization, 64,
|
||||
NULL,
|
||||
NULL) == 0) {
|
||||
_tprintf_s(TEXT("Failed to convert name to UTF-8. CODE: 0x%08x\r\n"), GetLastError());
|
||||
RSA_free(PrivateKey);
|
||||
return GetLastError();
|
||||
}
|
||||
#else
|
||||
char* Name = tName;
|
||||
char* Organization = tOrganization;
|
||||
#endif
|
||||
|
||||
char SnKey[16] = { };
|
||||
GenerateSnKey(SnKey);
|
||||
|
||||
TCHAR Base64String[1024] = { };
|
||||
_tprintf_s(TEXT("Input request code (in Base64), empty line to return:\r\n"));
|
||||
for (TCHAR* cur = Base64String; cur < Base64String + 1024;) {
|
||||
DWORD ReadCount;
|
||||
if (!ReadConsole(GetStdHandle(STD_INPUT_HANDLE), cur, Base64String + 1024 - cur, &ReadCount, NULL))
|
||||
break;
|
||||
|
||||
cur += ReadCount;
|
||||
#ifdef UNICODE
|
||||
if (*reinterpret_cast<uint32_t*>(cur - 4) == '\r\0\n\0')
|
||||
break;
|
||||
#else
|
||||
if (*reinterpret_cast<uint32_t*>(cur - 4) == '\r\n\r\n')
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
Base64String[1024 - 1] = NULL;
|
||||
|
||||
BYTE enc_request_code[1024] = { };
|
||||
DWORD enc_request_code_length = 1024;
|
||||
if (!CryptStringToBinary(Base64String, NULL, CRYPT_STRING_BASE64, enc_request_code, &enc_request_code_length, NULL, NULL)) {
|
||||
_tprintf_s(TEXT("Failed to decode Base64 string. CODE: 0x%08x\r\n"), GetLastError());
|
||||
RSA_free(PrivateKey);
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
char request_code[1024] = { };
|
||||
if (!RSA_private_decrypt(enc_request_code_length,
|
||||
enc_request_code,
|
||||
reinterpret_cast<BYTE*>(request_code),
|
||||
PrivateKey, RSA_PKCS1_PADDING)) {
|
||||
_tprintf_s(TEXT("Failed to decrypt request code.\r\n"));
|
||||
RSA_free(PrivateKey);
|
||||
return -3;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifdef UNICODE
|
||||
_tprintf_s(TEXT("%S\r\n"), request_code);
|
||||
#else
|
||||
_tprintf_s(TEXT("%s\r\n"), request_code);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
|
||||
if (strlen(request_code) >= 256) {
|
||||
_tprintf_s(TEXT("Not a valid request code.\r\n"));
|
||||
RSA_free(PrivateKey);
|
||||
return -4;
|
||||
}
|
||||
|
||||
char* DI_ptr = request_code;
|
||||
for (; DI_ptr < request_code + 256; DI_ptr++) {
|
||||
if (*reinterpret_cast<uint32_t*>(DI_ptr) == '"ID"') {
|
||||
DI_ptr += 4;
|
||||
while (*DI_ptr++ != '"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DI_ptr >= request_code + 256) {
|
||||
_tprintf_s(TEXT("Not a valid request code.\r\n"));
|
||||
RSA_free(PrivateKey);
|
||||
return -5;
|
||||
}
|
||||
|
||||
for (char* ptr = DI_ptr; ptr < request_code + 256; ptr++) {
|
||||
if (*ptr == '"') {
|
||||
*ptr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
GenerateLicense(PrivateKey, SnKey, Name, Organization, DI_ptr);
|
||||
|
||||
RSA_free(PrivateKey);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
#include <stddef.h> // NOLINT
|
||||
#include <stdint.h> // NOLINT
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../common/RSACipher.hpp"
|
||||
#include "NavicatKeygen.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
|
||||
template<int min_num, int max_num>
|
||||
bool read_int(int& num, const char* prompt, const char* err_msg) {
|
||||
int temp;
|
||||
std::string input;
|
||||
|
||||
while (true) {
|
||||
std::cout << prompt;
|
||||
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(err_msg);
|
||||
}
|
||||
} catch (...) {
|
||||
std::cout << err_msg << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string base64_encode(const std::vector<uint8_t>& bindata);
|
||||
std::vector<uint8_t> base64_decode(const std::string& ascdata);
|
||||
|
||||
void Help() {
|
||||
std::cout << "***************************************************" << std::endl;
|
||||
std::cout << "* Navicat Keygen by @DoubleLabyrinth *" << std::endl;
|
||||
std::cout << "* Version: 4.0 *" << std::endl;
|
||||
std::cout << "***************************************************" << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Usage:" << std::endl;
|
||||
std::cout << " navicat-keygen <RSA-2048 Private Key File>" << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << " <RSA-2048 Private Key File> Path to a PEM-format RSA-2048 private key file." << std::endl;
|
||||
std::cout << " This parameter must be specified." << std::endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
Help();
|
||||
return -1;
|
||||
} else {
|
||||
try {
|
||||
std::cout << "***************************************************" << std::endl;
|
||||
std::cout << "* Navicat Keygen by @DoubleLabyrinth *" << std::endl;
|
||||
std::cout << "* Version: 4.0 *" << std::endl;
|
||||
std::cout << "***************************************************" << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
RSACipher RsaCipher;
|
||||
|
||||
int select_num;
|
||||
|
||||
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);
|
||||
|
||||
RsaCipher.ImportKeyFromFile<RSAKeyType::PrivateKey, RSAKeyFormat::PEM>(argv[1]);
|
||||
if (RsaCipher.Bits() != 2048) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Not RSA-2048 private key file.");
|
||||
}
|
||||
|
||||
keygen.SetProductSignature(NavicatKeygen::Product::Premium);
|
||||
|
||||
std::cout
|
||||
<< "Which is your Navicat Premium language?" << std::endl
|
||||
<< "0. English" << std::endl
|
||||
<< "1. Simplified Chinese" << std::endl
|
||||
<< "2. Traditional Chinese" << std::endl
|
||||
<< "3. Japanese" << std::endl
|
||||
<< "4. Polish" << std::endl
|
||||
<< "5. Spanish" << std::endl
|
||||
<< "6. French" << std::endl
|
||||
<< "7. German" << std::endl
|
||||
<< "8. Korean" << std::endl
|
||||
<< "9. Russian" << std::endl
|
||||
<< "10. Portuguese" << std::endl
|
||||
<< std::endl;
|
||||
if (read_int<0, 10>(select_num, "(Input index)> ", "Invalid index.")) {
|
||||
keygen.SetLanguageSignature(static_cast<NavicatKeygen::Language>(select_num));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
if (read_int<0, 15>(select_num, "(Input major version number, range: 0 ~ 15, default: 12)> ", "Invalid number.")) {
|
||||
keygen.SetVersion(static_cast<uint8_t>(select_num));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// Generate snKey
|
||||
//
|
||||
keygen.Generate();
|
||||
std::cout << std::endl;
|
||||
std::cout << "Serial number:" << std::endl;
|
||||
std::cout << keygen.GetFormatedKey() << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
//
|
||||
// Get user name
|
||||
//
|
||||
std::cout << "Your name: ";
|
||||
if (!std::getline(std::cin, username)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// Get organization name
|
||||
//
|
||||
std::cout << "Your organization: ";
|
||||
if (!std::getline(std::cin, organization)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!std::getline(std::cin, temp))
|
||||
return 0;
|
||||
|
||||
if (temp.empty())
|
||||
break;
|
||||
|
||||
RequestCode_b64 += temp;
|
||||
}
|
||||
|
||||
//
|
||||
// Get request code in raw bytes
|
||||
//
|
||||
RequestCode = base64_decode(RequestCode_b64);
|
||||
if (RequestCode.size() > ((RsaCipher.Bits() + 7) / 8)) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Request code is too long.");
|
||||
}
|
||||
|
||||
//
|
||||
// Decrypt to get request info
|
||||
//
|
||||
RsaCipher.Decrypt(RequestCode.data(), static_cast<int>(RequestCode.size()), RequestInfo, RSA_PKCS1_PADDING);
|
||||
|
||||
//
|
||||
// print out request info
|
||||
//
|
||||
std::cout << "Request Info:" << std::endl;
|
||||
std::cout << RequestInfo << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
//
|
||||
// Generate response info
|
||||
//
|
||||
json.Parse(RequestInfo);
|
||||
json.RemoveMember("P"); // remove Platform info
|
||||
|
||||
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)));
|
||||
|
||||
json.AddMember(N_Key, N_Value, json.GetAllocator());
|
||||
json.AddMember(O_Key, O_Value, json.GetAllocator());
|
||||
json.AddMember(T_Key, T_Value, json.GetAllocator());
|
||||
|
||||
json.Accept(writer);
|
||||
|
||||
if (buffer.GetSize() > 240) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Response info is too long.");
|
||||
}
|
||||
|
||||
//
|
||||
// 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((RsaCipher.Bits() + 7) / 8);
|
||||
RsaCipher.Encrypt<RSAKeyType::PrivateKey>(ResponseInfo, static_cast<int>(strlen(ResponseInfo)), ResponseCode.data(), RSA_PKCS1_PADDING);
|
||||
|
||||
//
|
||||
// encode encrypted response info in base64 format
|
||||
//
|
||||
ResponseCode_b64 = base64_encode(ResponseCode);
|
||||
|
||||
//
|
||||
// print out activation code
|
||||
//
|
||||
std::cout << "License:" << std::endl;
|
||||
std::cout << ResponseCode_b64 << std::endl;
|
||||
|
||||
return 0;
|
||||
} catch (nkg::Exception& e) {
|
||||
std::cout << "[-] " << e.File() << ":" << e.Line() << " ->" << std::endl;
|
||||
std::cout << " " << e.Message() << std::endl;
|
||||
if (e.HasErrorCode()) {
|
||||
std::cout << " " << e.ErrorString() << std::endl;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{CB49F054-83AB-4C16-B73B-33D7DF696E16}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>navicatkeygen</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>D:\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>D:\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>D:\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>D:\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="_tmain.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="_tmain.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,76 @@
|
||||
#include "CapstoneDisassembler.hpp"
|
||||
|
||||
CapstoneDisassembler CapstoneDisassembler::Create(cs_arch ArchType, cs_mode Mode) {
|
||||
CapstoneDisassembler NewDisassembler;
|
||||
|
||||
auto err = cs_open(ArchType, Mode, NewDisassembler.pvt_Handle.GetAddress());
|
||||
if (err != CS_ERR_OK) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::CapstoneError(__FILE__, __LINE__, err, "cs_open failed.");
|
||||
}
|
||||
|
||||
NewDisassembler.pvt_Insn.TakeOver(cs_malloc(NewDisassembler.pvt_Handle));
|
||||
if (NewDisassembler.pvt_Insn.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::CapstoneError(__FILE__, __LINE__, cs_errno(NewDisassembler.pvt_Handle), "cs_malloc failed.");
|
||||
}
|
||||
|
||||
return NewDisassembler;
|
||||
}
|
||||
|
||||
void CapstoneDisassembler::Option(cs_opt_type Type, size_t Value) {
|
||||
auto err = cs_option(pvt_Handle, Type, Value);
|
||||
if (err != CS_ERR_OK) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::CapstoneError(__FILE__, __LINE__, err, "cs_option failed.");
|
||||
}
|
||||
|
||||
pvt_CurrentInsn = nullptr;
|
||||
|
||||
pvt_Insn.TakeOver(cs_malloc(pvt_Handle));
|
||||
if (pvt_Insn.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::CapstoneError(__FILE__, __LINE__, cs_errno(pvt_Handle), "cs_malloc failed.");
|
||||
}
|
||||
}
|
||||
|
||||
void CapstoneDisassembler::SetContext(uintptr_t lpOpcode, size_t cbOpcode, uint64_t Address) noexcept {
|
||||
pvt_CurrentCtx.pbOpcode = reinterpret_cast<const uint8_t*>(lpOpcode);
|
||||
pvt_CurrentCtx.cbOpcode = cbOpcode;
|
||||
pvt_CurrentCtx.Address = Address;
|
||||
pvt_CurrentInsn = nullptr;
|
||||
}
|
||||
|
||||
void CapstoneDisassembler::SetContext(const void* lpOpcode, size_t cbOpcode, uint64_t Address) noexcept {
|
||||
pvt_CurrentCtx.pbOpcode = reinterpret_cast<const uint8_t*>(lpOpcode);
|
||||
pvt_CurrentCtx.cbOpcode = cbOpcode;
|
||||
pvt_CurrentCtx.Address = Address;
|
||||
pvt_CurrentInsn = nullptr;
|
||||
}
|
||||
|
||||
const CapstoneDisassembler::Context& CapstoneDisassembler::GetContext() const noexcept {
|
||||
return pvt_CurrentCtx;
|
||||
}
|
||||
|
||||
const cs_insn* CapstoneDisassembler::GetInstruction() const noexcept {
|
||||
return pvt_CurrentInsn;
|
||||
}
|
||||
|
||||
CapstoneDisassembler::Context CapstoneDisassembler::GetInstructionContext() const noexcept {
|
||||
Context CtxOfInsn = {};
|
||||
CtxOfInsn.pbOpcode = pvt_CurrentCtx.pbOpcode - pvt_CurrentInsn->size;
|
||||
CtxOfInsn.cbOpcode = pvt_CurrentCtx.cbOpcode + pvt_CurrentInsn->size;
|
||||
CtxOfInsn.Address = pvt_CurrentCtx.Address - pvt_CurrentInsn->size;
|
||||
return CtxOfInsn;
|
||||
}
|
||||
|
||||
bool CapstoneDisassembler::Next() noexcept {
|
||||
bool bSucceed = cs_disasm_iter(pvt_Handle, &pvt_CurrentCtx.pbOpcode, &pvt_CurrentCtx.cbOpcode, &pvt_CurrentCtx.Address, pvt_Insn);
|
||||
if (bSucceed) {
|
||||
if (pvt_CurrentInsn == nullptr) pvt_CurrentInsn = pvt_Insn.Get();
|
||||
} else {
|
||||
pvt_CurrentInsn = nullptr;
|
||||
}
|
||||
return bSucceed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <capstone/capstone.h>
|
||||
#include "../common/ResourceOwned.hpp"
|
||||
#include "ResourceTraitsCapstone.hpp"
|
||||
|
||||
class CapstoneDisassembler {
|
||||
public:
|
||||
|
||||
struct Context {
|
||||
const uint8_t* pbOpcode;
|
||||
size_t cbOpcode;
|
||||
uint64_t Address;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
ResourceOwned<CapstoneHandleTraits> pvt_Handle;
|
||||
ResourceOwned<CapstoneInsnTraits> pvt_Insn;
|
||||
Context pvt_CurrentCtx;
|
||||
cs_insn* pvt_CurrentInsn;
|
||||
|
||||
CapstoneDisassembler() noexcept :
|
||||
pvt_Handle(CapstoneHandleTraits{}),
|
||||
pvt_Insn(CapstoneInsnTraits{}),
|
||||
pvt_CurrentCtx{},
|
||||
pvt_CurrentInsn(nullptr) {}
|
||||
public:
|
||||
|
||||
[[nodiscard]]
|
||||
static CapstoneDisassembler Create(cs_arch ArchType, cs_mode Mode);
|
||||
|
||||
void Option(cs_opt_type Type, size_t Value);
|
||||
|
||||
void SetContext(uintptr_t lpOpcode, size_t cbOpcode, uint64_t Address = 0) noexcept ;
|
||||
|
||||
void SetContext(const void* lpOpcode, size_t cbOpcode, uint64_t Address = 0) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const Context& GetContext() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
const cs_insn* GetInstruction() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
Context GetInstructionContext() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
bool Next() noexcept;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "../common/Exception.hpp"
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
namespace nkg {
|
||||
|
||||
class CapstoneError final : public Exception {
|
||||
private:
|
||||
|
||||
cs_err pvt_ErrorCode;
|
||||
|
||||
public:
|
||||
|
||||
CapstoneError(const char* File, unsigned Line, cs_err ErrorCode, const char* Message) noexcept :
|
||||
Exception(File, Line, Message),
|
||||
pvt_ErrorCode(ErrorCode) {}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual intptr_t ErrorCode() const noexcept override {
|
||||
return pvt_ErrorCode;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return cs_strerror(pvt_ErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "../common/Exception.hpp"
|
||||
#include <keystone/keystone.h>
|
||||
|
||||
namespace nkg {
|
||||
|
||||
class KeystoneError final : public Exception {
|
||||
private:
|
||||
|
||||
ks_err pvt_ErrorCode;
|
||||
|
||||
public:
|
||||
|
||||
KeystoneError(const char* File, unsigned Line, ks_err ErrorCode, const char* Message) noexcept :
|
||||
Exception(File, Line, Message),
|
||||
pvt_ErrorCode(ErrorCode) {}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual intptr_t ErrorCode() const noexcept override {
|
||||
return pvt_ErrorCode;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return ks_strerror(pvt_ErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <string.h> // NOLINT
|
||||
#include "ExceptionCapstone.hpp"
|
||||
#include "CapstoneDisassembler.hpp"
|
||||
#include "X64ImageInterpreter.hpp"
|
||||
|
||||
namespace nkg {
|
||||
|
||||
bool IsResolvedTo(const X64ImageInterpreter& Image, const void* StubHelperProc, const char *Symbol) {
|
||||
CapstoneDisassembler Disassembler = CapstoneDisassembler::Create(CS_ARCH_X86, CS_MODE_64);
|
||||
|
||||
Disassembler.Option(CS_OPT_DETAIL, CS_OPT_ON);
|
||||
|
||||
// A stub-helper proc must look like:
|
||||
// push xxxxh;
|
||||
// jmp loc_xxxxxxxx
|
||||
// which should be 10 bytes long.
|
||||
Disassembler.SetContext(StubHelperProc, 10);
|
||||
|
||||
if (Disassembler.Next() == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto insn = Disassembler.GetInstruction();
|
||||
if (strcasecmp(insn->mnemonic, "push") != 0 || insn->detail->x86.operands[0].type != X86_OP_IMM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bind_opcode_offset = static_cast<uint32_t>(insn->detail->x86.operands[0].imm);
|
||||
if (Image.CommandOf<LC_DYLD_INFO_ONLY>() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bind_opcode_ptr =
|
||||
Image.ImageOffset<uint8_t*>(Image.CommandOf<LC_DYLD_INFO_ONLY>()->lazy_bind_off) +
|
||||
bind_opcode_offset;
|
||||
|
||||
while ((*bind_opcode_ptr & BIND_OPCODE_MASK) != BIND_OPCODE_DONE) {
|
||||
switch (*bind_opcode_ptr & BIND_OPCODE_MASK) {
|
||||
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: // 0x10
|
||||
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // 0x30
|
||||
case BIND_OPCODE_SET_TYPE_IMM: // 0x50
|
||||
case BIND_OPCODE_DO_BIND: // 0x90
|
||||
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: // 0xB0
|
||||
++bind_opcode_ptr;
|
||||
break;
|
||||
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: // 0x20
|
||||
case BIND_OPCODE_SET_ADDEND_SLEB: // 0x60
|
||||
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: // 0x70
|
||||
case BIND_OPCODE_ADD_ADDR_ULEB: // 0x80
|
||||
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: // 0xA0
|
||||
while (*(++bind_opcode_ptr) & 0x80u);
|
||||
++bind_opcode_ptr;
|
||||
break;
|
||||
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // 0x40
|
||||
return strcmp(reinterpret_cast<const char *>(bind_opcode_ptr + 1), Symbol) == 0;
|
||||
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: // 0xC0
|
||||
//
|
||||
// This opcode is too rare to appear,
|
||||
// It is okay to dismiss this opcode
|
||||
//
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <stddef.h> // NOLINT
|
||||
#include <stdint.h> // NOLINT
|
||||
#include <stdio.h> // NOLINT
|
||||
#include <signal.h> // NOLINT
|
||||
#include <setjmp.h> // NOLINT
|
||||
|
||||
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`
|
||||
//
|
||||
namespace nkg {
|
||||
|
||||
void PrintMemory(const void *from, const void *to, const void *base) {
|
||||
auto start = reinterpret_cast<const uint8_t *>(from);
|
||||
auto end = reinterpret_cast<const uint8_t *>(to);
|
||||
auto 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>(end) % 16)
|
||||
end++;
|
||||
|
||||
void (*prev_handler)(int) = signal(SIGSEGV, SIGSEGV_Handler);
|
||||
while (start < end) {
|
||||
uint16_t value[16] = {};
|
||||
|
||||
if (base_ptr) {
|
||||
uintptr_t d = start >= base ? start - base_ptr : base_ptr - start;
|
||||
if (start >= base) {
|
||||
printf("+0x%.*zx ", static_cast<int>(2 * sizeof(void *)), d);
|
||||
} else {
|
||||
printf("-0x%.*zx ", static_cast<int>(2 * sizeof(void *)), d);
|
||||
}
|
||||
} else {
|
||||
printf("0x%.*zx ", static_cast<int>(2 * sizeof(void *)), reinterpret_cast<uintptr_t >(start));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (start + i < from) {
|
||||
printf(" ");
|
||||
value[i] = 0xfffe;
|
||||
} else if (ProbeForRead<uint8_t>(start + i, value + i)) {
|
||||
printf("%02x ", value[i]);
|
||||
} else {
|
||||
printf("?? ");
|
||||
value[i] = 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
for (int i = 0; i < 16; ++i) { // NOLINT
|
||||
if (0x20 <= value[i] && value[i] < 0x7f) {
|
||||
printf("%c", value[i]);
|
||||
} else if (value[i] == 0xfffe) {
|
||||
printf(" ");
|
||||
} else {
|
||||
printf(".");
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
start += 0x10;
|
||||
}
|
||||
signal(SIGSEGV, prev_handler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "KeystoneAssembler.hpp"
|
||||
|
||||
[[nodiscard]]
|
||||
KeystoneAssembler KeystoneAssembler::Create(ks_arch ArchType, ks_mode Mode) {
|
||||
KeystoneAssembler NewAssembler;
|
||||
|
||||
auto err = ks_open(ArchType, Mode, NewAssembler.pvt_Engine.GetAddress());
|
||||
if (err != KS_ERR_OK) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_open failed.");
|
||||
}
|
||||
|
||||
return NewAssembler;
|
||||
}
|
||||
|
||||
void KeystoneAssembler::Option(ks_opt_type Type, size_t Value) {
|
||||
auto err = ks_option(pvt_Engine, Type, Value);
|
||||
if (err != KS_ERR_OK) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_open failed.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<uint8_t> KeystoneAssembler::GenerateOpcode(const char *AssemblyCode, uint64_t Address) const {
|
||||
ResourceOwned pbOpcode(KeystoneMallocTraits{});
|
||||
size_t cbOpCode = 0;
|
||||
size_t InstructionsProcessed = 0;
|
||||
|
||||
if (ks_asm(pvt_Engine, AssemblyCode, Address, pbOpcode.GetAddress(), &cbOpCode, &InstructionsProcessed) != 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::KeystoneError(__FILE__, __LINE__, ks_errno(pvt_Engine), "ks_asm failed.");
|
||||
}
|
||||
|
||||
return std::vector<uint8_t>(pbOpcode.Get(), pbOpcode.Get() + cbOpCode);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<uint8_t> KeystoneAssembler::GenerateOpcode(const std::string& AssemblyCode, uint64_t Address) const {
|
||||
ResourceOwned pbOpcode(KeystoneMallocTraits{});
|
||||
size_t cbOpCode = 0;
|
||||
size_t InstructionsProcessed = 0;
|
||||
|
||||
if (ks_asm(pvt_Engine, AssemblyCode.c_str(), Address, pbOpcode.GetAddress(), &cbOpCode, &InstructionsProcessed) != 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::KeystoneError(__FILE__, __LINE__, ks_errno(pvt_Engine), "ks_asm failed.");
|
||||
}
|
||||
|
||||
return std::vector<uint8_t>(pbOpcode.Get(), pbOpcode.Get() + cbOpCode);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <keystone/keystone.h>
|
||||
#include "../common/ResourceOwned.hpp"
|
||||
#include "ResourceTraitsKeystone.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class KeystoneAssembler {
|
||||
private:
|
||||
|
||||
ResourceOwned<KeystoneHandleTraits> pvt_Engine;
|
||||
|
||||
KeystoneAssembler() noexcept :
|
||||
pvt_Engine(KeystoneHandleTraits{}) {}
|
||||
public:
|
||||
|
||||
[[nodiscard]]
|
||||
static KeystoneAssembler Create(ks_arch ArchType, ks_mode Mode);
|
||||
|
||||
void Option(ks_opt_type Type, size_t Value);
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<uint8_t> GenerateOpcode(const char* AssemblyCode, uint64_t Address = 0) const;
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<uint8_t> GenerateOpcode(const std::string& AssemblyCode, uint64_t Address = 0) const;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include "PatchSolutions.hpp"
|
||||
#include <memory.h>
|
||||
|
||||
const char PatchSolution0::Keyword[452] =
|
||||
"-----BEGIN PUBLIC KEY-----\x00"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00"
|
||||
"qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00"
|
||||
"a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00"
|
||||
"R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00"
|
||||
"WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00"
|
||||
"YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00"
|
||||
"awIDAQAB\x00"
|
||||
"-----END PUBLIC KEY-----\x00";
|
||||
|
||||
PatchSolution0::PatchSolution0(const X64ImageInterpreter& Image) noexcept :
|
||||
pvt_Image(Image),
|
||||
pvt_PatchOffset(X64ImageInterpreter::InvalidOffset) {}
|
||||
|
||||
bool PatchSolution0::FindPatchOffset() noexcept {
|
||||
try {
|
||||
pvt_PatchOffset = pvt_Image.SearchSectionOffset("__TEXT", "__cstring", [](const uint8_t* p) {
|
||||
return memcmp(p, Keyword, sizeof(Keyword) - 1) == 0;
|
||||
});
|
||||
|
||||
printf("[+] PatchSolution0 ...... Ready to apply.\n");
|
||||
printf(" Keyword offset = +0x%.8x\n", pvt_PatchOffset);
|
||||
return true;
|
||||
} catch (...) {
|
||||
printf("[-] PatchSolution0 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PatchSolution0::CheckKey(const RSACipher& RsaCipher) const noexcept {
|
||||
try {
|
||||
if (RsaCipher.Bits() != 2048) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
for (auto& c : PublicKeyPEM) {
|
||||
if (c == '\n') c = '\x00';
|
||||
}
|
||||
|
||||
return PublicKeyPEM.length() == sizeof(Keyword) - 1;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PatchSolution0::MakePatch(const RSACipher& RsaCipher) const {
|
||||
if (pvt_PatchOffset == X64ImageInterpreter::InvalidOffset) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PatchSolution0 is not ready.");
|
||||
}
|
||||
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
for (auto& c : PublicKeyPEM) {
|
||||
if (c == '\n') c = '\x00';
|
||||
}
|
||||
|
||||
auto pbPatch = pvt_Image.ImageOffset<uint8_t*>(pvt_PatchOffset);
|
||||
|
||||
puts("**************************************************************");
|
||||
puts("* PatchSolution0 *");
|
||||
puts("**************************************************************");
|
||||
printf("@+0x%.8x\n", pvt_PatchOffset);
|
||||
|
||||
puts("Previous:");
|
||||
nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch);
|
||||
|
||||
memcpy(pbPatch, PublicKeyPEM.data(), PublicKeyPEM.size());
|
||||
|
||||
puts("After:");
|
||||
nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch);
|
||||
|
||||
puts("");
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "PatchSolutions.hpp"
|
||||
#include <memory.h>
|
||||
|
||||
const uint8_t PatchSolution1::Keyword[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
|
||||
};
|
||||
|
||||
PatchSolution1::PatchSolution1(const X64ImageInterpreter& Image) noexcept :
|
||||
pvt_Image(Image),
|
||||
pvt_PatchOffset(X64ImageInterpreter::InvalidOffset) {}
|
||||
|
||||
bool PatchSolution1::FindPatchOffset() noexcept {
|
||||
uint8_t* pbPatch = nullptr;
|
||||
|
||||
for (size_t i = 0; i < pvt_Image.NumberOfSections(); ++i) {
|
||||
auto pbSectionView = pvt_Image.SectionView<uint8_t*>(i);
|
||||
auto cbSectiveView = pvt_Image.ImageSection(i)->size;
|
||||
|
||||
if (cbSectiveView < sizeof(Keyword)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < cbSectiveView - sizeof(Keyword); ++j) {
|
||||
if (memcmp(pbSectionView + j, Keyword, sizeof(Keyword)) == 0) {
|
||||
pbPatch = pbSectionView + j;
|
||||
i = pvt_Image.NumberOfSections();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pbPatch == nullptr) {
|
||||
printf("[-] PatchSolution1 ...... Omitted.\n");
|
||||
return false;
|
||||
} else {
|
||||
pvt_PatchOffset = pbPatch - pvt_Image.ImageBase<uint8_t*>();
|
||||
|
||||
printf("[+] PatchSolution1 ...... Ready to apply.\n");
|
||||
printf(" Keyword offset = +0x%.8x\n", pvt_PatchOffset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool PatchSolution1::CheckKey(const RSACipher& RsaCipher) const noexcept {
|
||||
try {
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return PublicKeyPEM.length() == sizeof(Keyword);
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PatchSolution1::MakePatch(const RSACipher& RsaCipher) const {
|
||||
if (pvt_PatchOffset == X64ImageInterpreter::InvalidOffset) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PatchSolution1 is not ready.");
|
||||
}
|
||||
|
||||
auto pbPatch = pvt_Image.ImageOffset<uint8_t*>(pvt_PatchOffset);
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t key = 8;
|
||||
for (auto& c : PublicKeyPEM) {
|
||||
if (key == 0) key = 8;
|
||||
reinterpret_cast<uint8_t&>(c) ^= 0xbbu - key;
|
||||
--key;
|
||||
}
|
||||
|
||||
puts("**************************************************************");
|
||||
puts("* PatchSolution1 *");
|
||||
puts("**************************************************************");
|
||||
printf("@+0x%.8x\n", pvt_PatchOffset);
|
||||
|
||||
puts("Previous:");
|
||||
nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch);
|
||||
|
||||
memcpy(pbPatch, PublicKeyPEM.data(), PublicKeyPEM.length());
|
||||
|
||||
puts("After:");
|
||||
nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch);
|
||||
|
||||
puts("");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
#include "PatchSolutions.hpp"
|
||||
#include <memory.h>
|
||||
|
||||
const char PatchSolution2::Keyword[1114] =
|
||||
"BIjWyoeRR0NBgkqnDZWxCgKCEAw1dqF3DTvOB91ZHwecJYFrdM1KEh"
|
||||
"1yVeRoGqSdLLGZGUlngig3OD5mMzs889IqWqqfHSeHMvzyg1p6UPCY"
|
||||
"nesxa9M2dDUrXHomRHOFHSfsbSXRFwt5GivtnJG9lLJHZ7XWeIQABi"
|
||||
"dKionYD3O6c9tvUAoDosUJAdQ1RaSXTzyETbHTRtnTPeLpO3EedGMs"
|
||||
"v3jG9yPcmmdYkddSeJRwn2raPJmnvdHScHUACw0sUNuosAqPaQbTQN"
|
||||
"PATDzcrnd1Sf8RIbUp4MQJFVJugPLVZbP53Gjtyyniqe5q75kva8Qm"
|
||||
"Hr1uOuXkVppe3cwECaGamupG43L1XfcpRjCMrxRep3s2VlbL01xmfz"
|
||||
"5cIhrj34iVmgZSAmIb8ZxiHPdp1oDMFkbNetZyWegqjAHQQ9eoSOTD"
|
||||
"bERbKEwZ5FLeLsbNAxfqsapB1XBvCavFHualx6bxVxuRQceh4z8kaZ"
|
||||
"iv2pOKbZQSJ2Dx5HEq0bYZ6y6b7sN9IaeDFNQwjzQn1K7k3XlYAPWC"
|
||||
"IvDe8Ln0FUe4yMNmuUhu5RTjxE05hUqtz1HjJvYQ9Es1VA6LflKQ87"
|
||||
"TwIXBNvfrcHaZ72QM4dQtDUyEMrLgMDkJBDM9wqIDps65gSlAz6eHD"
|
||||
"8tYWUttrWose0cH0yykVnqFzPtdRiZyZRfio6lGyK48mIC9z7T6MN3"
|
||||
"a7OaLZHZSwzcpQLcGi7M9q1wXLq4Ms1UvlwntB9FLHc63tHPpG8rhn"
|
||||
"XhZIk4QrSm4GYuEKQVHwku6ulw6wfggVL8FZPhoPCGsrb2rQGurBUL"
|
||||
"3lkVJ6RO9VGHcczDYomXqAJqlt4y9pkQIj9kgwTrxTzEZgMGdYZqsV"
|
||||
"4Bd5JjtrL7u3LA0N2Hq9Xvmmis2jDVhSQoUoGukNIoqng3SBsf0E7b"
|
||||
"4W0S1aZSSOJ90nQHQkQShE9YIMDBbNwIg2ncthwADYqibYUgIvJcK9"
|
||||
"89XHnYmZsdMWtt53lICsXE1vztR5WrQjSw4WXDiB31LXTrvudCB6vw"
|
||||
"kCQa4leutETpKLJ2bYaOYBdoiBFOwvf36YaSuRoY4SP2x1pWOwGFTg"
|
||||
"d90J2uYyCqUa3Q3iX52iigT4EKL2vJKdJ";
|
||||
|
||||
const uint8_t PatchSolution2::FunctionHeader[9] = {
|
||||
0x55, // push rbp
|
||||
0x48, 0x89, 0xe5, // mov rbp, rsp
|
||||
0x41, 0x57, // push r15
|
||||
0x41, 0x56, // push r14
|
||||
0x53, // push rbx
|
||||
};
|
||||
|
||||
PatchSolution2::PatchSolution2(const X64ImageInterpreter& Image) noexcept :
|
||||
pvt_Image(Image),
|
||||
pvt_Disassembler(CapstoneDisassembler::Create(CS_ARCH_X86, CS_MODE_64)),
|
||||
pvt_Assembler(KeystoneAssembler::Create(KS_ARCH_X86, KS_MODE_64)),
|
||||
pvt_FunctionOffset(X64ImageInterpreter::InvalidOffset),
|
||||
pvt_KeywordOffset(X64ImageInterpreter::InvalidOffset),
|
||||
pvt_StdStringAppendStubRva(X64ImageInterpreter::InvalidAddress)
|
||||
{
|
||||
pvt_Disassembler.Option(CS_OPT_DETAIL, CS_OPT_ON);
|
||||
}
|
||||
|
||||
bool PatchSolution2::FindPatchOffset() noexcept {
|
||||
auto FunctionOffset = X64ImageInterpreter::InvalidOffset;
|
||||
auto KeywordOffset = X64ImageInterpreter::InvalidOffset;
|
||||
auto StdStringAppendStubRva = X64ImageInterpreter::InvalidAddress;
|
||||
|
||||
try {
|
||||
auto Sec__text = pvt_Image.ImageSection("__TEXT", "__text");
|
||||
auto Sec__const = pvt_Image.ImageSection("__TEXT", "__const");
|
||||
auto Sec__stubs = pvt_Image.ImageSection("__TEXT", "__stubs");
|
||||
auto SecView__text = pvt_Image.SectionView<uint8_t*>(Sec__text);
|
||||
auto SecView__const = pvt_Image.SectionView<uint8_t*>(Sec__const);
|
||||
auto SecView__stubs = pvt_Image.SectionView<uint8_t*>(Sec__stubs);
|
||||
|
||||
KeywordOffset = pvt_Image.SearchSectionOffset("__TEXT", "__const", [](const uint8_t* p) {
|
||||
return memcmp(p, Keyword, sizeof(Keyword)) == 0;
|
||||
});
|
||||
|
||||
auto KeywordRva = pvt_Image.OffsetToRva(KeywordOffset);
|
||||
|
||||
auto Hint = pvt_Image.SearchSectionOffset("__TEXT", "__text", [Sec__text, SecView__text, KeywordRva](const uint8_t* p) {
|
||||
auto rip = (p - SecView__text) + Sec__text->addr + 4;
|
||||
auto off = *reinterpret_cast<const uint32_t*>(p);
|
||||
return rip + off == KeywordRva;
|
||||
}) - 0xc0;
|
||||
|
||||
for (uint32_t i = 0; i < 0xc0; ++i) {
|
||||
if (memcmp(pvt_Image.ImageOffset(Hint + i), FunctionHeader, sizeof(FunctionHeader)) == 0) {
|
||||
FunctionOffset = Hint + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FunctionOffset == X64ImageInterpreter::InvalidOffset) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Not found.");
|
||||
}
|
||||
|
||||
pvt_Disassembler.SetContext(SecView__stubs, Sec__stubs->size, Sec__stubs->addr);
|
||||
while (pvt_Disassembler.Next()) {
|
||||
auto insn = pvt_Disassembler.GetInstruction();
|
||||
|
||||
//
|
||||
// As far as I know, all stub functions have a pattern looking like:
|
||||
// jmp qword ptr [RIP + xxxx]
|
||||
//
|
||||
if (strcasecmp(insn->mnemonic, "jmp") == 0 && insn->detail->x86.operands[0].type == X86_OP_MEM && insn->detail->x86.operands[0].mem.base == X86_REG_RIP) {
|
||||
uint64_t la_symbol_ptr_rva = pvt_Disassembler.GetContext().Address + insn->detail->x86.operands[0].mem.disp;
|
||||
uint32_t la_symbol_ptr_offset = pvt_Image.RvaToOffset(la_symbol_ptr_rva);
|
||||
if (la_symbol_ptr_offset == X64ImageInterpreter::InvalidOffset)
|
||||
continue;
|
||||
|
||||
uint64_t stub_helper_rva = *pvt_Image.ImageOffset<const uint64_t*>(la_symbol_ptr_offset);
|
||||
uint32_t stub_helper_offset = pvt_Image.RvaToOffset(stub_helper_rva);
|
||||
if (stub_helper_offset == X64ImageInterpreter::InvalidOffset)
|
||||
continue;
|
||||
|
||||
//
|
||||
// __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
|
||||
// is the mangled name of "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::append(char const*)",
|
||||
// which is, as known as, "std::string::append(const char*)"
|
||||
// You can demangle it by c++flit
|
||||
// e.g.
|
||||
// c++filt -_ '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc'
|
||||
//
|
||||
if (nkg::IsResolvedTo(pvt_Image, pvt_Image.ImageOffset(stub_helper_offset), "__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc")) {
|
||||
auto StdStringAppendStubOffset = pvt_Disassembler.GetInstructionContext().pbOpcode - pvt_Image.ImageBase<const uint8_t*>();
|
||||
StdStringAppendStubRva = pvt_Image.OffsetToRva(StdStringAppendStubOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StdStringAppendStubRva == X64ImageInterpreter::InvalidAddress) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Not found.");
|
||||
}
|
||||
|
||||
pvt_FunctionOffset = FunctionOffset;
|
||||
pvt_KeywordOffset = KeywordOffset;
|
||||
pvt_StdStringAppendStubRva = StdStringAppendStubRva;
|
||||
|
||||
printf("[+] PatchSolution2 ...... Ready to apply.\n");
|
||||
printf(" Function offset = +0x%.8x\n", pvt_FunctionOffset);
|
||||
printf(" Keyword offset = +0x%.8x\n", pvt_KeywordOffset);
|
||||
printf(" std::string::append(const char*) RVA = 0x%.16llx\n", pvt_StdStringAppendStubRva);
|
||||
return true;
|
||||
} catch (...) {
|
||||
printf("[-] PatchSolution2 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool PatchSolution2::CheckKey(const RSACipher& RsaCipher) const noexcept {
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return PublicKeyPEM.length() == 0x188;
|
||||
}
|
||||
|
||||
void PatchSolution2::MakePatch(const RSACipher& RsaCipher) const {
|
||||
if (pvt_FunctionOffset == X64ImageInterpreter::InvalidOffset ||
|
||||
pvt_KeywordOffset == X64ImageInterpreter::InvalidOffset ||
|
||||
pvt_StdStringAppendStubRva == X64ImageInterpreter::InvalidAddress)
|
||||
{
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "PatchSolution2 is not ready.");
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare public key string
|
||||
//
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Prepare new function opcodes
|
||||
//
|
||||
uint64_t FunctionRVA = pvt_Image.OffsetToRva(pvt_FunctionOffset);
|
||||
uint64_t KeywordRVA = pvt_Image.OffsetToRva(pvt_KeywordOffset);
|
||||
|
||||
char AssemblyCode[512] = {};
|
||||
sprintf(AssemblyCode,
|
||||
"push rbp;"
|
||||
"mov rbp, rsp;"
|
||||
"push r15;"
|
||||
"push r14;"
|
||||
"push rbx;"
|
||||
"sub rsp, 0x48;"
|
||||
|
||||
"mov rbx, rdi;"
|
||||
|
||||
"xor rax, rax;" // initialize std::string with null
|
||||
"mov qword ptr[rsp], rax;"
|
||||
"mov qword ptr[rsp + 0x8], rax;"
|
||||
"mov qword ptr[rsp + 0x10], rax;"
|
||||
|
||||
"lea rdi, qword ptr[rsp];"
|
||||
"lea rsi, qword ptr[0x%.16llx];" // filled with address to Keyword
|
||||
"call 0x%.16llx;" // filled with address to std::string::append(const char*)
|
||||
|
||||
"mov rax, qword ptr[rsp];"
|
||||
"mov qword ptr[rbx], rax;"
|
||||
"mov rax, qword ptr[rsp + 0x8];"
|
||||
"mov qword ptr[rbx + 0x8], rax;"
|
||||
"mov rax, qword ptr[rsp + 0x10];"
|
||||
"mov qword ptr[rbx + 0x10], rax;"
|
||||
|
||||
"mov rax, rbx;"
|
||||
"add rsp, 0x48;"
|
||||
"pop rbx;"
|
||||
"pop r14;"
|
||||
"pop r15;"
|
||||
"pop rbp;"
|
||||
"ret;",
|
||||
KeywordRVA,
|
||||
pvt_StdStringAppendStubRva
|
||||
);
|
||||
|
||||
auto NewFunctionOpcode = pvt_Assembler.GenerateOpcode(AssemblyCode, FunctionRVA);
|
||||
|
||||
auto pbFunctionPatch = pvt_Image.ImageOffset<uint8_t*>(pvt_FunctionOffset);
|
||||
auto pbKeywordPatch = pvt_Image.ImageOffset<uint8_t*>(pvt_KeywordOffset);
|
||||
|
||||
puts("**************************************************************");
|
||||
puts("* PatchSolution2 *");
|
||||
puts("**************************************************************");
|
||||
printf("@+0x%.8x\n", pvt_KeywordOffset);
|
||||
|
||||
puts("Previous:");
|
||||
nkg::PrintMemory(pbKeywordPatch, pbKeywordPatch + sizeof(Keyword) - 1, pbKeywordPatch);
|
||||
|
||||
memcpy(pbKeywordPatch, PublicKeyPEM.c_str(), PublicKeyPEM.length() + 1); // with a null-terminator
|
||||
|
||||
puts("After:");
|
||||
nkg::PrintMemory(pbKeywordPatch, pbKeywordPatch + sizeof(Keyword) - 1, pbKeywordPatch);
|
||||
|
||||
|
||||
|
||||
|
||||
puts("");
|
||||
printf("@+0x%.8x\n", pvt_FunctionOffset);
|
||||
|
||||
puts("Previous:");
|
||||
nkg::PrintMemory(pbFunctionPatch, pbFunctionPatch + NewFunctionOpcode.size(), pbFunctionPatch);
|
||||
|
||||
memcpy(pbFunctionPatch, NewFunctionOpcode.data(), NewFunctionOpcode.size());
|
||||
|
||||
puts("After:");
|
||||
nkg::PrintMemory(pbFunctionPatch, pbFunctionPatch + NewFunctionOpcode.size(), pbFunctionPatch);
|
||||
|
||||
puts("");
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
#include "../common/Exception.hpp"
|
||||
#include "../common/ResourceOwned.hpp"
|
||||
#include "../common/RSACipher.hpp"
|
||||
#include "ResourceTraitsUnix.hpp"
|
||||
#include "X64ImageInterpreter.hpp"
|
||||
#include "CapstoneDisassembler.hpp"
|
||||
#include "KeystoneAssembler.hpp"
|
||||
|
||||
namespace nkg {
|
||||
//
|
||||
// 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 = nullptr);
|
||||
bool IsResolvedTo(const X64ImageInterpreter& Image, const void* StubHelperProc, const char *Symbol);
|
||||
}
|
||||
|
||||
class PatchSolution {
|
||||
public:
|
||||
|
||||
[[nodiscard]]
|
||||
virtual bool FindPatchOffset() noexcept = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept = 0;
|
||||
|
||||
virtual void MakePatch(const RSACipher& RsaCipher) const = 0;
|
||||
|
||||
virtual ~PatchSolution() = default;
|
||||
};
|
||||
|
||||
class PatchSolution0 : public PatchSolution {
|
||||
private:
|
||||
|
||||
static const char Keyword[452];
|
||||
|
||||
const X64ImageInterpreter& pvt_Image;
|
||||
uint32_t pvt_PatchOffset;
|
||||
|
||||
public:
|
||||
|
||||
explicit
|
||||
PatchSolution0(const X64ImageInterpreter& Image) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override;
|
||||
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual void MakePatch(const RSACipher& RsaCipher) const override;
|
||||
};
|
||||
|
||||
class PatchSolution1 : public PatchSolution {
|
||||
private:
|
||||
|
||||
static const uint8_t Keyword[0x188];
|
||||
|
||||
const X64ImageInterpreter& pvt_Image;
|
||||
uint32_t pvt_PatchOffset;
|
||||
|
||||
public:
|
||||
|
||||
explicit
|
||||
PatchSolution1(const X64ImageInterpreter& Image) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override;
|
||||
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual void MakePatch(const RSACipher& RsaCipher) const override;
|
||||
};
|
||||
|
||||
class PatchSolution2 : public PatchSolution {
|
||||
private:
|
||||
|
||||
static const char Keyword[1114];
|
||||
static const uint8_t FunctionHeader[9];
|
||||
|
||||
const X64ImageInterpreter& pvt_Image;
|
||||
CapstoneDisassembler pvt_Disassembler;
|
||||
KeystoneAssembler pvt_Assembler;
|
||||
uint32_t pvt_FunctionOffset;
|
||||
uint32_t pvt_KeywordOffset;
|
||||
uint64_t pvt_StdStringAppendStubRva;
|
||||
|
||||
public:
|
||||
|
||||
explicit
|
||||
PatchSolution2(const X64ImageInterpreter& Image) noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
|
||||
[[nodiscard]]
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override;
|
||||
|
||||
// NOLINTNEXTLINE: mark "virtual" explicitly for more readability
|
||||
virtual void MakePatch(const RSACipher& RsaCipher) const override;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <capstone/capstone.h>
|
||||
#include "ExceptionCapstone.hpp"
|
||||
|
||||
struct CapstoneHandleTraits {
|
||||
using HandleType = csh;
|
||||
|
||||
static inline const HandleType InvalidValue = 0;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(HandleType& Handle) {
|
||||
auto err = cs_close(&Handle);
|
||||
if (err != CS_ERR_OK) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::CapstoneError(__FILE__, __LINE__, err, "ks_close failed.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct CapstoneInsnTraits {
|
||||
using HandleType = cs_insn*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) noexcept {
|
||||
cs_free(Handle, 1);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <keystone/keystone.h>
|
||||
#include "ExceptionKeystone.hpp"
|
||||
|
||||
struct KeystoneHandleTraits {
|
||||
using HandleType = ks_engine*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) {
|
||||
auto err = ks_close(Handle);
|
||||
if (err != KS_ERR_OK) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_close failed.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct KeystoneMallocTraits {
|
||||
using HandleType = uint8_t*;
|
||||
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) noexcept {
|
||||
ks_free(Handle);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <errno.h> // NOLINT
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include "../common/ExceptionSystem.hpp"
|
||||
|
||||
struct FileHandleTraits {
|
||||
using HandleType = int;
|
||||
|
||||
static inline const HandleType InvalidValue = -1;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
|
||||
static void Releasor(const HandleType& Handle) {
|
||||
if (close(Handle) != 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "close failed.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct MapViewTraits {
|
||||
using HandleType = void*;
|
||||
|
||||
static inline const HandleType InvalidValue = MAP_FAILED;
|
||||
|
||||
[[nodiscard]]
|
||||
static bool IsValid(const HandleType& Handle) noexcept {
|
||||
return Handle != InvalidValue;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "X64ImageInterpreter.hpp"
|
||||
|
||||
[[nodiscard]]
|
||||
X64ImageInterpreter X64ImageInterpreter::Parse(void* ImageBase) {
|
||||
X64ImageInterpreter NewImage;
|
||||
|
||||
NewImage.pvt_MachHeader = reinterpret_cast<mach_header_64*>(ImageBase);
|
||||
if (NewImage.pvt_MachHeader->magic != MH_MAGIC_64) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Bad Image");
|
||||
}
|
||||
|
||||
auto cmd_p = reinterpret_cast<load_command*>(NewImage.pvt_MachHeader + 1);
|
||||
for (uint32_t i = 0; i < NewImage.pvt_MachHeader->ncmds; ++i) {
|
||||
switch (cmd_p->cmd) {
|
||||
case LC_SEGMENT_64: {
|
||||
auto segcmd_p = reinterpret_cast<segment_command_64*>(cmd_p);
|
||||
auto section_p = reinterpret_cast<section_64*>(segcmd_p + 1);
|
||||
|
||||
NewImage.pvt_SegmentCommands.emplace_back(segcmd_p);
|
||||
|
||||
for (uint32_t j = 0; j < segcmd_p->nsects; ++j) {
|
||||
NewImage.pvt_Sections.ByIndex[NewImage.pvt_Sections.ByIndex.size()] = §ion_p[j];
|
||||
NewImage.pvt_Sections.ByMapAddress[section_p[j].addr] = §ion_p[j];
|
||||
NewImage.pvt_Sections.ByFileOffset[section_p[j].offset] = §ion_p[j];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LC_DYSYMTAB: {
|
||||
NewImage.pvt_DynamicSymbol.SegmentCommand = reinterpret_cast<dysymtab_command*>(cmd_p);
|
||||
break;
|
||||
}
|
||||
case LC_SYMTAB: {
|
||||
NewImage.pvt_Symbol.SegmentCommand = reinterpret_cast<symtab_command*>(cmd_p);
|
||||
NewImage.pvt_Symbol.StringTable = NewImage.ImageOffset<char*>(NewImage.pvt_Symbol.SegmentCommand->stroff);
|
||||
NewImage.pvt_Symbol.SymbolTable = NewImage.ImageOffset<nlist_64*>(NewImage.pvt_Symbol.SegmentCommand->symoff);
|
||||
break;
|
||||
}
|
||||
case LC_DYLD_INFO_ONLY: { // NOLINT
|
||||
NewImage.pvt_DynamicLoaderInfoOnly.SegmentCommand = reinterpret_cast<dyld_info_command*>(cmd_p);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cmd_p = reinterpret_cast<load_command*>(
|
||||
reinterpret_cast<uint8_t*>(cmd_p) + cmd_p->cmdsize
|
||||
);
|
||||
}
|
||||
|
||||
return NewImage;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
size_t X64ImageInterpreter::NumberOfSections() const noexcept {
|
||||
return pvt_Sections.ByIndex.size();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* X64ImageInterpreter::ImageSection(size_t Index) const {
|
||||
auto it = pvt_Sections.ByIndex.find(Index);
|
||||
if (it != pvt_Sections.ByIndex.cend()) {
|
||||
return it->second;
|
||||
} else {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Section is not found.");
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* X64ImageInterpreter::ImageSection(const char* SegmentName, const char* SectionName) const {
|
||||
for (auto segcmd_p : pvt_SegmentCommands) {
|
||||
if (strncmp(SegmentName, segcmd_p->segname, sizeof(segcmd_p->segname)) == 0) {
|
||||
auto sec_p = reinterpret_cast<section_64*>(segcmd_p + 1);
|
||||
|
||||
for (uint32_t i = 0; i < segcmd_p->nsects; ++i) {
|
||||
if (strncmp(SectionName, sec_p[i].sectname, sizeof(sec_p[i].sectname)) == 0)
|
||||
return &sec_p[i];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Section is not found.");
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* X64ImageInterpreter::ImageSectionByOffset(uint32_t Offset) const {
|
||||
for (const auto& it : pvt_Sections.ByFileOffset) {
|
||||
if (it.first <= Offset && Offset < it.first + it.second->size) {
|
||||
return it.second;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Section is not found.");
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* X64ImageInterpreter::ImageSectionByRva(uint64_t Rva) const {
|
||||
for (const auto& it : pvt_Sections.ByMapAddress) {
|
||||
if (it.first <= Rva && Rva < it.first + it.second->size) {
|
||||
return it.second;
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Section is not found.");
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
uint64_t X64ImageInterpreter::OffsetToRva(uint32_t Offset) const {
|
||||
auto section = ImageSectionByOffset(Offset);
|
||||
return section->addr + (Offset - section->offset);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
uint32_t X64ImageInterpreter::RvaToOffset(uint64_t Rva) const {
|
||||
auto section = ImageSectionByRva(Rva);
|
||||
return section->offset + (Rva - section->addr);
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
nlist_64* X64ImageInterpreter::ImageSymbolTable() const noexcept {
|
||||
return pvt_Symbol.SymbolTable;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
#include <string.h> // NOLINT
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "../common/Exception.hpp"
|
||||
|
||||
class X64ImageInterpreter {
|
||||
public:
|
||||
static constexpr uint64_t InvalidAddress = static_cast<uint64_t>(-1);
|
||||
static constexpr uint32_t InvalidOffset = static_cast<uint32_t>(-1);
|
||||
private:
|
||||
|
||||
mach_header_64* pvt_MachHeader;
|
||||
std::vector<segment_command_64*> pvt_SegmentCommands;
|
||||
|
||||
struct {
|
||||
std::map<size_t, section_64*> ByIndex;
|
||||
std::map<uint64_t, section_64*> ByMapAddress;
|
||||
std::map<uint32_t, section_64*> ByFileOffset;
|
||||
} pvt_Sections;
|
||||
|
||||
struct {
|
||||
dysymtab_command* SegmentCommand;
|
||||
} pvt_DynamicSymbol;
|
||||
|
||||
struct {
|
||||
symtab_command* SegmentCommand;
|
||||
char* StringTable;
|
||||
nlist_64* SymbolTable;
|
||||
} pvt_Symbol;
|
||||
|
||||
struct {
|
||||
dyld_info_command* SegmentCommand;
|
||||
} pvt_DynamicLoaderInfoOnly;
|
||||
|
||||
X64ImageInterpreter() :
|
||||
pvt_MachHeader(nullptr),
|
||||
pvt_DynamicSymbol{},
|
||||
pvt_Symbol{},
|
||||
pvt_DynamicLoaderInfoOnly{} {}
|
||||
|
||||
public:
|
||||
|
||||
[[nodiscard]]
|
||||
static X64ImageInterpreter Parse(void* ImageBase);
|
||||
|
||||
template<typename __ReturnType = void*>
|
||||
[[nodiscard]]
|
||||
__ReturnType ImageBase() const noexcept {
|
||||
static_assert(std::is_pointer_v<__ReturnType>);
|
||||
return reinterpret_cast<__ReturnType>(pvt_MachHeader);
|
||||
}
|
||||
|
||||
template<typename __ReturnType = void*>
|
||||
[[nodiscard]]
|
||||
__ReturnType ImageOffset(size_t Offset) const noexcept {
|
||||
static_assert(std::is_pointer_v<__ReturnType>);
|
||||
return reinterpret_cast<__ReturnType>(
|
||||
reinterpret_cast<uint8_t*>(pvt_MachHeader) + Offset
|
||||
);
|
||||
}
|
||||
|
||||
template<unsigned __CommandMacro>
|
||||
[[nodiscard]]
|
||||
auto CommandOf() const noexcept {
|
||||
if constexpr (__CommandMacro == LC_DYSYMTAB) {
|
||||
return pvt_DynamicSymbol.SegmentCommand;
|
||||
} else if constexpr (__CommandMacro == LC_SYMTAB) {
|
||||
return pvt_Symbol.SegmentCommand;
|
||||
} else if constexpr (__CommandMacro == LC_DYLD_INFO_ONLY) { // NOLINT
|
||||
return pvt_DynamicLoaderInfoOnly.SegmentCommand;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
size_t NumberOfSections() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* ImageSection(size_t Index) const;
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* ImageSection(const char* SegmentName, const char* SectionName) const;
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* ImageSectionByOffset(uint32_t Offset) const;
|
||||
|
||||
[[nodiscard]]
|
||||
section_64* ImageSectionByRva(uint64_t Rva) const;
|
||||
|
||||
template<typename __ReturnType>
|
||||
[[nodiscard]]
|
||||
__ReturnType SectionView(size_t Index) const {
|
||||
auto Section = ImageSection(Index);
|
||||
return ImageOffset<__ReturnType>(Section->offset);
|
||||
}
|
||||
|
||||
template<typename __ReturnType>
|
||||
[[nodiscard]]
|
||||
__ReturnType SectionView(const char* SegmentName, const char* SectionName) const {
|
||||
auto Section = ImageSection(SegmentName, SectionName);
|
||||
return ImageOffset<__ReturnType>(Section->offset);
|
||||
}
|
||||
|
||||
template<typename __ReturnType>
|
||||
[[nodiscard]]
|
||||
__ReturnType SectionView(section_64* Section) const {
|
||||
return ImageOffset<__ReturnType>(Section->offset);
|
||||
}
|
||||
|
||||
template<typename __ReturnType, typename __Hint>
|
||||
[[nodiscard]]
|
||||
__ReturnType SearchSection(size_t Index, __Hint&& Hint) const {
|
||||
return SearchSection<__ReturnType>(ImageSection(Index), std::forward<__Hint>(Hint));
|
||||
}
|
||||
|
||||
template<typename __ReturnType, typename __Hint>
|
||||
[[nodiscard]]
|
||||
__ReturnType SearchSection(const char* SegmentName, const char* SectionName, __Hint&& Hint) const {
|
||||
return SearchSection<__ReturnType>(ImageSection(SegmentName, SectionName), std::forward<__Hint>(Hint));
|
||||
}
|
||||
|
||||
template<typename __ReturnType, typename __Hint>
|
||||
[[nodiscard]]
|
||||
__ReturnType SearchSection(section_64* Section, __Hint&& Hint) const {
|
||||
static_assert(std::is_pointer_v<__ReturnType>);
|
||||
|
||||
auto begin = SectionView<const uint8_t*>(Section);
|
||||
auto end = begin + Section->size;
|
||||
|
||||
for (; begin < end; ++begin) {
|
||||
if (Hint(begin) == true) {
|
||||
return reinterpret_cast<__ReturnType>(const_cast<uint8_t*>(begin));
|
||||
}
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Data is not found.");
|
||||
}
|
||||
|
||||
template<typename __Hint>
|
||||
[[nodiscard]]
|
||||
uint32_t SearchSectionOffset(size_t Index, __Hint&& Hint) const {
|
||||
return SearchSection<uint8_t*>(Index, std::forward<__Hint>(Hint)) - ImageBase<uint8_t*>();
|
||||
}
|
||||
|
||||
template<typename __Hint>
|
||||
[[nodiscard]]
|
||||
uint32_t SearchSectionOffset(const char* SegmentName, const char* SectionName, __Hint&& Hint) const {
|
||||
return SearchSection<uint8_t*>(SegmentName, SectionName, std::forward<__Hint>(Hint)) - ImageBase<uint8_t*>();
|
||||
}
|
||||
|
||||
template<typename __Hint>
|
||||
[[nodiscard]]
|
||||
uint32_t SearchSectionOffset(section_64* Section, __Hint&& Hint) const {
|
||||
return SearchSection<uint8_t*>(Section, std::forward<__Hint>(Hint)) - ImageBase<uint8_t*>();
|
||||
}
|
||||
|
||||
template<typename __Hint>
|
||||
[[nodiscard]]
|
||||
uint64_t SearchSectionRva(size_t Index, __Hint&& Hint) const {
|
||||
auto Section = ImageSection(Index);
|
||||
auto Offset = SearchSection<uint8_t*>(Section, std::forward<__Hint>(Hint)) - SectionView<uint8_t*>(Section);
|
||||
return Section->addr + Offset;
|
||||
}
|
||||
|
||||
template<typename __Hint>
|
||||
[[nodiscard]]
|
||||
uint64_t SearchSectionRva(const char* SegmentName, const char* SectionName, __Hint&& Hint) const {
|
||||
auto Section = ImageSection(SegmentName, SectionName);
|
||||
auto Offset = SearchSection<uint8_t*>(Section, std::forward<__Hint>(Hint)) - SectionView<uint8_t*>(Section);
|
||||
return Section->addr + Offset;
|
||||
}
|
||||
|
||||
template<typename __Hint>
|
||||
[[nodiscard]]
|
||||
uint64_t SearchSectionRva(section_64* Section, __Hint&& Hint) const {
|
||||
auto Offset = SearchSection<uint8_t*>(Section, std::forward<__Hint>(Hint)) - SectionView<uint8_t*>(Section);
|
||||
return Section->addr + Offset;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
uint64_t OffsetToRva(uint32_t Offset) const;
|
||||
|
||||
[[nodiscard]]
|
||||
uint32_t RvaToOffset(uint64_t Address) const;
|
||||
|
||||
[[nodiscard]]
|
||||
nlist_64* ImageSymbolTable() const noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
char* LookupStringTable(size_t Offset) const noexcept {
|
||||
return pvt_Symbol.StringTable + Offset;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
#include <tchar.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#pragma comment(lib, "libcrypto.lib")
|
||||
|
||||
BOOL BackupNavicat(PTSTR NavicatFileName) {
|
||||
TCHAR new_NavicatFileName[1024] = { };
|
||||
|
||||
if (NavicatFileName == nullptr)
|
||||
NavicatFileName = TEXT("navicat.exe");
|
||||
|
||||
_stprintf_s(new_NavicatFileName, TEXT("%s%s"), NavicatFileName, TEXT(".backup"));
|
||||
|
||||
if (!CopyFile(NavicatFileName, new_NavicatFileName, TRUE)) {
|
||||
switch (GetLastError()) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
_tprintf_s(TEXT("Cannot find %s.\r\n"), NavicatFileName);
|
||||
break;
|
||||
case ERROR_FILE_EXISTS:
|
||||
_tprintf_s(TEXT("Backup file already exists.\r\n"));
|
||||
break;
|
||||
default:
|
||||
_tprintf_s(TEXT("Unknown error. CODE: 0x%08x.\r\n"), GetLastError());
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
_tprintf_s(TEXT("%s has been backed up.\r\n"), NavicatFileName);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ReplaceNavicatPublicKey(HANDLE resUpdater, void* pemPublicKey, size_t length) {
|
||||
return UpdateResource(resUpdater,
|
||||
RT_RCDATA,
|
||||
TEXT("ActivationPubKey"),
|
||||
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
||||
pemPublicKey, length);
|
||||
}
|
||||
|
||||
RSA* GeneratePrivateKey() {
|
||||
RSA* PrivateKey = RSA_generate_key(2048, RSA_F4, nullptr, nullptr);
|
||||
BIO* PrivateKeyFile = BIO_new(BIO_s_file());
|
||||
BIO_write_filename(PrivateKeyFile, "RegPrivateKey.pem");
|
||||
PEM_write_bio_RSAPrivateKey(PrivateKeyFile, PrivateKey, nullptr, nullptr, 0, nullptr, nullptr);
|
||||
BIO_free_all(PrivateKeyFile);
|
||||
|
||||
return PrivateKey;
|
||||
}
|
||||
|
||||
int _tmain(int argc, TCHAR* argv[]) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
_tprintf_s(TEXT("Usage:\r\n"));
|
||||
_tprintf_s(TEXT(" navicat-patcher.exe <navicat.exe path>\r\n"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (BackupNavicat(argv[1]) == FALSE)
|
||||
return GetLastError();
|
||||
|
||||
RSA* PrivateKey = GeneratePrivateKey();
|
||||
|
||||
HANDLE hUpdater = BeginUpdateResource(argv[1], FALSE);
|
||||
if (hUpdater == NULL) {
|
||||
_tprintf_s(TEXT("Cannot open file. CODE: 0x%08x\r\n"), GetLastError());
|
||||
RSA_free(PrivateKey);
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
char pemPublicKey[1024] = { };
|
||||
{
|
||||
char temp_buf[1024] = { };
|
||||
BIO* BIO_pemPublicKey = BIO_new(BIO_s_mem());
|
||||
|
||||
PEM_write_bio_RSA_PUBKEY(BIO_pemPublicKey, PrivateKey);
|
||||
BIO_read(BIO_pemPublicKey, temp_buf, 1024);
|
||||
BIO_free_all(BIO_pemPublicKey);
|
||||
|
||||
char* pemPublicKey_ptr = pemPublicKey;
|
||||
for (size_t i = 0; i < 1024; ++i) {
|
||||
if (temp_buf[i] == '\n')
|
||||
*(pemPublicKey_ptr++) = '\r';
|
||||
if (temp_buf[i] == 0)
|
||||
break;
|
||||
*(pemPublicKey_ptr++) = temp_buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (ReplaceNavicatPublicKey(hUpdater, pemPublicKey, strlen(pemPublicKey)) == FALSE) {
|
||||
_tprintf_s(TEXT("Cannot replace public key. CODE: 0x%08x\r\n"), GetLastError());
|
||||
EndUpdateResource(hUpdater, TRUE);
|
||||
return GetLastError();
|
||||
} else {
|
||||
_tprintf_s(TEXT("Public key has been replaced.\r\n"));
|
||||
EndUpdateResource(hUpdater, FALSE);
|
||||
}
|
||||
|
||||
_tprintf_s(TEXT("Success!.\r\n"));
|
||||
RSA_free(PrivateKey);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <plist/plist++.h>
|
||||
#include <string>
|
||||
|
||||
#include "../common/ExceptionSystem.hpp"
|
||||
#include "../common/ResourceOwned.hpp"
|
||||
#include "ResourceTraitsUnix.hpp"
|
||||
#include "PatchSolutions.hpp"
|
||||
|
||||
static void Welcome() {
|
||||
puts("***************************************************");
|
||||
puts("* Navicat Patcher by @DoubleLabyrinth *");
|
||||
puts("* Version: 4.0 *");
|
||||
puts("***************************************************");
|
||||
puts("");
|
||||
puts("Press Enter to continue or Ctrl + C to abort.");
|
||||
getchar();
|
||||
}
|
||||
|
||||
static void Help() {
|
||||
puts("***************************************************");
|
||||
puts("* Navicat Patcher by @DoubleLabyrinth *");
|
||||
puts("* Version: 4.0 *");
|
||||
puts("***************************************************");
|
||||
puts("");
|
||||
puts("Usage:");
|
||||
puts(" navicat-patcher <Navicat installation path> [RSA-2048 Private Key File]");
|
||||
puts("");
|
||||
puts(" <Navicat installation path> Path to `Navicat Premium.app`.");
|
||||
puts(" Example:");
|
||||
puts(" /Applications/Navicat\\ Premium.app/");
|
||||
puts(" This parameter must be specified.");
|
||||
puts("");
|
||||
puts(" [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.");
|
||||
puts(" This parameter is optional.");
|
||||
puts("");
|
||||
}
|
||||
|
||||
static std::string GetNavicatVersion(const char* AppPath) {
|
||||
ResourceOwned hInfoPlist(FileHandleTraits{});
|
||||
ResourceOwned InfoPlist(CppObjectTraits<PList::Dictionary>{});
|
||||
|
||||
hInfoPlist.TakeOver(open((std::string(AppPath) + "/Contents/Info.plist").c_str(), O_RDONLY));
|
||||
if (hInfoPlist.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to open Contents/Info.plist.");
|
||||
}
|
||||
|
||||
struct stat statInfoPlist = {};
|
||||
if (fstat(hInfoPlist, &statInfoPlist) != 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to get file size of Contents/Info.plist.");
|
||||
}
|
||||
|
||||
std::string contentInfoPlist(statInfoPlist.st_size, '\x00');
|
||||
if (read(hInfoPlist, contentInfoPlist.data(), contentInfoPlist.size()) != contentInfoPlist.size()) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to read Contents/Info.plist.");
|
||||
}
|
||||
|
||||
InfoPlist.TakeOver(dynamic_cast<PList::Dictionary*>(PList::Structure::FromXml(contentInfoPlist)));
|
||||
if (InfoPlist.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Failed to parse Contents/Info.plist.");
|
||||
}
|
||||
|
||||
auto kv = InfoPlist->Find("CFBundleShortVersionString");
|
||||
if (kv == InfoPlist->End()) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Cannot find CFBundleShortVersionString in Contents/Info.plist.");
|
||||
}
|
||||
|
||||
if (kv->second->GetType() == PLIST_STRING) {
|
||||
return dynamic_cast<PList::String*>(kv->second)->GetValue();
|
||||
} else {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "Failed to get Navicat version.");
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadKey(RSACipher& RsaCipher, const char* RsaKeyFileName,
|
||||
PatchSolution* lpSolution0,
|
||||
PatchSolution* lpSolution1,
|
||||
PatchSolution* lpSolution2) {
|
||||
if (RsaKeyFileName) {
|
||||
RsaCipher.ImportKeyFromFile<RSAKeyType::PrivateKey, RSAKeyFormat::PEM>(RsaKeyFileName);
|
||||
|
||||
if ((lpSolution0 && !lpSolution0->CheckKey(RsaCipher)) ||
|
||||
(lpSolution1 && !lpSolution1->CheckKey(RsaCipher)) ||
|
||||
(lpSolution2 && !lpSolution2->CheckKey(RsaCipher)))
|
||||
{
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::Exception(__FILE__, __LINE__, "The RSA private key you provide cannot be used.");
|
||||
}
|
||||
} else {
|
||||
puts("");
|
||||
puts("[*] Generating new RSA private key, it may take a long time...");
|
||||
|
||||
do {
|
||||
RsaCipher.GenerateKey(2048);
|
||||
} while ((lpSolution0 && !lpSolution0->CheckKey(RsaCipher)) ||
|
||||
(lpSolution1 && !lpSolution1->CheckKey(RsaCipher)) ||
|
||||
(lpSolution2 && !lpSolution2->CheckKey(RsaCipher))); // re-generate RSA key if CheckKey return false
|
||||
|
||||
RsaCipher.ExportKeyToFile<RSAKeyType::PrivateKey, RSAKeyFormat::PEM>("RegPrivateKey.pem");
|
||||
|
||||
puts("[+] New RSA private key has been saved to RegPrivateKey.pem.");
|
||||
}
|
||||
|
||||
std::string PublicKeyPEM = RsaCipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
puts("");
|
||||
puts("[*] Your RSA public key:");
|
||||
puts(PublicKeyPEM.c_str());
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
Help();
|
||||
return -1;
|
||||
} else {
|
||||
Welcome();
|
||||
|
||||
try {
|
||||
RSACipher RsaCipher;
|
||||
ResourceOwned Solution0(CppObjectTraits<PatchSolution>{});
|
||||
ResourceOwned Solution1(CppObjectTraits<PatchSolution>{});
|
||||
ResourceOwned Solution2(CppObjectTraits<PatchSolution>{});
|
||||
|
||||
ResourceOwned hMainApp(FileHandleTraits{}, open((std::string(argv[1]) + "/Contents/MacOS/Navicat Premium").c_str(), O_RDWR));
|
||||
if (hMainApp.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "open failed.");
|
||||
}
|
||||
|
||||
struct stat statMainApp = {};
|
||||
if (fstat(hMainApp, &statMainApp) != 0) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "fstat failed.");
|
||||
}
|
||||
|
||||
ResourceOwned lpMainApp(MapViewTraits{},
|
||||
mmap(nullptr, static_cast<size_t>(statMainApp.st_size), PROT_READ | PROT_WRITE, MAP_SHARED, hMainApp, 0),
|
||||
[&statMainApp](void* p) { munmap(p, statMainApp.st_size); }
|
||||
);
|
||||
if (lpMainApp.IsValid() == false) {
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "mmap failed.");
|
||||
}
|
||||
|
||||
int Ver0, Ver1, Ver2;
|
||||
if (sscanf(GetNavicatVersion(argv[1]).c_str(), "%d.%d.%d", &Ver0, &Ver1, &Ver2) != 3) { // NOLINT
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to get version of Navicat.");
|
||||
}
|
||||
|
||||
X64ImageInterpreter MainApp = X64ImageInterpreter::Parse(lpMainApp);
|
||||
|
||||
printf("[*] Your Navicat version: %d.%d.%d\n", Ver0, Ver1, Ver2);
|
||||
printf("\n");
|
||||
|
||||
Solution0.TakeOver(new PatchSolution0(MainApp));
|
||||
Solution1.TakeOver(new PatchSolution1(MainApp));
|
||||
Solution2.TakeOver(new PatchSolution2(MainApp));
|
||||
|
||||
if (Solution0->FindPatchOffset() == false) {
|
||||
Solution0.Release();
|
||||
}
|
||||
if (Solution1->FindPatchOffset() == false) {
|
||||
Solution1.Release();
|
||||
}
|
||||
if (Solution2->FindPatchOffset() == false) {
|
||||
Solution2.Release();
|
||||
}
|
||||
|
||||
//
|
||||
// Begin strategies by different Navicat versions
|
||||
//
|
||||
if (Ver0 < 12) { // ver < 12.0.0
|
||||
// NOLINTNEXTLINE: allow exceptions that is not derived from std::exception
|
||||
throw nkg::SystemError(__FILE__, __LINE__, errno, "Unsupported version of Navicat.");
|
||||
} else if (Ver0 == 12 && Ver1 == 0 && Ver2 < 24) { // ver < 12.0.24
|
||||
std::string path(argv[1]);
|
||||
while(path.back() == '/') {
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
printf("[*] Your Navicat version is < 12.0.24. So there would be nothing patched.\n");
|
||||
printf(" Just use `openssl` to generate `RegPrivateKey.pem` and `rpk` file:\n");
|
||||
printf(" openssl genrsa -out RegPrivateKey.pem 2048\n");
|
||||
printf(" openssl rsa -in RegPrivateKey.pem -pubout -out rpk\n");
|
||||
printf(" and replace `%s/Contents/Resources/rpk` with the `rpk` file you just generated.\n", path.c_str());
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
} else if (Ver0 == 12 && (Ver1 == 0 || (Ver1 == 1 && Ver2 < 14))) { // 12.0.24 <= ver && ver < 12.1.14
|
||||
// In this case, Solution0 must be applied
|
||||
if (Solution0.IsValid() == false) {
|
||||
puts("[-] Patch abort. None of PatchSolutions will be applied.");
|
||||
puts(" Are you sure your Navicat has not been patched before?");
|
||||
return -1;
|
||||
}
|
||||
} else if (Ver0 == 12 && Ver1 == 1 && Ver2 == 14) { // ver == 12.1.14
|
||||
// In this case, Solution0 and Solution1 must be applied
|
||||
if ((Solution0.IsValid() && Solution1.IsValid()) == false) {
|
||||
puts("[-] Patch abort. None of PatchSolutions will be applied.");
|
||||
puts(" Are you sure your Navicat has not been patched before?");
|
||||
return -1;
|
||||
}
|
||||
} else { // ver > 12.1.14
|
||||
// In this case, Solution0 and Solution2 must be applied
|
||||
if ((Solution0.IsValid() && Solution2.IsValid()) == false) {
|
||||
puts("[-] Patch abort. None of PatchSolutions will be applied.");
|
||||
puts(" Are you sure your Navicat has not been patched before?");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//
|
||||
// End strategies by different Navicat versions
|
||||
//
|
||||
|
||||
LoadKey(RsaCipher, argc == 3 ? argv[2] : nullptr, Solution0, Solution1, Solution2);
|
||||
|
||||
if (Solution0.IsValid()) {
|
||||
Solution0->MakePatch(RsaCipher);
|
||||
}
|
||||
if (Solution1.IsValid()) {
|
||||
Solution1->MakePatch(RsaCipher);
|
||||
}
|
||||
if (Solution2.IsValid()) {
|
||||
Solution2->MakePatch(RsaCipher);
|
||||
}
|
||||
|
||||
if (Solution0.IsValid())
|
||||
puts("[+] PatchSolution0 has been applied.");
|
||||
if (Solution1.IsValid())
|
||||
puts("[+] PatchSolution1 has been applied.");
|
||||
if (Solution2.IsValid())
|
||||
puts("[+] PatchSolution2 has been applied.");
|
||||
|
||||
puts("");
|
||||
puts("**************************************************************");
|
||||
puts("* Patch has been done successfully. Have fun and enjoy~~ *");
|
||||
puts("* DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! *");
|
||||
puts("**************************************************************");
|
||||
|
||||
return 0;
|
||||
} catch (nkg::Exception& e) {
|
||||
printf("[-] %s:%zu -> \n", e.File(), e.Line());
|
||||
printf(" %s\n", e.Message());
|
||||
if (e.HasErrorCode()) {
|
||||
printf(" %s\n", e.ErrorString());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{30BE8CAB-6137-4441-9F64-599D0A6EEA0C}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>navicatpatcher</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>D:\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>D:\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>D:\OpenSSL-Win64\include;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>D:\OpenSSL-Win64\lib;$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="_tmain.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="源文件">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="头文件">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="资源文件">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="_tmain.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user