Home Finding the start of Emotet malware in MFC app
Post
Cancel

Finding the start of Emotet malware in MFC app

Intro

Sometimes you need to dig a little to really find where the start of malicious code is in a binary. Sometimes it is not obvious from static analysis how exactly code flows to the malicious part. This post shows one case for Emotet found in an old MFC application and where exactly it gets called during the MFC initialization process.

The malware sample used in this blog post

A note on dynamic analysis

When I start to analyze malware, I first use binary inspection tools and other static analysis tools to understand what is in the binary to the best of my ability. This can give a lot of details and in some cases be the only thing needed. At a minimum, it will give insight into what to look for when starting to debug into it live.

During the debugging process I take a snapshot in VMware once I have the binary open in IDA Pro and also have it open in Immunity Debugger and breaked on the Entry point of the binary. When something goes wrong and the malware breaks free you can just revert to this snapshot and start over (this happened many times as I was figuring out the final user code function called by the MFC application).

Lastly, it is very helpful to not just stay in Immunity Debugger but to swap back and forth between IDA Pro (or Ghidra) and the debugger as you move through it. For me, at least, it is easier to understand and visualize the potential branches and jumps in IDA to prepare for it in the debugger.

Debugging workflow

A visual of functions and calls we step through

Through IDA and Immunity Debugger’s eyes

Break at entry:

1
.text:00404878                 push    60h

Scrolling down you eventually see AfxWinMain (WinMain is wrapped by AfxWinMain by the MFC framework)

1
.text:004049f7 e8 02 98        CALL       AfxWinMain

The function has some calls that are to pointers defined at runtime:

1
2
3
.text:00413E71                 call    dword ptr [eax+50h]
.text:00413E82                 call    dword ptr [eax+60h]
.text:00413E89                 call    dword ptr [eax+68h]

In ImmDbg I stepped through to see which call it hit:

1
00413E71   .  FF50 50       CALL DWORD PTR DS:[EAX+50]               ;  ccd380ea.00402410

Which points to the following function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
.text:00402410 sub_402410      proc near  ; DATA XREF: .rdata:00419680o
.text:00402410
.text:00402410 var_1D4         = byte ptr -1D4h
.text:00402410 var_C           = dword ptr -0Ch
.text:00402410 var_4           = dword ptr -4
.text:00402410
.text:00402410                 push    0FFFFFFFFh
.text:00402412                 push    offset SEH_402410
.text:00402417                 mov     eax, large fs:0
.text:0040241D                 push    eax
.text:0040241E                 mov     large fs:0, esp
.text:00402425                 sub     esp, 1C8h
.text:0040242B                 push    esi
.text:0040242C                 mov     esi, ecx
.text:0040242E                 push    0
.text:00402430                 lea     ecx, [esp+1DCh+var_1D4]
.text:00402434                 call    sub_402610
.text:00402439                 lea     eax, [esp+1D8h+var_1D4]
.text:0040243D                 lea     ecx, [esp+1D8h+var_1D4]
.text:00402441                 mov     [esp+1D8h+var_4], 0
.text:0040244C                 mov     [esi+1Ch], eax
.text:0040244F                 call    DoModel ; CDialog::DoModel()
.text:00402454                 lea     ecx, [esp+1D8h+var_1D4]
.text:00402458                 mov     [esp+1D8h+var_4], 0FFFFFFFFh
.text:00402463                 call    sub_402390
.text:00402468                 mov     ecx, [esp+1D8h+var_C]
.text:0040246F                 xor     eax, eax
.text:00402471                 pop     esi
.text:00402472                 mov     large fs:0, ecx
.text:00402479                 add     esp, 1D4h
.text:0040247F                 retn
.text:0040247F sub_402410      endp

If you execute the DoModel call, the malware will run away so we need to step into this

1
.text:0040244f e8 f0 0e        CALL       CDialog::DoModal ; int DoModal(CDialog * this)

Stepping in you will run across many more CALL’s:

1
2
3
4
5
6
7
8
9
10
11
12
13
.text:00413365 e8 42 3a        CALL       AfxGetModuleState
.text:00413373 e8 34 3a        CALL       AfxGetModuleState
.text:00413381 ff 15 20        CALL       dword ptr [->KERNEL32.DLL::FindResourceA]
.text:00413389 ff 15 24        CALL       dword ptr [->KERNEL32.DLL::LoadResource]
.text:0041339b ff 15 28        CALL       dword ptr [->KERNEL32.DLL::LockResource]
.text:004133b1 e8 ce fa        CALL       CDialog::PreModal
.text:004133b9 e8 8b c9        CALL       AfxUnhookWindowCreate
.text:004133c8 ff 15 84        CALL       dword ptr [->USER32.DLL::GetDesktopWindow]
.text:004133d6 ff 15 ec        CALL       dword ptr [->USER32.DLL::IsWindowEnabled]
.text:004133e5 ff 15 5c        CALL       dword ptr [->USER32.DLL::EnableWindow]
.text:004133f7 e8 71 db        CALL       AfxHookWindowCreate
.text:004133ff e8 9d c8        CALL       CWnd::FromHandle
.text:00413409 e8 29 fd        CALL       CWnd::CreateDlgIndirect

