Fork me on GitHub

Finfisher extracted binary (Part 1)

This article will be about the spawned resource1.exe, that we extracted in article number 2. Part 1 Part 2 Part 3 Part 5

Filename sha256
resource1.exe f18afbad0230af8c7ec7b4c1d25544f3d3445a825861a1de18432de5b4586c7b
  • The most interesting part happens at 0x4020d0, a call to EnumWindows installs a hook that is called on every found window on the current desktop. This function is located at 0x401c1b and makes again use of the hook KiUserExceptionDispatcher -> ud2 trick. As the target function just increments the Eip in the given CONTEXT struct with 2.
0x00401c1b    8bff         mov edi, edi
0x00401c1d    55           push ebp
0x00401c1e    8bec         mov ebp, esp
0x00401c20    81eca8060000 sub esp, 0x6a8
0x00401c26    a170524000   mov eax, dword [0x405270] 
0x00401c2b    33c5         xor eax, ebp
0x00401c2d    8945fc       mov dword [ebp - 4], eax
0x00401c30    53           push ebx
0x00401c31    56           push esi
0x00401c32    33f6         xor esi, esi
0x00401c34    57           push edi
0x00401c35    89b5bcf9ffff mov dword [ebp - 0x644], esi
0x00401c3b    e89df8ffff   call 0x4014dd
0x00401c40    85c0         test eax, eax
0x00401c42    7507         jne 0x401c4b
0x00401c44    56           push esi
0x00401c45    ff1574104000 call dword [reloc.KERNEL32.dll_ExitProcess_116]
0x00401c4b    8d45ec       lea eax, dword [ebp - 0x14]
0x00401c4e    50           push eax
0x00401c4f    e87a180000   call 0x4034ce
0x00401c54    8985c8f9ffff mov dword [ebp - 0x638], eax
0x00401c5a    0f0b         ud2
  • Right behind the three ud2 instructions, the installed hook at KiUserExceptionDispatcher will be removed and it will be checked if the current process is a Wow64bit process. In this article i will follow the 32 Bit part. After checking the priviliges of the process, a ressource will be decrypted like the one in the last article. With some changes to the offset used to get the resource and probably the id, i was able to use my vala script from the last article again. Part 2
uint64 off = getressource2(core, (uint64)0x406000, 5, 101, &len);
uint64 off2 = getressource2(core, (uint64)0x406000, 5, 102,  &len2);
  • A search for some modules and processes will decide the kind of execution of the decrypted resource. This happens at 0x004039DD. The function first gets all modules and processes found in the current context. Its searching for strings located at 0x401288, 0x401278, 0x401264 and 0x401254. The resulting strings are: cmdguard.sys, cfp.exe, klif.sys and avp.exe.

    Note to check the values at these position:

