破解这个程序始于2015年8月27日,那时纯粹是为了“练手”,四六级每年两次,所以客户端也会发布两次,每次发布都会增加软件限制,所以每次都要花更多的时间折腾这玩意儿。
序
首先自己得熟悉整个软件的使用流程及原理,这样才会有破解思路。这个软件首先是取出电脑的Mac地址然后拼接到Post参数(这个在OllyDebug(反汇编工具)或是在WireShark(抓包)软件里面都可以看到)里面,再发送给服务器,服务器判断你是否查询成功,成功则记录你的Mac,下次查询的时候它就会和记录的Mac地址进行比较,从而达到限制。
第一个版本(2015年8月27日)
我的破解流程当然首先是查壳,拖入PEID结果发现木有加壳而且是用MFC写的;接下来则是用OllyDebug进行反汇编调试了,找到Mac地址相关的汇编指令,修改即可。我在吾爱破解论坛发布了破解方法:链接http://www.52pojie.cn/thread-406156-1-1.html
第二个版本(2016年3月1日 )
破解方法与第一个版本一致,但是官方会提示版本更新,所以需要破解掉更新限制,用OllyDebug打开客户端Nop掉0x40370f-0x403714的地址。链接:http://www.52pojie.cn/thread-470798-1-1.html
第三个版本(2016年8月)
这个版本限制增多了,除了限制Mac地址还增加了30秒限制,以及IP限制,30秒限制还好,修改一个跳转就好了,而IP限制则需要花费比较大的代价去破解。我的初次尝试是这样的,自己构造Post参数,设置代理,然后发送到它们的服务器,可无奈Post参数被加密了,逼不得已只能反汇编找到加密函数,然后把加密函数的汇编代码抠出来放在C语言里面编译,抠了很久发现调用关系比较复杂就放弃了这种做法。想了很久,又采用了DLL注入的方法,原理是客户端发送加密之后的Post参数给服务器的时候必定使用了send函数,我想Hook掉这个函数让它跳到我的DLL里面执行我的函数,那么我就可以设置代理访问了,基础思路就是这些,以下是代码,编译成DLL后用远程注入器注入到客户端即可。
#include <Windows.h> #include <winsock.h> #include <stdio.h> #include <time.h> #pragma comment(lib,"ws2_32.lib") const char chs[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; BYTE jmpMySend[5] = {0xE9}; BYTE oldSend[5] = {0}; DWORD msend; DWORD pSend; BYTE jmpMyMac[5] = {0xE9}; DWORD changeMacAddr; DWORD jmpAddrOfMac = 0x0040CDB6; DWORD getMacAddr; int curLine = 0; bool DEBUG = true; char *logFile = "log.txt"; void log(char *fileName, char *msg) { FILE *fp = fopen(fileName, "a+"); char tmp[4096] = {0}; sprintf(tmp, "\n%s", msg); fputs(tmp, fp); fclose(fp); } int gbkToUtf8(unsigned char * lpGBKStr,unsigned char * lpUTF8Str,int nUTF8StrLen) { wchar_t * lpUnicodeStr = NULL; int nRetLen = 0; if(!lpGBKStr) return 0; nRetLen = MultiByteToWideChar(CP_ACP,0,(char *)lpGBKStr,-1,NULL,NULL); lpUnicodeStr = new WCHAR[nRetLen + 1]; nRetLen = MultiByteToWideChar(CP_ACP,0,(char *)lpGBKStr,-1,lpUnicodeStr,nRetLen); if(!nRetLen) return 0; nRetLen = WideCharToMultiByte(CP_UTF8,0,lpUnicodeStr,-1,NULL,0,NULL,NULL); if(!lpUTF8Str) { if(lpUnicodeStr) delete []lpUnicodeStr; return nRetLen; } if(nUTF8StrLen < nRetLen) { if(lpUnicodeStr) delete []lpUnicodeStr; return 0; } nRetLen = WideCharToMultiByte(CP_UTF8,0,lpUnicodeStr,-1,(char *)lpUTF8Str,nUTF8StrLen,NULL,NULL); if(lpUnicodeStr) delete []lpUnicodeStr; return nRetLen; } char* rtrim(char *target) { int end = strlen(target) - 1; if (target[end] == '\n') { target[end] = '\0'; } return target; } void strrpl(char* strSrc, char* strFind, char* strReplace) { while (*strSrc != '\0') { if (*strSrc == *strFind) { if (strncmp(strSrc, strFind, strlen(strFind)) == 0) { int i = strlen(strFind); int j = strlen(strReplace); char* q = strSrc+i; char* p = q; char* repl = strReplace; int lastLen = 0; while (*q++ != '\0') lastLen++; char* temp = new char[lastLen+1]; for (int k = 0; k < lastLen; k++) { *(temp+k) = *(p+k); } *(temp+lastLen) = '\0'; while (*repl != '\0') { *strSrc++ = *repl++; } p = strSrc; char* pTemp = temp; while (*pTemp != '\0') { *p++ = *pTemp++; } delete temp; *p = '\0'; } else strSrc++; } else strSrc++; } } //base64编码 void toBase64(unsigned char *out, const unsigned char *in, int inlen) { const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; for (; inlen >= 3; inlen -= 3) { *out++ = base64digits[in[0] >> 2]; *out++ = base64digits[((in[0] << 4) & 0x30) | (in[1] >> 4)]; *out++ = base64digits[((in[1] << 2) & 0x3c) | (in[2] >> 6)]; *out++ = base64digits[in[2] & 0x3f]; in += 3; } if (inlen > 0) { unsigned char fragment; *out++ = base64digits[in[0] >> 2]; fragment = (in[0] << 4) & 0x30; if (inlen > 1) fragment |= in[1] >> 4; *out++ = base64digits[fragment]; *out++ = (inlen < 2) ? '=' : base64digits[(in[1] << 2) & 0x3c]; *out++ = '='; } *out = '\0'; } char getCh() { return chs[rand()%16]; } void __stdcall getMac(char *target) { srand((unsigned int)time(0)); for (int i=0; i<17; i++) { switch (i) { case 2: case 5: case 8: case 11: case 14: target[i] = '-'; break; default: target[i] = getCh(); } } if (DEBUG) { // log(logFile, target); //输出mac到日志 } } void __declspec(naked) changeMac() { __asm { push esi; call getMacAddr; retn; } } void hookMac() { WriteProcessMemory((LPVOID)-1, (LPVOID)jmpAddrOfMac, (LPVOID)jmpMyMac, 5, 0); } void initMac() { changeMacAddr = (DWORD)changeMac; getMacAddr = (DWORD)getMac; DWORD jmpAddr = changeMacAddr - jmpAddrOfMac - 5; //jmp near 自定义函数-jmp所在地址-jmp的指令长度 memcpy(jmpMyMac+1, &jmpAddr, 4); } void showMessageNumber(int number) { char buffer[1024]; ZeroMemory(buffer, 1024); sprintf(buffer, "%d", number); MessageBox(0, buffer, "", 0); } void showMessageText(char *text) { MessageBox(0, text, "", 0); } void hook() { WriteProcessMemory((LPVOID)-1, (LPVOID)pSend, jmpMySend, 5, 0); } void unhook() { WriteProcessMemory((LPVOID)-1, (LPVOID)pSend, oldSend, 5, 0); } bool advanceConnect(SOCKET s, char *ip, int port , int time) { sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = inet_addr(ip); serverAddr.sin_port = htons(port); //非阻塞模式:1 阻塞:0(默认) u_long mode = 1; ioctlsocket(s, FIONBIO, &mode); //得到connect结果 bool conResult = false; if (connect(s, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) { timeval tm; tm.tv_sec = time; tm.tv_usec = 0; fd_set set; FD_ZERO(&set); FD_SET(s, &set); if (select(s+1, 0, &set, 0, &tm) > 0) { char error = -1; int len = 4; getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len); if (!error) conResult = true; else conResult = false; } else conResult = false; } else conResult = true; mode = 0; ioctlsocket(s, FIONBIO, &mode); return conResult; } int getLine(char *fileName, int line, char *target) { int curLine = 0, result = 0; FILE *fp = fopen(fileName, "r"); while(!feof(fp)) { char buffer[100] = {0}; fgets(buffer, 100, fp); if (curLine == line) { memcpy(target, buffer, 100); result = 1; break; } curLine++; } fclose(fp); return result; } int substr(char *src, int pos, char *target) { for (int i=0, j=0; i<strlen(src); i++) { if (i>pos) { memcpy(target+j, src+i, 1); j++; } } return 1; } int getIp(char *src, char *target) { char ip[17] = {0}; memcpy(ip, src, 16); char *tmp = strtok(ip, ":"); tmp[strlen(tmp)] = '\0'; memcpy(target, tmp, strlen(tmp)); return 1; } int getPort(char *src, char *target) { char ip[17] = {0}; getIp(src, ip); substr(src, strlen(ip), target); return 1; } int __stdcall mysend(SOCKET s, char *data, int len, int flag) { //恢复现场 unhook(); closesocket(s); //设置代理访问 s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { if (DEBUG) { log(logFile, "初始化socket失败..."); } hook(); return 0; } if (DEBUG) { log(logFile, "初始化socket成功..."); } //获取代理服务器ip和端口 char ipPort[100] = {0}; if (!getLine("ip.txt", curLine, ipPort)) { curLine = 0; getLine("ip.txt", curLine, ipPort); } if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "读取IP文件成功, 地址为:%s", rtrim(ipPort)); log(logFile, msg); } char ip[17] = {0}; getIp(ipPort, ip); char port[6] = {0}; getPort(ipPort, port); curLine++; if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "拆分IP端口, IP为:%s 端口:%s", ip, port); log(logFile, msg); } sockaddr_in proxySever; proxySever.sin_family = AF_INET; proxySever.sin_addr.s_addr = inet_addr(ip); proxySever.sin_port = htons(atoi(port)); //连接代理服务器 int con = connect(s, (sockaddr*)&proxySever, sizeof(proxySever)); if (con < 0) { if (DEBUG) { log(logFile, "连接代理服务器失败..."); log(logFile, "-------------------------------------------------------------"); } hook(); return 0; } if (DEBUG) { log(logFile, "连接代理服务器成功..."); } //代理服务器验证用户 const char *user = "domain\\xxx"; const char *psw = "xxx"; char postData[10240+1] = {0}; char base64Data[10240+1] = {0}; sprintf(postData, "%s:%s", user, psw); toBase64((unsigned char*)base64Data, (unsigned char*)postData, strlen(postData)); sprintf(postData, "CONNECT %s HTTP/1.1\r\nAccept: */*\r\nContent-Type: text/html\r\nHost: %s\r\nProxy-Connection: close\r\nProxy-Authorization: Basic %s\r\nContent-Length: 0\r\n\r\n", "http://find.cet.99sushe.com/search", "find.cet.99sushe.com", base64Data); //http://find.cet.99sushe.com/search if (DEBUG) { log(logFile, postData); } int sret = send(s, postData, strlen(postData), 0); if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "send返回值:%d", sret); log(logFile, msg); } int sendError = WSAGetLastError(); if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "send代理服务器返回值:%d", sendError); log(logFile, msg); } memset(postData, 0, sizeof(postData)); int rret = recv(s, postData, sizeof(postData), 0); if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "recv返回值:%d", rret); log(logFile, msg); } int recvError = WSAGetLastError(); if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "recv代理服务器返回值:%d", recvError); log(logFile, msg); } if (DEBUG) { char msg[1024] = {0}; sprintf(msg, "代理服务器返回值:%s", postData); log(logFile, msg); } //访问真正网址 strrpl(data, "/search", "http://find.cet.99sushe.com/search"); strrpl(data, "keep-alive\r\n", "close\r\nProxy-Connection: close\r\n"); if (DEBUG) { log(logFile, data); } int result = send(s, data, strlen(data), 0); if (DEBUG) { log(logFile, "POST结束..."); log(logFile, "-------------------------------------------------------------"); } hook(); return result; } void init() { msend = DWORD(mysend), pSend = (DWORD)GetProcAddress(GetModuleHandle("ws2_32.dll"), "send"); //先保存现场 ReadProcessMemory((LPVOID)-1, (LPVOID)pSend, oldSend, 5, 0); msend = msend - pSend - 5; //jmp near 自定义函数-jmp所在地址-jmp的指令长度 memcpy(jmpMySend+1, &msend, 4); } BOOL APIENTRY DllMain(HANDLE hModule, DWORD reasonForCall, LPVOID lpReserved) { switch (reasonForCall) { case DLL_PROCESS_ATTACH: init(); hook(); initMac(); hookMac(); break; case DLL_PROCESS_DETACH: break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; } return true; }