關於KeyFile的破解,含註冊機原始碼
阿新 • • 發佈:2018-11-17
程式來自於《加密與解密3》的第五章的PacMe.exe。書中並沒有給出C語言實現的加密與解密程式碼,自己花了一些時間,把程式碼還原了,並且寫了一個C語言的註冊機。
加密原理:正如書中所說,此程式是生成一個11x16的迷宮,其中*表示不通,.表示通,起點為C,終點為X。所經過的路徑的方向,每4個作為1位元組,成為加密的資料。
解密思路:通過IDA匯出程式的迷宮資料,通過尋路演算法,找到唯一路徑,然後按照KeyFile格式加密並生成KeyFile。
迷宮資料:
char Maze[] = { "****************" "C*......*...****" ".*.****...*....*" ".*..**********.*" "..*....*...*...*" "*.****.*.*...***" "*.*....*.*******" "..*.***..*.....*" ".*..***.**.***.*" "...****....*X..*" "****************" };//11*16
先看下從程式裡反彙編出的解密函式:
bool KeyCode(const char *szFilename, char *pMaze) {//KwazyWeb.bit HANDLE hFile; if ((hFile = CreateFileA(szFilename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { CloseHandle(hFile); return false; } DWORD dwByteRead; BYTE bUsersize; ReadFile(hFile, &bUsersize, 1, &dwByteRead, NULL); if (bUsersize == 0) { CloseHandle(hFile); return false; } //0x403288 DWORD dwByteUser = 0; char szUserInfo[100]; ReadFile(hFile, szUserInfo, bUsersize, &dwByteRead, NULL); //使用者名稱求和 for (int i = 0; i < (int)bUsersize; i++) dwByteUser += szUserInfo[i]; //0x4034e8 BYTE szDataInfo[18]; ReadFile(hFile, szDataInfo, 18, &dwByteRead, NULL); //異或資料 for (int i = 0; i < 18; i++) szDataInfo[i] = szDataInfo[i] ^ (BYTE)dwByteUser; int k; char *pos = &pMaze[16]; for (int j = 0; j < 18; j++) for (int i = 8; i != 0;) { i -= 2; k = szDataInfo[j] >> i & 3; if (k == 0) pos = pos - 16;//↑ else if (k == 1)//→ pos++; else if (k == 2)//↓ pos = pos + 16; else//← pos--; } //判斷是否到達位置X if (pos[0] != 'X'){ CloseHandle(hFile); return false; } CloseHandle(hFile); return true; }
註冊機實現(第一次寫註冊機,還專門學習了下尋路演算法):
void KeyGen(const char *szFilename, char *pMaze, const char *szName) { HANDLE hFile; if ((hFile = CreateFileA(szFilename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { CloseHandle(hFile); return; } std::list<int> path; std::vector<int> pack; int len = lstrlenA(pMaze), pos; char *pMaze1 = new char[len + 1]; memcpy(pMaze1, pMaze, len + 1);//包含'\0' for (pos = 0; pos < len; pos++) if (pMaze1[pos] == 'C') break; path.push_back(pos); Walk(pMaze1, pos / 16, pos - pos / 16 * 16, path); //計算序列值 int i, j; pos = path.front(); path.pop_front(); while (!path.empty()) { i = path.front() / 16 - pos / 16; j = path.front() - path.front() / 16 * 16 - (pos - pos / 16 * 16); if (j == 0) { if (i > 0) pack.push_back(2); else if (i < 0) pack.push_back(0); } else if (i == 0) { if (j > 0) pack.push_back(1); else if (j < 0) pack.push_back(3); } pos = path.front(); path.pop_front(); } pos = lstrlenA(szName); len = 0; for (i = 0; i < pos; i++) len += szName[i]; for (i = 0; i < (int)pack.size(); i += 4) path.push_back((pack[i] << 6 | pack[i + 1] << 4 | pack[i + 2] << 2 | pack[i + 3]) ^ (BYTE)len); WriteFile(hFile, &pos, 1, (DWORD *)&j, NULL); WriteFile(hFile, szName, pos, (DWORD *)&j, NULL); for (auto itr = path.begin(); itr != path.end(); itr++) WriteFile(hFile, &(*itr), 1, (DWORD *)&j, NULL); delete[] pMaze1; CloseHandle(hFile); }
其中尋路演算法函式Walk實現如下:
bool Walk(char *pMaze, int i, int j, std::list<int> &path) { if (pMaze[i * 16 + j] == 'X') return true; int distX[] = { -1,0,1,0 }, distY[] = { 0,1,0,-1 }; for (int k = 0; k < 4; k++) { if (i + distX[k] >= 0 && i + distX[k] < 11 && j + distY[k] >= 0 && j + distY[k] < 16 && (pMaze[(i + distX[k]) * 16 + j + distY[k]] == '.' || pMaze[(i + distX[k]) * 16 + j + distY[k]]=='X')) {//下一步通路 pMaze[i * 16 + j] = '*';//當前封路 path.push_back((i + distX[k]) * 16 + j + distY[k]);//下一步入棧 if (!Walk(pMaze, i + distX[k], j + distY[k], path)) { path.pop_back(); pMaze[i * 16 + j] = '.';//開路 continue; } return true; } } return false; }
Main函式測試如下:
int main(){ KeyGen("key.bit", Maze, "Thanks, pediy!");//註冊機 if (KeyCode("key.bit", Maze))//解碼 printf("Valid Key file\n"); else printf("Contact me to get Key file\n"); system("pause"); }
程式碼測試圖