程序来自于《加密与解密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"); }
代码测试图