查看: 6888|回复: 46
收起左侧

[讨论] 木马也革命,继云杀毒后“云马”出现之云马样本

  [复制链接]
rsin
发表于 2012-1-23 21:20:12 | 显示全部楼层 |阅读模式
                                                                转自“小七论坛”       注明:云马并不是现在出现,而是现在开始卡饭首发"云马"运行方式。
运行后释放1. C:\Program Files\Microsoft Offic\servlces.exe
               2. C:\Program Files\Microsoft Offic\

调用函数去运行servlces.exe文件 这里的函数动态调用了

然后servlces.exe运行 调用 了同目录的dll

servlces.exe文件是签名文件  他写启动是绿色提示的

HaoZip.dll不是木马主程序 他就是今天云马的主角

起到下载dll主体文件 然后内存调用到下载的dll
关键内存调用dll技术下面给大家资料

云马好处 dll主程序不会在系统中实体存在



以下源码分析:
http://www.cppblog.com/mybios/archive/2006/11/21/15483.html
直接载入内存中的DLL


Win32 程序调用 DLL 的机制

   Win32 EXE 在调用一个外部 DLL 中的函数时,首先要调用 LoadLibary 函数来载入此 DLL 到程序的进程地址空间。 如果 LoadLibary 载入此 DLL 成功,将返回一个该 DLL 的句柄。 这个句柄实际上就是该 DLL 在内存中的起始地址。 在载入 DLL 成功后,还必须调用 GetProcAddress 函数来获取要调用的函数的地址。然后再根据该地址来调用这个函数。
根据上述原理,我们可以把一个 DLL 作为资源文件放到 EXE 文件中,在程序运行时,分配一块内存,然后将此资源复制到该分配的内存中,并根据该内存地址计算得到相关的导出函数地址,然后,当我们需要调用某一函数时,可以用该函数在内存中的地址来调用它。
程序实现。
  首先,把要合并的 DLL 作为资源加入到项目的资源文件中,然后在程序运行时,从资源中载入该资源,以得到该 DLL 在内存中的位置:

LPVOID sRawDll; // 资源文件在内存中的地址
HRSRC hRes;
HMODULE hLibrary;
HGLOBAL hResourceLoaded;
char lib_name[MAX_PATH];
GetModuleFileName(hInstance, lib_name, MAX_PATH ); // 得到运行程序的名字
hLibrary = LoadLibrary(lib_name);                  // 就象载入一个 DLL 一样载入运行程序到内存中

if (NULL != hLibrary)
{
  // 得到指定的资源文件在内存中的位置
  hRes = FindResource(hLibrary, MAKEINTRESOURCE(IDR_DATA1), RT_RCDATA);
  if (NULL != hRes)
  {
    // 将资源文件载入内存
    hResourceLoaded = LoadResource(hLibrary, hRes);
    if (NULL != hResourceLoaded)
    {
      // 得到资源文件大小
      SizeofResource(hLibrary, hRes);

      // 锁定资源以得到它在内存中的地址
      sRawDll = (LPVOID)LockResource(hResourceLoaded);
    }
  }
  else return 1;
  FreeLibrary(hLibrary);
}
else return 1;

