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.
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:
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:
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
cmp [ebp+0xC], 1: only onDLL_PROCESS_ATTACH.GetEnvironmentVariableA("LOCALAPPDATA")+lstrcatA(…, "\\sync.dll"): the drop path is%LOCALAPPDATA%\sync.dll.call 0x6D741485: decode the blob (step 5).CreateFileA(path, GENERIC_WRITE, …, CREATE_ALWAYS, …)andWriteFile(h, g_data, g_len, …): write the decoded payload.LoadLibraryA(path): run it as a DLL.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:
- The two 64-byte keys are unmasked first:
g_k1 ^= g_ko1(0x94) andg_k2 ^= g_ko2(0xA6). A key stored masked doesn't show up as plain bytes. - Every byte of
g_datathen goes through XOR withk2, rotate right by 2, NOT, XOR withk1, 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¶
- MITRE ATT&CK T1027.009: Embedded Payloads: a payload stored inside another file.
- MITRE ATT&CK T1140: Deobfuscate/Decode Files or Information: the XOR decoding step.