[0x004039dd]> px 25 @ 0x401288
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00401288  6300 6d00 6400 6700 7500 6100 7200 6400  c.m.d.g.u.a.r.d.
0x00401298  2e00 7300 7900 7300 00                   ..s.y.s..     
0x004039dd    8bff         mov edi, edi
0x004039df    55           push ebp
0x004039e0    8bec         mov ebp, esp
0x004039e2    56           push esi
0x004039e3    57           push edi
0x004039e4    e878fdffff   call 0x403761
0x004039e9    8bf0         mov esi, eax
0x004039eb    e8fffdffff   call 0x4037ef
0x004039f0    8bf8         mov edi, eax
0x004039f2    6888124000   push 0x401288                              
0x004039f7    57           push edi
0x004039f8    e808ffffff   call 0x403905
0x004039fd    85c0         test eax, eax
0x004039ff    7421         je 0x403a22
0x00403a01    6878124000   push 0x401278                              
0x00403a06    56           push esi
0x00403a07    e89ffeffff   call 0x4038ab
0x00403a0c    85c0         test eax, eax
0x00403a0e    7412         je 0x403a22
0x00403a10    8b4508       mov eax, dword [ebp + 8]                    ; [0x8:4]=4
0x00403a13    c74004a0204. mov dword [eax + 4], 0x4020a0               ; [0x4:4]=3
0x00403a1a    c700b8204000 mov dword [eax], 0x4020b8
0x00403a20    eb40         jmp 0x403a62
0x00403a22    6864124000   push 0x401264                              
0x00403a27    57           push edi
0x00403a28    e8d8feffff   call 0x403905
0x00403a2d    85c0         test eax, eax
0x00403a2f    7421         je 0x403a52
0x00403a31    6854124000   push 0x401254                              
0x00403a36    56           push esi
0x00403a37    e86ffeffff   call 0x4038ab
0x00403a3c    85c0         test eax, eax
0x00403a3e    7412         je 0x403a52
0x00403a40    8b4508       mov eax, dword [ebp + 8]                    ; [0x8:4]=4
0x00403a43    c740044e1a4. mov dword [eax + 4], 0x401a4e               ; [0x4:4]=3
0x00403a4a    c700831a4000 mov dword [eax], 0x401a83
0x00403a50    eb10         jmp 0x403a62
0x00403a52    8b4508       mov eax, dword [ebp + 8]                    ; [0x8:4]=4
0x00403a55    c74004ab1a4. mov dword [eax + 4], 0x401aab               ; [0x4:4]=3
0x00403a5c    c700e01a4000 mov dword [eax], 0x401ae0
0x00403a62    56           push esi
0x00403a63    e815feffff   call 0x40387d
0x00403a68    57           push edi
0x00403a69    e80ffeffff   call 0x40387d
0x00403a6e    33c0         xor eax, eax
0x00403a70    5f           pop edi
0x00403a71    40           inc eax
0x00403a72    5e           pop esi
0x00403a73    5d           pop ebp
0x00403a74    c20400       ret 4
  • For cmdguard.sys and cfp.exe the returned offsets are
0x00403a13    c74004a0204. mov dword [eax + 4], 0x4020a0               ; [0x4020a0:4]=0x8b55ff8b 
0x00403a1a    c700b8204000 mov dword [eax], 0x4020b8                   ; [0x4020b8:4]=0x8b55ff8b 
  • The kaspersky offsets are:
0x00403a43    c740044e1a4. mov dword [eax + 4], 0x401a4e               ; [0x401a4e:4]=0x8b55ff8b 
0x00403a4a    c700831a4000 mov dword [eax], 0x401a83                   ; [0x401a83:4]=0x8b55ff8b 
  • If none of these could be found
0x00403a13    c74004a0204. mov dword [eax + 4], 0x4020a0               ; [0x4020a0:4]=0x8b55ff8b 
0x00403a1a    c700b8204000 mov dword [eax], 0x4020b8                   ; [0x4020b8:4]=0x8b55ff8b 
  • The trick with these offsets is that the first one [eax + 4] gets called if the binary got called without admin rights. The other one if admin rights are available. It seems that the main goal of this is to start the extracted binary from the resource section.

No Antivir/Kaspersky with available admin rights

  • These two are handled through the same function:
0x00401ae0    8bff         mov edi, edi
0x00401ae2    55           push ebp
0x00401ae3    8bec         mov ebp, esp
0x00401ae5    6a00         push 0
0x00401ae7    6a00         push 0
0x00401ae9    8d4508       lea eax, dword [ebp + 8]
0x00401aec    50           push eax
0x00401aed    ff7508       push dword [ebp + 8]
0x00401af0    ff1550104000 call dword [reloc.KERNEL32.dll_GetCurrentProcessId_80]
0x00401af6    50           push eax
0x00401af7    e8e7100000   call 0x402be3
0x00401afc    33c0         xor eax, eax
0x00401afe    40           inc eax
0x00401aff    5d           pop ebp
0x00401b00    c20400       ret 4
  • The main difference is the first argument pushed on the stack. Its '0' if no antivir has been found and '1' in case of Kaspersky module and client process was found.