然后,从资源中载入 DLL 到内存函数 LoadPbDllFromMemory 将载入 DLL 到内存中, 该函数有两个参数,第一个参数是指向 DLL 资源在内存中的地址的指针,也就是前面代码中的 LockResource 函数的返回值。第二个参数是一个空的指针,如果函数 LoadPbDllFromMemory 运行成功,该指针将指向重新组合后的内存中的 DLL 的起始地址。该函数还有一个功能就是如果运行成功,它将手动地用 DLL_PROCESS_ATTACH 参数调用 DLL 的入口函数 DllMain 来初始化该 DLL。除此之外,它还会手动地载入合并的 DLL 的入口表中导入的 DLL 并调整它们在内存中的相对地址。以下是该函数代码:
DWORD LoadPbDllFromMemory(LPVOID lpRawDll, LPVOID lpImageDll)
{
  SYSTEM_INFO sSysInfo;
  PIMAGE_DOS_HEADER dosHeader;
  PIMAGE_NT_HEADERS pNTHeader;
  PIMAGE_SECTION_HEADER section;
  PIMAGE_IMPORT_DESCRIPTOR pImportDesc;
  PIMAGE_IMPORT_BY_NAME pOrdinalName;
  PIMAGE_BASE_RELOCATION baseReloc;
  PDWORD lpLink;
  unsigned char Protection[4096];
  HINSTANCE hDll;
  WORD i;
  DWORD ImagePages,fOldProtect,j,MaxLen,HdrLen,Addr1,Addr2,Pg,Pg1,Pg2;
  char * sDllName;

  if(NULL == lpRawDll) return 1 ;

  dosHeader = (PIMAGE_DOS_HEADER)lpRawDll;

  // Is this the MZ header?
  if ((TRUE == IsBadReadPtr(dosHeader,sizeof (IMAGE_DOS_HEADER))) ||
         (IMAGE_DOS_SIGNATURE != dosHeader->e_magic))
    return 2;

  // Get the PE header.
  pNTHeader = MakePtr(PIMAGE_NT_HEADERS,dosHeader,dosHeader->e_lfanew);

  // Is this a real PE image?
  if((TRUE == IsBadReadPtr(pNTHeader,sizeof ( IMAGE_NT_HEADERS))) ||
        ( IMAGE_NT_SIGNATURE != pNTHeader->Signature))
    return 3 ;

  if(( pNTHeader->FileHeader.SizeOfOptionalHeader !=
      sizeof(pNTHeader->OptionalHeader)) ||
    (pNTHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC))
    return 4;

  if (pNTHeader->FileHeader.NumberOfSections < 1) return 5;

  section = IMAGE_FIRST_SECTION( pNTHeader );
  int HeaderSize = sizeof(IMAGE_SECTION_HEADER);

  // 节头长度
  HdrLen = (DWORD)section - (DWORD)dosHeader +
      HeaderSize * pNTHeader->FileHeader.NumberOfSections;

  // 找出最大的节的长度,此节一般是代码所在的节(.text 节)
  MaxLen = HdrLen;
  int ii=0;

  for (i = 0;i<(DWORD)pNTHeader->FileHeader.NumberOfSections;i++)// find MaxLen
  {
    if(MaxLen < section.VirtualAddress + section.SizeOfRawData)
    {
      MaxLen = section.VirtualAddress + section.SizeOfRawData;
    }
    if(strcmp((const char *)section.Name,".rsrc") == 0) ii=i;
  }

  GetSystemInfo(&sSysInfo);
  ImagePages = MaxLen / sSysInfo.dwPageSize;
  if (MaxLen % sSysInfo.dwPageSize) ImagePages++;

  // 分配所需的内存
  DWORD NeededMemory = ImagePages * sSysInfo.dwPageSize;
  lpImageDll = VirtualAlloc(NULL, NeededMemory, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  if (lpImageDll == NULL) return 6; // 分配内存失败

  MoveMemory( lpImageDll, lpRawDll, HdrLen ); // 复制节头

  DWORD OrgAddr = 0;
  DWORD NewAddr = 0;
  DWORD Size = 0;

  // 复制 .text 节数据
  for (i = 0;i<pNTHeader->FileHeader.NumberOfSections;i++)
  {
    OrgAddr = (DWORD)lpImageDll + (DWORD)section.VirtualAddress;
    NewAddr = (DWORD)lpRawDll + (DWORD)section.PointerToRawData;
    Size = (DWORD)section.SizeOfRawData;
    MoveMemory((void *)OrgAddr, (void *)NewAddr, Size );
  }

  // 把指针指向新的 DLL 映像
  dosHeader = (PIMAGE_DOS_HEADER) lpImageDll; // Switch to new image
  pNTHeader = (PIMAGE_NT_HEADERS) ((DWORD)dosHeader + dosHeader->e_lfanew);
  section = (PIMAGE_SECTION_HEADER) ((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));
  pImageBase = (PBYTE)dosHeader;

  if((ii!=0) && (IsNT()==TRUE))
  {
    section[ii].VirtualAddress = section[ii].VirtualAddress + (DWORD)lpRawDll;
    section[ii].PointerToRawData = section[ii].PointerToRawData + (DWORD)lpRawDll;
  }

  DWORD importsStartRVA;

  // Look up where the imports section is (normally in the .idata section)
  // but not necessarily so. Therefore, grab the RVA from the data dir.
  importsStartRVA = GetImgDirEntryRVA(pNTHeader,IMAGE_DIRECTORY_ENTRY_IMPORT);
  if ( !importsStartRVA )
  {
    VirtualFree(dosHeader,0, MEM_RELEASE);
    return 7;
  }

  pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) pNTHeader->
    OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;

  if(pImportDesc!= 0)
    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR) ((DWORD)pImportDesc + (DWORD)dosHeader);
  else
  {
    VirtualFree(dosHeader,0, MEM_RELEASE);
    return 8;
  }

  while (1) // 处理各入口表中的 DLL
  {
    // 检查是否遇到了空的 IMAGE_IMPORT_DESCRIPTOR
    if ((pImportDesc->TimeDateStamp==0 ) && (pImportDesc->Name==0)) break;

    // 从磁盘载入必须的 Dll,
    // 注意,载入的 DLL 是合并的 DLL 的入口表中的 DLL,
    // 不是合并到 EXE 中的 DLL
    sDllName = (char *) (pImportDesc->Name + (DWORD)pImageBase);
    hDll = GetModuleHandle(sDllName);

    if (hDll == 0 ) hDll = LoadLibrary(sDllName);

    if (hDll == 0 )
    {
      MessageBox(NULL, "Can''t find required Dll",
          "Error in LoadPbDllFromMemory()",0);
      VirtualFree(dosHeader,0, MEM_RELEASE);
      return 9;
    }

    DWORD *lpFuncNameRef = (DWORD *) (pImportDesc->OriginalFirstThunk +
                 (DWORD)dosHeader);
    DWORD *lpFuncAddr = (DWORD *) (pImportDesc->FirstThunk +
                 (DWORD)dosHeader);

    while( *lpFuncNameRef != 0)
    {
      pOrdinalName = (PIMAGE_IMPORT_BY_NAME) (*lpFuncNameRef +
             (DWORD)dosHeader);
      DWORD pIMAGE_ORDINAL_FLAG = 0x80000000;

      if (*lpFuncNameRef & pIMAGE_ORDINAL_FLAG)
        *lpFuncAddr = (DWORD) GetProcAddress(hDll,
           (const char *)(*lpFuncNameRef & 0xFFFF));
      else
        *lpFuncAddr = (DWORD) GetProcAddress(hDll,
             (const char *)pOrdinalName->Name);

      if (lpFuncAddr == 0)
      {
        VirtualFree(dosHeader,0, MEM_RELEASE);
        return 10;// Can''t GetProcAddress
      }

      lpFuncAddr++;
      lpFuncNameRef++;
    }
    pImportDesc++;
  }

  DWORD TpOffset;
  baseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pNTHeader->
     OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

  if (baseReloc !=0)
  {
    baseReloc = (PIMAGE_BASE_RELOCATION) ((DWORD)baseReloc + (DWORD)dosHeader);
    while(baseReloc->VirtualAddress != 0)
    {
      PWORD lpTypeOffset = (PWORD) ((DWORD)baseReloc +
           sizeof(IMAGE_BASE_RELOCATION));
      while (lpTypeOffset < (PWORD)((DWORD)baseReloc +
             (DWORD)baseReloc->SizeOfBlock))
      {
        TpOffset = *lpTypeOffset & 0xF000;
        if(TpOffset == 0x3000)
        {
          lpLink = (PDWORD) ((DWORD)dosHeader +
                                   baseReloc->VirtualAddress +
                                                      (*lpTypeOffset & 0xFFF));
          *lpLink = (DWORD)dosHeader +
                                            (*lpLink) - pNTHeader->OptionalHeader.ImageBase;
        }
        else
        {
          if (TpOffset != 0)
          {
            VirtualFree(dosHeader,0, MEM_RELEASE);
            return 10;
          }
        }
        lpTypeOffset++;
      }
      baseReloc = (PIMAGE_BASE_RELOCATION)((DWORD)baseReloc +
        (DWORD)baseReloc->SizeOfBlock);
    }
  }

  // 取得原始的内存状态
  memset(Protection,0,4096);
  for (i = 0;i<=pNTHeader->FileHeader.NumberOfSections;i++)
  {
    if (i == pNTHeader->FileHeader.NumberOfSections)
    {
      Addr1 = 0;
      Addr2 = HdrLen;
      j = 0x60000000;
    }
    else
    {
      Addr1 = section.VirtualAddress;
      Addr2 = section.SizeOfRawData;
      j = section.Characteristics;
    }
    Addr2 += Addr1 - 1;

    Pg1 = Addr1 / sSysInfo.dwPageSize;
    Pg2 = Addr2 / sSysInfo.dwPageSize;
    for(Pg = Pg1 g<=Pg2g++)
    {
      if (j & 0x20000000) Protection[Pg] |= 1; // Execute
      if (j & 0x40000000) Protection[Pg] |= 2; // Read
      if (j & 0x80000000) Protection[Pg] |= 4; // Write
    }
  }

  // 恢复原始的内存状态
  Addr1 = (DWORD)dosHeader;
  for (Pg = 0 g<= ImagePagesg++)
  {
    switch(Protection[Pg])
    {
    case 2:
      fOldProtect = PAGE_READONLY;
      break;
    case 3:
      fOldProtect = PAGE_EXECUTE_READ;
      break;
    case 6:
      fOldProtect = PAGE_READWRITE;
      break;
    default:
      // Ignore strange combinations
      fOldProtect = PAGE_EXECUTE_READWRITE;  
      break;
    }

    if (fOldProtect !=PAGE_EXECUTE_READWRITE)
    {
      if (VirtualProtect((void *)Addr1,
        sSysInfo.dwPageSize,
        fOldProtect,
        &fOldProtect) == 0)
      {
        VirtualFree(dosHeader,0, MEM_RELEASE);
        return 11;
      }
    }
    Addr1 += sSysInfo.dwPageSize;
  }

  EntryPoint = (LPENTRYPOINT) ((DWORD)pNTHeader->OptionalHeader.AddressOfEntryPoint +
         (DWORD)dosHeader);
  LPVOID lpReserved = 0;
  EntryPoint((HINSTANCE)dosHeader, DLL_PROCESS_ATTACH, lpReserved);
  lpImageDll2=lpImageDll;
  return 0;
}

  一但 DLL 被正确地载入到内存中,我们就可以通过自定义函数 GetProcAddressDirectly 来获取某函数在内存中的地址,并根据该地址来调用该函数,该函数也有两个参数,第一个参数是指向载入到内存中的 DLL 的起始地址的指针,第二个是要调用的函数的函数名。以下是 GetProcAddressDirectly 函数代码:
