eMule是一个GPL程序,所以VeryCD的改版必须公开源码。今天听说VeryCD版有封锁的现象,所以贝壳抓源码来看看。如果大家认为老调重弹的话,不妨把文章拉到最后。

源码从此处下载:http://www.emule.org.cn/download/

最下方链接:http://download.verycd.com/eMule-VeryCD-src.rar

贝壳下到的文件大小13,703,064字节,打包时间2008-09-11。经过贝壳查找,在eMule-VeryCD-srcsrc WordFilter发现两个文件,WordFilter.cpp 2008-03-12 09:57 13374和WordFilter.h 2007-11-20 17:56 1009。仔细阅读里面,发现有以下内容。

void    CWordFilter::Init()
{
	HANDLE    hFile;
	DWORD    dwRead;
	int        nLen;
	BOOL    bResult;
	CStringList list;

	//m_count = 0;

	CString saaa = thePrefs.GetMuleDirectory(EMULE_EXECUTEABLEDIR) + FLITER_FILE;
	CString sbbb = thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE;

	// 如果文件目录不对,程序移动一下,到config目录下 added by kernel1983 2006.07.31
	if (PathFileExists(thePrefs.GetMuleDirectory(EMULE_EXECUTEABLEDIR) + FLITER_FILE))
		MoveFile(thePrefs.GetMuleDirectory(EMULE_EXECUTEABLEDIR) + FLITER_FILE, thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE);

	if (!PathFileExists(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE))
	{
		// 不存在,所有的都过滤 added by kernel1983 2006.08.08
		m_filterall = true;
		return;
	}

	// Open file for read
	hFile = CreateFile(thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	//AddLogLine(false,_T(":%sn"),thePrefs.GetConfigDir() + FLITER_FILE);
	if(hFile == NULL || hFile == INVALID_HANDLE_VALUE)
	{
		// 读取错误,所有的都过滤 added by kernel1983 2006.08.08
		m_filterall = true;
		return;
	}

	DWORD dwSize = GetFileSize(hFile, NULL);

	TCHAR * pszData = new TCHAR[(dwSize / sizeof(TCHAR)) + 1];            // 申请空间
	bResult = ReadFile(hFile, pszData, dwSize, &dwRead, NULL);        // 读入文件1
	CloseHandle(hFile);
	pszData[(dwSize / sizeof(TCHAR))] = 0;

	if(bResult)
	{
		// 加入解码算法
		{
			std::string tempstr( (char*)pszData + 1 , ((int)dwSize - 1) > 0 ? dwSize -1 : 0 );

			// 查看是否是老格式
			char * pszData_a = (char*) pszData;

			if( pszData_a[0] != 0x15 ) {
				// 老格式,进行转换
				CUnicodeToMultiByte wc2mb( thePrefs.GetMuleDirectory(EMULE_CONFIGDIR) + FLITER_FILE );
				tempstr.assign( (char*)pszData , dwSize );
				InternalBase64::encode2file( tempstr , std::string((LPCSTR)wc2mb , wc2mb.GetLength()) );

				delete [] pszData;
				// 重新载入
				return Init();
			}

			vector vec = InternalBase64::decode( tempstr );
			char * pszt = (char*) pszData;
			for( size_t i = 0; i < vec.size() ; i++ ) {
				pszt[i] = vec[i];
			}
			dwSize = vec.size();
		}

		TCHAR * pszTemp = wcstok(pszData + 1, _T("rn"));
		while(pszTemp != NULL)
		{
			nLen = wcslen(pszTemp);
			while(pszTemp[nLen - 1] == 't' || pszTemp[nLen - 1] == ' ')
			{
				nLen --;
				pszTemp[nLen] = 0;
			}
			while(*pszTemp == 't' || *pszTemp == ' ')
			{
				pszTemp ++;
				nLen --;
			}
			//AddLogLine(false,_T("pszTemp:%s"),pszTemp);
			//AddLogLine(false,_T("nLen:%d"),nLen);
			if(nLen > 0)list.AddTail(pszTemp);
			//if(nLen == 8)AddLogLine(false,_T(":%d %d %d %d "),((char*)pszTemp)[0],((char*)pszTemp)[1],((char*)pszTemp)[2],((char*)pszTemp)[3]);
			pszTemp = wcstok(NULL, _T("rn"));
		}
	}

	delete[] pszData;

	m_count = list.GetCount();
	//AddLogLine(false,_T("m_count:%d"),m_count);

	if(bResult && m_count > 0)
	{
		m_filterwords = new TCHAR*[m_count+1];
		m_kmpvalue = new int*[m_count+1];
		ZeroMemory(m_filterwords, sizeof(TCHAR *) * m_count);
		ZeroMemory(m_kmpvalue, sizeof(int *) * m_count);
	}

	for(int i = 0; bResult && (i < m_count); i ++)
	{
		CString s = list.GetAt(list.FindIndex(i));
		s.MakeLower();
		nLen = s.GetLength();
		//AddLogLine(false,_T("nLen:%d"),nLen);
		m_filterwords[i] = new TCHAR[nLen + 1];
		m_filterwords[i][nLen] = 0;    // 最后一个字节设为0
		m_kmpvalue[i] = new int[nLen];
		//AddLogLine(false,_T("nLen:%d"),nLen);
		_tcscpy(m_filterwords[i],s);
		//AddLogLine(false,_T("m_filterwords[i]:%s"),m_filterwords[i]);
		KMP_GetNext(m_filterwords[i], m_kmpvalue[i]);    // 得到一个与内容有关的数值m_kmpvalue[i]
	}

	if(m_count == 0 || !bResult)
	{
		Free();
		//m_filterall = true;
	}
}

bool    CWordFilter::VerifyString(const CString & sString)    // 验证字符是否合法
{
	bool bIsRm = sString.Right(3)==_T(".rm");
	CString sReduceString=sString;
	CString sInterpunctionString = _T("(),().。·;:-《》『』~ “”〓!【】★×┇");
	try // VC-Huby[2007-03-20]:满足中国国情特色,加强过滤
	{
		int j=0;
		for( int i=0; i< sString.GetLength(); i++ )
		{
			if( sString.GetAt(i)<=_T('/') && sString.GetAt(i)>=_T(' ') ) //从空格到'/'之间的字符减掉后再过滤
			{
				continue;
			}
			else if( sString.GetAt(i)<=_T('@') && sString.GetAt(i)>=_T(':') )
			{
				continue;
			}
			else if( sString.GetAt(i)<=_T('`') && sString.GetAt(i)>=_T('[') )
			{
				continue;
			}
			else if( sString.GetAt(i)<=_T('~') && sString.GetAt(i)>=_T('{') )
			{
				continue;
			}
			else if( sInterpunctionString.Find(sString.GetAt(i))>=0 )
			{
				continue;
			}
			else
			{
				sReduceString.SetAt(j,sString.GetAt(i));
				j++;
			}
		}
		if( j< sString.GetLength() )
			sReduceString.SetAt(j,_T(''));
	}
	catch (...)
	{
	}

	if(m_filterall){
		//AddLogLine(false,_T("m_filterall"));
		return true;    // 检测不到文件,或者读取错误的情况下放弃过滤
	}
	if(m_count == 0){
		//AddLogLine(false,_T("m_count == 0"));
		return true;    // 文件是空的时候,放弃过滤功能
	}
	CString strSearch = ((CString)sReduceString).MakeLower();

	//vc-huby: 过滤中文字符超过15字符
	//CString sReduceString2=strSearch;
	int k=0;
	for( int i=0; i< strSearch.GetLength(); i++ )
	{
		if( strSearch.GetAt(i)<=_T('9') && strSearch.GetAt(i)>=_T('0') )
		{
			continue;
		}
		if( strSearch.GetAt(i)<=_T('z') && strSearch.GetAt(i)>=_T('a') )
		{
			continue;
		}
		else
		{
			k++;
		}
	}

	if( k>=20 && bIsRm )
		return false;
//int m = sReduceString2.GetLength();
/*
  if( k>=60 )
  return false;*/

/*if (strSearch.GetLength() > 20)
  {
  return false;
  }*/

	for(int i = 0; i < m_count; i ++)
	{
		if(KMP_Match(strSearch, m_filterwords[i], m_kmpvalue[i]))
		{
			//AddLogLine(false,_T("KMP_Match"));
			return false;    // 关键词命中了,被fliter了
		}
	}
	//AddLogLine(false,_T("漏掉的"));
	return true;

}

void CWordFilter::Free()    //
{
	for(int i = 0; i < m_count; i ++)
	{
		if(m_filterwords[i])
			delete[] m_filterwords[i];
		if(m_kmpvalue[i])
			delete[] m_kmpvalue[i];
	}
	delete[] m_filterwords;
	delete[] m_kmpvalue;
}

CWordFilter::~CWordFilter()
{
	Free();
}

其中WordFilter.h的第17行有以下定义。

#define FLITER_FILE _T("wordfilter.txt")

于是贝壳查看了C:Program FileseMuleconfig目录,在下面发现了wordfilter.txt 2007-09-30 12:58 10788。大家有兴趣自己看看里面的内容,贝壳就不贴了,贴出来绝对被封,死1090次。

下面说一点起效方式,也许大家很奇怪,这些内容是可以搜索的。贝壳仔细查看了代码,类在两处被引用了,一个是MFC初始化系统的时候初始化类,载入词典。另外一个是在SearchList.cpp 2007-11-20 17:56 22505,351行AddToList函数,第360行,内容如下。

// WordFilter added by kernel1983 2006.07.31
if(!WordFilter.VerifyString(toadd->GetFileName()))
{
	delete toadd;
	return false;
}

这个封锁手法尤其狠毒,并非封锁你的搜索,而是如果你的文件信息内有这些关键词,那么文件共享消息就不会被发送到服务器上,如同这个文件没有被共享一样。这样既没有用户会发现被封锁的事实(因为有少量其他客户端的数据会被检索出来),又能达到封锁的目地。

当然,贝壳理解VeryCD这帮人的苦心,毕竟他们还住在中国,不过估计从此后,贝壳和朋友的机器上不会装VeryCD了。