0x00401a83    8bff         mov edi, edi
0x00401a85    55           push ebp
0x00401a86    8bec         mov ebp, esp
0x00401a88    6a01         push 1
0x00401a8a    6a00         push 0
0x00401a8c    8d4508       lea eax, dword [ebp + 8]
0x00401a8f    50           push eax
0x00401a90    ff7508       push dword [ebp + 8]
0x00401a93    ff1550104000 call dword [reloc.KERNEL32.dll_GetCurrentProcessId_80]
0x00401a99    50           push eax
0x00401a9a    e844110000   call 0x402be3
0x00401a9f    33c0         xor eax, eax
0x00401aa1    40           inc eax
0x00401aa2    5d           pop ebp
0x00401aa3    c20400       ret 4
  • The interesting stuff happens at 'call 0x402be3'. First of all it allocates two memory spaces with the size of the decrypted resource block (0x4c000). It then copies the header to the memory and also all sections to the correct VirtualAddresses in the new buffer in order to execute the code correctly.
typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];      // 0x00
  union {
    DWORD PhysicalAddress                                   // 0x08
    DWORD VirtualSize;                                      // 0x08
  } Misc;
  DWORD VirtualAddress;                                     // 0x0C
  DWORD SizeOfRawData;                                      // 0x10
  DWORD PointerToRawData;                                   // 0x14 
  DWORD PointerToRelocations;                           // 0x18 
  DWORD PointerToLinenumbers;                           // 0x1C
  WORD  NumberOfRelocations;                            // 0x20 
  WORD  NumberOfLinenumbers;                            // 0x22
  DWORD Characteristics;                                    // 0x24
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
0x00402cf0    ff7654       push dword [esi + 0x54]                     ; IMAGE_NT_HEADERS.OptionalHeader.SizeOfHeader
0x00402cf3    ff750c       push dword [ebp + 0xc]                      ; src
0x00402cf6    50           push eax                                    ; dst
0x00402cf7    e84c100000   call 0x403d48                               ; memcpy
0x00402cfc    8365e800     and dword [ebp - 0x18], 0
0x00402d00    83c40c       add esp, 0xc
0x00402d03    66837e0600   cmp word [esi + 6], 0                        ; [0x6:2]=0
0x00402d08    7636         jbe 0x402d40
0x00402d0a    8b45ec       mov eax, dword [ebp - 0x14]
0x00402d0d    83c014       add eax, 0x14                               ; IMAGE_SECTION_HEADER.PointerToRelocation
0x00402d10    8945ec       mov dword [ebp - 0x14], eax
0x00402d13    eb03         jmp 0x402d18
0x00402d15    8b45ec       mov eax, dword [ebp - 0x14]
0x00402d18    ff70fc       push dword [eax - 4]                        ; SizeOfRawData
0x00402d1b    8b08         mov ecx, dword [eax]
0x00402d1d    034d0c       add ecx, dword [ebp + 0xc]
0x00402d20    8b40f8       mov eax, dword [eax - 8]                    ; VirtualAddress
0x00402d23    0345f8       add eax, dword [ebp - 8]
0x00402d26    51           push ecx
0x00402d27    50           push eax
0x00402d28    e81b100000   call 0x403d48
0x00402d2d    0fb74606     movzx eax, word [esi + 6]                    ; [0x6:2]=0 ; NumberOfSections
0x00402d31    8345ec28     add dword [ebp - 0x14], 0x28                ; IMAGE_SECTION_HEADER++
0x00402d35    83c40c       add esp, 0xc
0x00402d38    ff45e8       inc dword [ebp - 0x18]
0x00402d3b    3945e8       cmp dword [ebp - 0x18], eax
0x00402d3e    72d5         jb 0x402d15
  • Now the code adds 0xA0 to the IMAGE_NT_HEADER pointer. The DataDirectory Array starts at 0x78 offset and one IMAGE_DATA_DIRECTORY entry is 8 bytes in size. So the resulting DataDirectory index is calculated (0xA0 - 0x78) / 8 = 5.
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
typedef struct _IMAGE_NT_HEADERS {
  DWORD Signature; /* "PE"\0\0 */   /* 0x00 */
  IMAGE_FILE_HEADER FileHeader;     /* 0x04 */
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;   /* 0x18 */
} IMAGE_NT_HEADERS32