DWORD GetProcAddressDirectly(PIMAGE_DOS_HEADER dosHeader, char * FuncName)  
{
  PIMAGE_NT_HEADERS pNTHeader;  
  PIMAGE_EXPORT_DIRECTORY pExportDir;  
  PWORD lpNameOrdinals;  
  LPDWORD lpFunctions;  
  DWORD * lpName;  
  char * lpExpFuncName;  
  DWORD i;  
  DWORD j;  
  char * lpFuncName;  

  if(dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return 0;  

  pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + dosHeader->e_lfanew);  

  if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) return 0;  

  if ((pNTHeader->FileHeader.SizeOfOptionalHeader != sizeof(pNTHeader->OptionalHeader)) ||  
    (pNTHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC))  
    return 0;  

  DWORD exportsStartRVA, exportsEndRVA;  
  pImageBase = (PBYTE)dosHeader;  

  // Make pointers to 32 and 64 bit versions of the header.  
  pNTHeader = MakePtr( PIMAGE_NT_HEADERS, dosHeader,dosHeader->e_lfanew );  

  exportsStartRVA = GetImgDirEntryRVA(pNTHeader,IMAGE_DIRECTORY_ENTRY_EXPORT);  
  exportsEndRVA = exportsStartRVA +  
    GetImgDirEntrySize(pNTHeader, IMAGE_DIRECTORY_ENTRY_EXPORT);  

  // Get the IMAGE_SECTION_HEADER that contains the exports. This is  
  // usually the .edata section, but doesn''t have to be.  
  PIMAGE_SECTION_HEADER header;  
  header = GetEnclosingSectionHeader( exportsStartRVA, pNTHeader );  
  if ( !header ) return 0;  

  INT delta;  
  delta = (INT)(header->VirtualAddress - header->ointerToRawData);  
  pExportDir = (PIMAGE_EXPORT_DIRECTORY)GetPtrFromRVA(exportsStartRVA,
        pNTHeader, pImageBase);  


  pExportDir =(PIMAGE_EXPORT_DIRECTORY) (pNTHeader->
  OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);  

  if (pExportDir == 0)  
  {
    MessageBox(NULL,"Error in GetProcAddressDirectly()",0,0);  
    return 0;  
  }

  pExportDir =(PIMAGE_EXPORT_DIRECTORY) ((DWORD)pExportDir + (DWORD)dosHeader);  
  lpNameOrdinals =(PWORD)((DWORD)pExportDir->AddressOfNameOrdinals + (DWORD)dosHeader);  
  lpName =(LPDWORD) (pExportDir->AddressOfNames + (DWORD)dosHeader);  
  lpFunctions =(LPDWORD) (pExportDir->AddressOfFunctions + (DWORD)dosHeader);  
  lpFuncName = FuncName;  

  if(HIWORD(lpFuncName)!=0 )  
  {
    for( i = 0;i<=pExportDir->NumberOfFunctions - 1;i++)  
    {
      DWORD entryPointRVA = *lpFunctions;  

      // Skip over gaps in exported function  
      if ( entryPointRVA == 0 ) continue;
      for( j = 0;j<=pExportDir->NumberOfNames-1;j++)  
      {
        if( lpNameOrdinals[j] == i)  
        {
          lpExpFuncName = (char *) (lpName[j] +
              (DWORD)dosHeader);  
          if(strcmp((char *)lpExpFuncName,(char *)FuncName)==0)  
            return (DWORD) (lpFunctions +
                (DWORD)dosHeader);  
        }
      }
    }
  }
  else
  {
    for (i = 0 ;i<=pExportDir->NumberOfFunctions - 1;i++)  
    {
      if (lpFuncName == (char *)(pExportDir->Base + i))  
      {
        if (lpFunctions) return (unsigned long) (lpFunctions +
              dosHeader);  
      }
    }
  }
  return 0;  
}