The call to CreateDlgIndirect once again causes the malware to run away so we step into that and continue tracing through more calls:

1
2
3
4
5
6
.text:00413156 e8 51 3c        CALL       AfxGetModuleState
.text:00413161 e8 46 3c        CALL       AfxGetModuleState
.text:0041317a e8 7b c7        CALL       AfxEndDeferRegisterClass
.text:00413184 e8 71 c7        CALL       AfxEndDeferRegisterClass
.text:004131e1 e8 e3 0b        CALL       CDialogTemplate::GetFont
.text:0041327c e8 ec dc        CALL       AfxHookWindowCreate

Finally, we get into another call that leads to the user code being called:

1
2
3
4
5
00413290   .  68 D12B4100   PUSH ccd380ea.00412BD1                   ; |pDlgProc = ccd380ea.00412BD1
00413295   .  50            PUSH EAX                                 ; |hOwner
00413296   .  56            PUSH ESI                                 ; |pTemplate
00413297   .  FF75 10       PUSH DWORD PTR SS:[EBP+10]               ; |hInst
0041329A   .  FF15 88924100 CALL DWORD PTR DS:[<&USER32.CreateDialog>; \CreateDialogIndirectParamA

The part to take note of is the pDlgProc address as this points to the user code that MFC will execute as a callback. Navigating to ccd380ea.00412BD1 you will find the DialogFunc function where we are about to isolate the true start of the malware we want to analyze. This is close to where I would take another snapshot during our debugging process.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.text:00412BD1 ; BOOL __stdcall DialogFunc(HWND, UINT, WPARAM, LPARAM)
.text:00412BD1 DialogFunc      proc near               ; DATA XREF: sub_413137+159o
.text:00412BD1
.text:00412BD1 arg_0           = dword ptr  4
.text:00412BD1 arg_4           = dword ptr  8
.text:00412BD1
.text:00412BD1                 cmp     [esp+arg_4], 110h
.text:00412BD9                 jnz     short loc_412C06
.text:00412BDB                 push    [esp+arg_0]
.text:00412BDF                 call    sub_40FCC8
.text:00412BE4                 push    eax
.text:00412BE5                 push    offset off_41ACC0
.text:00412BEA                 call    sub_4139EB
.text:00412BEF                 test    eax, eax
.text:00412BF1                 pop     ecx
.text:00412BF2                 pop     ecx
.text:00412BF3                 jz      short loc_412C01
.text:00412BF5                 mov     edx, [eax]
.text:00412BF7                 mov     ecx, eax
.text:00412BF9                 call    dword ptr [edx+144h] ; The malicious code starts with this call
.text:00412BFF                 jmp     short locret_412C08
.text:00412C01 ; ---------------------------------------------------------------------------
.text:00412C01
.text:00412C01 loc_412C01:                             ; CODE XREF: DialogFunc+22j
.text:00412C01                 xor     eax, eax
.text:00412C03                 inc     eax
.text:00412C04                 jmp     short locret_412C08
.text:00412C06 ; ---------------------------------------------------------------------------
.text:00412C06
.text:00412C06 loc_412C06:                             ; CODE XREF: DialogFunc+8j
.text:00412C06                 xor     eax, eax
.text:00412C08
.text:00412C08 locret_412C08:                          ; CODE XREF: DialogFunc+2Ej
.text:00412C08                                         ; DialogFunc+33j
.text:00412C08                 retn    10h
.text:00412C08 DialogFunc      endp

Step into this call:

1
.text:00412bf9 ff 92 44        CALL       dword ptr [EDX + 0x144]    ;  ccd380ea.00402710

This gets us to part where the malware starts allocating new memory and decrypting new program code of which I’ll have separate posts on:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.text:00402710 sub_402710      proc near               ; DATA XREF: .rdata:004198ECo
.text:00402710
.text:00402710 phProv          = dword ptr -44h
.text:00402710 var_40          = byte ptr -40h
.text:00402710
.text:00402710                 sub     esp, 44h
.text:00402713                 push    ebx
.text:00402714                 push    esi
.text:00402715                 push    edi
.text:00402716                 mov     ebx, ecx
.text:00402718                 mov     ecx, 0Fh
.text:0040271D                 mov     esi, offset aF7R9rdcmukacUM ; "%F}7~R9RdcMUkAc{U*Mzcn#F~U}e%#nVFwu~zio"...
.text:00402722                 lea     edi, [esp+50h+var_40]
.text:00402726                 rep movsd
.text:00402728                 push    40h             ; flProtect
.text:0040272A                 push    3000h           ; flAllocationType
.text:0040272F                 movsw
.text:00402731                 push    810h            ; dwSize
.text:00402736                 push    0               ; lpAddress
.text:00402738                 movsb
.text:00402739                 call    ds:VirtualAlloc
.text:0040273F                 mov     edi, ds:CryptAcquireContextA
...and more...

Lessons learned

  • There are many places that malware can start but in the case of MFC applications where you see AfxWinMain called from the entry point, do a quick search for a function called DialogFunc
    • This function labeled itself in IDA so came up in the search
    • This function did NOT come up in Ghidra

MSDN References

  • Reference for the MSDN docs on the CreateDialogIndirectParamA func
    • https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createdialogindirectparama
This post is licensed under CC BY 4.0 by the author.