Skip to content

A DLL that is only a payload container

At a glance

Sample
ef431e36a8ac12051af588d800e1d8128d583a9d4c68187709263a13564081cd.dll (3.7 MB, PE32, MinGW-built, unsigned)
Question
Its exports have names but no code. What is this DLL for, and what does it do when it is loaded?
You'll use
Navigator strip · Exports · TLS · Code window · --disasm
Time
about 10 minutes, nothing executed

Step 1 Look at the shape of the file

Open the DLL and look at the navigator strip before anything else.

Windows screenshot: Exports of the DLL, with the navigator strip showing one 3.7 MB block

Exports of the DLL, with the navigator strip showing one 3.7 MB block

Almost the whole file is one block. The code (.text) is a sliver at the left edge: 6 KB of code next to 3.7 MB of something else. Section Headers and --hash-range say what it is:

$ ppee-cli --hash-range ef431e36….dll
.text  offset=0x400 size=6144 entropy=6.055
.data  offset=0x1C00 size=3867136 entropy=7.952
…
PS C:\> ppee-cli.exe --hash-range C:\MalwareSamples\ef431e36a8ac12051af588d800e1d8128d583a9d4c68187709263a13564081cd.dll
headers  offset=0x0 size=1024 entropy=2.225
  md5     7b0ef189ff302d1856891d53cf18dbd7
  sha1    9129b5e68271a96eeed90109b66bdddda7ce3110
  sha256  02966a6449d638835889b142a2c29f8c5d5db0767833d14454c8af26fcf16504
  crc32   A688A039
  ssdeep  6:idqGVg3F+X32QRFos6KUvQlLqsbEOpiQaA0I0bzA5l/sAXPAhixm8EEZj2e/3uM:etGSGQRGILPbEO7aOlzsrolE8xC
  tlsh    T14111ED6363B98EF2E41C0278018F47123039713006A29ED38ED411EABD70A983B07B42
.text  offset=0x400 size=6144 entropy=6.055
  md5     2ba8d872afd3bf38fcefae4271e1f0fd
  sha1    55a320983522b82a86eaef2fce6a54d6fcee3632
  sha256  f3538d207c4b460b52221bcc8e7f7056abcd4674ea3bef65c23417178f1cec4f
  crc32   EB2675C6
  ssdeep  96:PtxO2IHjdia7tf+Lt4ZS+vgze9l/QJeejRtSoyfHqYTPCr/M8h/SVqLBAIgCnyo:FxB8dxpfg4xBe/SFf5rCrT/BBAowtw
  tlsh    T1F2C19429ACD750F2C92744F11DC7E7FA0A14A6229419CDF1F2F9D111F8BAA04AAD91F8
.data  offset=0x1C00 size=3867136 entropy=7.952
  md5     f149872e0a472bdf0a9b615dcf1a9af2
  sha1    0966ea72a17c2a302ac226f2c880e9597dd9111b
  sha256  c7b4798f77a82118c5b4232407c5f8d7b3ca91d20f1de0bbf2e6c07a613160c0
  crc32   71EE1739
  ssdeep  98304:0VnxNKUD4ErrOLU2YKRi+qt4vg2BntsIs5M5wJQvHFg+v0:0VnxNneYolt5s5/QvNv0
  tlsh    T1EC0633A456AA55D0F6217686F60CFDEC03C2E4550FA91B1735B6FFCADB0AAC0D80E493
.rdata  offset=0x3B1E00 size=1024 entropy=4.814
  md5     823c3c09fbe26f5d1c39741e04db0c7d
  sha1    b7f76cf84b417026d150298e54af3be353333b14
  sha256  7a979665ecd15a96919d05e0e8e2d002ed3ac7615e2cc31ed3af112faab590d2
  crc32   212F9824
  ssdeep  24:rOovorcIomq2xg1QLFyWFEWFEWFyWFyWFyWFyWFyWFyWFyWFyWFyWFyWFyWFEWF:rOovoQccqzRRzzzzzzzzzzzRzzz
  tlsh    T1111135432F00D193C74C2E7615E54E2C9A927CC9CED04020E57DFECAAB126E95E197A3
