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
- MD5 hash:
a4513379dad5233afa402cc56a8b9222
- VirusTotal link to sample: https://www.virustotal.com/gui/file/ccd380ea868ffad4f960d7455fecf88c2ac3550001bbb6c21c31ae70b3bbf4f6/detection
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:00419680↓o
.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+159↓o
.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+22↑j
.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+8↑j
.text:00412C06 xor eax, eax
.text:00412C08
.text:00412C08 locret_412C08: ; CODE XREF: DialogFunc+2E↑j
.text:00412C08 ; DialogFunc+33↑j
.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:004198EC↓o
.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 calledDialogFunc
- 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