可以检测到打包的可执行文件吗?

信息安全 恶意软件 杀毒软件 反恶意软件 检测
2021-09-10 05:51:05

加壳程序是一种对可执行程序进行混淆处理的方法,即进行转换,使结果仍然是可执行的,并且在运行时具有相同的效果,但看起来不同(因此静态防病毒不会检测到它)。坏人经常使用自定义打包程序来混淆他们的恶意软件,以降低反病毒软件检测到恶意软件的可能性,或者使反病毒软件供应商更难对恶意软件进行逆向工程并弄清楚它在做什么。

是否可以检测特定的可执行文件是否已使用自定义打包程序打包?换句话说,给定一个可执行文件,我想将其归类为“已使用自定义打包程序打包”或“尚未打包”。现有的防病毒工具是否擅长检测可执行文件是否已使用自定义打包程序打包?

我对以下更简单的问题变体特别好奇。假设我知道坏人正在使用一个特定的自定义打包程序。识别已使用此打包程序打包的可执行文件是否可行?换句话说,给定一个可执行文件 E 和一个打包程序 P,我想将 E 分类为“被 P 打包”或“不是”。有没有已知的技术可以做到这一点?它们有多有效?

4个回答

有许多已知的方法来识别封隔器。由于 EXe 中的签名检测, “最常见”的打包程序 UPX及其变体通常被防病毒引擎标记为“可疑”。

有几个方便的工具叫做:

  • RDG Packer Detector,它根据签名检查检测特定的打包程序(大概与 AV 的方式相同)

  • PEiD检测 PE 文件的最常见的打包程序、加密程序和编译器,并允许反汇编(可通过 softpedia 下载)

  • 一个简单的python签名数据库检查器供您使用(不知道从哪里获取数据库(试试这里?

这里还有一个列表,列出了几种包装器的变体

一旦你检测到打包器的类型,你可以使用自动解包器(如果已经有的话)或者你可以开始手动解包它。

现有的防病毒工具是否擅长检测可执行文件是否已使用自定义打包程序打包?

不,不是真的,他们会(像上面的工具一样)寻找签名。如果您曾经花时间在黑帽论坛上,人们会宣传“FUD Crypters/Packers”,这意味着他们通常会修改其打包程序的工作方式,以便 AV 暂时不会获取签名,从而允许恶意软件溜走。

根据 symantect(如本文所引

赛门铁克收集了大量的加壳程序——200 多个家族中的 2000 多个变种。

上面提到的论文还有很多关于所使用的技术、所使用的反技术以及更多的信息,这些信息将回答你问题的第二部分。

有一些科学论文提供了自动检测打包可执行文件的可能技术。

例如 Perdisci、Lanzi 和 Lee链接应用模式识别来区分打包文件和非打包文件。他们用于模式识别的功能可以深入了解它的工作原理:

标准和非标准部分的数量
打包的可执行文件通常不遵循标准名称。

仅可执行部分的数量
打包的可执行文件通常没有任何仅可执行部分。

可读/可写/可执行部分的数量
打包的可执行文件至少需要一个可读、可写和可执行的部分。非打包文件的可执行部分不需要是可写的。

IAT 中的条目数
打包的可执行文件通常具有较少的导入。

PE 标头、代码、数据和文件熵
加密的代码看起来像随机的。

Lyda 和 Hamrock 描述了一种只使用熵的技术。关联

很容易识别exe文件是否打包

1)打开文件PEView并检查部分名称。很多时候,它将部分显示为UPX0, UPX1, MPRESS1

2)打开文件PEView并转到文本部分并检查行数据的大小和虚拟大小。如果这两个值之间的差异很大,则认为文件已打包。有时文本部分的所有内容都为零。

如果您想以编程方式执行此操作,您应该使用这种(或类似的)方式/功能来检测 PE 中的打包器/保护器或编译器签名,然后通过存在于已知签名数据库中的签名列表比较此签名(例如 PEiD 的“userdb.txt”)。

#include <Windows.h>
#include <iostream>
using namespace std;
IMAGE_DOS_HEADER Dos_Header;
IMAGE_FILE_HEADER Pe_Header;
IMAGE_OPTIONAL_HEADER Opt_Header;
IMAGE_SECTION_HEADER ImgSection;
DWORD sections = 0, Count = 0;
void Get_Sign(LPSTR szFileName, DWORD Lenght);
int main() {
Get_Sign(
  "C:\\Program Files (x86)\\NoVirusThanks\\Smart PC Locker Pro\\NSPL.exe" /* used in this example ! */
  , 399);
system("pause");
return 0;
} /* */
void Get_Sign(LPSTR szFileName, DWORD Lenght)
// Lenght=399 by defult
{
DWORD i;
HANDLE hFile;
unsigned char Buff;
DWORD Signature = 0;
DWORD EP, IVA, RAW, UNL, Offset;
DWORD BytRet;
hFile = CreateFileA((LPCSTR) szFileName, GENERIC_READ, FILE_SHARE_READ,
  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
  cout << "Error in opening file\n";
  exit(0);
}
else {
  ReadFile(hFile, &Dos_Header, sizeof(IMAGE_DOS_HEADER), &BytRet, NULL);
  if (Dos_Header.e_magic = IMAGE_DOS_SIGNATURE) {
   SetFilePointer(hFile, Dos_Header.e_lfanew, NULL, 0);
   ReadFile(hFile, &Signature, sizeof(Signature), &BytRet, NULL);
   if (Signature == IMAGE_NT_SIGNATURE) {
    ReadFile(hFile, &Pe_Header, sizeof(Pe_Header), &BytRet, NULL);
    sections = Pe_Header.NumberOfSections;
    if (Pe_Header.SizeOfOptionalHeader > 0) {
     ReadFile(hFile, &Opt_Header, sizeof(Opt_Header),
      &BytRet, NULL);
     EP = Opt_Header.AddressOfEntryPoint;
     for (Count = 1; Count <= sections; Count++) {
      ReadFile(hFile, &ImgSection, sizeof(ImgSection),
       &BytRet, NULL);
      if (EP < ImgSection.VirtualAddress +
       ImgSection.SizeOfRawData)
       break;
     }
     IVA = ImgSection.VirtualAddress;
     RAW = ImgSection.PointerToRawData;
     UNL = Opt_Header.SizeOfUninitializedData;
     Offset = EP - IVA + RAW;
     // get size of  uninitialized data
     // UNL = IVA - UNL;
     // Offset = EP - UNL + RAW;
     // get offset of signatures to read
    }
    for (i = Offset; i <= (Offset + Lenght); i++) {
     SetFilePointer(hFile, i, NULL, 0);
     ReadFile(hFile, &Buff, sizeof(Buff), &BytRet, NULL);
     printf("%X", Buff);
    }
    cout << endl << endl;
   }
  }
}
}

(如果我的英语不好,请原谅)