.eh_fram  offset=0x3B2200 size=2048 entropy=4.329
  md5     c43335940ba66b730fed1bea1ea97d43
  sha1    1a6767bc48c376ff98047f030979df923c706a81
  sha256  fdd3cc3427914d4ce850ac8846916ab7bafa72f991f009f8cb75ba41fa94d195
  crc32   B7A7243
  ssdeep  48:yY1McFTQ+b3uttF/7cV4wW9fSRCYGH4V4UQx4xznx5YihlEZUaxsf:AYuwmLsQYGYTznXJ0K
  tlsh    T10241C31EE9081A0DE576FE3499DEC632CA097D7DC317472B3E3B5E00306B2596C4D446
.edata  offset=0x3B2A00 size=512 entropy=1.889
  md5     6f7cd44feb236ba046f67fb04d00ecfd
  sha1    6ad867a9fdf3b861d3692fa2b6136641c0704dc9
  sha256  937dedc584842e975aa537a885b0f130e479ef095ea738ffe6dbcd2f9d8ac755
  crc32   72D43386
  ssdeep  3:qcbckDjslcqHl0cdeHtGV6JUQVetk9EENXPczEUDW1SDfO7/l:qcIkDAvmcdeAAetkvN/czEd1SDm
  tlsh    T165F08CA6933CAB68D2992331400F1CE6F32090B078332680C68314C01CE22223116A21
.idata  offset=0x3B2C00 size=1536 entropy=3.934
  md5     d7ba9dcdc7cd526a9777fad587644f4b
  sha1    0757095361f6c0db04e1057cfb9841307d284c8c
  ...

.data, 3.87 MB at entropy 7.95: encrypted or compressed. A DLL that is 99.6 % data is a container, not a library.

Step 2 Read the exports

Select DIR_ENTRY_EXPORT (the screenshot above).

  • The internal Dll Name, affiliate_21sys_4a1f3a9c.dll, isn't the file's name and is shown in the warning color. It reads like a per-affiliate build ID.
  • All six exports point into .data [RW], not into code:
Export RVA What it is
g_data 3020 The start of .data: the 3.7 MB blob
g_len 3B3020 Its length
g_k1, g_k2 3B3040, 3B3080 Two 64-byte keys
g_ko1, g_ko2 3B30C0, 3B30C1 One byte each

The exports are a table of contents for the payload, left in by the builder that generated the DLL.

Step 3 Rule out the TLS callbacks

DIR_ENTRY_TLS (2) means code runs before DllMain. Click a callback's corner mark:

Windows screenshot: The first TLS callback: compares the Reason argument with 2 and 1

The first TLS callback: compares the Reason argument with 2 and 1

cmp eax, 0x2 / cmp eax, 0x1 on the Reason argument, a global set to 2: this is MinGW-w64's own TLS callback (the file has no Rich header and is built with GCC). Not a trick; see the TLS walkthrough.

Step 4 Follow DllMain

Analysis → Code → API call sites shows one function using GetEnvironmentVariableA, lstrcatA, CreateFileA, WriteFile, LoadLibraryA and DeleteFileA: sub_6D7415F9. Open it in the Code window (G, 6D7415F9):

Windows screenshot: DllMain in the Code window: LOCALAPPDATA and \sync.dll, a decode call, then CreateFileA and WriteFile of g_len bytes of g_data

DllMain in the Code window: LOCALAPPDATA and \sync.dll, a decode call, then CreateFileA and WriteFile of g_len bytes of g_data

  1. cmp [ebp+0xC], 1: only on DLL_PROCESS_ATTACH.
  2. GetEnvironmentVariableA("LOCALAPPDATA") + lstrcatA(…, "\\sync.dll"): the drop path is %LOCALAPPDATA%\sync.dll.
  3. call 0x6D741485: decode the blob (step 5).
  4. CreateFileA(path, GENERIC_WRITE, …, CREATE_ALWAYS, …) and WriteFile(h, g_data, g_len, …): write the decoded payload.
  5. LoadLibraryA(path): run it as a DLL.
  6. DeleteFileA(path): delete the file once it is loaded.