在调用完函数后,不要忘记用 UnloadPbDllFromMemory 来从内存中移去 DLL 以释放分配的内存,该函数还会用 DLL_PROCESS_DETACH 参数调用 DLL 的入口函数 DllMain 来从调用进程的地址空间卸载该 DLL。 以下是 UnloadPbDllFromMemory 函数代码:
DWORD UnloadPbDllFromMemory(PIMAGE_DOS_HEADER dosHeader)
{
  PIMAGE_NT_HEADERS pNTHeader;
  pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + (DWORD)dosHeader->e_lfanew);
  EntryPoint = (LPENTRYPOINT)(pNTHeader->OptionalHeader.AddressOfEntryPoint +
           (DWORD)dosHeader);
  EntryPoint((HINSTANCE)dosHeader, DLL_PROCESS_DETACH, 0);
  return VirtualFree(dosHeader, 0, MEM_RELEASE);
}


--------------------------------------------------------------------------------
这里发一个云马样本文件过360主防(特制云hips,提示,未对云qvm处理),测试请删除C:\Program Files\360\360Safe\deepscan目录下的qvm文件夹测试
样本地址:http://bbs.kafan.cn/thread-1210747-1-1.html
貝殼
发表于 2012-1-23 21:22:21 | 显示全部楼层
哪有人會無聊去把云QVM關了
rsin
 楼主| 发表于 2012-1-23 21:24:40 | 显示全部楼层
