Shell's Home

Sep 24, 2008 - 4 minute read - Comments

VeryCD版电驴(eMule)存在封锁

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了。