$ ppee-cli --disasm va:0x6D7415F9 --count 80 ef431e36….dll
   6D741602  83 7D 0C 01           cmp dword ptr [ebp+0xC], 0x1
   …
   6D74162E  C7 04 24 44 40 AF 6D  mov dword ptr [esp], 0x6DAF4044          ; "LOCALAPPDATA"
   6D741635  A1 F0 80 AF 6D        mov eax, dword ptr [kernel32.GetEnvironmentVariableA]
   …
   6D74163F  C7 44 24 04 51 40 AF 6D mov dword ptr [esp+0x4], 0x6DAF4051      ; "\\sync.dll"
   6D741650  A1 28 81 AF 6D        mov eax, dword ptr [kernel32.lstrcatA]
   …
   6D74165A  E8 26 FE FF FF        call 0x6D741485
   …
   6D741698  A1 D8 80 AF 6D        mov eax, dword ptr [kernel32.CreateFileA]
   …
   6D7416AF  A1 20 30 AF 6D        mov eax, dword ptr [g_len]
   6D7416C7  C7 44 24 04 20 30 74 6D mov dword ptr [esp+0x4], 0x6D743020      ; g_data
   6D7416D5  A1 24 81 AF 6D        mov eax, dword ptr [kernel32.WriteFile]
   …
   6D741709  A1 10 81 AF 6D        mov eax, dword ptr [kernel32.LoadLibraryA]
   …
   6D741725  A1 E0 80 AF 6D        mov eax, dword ptr [kernel32.DeleteFileA]

Step 5 See how the blob is decoded

$ ppee-cli --disasm va:0x6D741485 --count 75 ef431e36….dll
   6D741494  mov eax, dword ptr [ebp-0xC]                         ; i = 0 … 0x3F
   6D741497  add eax, 0x6DAF3040                      ; g_k1
   6D74149F  movzx eax, byte ptr [g_ko1]
   6D7414A6  xor edx, eax                                         ; g_k1[i] ^= g_ko1
   …
   6D7414BD  movzx eax, byte ptr [g_ko2]
   6D7414C4  xor edx, eax                                         ; g_k2[i] ^= g_ko2
   6D7414D4  cmp dword ptr [ebp-0xC], 0x3F
   …                                                              ; then for i = 0 … g_len-1:
   6D7414F7  movzx eax, byte ptr [eax+0x6DAF3080]                 ;   b ^= g_k2[i & 0x3F]
   6D741515  shl eax, 0x6   …   6D741525  shr al, 0x2             ;   b = ror(b, 2)
   6D741541  not eax                                              ;   b = ~b
   6D741560  movzx eax, byte ptr [eax+0x6DAF3040]                 ;   b ^= g_k1[i & 0x3F]
PS C:\> ppee-cli.exe --disasm va:0x6D741485 --count 75 C:\MalwareSamples\ef431e36a8ac12051af588d800e1d8128d583a9d4c68187709263a13564081cd.dll
C:\MalwareSamples\ef431e36a8ac12051af588d800e1d8128d583a9d4c68187709263a13564081cd.dll: 3880960 bytes, PE32