本帖最后由 rsin 于 2012-1-23 21:30 编辑
貝殼 发表于 2012-1-23 21:22
哪有人會無聊去把云QVM關了?


嗯,我说过了是过360的提示,而且只要我有时间就能弄完美一点,再把云qvm过掉。可是现在没时间啊,大过年的或者李白有时间不 ,有时间就弄完美点。

评分

参与人数 1经验 0 收起 理由
XMonster 0

查看全部评分

billgates1996
发表于 2012-1-23 21:25:21 | 显示全部楼层
先占座,再慢慢看
-oAo-
发表于 2012-1-23 21:25:43 | 显示全部楼层
测试请删除C:\Program Files\360\360Safe\deepscan目录下的qvm文件夹测试
也就是说过不了360?
貝殼
发表于 2012-1-23 21:27:07 | 显示全部楼层
rsin 发表于 2012-1-23 21:24
嗯,我说过了是过360的提示,而且只要我有时间就能弄完美一点,再把云qvm过掉。可是现在没时间啊,大过年 ...

等乃過云qvm
黑暗執行者
发表于 2012-1-23 21:27:42 | 显示全部楼层
貝殼 发表于 2012-1-23 21:22
哪有人會無聊去把云QVM關了?

能过360杀毒(关闭云QVM情况下)已经很不错了!等待高手验证....
Ps:话说360杀毒,QVM实时防护默认不是关闭的吗?
rsin
 楼主| 发表于 2012-1-23 21:28:29 | 显示全部楼层
