Add support for Navicat Premium 12.1.11 x64
This commit is contained in:
parent
e5300eee16
commit
5ffe7a198e
146
navicat-patcher/Solution2.cpp
Normal file
146
navicat-patcher/Solution2.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "def.hpp"
|
||||
|
||||
namespace Patcher {
|
||||
|
||||
const char Solution2::KeywordsMeta[KeywordsCount + 1] =
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I"
|
||||
"qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv"
|
||||
"a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF"
|
||||
"R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2"
|
||||
"WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt"
|
||||
"YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ"
|
||||
"awIDAQAB";
|
||||
|
||||
uint8_t Solution2::Keywords[KeywordsCount][5];
|
||||
|
||||
void Solution2::BuildKeywords() noexcept {
|
||||
for (size_t i = 0; i < KeywordsCount; ++i) {
|
||||
Keywords[i][0] = 0x83; // Keywords[i] = asm('xor eax, KeywordsMeta[i]') +
|
||||
Keywords[i][1] = 0xf0;
|
||||
Keywords[i][2] = KeywordsMeta[i];
|
||||
Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxxxxxxxxxx, al')
|
||||
Keywords[i][4] = 0x05;
|
||||
}
|
||||
}
|
||||
|
||||
bool Solution2::FindPatchOffset() noexcept {
|
||||
PIMAGE_SECTION_HEADER textSection = nullptr;
|
||||
uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
|
||||
uint8_t* ptextSectionData = nullptr;
|
||||
off_t Offsets[KeywordsCount];
|
||||
memset(Offsets, -1, sizeof(Offsets));
|
||||
|
||||
textSection = Helper::ImageSectionHeader(pTargetFileView, ".text");
|
||||
if (textSection == nullptr)
|
||||
return false;
|
||||
ptextSectionData = pTargetFileView + textSection->PointerToRawData;
|
||||
|
||||
BuildKeywords();
|
||||
|
||||
// Find offsets
|
||||
{
|
||||
size_t FirstKeywordCounter = 0;
|
||||
uint32_t Hints[9];
|
||||
DWORD PossibleRangeStart = 0xffffffff;
|
||||
DWORD PossibleRangeEnd;
|
||||
for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
|
||||
if (memcmp(ptextSectionData + i, Keywords[0], 5) == 0) {
|
||||
Hints[FirstKeywordCounter++] =
|
||||
*reinterpret_cast<uint32_t*>(ptextSectionData + i + sizeof(Keywords[0])) +
|
||||
i + sizeof(Keywords[0]) + sizeof(uint32_t);
|
||||
if (i < PossibleRangeStart)
|
||||
PossibleRangeStart = i;
|
||||
}
|
||||
}
|
||||
|
||||
PossibleRangeStart -= 0x1000;
|
||||
PossibleRangeEnd = PossibleRangeStart + 0x100000;
|
||||
|
||||
// Keywords[0] should occur 9 times.
|
||||
// Because there's only 9 'M' chars in `KeywordsMeta`.
|
||||
if (FirstKeywordCounter != 9)
|
||||
return false;
|
||||
|
||||
Helper::QuickSort(Hints, 0, _countof(Hints));
|
||||
|
||||
// assert
|
||||
// if not satisfied, refuse to patch
|
||||
if (Hints[8] - Hints[0] != 0x18360F8F8 - 0x18360F7D0)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < KeywordsCount; ++i) {
|
||||
if (Offsets[i] != -1)
|
||||
continue;
|
||||
|
||||
for (DWORD j = PossibleRangeStart; j < PossibleRangeEnd; ++j) {
|
||||
if (memcmp(ptextSectionData + j, Keywords[i], sizeof(Keywords[i])) == 0) {
|
||||
off_t index =
|
||||
*reinterpret_cast<uint32_t*>(ptextSectionData + j + sizeof(Keywords[i])) +
|
||||
j + sizeof(Keywords[i]) + sizeof(uint32_t) - Hints[0];
|
||||
|
||||
if (0 <= index && index < KeywordsCount && KeywordsMeta[index] == KeywordsMeta[i]) {
|
||||
Offsets[index] = textSection->PointerToRawData + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if not found, refuse to patch
|
||||
if (Offsets[i] == -1)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(sizeof(PatchOffsets) == sizeof(Offsets), "static_assert failure!");
|
||||
memcpy(PatchOffsets, Offsets, sizeof(PatchOffsets));
|
||||
|
||||
for (size_t i = 0; i < KeywordsCount; ++i)
|
||||
_tprintf_s(TEXT("MESSAGE: [Solution2] Keywords[%zu] has been found: offset = +0x%08lx.\n"),
|
||||
i, PatchOffsets[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Solution2::MakePatch(RSACipher* cipher) const {
|
||||
std::string RSAPublicKeyPEM;
|
||||
uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
|
||||
|
||||
RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
|
||||
if (RSAPublicKeyPEM.empty()) {
|
||||
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = RSAPublicKeyPEM.find("\n", pos)) != std::string::npos) {
|
||||
RSAPublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (RSAPublicKeyPEM.length() != KeywordsCount) {
|
||||
REPORT_ERROR("ERROR: Public key length does not match.");
|
||||
return false;
|
||||
}
|
||||
|
||||
PRINT_MESSAGE("//");
|
||||
PRINT_MESSAGE("// Begin Solution2");
|
||||
PRINT_MESSAGE("//");
|
||||
for (size_t i = 0; i < KeywordsCount; ++i) {
|
||||
_tprintf_s(TEXT("@+0x%08X: %02X %02X %02X --> "),
|
||||
PatchOffsets[i],
|
||||
pTargetFileView[PatchOffsets[i]],
|
||||
pTargetFileView[PatchOffsets[i] + 1],
|
||||
pTargetFileView[PatchOffsets[i] + 2]);
|
||||
pTargetFileView[PatchOffsets[i] + 2] = RSAPublicKeyPEM[i];
|
||||
_tprintf_s(TEXT("%02X %02X %02X\n"),
|
||||
pTargetFileView[PatchOffsets[i]],
|
||||
pTargetFileView[PatchOffsets[i] + 1],
|
||||
pTargetFileView[PatchOffsets[i] + 2]);
|
||||
}
|
||||
|
||||
PRINT_MESSAGE("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
#include "def.hpp"
|
||||
|
||||
#define SAFE_DELETE(x) { delete x; x = nullptr; }
|
||||
|
||||
static void help() {
|
||||
PRINT_MESSAGE("Usage:");
|
||||
PRINT_MESSAGE(" navicat-patcher.exe <Navicat installation path> [RSA-2048 PEM file]");
|
||||
@ -42,7 +44,8 @@ static DWORD BackupFile(std::Tstring& from, std::Tstring& to) {
|
||||
|
||||
static BOOL LoadKey(RSACipher* cipher, LPTSTR filename,
|
||||
Patcher::Solution* pSolution0,
|
||||
Patcher::Solution* pSolution1) {
|
||||
Patcher::Solution* pSolution1,
|
||||
Patcher::Solution* pSolution2) {
|
||||
if (filename) {
|
||||
std::string PrivateKeyFileName;
|
||||
|
||||
@ -57,7 +60,8 @@ static BOOL LoadKey(RSACipher* cipher, LPTSTR filename,
|
||||
}
|
||||
|
||||
if (pSolution0 && !pSolution0->CheckKey(cipher) ||
|
||||
pSolution1 && !pSolution1->CheckKey(cipher)) {
|
||||
pSolution1 && !pSolution1->CheckKey(cipher) ||
|
||||
pSolution2 && !pSolution2->CheckKey(cipher)) {
|
||||
REPORT_ERROR("ERROR: The RSA private key you provide cannot be used.");
|
||||
return FALSE;
|
||||
}
|
||||
@ -68,7 +72,8 @@ static BOOL LoadKey(RSACipher* cipher, LPTSTR filename,
|
||||
do {
|
||||
cipher->GenerateKey(2048);
|
||||
} while (pSolution0 && !pSolution0->CheckKey(cipher) ||
|
||||
pSolution1 && !pSolution1->CheckKey(cipher)); // re-generate RSA key if one of CheckKey return FALSE
|
||||
pSolution1 && !pSolution1->CheckKey(cipher) ||
|
||||
pSolution2 && !pSolution2->CheckKey(cipher)); // re-generate RSA key if one of CheckKey return false
|
||||
|
||||
if (!cipher->ExportKeyToFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::NotSpecified>("RegPrivateKey.pem")) {
|
||||
REPORT_ERROR("ERROR: Failed to save RSA private key.");
|
||||
@ -100,6 +105,7 @@ int _tmain(int argc, TCHAR* argv[]) {
|
||||
FileMapper* pLibcc = nullptr;
|
||||
Patcher::Solution* pSolution0 = nullptr;
|
||||
Patcher::Solution* pSolution1 = nullptr;
|
||||
Patcher::Solution* pSolution2 = nullptr;
|
||||
|
||||
DWORD ErrorCode;
|
||||
|
||||
@ -113,6 +119,7 @@ int _tmain(int argc, TCHAR* argv[]) {
|
||||
pLibcc = new FileMapper();
|
||||
pSolution0 = new Patcher::Solution0();
|
||||
pSolution1 = new Patcher::Solution1();
|
||||
pSolution2 = new Patcher::Solution2();
|
||||
|
||||
if (!SetPath(argv[1])) {
|
||||
PRINT_MESSAGE("The path you specified:");
|
||||
@ -180,10 +187,9 @@ FindLibcc:
|
||||
PRINT_MESSAGE("MESSAGE: libcc.dll has been found.");
|
||||
} else if (ErrorCode == ERROR_FILE_NOT_FOUND) {
|
||||
PRINT_MESSAGE("MESSAGE: libcc.dll is not found. Solution1 and Solution2 will be omitted.");
|
||||
delete pSolution1;
|
||||
pSolution1 = nullptr;
|
||||
delete pLibcc;
|
||||
pLibcc = nullptr;
|
||||
SAFE_DELETE(pSolution2);
|
||||
SAFE_DELETE(pSolution1);
|
||||
SAFE_DELETE(pLibcc);
|
||||
} else if (ErrorCode == ERROR_ACCESS_DENIED) {
|
||||
PRINT_MESSAGE("ERROR: Cannot open libcc.dll for ERROR_ACCESS_DENIED.");
|
||||
PRINT_MESSAGE("Please re-run with Administrator privilege.");
|
||||
@ -196,6 +202,7 @@ FindLibcc:
|
||||
SearchPublicKey:
|
||||
pSolution0->SetFile(pMainApp);
|
||||
if (pSolution1) pSolution1->SetFile(pLibcc);
|
||||
if (pSolution2) pSolution2->SetFile(pLibcc);
|
||||
|
||||
PRINT_MESSAGE("");
|
||||
if (!pSolution0->FindPatchOffset()) {
|
||||
@ -207,13 +214,18 @@ SearchPublicKey:
|
||||
if (pSolution1 && !pSolution1->FindPatchOffset()) {
|
||||
PRINT_MESSAGE("MESSAGE: Cannot find RSA public key in libcc.dll. Solution1 will be omitted.");
|
||||
pSolution1->SetFile(nullptr);
|
||||
delete pSolution1;
|
||||
pSolution1 = nullptr;
|
||||
SAFE_DELETE(pSolution1);
|
||||
}
|
||||
|
||||
if (pSolution2 && !pSolution2->FindPatchOffset()) {
|
||||
PRINT_MESSAGE("MESSAGE: Cannot find RSA public key in libcc.dll. Solution2 will be omitted.");
|
||||
pSolution2->SetFile(nullptr);
|
||||
SAFE_DELETE(pSolution2);
|
||||
}
|
||||
|
||||
LoadingKey:
|
||||
PRINT_MESSAGE("");
|
||||
if (!LoadKey(cipher, argc == 3 ? argv[2] : nullptr, pSolution0, pSolution1))
|
||||
if (!LoadKey(cipher, argc == 3 ? argv[2] : nullptr, pSolution0, pSolution1, pSolution2))
|
||||
goto ON_tmain_ERROR;
|
||||
|
||||
BackupFiles:
|
||||
@ -242,7 +254,7 @@ BackupFiles:
|
||||
goto ON_tmain_ERROR;
|
||||
}
|
||||
|
||||
if (pSolution1) {
|
||||
if (pSolution1 || pSolution2) {
|
||||
ErrorCode = BackupFile(InstallationPath + LibccName, InstallationPath + LibccName + TEXT(".backup"));
|
||||
if (ErrorCode == ERROR_SUCCESS) {
|
||||
PRINT_MESSAGE("MESSAGE: libcc.dll has been backed up successfully.");
|
||||
@ -269,16 +281,22 @@ MakingPatch:
|
||||
if (pSolution1 && !pSolution1->MakePatch(cipher))
|
||||
goto ON_tmain_ERROR;
|
||||
|
||||
if (pSolution2 && !pSolution2->MakePatch(cipher))
|
||||
goto ON_tmain_ERROR;
|
||||
|
||||
PRINT_MESSAGE("Solution0 has been done successfully.");
|
||||
if (pSolution1)
|
||||
PRINT_MESSAGE("Solution1 has been done successfully.");
|
||||
if (pSolution2)
|
||||
PRINT_MESSAGE("Solution2 has been done successfully.");
|
||||
|
||||
ON_tmain_ERROR:
|
||||
delete pSolution1;
|
||||
delete pSolution0;
|
||||
delete pLibcc;
|
||||
delete pMainApp;
|
||||
delete cipher;
|
||||
SAFE_DELETE(pSolution2);
|
||||
SAFE_DELETE(pSolution1);
|
||||
SAFE_DELETE(pSolution0);
|
||||
SAFE_DELETE(pLibcc);
|
||||
SAFE_DELETE(pMainApp);
|
||||
SAFE_DELETE(cipher);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,48 @@ namespace Helper {
|
||||
void PrintMemory(const void* from, const void* to, const void* base = nullptr);
|
||||
|
||||
PIMAGE_SECTION_HEADER ImageSectionHeader(PVOID lpBase, LPCSTR lpSectionName);
|
||||
|
||||
template<typename _Type, bool _Ascending = true>
|
||||
void QuickSort(_Type* pArray, off_t begin, off_t end) {
|
||||
if (end - begin <= 1)
|
||||
return;
|
||||
|
||||
off_t i = begin;
|
||||
off_t j = end - 1;
|
||||
_Type seperator = static_cast<_Type&&>(pArray[begin]);
|
||||
|
||||
while (i < j) {
|
||||
if (_Ascending) {
|
||||
while (i < j && seperator <= pArray[j])
|
||||
--j;
|
||||
|
||||
if (i < j)
|
||||
pArray[i++] = static_cast<_Type&&>(pArray[j]);
|
||||
|
||||
while (i < j && pArray[i] <= seperator)
|
||||
++i;
|
||||
|
||||
if (i < j)
|
||||
pArray[j--] = static_cast<_Type&&>(pArray[i]);
|
||||
} else {
|
||||
while (i < j && seperator >= pArray[j])
|
||||
--j;
|
||||
|
||||
if (i < j)
|
||||
pArray[i++] = static_cast<_Type&&>(pArray[j]);
|
||||
|
||||
while (i < j && pArray[i] >= seperator)
|
||||
++i;
|
||||
|
||||
if (i < j)
|
||||
pArray[j--] = static_cast<_Type&&>(pArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
pArray[i] = static_cast<_Type&&>(seperator);
|
||||
QuickSort<_Type, _Ascending>(pArray, begin, i);
|
||||
QuickSort<_Type, _Ascending>(pArray, i + 1, end);
|
||||
}
|
||||
}
|
||||
|
||||
#define REPORT_ERROR(msg) Helper::ErrorReport(TEXT(__FUNCTION__), __LINE__, TEXT(msg))
|
||||
@ -199,4 +241,37 @@ namespace Patcher {
|
||||
// Return true if success, otherwise return false
|
||||
virtual bool MakePatch(RSACipher* cipher) const override;
|
||||
};
|
||||
|
||||
class Solution2 : public Solution {
|
||||
private:
|
||||
static constexpr size_t KeywordsCount = 0x188;
|
||||
static const char KeywordsMeta[KeywordsCount + 1];
|
||||
static uint8_t Keywords[KeywordsCount][5];
|
||||
|
||||
FileMapper* pTargetFile;
|
||||
off_t PatchOffsets[KeywordsCount];
|
||||
|
||||
void BuildKeywords() noexcept;
|
||||
public:
|
||||
Solution2() :
|
||||
pTargetFile(nullptr) {
|
||||
memset(PatchOffsets, -1, sizeof(PatchOffsets));
|
||||
}
|
||||
|
||||
virtual void SetFile(FileMapper* pLibccFile) noexcept override {
|
||||
pTargetFile = pLibccFile;
|
||||
}
|
||||
|
||||
// Solution2 has no requirements for an RSA-2048 key
|
||||
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return true if found, otherwise return false
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
|
||||
// Make a patch based on an RSA private key given
|
||||
// Return true if success, otherwise return false
|
||||
virtual bool MakePatch(RSACipher* cipher) const override;
|
||||
};
|
||||
}
|
||||
|
||||
@ -169,6 +169,7 @@
|
||||
<ClCompile Include="Helper.cpp" />
|
||||
<ClCompile Include="Solution0.cpp" />
|
||||
<ClCompile Include="Solution1.cpp" />
|
||||
<ClCompile Include="Solution2.cpp" />
|
||||
<ClCompile Include="_tmain.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
<ClCompile Include="Helper.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Solution2.cpp">
|
||||
<Filter>源文件</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="def.hpp">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user