Disassembly: va 0x6D741485, x86, section .text
   6D741485  55                    push ebp
   6D741486  89 E5                 mov ebp, esp
   6D741488  83 EC 48              sub esp, 0x48
   6D74148B  C7 45 F4 00 00 00 00  mov dword ptr [ebp-0xC], 0x0
   6D741492  EB 40                 jmp 0x6D7414D4
   6D741494  8B 45 F4              mov eax, dword ptr [ebp-0xC]
   6D741497  05 40 30 AF 6D        add eax, 0x6DAF3040                      ; g_k1
   6D74149C  0F B6 10              movzx edx, byte ptr [eax]
   6D74149F  0F B6 05 C0 30 AF 6D  movzx eax, byte ptr [g_ko1]
   6D7414A6  31 C2                 xor edx, eax
   6D7414A8  8B 45 F4              mov eax, dword ptr [ebp-0xC]
   6D7414AB  05 40 30 AF 6D        add eax, 0x6DAF3040                      ; g_k1
   6D7414B0  88 10                 mov byte ptr [eax], dl
   6D7414B2  8B 45 F4              mov eax, dword ptr [ebp-0xC]
   6D7414B5  05 80 30 AF 6D        add eax, 0x6DAF3080                      ; g_k2
   6D7414BA  0F B6 10              movzx edx, byte ptr [eax]
   6D7414BD  0F B6 05 C1 30 AF 6D  movzx eax, byte ptr [g_ko2]
   6D7414C4  31 C2                 xor edx, eax
   6D7414C6  8B 45 F4              mov eax, dword ptr [ebp-0xC]
   6D7414C9  05 80 30 AF 6D        add eax, 0x6DAF3080                      ; g_k2
   6D7414CE  88 10                 mov byte ptr [eax], dl
   6D7414D0  83 45 F4 01           add dword ptr [ebp-0xC], 0x1
   6D7414D4  83 7D F4 3F           cmp dword ptr [ebp-0xC], 0x3F
   6D7414D8  76 BA                 jbe 0x6D741494
   6D7414DA  C7 45 F0 00 00 00 00  mov dword ptr [ebp-0x10], 0x0
   6D7414E1  E9 91 00 00 00        jmp 0x6D741577
   6D7414E6  8B 45 F0              mov eax, dword ptr [ebp-0x10]
   6D7414E9  05 20 30 74 6D        add eax, 0x6D743020                      ; g_data
   6D7414EE  0F B6 10              movzx edx, byte ptr [eax]
   6D7414F1  8B 45 F0              mov eax, dword ptr [ebp-0x10]
   6D7414F4  83 E0 3F              and eax, 0x3F
   6D7414F7  0F B6 80 80 30 AF 6D  movzx eax, byte ptr [eax+0x6DAF3080]
   6D7414FE  31 C2                 xor edx, eax
   6D741500  8B 45 F0              mov eax, dword ptr [ebp-0x10]
   6D741503  05 20 30 74 6D        add eax, 0x6D743020                      ; g_data
   6D741508  88 10                 mov byte ptr [eax], dl
   6D74150A  8B 45 F0              mov eax, dword ptr [ebp-0x10]
   6D74150D  05 20 30 74 6D        add eax, 0x6D743020                      ; g_data
   6D741512  0F B6 00              movzx eax, byte ptr [eax]
   6D741515  C1 E0 06              shl eax, 0x6
   6D741518  89 C2                 mov edx, eax
   6D74151A  8B 45 F0              mov eax, dword ptr [ebp-0x10]
  ...

(Bytes columns removed and comments added to fit.) The names from step 2 make it readable:

  1. The two 64-byte keys are unmasked first: g_k1 ^= g_ko1 (0x94) and g_k2 ^= g_ko2 (0xA6). A key stored masked doesn't show up as plain bytes.
  2. Every byte of g_data then goes through XOR with k2, rotate right by 2, NOT, XOR with k1, in place, g_len (0x3B0000) bytes.

Step 6 Decode the next stage without running anything

Everything the decoder needs is in the file, and the four operations are all decode steps. Unmask the two keys (XOR each byte of g_k1 with 94, of g_k2 with A6) and run:

$ ppee-cli --decode rva:0x3020 --length 0x3B0000 \
    --steps "xor:2E41351E…9E90C328, ror:2, not, xor:CF1D45EE…66CAA06C" ef431e36….dll
Source: 0x1C20, 3866624 bytes, in .data
Result: 3866624 bytes, entropy 6.097, PE image (starts with MZ)
        md5 14320f1d6e6e5ac0d53c9d4ce2380b24
        sha256 006bca7fca78e4cb9a9629c007d629d08f0ef64a58cf045860e5668b774b4539

(The two 64-byte keys are shortened here.) The result is a PE image, entropy down from 7.95 to 6.10: the decoded sync.dll. Its SHA-256 is the indicator to look up. In the GUI, the hex view's Decode Selection… takes the same steps.

Verdict

A loader DLL generated per affiliate. When it is loaded it decodes its 3.7 MB .data blob (two masked 64-byte keys, XOR / rotate / NOT), writes the result to %LOCALAPPDATA%\sync.dll, loads it and deletes it. The next stage was recovered statically (SHA-256 006bca7f…4539). Indicators: the drop path, the export names (g_data, g_k1, …) and the internal name affiliate_21sys_4a1f3a9c.dll.

More walkthroughs: all walkthroughs · A signed dropper

References