mirror of
https://github.com/DoubleLabyrinth/navicat-keygen.git
synced 2019-12-02 05:10:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3548220ab | ||
|
|
b588e7f608 | ||
|
|
5ffe7a198e | ||
|
|
e5300eee16 | ||
|
|
04fd1ba315 | ||
|
|
d4202a907c | ||
|
|
23987cb031 |
@@ -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
@@ -74,7 +74,7 @@
|
||||
|
||||
如果指定了`-bin`,`navicat-keygen.exe`最终将生成`license_file`文件。这个选项是给Navicat旧激活方式使用的。
|
||||
|
||||
如果指定了`-bin`,`navicat-keygen.exe`最终将生成Base64样式的激活码。这个选项是给Navicat新激活方式使用的。
|
||||
如果指定了`-text`,`navicat-keygen.exe`最终将生成Base64样式的激活码。这个选项是给Navicat新激活方式使用的。
|
||||
|
||||
__这个参数必须指定。__
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user