7 Commits
10 changed files with 746 additions and 544 deletions
+26 -7
View File
@@ -30,18 +30,30 @@ This repository will tell you how Navicat offline activation works.
navicat-patcher.exe "C:\Program Files\PremiumSoft\Navicat Premium 12" .\RegPrivateKey.pem
```
It has been tested on __Navicat Premium 12.1.7 Simplified Chinese version__. The following is an example of output.
It has been tested on __Navicat Premium 12.1.11 Simplified Chinese version__. The following is an example of output.
```
MESSAGE: Navicat.exe has been found.
MESSAGE: libcc.dll has been found.
MESSAGE: [Solution0] Keyword has been found: offset = +0x0297a6e0.
MESSAGE: [Solution1] Keywords[0] has been found: offset = +0x02057530.
MESSAGE: [Solution1] Keywords[1] has been found: offset = +0x006c4f89.
MESSAGE: [Solution1] Keywords[2] has been found: offset = +0x02057240.
MESSAGE: [Solution1] Keywords[3] has been found: offset = +0x006c4f6f.
MESSAGE: [Solution1] Keywords[4] has been found: offset = +0x0205722c.
MESSAGE: [Solution0] Keyword has been found: offset = +0x029a4b9c.
MESSAGE: [Solution1] Keywords[0] has been found: offset = +0x02294960.
MESSAGE: [Solution1] Keywords[1] has been found: offset = +0x0074bd29.
MESSAGE: [Solution1] Keywords[2] has been found: offset = +0x02294670.
MESSAGE: [Solution1] Keywords[3] has been found: offset = +0x0074bd0f.
MESSAGE: [Solution1] Keywords[4] has been found: offset = +0x02294664.
MESSAGE: [Solution2] Keywords[0] has been found: offset = +0x01643118.
MESSAGE: [Solution2] Keywords[1] has been found: offset = +0x016437c1.
MESSAGE: [Solution2] Keywords[2] has been found: offset = +0x01643ed0.
MESSAGE: [Solution2] Keywords[3] has been found: offset = +0x016445df.
MESSAGE: [Solution2] Keywords[4] has been found: offset = +0x01644cee.
MESSAGE: [Solution2] Keywords[5] has been found: offset = +0x016453fd.
MESSAGE: [Solution2] Keywords[6] has been found: offset = +0x01645b0b.
MESSAGE: [Solution2] Keywords[7] has been found: offset = +0x01646217.
MESSAGE: [Solution2] Keywords[8] has been found: offset = +0x01646926.
MESSAGE: [Solution2] Keywords[9] has been found: offset = +0x01647035.
...
...
Your RSA public key:
-----BEGIN PUBLIC KEY-----
@@ -61,9 +73,16 @@ This repository will tell you how Navicat offline activation works.
......
......
......
@+0x016ED490: 83 F0 49 --> 83 F0 49
@+0x016EDB9F: 83 F0 44 --> 83 F0 44
@+0x016EE2AE: 83 F0 41 --> 83 F0 41
@+0x016EE9BD: 83 F0 51 --> 83 F0 51
@+0x016EF0CB: 83 F0 41 --> 83 F0 41
@+0x016EF7D7: 83 F0 42 --> 83 F0 42
Solution0 has been done successfully.
Solution1 has been done successfully.
Solution2 has been done successfully.
```
3. Then use `navicat-keygen.exe` to generate __snKey__ and __Activation Code__
+1 -1
View File
@@ -74,7 +74,7 @@
如果指定了`-bin``navicat-keygen.exe`最终将生成`license_file`文件。这个选项是给Navicat旧激活方式使用的。
如果指定了`-bin``navicat-keygen.exe`最终将生成Base64样式的激活码。这个选项是给Navicat新激活方式使用的。
如果指定了`-text``navicat-keygen.exe`最终将生成Base64样式的激活码。这个选项是给Navicat新激活方式使用的。
__这个参数必须指定。__
+37 -5
View File
@@ -91,19 +91,29 @@ namespace Helper {
_tprintf_s(TEXT("%s CODE: 0x%08X\n"), msg, err_code);
}
//
// read byte(s) at address `p` as _Type to `out`
// succeed if return true, otherwise return false
//
template<typename _Type>
static __forceinline bool ProbeForRead(const void* p, void* out) {
__try {
*reinterpret_cast<_Type*>(out) = *reinterpret_cast<const _Type*>(p);
return true;
} __except (1) {
} __except (EXCEPTION_EXECUTE_HANDLER) {
return false;
}
}
void PrintMemory(const void* a, const void* b, const void* base) {
const uint8_t* start = reinterpret_cast<const uint8_t*>(a);
const uint8_t* end = reinterpret_cast<const uint8_t*>(b);
//
// Print memory data in [from, to) at least
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
// NOTICE:
// `base` must >= `from`
//
void PrintMemory(const void* from, const void* to, const void* base) {
const uint8_t* start = reinterpret_cast<const uint8_t*>(from);
const uint8_t* end = reinterpret_cast<const uint8_t*>(to);
const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(base);
if (start >= end)
@@ -150,4 +160,26 @@ namespace Helper {
}
}
}
PIMAGE_SECTION_HEADER ImageSectionHeader(PVOID lpBase, LPCSTR lpSectionName) {
PIMAGE_DOS_HEADER pFileHeader = NULL;
PIMAGE_NT_HEADERS pNtHeader = NULL;
IMAGE_SECTION_HEADER* pSectionHeaders = NULL;
pFileHeader = (IMAGE_DOS_HEADER*)lpBase;
if (pFileHeader->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
pNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)lpBase + pFileHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return NULL;
pSectionHeaders = (IMAGE_SECTION_HEADER*)((BYTE*)pNtHeader +
offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
pNtHeader->FileHeader.SizeOfOptionalHeader);
for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
if (_stricmp((const char*)pSectionHeaders[i].Name, lpSectionName) == 0)
return pSectionHeaders + i;
return NULL;
}
}
+33 -153
View File
@@ -19,105 +19,22 @@ namespace Patcher {
"awIDAQAB\r\n"
"-----END PUBLIC KEY-----\r\n";
BOOL Solution0::SetPath(const std::Tstring& Path) {
DWORD Attr;
bool Solution0::FindPatchOffset() noexcept {
bool bFound = false;
Attr = GetFileAttributes(Path.c_str());
if (Attr == INVALID_FILE_ATTRIBUTES) {
if (GetLastError() == ERROR_INVALID_NAME || GetLastError() == ERROR_FILE_NOT_FOUND)
REPORT_ERROR("ERROR: Invalid path. Are you sure the path you specified is correct?");
else
REPORT_ERROR_WITH_CODE("ERROR: GetFileAttributes failed.", GetLastError());
return FALSE;
}
if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
REPORT_ERROR("ERROR: Path is not a directory.");
return FALSE;
}
ReleaseFile();
InstallationPath = Path;
if (InstallationPath.back() != TEXT('\\') && InstallationPath.back() != TEXT('/'))
InstallationPath.push_back(TEXT('/')); // for Linux compatible
return TRUE;
}
// Solution0 does not have any requirements for RSA-2048 key
BOOL Solution0::CheckKey(RSACipher* cipher) const {
return TRUE;
}
DWORD Solution0::TryFile(const std::Tstring& Name) {
std::Tstring MainAppFullName = InstallationPath + Name;
HANDLE hFile;
PIMAGE_SECTION_HEADER pResourceSection =
Helper::ImageSectionHeader(pTargetFile->GetView<uint8_t>(), ".rsrc");
hFile = CreateFile(MainAppFullName.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
nullptr, // default SA
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
return GetLastError();
ReleaseFile();
MainAppHandle = hFile;
MainAppName = Name;
return ERROR_SUCCESS;
}
DWORD Solution0::MapFile() {
DWORD dwLastError = ERROR_SUCCESS;
HANDLE hMapping = NULL;
PVOID lpMapView = nullptr;
hMapping = CreateFileMapping(MainAppHandle,
nullptr, // default SA
PAGE_READWRITE,
0, 0, // map all
nullptr); // we don't need a name
if (hMapping == NULL) {
dwLastError = GetLastError();
goto ON_Solution0_MapFile_ERROR;
}
lpMapView = MapViewOfFile(hMapping,
FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, 0); // map all
if (lpMapView == nullptr) {
dwLastError = GetLastError();
goto ON_Solution0_MapFile_ERROR;
}
ReleaseMap();
MainAppMappingView = lpMapView;
lpMapView = nullptr;
MainAppMappingHandle = hMapping;
hMapping = NULL;
ON_Solution0_MapFile_ERROR:
if (hMapping)
CloseHandle(hMapping);
return dwLastError;
}
BOOL Solution0::FindPatchOffset() {
BOOL bFound = FALSE;
DWORD dwFileSize = 0;
uint8_t* lpFileContent = reinterpret_cast<uint8_t*>(MainAppMappingView);
dwFileSize = GetFileSize(MainAppHandle, nullptr);
if (pResourceSection == nullptr)
return false;
for (DWORD i = 0; i < dwFileSize; ++i) {
if (memcmp(lpFileContent + i, Keyword, KeywordLength) == 0) {
PatchOffset = i;
bFound = TRUE;
uint8_t* pResourceSectionData =
pTargetFile->GetView<uint8_t>() + pResourceSection->PointerToRawData;
for (DWORD i = 0; i < pResourceSection->SizeOfRawData; ++i) {
if (memcmp(pResourceSectionData + i, Keyword, KeywordLength) == 0) {
PatchOffset = pResourceSection->PointerToRawData + i;
bFound = true;
break;
}
}
@@ -127,25 +44,15 @@ namespace Patcher {
return bFound;
}
DWORD Solution0::BackupFile() {
std::Tstring TargetFileFullName = InstallationPath + MainAppName;
std::Tstring BackupFileFullName = InstallationPath + MainAppName + TEXT(".backup");
if (!CopyFile(TargetFileFullName.c_str(), BackupFileFullName.c_str(), TRUE))
return GetLastError();
else
return ERROR_SUCCESS;
}
BOOL Solution0::MakePatch(RSACipher* cipher) {
BOOL bSuccess = FALSE;
uint8_t* lpFileContent = reinterpret_cast<uint8_t*>(MainAppMappingView);
bool Solution0::MakePatch(RSACipher* cipher) const {
uint8_t* lpTargetFileView = pTargetFile->GetView<uint8_t>();
std::string RSAPublicKeyPEM;
RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
RSAPublicKeyPEM =
cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
if (RSAPublicKeyPEM.empty()) {
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
goto ON_Do_ERROR;
return false;
}
// lambda function, replace '\n' to '\r\n'
@@ -162,25 +69,26 @@ namespace Patcher {
if (RSAPublicKeyPEM.length() != KeywordLength) {
REPORT_ERROR("ERROR: Public key length does not match.");
goto ON_Do_ERROR;
return false;
}
_tprintf_s(TEXT("@%s+0x%08X\nPrevious:\n"), MainAppName.c_str(), PatchOffset);
Helper::PrintMemory(lpFileContent + PatchOffset,
lpFileContent + PatchOffset + KeywordLength,
lpFileContent);
PRINT_MESSAGE("//");
PRINT_MESSAGE("// Begin Solution0");
PRINT_MESSAGE("//");
_tprintf_s(TEXT("@+0x%08X\nPrevious:\n"), PatchOffset);
Helper::PrintMemory(lpTargetFileView + PatchOffset,
lpTargetFileView + PatchOffset + KeywordLength,
lpTargetFileView);
memcpy(lpFileContent + PatchOffset, RSAPublicKeyPEM.c_str(), KeywordLength);
memcpy(lpTargetFileView + PatchOffset, RSAPublicKeyPEM.c_str(), KeywordLength);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpFileContent + PatchOffset,
lpFileContent + PatchOffset + KeywordLength,
lpFileContent);
Helper::PrintMemory(lpTargetFileView + PatchOffset,
lpTargetFileView + PatchOffset + KeywordLength,
lpTargetFileView);
PRINT_MESSAGE("");
bSuccess = TRUE;
ON_Do_ERROR:
return bSuccess;
return true;
}
// DWORD Solution0::GetMainAppVersion(LPDWORD lpMajorVer, LPDWORD lpMinorVer) {
@@ -230,33 +138,5 @@ namespace Patcher {
// HeapFree(GetProcessHeap(), NULL, lpData);
// return bSuccess;
// }
const std::Tstring& Solution0::GetMainAppName() {
return MainAppName;
}
void Solution0::ReleaseFile() {
ReleaseMap();
if (MainAppHandle != INVALID_HANDLE_VALUE && MainAppHandle) {
CloseHandle(MainAppHandle);
MainAppHandle = INVALID_HANDLE_VALUE;
}
}
void Solution0::ReleaseMap() {
if (MainAppMappingView) {
UnmapViewOfFile(MainAppMappingView);
MainAppMappingView = nullptr;
}
if (MainAppMappingHandle) {
CloseHandle(MainAppMappingHandle);
MainAppMappingHandle = NULL;
}
}
Solution0::~Solution0() {
ReleaseFile();
}
}
+74 -215
View File
@@ -1,32 +1,5 @@
#include "def.hpp"
namespace Helper {
std::string EncryptPublicKey(const std::string& public_key);
static PIMAGE_SECTION_HEADER ImageSectionHeader(PVOID lpBase, LPCSTR lpSectionName) {
IMAGE_DOS_HEADER* pFileHeader = NULL;
IMAGE_NT_HEADERS* pNtHeader = NULL;
IMAGE_SECTION_HEADER* pSectionHeaders = NULL;
pFileHeader = (IMAGE_DOS_HEADER*)lpBase;
if (pFileHeader->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
pNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)lpBase + pFileHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
return NULL;
pSectionHeaders = (IMAGE_SECTION_HEADER*)((BYTE*)pNtHeader +
offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
pNtHeader->FileHeader.SizeOfOptionalHeader);
for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
if (_stricmp((const char*)pSectionHeaders[i].Name, lpSectionName) == 0)
return pSectionHeaders + i;
return NULL;
}
}
namespace Patcher {
const char* Solution1::Keywords[5] = {
@@ -76,41 +49,15 @@ namespace Patcher {
5
};
BOOL Solution1::SetPath(const std::Tstring& Path) {
DWORD Attr;
Attr = GetFileAttributes(Path.c_str());
if (Attr == INVALID_FILE_ATTRIBUTES) {
if (GetLastError() == ERROR_INVALID_NAME || GetLastError() == ERROR_FILE_NOT_FOUND)
REPORT_ERROR("ERROR: Invalid path. Are you sure the path you specified is correct?");
else
REPORT_ERROR_WITH_CODE("ERROR: GetFileAttributes failed.", GetLastError());
return FALSE;
}
if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
REPORT_ERROR("ERROR: Path is not a directory.");
return FALSE;
}
ReleaseFile();
InstallationPath = Path;
if (InstallationPath.back() != TEXT('\\') && InstallationPath.back() != TEXT('/'))
InstallationPath.push_back(TEXT('/')); // for Linux compatible
return TRUE;
}
BOOL Solution1::CheckKey(RSACipher* cipher) const {
bool Solution1::CheckKey(RSACipher* cipher) const noexcept {
BOOL bOk = FALSE;
std::string RSAPublicKeyPEM;
RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
RSAPublicKeyPEM =
cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
if (RSAPublicKeyPEM.empty()) {
_tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
_tprintf_s(TEXT("ERROR: cipher->ExportKeyString failed.\n"));
return FALSE;
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
return false;
}
[](std::string& str, const std::string& OldSub, const std::string& NewSub) {
@@ -127,103 +74,46 @@ namespace Patcher {
std::string encrypted_pem_text = Helper::EncryptPublicKey(RSAPublicKeyPEM);
if (encrypted_pem_text[160] > '9' || encrypted_pem_text[160] < '1')
return FALSE;
return false;
for (int i = 1; i < 8; ++i)
if (encrypted_pem_text[160 + i] > '9' || encrypted_pem_text[160 + i] < '0')
return FALSE;
return false;
if (encrypted_pem_text[910] > '9' || encrypted_pem_text[910] < '1')
return FALSE;
return false;
for (int i = 1; i < 5; ++i)
if (encrypted_pem_text[910 + i] > '9' || encrypted_pem_text[910 + i] < '0')
return FALSE;
return false;
return TRUE;
return true;
}
DWORD Solution1::TryFile(const std::Tstring& Name) {
std::Tstring MainAppFullName = InstallationPath + Name;
HANDLE hFile;
bool Solution1::FindPatchOffset() noexcept {
PIMAGE_SECTION_HEADER textSection = nullptr;
PIMAGE_SECTION_HEADER rdataSection = nullptr;
hFile = CreateFile(MainAppFullName.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
nullptr, // default SA
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
return GetLastError();
ReleaseFile();
LibccHandle = hFile;
LibccName = Name;
return ERROR_SUCCESS;
}
DWORD Solution1::MapFile() {
DWORD dwLastError = ERROR_SUCCESS;
HANDLE hMapping = NULL;
PVOID lpMapView = nullptr;
hMapping = CreateFileMapping(LibccHandle,
nullptr, // default SA
PAGE_READWRITE,
0, 0, // map all
nullptr); // we don't need a name
if (hMapping == NULL) {
dwLastError = GetLastError();
goto ON_Solution0_MapFile_ERROR;
}
lpMapView = MapViewOfFile(hMapping,
FILE_MAP_READ | FILE_MAP_WRITE,
0, 0, 0); // map all
if (lpMapView == nullptr) {
dwLastError = GetLastError();
goto ON_Solution0_MapFile_ERROR;
}
ReleaseMap();
LibccMappingView = lpMapView;
lpMapView = nullptr;
LibccMappingHandle = hMapping;
hMapping = NULL;
ON_Solution0_MapFile_ERROR:
if (hMapping)
CloseHandle(hMapping);
return dwLastError;
}
BOOL Solution1::FindPatchOffset() {
IMAGE_SECTION_HEADER* textSection = nullptr;
IMAGE_SECTION_HEADER* rdataSection = nullptr;
uint8_t* lpFileContent = reinterpret_cast<uint8_t*>(LibccMappingView);
uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
off_t Offsets[5] = { -1, -1, -1, -1, -1 };
textSection = Helper::ImageSectionHeader(lpFileContent, ".text");
textSection = Helper::ImageSectionHeader(pTargetFileView, ".text");
if (textSection == nullptr) {
// REPORT_ERROR("ERROR: Cannot find .text section.");
return FALSE;
return false;
}
rdataSection = Helper::ImageSectionHeader(lpFileContent, ".rdata");
rdataSection = Helper::ImageSectionHeader(pTargetFileView, ".rdata");
if (textSection == nullptr) {
// REPORT_ERROR("ERROR: Cannot find .rdata section.");
return FALSE;
return false;
}
// -------------------------
// try to search Keywords[0]
// -------------------------
for (DWORD i = 0; i < rdataSection->SizeOfRawData; ++i) {
if (memcmp(lpFileContent + rdataSection->PointerToRawData + i, Keywords[0], KeywordsLength[0]) == 0) {
if (memcmp(pTargetFileView + rdataSection->PointerToRawData + i, Keywords[0], KeywordsLength[0]) == 0) {
Offsets[0] = rdataSection->PointerToRawData + i;
break;
}
@@ -231,14 +121,14 @@ namespace Patcher {
if (Offsets[0] == -1) {
// REPORT_ERROR("ERROR: Cannot find Keywords[0].");
return FALSE;
return false;
}
// -------------------------
// try to search Keywords[2]
// -------------------------
for (DWORD i = 0; i < rdataSection->SizeOfRawData; ++i) {
if (memcmp(lpFileContent + rdataSection->PointerToRawData + i, Keywords[2], KeywordsLength[2]) == 0) {
if (memcmp(pTargetFileView + rdataSection->PointerToRawData + i, Keywords[2], KeywordsLength[2]) == 0) {
Offsets[2] = rdataSection->PointerToRawData + i;
break;
}
@@ -246,14 +136,14 @@ namespace Patcher {
if (Offsets[2] == -1) {
// REPORT_ERROR("ERROR: Cannot find Keywords[2].");
return FALSE;
return false;
}
// -------------------------
// try to search Keywords[4]
// -------------------------
for (DWORD i = 0; i < rdataSection->SizeOfRawData; ++i) {
if (memcmp((uint8_t*)lpFileContent + rdataSection->PointerToRawData + i, Keywords[4], KeywordsLength[4]) == 0) {
if (memcmp((uint8_t*)pTargetFileView + rdataSection->PointerToRawData + i, Keywords[4], KeywordsLength[4]) == 0) {
Offsets[4] = rdataSection->PointerToRawData + i;
break;
}
@@ -261,18 +151,18 @@ namespace Patcher {
if (Offsets[4] == -1) {
// REPORT_ERROR("ERROR: Cannot find Keywords[4].");
return FALSE;
return false;
}
// -------------------------
// try to search Keywords[1] and Keywords[3]
// -------------------------
for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
if (memcmp(lpFileContent + textSection->PointerToRawData + i, Keywords[1], KeywordsLength[1]) == 0) {
if (memcmp(pTargetFileView + textSection->PointerToRawData + i, Keywords[1], KeywordsLength[1]) == 0) {
// Keywords[3] must be close to Keywords[1]
for (DWORD j = i - 64; j < i + 64; ++j) {
if (memcmp(lpFileContent + textSection->PointerToRawData + j, Keywords[3], KeywordsLength[3]) == 0) {
if (memcmp(pTargetFileView + textSection->PointerToRawData + j, Keywords[3], KeywordsLength[3]) == 0) {
Offsets[1] = textSection->PointerToRawData + i;
Offsets[3] = textSection->PointerToRawData + j;
break;
@@ -288,7 +178,7 @@ namespace Patcher {
if (Offsets[1] == -1) {
// REPORT_ERROR("ERROR: Cannot find Keywords[1] and Keywords[3].");
return FALSE;
return false;
}
PatchOffsets[0] = Offsets[0];
@@ -302,23 +192,13 @@ namespace Patcher {
_tprintf_s(TEXT("MESSAGE: [Solution1] Keywords[3] has been found: offset = +0x%08lx.\n"), PatchOffsets[3]);
_tprintf_s(TEXT("MESSAGE: [Solution1] Keywords[4] has been found: offset = +0x%08lx.\n"), PatchOffsets[4]);
return TRUE;
return true;
}
DWORD Solution1::BackupFile() {
std::Tstring TargetFileFullName = InstallationPath + LibccName;
std::Tstring BackupFileFullName = InstallationPath + LibccName + TEXT(".backup");
if (!CopyFile(TargetFileFullName.c_str(), BackupFileFullName.c_str(), TRUE))
return GetLastError();
else
return ERROR_SUCCESS;
}
BOOL Solution1::MakePatch(RSACipher* cipher) {
bool Solution1::MakePatch(RSACipher* cipher) const {
std::string RSAPublicKeyPEM;
std::string encrypted_pem_pubkey;
uint8_t* lpFileContent = reinterpret_cast<uint8_t*>(LibccMappingView);
uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
if (RSAPublicKeyPEM.empty()) {
@@ -351,102 +231,81 @@ namespace Patcher {
uint32_t imm1 = std::stoul(encrypted_pem_pubkey1.c_str());
uint32_t imm3 = std::stoul(encrypted_pem_pubkey3.c_str());
PRINT_MESSAGE("//");
PRINT_MESSAGE("// Begin Solution1");
PRINT_MESSAGE("//");
// ----------------------------------
// process PatchOffsets[0]
// ----------------------------------
_tprintf_s(TEXT("@libcc.dll+0x%08X\nPrevious:\n"), PatchOffsets[0]);
Helper::PrintMemory(lpFileContent + PatchOffsets[0],
lpFileContent + PatchOffsets[0] + KeywordsLength[0],
lpFileContent);
memcpy(lpFileContent + PatchOffsets[0], encrypted_pem_pubkey0.c_str(), KeywordsLength[0]);
_tprintf_s(TEXT("@+0x%08X\nPrevious:\n"), PatchOffsets[0]);
Helper::PrintMemory(pTargetFileView + PatchOffsets[0],
pTargetFileView + PatchOffsets[0] + KeywordsLength[0],
pTargetFileView);
memcpy(pTargetFileView + PatchOffsets[0], encrypted_pem_pubkey0.c_str(), KeywordsLength[0]);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpFileContent + PatchOffsets[0],
lpFileContent + PatchOffsets[0] + KeywordsLength[0],
lpFileContent);
Helper::PrintMemory(pTargetFileView + PatchOffsets[0],
pTargetFileView + PatchOffsets[0] + KeywordsLength[0],
pTargetFileView);
PRINT_MESSAGE("");
// ----------------------------------
// process PatchOffsets[1]
// ----------------------------------
_tprintf_s(TEXT("@libcc.dll+0x%08X\nPrevious:\n"), PatchOffsets[1]);
Helper::PrintMemory(lpFileContent + PatchOffsets[1],
lpFileContent + PatchOffsets[1] + KeywordsLength[1],
lpFileContent);
memcpy(lpFileContent + PatchOffsets[1], &imm1, KeywordsLength[1]);
_tprintf_s(TEXT("@+0x%08X\nPrevious:\n"), PatchOffsets[1]);
Helper::PrintMemory(pTargetFileView + PatchOffsets[1],
pTargetFileView + PatchOffsets[1] + KeywordsLength[1],
pTargetFileView);
memcpy(pTargetFileView + PatchOffsets[1], &imm1, KeywordsLength[1]);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpFileContent + PatchOffsets[1],
lpFileContent + PatchOffsets[1] + KeywordsLength[1],
lpFileContent);
Helper::PrintMemory(pTargetFileView + PatchOffsets[1],
pTargetFileView + PatchOffsets[1] + KeywordsLength[1],
pTargetFileView);
PRINT_MESSAGE("");
// ----------------------------------
// process PatchOffsets[2]
// ----------------------------------
_tprintf_s(TEXT("@libcc.dll+0x%08X\nPrevious:\n"), PatchOffsets[2]);
Helper::PrintMemory(lpFileContent + PatchOffsets[2],
lpFileContent + PatchOffsets[2] + KeywordsLength[2],
lpFileContent);
memcpy(lpFileContent + PatchOffsets[2], encrypted_pem_pubkey2.c_str(), KeywordsLength[2]);
_tprintf_s(TEXT("@+0x%08X\nPrevious:\n"), PatchOffsets[2]);
Helper::PrintMemory(pTargetFileView + PatchOffsets[2],
pTargetFileView + PatchOffsets[2] + KeywordsLength[2],
pTargetFileView);
memcpy(pTargetFileView + PatchOffsets[2], encrypted_pem_pubkey2.c_str(), KeywordsLength[2]);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpFileContent + PatchOffsets[2],
lpFileContent + PatchOffsets[2] + KeywordsLength[2],
lpFileContent);
Helper::PrintMemory(pTargetFileView + PatchOffsets[2],
pTargetFileView + PatchOffsets[2] + KeywordsLength[2],
pTargetFileView);
PRINT_MESSAGE("");
// ----------------------------------
// process PatchOffsets[3]
// ----------------------------------
_tprintf_s(TEXT("@libcc.dll+0x%08X\nPrevious:\n"), PatchOffsets[3]);
Helper::PrintMemory(lpFileContent + PatchOffsets[3],
lpFileContent + PatchOffsets[3] + KeywordsLength[3],
lpFileContent);
memcpy(lpFileContent + PatchOffsets[3], &imm3, KeywordsLength[3]);
_tprintf_s(TEXT("@+0x%08X\nPrevious:\n"), PatchOffsets[3]);
Helper::PrintMemory(pTargetFileView + PatchOffsets[3],
pTargetFileView + PatchOffsets[3] + KeywordsLength[3],
pTargetFileView);
memcpy(pTargetFileView + PatchOffsets[3], &imm3, KeywordsLength[3]);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpFileContent + PatchOffsets[3],
lpFileContent + PatchOffsets[3] + KeywordsLength[3],
lpFileContent);
Helper::PrintMemory(pTargetFileView + PatchOffsets[3],
pTargetFileView + PatchOffsets[3] + KeywordsLength[3],
pTargetFileView);
PRINT_MESSAGE("");
// ----------------------------------
// process PatchOffsets[4]
// ----------------------------------
_tprintf_s(TEXT("@libcc.dll+0x%08X\nPrevious:\n"), PatchOffsets[4]);
Helper::PrintMemory(lpFileContent + PatchOffsets[4],
lpFileContent + PatchOffsets[4] + KeywordsLength[4],
lpFileContent);
memcpy(lpFileContent + PatchOffsets[4], encrypted_pem_pubkey4.c_str(), KeywordsLength[4]);
_tprintf_s(TEXT("@+0x%08X\nPrevious:\n"), PatchOffsets[4]);
Helper::PrintMemory(pTargetFileView + PatchOffsets[4],
pTargetFileView + PatchOffsets[4] + KeywordsLength[4],
pTargetFileView);
memcpy(pTargetFileView + PatchOffsets[4], encrypted_pem_pubkey4.c_str(), KeywordsLength[4]);
PRINT_MESSAGE("After:");
Helper::PrintMemory(lpFileContent + PatchOffsets[4],
lpFileContent + PatchOffsets[4] + KeywordsLength[4],
lpFileContent);
Helper::PrintMemory(pTargetFileView + PatchOffsets[4],
pTargetFileView + PatchOffsets[4] + KeywordsLength[4],
pTargetFileView);
PRINT_MESSAGE("");
return TRUE;
}
void Solution1::ReleaseFile() {
ReleaseMap();
}
if (LibccHandle != INVALID_HANDLE_VALUE && LibccHandle) {
CloseHandle(LibccHandle);
LibccHandle = INVALID_HANDLE_VALUE;
}
}
void Solution1::ReleaseMap() {
if (LibccMappingView) {
UnmapViewOfFile(LibccMappingView);
LibccMappingView = nullptr;
}
if (LibccMappingHandle) {
CloseHandle(LibccMappingHandle);
LibccMappingHandle = NULL;
}
}
Solution1::~Solution1() {
ReleaseFile();
}
}
+253
View File
@@ -0,0 +1,253 @@
#include "def.hpp"
namespace Patcher {
const char Solution2::KeywordsMeta[KeywordsCount + 1] =
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I"
"qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv"
"a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF"
"R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2"
"WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt"
"YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ"
"awIDAQAB";
uint8_t Solution2::Keywords[KeywordsCount][5];
#if defined(_M_X64)
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], sizeof(Keywords[0])) == 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;
}
#else
void Solution2::BuildKeywords() noexcept {
for (size_t i = 0; i < KeywordsCount; ++i) {
switch (i % 3) {
case 0:
Keywords[i][0] = 0x83; // Keywords[i] = asm('xor edx, KeywordsMeta[i]') +
Keywords[i][1] = 0xf2;
Keywords[i][2] = KeywordsMeta[i];
Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxx, dl')
Keywords[i][4] = 0x15;
break;
case 1:
Keywords[i][0] = 0x83; // Keywords[i] = asm('xor eax, KeywordsMeta[i]') +
Keywords[i][1] = 0xf0;
Keywords[i][2] = KeywordsMeta[i];
Keywords[i][3] = 0xa2; // asm_prefix('mov byte ptr ds:xxxxxxxx, al')
break;
default:
Keywords[i][0] = 0x83; // Keywords[i] = asm('xor ecx, KeywordsMeta[i]') +
Keywords[i][1] = 0xf1;
Keywords[i][2] = KeywordsMeta[i];
Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxx, cl')
Keywords[i][4] = 0x0D;
break;
}
}
}
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[3];
DWORD PossibleRangeStart = 0xffffffff;
DWORD PossibleRangeEnd;
for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
if (memcmp(ptextSectionData + i, Keywords[0], sizeof(Keywords[0])) == 0) {
Hints[FirstKeywordCounter++] =
*reinterpret_cast<uint32_t*>(ptextSectionData + i + sizeof(Keywords[0]));
if (i < PossibleRangeStart)
PossibleRangeStart = i;
}
}
PossibleRangeStart -= 0x1000;
PossibleRangeEnd = PossibleRangeStart + 0x100000;
// Keywords[0] should occur 3 times.
if (FirstKeywordCounter != 3)
return false;
Helper::QuickSort(Hints, 0, _countof(Hints));
// assert
// if not satisfied, refuse to patch
if (Hints[2] - Hints[0] != 0x127382BE - 0x12738210)
return false;
for (size_t i = 0; i < KeywordsCount; ++i) {
uint8_t CurrentKeyword[9];
size_t CurrentKeywordSize = i % 3 == 1 ? 4 : 5;
memcpy(CurrentKeyword, Keywords[i], CurrentKeywordSize);
*reinterpret_cast<uint32_t*>(CurrentKeyword + CurrentKeywordSize) = Hints[0] + i;
CurrentKeywordSize += sizeof(uint32_t);
for (DWORD j = PossibleRangeStart; j < PossibleRangeEnd; ++j) {
if (memcmp(ptextSectionData + j, CurrentKeyword, CurrentKeywordSize) == 0) {
Offsets[i] = textSection->PointerToRawData + j;
break;
}
}
// 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;
}
#endif
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;
}
}
+108 -60
View File
@@ -1,11 +1,51 @@
#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]");
}
static BOOL LoadKey(RSACipher* cipher, LPTSTR filename, Patcher::Solution0* pSolution0, Patcher::Solution1* pSolution1) {
std::Tstring InstallationPath;
std::Tstring MainAppName;
std::Tstring LibccName = TEXT("libcc.dll");
static BOOL SetPath(const std::Tstring& Path) {
DWORD Attr;
Attr = GetFileAttributes(Path.c_str());
if (Attr == INVALID_FILE_ATTRIBUTES) {
if (GetLastError() == ERROR_INVALID_NAME || GetLastError() == ERROR_FILE_NOT_FOUND)
REPORT_ERROR("ERROR: Invalid path. Are you sure the path you specified is correct?");
else
REPORT_ERROR_WITH_CODE("ERROR: GetFileAttributes failed.", GetLastError());
return FALSE;
}
if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
REPORT_ERROR("ERROR: Path is not a directory.");
return FALSE;
}
InstallationPath = Path;
if (InstallationPath.back() != TEXT('\\') && InstallationPath.back() != TEXT('/'))
InstallationPath.push_back(TEXT('/')); // for Linux compatible
return TRUE;
}
static DWORD BackupFile(std::Tstring& from, std::Tstring& to) {
if (::CopyFile(from.c_str(), to.c_str(), TRUE))
return ERROR_SUCCESS;
else
return GetLastError();
}
static BOOL LoadKey(RSACipher* cipher, LPTSTR filename,
Patcher::Solution* pSolution0,
Patcher::Solution* pSolution1,
Patcher::Solution* pSolution2) {
if (filename) {
std::string PrivateKeyFileName;
@@ -20,7 +60,8 @@ static BOOL LoadKey(RSACipher* cipher, LPTSTR filename, Patcher::Solution0* pSol
}
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;
}
@@ -31,7 +72,8 @@ static BOOL LoadKey(RSACipher* cipher, LPTSTR filename, Patcher::Solution0* pSol
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.");
@@ -59,8 +101,11 @@ int _tmain(int argc, TCHAR* argv[]) {
}
RSACipher* cipher = nullptr;
Patcher::Solution0* pSolution0 = nullptr;
Patcher::Solution1* pSolution1 = nullptr;
FileMapper* pMainApp = nullptr;
FileMapper* pLibcc = nullptr;
Patcher::Solution* pSolution0 = nullptr;
Patcher::Solution* pSolution1 = nullptr;
Patcher::Solution* pSolution2 = nullptr;
DWORD ErrorCode;
@@ -70,26 +115,26 @@ int _tmain(int argc, TCHAR* argv[]) {
goto ON_tmain_ERROR;
}
pMainApp = new FileMapper();
pLibcc = new FileMapper();
pSolution0 = new Patcher::Solution0();
pSolution1 = new Patcher::Solution1();
pSolution2 = new Patcher::Solution2();
if (!pSolution0->SetPath(argv[1])) {
PRINT_MESSAGE("The path you specified:");
PRINT_LPCTSTR(argv[1]);
goto ON_tmain_ERROR;
}
if (!pSolution1->SetPath(argv[1])) {
if (!SetPath(argv[1])) {
PRINT_MESSAGE("The path you specified:");
PRINT_LPCTSTR(argv[1]);
goto ON_tmain_ERROR;
}
FindMainApp:
ErrorCode = pSolution0->TryFile(TEXT("Navicat.exe"));
ErrorCode = pMainApp->MapFile(InstallationPath + TEXT("Navicat.exe"));
if (ErrorCode == ERROR_SUCCESS) {
MainAppName = TEXT("Navicat.exe");
PRINT_MESSAGE("MESSAGE: Navicat.exe has been found.");
goto FindLibcc;
}else if (ErrorCode == ERROR_ACCESS_DENIED) {
}
if (ErrorCode == ERROR_ACCESS_DENIED) {
PRINT_MESSAGE("ERROR: Cannot open Navicat.exe for ERROR_ACCESS_DENIED.");
PRINT_MESSAGE("Please re-run with Administrator privilege.");
goto ON_tmain_ERROR;
@@ -99,8 +144,9 @@ FindMainApp:
goto ON_tmain_ERROR;
}
ErrorCode = pSolution0->TryFile(TEXT("Modeler.exe"));
ErrorCode = pMainApp->MapFile(InstallationPath + TEXT("Modeler.exe"));
if (ErrorCode == ERROR_SUCCESS) {
MainAppName = TEXT("Modeler.exe");
PRINT_MESSAGE("MESSAGE: Modeler.exe has been found.");
goto FindLibcc;
}
@@ -114,8 +160,9 @@ FindMainApp:
goto ON_tmain_ERROR;
}
ErrorCode = pSolution0->TryFile(TEXT("Rviewer.exe"));
ErrorCode = pMainApp->MapFile(InstallationPath + TEXT("Rviewer.exe"));
if (ErrorCode == ERROR_SUCCESS) {
MainAppName = TEXT("Rviewer.exe");
PRINT_MESSAGE("MESSAGE: Rviewer.exe has been found.");
goto FindLibcc;
}
@@ -135,13 +182,14 @@ FindMainApp:
goto ON_tmain_ERROR;
FindLibcc:
ErrorCode = pSolution1->TryFile(TEXT("libcc.dll"));
ErrorCode = pLibcc->MapFile(InstallationPath + LibccName);
if (ErrorCode == ERROR_SUCCESS) {
PRINT_MESSAGE("MESSAGE: libcc.dll has been found.");
} else if (ErrorCode == ERROR_FILE_NOT_FOUND) {
PRINT_MESSAGE("MESSAGE: libcc.dll is not found. Solution1 will be omitted.");
delete pSolution1;
pSolution1 = nullptr;
PRINT_MESSAGE("MESSAGE: libcc.dll is not found. Solution1 and Solution2 will be omitted.");
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.");
@@ -152,71 +200,62 @@ FindLibcc:
}
SearchPublicKey:
pSolution0->SetFile(pMainApp);
if (pSolution1) pSolution1->SetFile(pLibcc);
if (pSolution2) pSolution2->SetFile(pLibcc);
PRINT_MESSAGE("");
ErrorCode = pSolution0->MapFile();
if (ErrorCode != ERROR_SUCCESS) {
_tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
_tprintf_s(TEXT("ERROR: Cannot map %s. CODE: 0x%08X\n"),
pSolution0->GetMainAppName().c_str(),
ErrorCode);
goto ON_tmain_ERROR;
}
if (!pSolution0->FindPatchOffset()) {
_tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
_tprintf_s(TEXT("ERROR: Cannot find RSA public key in %s.\n"),
pSolution0->GetMainAppName().c_str());
_tprintf_s(TEXT("ERROR: Cannot find RSA public key in %s.\n"), MainAppName.c_str());
goto ON_tmain_ERROR;
}
if (pSolution1) {
ErrorCode = pSolution1->MapFile();
if (ErrorCode != ERROR_SUCCESS) {
REPORT_ERROR_WITH_CODE("ERROR: Cannot map libcc.dll.", ErrorCode);
goto ON_tmain_ERROR;
}
if (!pSolution1->FindPatchOffset()) {
PRINT_MESSAGE("MESSAGE: Cannot find RSA public key in libcc.dll. Solution1 will be omitted.");
delete pSolution1;
pSolution1 = nullptr;
}
if (pSolution1 && !pSolution1->FindPatchOffset()) {
PRINT_MESSAGE("MESSAGE: Cannot find RSA public key in libcc.dll. Solution1 will be omitted.");
pSolution1->SetFile(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:
PRINT_MESSAGE("");
ErrorCode = pSolution0->BackupFile();
ErrorCode = BackupFile(InstallationPath + MainAppName, InstallationPath + MainAppName + TEXT(".backup"));
if (ErrorCode == ERROR_SUCCESS) {
_tprintf_s(TEXT("MESSAGE: %s has been backed up successfully.\n"),
pSolution0->GetMainAppName().c_str());
_tprintf_s(TEXT("MESSAGE: %s has been backed up successfully.\n"), MainAppName.c_str());
} else if (ErrorCode == ERROR_ACCESS_DENIED) {
_tprintf_s(TEXT("ERROR: Cannot back up %s for ERROR_ACCESS_DENIED.\n"),
pSolution0->GetMainAppName().c_str());
_tprintf_s(TEXT("ERROR: Cannot back up %s for ERROR_ACCESS_DENIED.\n"), MainAppName.c_str());
_tprintf_s(TEXT("Please re-run with Administrator privilege.\n"));
goto ON_tmain_ERROR;
} else if (ErrorCode == ERROR_FILE_EXISTS) {
_tprintf_s(TEXT("ERROR: The backup of %s has been found.\n"),
pSolution0->GetMainAppName().c_str());
_tprintf_s(TEXT("ERROR: The backup of %s has been found.\n"), MainAppName.c_str());
_tprintf_s(TEXT("Please remove %s.backup in Navicat installation path if you're sure %s has not been patched.\n"),
pSolution0->GetMainAppName().c_str(),
pSolution0->GetMainAppName().c_str());
MainAppName.c_str(),
MainAppName.c_str());
_tprintf_s(TEXT("Otherwise please restore %s by %s.backup and remove %s.backup then try again.\n"),
pSolution0->GetMainAppName().c_str(),
pSolution0->GetMainAppName().c_str(),
pSolution0->GetMainAppName().c_str());
MainAppName.c_str(),
MainAppName.c_str(),
MainAppName.c_str());
goto ON_tmain_ERROR;
} else {
_tprintf_s(TEXT("ERROR: Cannot back up %s. CODE: 0x%08X\n"),
pSolution0->GetMainAppName().c_str(),
MainAppName.c_str(),
ErrorCode);
goto ON_tmain_ERROR;
}
if (pSolution1) {
ErrorCode = pSolution1->BackupFile();
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.");
} else if (ErrorCode == ERROR_ACCESS_DENIED) {
@@ -242,13 +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 cipher;
SAFE_DELETE(pSolution2);
SAFE_DELETE(pSolution1);
SAFE_DELETE(pSolution0);
SAFE_DELETE(pLibcc);
SAFE_DELETE(pMainApp);
SAFE_DELETE(cipher);
return 0;
}
}
+210 -103
View File
@@ -20,7 +20,57 @@ namespace Helper {
void ErrorReport(LPCTSTR at, UINT line, LPCTSTR msg);
void ErrorReport(LPCTSTR at, UINT line, LPCTSTR msg, DWORD err_code);
void PrintMemory(const void* a, const void* b, const void* base);
//
// 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);
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))
@@ -31,140 +81,197 @@ namespace Helper {
#define PRINT_LPCSTR(msg) printf_s("%s\n", (msg))
#define PRINT_LPCWSTR(msg) wprintf_s(L"%s\n", (msg))
template<HANDLE __Invalid>
class HandleGuard {
private:
bool bError;
HANDLE& Handle;
public:
HandleGuard(HANDLE& Target) noexcept : bError(true), Handle(Target) {}
void ErrorOccurs() noexcept { bError = true; }
void NoErrorOccurs() noexcept { bError = false; }
~HandleGuard() noexcept {
if (bError && Handle != __Invalid) {
CloseHandle(Handle);
Handle = __Invalid;
}
}
};
class FileMapper {
private:
HANDLE hFile;
HANDLE hMap;
PVOID pMapView;
public:
FileMapper() noexcept :
hFile(INVALID_HANDLE_VALUE),
hMap(NULL),
pMapView(nullptr) {}
void Release() noexcept {
if (pMapView) {
UnmapViewOfFile(pMapView);
pMapView = NULL;
}
if (hMap) {
CloseHandle(hMap);
hMap = NULL;
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
}
}
template<typename _Type>
_Type* GetView() const noexcept {
return reinterpret_cast<_Type*>(pMapView);
}
DWORD MapFile(std::Tstring& Name) noexcept {
HandleGuard<INVALID_HANDLE_VALUE> hFileGuard(hFile);
HandleGuard<NULL> hMapGuard(hMap);
hFile = CreateFile(Name.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, // share read so that we can copy
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
return GetLastError();
hMap = CreateFileMapping(hFile,
NULL,
PAGE_READWRITE,
0,
0,
NULL);
if (hMap == NULL)
return GetLastError();
pMapView = MapViewOfFile(hMap,
FILE_MAP_READ | FILE_MAP_WRITE,
0,
0,
0);
if (pMapView == NULL)
return GetLastError();
hFileGuard.NoErrorOccurs();
hMapGuard.NoErrorOccurs();
return ERROR_SUCCESS;
}
~FileMapper() {
Release();
}
};
namespace Patcher {
class Solution {
public:
virtual void SetFile(FileMapper* pFile) = 0;
virtual bool CheckKey(RSACipher* cipher) const = 0;
virtual bool FindPatchOffset() = 0;
virtual bool MakePatch(RSACipher* cipher) const = 0;
virtual ~Solution() {}
};
// Solution0 will replace the RSA public key stored in main application.
class Solution0 {
class Solution0 : public Solution {
private:
static const char Keyword[461];
static constexpr int KeywordLength = 460;
std::Tstring InstallationPath;
std::Tstring MainAppName;
HANDLE MainAppHandle;
HANDLE MainAppMappingHandle;
PVOID MainAppMappingView;
FileMapper* pTargetFile;
off_t PatchOffset;
public:
Solution0() : InstallationPath(),
MainAppName(),
MainAppHandle(INVALID_HANDLE_VALUE),
MainAppMappingHandle(NULL),
MainAppMappingView(nullptr),
PatchOffset(-1) {}
Solution0() noexcept :
pTargetFile(nullptr),
PatchOffset(-1) {}
BOOL SetPath(const std::Tstring& Path);
virtual void SetFile(FileMapper* pMainApp) noexcept override {
pTargetFile = pMainApp;
}
// Solution0 does not have any requirements for RSA-2048 key
BOOL CheckKey(RSACipher* cipher) const;
// Solution0 does not have any requirements for an RSA-2048 key
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
return true;
}
// Return error code
// It may return
// ERROR_SUCCESS (target has been set successfully)
// ERROR_FILE_NOT_FOUND (try another name)
// ERROR_ACCESS_DENIED (you need Administrator privilege)
// ...
DWORD TryFile(const std::Tstring& MainAppName);
// Return true if found, other return false
virtual bool FindPatchOffset() noexcept override;
// Return error code
// It may return
// ERROR_SUCCESS (target has been mapped successfully)
// ...
DWORD MapFile();
// Return TRUE if found, other return FALSE
BOOL FindPatchOffset();
// Return error code
// It may return
// ERROR_SUCCESS (file has been backed up successfully)
// ERROR_FILE_EXISTS (you should remove backup file first)
// ERROR_ACCESS_DENIED (you need Administrator privilege)
// ...
DWORD BackupFile();
// Make a patch based on RSA private key
// Return TRUE if success, otherwise return FALSE
BOOL MakePatch(RSACipher* cipher);
// Return error code
// Return ERROR_SUCCESS if success
// DWORD GetMainAppVersion(LPDWORD lpMajorVer, LPDWORD lpMinorVer);
const std::Tstring& GetMainAppName();
// Close handle returned by CreateFile with a implicit call towards ReleaseMap
void ReleaseFile();
// Unmap view returned by MapViewOfFile and
// close handle returned by CreateFileMapping
void ReleaseMap();
~Solution0();
// Make a patch based on an RSA private key given
// Return true if success, otherwise return false
virtual bool MakePatch(RSACipher* cipher) const override;
};
// Solution0 will replace the RSA public key stored in libcc.dll
class Solution1 {
class Solution1 : public Solution {
private:
static const char* Keywords[5];
static const int KeywordsLength[5];
std::Tstring InstallationPath;
std::Tstring LibccName;
HANDLE LibccHandle;
HANDLE LibccMappingHandle;
PVOID LibccMappingView;
FileMapper* pTargetFile;
off_t PatchOffsets[5];
public:
Solution1() : InstallationPath(),
LibccName(),
LibccHandle(INVALID_HANDLE_VALUE),
LibccMappingHandle(NULL),
LibccMappingView(nullptr),
PatchOffsets{-1, -1, -1, -1, -1} {}
Solution1() :
pTargetFile(nullptr),
PatchOffsets{ -1, -1, -1, -1, -1 } {}
BOOL SetPath(const std::Tstring& Path);
virtual void SetFile(FileMapper* pLibccFile) noexcept override {
pTargetFile = pLibccFile;
}
// Solution0 does not have any requirements for RSA-2048 key
BOOL CheckKey(RSACipher* cipher) const;
// Solution1 has some requirements for an RSA-2048 key
virtual bool CheckKey(RSACipher* cipher) const noexcept override;
// Return error code
// It may return
// ERROR_SUCCESS (target has been set successfully)
// ERROR_FILE_NOT_FOUND (try another name)
// ERROR_ACCESS_DENIED (you need Administrator privilege)
// ...
DWORD TryFile(const std::Tstring& MainAppName);
// Return true if found, otherwise return false
virtual bool FindPatchOffset() noexcept override;
// Return error code
// It may return
// ERROR_SUCCESS (target has been mapped successfully)
// ...
DWORD MapFile();
// Make a patch based on an RSA private key given
// Return true if success, otherwise return false
virtual bool MakePatch(RSACipher* cipher) const override;
};
// Return TRUE if found, other return FALSE
BOOL FindPatchOffset();
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];
// Return error code
// It may return
// ERROR_SUCCESS (file has been backed up successfully)
// ERROR_FILE_EXISTS (you should remove backup file first)
// ERROR_ACCESS_DENIED (you need Administrator privilege)
// ...
DWORD BackupFile();
void BuildKeywords() noexcept;
public:
Solution2() :
pTargetFile(nullptr) {
memset(PatchOffsets, -1, sizeof(PatchOffsets));
}
// Make a patch based on RSA private key
// Return TRUE if success, otherwise return FALSE
BOOL MakePatch(RSACipher* cipher);
virtual void SetFile(FileMapper* pLibccFile) noexcept override {
pTargetFile = pLibccFile;
}
// Close handle returned by CreateFile with a implicit call towards ReleaseMap
void ReleaseFile();
// Solution2 has no requirements for an RSA-2048 key
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
return true;
}
// Unmap view returned by MapViewOfFile and
// close handle returned by CreateFileMapping
void ReleaseMap();
// Return true if found, otherwise return false
virtual bool FindPatchOffset() noexcept override;
~Solution1();
// Make a patch based on an RSA private key given
// Return true if success, otherwise return false
virtual bool MakePatch(RSACipher* cipher) const override;
};
}
+1
View File
@@ -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">