-oAo- 发表于 2012-1-23 21:25
测试请删除C:\Program Files\360\360Safe\deepscan目录下的qvm文件夹测试
也就是说过不了360?

见3楼
rsin
 楼主| 发表于 2012-1-23 21:28:46 | 显示全部楼层
貝殼 发表于 2012-1-23 21:27
等乃過云qvm

节后.....
jefffire
头像被屏蔽
发表于 2012-1-23 21:35:11 | 显示全部楼层
本帖最后由 jefffire 于 2012-1-23 21:40 编辑

dll加载漏洞 servlces.exe既然能直接加载dll,何必再从内存加载呢?
您需要登录后才可以回帖 登录 | 快速注册

本版积分规则

手机版|杀毒软件|软件论坛| 卡饭论坛

Copyright © KaFan  KaFan.cn All Rights Reserved.

Powered by Discuz! X3.4( 沪ICP备2020031077号-2 ) GMT+8, 2025-2-25 11:48 , Processed in 0.137157 second(s), 18 queries .

卡饭网所发布的一切软件、样本、工具、文章等仅限用于学习和研究,不得将上述内容用于商业或者其他非法用途,否则产生的一切后果自负,本站信息来自网络,版权争议问题与本站无关,您必须在下载后的24小时之内从您的电脑中彻底删除上述信息,如有问题请通过邮件与我们联系。

快速回复 客服 返回顶部 返回列表