破解这个程序始于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;
}