typedef struct _IMAGE_OPTIONAL_HEADER {
  /* Standard fields */
  WORD  Magic; /* 0x10b or 0x107 */ /* 0x00 */
  BYTE  MajorLinkerVersion;                                     // 0x1A
  BYTE  MinorLinkerVersion;                                     // 0x1B
  DWORD SizeOfCode;                                                     // 0x1C
  DWORD SizeOfInitializedData;                              // 0x20
  DWORD SizeOfUninitializedData;                            // 0x24
  DWORD AddressOfEntryPoint;        /* 0x10 */      // 0x28
  DWORD BaseOfCode;                                                     // 0x2C
  DWORD BaseOfData;                                                     // 0x30
  DWORD ImageBase;                                                      // 0x34
  DWORD SectionAlignment;       /* 0x20 */              // 0x38
  DWORD FileAlignment;                                              // 0x3C
  WORD  MajorOperatingSystemVersion;                    // 0x40
  WORD  MinorOperatingSystemVersion;                    // 0x42
  WORD  MajorImageVersion;                                      // 0x44
  WORD  MinorImageVersion;                                      // 0x46
  WORD  MajorSubsystemVersion;      /* 0x30 */  // 0x48
  WORD  MinorSubsystemVersion;                              // 0x4A
  DWORD Win32VersionValue;                                      // 0x4C
  DWORD SizeOfImage;                                                    // 0x50
  DWORD SizeOfHeaders;                                              // 0x54
  DWORD CheckSum;           /* 0x40 */                          // 0x58
  WORD  Subsystem;                                                      // 0x5C
  WORD  DllCharacteristics;                                     // 0x5E
  DWORD SizeOfStackReserve;                                     // 0x60
  DWORD SizeOfStackCommit;                                      // 0x64
  DWORD SizeOfHeapReserve;      /* 0x50 */          // 0x68
  DWORD SizeOfHeapCommit;                                           // 0x6C
  DWORD LoaderFlags;                                                    // 0x70
  DWORD NumberOfRvaAndSizes;                                    // 0x74
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */ // 0x78
  /* 0xE0 */
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
  • This block reads the VirtualAddress of the BaseRelocation Table and adds the result to the offset of the allocated memory ( [ebp - 8]) to get the position of the copied relocation table in the allocated memory.
0x00402d40    8b86a0000000 mov eax, dword [esi + 0xa0]                  ; [0xa0:4]=0xf6b99169 
0x00402d46    85c0         test eax, eax
0x00402d48    7477         je 0x402dc1
0x00402d4a    8b4df8       mov ecx, dword [ebp - 8]
0x00402d4d    8b96a4000000 mov edx, dword [esi + 0xa4]                  ; [0xa4:4]=0xf6aab239 
0x00402d53    03c1         add eax, ecx
  • The relocating table contains a VirtialAddress and a size that defines the count of offsets for the actual block. It simply adds the current offset to the VirtualAddress to get the position that needs to be relocated.
typedef struct _IMAGE_BASE_RELOCATION
{
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
    /* WORD TypeOffset[1]; */
} IMAGE_BASE_RELOCATION,*PIMAGE_BASE_RELOCATION;
  • But first we have to get the delta between the original ImageBase and the position of the buffer (desired new position). [ebp - 0xc] contains the buffer with the copied resource and [esi + 0x34] is IMAGE_NT_HEADERS.OptionalHeader.ImageBase.
0x00402d55    8b4df4       mov ecx, dword [ebp - 0xc]
0x00402d58    2b4e34       sub ecx, dword [esi + 0x34]
  • Subtract size of VirtualAddress and SizeOfBlock and divide that with 2 to get the count of relocations for the current block.
0x00402d73    83ea08       sub edx, 8
0x00402d76    d1ea         shr edx, 1
  • VirtualAddress+buffer+offset lead us to the correct position that we have to relocate. Finally we add the delta to this position.
0x00402d97    8b55e4       mov edx, dword [ebp - 0x1c]
0x00402d9a    81e2ff0f0000 and edx, 0xfff
0x00402da0    03ca         add ecx, edx
0x00402da2    8b55e0       mov edx, dword [ebp - 0x20]
0x00402da5    0111         add dword [ecx], edx
  • The relocated content from the VirtualAlloc`ed memory gets copied to the memory allocated with VirtualAllocEx.
0x00402dc1    6a00         push 0
0x00402dc3    ff7650       push dword [esi + 0x50]
0x00402dc6    ff75f8       push dword [ebp - 8]
0x00402dc9    ff75f4       push dword [ebp - 0xc]
0x00402dcc    ff75fc       push dword [ebp - 4]
0x00402dcf    ff55f0       call dword [ebp - 0x10] ; WriteProcessMemory
  • Now the needet offsets to the used library functions will be collected and saved in a struct that is used later. The function at 0x4021bd is called like: function(modulebase, verysecretnumber) and returns the address of a library function. I wrote a little python script to get the name of the used function.
0x00402dd2    688dbdc13f   push 0x3fc1bd8d
0x00402dd7    ff7508       push dword [ebp + 8]
0x00402dda    e8def3ffff   call 0x4021bd
#!/bin/python2
import pefile
import sys
import numpy as np
from ctypes import c_uint
from ctypes import c_int


def calc (name):
    eax = np.uint32(0xffffffff)
    ecx = np.int32(0)
    esi = np.uint32(0)

    for c in name:
        eax = eax ^ np.uint32(ord(c))
        ecx = 7
        while ecx >= 0:
            esi = eax
            esi = esi & np.uint32(1)
            esi = esi * -1
            esi = esi & 0xedb88320
            eax = eax >> 0x1
            eax = eax ^ esi
            ecx = ecx - 1
    return np.uint32(~eax)


pe =  pefile.PE(sys.argv[1])
num = int(sys.argv[2], 0)
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
    if calc(exp.name) == np.uint32(num):
        print exp.name
        break
  • The size of the resulting struct is 0x2c as the following call to VirtualAllocEx offers.
0x00402e7b    6a04         push 4
0x00402e7d    57           push edi
0x00402e7e    6a2c         push 0x2c  ; Size
0x00402e80    6a00         push 0
0x00402e82    ff75fc       push dword [ebp - 4] ; 0xffffffff
0x00402e85    ffd3         call ebx     ; VirtualAllocEx
struct ShellcodeArguments {
void* GetProcAddress;
void* LoadLibraryA;
void* VirtualProtect;
void* VirtualFree;
void* RtlExitUserThread;
void* CsrClientCallServer;
void* UnpackedLoadedResource;
void* unknown_1;
void* kernel32.dll_base;
int unknown_2;
int unknown_3;
};
  • Next some kind of shellcode will be copied in a new allocated 0x300 bytes memory space.
0x00402ea9    6a00         push 0
0x00402eab    57           push edi
0x00402eac    8bd8         mov ebx, eax
0x00402eae    680c234000   push 0x40230c
0x00402eb3    53           push ebx
0x00402eb4    ff75fc       push dword [ebp - 4]
0x00402eb7    895d08       mov dword [ebp + 8], ebx                     ; [0x8:4]=4
0x00402eba    ff55f0       call dword [ebp - 0x10]
  • It will be used to call CreateRemoteThread with 0xffffffff as process-id lpStartAddress is the copied shellcode and lpParameter our created struct.
0x00402ebd    8d45d4       lea eax, dword [ebp - 0x2c]
0x00402ec0    50           push eax
0x00402ec1    33c0         xor eax, eax
0x00402ec3    50           push eax
0x00402ec4    ff750c       push dword [ebp + 0xc]
0x00402ec7    53           push ebx
0x00402ec8    50           push eax
0x00402ec9    50           push eax
0x00402eca    ff75fc       push dword [ebp - 4]
0x00402ecd    ff55d8       call dword [ebp - 0x28]
  • The shellcodes loads all needet libaries and their imports and finally calls AddressOfEntryPoint of the extracted binary. In the next article i will cover what happens if the executable does not have admin rights and what if comodo is running on the system.

links

social